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 if let Some(properties) = &content.properties {
118 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()) });
127
128 if let Some(overlay) = overlay {
129 let overlay_key = overlay.name.to_string(); 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 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 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 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 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 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}