pass rspace to backtrack (as yet unused), tweak DependencyMap API
[scpubgit/DX.git] / lib / DX / DependencyMap.pm
CommitLineData
efad53c4 1package DX::DependencyMap;
2
3use DX::Utils qw(CONTENTS_OF INDICES_OF);
3e465d5d 4use DX::Class;
efad53c4 5
6# { x => [ { y => [ ...
7# my $targ = $root; $targ = $targ->[0]{$_[0]} for @path
8# my $deps = $targ->[$${$dep_type}];
9
3e465d5d 10has deps => (is => 'ro', isa => DependencyTree, required => 1);
efad53c4 11
cfae7810 12has revdeps => (is => 'ro', required => 1, isa => HashRef[DependencyList]);
efad53c4 13
14sub new_empty {
15 my ($class) = @_;
16 $class->new(deps => {}, revdeps => {});
17}
18
77bc0c6e 19sub with_dependencies_for {
efad53c4 20 my ($self, $for_id, $deps_for) = @_;
77bc0c6e 21 my $old_revdeps = $self->revdeps;
efad53c4 22 my $new_revdeps = {
77bc0c6e 23 %{$old_revdeps},
24 $for_id => [ @{$old_revdeps->{$for_id}||[]}, @$deps_for ],
efad53c4 25 };
4016201b 26 my $new_deps = $self->_merge_deps_for(
8c6c9551 27 $self->deps, $for_id, @$deps_for
4016201b 28 );
efad53c4 29 ref($self)->new(
30 deps => $new_deps,
31 revdeps => $new_revdeps
32 );
33}
34
35sub without_entries_for {
36 my ($self, @for_ids) = @_;
37 my %new_revdeps = %{$self->revdeps};
38 my $new_deps = $self->deps;
39 $new_deps = $self->_unmerge_deps_for(
8c6c9551 40 $new_deps, $_, @{$new_revdeps{$_}}
efad53c4 41 ) for @for_ids;
42 delete @new_revdeps{@for_ids};
43 ref($self)->new(
44 deps => $new_deps,
45 revdeps => \%new_revdeps
46 );
47}
48
49sub _merge_deps_for {
50 my ($self, $deps, $for_id, @merge_these) = @_;
51 $self->_mangle_deps($deps, sub {
52 +{ %{$_[0]}, $for_id => 1 };
53 }, @merge_these);
54}
55
56sub _unmerge_deps_for {
57 my ($self, $deps, $for_id, @unmerge_these) = @_;
58 $self->_mangle_deps($deps, sub {
59 my %for_ids = %{$_[0]};
60 delete $for_ids{$for_id};
61 \%for_ids;
62 }, @unmerge_these);
63}
64
65sub _mangle_deps {
66 my ($self, $deps, $mangler, @to_mangle) = @_;
67 my $root = [ $deps ];
68 foreach my $mangle_this (@to_mangle) {
3e465d5d 69 assert_DependencySpec $mangle_this;
efad53c4 70 my ($type, @path) = @$mangle_this;
71 my $targ = $root;
72 foreach my $part (@path) {
73 my $sub = $targ->[0] = { %{$targ->[0]||{}} };
74 $targ = $sub->{$part} = [ @{$sub->{$part}||[]} ];
75 }
76 $targ->[$$$type] = $mangler->($targ->[$$$type]||{});
77 }
78 return $root->[0];
79}
80
77bc0c6e 81sub dependents_of {
efad53c4 82 my ($self, $event) = @_;
83 my ($type, @path) = @$event;
84 my $root = [ $self->deps ];
85 my $targ = $root;
86 my $last = pop @path;
87 my @dep_sets;
88 foreach my $part (@path) {
89 $targ = $targ->[0]{$part};
90 # set/add/remove x.y or x.y.z affects contents of x
91 push @dep_sets, $targ->[$${+CONTENTS_OF}];
92 }
93 if ($$$type) {
94 # add/remove x.y affects indices of x
95 push @dep_sets, $targ->[$${+INDICES_OF}];
96 }
97 $targ = $targ->[0]{$last};
98 {
99 # add/remove x affects existence of x
100 # set/add/remove x affects everything else too
101 push @dep_sets, @{$targ}[map $$$_, @$$type];
102 }
103 # set/add/remove x affects all dependencies on x.y, x.y.z etc.
104 my @q = values %{$targ->[0]};
105 while (my $el = shift @q) {
106 my ($el_kids, @el_deps) = @$el;
107 push @dep_sets, @el_deps;
108 push @q, values %{$el_kids||{}};
109 }
110 return keys %{{
111 map +($_ => 1), map keys %$_, grep defined, @dep_sets
112 }};
113}
114
115sub but_expire_dependents_of {
116 my ($self, @events) = @_;
117 my @expired = keys %{{
77bc0c6e 118 map +($_ => 1), map $self->dependents_of($_), @events
efad53c4 119 }};
120 # Didn't expire anything? Don't clone self
121 return $self unless @expired;
122 ($self->without_entries_for(@expired), @expired);
123}
124
3e465d5d 125sub dependencies_for { $_[0]->revdeps->{$_[1]} }
126
efad53c4 1271;