1use all_the_same::all_the_same;
4use derive_more::From;
5use uuid::Uuid;
6
7use serde::{Deserialize, Serialize};
8use tsify_next::Tsify;
9use wasm_bindgen::prelude::*;
10
11use catlog::dbl::model::{FgDblModel, MutDblModel};
12use catlog::dbl::model_diagram as diagram;
13use catlog::dbl::model_morphism::DblModelMapping;
14use catlog::one::FgCategory;
15
16use super::model::{DblModel, DblModelBox, DiscreteDblModel, Mor, Ob};
17use super::model_morphism::DiscreteDblModelMapping;
18use super::result::JsResult;
19use super::theory::{DblTheory, MorType, ObType};
20
21#[derive(Serialize, Deserialize, Tsify)]
23#[tsify(into_wasm_abi, from_wasm_abi, missing_as_null)]
24pub struct DiagramObDecl {
25 pub id: Uuid,
27
28 #[serde(rename = "obType")]
30 pub ob_type: ObType,
31
32 pub over: Option<Ob>,
34}
35
36#[derive(Serialize, Deserialize, Tsify)]
38#[tsify(into_wasm_abi, from_wasm_abi, missing_as_null)]
39pub struct DiagramMorDecl {
40 pub id: Uuid,
42
43 #[serde(rename = "morType")]
45 pub mor_type: MorType,
46
47 pub over: Option<Mor>,
49
50 pub dom: Option<Ob>,
52
53 pub cod: Option<Ob>,
55}
56
57#[derive(From)]
59pub enum DblModelDiagramBox {
60 Discrete(diagram::DblModelDiagram<DiscreteDblModelMapping, DiscreteDblModel>),
61 }
63
64#[wasm_bindgen]
66pub struct DblModelDiagram(#[wasm_bindgen(skip)] pub DblModelDiagramBox);
67
68#[wasm_bindgen]
69impl DblModelDiagram {
70 #[wasm_bindgen(constructor)]
72 pub fn new(theory: &DblTheory) -> Self {
73 let model = DblModel::new(theory);
74 Self(match model.0 {
75 DblModelBox::Discrete(model) => {
76 let mapping = Default::default();
77 diagram::DblModelDiagram(mapping, model).into()
78 }
79 DblModelBox::DiscreteTab(_) => {
80 panic!("Diagrams not implemented for tabulator theories")
81 }
82 })
83 }
84
85 #[wasm_bindgen(js_name = "addOb")]
87 pub fn add_ob(&mut self, decl: DiagramObDecl) -> Result<bool, String> {
88 all_the_same!(match &mut self.0 {
89 DblModelDiagramBox::[Discrete](diagram) => {
90 let (mapping, model) = diagram.into();
91 let ob_type = decl.ob_type.try_into()?;
92 if let Some(over) = decl.over.map(|ob| ob.try_into()).transpose()? {
93 mapping.assign_ob(decl.id, over);
94 }
95 Ok(model.add_ob(decl.id, ob_type))
96 }
97 })
98 }
99
100 #[wasm_bindgen(js_name = "addMor")]
102 pub fn add_mor(&mut self, decl: DiagramMorDecl) -> Result<bool, String> {
103 all_the_same!(match &mut self.0 {
104 DblModelDiagramBox::[Discrete](diagram) => {
105 let (mapping, model) = diagram.into();
106 let mor_type = decl.mor_type.try_into()?;
107 let res = model.make_mor(decl.id, mor_type);
108 if let Some(dom) = decl.dom.map(|ob| ob.try_into()).transpose()? {
109 model.set_dom(decl.id, dom);
110 }
111 if let Some(cod) = decl.cod.map(|ob| ob.try_into()).transpose()? {
112 model.set_cod(decl.id, cod);
113 }
114 if let Some(over) = decl.over.map(|mor| mor.try_into()).transpose()? {
115 mapping.assign_basic_mor(decl.id, over);
116 }
117 Ok(res)
118 }
119 })
120 }
121
122 #[wasm_bindgen]
124 pub fn objects(&self) -> Vec<Ob> {
125 all_the_same!(match &self.0 {
126 DblModelDiagramBox::[Discrete](diagram) => {
127 let (_, model) = diagram.into();
128 model.objects().map(|x| x.into()).collect()
129 }
130 })
131 }
132
133 #[wasm_bindgen]
135 pub fn morphisms(&self) -> Vec<Mor> {
136 all_the_same!(match &self.0 {
137 DblModelDiagramBox::[Discrete](diagram) => {
138 let (_, model) = diagram.into();
139 model.morphisms().map(|f| f.into()).collect()
140 }
141 })
142 }
143
144 #[wasm_bindgen(js_name = "objectsWithType")]
146 pub fn objects_with_type(&self, ob_type: ObType) -> Result<Vec<Ob>, String> {
147 all_the_same!(match &self.0 {
148 DblModelDiagramBox::[Discrete](diagram) => {
149 let (_, model) = diagram.into();
150 let ob_type = ob_type.try_into()?;
151 Ok(model.objects_with_type(&ob_type).map(|x| x.into()).collect())
152 }
153 })
154 }
155
156 #[wasm_bindgen(js_name = "morphismsWithType")]
158 pub fn morphisms_with_type(&self, mor_type: MorType) -> Result<Vec<Mor>, String> {
159 all_the_same!(match &self.0 {
160 DblModelDiagramBox::[Discrete](diagram) => {
161 let (_, model) = diagram.into();
162 let mor_type = mor_type.try_into()?;
163 Ok(model.morphisms_with_type(&mor_type).map(|f| f.into()).collect())
164 }
165 })
166 }
167
168 #[wasm_bindgen(js_name = "objectDeclarations")]
170 pub fn object_declarations(&self) -> Vec<DiagramObDecl> {
171 all_the_same!(match &self.0 {
172 DblModelDiagramBox::[Discrete](diagram) => {
173 let (mapping, model) = diagram.into();
174 let decls = model.ob_generators().map(|x| {
175 DiagramObDecl {
176 id: x,
177 ob_type: model.ob_generator_type(&x).into(),
178 over: mapping.apply_ob(&x).map(|ob| ob.into())
179 }
180 });
181 decls.collect()
182 }
183 })
184 }
185
186 #[wasm_bindgen(js_name = "morphismDeclarations")]
188 pub fn morphism_declarations(&self) -> Vec<DiagramMorDecl> {
189 all_the_same!(match &self.0 {
190 DblModelDiagramBox::[Discrete](diagram) => {
191 let (mapping, model) = diagram.into();
192 let decls = model.mor_generators().map(|f| {
193 DiagramMorDecl {
194 id: f,
195 mor_type: model.mor_generator_type(&f).into(),
196 over: mapping.apply_basic_mor(&f).map(|mor| mor.into()),
197 dom: model.get_dom(&f).cloned().map(|ob| ob.into()),
198 cod: model.get_cod(&f).cloned().map(|ob| ob.into()),
199 }
200 });
201 decls.collect()
202 }
203 })
204 }
205
206 #[wasm_bindgen(js_name = "inferMissingFrom")]
208 pub fn infer_missing_from(&mut self, model: &DblModel) -> Result<(), String> {
209 all_the_same!(match &mut self.0 {
210 DblModelDiagramBox::[Discrete](diagram) => {
211 let model = (&model.0).try_into().map_err(
212 |_| "Type of model should match type of diagram")?;
213 diagram.infer_missing_from(model);
214 Ok(())
215 }
216 })
217 }
218
219 #[wasm_bindgen(js_name = "validateIn")]
221 pub fn validate_in(&self, model: &DblModel) -> Result<ModelDiagramValidationResult, String> {
222 all_the_same!(match &self.0 {
223 DblModelDiagramBox::[Discrete](diagram) => {
224 let model = (&model.0).try_into().map_err(
225 |_| "Type of model should match type of diagram")?;
226 let res = diagram.validate_in(model);
227 Ok(ModelDiagramValidationResult(res.map_err(|errs| errs.into()).into()))
228 }
229 })
230 }
231}
232
233#[derive(Serialize, Deserialize, Tsify)]
235#[tsify(into_wasm_abi, from_wasm_abi)]
236pub struct ModelDiagramValidationResult(
237 pub JsResult<(), Vec<diagram::InvalidDiscreteDblModelDiagram<Uuid>>>,
238);
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243 use crate::model::tests::sch_walking_attr;
244 use crate::theories::*;
245
246 #[test]
247 fn diagram_schema() {
248 let th = ThSchema::new().theory();
249 let [attr, entity, attr_type] = [Uuid::now_v7(), Uuid::now_v7(), Uuid::now_v7()];
250 let model = sch_walking_attr(&th, [attr, entity, attr_type]);
251
252 let mut diagram = DblModelDiagram::new(&th);
253 let [x, y, var] = [Uuid::now_v7(), Uuid::now_v7(), Uuid::now_v7()];
254 assert!(
255 diagram
256 .add_ob(DiagramObDecl {
257 id: var,
258 ob_type: ObType::Basic("AttrType".into()),
259 over: Some(Ob::Basic(attr_type))
260 })
261 .is_ok()
262 );
263 for indiv in [x, y] {
264 assert!(
265 diagram
266 .add_ob(DiagramObDecl {
267 id: indiv,
268 ob_type: ObType::Basic("Entity".into()),
269 over: Some(Ob::Basic(entity))
270 })
271 .is_ok()
272 );
273 assert!(
274 diagram
275 .add_mor(DiagramMorDecl {
276 id: Uuid::now_v7(),
277 mor_type: MorType::Basic("Attr".into()),
278 dom: Some(Ob::Basic(indiv)),
279 cod: Some(Ob::Basic(var)),
280 over: Some(Mor::Basic(attr)),
281 })
282 .is_ok()
283 );
284 }
285 assert_eq!(diagram.objects().len(), 3);
286 assert_eq!(diagram.object_declarations().len(), 3);
287 assert_eq!(diagram.morphisms().len(), 2);
288 assert_eq!(diagram.morphism_declarations().len(), 2);
289 assert_eq!(diagram.validate_in(&model).unwrap().0, JsResult::Ok(()));
290 }
291}