1use std::rc::Rc;
4
5use all_the_same::all_the_same;
6use derive_more::From;
7use serde::{Deserialize, Serialize};
8use tsify::Tsify;
9use wasm_bindgen::prelude::*;
10
11use catlog::dbl::model::{DblModel as _, DiscreteDblModel, FgDblModel, MutDblModel};
12use catlog::dbl::model_diagram as diagram;
13use catlog::dbl::model_morphism::DiscreteDblModelMapping;
14use catlog::one::FgCategory;
15use catlog::zero::{MutMapping, NameLookup, NameSegment, Namespace, QualifiedLabel, QualifiedName};
16use notebook_types::current::*;
17
18use super::model::DblModel;
19use super::model_diagram_presentation::*;
20use super::notation::*;
21use super::result::JsResult;
22use super::theory::{DblTheory, DblTheoryBox};
23
24#[derive(From)]
26pub enum DblModelDiagramBox {
27 Discrete(diagram::DblModelDiagram<DiscreteDblModelMapping, DiscreteDblModel>),
29 }
31
32#[wasm_bindgen]
34pub struct DblModelDiagram {
35 #[wasm_bindgen(skip)]
37 pub diagram: DblModelDiagramBox,
38 ob_namespace: Namespace,
39}
40
41impl DblModelDiagram {
42 pub fn new(theory: &DblTheory) -> Self {
44 let diagram = match &theory.0 {
45 DblTheoryBox::Discrete(theory) => {
46 let mapping = Default::default();
47 let model = DiscreteDblModel::new(theory.clone());
48 diagram::DblModelDiagram(mapping, model).into()
49 }
50 _ => panic!("Diagrams only implemented for discrete double theories"),
51 };
52 Self {
53 diagram,
54 ob_namespace: Namespace::new_for_uuid(),
55 }
56 }
57
58 pub fn add_ob(&mut self, decl: &DiagramObDecl) -> Result<(), String> {
60 all_the_same!(match &mut self.diagram {
61 DblModelDiagramBox::[Discrete](diagram) => {
62 let (mapping, model) = diagram.into();
63 let ob_type: QualifiedName = Elaborator.elab(&decl.ob_type)?;
64 if let Some(over) = decl.over.as_ref().map(|ob| Elaborator.elab(ob)).transpose()? {
65 mapping.assign_ob(decl.id.into(), over);
66 }
67 model.add_ob(decl.id.into(), ob_type);
68 }
69 });
70 if !decl.name.is_empty() {
71 self.ob_namespace.set_label(decl.id, decl.name.as_str().into());
72 }
73 Ok(())
74 }
75
76 pub fn add_mor(&mut self, decl: &DiagramMorDecl) -> Result<(), String> {
78 all_the_same!(match &mut self.diagram {
79 DblModelDiagramBox::[Discrete](diagram) => {
80 let (mapping, model) = diagram.into();
81 let mor_type = Elaborator.elab(&decl.mor_type)?;
82 model.make_mor(decl.id.into(), mor_type);
83 if let Some(dom) = decl.dom.as_ref().map(|ob| Elaborator.elab(ob)).transpose()? {
84 model.set_dom(decl.id.into(), dom);
85 }
86 if let Some(cod) = decl.cod.as_ref().map(|ob| Elaborator.elab(ob)).transpose()? {
87 model.set_cod(decl.id.into(), cod);
88 }
89 if let Some(over) = decl.over.as_ref().map(|mor| Elaborator.elab(mor)).transpose()? {
90 mapping.assign_mor(decl.id.into(), over);
91 }
92 }
93 });
94 if decl.name.is_empty() {
95 Ok(())
96 } else {
97 Err("Indexing morphisms in diagrams cannot be labeled".into())
99 }
100 }
101}
102
103#[wasm_bindgen]
104impl DblModelDiagram {
105 #[wasm_bindgen(js_name = "obType")]
107 pub fn ob_type(&self, ob: Ob) -> Result<ObType, String> {
108 all_the_same!(match &self.diagram {
109 DblModelDiagramBox::[Discrete](diagram) => {
110 let (_, model) = diagram.into();
111 Ok(Quoter.quote(&model.ob_type(&Elaborator.elab(&ob)?)))
112 }
113 })
114 }
115
116 #[wasm_bindgen(js_name = "morType")]
118 pub fn mor_type(&self, mor: Mor) -> Result<MorType, String> {
119 all_the_same!(match &self.diagram {
120 DblModelDiagramBox::[Discrete](diagram) => {
121 let (_, model) = diagram.into();
122 Ok(Quoter.quote(&model.mor_type(&Elaborator.elab(&mor)?)))
123 }
124 })
125 }
126
127 #[wasm_bindgen(js_name = "obGenerators")]
129 pub fn ob_generators(&self) -> Vec<QualifiedName> {
130 all_the_same!(match &self.diagram {
131 DblModelDiagramBox::[Discrete](diagram) => {
132 let (_, model) = diagram.into();
133 model.ob_generators().collect()
134 }
135 })
136 }
137
138 #[wasm_bindgen(js_name = "morGenerators")]
140 pub fn mor_generators(&self) -> Vec<QualifiedName> {
141 all_the_same!(match &self.diagram {
142 DblModelDiagramBox::[Discrete](diagram) => {
143 let (_, model) = diagram.into();
144 model.mor_generators().collect()
145 }
146 })
147 }
148
149 #[wasm_bindgen(js_name = "obGeneratorsWithType")]
151 pub fn ob_generators_with_type(&self, ob_type: ObType) -> Result<Vec<QualifiedName>, String> {
152 all_the_same!(match &self.diagram {
153 DblModelDiagramBox::[Discrete](diagram) => {
154 let (_, model) = diagram.into();
155 let ob_type = Elaborator.elab(&ob_type)?;
156 Ok(model.ob_generators_with_type(&ob_type).collect())
157 }
158 })
159 }
160
161 #[wasm_bindgen(js_name = "morGeneratorsWithType")]
163 pub fn mor_generators_with_type(
164 &self,
165 mor_type: MorType,
166 ) -> Result<Vec<QualifiedName>, String> {
167 all_the_same!(match &self.diagram {
168 DblModelDiagramBox::[Discrete](diagram) => {
169 let (_, model) = diagram.into();
170 let mor_type = Elaborator.elab(&mor_type)?;
171 Ok(model.mor_generators_with_type(&mor_type).collect())
172 }
173 })
174 }
175
176 #[wasm_bindgen(js_name = "obGeneratorLabel")]
178 pub fn ob_generator_label(&self, id: &QualifiedName) -> Option<QualifiedLabel> {
179 self.ob_namespace.label(id)
180 }
181
182 #[wasm_bindgen(js_name = "obGeneratorWithLabel")]
184 pub fn ob_generator_with_label(&self, label: &QualifiedLabel) -> NameLookup {
185 self.ob_namespace.name_with_label(label)
186 }
187
188 #[wasm_bindgen(js_name = "obPresentation")]
190 pub fn ob_presentation(&self, id: QualifiedName) -> Option<DiagramObGenerator> {
191 let label = self.ob_generator_label(&id);
192 let (ob_type, over) = all_the_same!(match &self.diagram {
193 DblModelDiagramBox::[Discrete](diagram) => {
194 let (mapping, model) = diagram.into();
195 (Quoter.quote(&model.ob_generator_type(&id)),
196 Quoter.quote(mapping.0.ob_generator_map.get(&id)?))
197 }
198 });
199 Some(DiagramObGenerator {
200 id,
201 label,
202 ob_type,
203 over,
204 })
205 }
206
207 #[wasm_bindgen(js_name = "morPresentation")]
209 pub fn mor_presentation(&self, id: QualifiedName) -> Option<DiagramMorGenerator> {
210 let (mor_type, over, dom, cod) = all_the_same!(match &self.diagram {
211 DblModelDiagramBox::[Discrete](diagram) => {
212 let (mapping, model) = diagram.into();
213 (Quoter.quote(&model.mor_generator_type(&id)),
214 Quoter.quote(mapping.0.mor_generator_map.get(&id)?),
215 Quoter.quote(model.get_dom(&id)?),
216 Quoter.quote(model.get_cod(&id)?))
217 }
218 });
219 Some(DiagramMorGenerator {
220 id,
221 mor_type,
222 over,
223 dom,
224 cod,
225 })
226 }
227
228 #[wasm_bindgen]
230 pub fn presentation(&self) -> ModelDiagramPresentation {
231 all_the_same!(match &self.diagram {
232 DblModelDiagramBox::[Discrete](diagram) => {
233 let (_, model) = diagram.into();
234 ModelDiagramPresentation {
235 ob_generators: {
236 model.ob_generators().filter_map(|id| self.ob_presentation(id)).collect()
237 },
238 mor_generators: {
239 model.mor_generators().filter_map(|id| self.mor_presentation(id)).collect()
240 }
241 }
242 }
243 })
244 }
245
246 #[wasm_bindgen(js_name = "inferMissingFrom")]
248 pub fn infer_missing_from(&mut self, model: &DblModel) -> Result<(), String> {
249 all_the_same!(match &mut self.diagram {
250 DblModelDiagramBox::[Discrete](diagram) => {
251 let model: &Rc<_> = (&model.model).try_into().map_err(
252 |_| "Type of model should match type of diagram")?;
253 diagram.infer_missing_from(model);
254 }
255 });
256
257 let mut nanon = 0;
259 for id in self.ob_generators() {
260 if self.ob_namespace.label(&id).is_none() {
261 let Some(NameSegment::Uuid(uuid)) = id.only() else {
262 todo!("Imputation for diagrams with instantiations");
263 };
264 nanon += 1;
265 self.ob_namespace.set_label(uuid, nanon.into());
266 }
267 }
268
269 Ok(())
270 }
271
272 #[wasm_bindgen(js_name = "validateIn")]
274 pub fn validate_in(&self, model: &DblModel) -> Result<ModelDiagramValidationResult, String> {
275 let result = all_the_same!(match &self.diagram {
276 DblModelDiagramBox::[Discrete](diagram) => {
277 let model: &Rc<_> = (&model.model).try_into().map_err(
278 |_| "Type of model should match type of diagram")?;
279 diagram.validate_in(model)
280 }
281 });
282 Ok(ModelDiagramValidationResult(result.map_err(|errs| errs.into()).into()))
283 }
284}
285
286#[derive(Serialize, Deserialize, Tsify)]
288#[tsify(into_wasm_abi, from_wasm_abi)]
289pub struct ModelDiagramValidationResult(
290 pub JsResult<(), Vec<diagram::InvalidDiscreteDblModelDiagram>>,
291);
292
293#[wasm_bindgen(js_name = "elaborateDiagram")]
295pub fn elaborate_diagram(
296 judgments: Vec<DiagramJudgment>,
297 theory: &DblTheory,
298) -> Result<DblModelDiagram, String> {
299 let mut diagram = DblModelDiagram::new(theory);
300 for judgment in judgments {
301 match judgment {
302 DiagramJudgment::Object(decl) => diagram.add_ob(&decl)?,
303 DiagramJudgment::Morphism(decl) => diagram.add_mor(&decl)?,
304 }
305 }
306 Ok(diagram)
307}
308
309#[cfg(test)]
310mod tests {
311 use uuid::Uuid;
312
313 use super::*;
314 use crate::model::tests::sch_walking_attr;
315 use crate::theories::*;
316
317 #[test]
318 fn diagram_schema() {
319 let th = ThSchema::new().theory();
320 let [attr, entity, attr_type] = [Uuid::now_v7(), Uuid::now_v7(), Uuid::now_v7()];
321 let model = sch_walking_attr(&th, [attr, entity, attr_type]);
322
323 let mut diagram = DblModelDiagram::new(&th);
324 let [x, y, var] = [Uuid::now_v7(), Uuid::now_v7(), Uuid::now_v7()];
325 assert!(
326 diagram
327 .add_ob(&DiagramObDecl {
328 name: "var".into(),
329 id: var,
330 ob_type: ObType::Basic("AttrType".into()),
331 over: Some(Ob::Basic(attr_type.to_string()))
332 })
333 .is_ok()
334 );
335 let [a, b] = [Uuid::now_v7(), Uuid::now_v7()];
336 for (name, indiv, f) in [("x", x, a), ("y", y, b)] {
337 assert!(
338 diagram
339 .add_ob(&DiagramObDecl {
340 name: name.into(),
341 id: indiv,
342 ob_type: ObType::Basic("Entity".into()),
343 over: Some(Ob::Basic(entity.to_string())),
344 })
345 .is_ok()
346 );
347 assert!(
348 diagram
349 .add_mor(&DiagramMorDecl {
350 name: "".into(),
351 id: f,
352 mor_type: MorType::Basic("Attr".into()),
353 dom: Some(Ob::Basic(indiv.to_string())),
354 cod: Some(Ob::Basic(var.to_string())),
355 over: Some(Mor::Basic(attr.to_string())),
356 })
357 .is_ok()
358 );
359 }
360 assert_eq!(diagram.ob_generator_label(&var.into()), Some("var".into()));
361 assert_eq!(diagram.ob_generator_with_label(&"var".into()), NameLookup::Unique(var.into()));
362 assert_eq!(diagram.ob_generators().len(), 3);
363 assert_eq!(diagram.mor_generators().len(), 2);
364 assert_eq!(diagram.validate_in(&model).unwrap().0, JsResult::Ok(()));
365
366 let presentation = diagram.presentation();
367 assert_eq!(presentation.ob_generators.len(), 3);
368 assert_eq!(presentation.mor_generators.len(), 2);
369 }
370}