transformation_file/
build.rs1use crate::state::Transformation;
2use oca_ast_transformation::ast;
3
4#[derive(Debug, Clone, serde::Serialize)]
5pub struct FromASTError {
6 pub line_number: usize,
7 pub raw_line: String,
8 pub message: String,
9}
10
11#[derive(thiserror::Error, Debug, Clone, serde::Serialize)]
12#[serde(untagged)]
13pub enum Error {
14 #[error("Error at line {line_number} ({raw_line}): {message}")]
15 FromASTError {
16 #[serde(rename = "ln")]
17 line_number: usize,
18 #[serde(rename = "c")]
19 raw_line: String,
20 #[serde(rename = "e")]
21 message: String,
22 },
23}
24
25pub fn from_ast(ast: &ast::TransformationAST) -> Result<Transformation, Vec<Error>> {
26 let mut errors = vec![];
27
28 let mut base: Option<Transformation> = None;
29 if !ast.meta.is_empty() {
30 let source = ast.meta.get("source").expect("missing source meta");
31 let source_said = source.replace("refs:", "");
32 let target = ast.meta.get("target").expect("missing target meta");
33 let target_said = target.replace("refs:", "");
34 let mut transformation = Transformation::new();
35 transformation.set_source(source_said);
36 transformation.set_target(target_said);
37 base = Some(transformation)
38 }
39
40 let default_command_meta = ast::CommandMeta {
41 line_number: 0,
42 raw_line: "unknown".to_string(),
43 };
44 for (i, command) in ast.commands.iter().enumerate() {
45 let command_index = i;
46 let command_meta = ast
48 .commands_meta
49 .get(&command_index)
50 .unwrap_or(&default_command_meta);
51 match apply_command(base.clone(), command.clone()) {
52 Ok(transformation) => {
53 base = Some(transformation);
54 }
55 Err(mut err) => {
56 errors.extend(err.iter_mut().map(|e| Error::FromASTError {
57 line_number: command_meta.line_number,
58 raw_line: command_meta.raw_line.clone(),
59 message: e.clone(),
60 }));
61 }
62 }
63 }
64 if errors.is_empty() {
65 let mut transformation = base.unwrap().clone();
66 transformation.fill_said();
67 Ok(transformation)
68 } else {
69 Err(errors)
70 }
71}
72
73pub fn apply_command(
74 base: Option<Transformation>,
75 op: ast::Command,
76) -> Result<Transformation, Vec<String>> {
77 let errors = vec![];
78 let mut transformation: Transformation = match base {
79 Some(transformation) => transformation,
80 None => Transformation::new(),
81 };
82
83 match (op.kind, op.object_kind) {
84 (ast::CommandType::Rename, ast::ObjectKind::Rename(content)) => {
85 if let Some(attributes) = content.attributes {
86 transformation.rename(attributes);
87 }
88 }
89 (ast::CommandType::Link, ast::ObjectKind::Link(content)) => {
90 if let Some(attributes) = content.attributes {
91 transformation.link(attributes);
92 }
93 }
94 _ => {}
95 }
96
97 if errors.is_empty() {
98 Ok(transformation)
99 } else {
100 Err(errors)
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use std::collections::HashMap;
107
108 use super::*;
109 use indexmap::IndexMap;
110 use said::{derivation::HashFunctionCode, sad::SerializationFormats, version::Encode};
111
112 #[test]
113 fn build_from_ast() {
114 let mut commands = vec![];
115
116 let mut attributes = IndexMap::new();
117 attributes.insert("digest".to_string(), "d".to_string());
118
119 commands.push(ast::Command {
120 kind: ast::CommandType::Rename,
121 object_kind: ast::ObjectKind::Rename(ast::RenameContent {
122 attributes: Some(attributes),
123 }),
124 });
125
126 let ast = ast::TransformationAST {
127 version: "1.0".to_string(),
128 commands,
129 commands_meta: IndexMap::new(),
130 meta: HashMap::new(),
131 };
132
133 let build_result = from_ast(&ast);
134 match build_result {
135 Ok(transformation) => {
136 let code = HashFunctionCode::Blake3_256;
137 let format = SerializationFormats::JSON;
138 let transformation_encoded = transformation.encode(&code, &format).unwrap();
139 let transformation_json = String::from_utf8(transformation_encoded).unwrap();
140 println!("{}", transformation_json);
141 }
142 Err(e) => {
143 println!("{:?}", e);
144 }
145 }
146 }
147}