1use std::{collections::HashMap, fmt, io};
31
32use serde::Deserialize;
33
34use crate::builder::ProcDesc;
35
36#[derive(Debug, PartialEq)]
38pub struct ComponentVersion<'a> {
39 pub name: String,
41 pub crate_name: &'a str,
43 pub version: &'a str,
45}
46
47impl fmt::Display for ComponentVersion<'_> {
48 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
49 if f.alternate() {
50 writeln!(f, "{}", self.name)?;
51 writeln!(f, " crate {}", self.crate_name)?;
52 writeln!(f, " version {}", self.version)
53 } else {
54 write!(
55 f,
56 "{} = {{ crate = {}, version = {} }}",
57 self.name, self.crate_name, self.version
58 )
59 }
60 }
61}
62
63#[derive(Debug, Clone, Deserialize)]
65pub struct Metadata {
66 #[serde(skip_deserializing)]
68 pub name: Option<String>,
69 #[serde(skip_deserializing)]
71 pub description: Option<String>,
72 pub proc: Option<String>,
74 pub settings: Option<String>,
76 pub adaptor: Option<Vec<String>>,
78}
79
80impl Metadata {
81 fn specify(&mut self, crate_name: &str, description: Option<String>) {
83 self.description = description;
84 let crate_prefix = format!("{crate_name}::");
85
86 if let Some(proc) = &mut self.proc {
87 proc.insert_str(0, crate_prefix.as_str());
88 }
89
90 if let Some(settings) = &mut self.settings {
91 settings.insert_str(0, crate_prefix.as_str());
92 }
93
94 if let Some(adaptor) = &mut self.adaptor {
95 for adaptor in adaptor {
96 adaptor.insert_str(0, crate_prefix.as_str());
97 }
98 }
99 }
100
101 pub fn merge(&mut self, prosa_metadata: Metadata) {
103 if self.proc.is_none() {
104 self.proc = prosa_metadata.proc;
105 }
106
107 if self.settings.is_none() {
108 self.settings = prosa_metadata.settings;
109 }
110
111 if let Some(adaptor_list) = &mut self.adaptor {
112 if let Some(prosa_adaptor_list) = prosa_metadata.adaptor {
113 adaptor_list.extend(prosa_adaptor_list);
114 }
115 } else {
116 self.adaptor = prosa_metadata.adaptor;
117 }
118 }
119
120 pub fn match_proc(&self, name: &str, crate_name: Option<&str>) -> Option<&String> {
122 if let Some(proc) = &self.proc {
123 let proc_name = proc.replace('-', "_");
124 if let Some(crate_name) = crate_name {
125 let proc_name = format!("{crate_name}::{proc_name}");
126 if proc_name.contains(name) {
127 return Some(proc);
128 }
129 } else if proc_name.contains(name) {
130 return Some(proc);
131 }
132 }
133
134 None
135 }
136
137 pub fn find_adaptor(&self, name: &str, crate_name: Option<&str>) -> Option<&String> {
139 if let Some(adaptors) = &self.adaptor {
140 for adaptor in adaptors {
141 let adaptor_name = adaptor.replace('-', "_");
142 if let Some(crate_name) = crate_name {
143 let adaptor_name = format!("{crate_name}::{adaptor_name}");
144 if adaptor_name.contains(name) {
145 return Some(adaptor);
146 }
147 } else if adaptor_name.contains(name) {
148 return Some(adaptor);
149 }
150 }
151 }
152
153 None
154 }
155
156 pub fn get_proc_desc(
158 &self,
159 adaptor_name: Option<&str>,
160 crate_name: Option<&str>,
161 ) -> Result<ProcDesc, String> {
162 let name = if let Some(name) = &self.name {
163 Some(name.as_str())
164 } else {
165 self.proc
166 .as_ref()
167 .and_then(|p| p.rsplit_once(':').map(|(_, proc_name)| proc_name))
168 }
169 .ok_or(String::from("Missing ProSA `name` metadata"))?;
170
171 let adaptor = if let Some(adaptor) =
172 adaptor_name.and_then(|name| self.find_adaptor(name, crate_name))
173 {
174 Some(adaptor)
175 } else if let Some(adaptors) = &self.adaptor {
176 adaptors.first()
177 } else {
178 None
179 }
180 .ok_or(format!("Can't find a ProSA `adaptor` for {name}"))?;
181
182 Ok(ProcDesc {
183 name: None,
184 proc_name: name.into(),
185 proc: self
186 .proc
187 .clone()
188 .map(|p| p.replace('-', "_"))
189 .ok_or(format!("Missing ProSA `proc` metadata for {name}"))?,
190 adaptor: adaptor.replace('-', "_"),
191 })
192 }
193}
194
195impl fmt::Display for Metadata {
196 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
197 if let Some(proc) = &self.proc {
198 writeln!(f, " Processor {proc}")?;
199 }
200
201 if let Some(settings) = &self.settings {
202 writeln!(f, " Settings {settings}")?;
203 }
204
205 if let Some(adaptor) = &self.adaptor {
206 writeln!(f, " Adaptor:")?;
207 for adaptor in adaptor {
208 writeln!(f, " - {adaptor}")?;
209 }
210 }
211
212 Ok(())
213 }
214}
215
216#[derive(Debug, Deserialize)]
218pub struct PackageMetadata {
219 pub name: String,
221 pub version: String,
223 pub license: Option<String>,
225 pub description: Option<String>,
227 pub documentation: Option<String>,
229 pub authors: Vec<String>,
231
232 metadata: Option<HashMap<String, serde_json::Value>>,
234 targets: Option<Vec<HashMap<String, serde_json::Value>>>,
236}
237
238impl PackageMetadata {
239 pub fn contain_metadata(&self, name: &str) -> bool {
241 if let Some(metadata) = &self.metadata {
242 metadata.contains_key(name)
243 } else {
244 false
245 }
246 }
247
248 pub fn get_targets(&self, kind: &str) -> Option<Vec<String>> {
250 if let Some(targets) = &self.targets {
251 let kind_tera_value = tera::Value::String(kind.to_string());
252 let mut binary_targets = Vec::new();
253 for target in targets {
254 if let Some(kinds) = target.get("kind").and_then(|k| k.as_array())
255 && kinds.contains(&kind_tera_value)
256 && let Some(name) = target.get("name").and_then(|k| k.as_str())
257 {
258 binary_targets.push(name.to_string());
259 }
260 }
261
262 if binary_targets.is_empty() {
263 None
264 } else {
265 Some(binary_targets)
266 }
267 } else {
268 None
269 }
270 }
271
272 pub fn is_prosa(&self) -> bool {
274 self.contain_metadata("prosa")
275 }
276
277 pub fn get_prosa_proc_metadata(&self) -> Option<HashMap<&str, Metadata>> {
279 if let Some(metadata) = self
280 .metadata
281 .as_ref()
282 .and_then(|m| m.get("prosa").and_then(|w| w.as_object()))
283 {
284 let mut proc_metadata = HashMap::new();
285 for (name, data) in metadata {
286 if name != "main"
287 && name != "tvf"
288 && let Ok(prosa_metadata) = serde_json::from_value::<Metadata>(data.clone())
289 {
290 proc_metadata.insert(name.as_str(), prosa_metadata);
291 }
292 }
293
294 Some(proc_metadata)
295 } else {
296 None
297 }
298 }
299
300 fn get_prosa_metadata(&self, ty: &str) -> Vec<String> {
302 let mut meta_list: Vec<String> = Vec::new();
303 if let Some(metadata) = self
304 .metadata
305 .as_ref()
306 .and_then(|m| m.get("prosa").and_then(|w| w.as_object()))
307 {
308 for (meta_name, data) in metadata {
309 if meta_name == ty
310 && let Ok(prosa_metadata) =
311 serde_json::from_value::<Vec<String>>(data.clone()).map(|v| v.into_iter())
312 {
313 meta_list.append(
314 &mut prosa_metadata
315 .map(|w| format!("{}::{}", self.name.replace('-', "_"), w))
316 .collect::<Vec<String>>(),
317 );
318 }
319 }
320 }
321
322 meta_list
323 }
324
325 pub fn get_prosa_main(&self) -> Vec<String> {
327 self.get_prosa_metadata("main")
328 }
329
330 pub fn get_prosa_tvf(&self) -> Vec<String> {
332 self.get_prosa_metadata("tvf")
333 }
334
335 fn get_version<'a>(&'a self, name: &str, ty: &str) -> Option<ComponentVersion<'a>> {
337 if let Some(metadata) = self
338 .metadata
339 .as_ref()
340 .and_then(|m| m.get("prosa").and_then(|w| w.as_object()))
341 {
342 for (meta_name, data) in metadata {
343 if meta_name != "main" && meta_name != "tvf" && ty != "main" && ty != "tvf" {
344 if let Ok(prosa_metadata) = serde_json::from_value::<Metadata>(data.clone()) {
345 if ty == "proc" {
346 if let Some(proc_name) = prosa_metadata.match_proc(
347 name.replace('-', "_").as_str(),
348 Some(self.name.replace('-', "_").as_str()),
349 ) {
350 return Some(ComponentVersion {
351 name: proc_name.clone(),
352 crate_name: &self.name,
353 version: &self.version,
354 });
355 }
356 } else if ty == "adaptor"
357 && let Some(adaptor_name) = prosa_metadata.find_adaptor(
358 name.replace('-', "_").as_str(),
359 Some(self.name.replace('-', "_").as_str()),
360 )
361 {
362 return Some(ComponentVersion {
363 name: adaptor_name.clone(),
364 crate_name: &self.name,
365 version: &self.version,
366 });
367 }
368 }
369 } else if meta_name == ty
370 && let Some(component_name) =
371 serde_json::from_value::<Vec<String>>(data.clone())
372 .ok()
373 .and_then(|m| {
374 m.into_iter().find(|w| {
375 format!("{}::{}", self.name.replace('-', "_"), w).contains(name)
376 })
377 })
378 {
379 return Some(ComponentVersion {
380 name: component_name,
381 crate_name: &self.name,
382 version: &self.version,
383 });
384 }
385 }
386 }
387
388 None
389 }
390
391 pub fn get_main_version<'a>(&'a self, name: &str) -> Option<ComponentVersion<'a>> {
393 self.get_version(name, "main")
394 }
395
396 pub fn get_tvf_version<'a>(&'a self, name: &str) -> Option<ComponentVersion<'a>> {
398 self.get_version(name, "tvf")
399 }
400
401 pub fn get_proc_version<'a>(&'a self, name: &str) -> Option<ComponentVersion<'a>> {
403 self.get_version(name, "proc")
404 }
405
406 pub fn get_adaptor_version<'a>(&'a self, name: &str) -> Option<ComponentVersion<'a>> {
408 self.get_version(name, "adaptor")
409 }
410
411 pub fn j2_context(&self, ctx: &mut tera::Context) {
413 ctx.insert("name", &self.name);
414 ctx.insert("version", &self.version);
415 if let Some(license) = &self.license {
416 ctx.insert("license", license);
417 }
418 if let Some(desc) = &self.description {
419 ctx.insert("description", desc);
420 }
421 if let Some(doc) = &self.documentation {
422 ctx.insert("documentation", doc);
423 }
424 if !self.authors.is_empty() {
425 ctx.insert("authors", &self.authors);
426 }
427
428 if let Some(metadata) = &self.metadata {
429 ctx.insert("deb_pkg", &metadata.contains_key("deb"));
430 ctx.insert("rpm_pkg", &metadata.contains_key("generate-rpm"));
431 }
432 }
433}
434
435impl fmt::Display for PackageMetadata {
436 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
437 if let Some(description) = &self.description {
438 writeln!(
439 f,
440 "Package {}[{}] ({})",
441 self.name, self.version, description
442 )?;
443 } else {
444 writeln!(f, "Package {}[{}]", self.name, self.version)?;
445 }
446
447 if let Some(metadata) = self
448 .metadata
449 .as_ref()
450 .and_then(|m| m.get("prosa").and_then(|w| w.as_object()))
451 {
452 for (name, data) in metadata {
453 writeln!(f, " - {name}")?;
454 if name != "main" && name != "tvf" {
455 if let Ok(prosa_metadata) = serde_json::from_value::<Metadata>(data.clone()) {
456 write!(f, "{prosa_metadata}")?;
457 }
458 } else if let Ok(prosa_metadata) =
459 serde_json::from_value::<Vec<String>>(data.clone())
460 {
461 for prosa_meta in prosa_metadata {
462 writeln!(f, " - {prosa_meta}")?;
463 }
464 }
465 }
466 }
467
468 Ok(())
469 }
470}
471
472#[derive(Debug, Deserialize)]
474pub struct CargoMetadata {
475 packages: Vec<PackageMetadata>,
476}
477
478impl CargoMetadata {
479 pub fn load_metadata() -> Result<CargoMetadata, io::Error> {
481 let cargo_metadata = std::process::Command::new("cargo")
483 .args(vec!["metadata", "-q"])
484 .output()?;
485
486 Ok(serde_json::from_slice(cargo_metadata.stdout.as_slice())?)
487 }
488
489 pub fn load_package_metadata() -> Result<PackageMetadata, io::Error> {
491 let cargo_metadata = std::process::Command::new("cargo")
493 .args(vec!["metadata", "-q", "--no-deps"])
494 .output()?;
495 if cargo_metadata.status.success() {
496 let mut metadata: CargoMetadata =
497 serde_json::from_slice(cargo_metadata.stdout.as_slice())?;
498 if metadata.packages.len() == 1 {
499 Ok(metadata.packages.pop().unwrap())
500 } else {
501 Err(io::Error::new(
502 io::ErrorKind::InvalidData,
503 "Local package metadata is not correct",
504 ))
505 }
506 } else {
507 Err(io::Error::other(
508 std::str::from_utf8(cargo_metadata.stderr.as_slice()).unwrap_or(
509 format!(
510 "Can't retrieve package metadata {:?}",
511 cargo_metadata.status.code()
512 )
513 .as_str(),
514 ),
515 ))
516 }
517 }
518
519 pub fn prosa_proc_metadata(&self) -> HashMap<&str, Metadata> {
521 let mut prosa_list: HashMap<&str, Metadata> = HashMap::with_capacity(self.packages.len());
522 for package in &self.packages {
523 if let Some(prosa_metadata) = package.get_prosa_proc_metadata() {
524 for (prosa_proc_name, mut prosa_proc_metadata) in prosa_metadata {
525 prosa_proc_metadata.specify(&package.name, package.description.clone());
526 if let Some(prosa_existing_metadata) = prosa_list.get_mut(prosa_proc_name) {
527 prosa_existing_metadata.merge(prosa_proc_metadata);
528 } else {
529 prosa_list.insert(prosa_proc_name, prosa_proc_metadata);
530 }
531 }
532 }
533 }
534
535 prosa_list
536 }
537
538 pub fn prosa_main(&self) -> Vec<String> {
540 let mut main = Vec::new();
541 for package in &self.packages {
542 main.append(&mut package.get_prosa_main());
543 }
544
545 main
546 }
547
548 pub fn prosa_tvf(&self) -> Vec<String> {
550 let mut tvf = Vec::new();
551 for package in &self.packages {
552 tvf.append(&mut package.get_prosa_tvf());
553 }
554
555 tvf
556 }
557
558 pub fn get_main_version<'a>(&'a self, main_name: &str) -> Option<ComponentVersion<'a>> {
560 for package in &self.packages {
561 if let Some(main) = package.get_main_version(main_name) {
562 return Some(main);
563 }
564 }
565
566 None
567 }
568
569 pub fn get_tvf_version<'a>(&'a self, main_name: &str) -> Option<ComponentVersion<'a>> {
571 for package in &self.packages {
572 if let Some(main) = package.get_tvf_version(main_name) {
573 return Some(main);
574 }
575 }
576
577 None
578 }
579
580 pub fn get_versions<'a>(
582 &'a self,
583 proc_name: &str,
584 adaptor_name: &str,
585 ) -> (Option<ComponentVersion<'a>>, Option<ComponentVersion<'a>>) {
586 let mut processor_version = None;
587 let mut adaptor_version = None;
588 for package in &self.packages {
589 if processor_version.is_none()
590 && let Some(proc) = package.get_proc_version(proc_name)
591 {
592 processor_version = Some(proc);
593
594 if adaptor_version.is_some() {
595 break;
596 }
597 }
598
599 if adaptor_version.is_none()
600 && let Some(adaptor) = package.get_adaptor_version(adaptor_name)
601 {
602 adaptor_version = Some(adaptor);
603
604 if processor_version.is_some() {
605 break;
606 }
607 }
608 }
609
610 (processor_version, adaptor_version)
611 }
612}
613
614impl fmt::Display for CargoMetadata {
615 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
616 for package in &self.packages {
617 if package.is_prosa() {
618 write!(f, "{package}")?;
619 }
620 }
621
622 Ok(())
623 }
624}