cadi_core/
manifest.rs

1//! Manifest types for CADI application build graphs
2
3use serde::{Deserialize, Serialize};
4
5/// Application manifest
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct Manifest {
8    pub manifest_id: String,
9    pub manifest_version: String,
10    pub application: ApplicationInfo,
11    pub build_graph: BuildGraph,
12    #[serde(default)]
13    pub build_targets: Vec<BuildTarget>,
14    #[serde(skip_serializing_if = "Option::is_none")]
15    pub dependencies: Option<DependencyConfig>,
16}
17
18/// Application information
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct ApplicationInfo {
21    pub name: String,
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub description: Option<String>,
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub version: Option<String>,
26    #[serde(default)]
27    pub authors: Vec<String>,
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub license: Option<String>,
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub repository: Option<String>,
32}
33
34/// Build graph structure
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct BuildGraph {
37    pub nodes: Vec<GraphNode>,
38    #[serde(default)]
39    pub edges: Vec<GraphEdge>,
40}
41
42/// A node in the build graph
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct GraphNode {
45    pub id: String,
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub source_cadi: Option<String>,
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub ir_cadi: Option<String>,
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub blob_cadi: Option<String>,
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub container_cadi: Option<String>,
54    #[serde(default)]
55    pub representations: Vec<Representation>,
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub selection_strategy: Option<String>,
58}
59
60/// A representation of a node
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct Representation {
63    pub form: String,
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub language: Option<String>,
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub format: Option<String>,
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub architecture: Option<String>,
70    pub chunk: String,
71}
72
73/// An edge in the build graph
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct GraphEdge {
76    pub from: String,
77    pub to: String,
78    #[serde(skip_serializing_if = "Option::is_none")]
79    pub interface: Option<String>,
80    #[serde(default = "default_relation")]
81    pub relation: String,
82}
83
84fn default_relation() -> String {
85    "depends_on".to_string()
86}
87
88/// Build target definition
89#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct BuildTarget {
91    pub name: String,
92    pub platform: String,
93    #[serde(default)]
94    pub nodes: Vec<TargetNode>,
95    #[serde(skip_serializing_if = "Option::is_none")]
96    pub bundle: Option<BundleConfig>,
97    #[serde(skip_serializing_if = "Option::is_none")]
98    pub deploy: Option<DeployConfig>,
99}
100
101/// Node configuration within a target
102#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct TargetNode {
104    pub id: String,
105    #[serde(skip_serializing_if = "Option::is_none")]
106    pub require: Option<Vec<String>>,
107    #[serde(skip_serializing_if = "Option::is_none")]
108    pub prefer: Option<Vec<String>>,
109}
110
111/// Bundle configuration
112#[derive(Debug, Clone, Default, Serialize, Deserialize)]
113pub struct BundleConfig {
114    #[serde(skip_serializing_if = "Option::is_none")]
115    pub format: Option<String>,
116    #[serde(skip_serializing_if = "Option::is_none")]
117    pub output: Option<String>,
118    #[serde(default)]
119    pub minify: bool,
120}
121
122/// Deployment configuration
123#[derive(Debug, Clone, Default, Serialize, Deserialize)]
124pub struct DeployConfig {
125    #[serde(skip_serializing_if = "Option::is_none")]
126    pub target: Option<String>,
127    #[serde(skip_serializing_if = "Option::is_none")]
128    pub replicas: Option<u32>,
129    #[serde(default)]
130    pub environment: std::collections::HashMap<String, String>,
131}
132
133/// Dependency configuration
134#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct DependencyConfig {
136    #[serde(skip_serializing_if = "Option::is_none")]
137    pub lock_file: Option<String>,
138    #[serde(default = "default_resolution_strategy")]
139    pub resolution_strategy: String,
140}
141
142fn default_resolution_strategy() -> String {
143    "newest".to_string()
144}
145
146impl Manifest {
147    /// Create a new manifest with the given ID and application name
148    pub fn new(manifest_id: String, app_name: String) -> Self {
149        Self {
150            manifest_id,
151            manifest_version: "1.0".to_string(),
152            application: ApplicationInfo {
153                name: app_name,
154                description: None,
155                version: None,
156                authors: Vec::new(),
157                license: None,
158                repository: None,
159            },
160            build_graph: BuildGraph {
161                nodes: Vec::new(),
162                edges: Vec::new(),
163            },
164            build_targets: Vec::new(),
165            dependencies: None,
166        }
167    }
168
169    /// Add a node to the build graph
170    pub fn add_node(&mut self, node: GraphNode) {
171        self.build_graph.nodes.push(node);
172    }
173
174    /// Add an edge to the build graph
175    pub fn add_edge(&mut self, edge: GraphEdge) {
176        self.build_graph.edges.push(edge);
177    }
178
179    /// Add a build target
180    pub fn add_target(&mut self, target: BuildTarget) {
181        self.build_targets.push(target);
182    }
183
184    /// Find a node by ID
185    pub fn find_node(&self, id: &str) -> Option<&GraphNode> {
186        self.build_graph.nodes.iter().find(|n| n.id == id)
187    }
188
189    /// Find a target by name
190    pub fn find_target(&self, name: &str) -> Option<&BuildTarget> {
191        self.build_targets.iter().find(|t| t.name == name)
192    }
193}