lift some of the rspace handling into the Resolution* classes
[scpubgit/DX.git] / lib / DX / Predicate / MemberAt.pm
1 package DX::Predicate::MemberAt;
2
3 use DX::Utils qw(:builders :dep_types);
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 = [
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           geometry_depends_on => $deps,
83           members => \@members
84         );
85
86       }
87
88       return rspace(
89         geometry_depends_on => [
90           [ CONTENTS_OF ,=> $dict, $key->string_value ],
91           [ TYPE_OF ,=> $value ],
92         ],
93         members => [
94           res(
95             actions => [ $value->action_for_set_value($cur_val) ],
96             veracity_depends_on => $deps,
97           ),
98         ]
99       );
100
101     }
102
103     if ($dict->can_add_member) {
104
105       my $deps = [
106         [ EXISTENCE_OF ,=> $dict, $key->string_value ],
107         [ TYPE_OF ,=> $value ],
108       ];
109
110       if ($value->is_set) {
111
112         # If we get here, it means (currently) that we entered recheck
113         # due to the deletion of the key from the dict and should fail
114         # (or there's a bug in the compiler but let's hope not)
115         return rspace(
116           geometry_depends_on => $deps,
117           members => [],
118         );
119       }
120
121       my @path = (@{$dict->value_path}, $key->string_value);
122       my $ab = DX::ActionBuilder::ProxySetToAdd->new(
123         target_path => \@path,
124         proxy_to => $dict->action_builder,
125       );
126
127       return rspace(
128         geometry_depends_on => $deps,
129         members => [
130           res(
131             actions => [
132               $value->action_for_set_value(
133                 $value->but(action_builder => $ab),
134               ),
135             ],
136             # Veracity only depends on EXISTENCE_OF at this stage - if the
137             # $value is later set, recheck will lead us down a different path
138             # that will update those dependencies to include CONTENTS_OF
139             veracity_depends_on => $deps,
140           ),
141         ],
142       );
143
144     }
145
146     # Dict doesn't allow adding keys and key doesn't exist, so
147     # the contents of the value is completely irrelevant to the failure
148     return rspace(
149       geometry_depends_on => [
150         [ EXISTENCE_OF ,=> $dict, $key->string_value ],
151       ],
152       members => [],
153     );
154
155   }
156
157   die "Fucked" if $value->is_set; # +D -K +V ? seriously ?
158
159   # Laaater we may need to look at autovivifying an additional key/index
160   # ala ProxySetToAdd but I'm not 100% sure how that will make sense and
161   # premature generalisation is the root of all eval.
162
163   my @cand = map [
164     [ $_ ],
165     [ $dict->get_member_at($_) ],
166   ], map string($_), $dict->index_list;
167
168   return rspace(
169     geometry_depends_on => [
170       [ INDICES_OF ,=> $dict ],
171       [ TYPE_OF ,=> $key ],
172       [ TYPE_OF ,=> $value ],
173     ],
174     members => [
175       rstrat(
176         action_prototypes => [
177           [ $key => 'set_value' ],
178           [ $value => 'set_value' ],
179         ],
180         veracity_depends_on_builder => sub {
181           my ($this_key, $this_val) = map @$_, @_;
182           return [
183             [ CONTENTS_OF ,=> $dict, $this_key->string_value ],
184             [ CONTENTS_OF ,=> $key ],
185             [ CONTENTS_OF ,=> $value ],
186           ];
187         },
188         implementation_candidates => \@cand,
189       ),
190     ],
191   );
192 }
193
194 1;