project_expand/tests/common/
json_desc.rs1use super::Language;
2use std::fs;
3use std::path::{Path, PathBuf};
4use yamake::c_nodes::{AFile, CFile, HFile, OFile};
5use yamake::command::log_build;
6use yamake::model::{Edge, ExpandError, ExpandResult, GNode};
7
8pub struct JsonDesc {
9 pub name: String,
10}
11
12impl JsonDesc {
13 pub fn new(name: &str) -> Self {
14 Self {
15 name: name.to_string(),
16 }
17 }
18}
19
20impl GNode for JsonDesc {
21 fn tag(&self) -> String {
22 "JsonDesc".to_string()
23 }
24
25 fn pathbuf(&self) -> PathBuf {
26 PathBuf::from(&self.name)
27 }
28
29 fn build(&self, sandbox: &Path, predecessors: &[&(dyn GNode + Send + Sync)]) -> bool {
30 assert!(
31 predecessors.len() == 1,
32 "JsonDesc expects exactly one predecessor, got {}",
33 predecessors.len()
34 );
35
36 let predecessor = predecessors[0];
37 assert!(
38 predecessor.tag() == "YmlDesc",
39 "JsonDesc predecessor must be YmlDesc, got {}",
40 predecessor.tag()
41 );
42
43 let input_path = sandbox.join(predecessor.pathbuf());
44 let yaml_content = fs::read_to_string(&input_path)
45 .unwrap_or_else(|e| panic!("Failed to read {}: {}", input_path.display(), e));
46
47 let languages: Vec<Language> = serde_yaml::from_str(&yaml_content)
48 .unwrap_or_else(|e| panic!("Failed to parse YAML {}: {}", input_path.display(), e));
49
50 let json_content = serde_json::to_string_pretty(&languages)
51 .unwrap_or_else(|e| panic!("Failed to serialize to JSON: {}", e));
52
53 let output_path = sandbox.join(&self.name);
54 if let Some(parent) = output_path.parent() {
55 fs::create_dir_all(parent).ok();
56 }
57
58 fs::write(&output_path, &json_content)
59 .unwrap_or_else(|e| panic!("Failed to write {}: {}", output_path.display(), e));
60
61 let description = format!(
63 "Convert YAML to JSON: {} -> {}",
64 input_path.display(),
65 output_path.display()
66 );
67 let stdout = format!(
68 "Read {} languages from {}\nWrote {} bytes to {}\n",
69 languages.len(),
70 input_path.display(),
71 json_content.len(),
72 output_path.display()
73 );
74 log_build(sandbox, &self.name, &description, &stdout, "");
75
76 true
77 }
78
79 fn expand(&self, sandbox: &Path, _predecessors: &[&(dyn GNode + Send + Sync)]) -> ExpandResult {
80 let input_path = sandbox.join(&self.name);
81 let json_content = fs::read_to_string(&input_path)
82 .map_err(|e| ExpandError::ReadError(input_path.clone(), e))?;
83
84 let languages: Vec<Language> = serde_json::from_str(&json_content).map_err(|e| {
85 ExpandError::ParseError(format!(
86 "Failed to parse JSON {}: {}",
87 input_path.display(),
88 e
89 ))
90 })?;
91
92 let generated_dir = sandbox.join("project_expand/generated");
93 fs::create_dir_all(&generated_dir).ok();
94
95 let mut nodes: Vec<Box<dyn GNode + Send + Sync>> = Vec::new();
96 let mut edges: Vec<Edge> = Vec::new();
97
98 let lib_path = "project_expand/generated/liblangs.a";
99
100 for lang in &languages {
101 let c_path = format!("project_expand/generated/{}.c", lang.language);
103 let c_content = format!(
104 "const char* helloworld_{}=\"{}\" ;\nconst char* get_{}() {{ return helloworld_{} ; }}\n",
105 lang.language, lang.helloworld, lang.language, lang.language
106 );
107 let c_full_path = sandbox.join(&c_path);
108 fs::write(&c_full_path, &c_content)
109 .map_err(|e| ExpandError::WriteError(c_full_path, e))?;
110 nodes.push(Box::new(CFile::new(&c_path)));
111
112 let h_path = format!("project_expand/generated/{}.h", lang.language);
114 let h_content = format!("const char* get_{}() ;\n", lang.language);
115 let h_full_path = sandbox.join(&h_path);
116 fs::write(&h_full_path, &h_content)
117 .map_err(|e| ExpandError::WriteError(h_full_path, e))?;
118 nodes.push(Box::new(HFile::new(&h_path)));
119
120 let o_path = format!("project_expand/generated/{}.o", lang.language);
122 nodes.push(Box::new(OFile::new(&o_path, vec![], vec![])));
123
124 edges.push(Edge {
126 nfrom: Box::new(JsonDesc::new(&self.name)),
127 nto: Box::new(CFile::new(&c_path)),
128 });
129 edges.push(Edge {
130 nfrom: Box::new(JsonDesc::new(&self.name)),
131 nto: Box::new(HFile::new(&h_path)),
132 });
133
134 edges.push(Edge {
136 nfrom: Box::new(CFile::new(&c_path)),
137 nto: Box::new(OFile::new(&o_path, vec![], vec![])),
138 });
139 edges.push(Edge {
140 nfrom: Box::new(OFile::new(&o_path, vec![], vec![])),
141 nto: Box::new(AFile::new(lib_path)),
142 });
143 }
144
145 let languages_h_path = PathBuf::from("project_expand/generated/languages.h");
147 let mut h_content = String::new();
148 h_content.push_str("#ifndef LANGUAGES_H\n");
149 h_content.push_str("#define LANGUAGES_H\n\n");
150
151 for lang in &languages {
152 h_content.push_str(&format!(
153 "#include \"project_expand/generated/{}.h\"\n",
154 lang.language
155 ));
156 }
157 h_content.push_str("\n");
158
159 h_content.push_str(&format!("#define N_languages {}\n\n", languages.len()));
160
161 h_content.push_str("typedef const char* (*get_language_fn)() ;\n\n");
162 h_content.push_str("get_language_fn languages[] = {\n");
163 for lang in &languages {
164 h_content.push_str(&format!(" get_{},\n", lang.language));
165 }
166 h_content.push_str("} ;\n\n");
167
168 h_content.push_str("#endif\n");
169
170 let languages_h_full_path = sandbox.join(&languages_h_path);
171 fs::write(&languages_h_full_path, &h_content)
172 .map_err(|e| ExpandError::WriteError(languages_h_full_path, e))?;
173 nodes.push(Box::new(HFile::new(&languages_h_path.to_string_lossy())));
174
175 edges.push(Edge {
177 nfrom: Box::new(JsonDesc::new(&self.name)),
178 nto: Box::new(HFile::new(&languages_h_path.to_string_lossy())),
179 });
180
181 Ok((nodes, edges))
182 }
183}