sim_lib_topology/
citizen.rs1use sim_citizen_derive::Citizen;
5use sim_kernel::{Error, Expr, Result, Symbol};
6use sim_value::build::entry;
7
8#[derive(Clone, Debug, PartialEq, Citizen)]
10#[citizen(symbol = "topology/Package", version = 1)]
11pub struct TopologyPackageDescriptor {
12 #[citizen(with = "package_source")]
13 source: String,
14}
15
16#[derive(Clone, Debug, PartialEq, Citizen)]
18#[citizen(symbol = "topology/Node", version = 1)]
19pub struct TopologyNodeDescriptor {
20 #[citizen(with = "node_expr")]
21 node: Expr,
22}
23
24#[derive(Clone, Debug, PartialEq, Citizen)]
26#[citizen(symbol = "topology/Edge", version = 1)]
27pub struct TopologyEdgeDescriptor {
28 #[citizen(with = "edge_expr")]
29 edge: Expr,
30}
31
32impl TopologyPackageDescriptor {
33 pub fn new(source: impl Into<String>) -> Result<Self> {
35 let source = source.into();
36 package_source::decode(&Expr::String(source.clone()))?;
37 Ok(Self { source })
38 }
39
40 pub fn source(&self) -> &str {
42 &self.source
43 }
44}
45
46impl Default for TopologyPackageDescriptor {
47 fn default() -> Self {
48 Self::new(
49 r#"
50graph:
51topology citizen-package
52node in verb=in
53node out verb=out
54wire in -> out
55"#,
56 )
57 .expect("default topology package descriptor should be valid")
58 }
59}
60
61impl TopologyNodeDescriptor {
62 pub fn from_expr(node: Expr) -> Result<Self> {
64 node_expr::decode(&node)?;
65 Ok(Self { node })
66 }
67
68 pub fn as_expr(&self) -> &Expr {
70 &self.node
71 }
72}
73
74impl Default for TopologyNodeDescriptor {
75 fn default() -> Self {
76 Self::from_expr(Expr::Map(vec![
77 entry("id", Expr::Symbol(Symbol::new("citizen-node"))),
78 entry("verb", Expr::Symbol(Symbol::new("wire"))),
79 ]))
80 .expect("default topology node descriptor should be valid")
81 }
82}
83
84impl TopologyEdgeDescriptor {
85 pub fn from_expr(edge: Expr) -> Result<Self> {
87 edge_expr::decode(&edge)?;
88 Ok(Self { edge })
89 }
90
91 pub fn as_expr(&self) -> &Expr {
93 &self.edge
94 }
95}
96
97impl Default for TopologyEdgeDescriptor {
98 fn default() -> Self {
99 Self::from_expr(Expr::Map(vec![
100 entry("from", Expr::Symbol(Symbol::new("citizen-node/out"))),
101 entry("to", Expr::Symbol(Symbol::new("citizen-out/in"))),
102 ]))
103 .expect("default topology edge descriptor should be valid")
104 }
105}
106
107pub fn topology_package_class_symbol() -> Symbol {
109 Symbol::qualified("topology", "Package")
110}
111
112pub fn topology_node_class_symbol() -> Symbol {
114 Symbol::qualified("topology", "Node")
115}
116
117pub fn topology_edge_class_symbol() -> Symbol {
119 Symbol::qualified("topology", "Edge")
120}
121
122pub(crate) mod package_source {
123 use sim_kernel::{Error, Expr, Result};
124
125 use crate::parse_package;
126
127 pub fn encode(source: &str) -> sim_kernel::Expr {
128 sim_kernel::Expr::String(source.to_owned())
129 }
130
131 pub fn decode(expr: &Expr) -> Result<String> {
132 let Expr::String(source) = expr else {
133 return Err(Error::Eval(
134 "topology package descriptor source must be a string".to_owned(),
135 ));
136 };
137 parse_package(source)?;
138 Ok(source.clone())
139 }
140}
141
142pub(crate) mod node_expr {
143 use sim_kernel::{Error, Expr, Result};
144
145 use super::has_required_field;
146
147 pub fn encode(expr: &Expr) -> Expr {
148 expr.clone()
149 }
150
151 pub fn decode(expr: &Expr) -> Result<Expr> {
152 let Expr::Map(entries) = expr else {
153 return Err(Error::Eval(
154 "topology node descriptor must be a map".to_owned(),
155 ));
156 };
157 has_required_field(entries, "id", "topology node descriptor")?;
158 has_required_field(entries, "verb", "topology node descriptor")?;
159 Ok(expr.clone())
160 }
161}
162
163pub(crate) mod edge_expr {
164 use sim_kernel::{Error, Expr, Result};
165
166 use super::has_required_field;
167
168 pub fn encode(expr: &Expr) -> Expr {
169 expr.clone()
170 }
171
172 pub fn decode(expr: &Expr) -> Result<Expr> {
173 let Expr::Map(entries) = expr else {
174 return Err(Error::Eval(
175 "topology edge descriptor must be a map".to_owned(),
176 ));
177 };
178 has_required_field(entries, "from", "topology edge descriptor")?;
179 has_required_field(entries, "to", "topology edge descriptor")?;
180 Ok(expr.clone())
181 }
182}
183
184fn has_required_field(entries: &[(Expr, Expr)], name: &str, context: &str) -> Result<()> {
185 if entries
186 .iter()
187 .any(|(key, _)| field_name(key).as_deref() == Some(name))
188 {
189 return Ok(());
190 }
191 Err(Error::Eval(format!("{context} is missing {name}")))
192}
193
194fn field_name(expr: &Expr) -> Option<String> {
195 match expr {
196 Expr::Symbol(symbol) if symbol.namespace.is_none() => Some(symbol.name.to_string()),
197 Expr::String(value) => Some(value.clone()),
198 _ => None,
199 }
200}