Skip to main content

acts_next/model/
output.rs

1use crate::{ActError, Vars};
2use core::fmt;
3use serde::{Deserialize, Serialize, de};
4use serde_json::Value;
5use std::collections::HashMap;
6
7#[derive(Debug, Default, Clone, Serialize, Deserialize, strum::AsRefStr)]
8pub enum OutputType {
9    #[default]
10    String,
11    Bool,
12    Number,
13    Array,
14    Object,
15}
16
17#[derive(Debug, Default, Clone, Serialize, Deserialize)]
18pub struct Output {
19    pub required: bool,
20    pub default: Value,
21    pub r#type: OutputType,
22}
23
24#[derive(Debug, Default, Clone)]
25pub struct Outputs {
26    inner: HashMap<String, Output>,
27}
28
29fn get<T>(name: &str, value: &Value) -> Option<T>
30where
31    T: for<'de> Deserialize<'de> + Clone,
32{
33    if let Some(v) = value.get(name)
34        && let Ok(v) = serde_json::from_value::<T>(v.clone())
35    {
36        Some(v)
37    } else {
38        None
39    }
40}
41
42impl fmt::Display for OutputType {
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        f.write_str(self.as_ref())
45    }
46}
47
48impl From<Value> for Output {
49    fn from(value: Value) -> Self {
50        let required = get::<bool>("required", &value).unwrap_or_default();
51        let default = get::<Value>("default", &value).unwrap_or_default();
52        let r#type = get::<OutputType>("type", &value).unwrap_or_default();
53
54        Self {
55            required,
56            default,
57            r#type,
58        }
59    }
60}
61
62impl Serialize for Outputs {
63    fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
64    where
65        S: serde::Serializer,
66    {
67        use serde::ser::SerializeMap;
68
69        let mut map = serializer.serialize_map(Some(self.inner.len()))?;
70        for (k, v) in &self.inner {
71            map.serialize_entry(k, v)?;
72        }
73        map.end()
74    }
75}
76
77impl<'de> Deserialize<'de> for Outputs {
78    fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
79    where
80        D: serde::Deserializer<'de>,
81    {
82        struct Visitor;
83
84        impl<'de> de::Visitor<'de> for Visitor {
85            type Value = HashMap<String, Output>;
86            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
87                formatter.write_str("a map")
88            }
89
90            #[inline]
91            fn visit_unit<E>(self) -> Result<Self::Value, E>
92            where
93                E: de::Error,
94            {
95                Ok(HashMap::new())
96            }
97
98            #[inline]
99            fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
100            where
101                V: de::MapAccess<'de>,
102            {
103                let mut values = HashMap::new();
104
105                while let Some((key, value)) = visitor.next_entry::<String, Value>()? {
106                    let value: Output = value.into();
107                    values.insert(key, value);
108                }
109
110                Ok(values)
111            }
112        }
113
114        core::result::Result::Ok(Self {
115            inner: deserializer.deserialize_map(Visitor)?,
116        })
117    }
118}
119
120impl Outputs {
121    pub fn push(&mut self, name: &str, output: &Output) {
122        self.inner.insert(name.to_string(), output.clone());
123    }
124
125    pub fn check(&self, vars: &Vars) -> crate::Result<()> {
126        for (k, output) in &self.inner {
127            let v = vars.get::<Value>(k);
128            if v.is_none() && output.required {
129                return Err(ActError::Runtime(format!("the key '{k}' is required",)));
130            }
131
132            if let Some(v) = &v {
133                let is_type_match = match output.r#type {
134                    OutputType::String => v.is_string(),
135                    OutputType::Bool => v.is_boolean(),
136                    OutputType::Number => v.is_number(),
137                    OutputType::Array => v.is_array(),
138                    OutputType::Object => v.is_object(),
139                };
140
141                if !is_type_match {
142                    return Err(ActError::Runtime(format!(
143                        "the value {k}({v}) is not matched the type '{}'",
144                        output.r#type
145                    )));
146                }
147            }
148        }
149        Ok(())
150    }
151}