rush_ecs_manifest/
manifest.rs1use crate::error::{utils::ensure_syntax, ManifestError};
6use anyhow::{bail, Result};
7use std::{
8 convert::From,
9 fs::{read_to_string, File},
10 io::Write,
11 path::Path,
12};
13use toml::Table;
14
15#[derive(Debug, Clone, Eq, PartialEq)]
16pub enum Repository {
17 InMemory,
18 Solana,
19}
20
21impl From<Repository> for String {
22 fn from(repository: Repository) -> Self {
23 match repository {
24 Repository::InMemory => "memory".to_string(),
25 Repository::Solana => "solana".to_string(),
26 }
27 }
28}
29
30#[derive(Debug, Clone, Eq, PartialEq)]
31pub enum Chain {
32 Solana {
33 store: String,
35 rpc: String,
36 keypair: String,
38 },
39}
40
41#[derive(Debug, Clone, Eq, PartialEq)]
42pub struct Manifest {
43 pub name: String,
44 pub storage: Repository,
45 pub chain: Chain,
46}
47
48impl Manifest {
49 pub const FILENAME: &'static str = "Rush.toml";
50
51 pub fn new_solana(name: String) -> Self {
52 Self {
53 name,
54 storage: Repository::Solana,
55 chain: Chain::Solana {
56 store: String::default(),
58 rpc: String::default(),
59 keypair: String::default(),
61 },
62 }
63 }
64
65 pub fn from_toml(path: &str) -> Result<Manifest> {
67 let manifest_path = match Path::new(path).canonicalize() {
68 Ok(p) => p,
69 Err(e) => bail!(e),
70 };
71
72 let manifest_string = read_to_string(manifest_path)?;
73
74 let table: Table = match manifest_string.parse::<Table>() {
75 Ok(t) => t,
76 Err(e) => bail!(e),
77 };
78
79 let workspace_table = match table["workspace"].as_table() {
84 Some(t) => t,
85 None => bail!(ManifestError::MissingTable("workspace".to_string())),
86 };
87
88 ensure_syntax(
89 "Workspace must have a name".to_string(),
90 workspace_table.contains_key("name"),
91 );
92
93 let name = workspace_table.get("name").unwrap().as_str().unwrap();
95
96 let storage_table = match table["storage"].as_table() {
101 Some(t) => t,
102 None => bail!(ManifestError::MissingTable("storage".to_string())),
103 };
104
105 ensure_syntax(
106 "Workspace storage must have a repository".to_string(),
107 storage_table.contains_key("repository"),
108 );
109
110 let repo_value = storage_table.get("repository").unwrap().as_str().unwrap();
112 let repository = Self::parse_repository(repo_value.to_string())?;
113
114 let mut manifest = Self::new_solana(name.to_string());
115 manifest.storage = repository;
116
117 let solana_table = match table["solana"].as_table() {
122 Some(t) => t,
123 None => bail!(ManifestError::MissingTable("solana".to_string())),
124 };
125
126 ensure_syntax(
131 "Solana table must have a store".to_string(),
132 solana_table.contains_key("store"),
133 );
134 ensure_syntax(
135 "Solana table must have an rpc".to_string(),
136 solana_table.contains_key("rpc"),
137 );
138 ensure_syntax(
143 "Solana table must have a keypair".to_string(),
144 solana_table.contains_key("keypair"),
145 );
146
147 let store = solana_table
149 .get("store")
150 .unwrap()
151 .as_str()
152 .unwrap()
153 .to_string();
154 let rpc = solana_table
155 .get("rpc")
156 .unwrap()
157 .as_str()
158 .unwrap()
159 .to_string();
160 let keypair = solana_table
162 .get("keypair")
163 .unwrap()
164 .as_str()
165 .unwrap()
166 .to_string();
167
168 manifest.chain = Chain::Solana {
169 store,
170 rpc,
171 keypair,
172 };
173
174 Ok(manifest)
175 }
176
177 pub fn save_toml(manifest: Manifest, path: &str) -> Result<()> {
178 let name = manifest.name;
179 let repo: String = manifest.storage.into();
180 let Chain::Solana {
181 store,
182 rpc,
183 keypair,
184 } = manifest.chain;
185
186 let filename = Self::FILENAME;
187
188 let path = Path::new(path);
189 let manifest_path = path.join(filename);
190 let mut toml_file = File::create(manifest_path)?;
191
192 toml_file.write_all(
194 format!("[workspace]\nname = \"{name}\"\n\n[storage]\nrepository = \"{repo}\"\n\n[solana]\nstore = \"{store}\"\nrpc = \"{rpc}\"\nkeypair = \"{keypair}\"",
195 ).as_bytes())?;
196
197 Ok(())
198 }
199
200 fn parse_repository(repo_string: String) -> Result<Repository> {
201 let repo = match repo_string.as_str() {
202 "solana" => Repository::Solana,
203 "memory" => Repository::InMemory,
204 _ => bail!(ManifestError::UnsupportedRepo(repo_string)),
205 };
206
207 Ok(repo)
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::*;
214
215 #[test]
217 fn test_from_toml() {
218 let manifest = Manifest::from_toml("fixtures/Rush.toml").unwrap();
219 assert_eq!(manifest.name, "WORKSPACE");
220 assert_eq!(manifest.storage, Repository::Solana);
221 assert_eq!(
222 manifest.chain,
223 Chain::Solana {
224 store: "STORE".to_string(),
225 rpc: "RPC".to_string(),
226 keypair: "KEYPAIR".to_string()
227 }
228 );
229 }
230
231 #[test]
232 fn test_save_toml_without_trailing_slash() {
234 let mut manifest = Manifest::new_solana("WORKSPACE".to_string());
235 let store = "STORE".to_string();
236 let rpc = "RPC".to_string();
237 let keypair = "KEYPAIR".to_string();
238
239 manifest.chain = Chain::Solana {
240 store: store.clone(),
241 rpc: rpc.clone(),
242 keypair: keypair.clone(),
243 };
244
245 Manifest::save_toml(manifest, "fixtures/save").unwrap();
246 }
247
248 #[test]
249 fn test_save_toml_with_trailing_slash() {
251 let mut manifest = Manifest::new_solana("WORKSPACE".to_string());
252 let store = "STORE".to_string();
253 let rpc = "RPC".to_string();
254 let keypair = "KEYPAIR".to_string();
255
256 manifest.chain = Chain::Solana {
257 store: store.clone(),
258 rpc: rpc.clone(),
259 keypair: keypair.clone(),
260 };
261
262 Manifest::save_toml(manifest, "fixtures/save/").unwrap();
263 }
264}