loam_cli/commands/build/
env_toml.rs1use indexmap::IndexMap;
2use serde::Deserialize;
3use std::collections::BTreeMap as Map;
4use std::path::Path;
5use toml::value::Table;
6
7pub const ENV_FILE: &str = "environments.toml";
8
9#[derive(thiserror::Error, Debug)]
10pub enum Error {
11 #[error("⛔ ️parsing environments.toml: {0}")]
12 ParsingToml(#[from] toml::de::Error),
13 #[error("⛔ ️no settings for current LOAM_ENV ({0:?}) found in environments.toml")]
14 NoSettingsForCurrentEnv(String),
15 #[error("⛔ ️reading environments.toml as a string: {0}")]
16 ParsingString(#[from] std::io::Error),
17}
18
19type Environments = Map<Box<str>, Environment>;
20
21#[derive(Debug, Clone)]
22pub struct Environment {
23 pub accounts: Option<Vec<Account>>,
24 pub network: Network,
25 pub contracts: Option<IndexMap<Box<str>, Contract>>,
26}
27
28fn deserialize_accounts<'de, D>(deserializer: D) -> Result<Option<Vec<Account>>, D::Error>
29where
30 D: serde::Deserializer<'de>,
31{
32 let opt: Option<Vec<AccountRepresentation>> = Option::deserialize(deserializer)?;
33 Ok(opt.map(|vec| vec.into_iter().map(Account::from).collect()))
34}
35
36impl<'de> Deserialize<'de> for Environment {
37 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
38 where
39 D: serde::Deserializer<'de>,
40 {
41 #[derive(Deserialize)]
42 struct EnvironmentHelper {
43 #[serde(default, deserialize_with = "deserialize_accounts")]
44 accounts: Option<Vec<Account>>,
45 network: Network,
46 contracts: Option<Table>,
47 }
48
49 let helper = EnvironmentHelper::deserialize(deserializer)?;
50
51 let contracts = helper
52 .contracts
53 .map(|contracts_table| {
54 contracts_table
55 .into_iter()
56 .map(|(key, value)| {
57 let contract: Contract =
58 Contract::deserialize(value).map_err(serde::de::Error::custom)?;
59 Ok((key.into_boxed_str(), contract))
60 })
61 .collect::<Result<IndexMap<_, _>, D::Error>>()
62 })
63 .transpose()?;
64
65 Ok(Environment {
66 accounts: helper.accounts,
67 network: helper.network,
68 contracts,
69 })
70 }
71}
72
73#[derive(Debug, serde::Deserialize, Clone)]
74#[serde(rename_all = "kebab-case")]
75pub struct Network {
76 pub name: Option<String>,
77 pub rpc_url: Option<String>,
78 pub network_passphrase: Option<String>,
79 pub rpc_headers: Option<Vec<(String, String)>>,
80 #[serde(skip_serializing_if = "is_false", default)]
81 pub run_locally: bool,
82}
83
84#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
85#[serde(untagged)]
86pub enum AccountRepresentation {
87 Simple(String),
88 Detailed(Account),
89}
90
91#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
92pub struct Account {
93 pub name: String,
94 #[serde(default)]
95 pub default: bool,
96}
97
98impl From<AccountRepresentation> for Account {
99 fn from(rep: AccountRepresentation) -> Self {
100 match rep {
101 AccountRepresentation::Simple(name) => Account {
102 name,
103 default: false,
104 },
105 AccountRepresentation::Detailed(account) => account,
106 }
107 }
108}
109
110#[derive(Debug, Deserialize, Clone, Default)]
111pub struct Contract {
112 #[serde(default = "default_client", skip_serializing_if = "std::ops::Not::not")]
113 pub client: bool,
114
115 #[serde(default, skip_serializing_if = "Option::is_none")]
116 pub init: Option<String>,
117
118 #[serde(default, skip_serializing_if = "Option::is_none")]
119 pub id: Option<String>,
120}
121
122fn default_client() -> bool {
123 true
124}
125
126impl Environment {
127 pub fn get(workspace_root: &Path, loam_env: &str) -> Result<Option<Environment>, Error> {
128 let env_toml = workspace_root.join(ENV_FILE);
129
130 if !env_toml.exists() {
131 return Ok(None);
132 }
133
134 let toml_str = std::fs::read_to_string(env_toml)?;
135 let mut parsed_toml: Environments = toml::from_str(&toml_str)?;
136 let current_env = parsed_toml.remove(loam_env);
137 if current_env.is_none() {
138 return Err(Error::NoSettingsForCurrentEnv(loam_env.to_string()));
139 };
140 Ok(current_env)
141 }
142}