catlog_wasm/
theory.rs

1//! Wasm bindings for double theories.
2
3use std::collections::HashMap;
4use std::hash::Hash;
5use std::rc::Rc;
6
7use all_the_same::all_the_same;
8use derive_more::From;
9use ustr::Ustr;
10
11use serde::{Deserialize, Serialize};
12use tsify::Tsify;
13use wasm_bindgen::prelude::*;
14
15use catlog::dbl::theory;
16use catlog::dbl::theory::{DblTheory as _, TabMorType, TabObType};
17use catlog::one::Path;
18
19/// Object type in a double theory.
20#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize, Tsify)]
21#[serde(tag = "tag", content = "content")]
22#[tsify(into_wasm_abi, from_wasm_abi)]
23pub enum ObType {
24    /// Basic or generating object type.
25    Basic(Ustr),
26
27    /// Tabulator of a morphism type.
28    Tabulator(Box<MorType>),
29}
30
31/// Morphism type in a double theory.
32#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize, Tsify)]
33#[serde(tag = "tag", content = "content")]
34#[tsify(into_wasm_abi, from_wasm_abi)]
35pub enum MorType {
36    /// Basic or generating morphism type.
37    Basic(Ustr),
38
39    /// Composite of morphism types.
40    Composite(Vec<MorType>),
41
42    /// Hom type on an object type.
43    Hom(Box<ObType>),
44}
45
46/// Convert from object type in a discrete double theory.
47impl From<Ustr> for ObType {
48    fn from(value: Ustr) -> Self {
49        ObType::Basic(value)
50    }
51}
52
53/// Convert from morphism type in a discrete double theory.
54impl From<Path<Ustr, Ustr>> for MorType {
55    fn from(mor: Path<Ustr, Ustr>) -> Self {
56        match mor {
57            Path::Id(v) => MorType::Hom(Box::new(ObType::Basic(v))),
58            Path::Seq(edges) => {
59                if edges.len() == 1 {
60                    MorType::Basic(edges.head)
61                } else {
62                    MorType::Composite(edges.into_iter().map(MorType::Basic).collect())
63                }
64            }
65        }
66    }
67}
68
69/// Convert into object type in a discrete double theory.
70impl TryFrom<ObType> for Ustr {
71    type Error = String;
72
73    fn try_from(ob_type: ObType) -> Result<Self, Self::Error> {
74        match ob_type {
75            ObType::Basic(name) => Ok(name),
76            _ => Err(format!("Cannot cast object type for discrete double theory: {ob_type:#?}")),
77        }
78    }
79}
80
81/// Convert into morphism type in a discrete double theory.
82impl TryFrom<MorType> for Path<Ustr, Ustr> {
83    type Error = String;
84
85    fn try_from(mor_type: MorType) -> Result<Self, Self::Error> {
86        match mor_type {
87            MorType::Basic(name) => Ok(name.into()),
88            MorType::Composite(fs) => {
89                let fs: Result<Vec<_>, _> = fs.into_iter().map(|f| f.try_into()).collect();
90                let path = Path::from_vec(fs?).ok_or("Composite should not be empty")?;
91                Ok(path.flatten())
92            }
93            MorType::Hom(x) => (*x).try_into().map(Path::Id),
94        }
95    }
96}
97
98/// Convert from object type in a discrete tabulator theory.
99impl From<TabObType<Ustr, Ustr>> for ObType {
100    fn from(ob_type: TabObType<Ustr, Ustr>) -> Self {
101        match ob_type {
102            TabObType::Basic(name) => ObType::Basic(name),
103            TabObType::Tabulator(m) => ObType::Tabulator(Box::new((*m).into())),
104        }
105    }
106}
107
108/// Convert from morphism type in a discrete tabulator theory.
109impl From<TabMorType<Ustr, Ustr>> for MorType {
110    fn from(mor_type: TabMorType<Ustr, Ustr>) -> Self {
111        match mor_type {
112            TabMorType::Basic(name) => MorType::Basic(name),
113            TabMorType::Hom(x) => MorType::Hom(Box::new((*x).into())),
114        }
115    }
116}
117
118/// Convert into object type in a discrete tabulator theory.
119impl TryFrom<ObType> for TabObType<Ustr, Ustr> {
120    type Error = String;
121
122    fn try_from(ob_type: ObType) -> Result<Self, Self::Error> {
123        match ob_type {
124            ObType::Basic(name) => Ok(TabObType::Basic(name)),
125            ObType::Tabulator(m) => (*m).try_into().map(|m| TabObType::Tabulator(Box::new(m))),
126        }
127    }
128}
129
130/// Convert into morphism type in a discrete tabulator theory.
131impl TryFrom<MorType> for TabMorType<Ustr, Ustr> {
132    type Error = String;
133
134    fn try_from(mor_type: MorType) -> Result<Self, Self::Error> {
135        match mor_type {
136            MorType::Basic(name) => Ok(TabMorType::Basic(name)),
137            MorType::Composite(_) => {
138                Err("Composites not yet implemented for tabulator theories".into())
139            }
140            MorType::Hom(x) => (*x).try_into().map(|x| TabMorType::Hom(Box::new(x))),
141        }
142    }
143}
144
145/** A box containing a double theory of any kind.
146
147Ideally the Wasm-bound [`DblTheory`] would just have a type parameter for the
148underlying double theory, but `wasm-bindgen` does not support
149[generics](https://github.com/rustwasm/wasm-bindgen/issues/3309). Instead, we
150explicitly enumerate the supported kinds of double theories in this enum.
151 */
152#[derive(From)]
153pub enum DblTheoryBox {
154    Discrete(Rc<theory::UstrDiscreteDblTheory>),
155    DiscreteTab(Rc<theory::UstrDiscreteTabTheory>),
156}
157
158/** Wasm bindings for a double theory.
159 */
160#[wasm_bindgen]
161pub struct DblTheory(#[wasm_bindgen(skip)] pub DblTheoryBox);
162
163#[wasm_bindgen]
164impl DblTheory {
165    /// Kind of double theory ("double doctrine").
166    #[wasm_bindgen(getter)]
167    pub fn kind(&self) -> String {
168        // TODO: Should return an enum so that we get type defs.
169        match &self.0 {
170            DblTheoryBox::Discrete(_) => "Discrete",
171            DblTheoryBox::DiscreteTab(_) => "DiscreteTab",
172        }
173        .into()
174    }
175
176    /// Source of a morphism type.
177    #[wasm_bindgen]
178    pub fn src(&self, mor_type: MorType) -> Result<ObType, String> {
179        all_the_same!(match &self.0 {
180            DblTheoryBox::[Discrete, DiscreteTab](th) => {
181                let m = mor_type.try_into()?;
182                Ok(th.src_type(&m).into())
183            }
184        })
185    }
186
187    /// Target of a morphism type.
188    #[wasm_bindgen]
189    pub fn tgt(&self, mor_type: MorType) -> Result<ObType, String> {
190        all_the_same!(match &self.0 {
191            DblTheoryBox::[Discrete, DiscreteTab](th) => {
192                let m = mor_type.try_into()?;
193                Ok(th.tgt_type(&m).into())
194            }
195        })
196    }
197}
198
199/** Mapping from object types to numerical indices.
200
201Like [`MorTypeIndex`], this struct just compensates for the lack of hash maps
202with arbitrary keys in JavaScript.
203 */
204#[wasm_bindgen]
205#[derive(Clone, Default)]
206pub struct ObTypeIndex(HashMap<ObType, usize>);
207
208#[wasm_bindgen]
209impl ObTypeIndex {
210    /// Creates a new object type index.
211    #[wasm_bindgen(constructor)]
212    pub fn new() -> Self {
213        Default::default()
214    }
215
216    /// Gets the index of an object type, if set.
217    #[wasm_bindgen]
218    pub fn get(&self, x: &ObType) -> Option<usize> {
219        self.0.get(x).copied()
220    }
221
222    /// Sets the index of an object type.
223    #[wasm_bindgen]
224    pub fn set(&mut self, x: ObType, i: usize) {
225        self.0.insert(x, i);
226    }
227}
228
229/** Mapping from morphism types to numerical indices.
230
231Like [`ObTypeIndex`], this struct just compensates for the lack of hash maps
232with arbitrary keys in JavaScript.
233 */
234#[wasm_bindgen]
235#[derive(Clone, Default)]
236pub struct MorTypeIndex(HashMap<MorType, usize>);
237
238#[wasm_bindgen]
239impl MorTypeIndex {
240    /// Creates a new morphism type index.
241    #[wasm_bindgen(constructor)]
242    pub fn new() -> Self {
243        Default::default()
244    }
245
246    /// Gets the index of a morphism type, if set.
247    #[wasm_bindgen]
248    pub fn get(&self, m: &MorType) -> Option<usize> {
249        self.0.get(m).copied()
250    }
251
252    /// Sets the index of a morphism type.
253    #[wasm_bindgen]
254    pub fn set(&mut self, m: MorType, i: usize) {
255        self.0.insert(m, i);
256    }
257}