move dep expansion into predicates for the moment
[scpubgit/DX.git] / lib / DX / Predicate / MemberAt.pm
1 package DX::Predicate::MemberAt;
2
3 use DX::Utils qw(:builders :dep_types expand_deps);
4 use DX::ActionBuilder::ProxySetToAdd;
5 use DX::ActionBuilder::Null;
6 use DX::Class;
7
8 with 'DX::Role::Predicate';
9
10 # member_at Dict Key Value
11 #
12 # Dict must be set to a dict (later maybe also an array and Key -> Index)
13 #
14 # Key bound ->
15 #
16 #   Key exists ->
17 #
18 #     Value bound ->
19 #
20 #       Dict.Key = Value ->
21
22 #         Trivial resolution
23 #
24 #       Dict.Key != Value ->
25 #
26 #         Failure
27 #
28 #     Value unbound ->
29 #
30 #       Set value to Dict.Key
31 #
32 #   Key does not exist ->
33 #
34 #     Dict allows add ->
35 #
36 #       Value bound ->
37 #
38 #         Failure on (exists Dict.Key, Value)
39 #
40 #       Value unbound ->
41 #
42 #         Set value to ProxySetToAdd value
43 #
44 #     Dict disallows add ->
45 #
46 #       Failure on (exists Dict.Key)
47 #
48 # Key unbound ->
49 #
50 #   Value must also be unbound, because seriously?
51 #
52 #   Set [Key, Value] to each pair in turn
53
54 sub _resolution_space_for {
55   my ($self, $dict, $key, $value) = @_;
56
57   die "Fucked" unless $dict->does('DX::Role::StructuredValue');
58
59   if ($key->is_set) {
60
61     if (my $cur_val = $dict->get_member_at($key)) {
62
63       my $deps = expand_deps([
64         [ CONTENTS_OF ,=> $dict, $key->string_value ],
65         [ CONTENTS_OF ,=> $value ],
66       ]);
67
68       if ($value->is_set) {
69
70         my @members = (
71           $cur_val->equals($value)
72             # Trivial resolution, D.K = V
73             ? res(
74                 actions => [],
75                 veracity_depends_on => $deps,
76               )
77             # Failure
78             : ()
79         );
80
81         return rspace(
82           aperture => [],
83           geometry_depends_on => $deps,
84           members => \@members
85         );
86
87       }
88
89       return rspace(
90         geometry_depends_on => expand_deps([
91           [ CONTENTS_OF ,=> $dict, $key->string_value ],
92           [ TYPE_OF ,=> $value ],
93         ]),
94         aperture => $value->aperture_for_set_value,
95         members => [
96           res(
97             actions => [ $value->action_for_set_value($cur_val) ],
98             veracity_depends_on => $deps,
99           ),
100         ]
101       );
102
103     }
104
105     if ($dict->can_add_member) {
106
107       my $deps = expand_deps([
108         [ EXISTENCE_OF ,=> $dict, $key->string_value ],
109         [ TYPE_OF ,=> $value ],
110       ]);
111
112       if ($value->is_set) {
113
114         # If we get here, it means (currently) that we entered recheck
115         # due to the deletion of the key from the dict and should fail
116         # (or there's a bug in the compiler but let's hope not)
117         return rspace(
118           geometry_depends_on => $deps,
119           members => [],
120         );
121       }
122
123       my @path = (@{$dict->value_path}, $key->string_value);
124       my $ab = DX::ActionBuilder::ProxySetToAdd->new(
125         target_path => \@path,
126         proxy_to => $dict->action_builder,
127       );
128
129       return rspace(
130         geometry_depends_on => $deps,
131         aperture => $value->aperture_for_set_value,
132         members => [
133           res(
134             actions => [
135               $value->action_for_set_value(
136                 $value->but(action_builder => $ab),
137               ),
138             ],
139             # Veracity only depends on EXISTENCE_OF at this stage - if the
140             # $value is later set, recheck will lead us down a different path
141             # that will update those dependencies to include CONTENTS_OF
142             veracity_depends_on => $deps,
143           ),
144         ],
145       );
146
147     }
148
149     # Dict doesn't allow adding keys and key doesn't exist, so
150     # the contents of the value is completely irrelevant to the failure
151     return rspace(
152       geometry_depends_on => expand_deps([
153         [ EXISTENCE_OF ,=> $dict, $key->string_value ],
154       ]),
155       aperture => [],
156       members => [],
157     );
158
159   }
160
161   die "Fucked" if $value->is_set; # +D -K +V ? seriously ?
162
163   # Laaater we may need to look at autovivifying an additional key/index
164   # ala ProxySetToAdd but I'm not 100% sure how that will make sense and
165   # premature generalisation is the root of all eval.
166
167   my @cand = map [
168     [ $_ ],
169     [ $dict->get_member_at($_) ],
170   ], map string($_), $dict->index_list;
171
172   return rspace(
173     geometry_depends_on => expand_deps([
174       [ INDICES_OF ,=> $dict ],
175       [ TYPE_OF ,=> $key ],
176       [ TYPE_OF ,=> $value ],
177     ]),
178     aperture => [ map @{$_->aperture_for_set_value}, $key, $value ],
179     members => [
180       rstrat(
181         action_prototypes => [
182           [ $key => 'set_value' ],
183           [ $value => 'set_value' ],
184         ],
185         veracity_depends_on_builder => sub {
186           my ($this_key, $this_val) = map @$_, @_;
187           return expand_deps([
188             [ CONTENTS_OF ,=> $dict, $this_key->string_value ],
189             [ CONTENTS_OF ,=> $key ],
190             [ CONTENTS_OF ,=> $value ],
191           ]);
192         },
193         implementation_candidates => \@cand,
194       ),
195     ],
196   );
197 }
198
199 1;