Skip to main content

oca_dag/
lib.rs

1pub mod data_storage;
2pub mod versioning;
3
4use oca_ast::ast;
5use said::{SelfAddressingIdentifier, derivation::HashFunction};
6use serde::Deserialize;
7use std::collections::HashMap;
8use std::str::FromStr;
9
10#[derive(Debug, Clone)]
11pub struct CommandNode {
12    pub digest: SelfAddressingIdentifier,
13    pub json: String,
14}
15impl CommandNode {
16    fn new(command: ast::Command) -> Self {
17        Self {
18            digest: Self::calculate_command_digest(&command),
19            json: serde_json::to_string(&command).unwrap(),
20        }
21    }
22
23    fn calculate_command_digest(command: &ast::Command) -> SelfAddressingIdentifier {
24        let command_json = serde_json::to_string(command).unwrap();
25        let hash_algorithm = HashFunction::from_str("E").unwrap();
26        hash_algorithm.derive(command_json.as_bytes())
27    }
28}
29
30#[derive(Debug, Clone)]
31pub struct CaptureBaseNode {
32    pub capture_base_said: SelfAddressingIdentifier,
33    pub parent: Option<SelfAddressingIdentifier>,
34    pub command_digest: SelfAddressingIdentifier,
35}
36
37#[derive(Debug, Clone)]
38pub struct OverlayNode {
39    pub overlay_said: SelfAddressingIdentifier,
40    pub parent: Option<SelfAddressingIdentifier>,
41    pub command_digest: SelfAddressingIdentifier,
42}
43
44#[derive(Debug, Clone, Deserialize)]
45pub struct OCABundleNode {
46    pub oca_bundle_said: SelfAddressingIdentifier,
47    pub parent: Option<SelfAddressingIdentifier>,
48    pub capture_base_said: SelfAddressingIdentifier,
49    pub overlays_said: Vec<SelfAddressingIdentifier>,
50}
51
52#[derive(Debug, Clone)]
53struct State {
54    oca_bundle: Option<SelfAddressingIdentifier>,
55    capture_base: Option<SelfAddressingIdentifier>,
56    overlays: HashMap<String, SelfAddressingIdentifier>,
57}
58impl State {
59    fn new() -> Self {
60        Self {
61            oca_bundle: None,
62            capture_base: None,
63            overlays: HashMap::new(),
64        }
65    }
66}
67
68#[derive(Debug)]
69pub struct ResultModel {
70    pub command: Option<CommandNode>,
71    pub oca_bundle: Option<OCABundleNode>,
72    pub capture_base: Option<CaptureBaseNode>,
73    pub overlay: Option<OverlayNode>,
74}
75impl ResultModel {
76    fn new() -> Self {
77        Self {
78            command: None,
79            oca_bundle: None,
80            capture_base: None,
81            overlay: None,
82        }
83    }
84}
85
86pub fn build_core_db_model(oca_build: &oca_bundle::build::OCABuild) -> Vec<ResultModel> {
87    let mut state = State::new();
88    let mut result_models = vec![];
89
90    for step in &oca_build.steps {
91        let (new_state, result_model) = apply_step(state, step);
92        state = new_state;
93        result_models.push(result_model);
94    }
95
96    result_models
97}
98
99fn apply_step(state: State, step: &oca_bundle::build::OCABuildStep) -> (State, ResultModel) {
100    let mut current_state = state.clone();
101    let mut result = ResultModel::new();
102    let command_model = CommandNode::new(step.command.clone());
103    result.command = Some(command_model.clone());
104
105    match &step.command.object_kind {
106        ast::ObjectKind::CaptureBase(_) => {
107            let capture_base_model = CaptureBaseNode {
108                capture_base_said: step.result.capture_base.digest.clone().unwrap(),
109                parent: state.capture_base,
110                command_digest: command_model.digest,
111            };
112            result.capture_base = Some(capture_base_model.clone());
113            current_state.capture_base = Some(capture_base_model.capture_base_said.clone());
114        }
115        ast::ObjectKind::Overlay(content) => {
116            // match &content.properties {
117            if let Some(properties) = &content.properties {
118                // TODO find out what is going on here and where it should be used
119                if let Some(ast::NestedValue::Value(lang_value)) = properties.get("lang") {
120                    isolang::Language::from_639_1(lang_value);
121                }
122            }
123
124            let overlay = step.result.overlays.iter().find(|overlay| {
125                overlay.name.eq(&content.overlay_def.get_name()) // TODO fetch UNIQUE ATTR AND use here && overlay.language() == lang.as_ref()
126            });
127
128            if let Some(overlay) = overlay {
129                // let overlay_key = match overlay.language() {
130                //     Some(lang) => {
131                //         format!("{}-{}", &overlay.overlay_type(), lang.to_639_1().unwrap())
132                //     }
133                //     None => format!("{}", overlay.overlay_type()),
134                // };
135                let overlay_key = overlay.name.to_string(); // TODO fetch UNIQUE ATTR AND use here
136                let parent_overlay = state.overlays.get(&overlay_key);
137                let overlay_model = OverlayNode {
138                    overlay_said: overlay.digest.clone().unwrap(),
139                    parent: parent_overlay.cloned(),
140                    command_digest: command_model.digest,
141                };
142                result.overlay = Some(overlay_model.clone());
143                current_state
144                    .overlays
145                    .insert(overlay_key, overlay_model.overlay_said.clone());
146            }
147        }
148        ast::ObjectKind::OCABundle(_) => {
149            dbg!("OCABundle");
150        }
151    }
152
153    let oca_bundle_model = OCABundleNode {
154        oca_bundle_said: step.result.digest.clone().unwrap(),
155        parent: state.oca_bundle,
156        capture_base_said: step.result.capture_base.digest.clone().unwrap(),
157        overlays_said: step
158            .result
159            .overlays
160            .iter()
161            .map(|overlay| overlay.digest.clone().unwrap())
162            .collect(),
163    };
164    result.oca_bundle = Some(oca_bundle_model.clone());
165    current_state.oca_bundle = Some(oca_bundle_model.oca_bundle_said.clone());
166
167    (current_state, result)
168}
169
170#[cfg(test)]
171mod tests {
172    use super::*;
173    use indexmap::{IndexMap, indexmap};
174    use oca_ast::ast::OverlayContent;
175    use overlay_file::OverlayDef;
176
177    #[test]
178    #[ignore]
179    fn test_build_core_db_model() -> Result<(), Vec<String>> {
180        let mut commands = vec![];
181
182        // 1. ADD ATTR abc
183        commands.push(ast::Command {
184            kind: ast::CommandType::Add,
185            object_kind: ast::ObjectKind::CaptureBase(ast::CaptureContent {
186                attributes: Some(indexmap! {
187                    "abc".to_string() => ast::NestedAttrType::Value(ast::AttributeType::Text)
188                }),
189            }),
190        });
191
192        // 2. add label en abc "ble"
193        commands.push(ast::Command {
194            kind: ast::CommandType::Add,
195            object_kind: ast::ObjectKind::Overlay(OverlayContent {
196                properties: Some(indexmap! {
197                    "lang".to_string() => ast::NestedValue::Value("en".to_string()),
198                    "abc".to_string() => ast::NestedValue::Value("ble".to_string())
199                }),
200                overlay_def: OverlayDef::default(),
201            }),
202        });
203
204        // 3. add attr def
205        commands.push(ast::Command {
206            kind: ast::CommandType::Add,
207            object_kind: ast::ObjectKind::CaptureBase(ast::CaptureContent {
208                attributes: Some(indexmap! {
209                    "def".to_string() => ast::NestedAttrType::Value(ast::AttributeType::Text)
210                }),
211            }),
212        });
213
214        // 4. add label fr abc "ble"
215        commands.push(ast::Command {
216            kind: ast::CommandType::Add,
217            object_kind: ast::ObjectKind::Overlay(OverlayContent {
218                properties: Some(indexmap! {
219                    "lang".to_string() => ast::NestedValue::Value("fr".to_string()),
220                    "abc".to_string() => ast::NestedValue::Value("ble".to_string())
221                }),
222                overlay_def: OverlayDef::default(),
223            }),
224        });
225
226        // 5. update attr "en" abc "bererg"
227        /*
228        commands.push(
229            ast::Command {
230                kind: ast::CommandType::Modify,
231                object_kind: ast::ObjectKind::Overlay(ast::OverlayType::Label),
232                content: Some(ast::Content {
233                    attributes: Some(indexmap! {
234                        "abc".to_string() => ast::NestedValue::Value("bererg".to_string())
235                    }),
236                    properties: Some(indexmap! {
237                        "lang".to_string() => ast::NestedValue::Value("en".to_string())
238                    }),
239                }),
240            }
241        );
242        */
243
244        let ast = ast::OCAAst {
245            version: "0.1.0".to_string(),
246            commands,
247            commands_meta: IndexMap::new(),
248            meta: HashMap::new(),
249        };
250        let oca_build = oca_bundle::build::from_ast(None, &ast).unwrap();
251
252        let result = build_core_db_model(&oca_build);
253        assert_eq!(result.len(), 4);
254        assert_eq!(
255            result[0].command.clone().unwrap().digest.to_string(),
256            "EJnPP-iVzRutbWJF4tt3pX_89NB77854DMyYdl_aVaWH"
257        );
258        assert!(result[0].oca_bundle.is_some());
259        assert!(result[0].capture_base.is_some());
260        assert!(result[0].overlay.is_none());
261
262        assert!(result[3].oca_bundle.is_some());
263        assert!(result[3].capture_base.is_none());
264        assert!(result[3].overlay.is_some());
265
266        Ok(())
267    }
268}