1use crate::error::OpsModelError;
2use crate::identifiers::ReportId;
3use crate::validation::require_non_empty;
4use coil_auth::Capability;
5use coil_core::{
6 ReportDefinition as ManifestReportDefinition, ReportDeliveryMode as ManifestReportDeliveryMode,
7 ReportFormat as ManifestReportFormat, ReportSensitivity as ManifestReportSensitivity,
8};
9use coil_jobs::RetryPolicy;
10use std::fmt;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum ReportFormat {
14 Csv,
15 Json,
16 Pdf,
17}
18
19impl ReportFormat {
20 pub fn extension(self) -> &'static str {
21 match self {
22 Self::Csv => "csv",
23 Self::Json => "json",
24 Self::Pdf => "pdf",
25 }
26 }
27}
28
29impl fmt::Display for ReportFormat {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 match self {
32 Self::Csv => f.write_str("csv"),
33 Self::Json => f.write_str("json"),
34 Self::Pdf => f.write_str("pdf"),
35 }
36 }
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum ReportSensitivity {
41 Public,
42 Internal,
43 Restricted,
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub enum ReportDeliveryMode {
48 PublicObjectStore,
49 SignedUrl,
50 InternalOnly,
51}
52
53impl ReportDeliveryMode {
54 pub fn is_public(self) -> bool {
55 matches!(self, Self::PublicObjectStore)
56 }
57}
58
59#[derive(Debug, Clone, PartialEq, Eq)]
60pub struct ReportDefinition {
61 pub id: ReportId,
62 pub source_module: String,
63 pub title: String,
64 pub description: Option<String>,
65 pub required_capability: Capability,
66 pub format: ReportFormat,
67 pub sensitivity: ReportSensitivity,
68 pub delivery_mode: ReportDeliveryMode,
69 pub export_prefix: String,
70 pub retry_policy: RetryPolicy,
71}
72
73impl ReportDefinition {
74 pub fn new(
75 id: ReportId,
76 source_module: impl Into<String>,
77 title: impl Into<String>,
78 description: Option<String>,
79 required_capability: Capability,
80 format: ReportFormat,
81 sensitivity: ReportSensitivity,
82 delivery_mode: ReportDeliveryMode,
83 export_prefix: impl Into<String>,
84 retry_policy: RetryPolicy,
85 ) -> Result<Self, OpsModelError> {
86 let source_module = require_non_empty("report_source_module", source_module.into())?;
87 let title = require_non_empty("report_title", title.into())?;
88 let export_prefix = require_non_empty("report_export_prefix", export_prefix.into())?;
89
90 if matches!(
91 (sensitivity, delivery_mode),
92 (ReportSensitivity::Public, ReportDeliveryMode::InternalOnly)
93 ) {
94 return Err(OpsModelError::InvalidReportDelivery {
95 report_id: id.to_string(),
96 reason:
97 "public reports should be delivered through a public object store or signed URL"
98 .to_string(),
99 });
100 }
101
102 if matches!(sensitivity, ReportSensitivity::Restricted)
103 && matches!(delivery_mode, ReportDeliveryMode::PublicObjectStore)
104 {
105 return Err(OpsModelError::InvalidReportDelivery {
106 report_id: id.to_string(),
107 reason: "restricted reports cannot be delivered through a public object store"
108 .to_string(),
109 });
110 }
111
112 Ok(Self {
113 id,
114 source_module,
115 title,
116 description,
117 required_capability,
118 format,
119 sensitivity,
120 delivery_mode,
121 export_prefix,
122 retry_policy,
123 })
124 }
125
126 pub fn allows(&self, capabilities: &[Capability]) -> bool {
127 capabilities.contains(&self.required_capability)
128 }
129
130 pub(crate) fn from_manifest_definition(
131 source_module: &str,
132 definition: &ManifestReportDefinition,
133 ) -> Result<Self, OpsModelError> {
134 Self::new(
135 ReportId::new(definition.id.clone())?,
136 source_module,
137 definition.title.clone(),
138 definition.description.clone(),
139 definition.required_capability,
140 map_report_format(definition.format),
141 map_report_sensitivity(definition.sensitivity),
142 map_report_delivery_mode(definition.delivery_mode),
143 definition.export_prefix.clone(),
144 definition.retry_policy.clone(),
145 )
146 }
147}
148
149pub(crate) fn map_report_format(format: ManifestReportFormat) -> ReportFormat {
150 match format {
151 ManifestReportFormat::Csv => ReportFormat::Csv,
152 ManifestReportFormat::Json => ReportFormat::Json,
153 ManifestReportFormat::Pdf => ReportFormat::Pdf,
154 }
155}
156
157pub(crate) fn map_report_sensitivity(sensitivity: ManifestReportSensitivity) -> ReportSensitivity {
158 match sensitivity {
159 ManifestReportSensitivity::Public => ReportSensitivity::Public,
160 ManifestReportSensitivity::Internal => ReportSensitivity::Internal,
161 ManifestReportSensitivity::Restricted => ReportSensitivity::Restricted,
162 }
163}
164
165pub(crate) fn map_report_delivery_mode(mode: ManifestReportDeliveryMode) -> ReportDeliveryMode {
166 match mode {
167 ManifestReportDeliveryMode::PublicObjectStore => ReportDeliveryMode::PublicObjectStore,
168 ManifestReportDeliveryMode::SignedUrl => ReportDeliveryMode::SignedUrl,
169 ManifestReportDeliveryMode::InternalOnly => ReportDeliveryMode::InternalOnly,
170 }
171}