clique_types/manifest/
mod.rs

1pub mod code;
2pub mod compat;
3pub mod input;
4pub mod output;
5pub mod refer;
6pub mod subtask;
7
8use std::collections::HashMap;
9use std::fmt;
10use std::str::FromStr;
11
12use anyhow::Result;
13use indexmap::IndexMap;
14use serde::{Deserialize, Serialize};
15
16use crate::value::{CliqueValueType, CustomType};
17
18pub use code::{CodeType, RawCode};
19pub use compat::{TaskCompatType, TaskCompatibility};
20pub use input::Input;
21pub use output::Output;
22pub use refer::{MapOrRefers, Reference};
23pub use subtask::RawSubTask;
24
25pub type RawCustomType = IndexMap<String, IndexMap<String, Input>>;
26
27pub(crate) type TaskName = String;
28pub(crate) type SubTaskID = String;
29pub(crate) type Identifier = String;
30pub(crate) type TaskInputRefers = Vec<(Identifier, Reference)>;
31pub(crate) type TaskOutputRefers = Vec<(Identifier, Reference)>;
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub enum ManifestType {
35    BuiltIn,
36    Schema,
37    Dynamic,
38}
39
40impl ManifestType {
41    pub fn is_buildin(&self) -> bool {
42        matches!(self, Self::BuiltIn)
43    }
44
45    pub fn is_dynamic(&self) -> bool {
46        matches!(self, Self::Dynamic)
47    }
48
49    pub fn is_schema(&self) -> bool {
50        matches!(self, Self::Schema)
51    }
52}
53
54#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
55pub enum ProofType {
56    TEE,
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize)]
60#[serde(rename_all = "kebab-case")]
61pub struct Manifest {
62    pub name: String,
63    pub spec_version: String,
64    #[serde(rename = "type")]
65    pub type_: ManifestType,
66
67    pub proof_type: Vec<ProofType>,
68
69    #[serde(rename = "types", skip_serializing_if = "Option::is_none")]
70    pub custom_type: Option<RawCustomType>,
71
72    pub input: IndexMap<String, Input>,
73    pub output: IndexMap<String, Output>,
74
75    #[serde(rename = "code", skip_serializing_if = "Option::is_none")]
76    pub raw_code: Option<RawCode>,
77
78    #[serde(rename = "tasks", skip_serializing_if = "Option::is_none")]
79    pub subtasks: Option<Vec<RawSubTask>>,
80}
81
82impl Manifest {
83    pub fn from_file(path: impl AsRef<std::path::Path>) -> Result<Self> {
84        let manifest_str = std::fs::read_to_string(path)?;
85        Ok(Self::from_str(&manifest_str)?)
86    }
87
88    pub fn parse_input(&self) -> Result<IndexMap<String, CliqueValueType>> {
89        let custom_types = self.parse_custom_types()?;
90
91        Ok(CustomType::from_map("Input", &self.input, custom_types.as_ref())?.type_)
92    }
93
94    pub fn parse_output(&self) -> Result<MapOrRefers> {
95        if self.type_.is_schema() {
96            let mut refers = Vec::with_capacity(self.output.len());
97            for (name, output) in self.output.iter() {
98                let ref_str = output
99                    .ref_
100                    .as_ref()
101                    .ok_or(anyhow::anyhow!("should have ref"))?;
102                let refer = Reference::from_str(ref_str)?;
103                refers.push((name.clone(), refer));
104            }
105            Ok(MapOrRefers::Refers(refers))
106        } else {
107            let mut map = IndexMap::new();
108            for (name, output) in self.output.iter() {
109                let type_str = output
110                    .type_
111                    .as_ref()
112                    .ok_or(anyhow::anyhow!("should have type"))?
113                    .clone();
114                let input = Input::new(type_str);
115                map.insert(name.clone(), input);
116            }
117
118            let custom_types = self.parse_custom_types()?;
119
120            Ok(MapOrRefers::Map(
121                CustomType::from_map("Output", &map, custom_types.as_ref())?.type_,
122            ))
123        }
124    }
125
126    pub fn parse_subtasks(&self) -> Result<HashMap<SubTaskID, (TaskName, TaskInputRefers)>> {
127        if self.subtasks.is_none() {
128            return Ok(HashMap::new());
129        }
130
131        let tasks = self.subtasks.as_ref().unwrap();
132        let mut res = HashMap::new();
133        for task in tasks {
134            let task_id = task.id.clone();
135            let task_name = task.name.clone();
136            let task_inputs = &task.input;
137            let mut list = Vec::with_capacity(task_inputs.len());
138            for (input_name, input_ref) in task_inputs {
139                let refer = Reference::from_str(&input_ref)?;
140                list.push((input_name.clone(), refer));
141            }
142            res.insert(task_id, (task_name, list));
143        }
144
145        Ok(res)
146    }
147
148    pub fn parse_custom_types(&self) -> Result<Option<IndexMap<String, CustomType>>> {
149        if self.custom_type.is_none() {
150            return Ok(None);
151        }
152
153        let mut custom_types = None;
154        // TODO: sorting based on dependencies to support nested custom_type
155        for (name, map) in self.custom_type.as_ref().unwrap().iter() {
156            let new_type = CustomType::from_map(name, map, custom_types.as_ref())?;
157            if custom_types.is_none() {
158                custom_types = Some(IndexMap::new());
159            }
160            custom_types
161                .as_mut()
162                .unwrap()
163                .insert(name.clone(), new_type);
164        }
165        Ok(custom_types)
166    }
167}
168
169impl fmt::Display for Manifest {
170    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171        let serialized = toml::to_string(self).map_err(|_| fmt::Error)?;
172        write!(f, "{}", serialized)
173    }
174}
175
176impl FromStr for Manifest {
177    type Err = toml::de::Error;
178
179    fn from_str(s: &str) -> Result<Self, Self::Err> {
180        toml::from_str(s)
181    }
182}
183
184#[cfg(test)]
185mod test {
186    use super::*;
187
188    #[test]
189    fn test_manifests() {
190        test_manifest("assets/dynamic.toml");
191        test_manifest("assets/builtin.toml");
192        test_manifest("assets/schema.toml");
193    }
194
195    fn test_manifest(file_path: &str) {
196        let manifest = Manifest::from_file(file_path).unwrap();
197        let manifest_str = manifest.to_string();
198        println!("{}", manifest_str);
199
200        let input = manifest.parse_input().unwrap();
201        println!("{:?}", input);
202    }
203}