Skip to main content

plugx_input/
input.rs

1use std::{
2    collections::HashMap,
3    fmt::{Debug, Display, Formatter},
4};
5
6/// Kind of value stored in [`Input`].
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8pub enum InputType {
9    Bool,
10    Int,
11    Str,
12    Float,
13    Map,
14    List,
15}
16
17impl Display for InputType {
18    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
19        f.write_str(match self {
20            Self::Bool => "boolean",
21            Self::Int => "integer",
22            Self::Str => "string",
23            Self::Float => "float",
24            Self::Map => "map",
25            Self::List => "list",
26        })
27    }
28}
29
30#[derive(Debug, Clone, PartialEq)]
31#[cfg_attr(
32    feature = "rkyv",
33    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize),
34    rkyv(derive(Debug)),
35    rkyv(serialize_bounds(
36        __S: rkyv::ser::Writer + rkyv::ser::Allocator,
37        __S::Error: rkyv::rancor::Source,
38    )),
39    rkyv(deserialize_bounds(__D::Error: rkyv::rancor::Source)),
40    rkyv(bytecheck(bounds(__C: rkyv::validation::ArchiveContext))),
41)]
42/// Dynamically typed value for plugin configuration and runtime state.
43///
44/// Six variants: boolean, integer ([`isize`]), float ([`f64`]), string, list, and map.
45/// Build values with [`From`] (scalars, slices, [`Vec`], [`HashMap`], etc.); inspect them
46/// with `is_*`, `as_*`, `into_*`, and `*_mut` accessors. Use [`Input::type_name`] for the
47/// active [`InputType`].
48///
49/// # Cargo features
50///
51/// - **`serde`**: [`Serialize`], [`Deserialize`], and [`Input::serialize`].
52/// - **`rkyv`**: archive derives plus [`Input::to_rkyv_bytes`] and [`Input::from_rkyv_bytes`].
53pub enum Input {
54    Bool(bool),
55    Int(isize),
56    Float(f64),
57    Str(String),
58    List(#[cfg_attr(feature = "rkyv", rkyv(omit_bounds))] Vec<Input>),
59    Map(#[cfg_attr(feature = "rkyv", rkyv(omit_bounds))] HashMap<String, Input>),
60}
61
62impl Input {
63    /// Empty map (`Input::Map`).
64    pub fn new_map() -> Self {
65        Self::Map(HashMap::new())
66    }
67
68    /// Empty list (`Input::List`).
69    pub fn new_list() -> Self {
70        Self::List(Vec::new())
71    }
72
73    /// Empty string (`Input::Str`).
74    pub fn new_str() -> Self {
75        Self::Str(String::new())
76    }
77
78    pub fn is_bool(&self) -> bool {
79        matches!(self, Self::Bool(_))
80    }
81
82    pub fn as_bool(&self) -> Option<&bool> {
83        match self {
84            Self::Bool(value) => Some(value),
85            _ => None,
86        }
87    }
88
89    pub fn into_bool(self) -> Option<bool> {
90        match self {
91            Self::Bool(value) => Some(value),
92            _ => None,
93        }
94    }
95
96    pub fn bool_mut(&mut self) -> Option<&mut bool> {
97        match self {
98            Self::Bool(value) => Some(value),
99            _ => None,
100        }
101    }
102
103    pub fn is_int(&self) -> bool {
104        matches!(self, Self::Int(_))
105    }
106
107    pub fn as_int(&self) -> Option<&isize> {
108        match self {
109            Self::Int(value) => Some(value),
110            _ => None,
111        }
112    }
113
114    pub fn into_int(self) -> Option<isize> {
115        match self {
116            Self::Int(value) => Some(value),
117            _ => None,
118        }
119    }
120
121    pub fn int_mut(&mut self) -> Option<&mut isize> {
122        match self {
123            Self::Int(value) => Some(value),
124            _ => None,
125        }
126    }
127
128    pub fn is_float(&self) -> bool {
129        matches!(self, Self::Float(_))
130    }
131
132    pub fn as_float(&self) -> Option<&f64> {
133        match self {
134            Self::Float(value) => Some(value),
135            _ => None,
136        }
137    }
138
139    pub fn into_float(self) -> Option<f64> {
140        match self {
141            Self::Float(value) => Some(value),
142            _ => None,
143        }
144    }
145
146    pub fn float_mut(&mut self) -> Option<&mut f64> {
147        match self {
148            Self::Float(value) => Some(value),
149            _ => None,
150        }
151    }
152
153    pub fn is_str(&self) -> bool {
154        matches!(self, Self::Str(_))
155    }
156
157    pub fn as_str(&self) -> Option<&String> {
158        match self {
159            Self::Str(value) => Some(value),
160            _ => None,
161        }
162    }
163
164    pub fn into_str(self) -> Option<String> {
165        match self {
166            Self::Str(value) => Some(value),
167            _ => None,
168        }
169    }
170
171    pub fn str_mut(&mut self) -> Option<&mut String> {
172        match self {
173            Self::Str(value) => Some(value),
174            _ => None,
175        }
176    }
177
178    pub fn is_list(&self) -> bool {
179        matches!(self, Self::List(_))
180    }
181
182    pub fn as_list(&self) -> Option<&Vec<Input>> {
183        match self {
184            Self::List(value) => Some(value),
185            _ => None,
186        }
187    }
188
189    pub fn into_list(self) -> Option<Vec<Input>> {
190        match self {
191            Self::List(value) => Some(value),
192            _ => None,
193        }
194    }
195
196    pub fn list_mut(&mut self) -> Option<&mut Vec<Input>> {
197        match self {
198            Self::List(value) => Some(value),
199            _ => None,
200        }
201    }
202
203    pub fn is_map(&self) -> bool {
204        matches!(self, Self::Map(_))
205    }
206
207    pub fn as_map(&self) -> Option<&HashMap<String, Input>> {
208        match self {
209            Self::Map(value) => Some(value),
210            _ => None,
211        }
212    }
213
214    pub fn into_map(self) -> Option<HashMap<String, Input>> {
215        match self {
216            Self::Map(value) => Some(value),
217            _ => None,
218        }
219    }
220
221    pub fn map_mut(&mut self) -> Option<&mut HashMap<String, Input>> {
222        match self {
223            Self::Map(value) => Some(value),
224            _ => None,
225        }
226    }
227
228    /// Kind of the active variant.
229    pub fn type_name(&self) -> InputType {
230        match self {
231            Self::Bool(_) => InputType::Bool,
232            Self::Int(_) => InputType::Int,
233            Self::Float(_) => InputType::Float,
234            Self::Str(_) => InputType::Str,
235            Self::List(_) => InputType::List,
236            Self::Map(_) => InputType::Map,
237        }
238    }
239}
240
241impl Display for Input {
242    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
243        match self {
244            Self::Bool(value) => write!(f, "{value}"),
245            Self::Int(value) => write!(f, "{value}"),
246            Self::Float(value) => write!(f, "{value}"),
247            Self::Str(value) => write!(f, "{value:?}"),
248            Self::List(value) => {
249                write!(f, "[")?;
250                let length = value.len();
251                for (index, inner_value) in value.iter().enumerate() {
252                    write!(f, "{inner_value}")?;
253                    if index < length - 1 {
254                        write!(f, ", ")?;
255                    }
256                }
257                write!(f, "]")
258            }
259            Self::Map(value) => {
260                write!(f, "{{")?;
261                let length = value.len();
262                for (index, (key, value)) in value.iter().enumerate() {
263                    write!(f, "{key:?}: {value}")?;
264                    if index < length - 1 {
265                        write!(f, ", ")?;
266                    }
267                }
268                write!(f, "}}")
269            }
270        }
271    }
272}