sim_lib_web_layout/
citizen.rs1use sim_citizen_derive::Citizen;
2use sim_kernel::{Expr, Result, Symbol};
3
4use crate::new_workspace;
5
6#[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 pub fn from_expr(workspace: Expr) -> Result<Self> {
22 workspace_expr::decode(&workspace)?;
23 Ok(Self { workspace })
24 }
25
26 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
39pub 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}