Skip to content

Commit 98b2611

Browse files
committed
Special case when using an optional map / composite in an if statement
1 parent 39a5108 commit 98b2611

File tree

11 files changed

+159
-60
lines changed

11 files changed

+159
-60
lines changed

anathema-runtime/src/runtime/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,10 @@ impl<'rt, 'bp, G: GlobalEventHandler> Frame<'rt, 'bp, G> {
442442
let Some(remote_state) = self.layout_ctx.states.get(event.state) else { return };
443443
let Some(remote_state) = remote_state.shared_state() else { return };
444444
self.with_component(widget_id, state_id, |comp, children, ctx| {
445+
// TODO:
446+
// Create a new event type
447+
// if the event is stopped then return from drain_assoc_events
448+
445449
let event_ident = self.document.strings.get_ref_unchecked(event.external);
446450
comp.dyn_component
447451
.any_receive(children, ctx, event_ident, &*remote_state);

anathema-state-derive/src/structs.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,10 @@ fn generate_list(name: &Ident, len: usize) -> proc_macro::TokenStream {
9090
fn generate_composite(name: &Ident, fields: Vec<data::Field>) -> proc_macro::TokenStream {
9191
let field_names = fields
9292
.iter()
93-
.map(|data::Field { display_name, .. }| quote::quote! { #display_name });
93+
.map(|data::Field { display_name, .. }| quote::quote! { #display_name })
94+
.collect::<Vec<_>>();
95+
96+
let is_empty = field_names.is_empty();
9497

9598
let field_idents = fields
9699
.iter()
@@ -131,6 +134,10 @@ fn generate_composite(name: &Ident, fields: Vec<data::Field>) -> proc_macro::Tok
131134
_ => None,
132135
}
133136
}
137+
138+
fn is_empty(&self) -> bool {
139+
#is_empty
140+
}
134141
}
135142
}
136143
.into()

anathema-state/src/states.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,12 +268,18 @@ impl AnyState for Box<dyn AnyState> {
268268

269269
pub trait AnyMap {
270270
fn lookup(&self, key: &str) -> Option<PendingValue>;
271+
272+
fn is_empty(&self) -> bool;
271273
}
272274

273275
pub trait AnyList {
274276
fn lookup(&self, index: usize) -> Option<PendingValue>;
275277

276278
fn len(&self) -> usize;
279+
280+
fn is_empty(&self) -> bool {
281+
self.len() == 0
282+
}
277283
}
278284

279285
// -----------------------------------------------------------------------------

anathema-state/src/value/list.rs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use super::{Shared, Type, Unique, Value};
77
use crate::states::{AnyList, AnyState};
88
use crate::store::changed;
99
use crate::store::values::{get_unique, try_make_shared};
10-
use crate::{Change, PendingValue};
10+
use crate::{Change, PendingValue, State};
1111

1212
#[derive(Debug)]
1313
pub struct List<T> {
@@ -206,19 +206,11 @@ impl<T: AnyState + 'static> AnyList for List<T> {
206206
}
207207
}
208208

209-
impl<T: AnyState + 'static> AnyState for List<T> {
209+
impl<T: AnyState + 'static> State for List<T> {
210210
fn type_info(&self) -> Type {
211211
Type::List
212212
}
213213

214-
fn to_any_ref(&self) -> &dyn std::any::Any {
215-
self
216-
}
217-
218-
fn to_any_mut(&mut self) -> &mut dyn std::any::Any {
219-
self
220-
}
221-
222214
fn as_any_list(&self) -> Option<&dyn AnyList> {
223215
Some(self)
224216
}
@@ -250,8 +242,8 @@ where
250242
mod test {
251243

252244
use super::*;
253-
use crate::Subscriber;
254245
use crate::store::testing::drain_changes;
246+
use crate::Subscriber;
255247

256248
#[test]
257249
fn insert() {

anathema-state/src/value/map.rs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::collections::HashMap;
22

33
use super::{Shared, Type, Unique, Value};
4-
use crate::PendingValue;
4+
use crate::{PendingValue, State};
55
use crate::states::{AnyMap, AnyState};
66
use crate::store::values::{get_unique, try_make_shared};
77

@@ -89,26 +89,23 @@ impl<T: AnyState + 'static> AnyMap for Map<T> {
8989
fn lookup(&self, key: &str) -> Option<PendingValue> {
9090
self.get(key).map(|val| val.reference())
9191
}
92+
93+
fn is_empty(&self) -> bool {
94+
self.is_empty()
95+
}
9296
}
9397

94-
impl<T: AnyState + 'static> AnyState for Map<T> {
98+
impl<T: AnyState + 'static> State for Map<T> {
9599
fn type_info(&self) -> Type {
96100
Type::Map
97101
}
98102

99-
fn to_any_ref(&self) -> &dyn std::any::Any {
100-
self
101-
}
102-
103-
fn to_any_mut(&mut self) -> &mut dyn std::any::Any {
104-
self
105-
}
106-
107103
fn as_any_map(&self) -> Option<&dyn AnyMap> {
108104
Some(self)
109105
}
110106
}
111107

108+
112109
#[cfg(test)]
113110
mod test {
114111
use super::*;
@@ -150,7 +147,7 @@ mod test {
150147
fn remove() {
151148
let mut map = Map::empty();
152149
map.insert("a", DM(1));
153-
let value_ref = map.get("a").as_ref().unwrap();
150+
let _ = map.get("a").as_ref().unwrap();
154151

155152
assert!(map.remove("a").is_some());
156153
assert!(map.is_empty());

anathema-value-resolver/src/expression.rs

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ use std::collections::HashMap;
33

44
use anathema_state::{Color, Hex, PendingValue, SubTo, Subscriber, Type};
55
use anathema_store::slab::Key;
6-
use anathema_templates::Primitive;
76
use anathema_templates::expressions::{Equality, LogicalOp, Op};
7+
use anathema_templates::Primitive;
88

9-
use crate::AttributeStorage;
109
use crate::value::ValueKind;
10+
use crate::AttributeStorage;
1111

1212
macro_rules! or_null {
1313
($val:expr) => {
@@ -18,13 +18,13 @@ macro_rules! or_null {
1818
};
1919
}
2020

21-
pub struct ValueThingy<'a, 'bp> {
22-
sub: Subscriber,
23-
sub_to: &'a mut SubTo,
21+
pub struct ValueResolutionContext<'a, 'bp> {
22+
pub(crate) sub: Subscriber,
23+
pub(crate) sub_to: &'a mut SubTo,
2424
attribute_storage: &'a AttributeStorage<'bp>,
2525
}
2626

27-
impl<'a, 'bp> ValueThingy<'a, 'bp> {
27+
impl<'a, 'bp> ValueResolutionContext<'a, 'bp> {
2828
pub fn new(attribute_storage: &'a AttributeStorage<'bp>, sub: Subscriber, sub_to: &'a mut SubTo) -> Self {
2929
Self {
3030
attribute_storage,
@@ -127,7 +127,7 @@ impl<'bp> From<PendingValue> for ValueExpr<'bp> {
127127
}
128128

129129
// Resolve an expression to a value kind, this is the final value in the chain
130-
pub(crate) fn resolve_value<'a, 'bp>(value_expr: &ValueExpr<'bp>, ctx: &mut ValueThingy<'a, 'bp>) -> ValueKind<'bp> {
130+
pub(crate) fn resolve_value<'a, 'bp>(value_expr: &ValueExpr<'bp>, ctx: &mut ValueResolutionContext<'a, 'bp>) -> ValueKind<'bp> {
131131
match value_expr {
132132
// -----------------------------------------------------------------------------
133133
// - Primitives -
@@ -255,7 +255,8 @@ pub(crate) fn resolve_value<'a, 'bp>(value_expr: &ValueExpr<'bp>, ctx: &mut Valu
255255
// -----------------------------------------------------------------------------
256256
// - Maps and lists -
257257
// -----------------------------------------------------------------------------
258-
ValueExpr::DynMap(_) | ValueExpr::Map(_) => ValueKind::Map,
258+
ValueExpr::Map(_) => ValueKind::Map,
259+
ValueExpr::DynMap(map) => ValueKind::DynMap(*map),
259260
ValueExpr::Attributes(_) => ValueKind::Attributes,
260261
ValueExpr::DynList(value) => {
261262
value.subscribe(ctx.sub);
@@ -270,7 +271,7 @@ pub(crate) fn resolve_value<'a, 'bp>(value_expr: &ValueExpr<'bp>, ctx: &mut Valu
270271
let expr = resolve_index(src, index, ctx);
271272
resolve_value(&expr, ctx)
272273
}
273-
ValueExpr::Composite(_) => ValueKind::Composite,
274+
ValueExpr::Composite(comp) => ValueKind::Composite(*comp),
274275

275276
// -----------------------------------------------------------------------------
276277
// - Call -
@@ -299,7 +300,7 @@ fn resolve_pending(val: PendingValue) -> ValueExpr<'static> {
299300
}
300301
}
301302

302-
fn resolve_index<'bp>(src: &ValueExpr<'bp>, index: &ValueExpr<'bp>, ctx: &mut ValueThingy<'_, 'bp>) -> ValueExpr<'bp> {
303+
fn resolve_index<'bp>(src: &ValueExpr<'bp>, index: &ValueExpr<'bp>, ctx: &mut ValueResolutionContext<'_, 'bp>) -> ValueExpr<'bp> {
303304
match src {
304305
ValueExpr::DynMap(value) | ValueExpr::Composite(value) => {
305306
let s = or_null!(value.as_state());
@@ -371,11 +372,11 @@ fn resolve_index<'bp>(src: &ValueExpr<'bp>, index: &ValueExpr<'bp>, ctx: &mut Va
371372
resolve_index(&src, index, ctx)
372373
}
373374
ValueExpr::Null => ValueExpr::Null,
374-
val => unreachable!("resolving index: this should return null eventually: {val:?}"),
375+
val => unreachable!("resolving index: this should return null eventually: {val:?} (you probably did something like x.y on a string)"),
375376
}
376377
}
377378

378-
fn resolve_expr<'a, 'bp>(expr: &'a ValueExpr<'bp>, ctx: &mut ValueThingy<'_, 'bp>) -> Option<ValueExpr<'bp>> {
379+
fn resolve_expr<'a, 'bp>(expr: &'a ValueExpr<'bp>, ctx: &mut ValueResolutionContext<'_, 'bp>) -> Option<ValueExpr<'bp>> {
379380
match expr {
380381
ValueExpr::Either(first, second) => match resolve_expr(first, ctx) {
381382
None | Some(ValueExpr::Null) => resolve_expr(second, ctx),
@@ -386,7 +387,7 @@ fn resolve_expr<'a, 'bp>(expr: &'a ValueExpr<'bp>, ctx: &mut ValueThingy<'_, 'bp
386387
}
387388
}
388389

389-
fn resolve_str<'a, 'bp>(index: &'a ValueExpr<'bp>, ctx: &mut ValueThingy<'_, 'bp>) -> Option<Kind<&'bp str>> {
390+
fn resolve_str<'a, 'bp>(index: &'a ValueExpr<'bp>, ctx: &mut ValueResolutionContext<'_, 'bp>) -> Option<Kind<&'bp str>> {
390391
match index {
391392
ValueExpr::Str(kind) => Some(*kind),
392393
ValueExpr::Index(src, index) => match resolve_index(src, index, ctx) {
@@ -400,7 +401,7 @@ fn resolve_str<'a, 'bp>(index: &'a ValueExpr<'bp>, ctx: &mut ValueThingy<'_, 'bp
400401
}
401402
}
402403

403-
fn resolve_int<'a, 'bp>(index: &'a ValueExpr<'bp>, ctx: &mut ValueThingy<'_, 'bp>) -> i64 {
404+
fn resolve_int<'a, 'bp>(index: &'a ValueExpr<'bp>, ctx: &mut ValueResolutionContext<'_, 'bp>) -> i64 {
404405
let value = resolve_value(index, ctx);
405406
match value {
406407
ValueKind::Int(index) => index,
@@ -411,9 +412,10 @@ fn resolve_int<'a, 'bp>(index: &'a ValueExpr<'bp>, ctx: &mut ValueThingy<'_, 'bp
411412
| ValueKind::Hex(_)
412413
| ValueKind::Color(_)
413414
| ValueKind::Str(_)
414-
| ValueKind::Composite
415+
| ValueKind::Composite(_)
415416
| ValueKind::Null
416417
| ValueKind::Map
418+
| ValueKind::DynMap(_)
417419
| ValueKind::Attributes
418420
| ValueKind::List(_)
419421
| ValueKind::DynList(_) => todo!("resolving int: the value is {value:?}"),
@@ -442,7 +444,7 @@ fn float_op(lhs: f64, rhs: f64, op: Op) -> f64 {
442444

443445
#[cfg(test)]
444446
mod test {
445-
use anathema_state::{Changes, States, drain_changes};
447+
use anathema_state::{drain_changes, Changes, Map, States};
446448
use anathema_templates::expressions::{ident, index, num, strlit};
447449

448450
use crate::testing::setup;
@@ -500,14 +502,34 @@ mod test {
500502

501503
drain_changes(&mut changes);
502504
for (subs, _) in changes.drain() {
503-
for sub in subs.iter() {
504-
if sub == value.sub {
505-
value.reload(&test.attributes);
506-
}
505+
if subs.iter().any(|sub| sub == value.sub) {
506+
value.reload(&test.attributes);
507507
}
508508
}
509-
510509
assert_eq!(value.as_str().unwrap(), "c");
511510
});
512511
}
512+
513+
#[test]
514+
fn optional_map_from_empty_to_value() {
515+
let mut changes = Changes::empty();
516+
517+
let mut states = States::new();
518+
setup(&mut states, Default::default(), |test| {
519+
// let expr = index(index(ident("state"), strlit("opt_map")), strlit("key"));
520+
let expr = index(ident("state"), strlit("opt_map"));
521+
522+
let mut value = test.eval(&expr);
523+
assert!(value.as_str().is_none());
524+
525+
test.with_state(|state| {
526+
let mut map = Map::empty();
527+
map.insert("key", 123);
528+
state.opt_map.set(Some(map));
529+
});
530+
531+
drain_changes(&mut changes);
532+
assert!(!changes.is_empty());
533+
});
534+
}
513535
}

anathema-value-resolver/src/testing.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub(crate) struct TestState {
1414
pub(crate) float: Value<f64>,
1515
pub(crate) list: Value<List<&'static str>>,
1616
pub(crate) map: Value<Map<i32>>,
17+
pub(crate) opt_map: Value<Option<Map<i32>>>,
1718
}
1819

1920
impl TestState {
@@ -25,6 +26,7 @@ impl TestState {
2526
float: 0.0.into(),
2627
list: List::empty().into(),
2728
map: Map::empty().into(),
29+
opt_map: Value::new(None),
2830
}
2931
}
3032
}
@@ -48,9 +50,14 @@ impl AnyMap for TestState {
4850
"float" => self.float.reference().into(),
4951
"string" => self.string.reference().into(),
5052
"map" => self.map.reference().into(),
53+
"opt_map" => self.opt_map.reference().into(),
5154
_ => None,
5255
}
5356
}
57+
58+
fn is_empty(&self) -> bool {
59+
false
60+
}
5461
}
5562

5663
pub(crate) struct TestCase<'a, 'bp> {
@@ -74,7 +81,6 @@ impl<'a, 'bp> TestCase<'a, 'bp> {
7481
pub(crate) fn eval(&self, expr: &'bp Expression) -> crate::value::Value<'bp> {
7582
let state_id = StateId::ZERO;
7683
let scope = Scope::with_component(state_id, Key::ZERO, None);
77-
7884
let ctx = ResolverCtx::new(&self.globals, &scope, &self.states, &self.attributes);
7985
resolve(expr, &ctx, Subscriber::ZERO)
8086
}

0 commit comments

Comments
 (0)