coil_ops/reports/
catalog.rs1use std::collections::HashSet;
2
3use coil_core::ModuleManifest;
4
5use crate::error::OpsModelError;
6use crate::identifiers::ReportId;
7
8use super::ReportDefinition;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct ReportCatalog {
12 pub definitions: Vec<ReportDefinition>,
13}
14
15impl ReportCatalog {
16 pub fn new(definitions: Vec<ReportDefinition>) -> Self {
17 Self { definitions }
18 }
19
20 pub fn standard() -> Self {
21 Self {
22 definitions: Vec::new(),
23 }
24 }
25
26 pub fn from_manifests(manifests: &[ModuleManifest]) -> Result<Self, OpsModelError> {
27 let mut definitions = Vec::new();
28 for manifest in manifests {
29 for definition in &manifest.report_definitions {
30 definitions.push(ReportDefinition::from_manifest_definition(
31 &manifest.name,
32 definition,
33 )?);
34 }
35 }
36 let catalog = Self::new(definitions);
37 catalog.validate()?;
38 Ok(catalog)
39 }
40
41 pub fn validate(&self) -> Result<(), OpsModelError> {
42 let mut seen = HashSet::new();
43 for definition in &self.definitions {
44 if !seen.insert(definition.id.as_str().to_string()) {
45 return Err(OpsModelError::DuplicateIdentifier {
46 kind: "report",
47 id: definition.id.to_string(),
48 });
49 }
50
51 ReportDefinition::new(
52 definition.id.clone(),
53 definition.source_module.clone(),
54 definition.title.clone(),
55 definition.description.clone(),
56 definition.required_capability,
57 definition.format,
58 definition.sensitivity,
59 definition.delivery_mode,
60 definition.export_prefix.clone(),
61 definition.retry_policy.clone(),
62 )?;
63 }
64
65 Ok(())
66 }
67
68 pub fn definition(&self, id: &ReportId) -> Option<&ReportDefinition> {
69 self.definitions
70 .iter()
71 .find(|definition| &definition.id == id)
72 }
73}
74
75impl Default for ReportCatalog {
76 fn default() -> Self {
77 Self::standard()
78 }
79}