diff --git a/dsl/src/dsl_provide.rs b/dsl/src/dsl_provide.rs index cbd99ff..c2f411f 100644 --- a/dsl/src/dsl_provide.rs +++ b/dsl/src/dsl_provide.rs @@ -1,34 +1,34 @@ use crate::*; pub trait Dsl { - fn take <'source> (&self, token: &mut TokenIter<'source>) -> Perhaps; - fn take_or_fail <'source> ( - &self, token: &mut TokenIter<'source>, error: impl Into> + fn take <'source> (&self, words: &mut TokenIter<'source>) -> Perhaps; + fn take_or_fail <'source, E: Into>, F: Fn()->E> ( + &self, words: &mut TokenIter<'source>, error: F ) -> Usually { - if let Some(value) = Dsl::::take(self, token)? { + if let Some(value) = Dsl::::take(self, words)? { Ok(value) } else { - Result::Err(error.into()) + Result::Err(format!("{}: {:?}", error().into(), words.peek().map(|x|x.value)).into()) } } } impl, State> Dsl for State { - fn take <'source> (&self, token: &mut TokenIter<'source>) -> Perhaps { - Namespace::take_from(self, token) + fn take <'source> (&self, words: &mut TokenIter<'source>) -> Perhaps { + Namespace::take_from(self, words) } } pub trait Namespace: Sized { - fn take_from <'source> (state: &State, token: &mut TokenIter<'source>) + fn take_from <'source> (state: &State, words: &mut TokenIter<'source>) -> Perhaps; - fn take_from_or_fail <'source> ( - state: &State, token: &mut TokenIter<'source>, error: impl Into> + fn take_from_or_fail <'source, E: Into>, F: Fn()->E> ( + state: &State, words: &mut TokenIter<'source>, error: F ) -> Usually { - if let Some(value) = Namespace::::take_from(state, token)? { + if let Some(value) = Namespace::::take_from(state, words)? { Ok(value) } else { - Result::Err(error.into()) + Result::Err(format!("{}: {:?}", error().into(), words.peek().map(|x|x.value)).into()) } } } diff --git a/output/src/lib.rs b/output/src/lib.rs index 2b12b04..394f332 100644 --- a/output/src/lib.rs +++ b/output/src/lib.rs @@ -1,190 +1,10 @@ #![feature(step_trait)] #![feature(type_alias_impl_trait)] #![feature(impl_trait_in_assoc_type)] -//#![feature(impl_trait_in_fn_trait_return)] - +pub(crate) use tengri_core::*; +pub(crate) use std::marker::PhantomData; +#[cfg(feature = "dsl")] pub(crate) use ::tengri_dsl::*; mod space; pub use self::space::*; mod ops; pub use self::ops::*; mod output; pub use self::output::*; - -pub(crate) use tengri_core::*; -pub(crate) use std::marker::PhantomData; - -#[cfg(feature = "dsl")] pub(crate) use ::tengri_dsl::*; -#[cfg(feature = "dsl")] mod view; -#[cfg(feature = "dsl")] pub use self::view::*; - -#[cfg(test)] use proptest_derive::Arbitrary; - -#[cfg(test)] #[test] fn test_stub_output () -> Usually<()> { - use crate::*; - struct TestOutput([u16;4]); - impl Output for TestOutput { - type Unit = u16; - type Size = [u16;2]; - type Area = [u16;4]; - fn area (&self) -> [u16;4] { - self.0 - } - fn area_mut (&mut self) -> &mut [u16;4] { - &mut self.0 - } - fn place (&mut self, _: [u16;4], _: &impl Render) { - () - } - } - impl Content for String { - fn render (&self, to: &mut TestOutput) { - to.area_mut().set_w(self.len() as u16); - } - } - Ok(()) -} - -#[cfg(test)] #[test] fn test_space () { - use crate::*; - assert_eq!(Area::center(&[10u16, 10, 20, 20]), [20, 20]); -} - -#[cfg(test)] #[test] fn test_iter_map () { - struct Foo; - impl Content for Foo {} - fn make_map + Send + Sync> (data: &Vec) -> impl Content { - Map::new(||data.iter(), |foo, index|{}) - } - let data = vec![Foo, Foo, Foo]; - //let map = make_map(&data); -} - -#[cfg(test)] mod test { - use crate::*; - use proptest::{prelude::*, option::of}; - - proptest! { - #[test] fn proptest_direction ( - d in prop_oneof![ - Just(North), Just(South), - Just(East), Just(West), - Just(Above), Just(Below) - ], - x in u16::MIN..u16::MAX, - y in u16::MIN..u16::MAX, - w in u16::MIN..u16::MAX, - h in u16::MIN..u16::MAX, - a in u16::MIN..u16::MAX, - ) { - let _ = d.split_fixed([x, y, w, h], a); - } - } - - proptest! { - #[test] fn proptest_size ( - x in u16::MIN..u16::MAX, - y in u16::MIN..u16::MAX, - a in u16::MIN..u16::MAX, - b in u16::MIN..u16::MAX, - ) { - let size = [x, y]; - let _ = size.w(); - let _ = size.h(); - let _ = size.wh(); - let _ = size.clip_w(a); - let _ = size.clip_h(b); - let _ = size.expect_min(a, b); - let _ = size.to_area_pos(); - let _ = size.to_area_size(); - } - } - - proptest! { - #[test] fn proptest_area ( - x in u16::MIN..u16::MAX, - y in u16::MIN..u16::MAX, - w in u16::MIN..u16::MAX, - h in u16::MIN..u16::MAX, - a in u16::MIN..u16::MAX, - b in u16::MIN..u16::MAX, - ) { - let _: [u16;4] = <[u16;4] as Area>::zero(); - let _: [u16;4] = <[u16;4] as Area>::from_position([a, b]); - let _: [u16;4] = <[u16;4] as Area>::from_size([a, b]); - let area: [u16;4] = [x, y, w, h]; - let _ = area.expect_min(a, b); - let _ = area.xy(); - let _ = area.wh(); - let _ = area.xywh(); - let _ = area.clip_h(a); - let _ = area.clip_w(b); - let _ = area.clip([a, b]); - let _ = area.set_w(a); - let _ = area.set_h(b); - let _ = area.x2(); - let _ = area.y2(); - let _ = area.lrtb(); - let _ = area.center(); - let _ = area.center_x(a); - let _ = area.center_y(b); - let _ = area.center_xy([a, b]); - let _ = area.centered(); - } - } - - macro_rules! test_op_transform { - ($fn:ident, $Op:ident) => { - proptest! { - #[test] fn $fn ( - op_x in of(u16::MIN..u16::MAX), - op_y in of(u16::MIN..u16::MAX), - content in "\\PC*", - x in u16::MIN..u16::MAX, - y in u16::MIN..u16::MAX, - w in u16::MIN..u16::MAX, - h in u16::MIN..u16::MAX, - ) { - if let Some(op) = match (op_x, op_y) { - (Some(x), Some(y)) => Some($Op::xy(x, y, content)), - (Some(x), None) => Some($Op::x(x, content)), - (Some(y), None) => Some($Op::y(y, content)), - _ => None - } { - assert_eq!(Content::layout(&op, [x, y, w, h]), - Render::layout(&op, [x, y, w, h])); - } - } - } - } - } - - test_op_transform!(proptest_op_fixed, Fixed); - test_op_transform!(proptest_op_min, Min); - test_op_transform!(proptest_op_max, Max); - test_op_transform!(proptest_op_push, Push); - test_op_transform!(proptest_op_pull, Pull); - test_op_transform!(proptest_op_shrink, Shrink); - test_op_transform!(proptest_op_expand, Expand); - test_op_transform!(proptest_op_margin, Margin); - test_op_transform!(proptest_op_padding, Padding); - - proptest! { - #[test] fn proptest_op_bsp ( - d in prop_oneof![ - Just(North), Just(South), - Just(East), Just(West), - Just(Above), Just(Below) - ], - a in "\\PC*", - b in "\\PC*", - x in u16::MIN..u16::MAX, - y in u16::MIN..u16::MAX, - w in u16::MIN..u16::MAX, - h in u16::MIN..u16::MAX, - ) { - let bsp = Bsp(d, a, b); - assert_eq!( - Content::layout(&bsp, [x, y, w, h]), - Render::layout(&bsp, [x, y, w, h]), - ); - } - } - -} +#[cfg(test)] mod test; diff --git a/output/src/ops.rs b/output/src/ops.rs index 7eba655..7b8010e 100644 --- a/output/src/ops.rs +++ b/output/src/ops.rs @@ -1,9 +1,10 @@ +//mod reduce; pub use self::reduce::*; mod align; pub use self::align::*; mod bsp; pub use self::bsp::*; -mod cond; pub use self::cond::*; +mod either; pub use self::either::*; mod map; pub use self::map::*; mod memo; pub use self::memo::*; mod stack; pub use self::stack::*; -//mod reduce; pub use self::reduce::*; mod thunk; pub use self::thunk::*; mod transform; pub use self::transform::*; +mod when; pub use self::when::*; diff --git a/output/src/ops/align.rs b/output/src/ops/align.rs index 5702a40..0809a10 100644 --- a/output/src/ops/align.rs +++ b/output/src/ops/align.rs @@ -42,7 +42,7 @@ from_dsl!(@a: Align: |state, iter|if let Some(Token { value: Value::Key(key), "align/n"|"align/s"|"align/e"|"align/w"| "align/nw"|"align/sw"|"align/ne"|"align/se" => { let _ = iter.next().unwrap(); - let content = state.take_or_fail(&mut iter.clone(), "expected content")?; + let content = state.take_or_fail(&mut iter.clone(), ||"expected content")?; return Ok(Some(match key { "align/c" => Self::c(content), "align/x" => Self::x(content), diff --git a/output/src/ops/bsp.rs b/output/src/ops/bsp.rs index 6062485..41fd5e6 100644 --- a/output/src/ops/bsp.rs +++ b/output/src/ops/bsp.rs @@ -21,35 +21,29 @@ impl, B: Content> Content for Bsp { } } #[cfg(feature = "dsl")] -from_dsl!(@ab: Bsp: |state, iter|Ok(if let Some(Token { - value: Value::Key("bsp/n"|"bsp/s"|"bsp/e"|"bsp/w"|"bsp/a"|"bsp/b"), - .. -}) = iter.peek() { - let base = iter.clone(); - return Ok(Some(match iter.next() { - Some(Token { value: Value::Key("bsp/n"), .. }) => - Self::n(state.take_or_fail(iter, "expected content 1")?, - state.take_or_fail(iter, "expected content 2")?), - Some(Token { value: Value::Key("bsp/s"), .. }) => - Self::s(state.take_or_fail(iter, "expected content 1")?, - state.take_or_fail(iter, "expected content 2")?), - Some(Token { value: Value::Key("bsp/e"), .. }) => - Self::e(state.take_or_fail(iter, "expected content 1")?, - state.take_or_fail(iter, "expected content 2")?), - Some(Token { value: Value::Key("bsp/w"), .. }) => - Self::w(state.take_or_fail(iter, "expected content 1")?, - state.take_or_fail(iter, "expected content 2")?), - Some(Token { value: Value::Key("bsp/a"), .. }) => - Self::a(state.take_or_fail(iter, "expected content 1")?, - state.take_or_fail(iter, "expected content 2")?), - Some(Token { value: Value::Key("bsp/b"), .. }) => - Self::b(state.take_or_fail(iter, "expected content 1")?, - state.take_or_fail(iter, "expected content 2")?), - _ => unreachable!(), - })) -} else { - None -})); +impl + Dsl + std::fmt::Debug, A, B> Namespace for Bsp { + fn take_from <'source> (state: &State, words: &mut TokenIter<'source>) -> Perhaps { + Ok(if let Some(Token { + value: Value::Key("bsp/n"|"bsp/s"|"bsp/e"|"bsp/w"|"bsp/a"|"bsp/b"), + .. + }) = words.peek() { + let base = words.clone(); + let a: A = state.take_or_fail(words, ||"expected content 1")?; + let b: B = state.take_or_fail(words, ||"expected content 2")?; + return Ok(Some(match words.next() { + Some(Token { value: Value::Key("bsp/n"), .. }) => Self::n(a, b), + Some(Token { value: Value::Key("bsp/s"), .. }) => Self::s(a, b), + Some(Token { value: Value::Key("bsp/e"), .. }) => Self::e(a, b), + Some(Token { value: Value::Key("bsp/w"), .. }) => Self::w(a, b), + Some(Token { value: Value::Key("bsp/a"), .. }) => Self::a(a, b), + Some(Token { value: Value::Key("bsp/b"), .. }) => Self::b(a, b), + _ => unreachable!(), + })) + } else { + None + }) + } +} impl Bsp { #[inline] pub const fn n (a: A, b: B) -> Self { Self(North, a, b) } #[inline] pub const fn s (a: A, b: B) -> Self { Self(South, a, b) } diff --git a/output/src/ops/cond.rs b/output/src/ops/cond.rs deleted file mode 100644 index be950fe..0000000 --- a/output/src/ops/cond.rs +++ /dev/null @@ -1,85 +0,0 @@ -use crate::*; - -/// Show an item only when a condition is true. -pub struct When(pub bool, pub A); -impl When { - /// Create a binary condition. - pub const fn new (c: bool, a: A) -> Self { - Self(c, a) - } -} - -/// Show one item if a condition is true and another if the condition is false -pub struct Either(pub bool, pub A, pub B); -impl Either { - /// Create a ternary condition. - pub const fn new (c: bool, a: A, b: B) -> Self { - Self(c, a, b) - } -} -#[cfg(feature = "dsl")] -impl + Dsl> Namespace for When { - fn take_from <'source> ( - state: &T, - token: &mut TokenIter<'source> - ) -> Perhaps { - Ok(if let Some(Token { - value: Value::Key("when"), - .. - }) = token.peek() { - let base = token.clone(); - return Ok(Some(Self( - state.take_or_fail(token, "cond: no condition")?, - state.take_or_fail(token, "cond: no content")?, - ))) - } else { - None - }) - } -} -impl> Content for When { - fn layout (&self, to: E::Area) -> E::Area { - let Self(cond, item) = self; - let mut area = E::Area::zero(); - if *cond { - let item_area = item.layout(to); - area[0] = item_area.x(); - area[1] = item_area.y(); - area[2] = item_area.w(); - area[3] = item_area.h(); - } - area.into() - } - fn render (&self, to: &mut E) { - let Self(cond, item) = self; - if *cond { item.render(to) } - } -} -#[cfg(feature = "dsl")] -impl + Dsl + Dsl> Namespace for Either { - fn take_from <'source> ( - state: &T, - token: &mut TokenIter<'source> - ) -> Perhaps { - if let Some(Token { value: Value::Key("either"), .. }) = token.peek() { - let base = token.clone(); - let _ = token.next().unwrap(); - return Ok(Some(Self( - state.take_or_fail(token, "either: no condition")?, - state.take_or_fail(token, "either: no content 1")?, - state.take_or_fail(token, "either: no content 2")?, - ))) - } - Ok(None) - } -} -impl, B: Render> Content for Either { - fn layout (&self, to: E::Area) -> E::Area { - let Self(cond, a, b) = self; - if *cond { a.layout(to) } else { b.layout(to) } - } - fn render (&self, to: &mut E) { - let Self(cond, a, b) = self; - if *cond { a.render(to) } else { b.render(to) } - } -} diff --git a/output/src/ops/either.rs b/output/src/ops/either.rs new file mode 100644 index 0000000..b8e6ef9 --- /dev/null +++ b/output/src/ops/either.rs @@ -0,0 +1,38 @@ +use crate::*; + +/// Show one item if a condition is true and another if the condition is false +pub struct Either(pub bool, pub A, pub B); +impl Either { + /// Create a ternary condition. + pub const fn new (c: bool, a: A, b: B) -> Self { + Self(c, a, b) + } +} +#[cfg(feature = "dsl")] +impl + Dsl + Dsl> Namespace for Either { + fn take_from <'source> ( + state: &T, + token: &mut TokenIter<'source> + ) -> Perhaps { + if let Some(Token { value: Value::Key("either"), .. }) = token.peek() { + let base = token.clone(); + let _ = token.next().unwrap(); + return Ok(Some(Self( + state.take_or_fail(token, ||"either: no condition")?, + state.take_or_fail(token, ||"either: no content 1")?, + state.take_or_fail(token, ||"either: no content 2")?, + ))) + } + Ok(None) + } +} +impl, B: Render> Content for Either { + fn layout (&self, to: E::Area) -> E::Area { + let Self(cond, a, b) = self; + if *cond { a.layout(to) } else { b.layout(to) } + } + fn render (&self, to: &mut E) { + let Self(cond, a, b) = self; + if *cond { a.render(to) } else { b.render(to) } + } +} diff --git a/output/src/ops/transform.rs b/output/src/ops/transform.rs index 776d95c..9719c9a 100644 --- a/output/src/ops/transform.rs +++ b/output/src/ops/transform.rs @@ -41,11 +41,11 @@ macro_rules! transform_xy { let mut base = token.clone(); return Ok(Some(match token.next() { Some(Token{value:Value::Key($x),..}) => - Self::x(state.take_or_fail(token, "x: no content")?), + Self::x(state.take_or_fail(token, ||"x: no content")?), Some(Token{value:Value::Key($y),..}) => - Self::y(state.take_or_fail(token, "y: no content")?), + Self::y(state.take_or_fail(token, ||"y: no content")?), Some(Token{value:Value::Key($xy),..}) => - Self::xy(state.take_or_fail(token, "xy: no content")?), + Self::xy(state.take_or_fail(token, ||"xy: no content")?), _ => unreachable!() })) } @@ -87,17 +87,17 @@ macro_rules! transform_xy_unit { let mut base = token.clone(); Some(match token.next() { Some(Token { value: Value::Key($x), .. }) => Self::x( - state.take_or_fail(token, "x: no unit")?, - state.take_or_fail(token, "x: no content")?, + state.take_or_fail(token, ||"x: no unit")?, + state.take_or_fail(token, ||"x: no content")?, ), Some(Token { value: Value::Key($y), .. }) => Self::y( - state.take_or_fail(token, "y: no unit")?, - state.take_or_fail(token, "y: no content")?, + state.take_or_fail(token, ||"y: no unit")?, + state.take_or_fail(token, ||"y: no content")?, ), Some(Token { value: Value::Key($x), .. }) => Self::xy( - state.take_or_fail(token, "xy: no unit x")?, - state.take_or_fail(token, "xy: no unit y")?, - state.take_or_fail(token, "xy: no content")? + state.take_or_fail(token, ||"xy: no unit x")?, + state.take_or_fail(token, ||"xy: no unit y")?, + state.take_or_fail(token, ||"xy: no content")? ), _ => unreachable!(), }) diff --git a/output/src/ops/when.rs b/output/src/ops/when.rs new file mode 100644 index 0000000..35f1910 --- /dev/null +++ b/output/src/ops/when.rs @@ -0,0 +1,48 @@ +use crate::*; + +/// Show an item only when a condition is true. +pub struct When(pub bool, pub A); +impl When { + /// Create a binary condition. + pub const fn new (c: bool, a: A) -> Self { + Self(c, a) + } +} +#[cfg(feature = "dsl")] +impl + Dsl> Namespace for When { + fn take_from <'source> ( + state: &T, + token: &mut TokenIter<'source> + ) -> Perhaps { + Ok(if let Some(Token { + value: Value::Key("when"), + .. + }) = token.peek() { + let base = token.clone(); + return Ok(Some(Self( + state.take_or_fail(token, ||"cond: no condition")?, + state.take_or_fail(token, ||"cond: no content")?, + ))) + } else { + None + }) + } +} +impl> Content for When { + fn layout (&self, to: E::Area) -> E::Area { + let Self(cond, item) = self; + let mut area = E::Area::zero(); + if *cond { + let item_area = item.layout(to); + area[0] = item_area.x(); + area[1] = item_area.y(); + area[2] = item_area.w(); + area[3] = item_area.h(); + } + area.into() + } + fn render (&self, to: &mut E) { + let Self(cond, item) = self; + if *cond { item.render(to) } + } +} diff --git a/output/src/test.rs b/output/src/test.rs new file mode 100644 index 0000000..517aa64 --- /dev/null +++ b/output/src/test.rs @@ -0,0 +1,170 @@ +use crate::*; +use proptest_derive::Arbitrary; +use proptest::{prelude::*, option::of}; + +proptest! { + #[test] fn proptest_direction ( + d in prop_oneof![ + Just(North), Just(South), + Just(East), Just(West), + Just(Above), Just(Below) + ], + x in u16::MIN..u16::MAX, + y in u16::MIN..u16::MAX, + w in u16::MIN..u16::MAX, + h in u16::MIN..u16::MAX, + a in u16::MIN..u16::MAX, + ) { + let _ = d.split_fixed([x, y, w, h], a); + } +} + +proptest! { + #[test] fn proptest_size ( + x in u16::MIN..u16::MAX, + y in u16::MIN..u16::MAX, + a in u16::MIN..u16::MAX, + b in u16::MIN..u16::MAX, + ) { + let size = [x, y]; + let _ = size.w(); + let _ = size.h(); + let _ = size.wh(); + let _ = size.clip_w(a); + let _ = size.clip_h(b); + let _ = size.expect_min(a, b); + let _ = size.to_area_pos(); + let _ = size.to_area_size(); + } +} + +proptest! { + #[test] fn proptest_area ( + x in u16::MIN..u16::MAX, + y in u16::MIN..u16::MAX, + w in u16::MIN..u16::MAX, + h in u16::MIN..u16::MAX, + a in u16::MIN..u16::MAX, + b in u16::MIN..u16::MAX, + ) { + let _: [u16;4] = <[u16;4] as Area>::zero(); + let _: [u16;4] = <[u16;4] as Area>::from_position([a, b]); + let _: [u16;4] = <[u16;4] as Area>::from_size([a, b]); + let area: [u16;4] = [x, y, w, h]; + let _ = area.expect_min(a, b); + let _ = area.xy(); + let _ = area.wh(); + let _ = area.xywh(); + let _ = area.clip_h(a); + let _ = area.clip_w(b); + let _ = area.clip([a, b]); + let _ = area.set_w(a); + let _ = area.set_h(b); + let _ = area.x2(); + let _ = area.y2(); + let _ = area.lrtb(); + let _ = area.center(); + let _ = area.center_x(a); + let _ = area.center_y(b); + let _ = area.center_xy([a, b]); + let _ = area.centered(); + } +} + +macro_rules! test_op_transform { + ($fn:ident, $Op:ident) => { + proptest! { + #[test] fn $fn ( + op_x in of(u16::MIN..u16::MAX), + op_y in of(u16::MIN..u16::MAX), + content in "\\PC*", + x in u16::MIN..u16::MAX, + y in u16::MIN..u16::MAX, + w in u16::MIN..u16::MAX, + h in u16::MIN..u16::MAX, + ) { + if let Some(op) = match (op_x, op_y) { + (Some(x), Some(y)) => Some($Op::xy(x, y, content)), + (Some(x), None) => Some($Op::x(x, content)), + (Some(y), None) => Some($Op::y(y, content)), + _ => None + } { + assert_eq!(Content::layout(&op, [x, y, w, h]), + Render::layout(&op, [x, y, w, h])); + } + } + } + } +} + +test_op_transform!(proptest_op_fixed, Fixed); +test_op_transform!(proptest_op_min, Min); +test_op_transform!(proptest_op_max, Max); +test_op_transform!(proptest_op_push, Push); +test_op_transform!(proptest_op_pull, Pull); +test_op_transform!(proptest_op_shrink, Shrink); +test_op_transform!(proptest_op_expand, Expand); +test_op_transform!(proptest_op_margin, Margin); +test_op_transform!(proptest_op_padding, Padding); + +proptest! { + #[test] fn proptest_op_bsp ( + d in prop_oneof![ + Just(North), Just(South), + Just(East), Just(West), + Just(Above), Just(Below) + ], + a in "\\PC*", + b in "\\PC*", + x in u16::MIN..u16::MAX, + y in u16::MIN..u16::MAX, + w in u16::MIN..u16::MAX, + h in u16::MIN..u16::MAX, + ) { + let bsp = Bsp(d, a, b); + assert_eq!( + Content::layout(&bsp, [x, y, w, h]), + Render::layout(&bsp, [x, y, w, h]), + ); + } +} + +#[test] fn test_stub_output () -> Usually<()> { + use crate::*; + struct TestOutput([u16;4]); + impl Output for TestOutput { + type Unit = u16; + type Size = [u16;2]; + type Area = [u16;4]; + fn area (&self) -> [u16;4] { + self.0 + } + fn area_mut (&mut self) -> &mut [u16;4] { + &mut self.0 + } + fn place (&mut self, _: [u16;4], _: &impl Render) { + () + } + } + impl Content for String { + fn render (&self, to: &mut TestOutput) { + to.area_mut().set_w(self.len() as u16); + } + } + Ok(()) +} + +#[test] fn test_space () { + use crate::*; + assert_eq!(Area::center(&[10u16, 10, 20, 20]), [20, 20]); +} + +#[test] fn test_iter_map () { + struct Foo; + impl Content for Foo {} + fn make_map + Send + Sync> (data: &Vec) -> impl Content { + Map::new(||data.iter(), |foo, index|{}) + } + let data = vec![Foo, Foo, Foo]; + //let map = make_map(&data); +} diff --git a/output/src/view.rs b/output/src/view.rs deleted file mode 100644 index ae252d9..0000000 --- a/output/src/view.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::*; - -//#[cfg(feature = "dsl")] -//#[macro_export] macro_rules! try_delegate { - //($s:ident, $dsl:expr, $T:ty) => { - //let value: Option<$T> = Dsl::take_from($s, $dsl)?; - //if let Some(value) = value { - //return Ok(Some(value.boxed())) - //} - //} -//} - -//// Provides components to the view. -//#[cfg(feature = "dsl")] -//pub trait ViewContext<'state, E: Output + 'state>: - //Namespace + Namespace + Namespace + Send + Sync -//{ - //fn get_content_sym <'source: 'state> (&'state self, iter: &mut TokenIter<'source>) - //-> Perhaps>; - //fn get_content_exp <'source: 'state> (&'state self, iter: &mut TokenIter<'source>) - //-> Perhaps> - //{ - //try_delegate!(self, iter, When::>); - //try_delegate!(self, iter, Either::, RenderBox<'state, E>>); - //try_delegate!(self, iter, Align::>); - //try_delegate!(self, iter, Bsp::, RenderBox<'state, E>>); - //try_delegate!(self, iter, Fill::>); - //try_delegate!(self, iter, Fixed::<_, RenderBox<'state, E>>); - //try_delegate!(self, iter, Min::<_, RenderBox<'state, E>>); - //try_delegate!(self, iter, Max::<_, RenderBox<'state, E>>); - //try_delegate!(self, iter, Shrink::<_, RenderBox<'state, E>>); - //try_delegate!(self, iter, Expand::<_, RenderBox<'state, E>>); - //try_delegate!(self, iter, Push::<_, RenderBox<'state, E>>); - //try_delegate!(self, iter, Pull::<_, RenderBox<'state, E>>); - //try_delegate!(self, iter, Margin::<_, RenderBox<'state, E>>); - //try_delegate!(self, iter, Padding::<_, RenderBox<'state, E>>); - //Ok(None) - //} -//} - -//#[cfg(feature = "dsl")] -//impl<'context, O: Output + 'context, T: ViewContext<'context, O>> Namespace for RenderBox<'context, O> { - //fn take_from <'state, 'source: 'state> (state: &'state T, token: &mut TokenIter<'source>) - //-> Perhaps> - //{ - //Ok(if let Some(content) = state.get_content_sym(token)? { - //Some(content) - //} else if let Some(content) = state.get_content_exp(token)? { - //Some(content) - //} else { - //None - //}) - //} -//} diff --git a/proc/src/proc_view.rs b/proc/src/proc_view.rs index 7223dc0..ac18a80 100644 --- a/proc/src/proc_view.rs +++ b/proc/src/proc_view.rs @@ -42,50 +42,53 @@ impl Parse for ViewImpl { impl ToTokens for ViewDef { fn to_tokens (&self, out: &mut TokenStream2) { let Self(ViewMeta { output }, ViewImpl { block, exposed }) = self; - let view = &block.self_ty; - let builtins = builtins().iter().map(|ty|write_quote(quote! { - if let Some(value) = Namespace::<#ty>::take_from(state, &mut exp.clone())? { - return Ok(Some(value.boxed())) - } - })).collect::>(); - let mut available = vec![]; - let exposed: Vec<_> = exposed.iter().map(|(key, value)|{ - available.push(key.clone()); - write_quote(quote! { #key => Some(#view::#value(state).boxed()), }) - }).collect(); - let available: String = available.join(", "); - let error_msg = LitStr::new( - &format!("expected Sym(content), got: {{token:?}}, available: {available}"), - Span::call_site() - ); + let self_ty = &block.self_ty; + let builtins: Vec<_> = builtins_with_types() + .iter() + .map(|ty|write_quote(quote! { + let value: Option<#ty> = Dsl::take(state, &mut exp.clone())?; + if let Some(value) = value { + return Ok(Some(value.boxed())) + } + })) + .collect(); + let exposed: Vec<_> = exposed + .iter() + .map(|(key, value)|write_quote(quote! { + #key => Some(#self_ty::#value(state).boxed()), + })).collect(); write_quote_to(out, quote! { #block /// Generated by [tengri_proc]. /// - /// Delegates the rendering of [#view] to the [#view::view} method, + /// Delegates the rendering of [#self_ty] to the [#self_ty::view} method, /// which you will need to implement, e.g. passing a [TokenIter] /// containing a layout and keybindings config from user dirs. - impl ::tengri::output::Content<#output> for #view { + impl ::tengri::output::Content<#output> for #self_ty { fn content (&self) -> impl Render<#output> { - self.view() + #self_ty::view(self) } } /// Generated by [tengri_proc]. /// - /// Gives [#view] the ability to construct the [Render]able + /// Gives [#self_ty] the ability to construct the [Render]able /// which might corresponds to a given [TokenStream], - /// while taking [#view]'s state into consideration. - impl ::tengri::dsl::Namespace<#view> for Box> { + /// while taking [#self_ty]'s state into consideration. + impl ::tengri::dsl::Namespace<#self_ty> for Box + '_> { fn take_from <'source> ( - state: &#view, + state: &#self_ty, words: &mut ::tengri::dsl::TokenIter<'source> ) -> Perhaps { Ok(if let Some(::tengri::dsl::Token { value, .. }) = words.peek() { match value { + // Expressions are handled by built-in functions + // that operate over constants and symbols. ::tengri::dsl::Value::Exp(_, exp) => { - //#(#builtins)* + #(#builtins)* None }, + // Symbols are handled by user-provided functions + // that take no parameters but `&self`. ::tengri::dsl::Value::Sym(sym) => match sym { #(#exposed)* _ => None @@ -101,21 +104,21 @@ impl ToTokens for ViewDef { } } -fn builtins () -> [TokenStream2;14] { +fn builtins_with_types () -> [TokenStream2;14] { [ - quote! { When::<_> }, - quote! { Either::<_, _> }, - quote! { Align::<_> }, - quote! { Bsp::<_, _> }, - quote! { Fill::<_> }, - quote! { Fixed::<_, _> }, - quote! { Min::<_, _> }, - quote! { Max::<_, _> }, - quote! { Shrink::<_, _> }, - quote! { Expand::<_, _> }, - quote! { Push::<_, _> }, - quote! { Pull::<_, _> }, - quote! { Margin::<_, _> }, - quote! { Padding::<_, _> }, + quote! { When< Box + '_> > }, + quote! { Either< Box + '_>, Box + '_>> }, + quote! { Align< Box + '_> > }, + quote! { Bsp< Box + '_>, Box + '_>> }, + quote! { Fill< Box + '_> > }, + quote! { Fixed<_, Box + '_> > }, + quote! { Min<_, Box + '_> > }, + quote! { Max<_, Box + '_> > }, + quote! { Shrink<_, Box + '_> > }, + quote! { Expand<_, Box + '_> > }, + quote! { Push<_, Box + '_> > }, + quote! { Pull<_, Box + '_> > }, + quote! { Margin<_, Box + '_> > }, + quote! { Padding<_, Box + '_> > }, ] }