1use std::collections::HashSet;
2
3use coil_core::ModuleManifest;
4
5use crate::error::OpsModelError;
6use crate::identifiers::BulkOperationId;
7
8use super::BulkOperationDefinition;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct BulkCatalog {
12 pub definitions: Vec<BulkOperationDefinition>,
13}
14
15impl BulkCatalog {
16 pub fn new(definitions: Vec<BulkOperationDefinition>) -> 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.bulk_operations {
30 definitions.push(BulkOperationDefinition::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: "bulk operation",
47 id: definition.id.to_string(),
48 });
49 }
50
51 BulkOperationDefinition::new(
52 definition.id.clone(),
53 definition.source_module.clone(),
54 definition.title.clone(),
55 definition.description.clone(),
56 definition.required_capability,
57 definition.kind,
58 definition.scope,
59 definition.retry_policy.clone(),
60 definition.max_items,
61 definition.requires_idempotency_key,
62 )?;
63 }
64
65 Ok(())
66 }
67
68 pub fn definition(&self, id: &BulkOperationId) -> Option<&BulkOperationDefinition> {
69 self.definitions
70 .iter()
71 .find(|definition| &definition.id == id)
72 }
73}
74
75impl Default for BulkCatalog {
76 fn default() -> Self {
77 Self::standard()
78 }
79}