Skip to main content

sim_lib_web_layout/
citizen.rs

1use sim_citizen_derive::Citizen;
2use sim_kernel::{Expr, Result, Symbol};
3
4use crate::new_workspace;
5
6/// A validated workspace layout wrapped as a runtime Citizen object.
7#[derive(Clone, Debug, PartialEq, Citizen)]
8#[citizen(symbol = "web/Workspace", version = 1)]
9pub struct WorkspaceDescriptor {
10    #[citizen(with = "workspace_expr")]
11    workspace: Expr,
12}
13
14impl WorkspaceDescriptor {
15    /// Builds a descriptor from a workspace expression, validating it.
16    ///
17    /// # Errors
18    ///
19    /// Returns an error when `workspace` is not a well-formed workspace
20    /// expression.
21    pub fn from_expr(workspace: Expr) -> Result<Self> {
22        workspace_expr::decode(&workspace)?;
23        Ok(Self { workspace })
24    }
25
26    /// Returns the underlying workspace expression.
27    pub fn as_expr(&self) -> &Expr {
28        &self.workspace
29    }
30}
31
32impl Default for WorkspaceDescriptor {
33    fn default() -> Self {
34        Self::from_expr(new_workspace("builder"))
35            .expect("default workspace descriptor should be valid")
36    }
37}
38
39/// Returns the class symbol for the workspace descriptor Citizen.
40pub fn workspace_descriptor_class_symbol() -> Symbol {
41    Symbol::qualified("web", "Workspace")
42}
43
44pub(crate) mod workspace_expr {
45    use sim_kernel::{Error, Expr, Result};
46
47    use crate::WORKSPACE_CLASS;
48
49    pub fn encode(expr: &Expr) -> Expr {
50        expr.clone()
51    }
52
53    pub fn decode(expr: &Expr) -> Result<Expr> {
54        let Expr::Map(entries) = expr else {
55            return Err(Error::Eval("workspace descriptor must be a map".to_owned()));
56        };
57        let class = entries.iter().find_map(|(key, value)| {
58            matches!(key, Expr::Symbol(symbol) if symbol.namespace.is_none() && symbol.name.as_ref() == "class")
59                .then_some(value)
60        });
61        match class {
62            Some(Expr::Symbol(symbol)) if symbol.to_string() == WORKSPACE_CLASS => Ok(expr.clone()),
63            _ => Err(Error::Eval(
64                "workspace descriptor class must be web/Workspace".to_owned(),
65            )),
66        }
67    }
68}