compose_library/foundations/cast/
reflect.rs

1use crate::repr::separated_list;
2use compose_library::{Type, Value};
3use ecow::{eco_format, EcoString};
4use std::fmt::Write;
5
6pub trait Reflect {
7    /// What can be turned into this value?
8    fn input() -> CastInfo;
9
10    /// What can this value turn into?
11    fn output() -> CastInfo;
12
13    /// Whether the given value can turn into this type
14    fn castable(value: &Value) -> bool;
15
16    fn error(found: &Value) -> EcoString {
17        Self::input().error(found)
18    }
19}
20
21pub enum CastInfo {
22    Any,
23    /// Any value of a specific type
24    Type(Type),
25    Union(Vec<Self>),
26}
27
28impl CastInfo {
29    pub fn error(&self, found: &Value) -> EcoString {
30        let mut parts = vec![];
31
32        self.walk(|info| match info {
33            CastInfo::Any => parts.push("any".into()),
34            CastInfo::Type(ty) => parts.push(eco_format!("{ty}")),
35            CastInfo::Union(_) => {}
36        });
37
38        let mut msg = String::from("expected ");
39
40        msg.push_str(&separated_list(&parts, "or"));
41
42        msg.push_str(", found ");
43        write!(msg, "{}", found.ty()).unwrap();
44
45        msg.into()
46    }
47
48    pub fn walk<F>(&self, mut f: F)
49    where
50        F: FnMut(&Self),
51    {
52        fn inner<F>(info: &CastInfo, f: &mut F)
53        where
54            F: FnMut(&CastInfo),
55        {
56            match info {
57                CastInfo::Union(infos) => {
58                    for info in infos {
59                        inner(info, f);
60                    }
61                }
62                _ => f(info),
63            }
64        }
65
66        inner(self, &mut f);
67    }
68}