1use std::{
6 fmt, fs,
7 io::{self, Write},
8 path::Path,
9};
10
11use serde::{Deserialize, Serialize};
12use toml_edit::{Item, Table, Value};
13
14use crate::cargo::{CargoMetadata, ComponentVersion};
15
16#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/doc_assets/main.svg"))]
20#[derive(Debug, PartialEq, Deserialize, Serialize)]
22pub struct MainDesc {
23 pub main: String,
25 pub tvf: String,
27}
28
29impl Default for MainDesc {
30 fn default() -> Self {
31 MainDesc {
32 main: String::from("prosa::core::main::MainProc"),
33 tvf: String::from("prosa_utils::msg::simple_string_tvf::SimpleStringTvf"),
34 }
35 }
36}
37
38#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/doc_assets/proc.svg"))]
42#[derive(Debug, PartialEq, Deserialize, Serialize)]
44pub struct ProcDesc {
45 pub name: Option<String>,
47 pub proc_name: String,
49 pub proc: String,
51 pub adaptor: String,
53}
54
55impl ProcDesc {
56 pub fn new(proc_name: String, proc: String, adaptor: String) -> Self {
58 ProcDesc {
59 name: None,
60 proc_name,
61 proc,
62 adaptor,
63 }
64 }
65
66 pub fn get_name(&self) -> String {
68 if let Some(name) = &self.name {
69 name.clone()
70 } else {
71 self.proc_name.clone()
72 }
73 }
74
75 pub fn get_versions<'a>(
77 &self,
78 cargo_metadata: &'a CargoMetadata,
79 ) -> (Option<ComponentVersion<'a>>, Option<ComponentVersion<'a>>) {
80 cargo_metadata.get_versions(&self.proc, &self.adaptor)
81 }
82}
83
84impl fmt::Display for ProcDesc {
85 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
86 writeln!(
87 f,
88 "ProSA processor {} ({})",
89 self.name.as_ref().unwrap_or(&self.proc),
90 self.proc_name,
91 )?;
92 writeln!(f, " Processor {}", self.proc)?;
93 writeln!(f, " Adaptor {}", self.adaptor)
94 }
95}
96
97impl TryFrom<&Item> for ProcDesc {
98 type Error = &'static str;
99
100 fn try_from(item: &Item) -> Result<Self, Self::Error> {
101 if let Item::ArrayOfTables(array_tables) = item {
102 let mut name = None;
103 let mut proc_name = None;
104 let mut proc = None;
105 let mut adaptor = None;
106 for array in array_tables {
107 if let Some(Item::Value(Value::String(item_name))) = array.get("name") {
108 name = Some(item_name.value().clone());
109 } else if let Some(Item::Value(Value::String(item_name))) = array.get("proc_name") {
110 proc_name = Some(item_name.value().clone());
111 } else if let Some(Item::Value(Value::String(item_name))) = array.get("proc") {
112 proc = Some(item_name.value().clone());
113 } else if let Some(Item::Value(Value::String(item_name))) = array.get("adaptor") {
114 adaptor = Some(item_name.value().clone());
115 }
116 }
117
118 if let Some(proc_name) = proc_name {
119 if let Some(proc) = proc {
120 if let Some(adaptor) = adaptor {
121 Ok(ProcDesc {
122 name,
123 proc_name,
124 proc,
125 adaptor,
126 })
127 } else {
128 Err("No `adaptor` key in toml ProSA description")
129 }
130 } else {
131 Err("No `proc` key in toml ProSA description")
132 }
133 } else {
134 Err("No `proc_name` key in toml ProSA description")
135 }
136 } else {
137 Err("The item type is not correct for ProSAProxDesc")
138 }
139 }
140}
141
142impl From<ProcDesc> for Table {
143 fn from(proc_desc: ProcDesc) -> Table {
144 let mut table = toml_edit::Table::new();
145 if let Some(name) = proc_desc.name {
146 table.insert(
147 "name",
148 Item::Value(toml_edit::Value::String(toml_edit::Formatted::new(name))),
149 );
150 }
151 table.insert(
152 "proc_name",
153 Item::Value(toml_edit::Value::String(toml_edit::Formatted::new(
154 proc_desc.proc_name,
155 ))),
156 );
157 table.insert(
158 "proc",
159 Item::Value(toml_edit::Value::String(toml_edit::Formatted::new(
160 proc_desc.proc,
161 ))),
162 );
163 table.insert(
164 "adaptor",
165 Item::Value(toml_edit::Value::String(toml_edit::Formatted::new(
166 proc_desc.adaptor,
167 ))),
168 );
169
170 table
171 }
172}
173
174#[derive(Debug, Default, PartialEq, Deserialize, Serialize)]
176pub struct Desc {
177 #[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/doc_assets/main.svg"))]
181 pub prosa: MainDesc,
183 #[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/doc_assets/proc.svg"))]
187 pub proc: Option<Vec<ProcDesc>>,
189}
190
191impl Desc {
192 pub fn create<P>(&self, path: P) -> Result<(), io::Error>
194 where
195 P: AsRef<Path>,
196 {
197 fn inner(desc: &Desc, mut file: fs::File) -> Result<(), io::Error> {
198 writeln!(file, "# ProSA definition")?;
199 writeln!(
200 file,
201 "{}",
202 toml::to_string(&desc)
203 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?
204 )
205 }
206 inner(self, fs::File::create(&path)?)
207 }
208
209 pub fn read<P>(path: P) -> Result<Desc, io::Error>
211 where
212 P: AsRef<Path>,
213 {
214 let prosa_desc_file = fs::read_to_string(path)?;
215 toml::from_str::<Desc>(prosa_desc_file.as_str())
216 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
217 }
218
219 #[cfg(test)]
221 pub fn add_proc(&mut self, proc_desc: ProcDesc) {
222 if let Some(proc) = self.proc.as_mut() {
223 proc.push(proc_desc);
224 } else {
225 self.proc = Some(vec![proc_desc]);
226 }
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233
234 #[test]
235 fn prosa_desc_toml() {
236 let mut prosa_desc = Desc::default();
237 prosa_desc.add_proc(ProcDesc::new(
238 "proc".into(),
239 "crate::proc".into(),
240 "crate::adaptor".into(),
241 ));
242
243 let prosa_toml = "[prosa]
244main = \"prosa::core::main::MainProc\"
245tvf = \"prosa_utils::msg::simple_string_tvf::SimpleStringTvf\"
246
247[[proc]]
248proc_name = \"proc\"
249proc = \"crate::proc\"
250adaptor = \"crate::adaptor\"
251";
252 assert_eq!(prosa_toml, toml::to_string(&prosa_desc).unwrap());
253
254 let toml_path_file = Path::new("/tmp/test_prosa_desc.toml");
256 let mut toml_file = fs::File::create(toml_path_file).unwrap();
257 toml_file.write_all(prosa_toml.as_bytes()).unwrap();
258
259 let prosa_desc_from_file = Desc::read(toml_path_file).unwrap();
260 assert_eq!(prosa_desc, prosa_desc_from_file);
261 }
262}