cadi_core/
chunk.rs

1//! Chunk types and operations
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6/// Type of CADI chunk
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
8#[serde(rename_all = "lowercase")]
9pub enum CadiType {
10    Source,
11    Intermediate,
12    Blob,
13    Container,
14}
15
16/// Metadata for a chunk
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct ChunkMeta {
19    pub name: String,
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub description: Option<String>,
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub version: Option<String>,
24    #[serde(default)]
25    pub tags: Vec<String>,
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub created_at: Option<String>,
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub updated_at: Option<String>,
30}
31
32/// What a chunk provides
33#[derive(Debug, Clone, Default, Serialize, Deserialize)]
34pub struct ChunkProvides {
35    #[serde(default)]
36    pub concepts: Vec<String>,
37    #[serde(default)]
38    pub interfaces: Vec<String>,
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub abi: Option<String>,
41}
42
43/// Licensing information
44#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct ChunkLicensing {
46    pub license: String,
47    #[serde(default)]
48    pub restrictions: Vec<String>,
49}
50
51/// Chunk lineage (provenance)
52#[derive(Debug, Clone, Default, Serialize, Deserialize)]
53pub struct ChunkLineage {
54    #[serde(default)]
55    pub parents: Vec<String>,
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub build_receipt: Option<String>,
58}
59
60/// Base chunk structure
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct Chunk {
63    pub chunk_id: String,
64    pub cadi_type: CadiType,
65    pub meta: ChunkMeta,
66    #[serde(default)]
67    pub provides: ChunkProvides,
68    pub licensing: ChunkLicensing,
69    #[serde(default)]
70    pub lineage: ChunkLineage,
71    #[serde(default)]
72    pub signatures: Vec<String>,
73}
74
75/// A file entry in source CADI
76#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct SourceFile {
78    pub path: String,
79    pub hash: String,
80    #[serde(skip_serializing_if = "Option::is_none")]
81    pub size: Option<usize>,
82}
83
84/// An entrypoint in source CADI
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct Entrypoint {
87    pub symbol: String,
88    pub path: String,
89}
90
91/// A dependency declaration
92#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct Dependency {
94    pub id: String,
95    #[serde(default)]
96    pub optional: bool,
97    #[serde(default)]
98    pub features: Vec<String>,
99}
100
101/// Source-specific data
102#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct SourceData {
104    pub language: String,
105    #[serde(skip_serializing_if = "Option::is_none")]
106    pub version: Option<String>,
107    #[serde(skip_serializing_if = "Option::is_none")]
108    pub dialect: Option<String>,
109    pub files: Vec<SourceFile>,
110    #[serde(default)]
111    pub entrypoints: Vec<Entrypoint>,
112    #[serde(default)]
113    pub runtime_dependencies: Vec<Dependency>,
114}
115
116/// Source CADI chunk
117#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct SourceCadi {
119    #[serde(flatten)]
120    pub chunk: Chunk,
121    pub source: SourceData,
122    #[serde(default)]
123    pub compiled_forms: Vec<CompiledForm>,
124}
125
126/// Link to a compiled form
127#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct CompiledForm {
129    #[serde(skip_serializing_if = "Option::is_none")]
130    pub ir_cadi: Option<String>,
131    #[serde(skip_serializing_if = "Option::is_none")]
132    pub blob_cadi: Option<String>,
133    #[serde(default)]
134    pub architectures: Vec<String>,
135    #[serde(skip_serializing_if = "Option::is_none")]
136    pub derived_at: Option<String>,
137    #[serde(skip_serializing_if = "Option::is_none")]
138    pub compiler: Option<String>,
139    #[serde(skip_serializing_if = "Option::is_none")]
140    pub deterministic: Option<bool>,
141}
142
143/// IR module data
144#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct IrModule {
146    pub hash: String,
147    #[serde(skip_serializing_if = "Option::is_none")]
148    pub size: Option<usize>,
149    #[serde(default)]
150    pub exports: Vec<ModuleExport>,
151}
152
153/// Module export
154#[derive(Debug, Clone, Serialize, Deserialize)]
155pub struct ModuleExport {
156    pub name: String,
157    pub kind: String,
158    #[serde(skip_serializing_if = "Option::is_none")]
159    pub signature: Option<String>,
160}
161
162/// IR-specific data
163#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct IntermediateData {
165    pub format: String,
166    #[serde(skip_serializing_if = "Option::is_none")]
167    pub version: Option<String>,
168    pub module: IrModule,
169    #[serde(default)]
170    pub imports: Vec<ModuleImport>,
171}
172
173/// Module import
174#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct ModuleImport {
176    pub module: String,
177    pub name: String,
178    pub kind: String,
179    #[serde(skip_serializing_if = "Option::is_none")]
180    pub signature: Option<String>,
181}
182
183/// IR CADI chunk
184#[derive(Debug, Clone, Serialize, Deserialize)]
185pub struct IrCadi {
186    #[serde(flatten)]
187    pub chunk: Chunk,
188    pub intermediate: IntermediateData,
189    #[serde(skip_serializing_if = "Option::is_none")]
190    pub source_link: Option<SourceLink>,
191    #[serde(default)]
192    pub compiled_forms: Vec<CompiledForm>,
193}
194
195/// Link to source
196#[derive(Debug, Clone, Serialize, Deserialize)]
197pub struct SourceLink {
198    #[serde(skip_serializing_if = "Option::is_none")]
199    pub source_cadi: Option<String>,
200    #[serde(skip_serializing_if = "Option::is_none")]
201    pub verification: Option<String>,
202}
203
204/// Binary blob entry
205#[derive(Debug, Clone, Serialize, Deserialize)]
206pub struct BlobEntry {
207    pub architecture: String,
208    pub format: String,
209    pub hash: String,
210    pub size: usize,
211    #[serde(skip_serializing_if = "Option::is_none")]
212    pub linking: Option<LinkingInfo>,
213    #[serde(skip_serializing_if = "Option::is_none")]
214    pub security: Option<SecurityInfo>,
215    #[serde(skip_serializing_if = "Option::is_none")]
216    pub build_info: Option<BuildInfo>,
217}
218
219/// Linking information
220#[derive(Debug, Clone, Serialize, Deserialize)]
221pub struct LinkingInfo {
222    #[serde(rename = "type")]
223    pub linking_type: String,
224    #[serde(default)]
225    pub dependencies: Vec<String>,
226}
227
228/// Security features
229#[derive(Debug, Clone, Serialize, Deserialize)]
230pub struct SecurityInfo {
231    #[serde(default)]
232    pub pie: bool,
233    #[serde(default)]
234    pub nx: bool,
235    #[serde(default)]
236    pub stack_canary: bool,
237    #[serde(skip_serializing_if = "Option::is_none")]
238    pub relro: Option<String>,
239}
240
241/// Build information
242#[derive(Debug, Clone, Serialize, Deserialize)]
243pub struct BuildInfo {
244    #[serde(skip_serializing_if = "Option::is_none")]
245    pub compiler: Option<String>,
246    #[serde(default)]
247    pub flags: Vec<String>,
248    #[serde(skip_serializing_if = "Option::is_none")]
249    pub source_hash: Option<String>,
250    #[serde(skip_serializing_if = "Option::is_none")]
251    pub reproducible: Option<bool>,
252}
253
254/// Blob CADI chunk
255#[derive(Debug, Clone, Serialize, Deserialize)]
256pub struct BlobCadi {
257    #[serde(flatten)]
258    pub chunk: Chunk,
259    pub blobs: Vec<BlobEntry>,
260}
261
262/// Container-specific data
263#[derive(Debug, Clone, Serialize, Deserialize)]
264pub struct ContainerData {
265    pub format: String,
266    pub image_ref: String,
267    #[serde(default)]
268    pub layers: Vec<ContainerLayer>,
269    #[serde(default)]
270    pub entrypoint: Vec<String>,
271    #[serde(default)]
272    pub cmd: Vec<String>,
273    #[serde(default)]
274    pub environment: Vec<EnvVar>,
275    #[serde(default)]
276    pub exposed_ports: Vec<String>,
277    #[serde(default)]
278    pub labels: HashMap<String, String>,
279    #[serde(skip_serializing_if = "Option::is_none")]
280    pub architecture: Option<String>,
281    #[serde(default = "default_os")]
282    pub os: String,
283}
284
285fn default_os() -> String {
286    "linux".to_string()
287}
288
289/// Container layer
290#[derive(Debug, Clone, Serialize, Deserialize)]
291pub struct ContainerLayer {
292    pub hash: String,
293    pub size: usize,
294    #[serde(skip_serializing_if = "Option::is_none")]
295    pub description: Option<String>,
296    #[serde(skip_serializing_if = "Option::is_none")]
297    pub cadi_chunk: Option<String>,
298}
299
300/// Environment variable
301#[derive(Debug, Clone, Serialize, Deserialize)]
302pub struct EnvVar {
303    pub name: String,
304    pub value: String,
305}
306
307/// Container CADI chunk
308#[derive(Debug, Clone, Serialize, Deserialize)]
309pub struct ContainerCadi {
310    #[serde(flatten)]
311    pub chunk: Chunk,
312    pub container: ContainerData,
313    #[serde(default)]
314    pub deployment_target: Vec<String>,
315}
316
317impl Chunk {
318    /// Create a new chunk with the given ID and type
319    pub fn new(chunk_id: String, cadi_type: CadiType, name: String) -> Self {
320        Self {
321            chunk_id,
322            cadi_type,
323            meta: ChunkMeta {
324                name,
325                description: None,
326                version: None,
327                tags: Vec::new(),
328                created_at: Some(chrono::Utc::now().to_rfc3339()),
329                updated_at: None,
330            },
331            provides: ChunkProvides::default(),
332            licensing: ChunkLicensing {
333                license: "MIT".to_string(),
334                restrictions: Vec::new(),
335            },
336            lineage: ChunkLineage::default(),
337            signatures: Vec::new(),
338        }
339    }
340}