rez_next_package/
serialization.rs1use crate::{Package, PythonAstParser};
4use rez_next_common::RezCoreError;
5use rez_next_version::Version;
6use serde_json;
7use serde_yaml;
8use std::collections::HashMap;
9use std::fs;
10use std::path::Path;
11
12#[derive(Debug, Clone)]
17pub enum PackageFormat {
18 Yaml,
20 Json,
22 Python,
24}
25
26impl PackageFormat {
27 pub fn from_extension(path: &Path) -> Option<Self> {
29 match path.extension()?.to_str()? {
30 "yaml" | "yml" => Some(Self::Yaml),
31 "json" => Some(Self::Json),
32 "py" => Some(Self::Python),
33 _ => None,
34 }
35 }
36
37 pub fn default_filename(&self) -> &'static str {
39 match self {
40 Self::Yaml => "package.yaml",
41 Self::Json => "package.json",
42 Self::Python => "package.py",
43 }
44 }
45}
46
47pub struct PackageSerializer;
49
50impl PackageSerializer {
51 pub fn load_from_file(path: &Path) -> Result<Package, RezCoreError> {
53 let format = PackageFormat::from_extension(path).ok_or_else(|| {
54 RezCoreError::PackageParse(format!("Unsupported file format: {}", path.display()))
55 })?;
56
57 let content = fs::read_to_string(path).map_err(|e| {
58 RezCoreError::PackageParse(format!("Failed to read file {}: {}", path.display(), e))
59 })?;
60
61 Self::load_from_string(&content, format)
62 }
63
64 pub fn load_from_string(content: &str, format: PackageFormat) -> Result<Package, RezCoreError> {
66 match format {
67 PackageFormat::Yaml => Self::load_from_yaml(content),
68 PackageFormat::Json => Self::load_from_json(content),
69 PackageFormat::Python => Self::load_from_python(content),
70 }
71 }
72
73 pub fn load_from_yaml(content: &str) -> Result<Package, RezCoreError> {
75 let data: HashMap<String, serde_yaml::Value> = serde_yaml::from_str(content)
76 .map_err(|e| RezCoreError::PackageParse(format!("Failed to parse YAML: {}", e)))?;
77
78 Self::load_from_yaml_data(data)
79 }
80
81 pub fn load_from_json(content: &str) -> Result<Package, RezCoreError> {
83 let data: HashMap<String, serde_json::Value> = serde_json::from_str(content)
84 .map_err(|e| RezCoreError::PackageParse(format!("Failed to parse JSON: {}", e)))?;
85
86 Self::load_from_data(data)
87 }
88
89 pub fn load_from_python(content: &str) -> Result<Package, RezCoreError> {
91 PythonAstParser::parse_package_py(content)
93 }
94
95 fn load_from_data<T>(data: HashMap<String, T>) -> Result<Package, RezCoreError>
97 where
98 T: Into<serde_json::Value>,
99 {
100 let json_data: HashMap<String, serde_json::Value> =
102 data.into_iter().map(|(k, v)| (k, v.into())).collect();
103
104 let name = json_data
106 .get("name")
107 .and_then(|v| v.as_str())
108 .ok_or_else(|| {
109 RezCoreError::PackageParse("Missing or invalid 'name' field".to_string())
110 })?
111 .to_string();
112
113 let mut package = Package::new(name);
114
115 if let Some(version_value) = json_data.get("version") {
117 if let Some(version_str) = version_value.as_str() {
118 let version = Version::parse(version_str)
119 .map_err(|e| RezCoreError::PackageParse(format!("Invalid version: {}", e)))?;
120 package.version = Some(version);
121 }
122 }
123
124 if let Some(description_value) = json_data.get("description") {
125 if let Some(description) = description_value.as_str() {
126 package.description = Some(description.to_string());
127 }
128 }
129
130 if let Some(authors_value) = json_data.get("authors") {
131 if let Some(authors_array) = authors_value.as_array() {
132 package.authors = authors_array
133 .iter()
134 .filter_map(|v| v.as_str())
135 .map(|s| s.to_string())
136 .collect();
137 }
138 }
139
140 if let Some(requires_value) = json_data.get("requires") {
141 if let Some(requires_array) = requires_value.as_array() {
142 package.requires = requires_array
143 .iter()
144 .filter_map(|v| v.as_str())
145 .map(|s| s.to_string())
146 .collect();
147 }
148 }
149
150 if let Some(build_requires_value) = json_data.get("build_requires") {
151 if let Some(build_requires_array) = build_requires_value.as_array() {
152 package.build_requires = build_requires_array
153 .iter()
154 .filter_map(|v| v.as_str())
155 .map(|s| s.to_string())
156 .collect();
157 }
158 }
159
160 if let Some(variants_value) = json_data.get("variants") {
161 if let Some(variants_array) = variants_value.as_array() {
162 package.variants = variants_array
163 .iter()
164 .filter_map(|v| v.as_array())
165 .map(|variant_array| {
166 variant_array
167 .iter()
168 .filter_map(|v| v.as_str())
169 .map(|s| s.to_string())
170 .collect()
171 })
172 .collect();
173 }
174 }
175
176 if let Some(tools_value) = json_data.get("tools") {
177 if let Some(tools_array) = tools_value.as_array() {
178 package.tools = tools_array
179 .iter()
180 .filter_map(|v| v.as_str())
181 .map(|s| s.to_string())
182 .collect();
183 }
184 }
185
186 package.validate()?;
188
189 Ok(package)
190 }
191
192 fn load_from_yaml_data(
194 data: HashMap<String, serde_yaml::Value>,
195 ) -> Result<Package, RezCoreError> {
196 let json_data: HashMap<String, serde_json::Value> = data
198 .into_iter()
199 .map(|(k, v)| (k, yaml_to_json_value(v)))
200 .collect();
201
202 let name = json_data
204 .get("name")
205 .and_then(|v| v.as_str())
206 .ok_or_else(|| {
207 RezCoreError::PackageParse("Missing or invalid 'name' field".to_string())
208 })?
209 .to_string();
210
211 let mut package = Package::new(name);
212
213 if let Some(version_value) = json_data.get("version") {
215 if let Some(version_str) = version_value.as_str() {
216 let version = Version::parse(version_str)
217 .map_err(|e| RezCoreError::PackageParse(format!("Invalid version: {}", e)))?;
218 package.version = Some(version);
219 }
220 }
221
222 if let Some(description_value) = json_data.get("description") {
223 if let Some(description) = description_value.as_str() {
224 package.description = Some(description.to_string());
225 }
226 }
227
228 if let Some(authors_value) = json_data.get("authors") {
229 if let Some(authors_array) = authors_value.as_array() {
230 package.authors = authors_array
231 .iter()
232 .filter_map(|v| v.as_str())
233 .map(|s| s.to_string())
234 .collect();
235 }
236 }
237
238 if let Some(requires_value) = json_data.get("requires") {
239 if let Some(requires_array) = requires_value.as_array() {
240 package.requires = requires_array
241 .iter()
242 .filter_map(|v| v.as_str())
243 .map(|s| s.to_string())
244 .collect();
245 }
246 }
247
248 if let Some(build_requires_value) = json_data.get("build_requires") {
249 if let Some(build_requires_array) = build_requires_value.as_array() {
250 package.build_requires = build_requires_array
251 .iter()
252 .filter_map(|v| v.as_str())
253 .map(|s| s.to_string())
254 .collect();
255 }
256 }
257
258 if let Some(variants_value) = json_data.get("variants") {
259 if let Some(variants_array) = variants_value.as_array() {
260 package.variants = variants_array
261 .iter()
262 .filter_map(|v| v.as_array())
263 .map(|variant_array| {
264 variant_array
265 .iter()
266 .filter_map(|v| v.as_str())
267 .map(|s| s.to_string())
268 .collect()
269 })
270 .collect();
271 }
272 }
273
274 if let Some(tools_value) = json_data.get("tools") {
275 if let Some(tools_array) = tools_value.as_array() {
276 package.tools = tools_array
277 .iter()
278 .filter_map(|v| v.as_str())
279 .map(|s| s.to_string())
280 .collect();
281 }
282 }
283
284 package.validate()?;
286
287 Ok(package)
288 }
289
290 pub fn save_to_file(
292 package: &Package,
293 path: &Path,
294 format: PackageFormat,
295 ) -> Result<(), RezCoreError> {
296 let content = Self::save_to_string(package, format)?;
297
298 fs::write(path, content).map_err(|e| {
299 RezCoreError::PackageParse(format!("Failed to write file {}: {}", path.display(), e))
300 })
301 }
302
303 pub fn save_to_string(
305 package: &Package,
306 format: PackageFormat,
307 ) -> Result<String, RezCoreError> {
308 match format {
309 PackageFormat::Yaml => Self::save_to_yaml(package),
310 PackageFormat::Json => Self::save_to_json(package),
311 PackageFormat::Python => Self::save_to_python(package),
312 }
313 }
314
315 pub fn save_to_yaml(package: &Package) -> Result<String, RezCoreError> {
317 serde_yaml::to_string(package)
318 .map_err(|e| RezCoreError::PackageParse(format!("Failed to serialize to YAML: {}", e)))
319 }
320
321 pub fn save_to_json(package: &Package) -> Result<String, RezCoreError> {
323 serde_json::to_string_pretty(package)
324 .map_err(|e| RezCoreError::PackageParse(format!("Failed to serialize to JSON: {}", e)))
325 }
326
327 pub fn save_to_python(package: &Package) -> Result<String, RezCoreError> {
329 let mut content = String::new();
330
331 content.push_str(&format!("name = \"{}\"\n", package.name));
332
333 if let Some(ref version) = package.version {
334 content.push_str(&format!("version = \"{}\"\n", version.as_str()));
335 }
336
337 if let Some(ref description) = package.description {
338 content.push_str(&format!("description = \"{}\"\n", description));
339 }
340
341 if !package.authors.is_empty() {
342 content.push_str("authors = [\n");
343 for author in &package.authors {
344 content.push_str(&format!(" \"{}\",\n", author));
345 }
346 content.push_str("]\n");
347 }
348
349 if !package.requires.is_empty() {
350 content.push_str("requires = [\n");
351 for req in &package.requires {
352 content.push_str(&format!(" \"{}\",\n", req));
353 }
354 content.push_str("]\n");
355 }
356
357 if !package.build_requires.is_empty() {
358 content.push_str("build_requires = [\n");
359 for req in &package.build_requires {
360 content.push_str(&format!(" \"{}\",\n", req));
361 }
362 content.push_str("]\n");
363 }
364
365 if !package.variants.is_empty() {
366 content.push_str("variants = [\n");
367 for variant in &package.variants {
368 content.push_str(" [");
369 for (i, req) in variant.iter().enumerate() {
370 if i > 0 {
371 content.push_str(", ");
372 }
373 content.push_str(&format!("\"{}\"", req));
374 }
375 content.push_str("],\n");
376 }
377 content.push_str("]\n");
378 }
379
380 if !package.tools.is_empty() {
381 content.push_str("tools = [\n");
382 for tool in &package.tools {
383 content.push_str(&format!(" \"{}\",\n", tool));
384 }
385 content.push_str("]\n");
386 }
387
388 Ok(content)
389 }
390}
391
392fn yaml_to_json_value(yaml_value: serde_yaml::Value) -> serde_json::Value {
394 match yaml_value {
395 serde_yaml::Value::Null => serde_json::Value::Null,
396 serde_yaml::Value::Bool(b) => serde_json::Value::Bool(b),
397 serde_yaml::Value::Number(n) => {
398 if let Some(i) = n.as_i64() {
399 serde_json::Value::Number(serde_json::Number::from(i))
400 } else if let Some(f) = n.as_f64() {
401 serde_json::Number::from_f64(f)
402 .map(serde_json::Value::Number)
403 .unwrap_or(serde_json::Value::Null)
404 } else {
405 serde_json::Value::Null
406 }
407 }
408 serde_yaml::Value::String(s) => serde_json::Value::String(s),
409 serde_yaml::Value::Sequence(seq) => {
410 let json_array: Vec<serde_json::Value> =
411 seq.into_iter().map(yaml_to_json_value).collect();
412 serde_json::Value::Array(json_array)
413 }
414 serde_yaml::Value::Mapping(map) => {
415 let mut json_object = serde_json::Map::new();
416 for (k, v) in map {
417 if let serde_yaml::Value::String(key) = k {
418 json_object.insert(key, yaml_to_json_value(v));
419 }
420 }
421 serde_json::Value::Object(json_object)
422 }
423 serde_yaml::Value::Tagged(_) => serde_json::Value::Null, }
425}