Skip to content

Commit 5119a7d

Browse files
committed
Refactor configuration package
commit-id:788f97c7
1 parent b05b202 commit 5119a7d

File tree

13 files changed

+166
-154
lines changed

13 files changed

+166
-154
lines changed

Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/configuration/Cargo.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,3 @@ serde.workspace = true
1313
camino.workspace = true
1414
toml.workspace = true
1515
tempfile.workspace = true
16-
scarb-metadata.workspace = true
17-

crates/configuration/src/core.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use crate::Config;
2+
use anyhow::anyhow;
3+
use serde_json::Number;
4+
use std::env;
5+
6+
fn resolve_env_variables(config: serde_json::Value) -> anyhow::Result<serde_json::Value> {
7+
match config {
8+
serde_json::Value::Object(map) => {
9+
let val = map
10+
.into_iter()
11+
.map(|(k, v)| -> anyhow::Result<(String, serde_json::Value)> {
12+
Ok((k, resolve_env_variables(v)?))
13+
})
14+
.collect::<anyhow::Result<serde_json::Map<String, serde_json::Value>>>()?;
15+
Ok(serde_json::Value::Object(val))
16+
}
17+
serde_json::Value::Array(val) => {
18+
let val = val
19+
.into_iter()
20+
.map(resolve_env_variables)
21+
.collect::<anyhow::Result<Vec<serde_json::Value>>>()?;
22+
Ok(serde_json::Value::Array(val))
23+
}
24+
serde_json::Value::String(val) if val.starts_with('$') => resolve_env_variable(&val),
25+
val => Ok(val),
26+
}
27+
}
28+
29+
fn resolve_env_variable(var: &str) -> anyhow::Result<serde_json::Value> {
30+
assert!(var.starts_with('$'));
31+
let mut initial_value = &var[1..];
32+
if initial_value.starts_with('{') && initial_value.ends_with('}') {
33+
initial_value = &initial_value[1..initial_value.len() - 1];
34+
}
35+
let value = env::var(initial_value)?;
36+
37+
if let Ok(value) = value.parse::<Number>() {
38+
return Ok(serde_json::Value::Number(value));
39+
}
40+
if let Ok(value) = value.parse::<bool>() {
41+
return Ok(serde_json::Value::Bool(value));
42+
}
43+
Ok(serde_json::Value::String(value))
44+
}
45+
46+
fn get_with_ownership(config: serde_json::Value, key: &str) -> Option<serde_json::Value> {
47+
match config {
48+
serde_json::Value::Object(mut map) => map.remove(key),
49+
_ => None,
50+
}
51+
}
52+
53+
fn get_profile(
54+
raw_config: serde_json::Value,
55+
tool: &str,
56+
profile: &str,
57+
) -> Option<serde_json::Value> {
58+
let profile_name = profile;
59+
let tool_config = get_with_ownership(raw_config, tool)
60+
.unwrap_or(serde_json::Value::Object(serde_json::Map::new()));
61+
62+
get_with_ownership(tool_config, profile_name)
63+
}
64+
65+
pub enum Profile {
66+
None,
67+
Default,
68+
Some(String),
69+
}
70+
71+
pub fn load_config<T: Config + Default>(
72+
raw_config: serde_json::Value,
73+
profile: Profile,
74+
) -> anyhow::Result<T> {
75+
let raw_config_json = match profile {
76+
Profile::None => raw_config,
77+
Profile::Default => get_profile(raw_config, T::tool_name(), "default")
78+
.unwrap_or_else(|| serde_json::Value::Object(serde_json::Map::new())),
79+
Profile::Some(profile) => get_profile(raw_config, T::tool_name(), &profile)
80+
.ok_or_else(|| anyhow!("Profile [{profile}] not found in config"))?,
81+
};
82+
T::from_raw(resolve_env_variables(raw_config_json)?)
83+
}

crates/configuration/src/lib.rs

Lines changed: 18 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1+
use crate::core::Profile;
12
use anyhow::{Context, Result, anyhow};
23
use camino::Utf8PathBuf;
3-
use scarb_metadata::{Metadata, PackageId};
4-
use serde_json::{Map, Number};
54
use std::fs::File;
65
use std::{env, fs};
7-
use tempfile::{TempDir, tempdir};
86
use toml::Value;
7+
8+
pub mod core;
9+
pub mod test_utils;
10+
911
pub const CONFIG_FILENAME: &str = "snfoundry.toml";
1012

11-
/// Defined in snfoundry.toml
1213
/// Configuration not associated with any specific package
1314
pub trait Config {
1415
#[must_use]
@@ -19,40 +20,6 @@ pub trait Config {
1920
Self: Sized;
2021
}
2122

22-
/// Defined in scarb manifest
23-
/// Configuration associated with a specific package
24-
pub trait PackageConfig {
25-
#[must_use]
26-
fn tool_name() -> &'static str;
27-
28-
fn from_raw(config: &serde_json::Value) -> Result<Self>
29-
where
30-
Self: Sized;
31-
}
32-
33-
fn get_with_ownership(config: serde_json::Value, key: &str) -> Option<serde_json::Value> {
34-
match config {
35-
serde_json::Value::Object(mut map) => map.remove(key),
36-
_ => None,
37-
}
38-
}
39-
40-
pub fn get_profile(
41-
raw_config: serde_json::Value,
42-
tool: &str,
43-
profile: Option<&str>,
44-
) -> Result<serde_json::Value> {
45-
let profile_name = profile.unwrap_or("default");
46-
let tool_config = get_with_ownership(raw_config, tool)
47-
.unwrap_or(serde_json::Value::Object(serde_json::Map::new()));
48-
49-
match get_with_ownership(tool_config, profile_name) {
50-
Some(profile_value) => Ok(profile_value),
51-
None if profile_name == "default" => Ok(serde_json::Value::Object(Map::default())),
52-
None => Err(anyhow!("Profile [{profile_name}] not found in config")),
53-
}
54-
}
55-
5623
#[must_use]
5724
pub fn resolve_config_file() -> Utf8PathBuf {
5825
find_config_file().unwrap_or_else(|_| {
@@ -83,70 +50,14 @@ pub fn load_config<T: Config + Default>(
8350
let raw_config_json = serde_json::to_value(raw_config_toml)
8451
.context("Conversion from TOML value to JSON value should not fail.")?;
8552

86-
let profile = get_profile(raw_config_json, T::tool_name(), profile)?;
87-
T::from_raw(resolve_env_variables(profile)?)
53+
core::load_config(
54+
raw_config_json,
55+
profile.map_or_else(|| Profile::Default, |p| Profile::Some(p.to_string())),
56+
)
8857
}
8958
None => Ok(T::default()),
9059
}
9160
}
92-
/// Loads config for a specific package from the `Scarb.toml` file
93-
/// # Arguments
94-
/// * `metadata` - Scarb metadata object
95-
/// * `package` - Id of the Scarb package
96-
pub fn load_package_config<T: PackageConfig + Default>(
97-
metadata: &Metadata,
98-
package: &PackageId,
99-
) -> Result<T> {
100-
let maybe_raw_metadata = metadata
101-
.get_package(package)
102-
.ok_or_else(|| anyhow!("Failed to find metadata for package = {package}"))?
103-
.tool_metadata(T::tool_name())
104-
.cloned();
105-
match maybe_raw_metadata {
106-
Some(raw_metadata) => T::from_raw(&resolve_env_variables(raw_metadata)?),
107-
None => Ok(T::default()),
108-
}
109-
}
110-
111-
fn resolve_env_variables(config: serde_json::Value) -> Result<serde_json::Value> {
112-
match config {
113-
serde_json::Value::Object(map) => {
114-
let val = map
115-
.into_iter()
116-
.map(|(k, v)| -> Result<(String, serde_json::Value)> {
117-
Ok((k, resolve_env_variables(v)?))
118-
})
119-
.collect::<Result<serde_json::Map<String, serde_json::Value>>>()?;
120-
Ok(serde_json::Value::Object(val))
121-
}
122-
serde_json::Value::Array(val) => {
123-
let val = val
124-
.into_iter()
125-
.map(resolve_env_variables)
126-
.collect::<Result<Vec<serde_json::Value>>>()?;
127-
Ok(serde_json::Value::Array(val))
128-
}
129-
serde_json::Value::String(val) if val.starts_with('$') => resolve_env_variable(&val),
130-
val => Ok(val),
131-
}
132-
}
133-
134-
fn resolve_env_variable(var: &str) -> Result<serde_json::Value> {
135-
assert!(var.starts_with('$'));
136-
let mut initial_value = &var[1..];
137-
if initial_value.starts_with('{') && initial_value.ends_with('}') {
138-
initial_value = &initial_value[1..initial_value.len() - 1];
139-
}
140-
let value = env::var(initial_value)?;
141-
142-
if let Ok(value) = value.parse::<Number>() {
143-
return Ok(serde_json::Value::Number(value));
144-
}
145-
if let Ok(value) = value.parse::<bool>() {
146-
return Ok(serde_json::Value::Bool(value));
147-
}
148-
Ok(serde_json::Value::String(value))
149-
}
15061

15162
pub fn search_config_upwards_relative_to(current_dir: &Utf8PathBuf) -> Result<Utf8PathBuf> {
15263
current_dir
@@ -166,30 +77,18 @@ pub fn find_config_file() -> Result<Utf8PathBuf> {
16677
)?)
16778
}
16879

169-
pub fn copy_config_to_tempdir(src_path: &str, additional_path: Option<&str>) -> Result<TempDir> {
170-
let temp_dir = tempdir().context("Failed to create a temporary directory")?;
171-
if let Some(dir) = additional_path {
172-
let path = temp_dir.path().join(dir);
173-
fs::create_dir_all(path).context("Failed to create directories in temp dir")?;
174-
}
175-
let temp_dir_file_path = temp_dir.path().join(CONFIG_FILENAME);
176-
fs::copy(src_path, temp_dir_file_path).context("Failed to copy config file to temp dir")?;
177-
178-
Ok(temp_dir)
179-
}
180-
18180
#[cfg(test)]
18281
mod tests {
18382
use std::fs::{self, File};
18483

84+
use super::*;
85+
use crate::test_utils::copy_config_to_tempdir;
18586
use serde::{Deserialize, Serialize};
18687
use tempfile::tempdir;
18788

188-
use super::*;
189-
19089
#[test]
19190
fn find_config_in_current_dir() {
192-
let tempdir = copy_config_to_tempdir("tests/data/stubtool_snfoundry.toml", None).unwrap();
91+
let tempdir = copy_config_to_tempdir("tests/data/stubtool_snfoundry.toml", None);
19392
let path = search_config_upwards_relative_to(
19493
&Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap(),
19594
)
@@ -200,7 +99,7 @@ mod tests {
20099
#[test]
201100
fn find_config_in_parent_dir() {
202101
let tempdir =
203-
copy_config_to_tempdir("tests/data/stubtool_snfoundry.toml", Some("childdir")).unwrap();
102+
copy_config_to_tempdir("tests/data/stubtool_snfoundry.toml", Some("childdir"));
204103
let path = search_config_upwards_relative_to(
205104
&Utf8PathBuf::try_from(tempdir.path().to_path_buf().join("childdir")).unwrap(),
206105
)
@@ -213,8 +112,7 @@ mod tests {
213112
let tempdir = copy_config_to_tempdir(
214113
"tests/data/stubtool_snfoundry.toml",
215114
Some("childdir1/childdir2"),
216-
)
217-
.unwrap();
115+
);
218116
let path = search_config_upwards_relative_to(
219117
&Utf8PathBuf::try_from(tempdir.path().to_path_buf().join("childdir1/childdir2"))
220118
.unwrap(),
@@ -226,8 +124,7 @@ mod tests {
226124
#[test]
227125
fn find_config_in_parent_dir_available_in_multiple_parents() {
228126
let tempdir =
229-
copy_config_to_tempdir("tests/data/stubtool_snfoundry.toml", Some("childdir1"))
230-
.unwrap();
127+
copy_config_to_tempdir("tests/data/stubtool_snfoundry.toml", Some("childdir1"));
231128
fs::copy(
232129
"tests/data/stubtool_snfoundry.toml",
233130
tempdir.path().join("childdir1").join(CONFIG_FILENAME),
@@ -270,7 +167,7 @@ mod tests {
270167
}
271168
#[test]
272169
fn load_config_happy_case_with_profile() {
273-
let tempdir = copy_config_to_tempdir("tests/data/stubtool_snfoundry.toml", None).unwrap();
170+
let tempdir = copy_config_to_tempdir("tests/data/stubtool_snfoundry.toml", None);
274171
let config = load_config::<StubConfig>(
275172
Some(&Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap()),
276173
Some(&String::from("profile1")),
@@ -282,7 +179,7 @@ mod tests {
282179

283180
#[test]
284181
fn load_config_happy_case_default_profile() {
285-
let tempdir = copy_config_to_tempdir("tests/data/stubtool_snfoundry.toml", None).unwrap();
182+
let tempdir = copy_config_to_tempdir("tests/data/stubtool_snfoundry.toml", None);
286183
let config = load_config::<StubConfig>(
287184
Some(&Utf8PathBuf::try_from(tempdir.path().to_path_buf()).unwrap()),
288185
None,
@@ -354,8 +251,7 @@ mod tests {
354251
#[expect(clippy::float_cmp)]
355252
fn resolve_env_vars() {
356253
let tempdir =
357-
copy_config_to_tempdir("tests/data/stubtool_snfoundry.toml", Some("childdir1"))
358-
.unwrap();
254+
copy_config_to_tempdir("tests/data/stubtool_snfoundry.toml", Some("childdir1"));
359255
fs::copy(
360256
"tests/data/stubtool_snfoundry.toml",
361257
tempdir.path().join("childdir1").join(CONFIG_FILENAME),
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use crate::CONFIG_FILENAME;
2+
use std::fs;
3+
use tempfile::{TempDir, tempdir};
4+
5+
#[must_use]
6+
pub fn copy_config_to_tempdir(src_path: &str, additional_path: Option<&str>) -> TempDir {
7+
let temp_dir = tempdir().expect("Failed to create a temporary directory");
8+
if let Some(dir) = additional_path {
9+
let path = temp_dir.path().join(dir);
10+
fs::create_dir_all(path).expect("Failed to create directories in temp dir");
11+
}
12+
let temp_dir_file_path = temp_dir.path().join(CONFIG_FILENAME);
13+
fs::copy(src_path, temp_dir_file_path).expect("Failed to copy config file to temp dir");
14+
15+
temp_dir
16+
}

crates/forge/src/run_tests/package.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use super::{
22
resolve_config::resolve_config,
33
test_target::{TestTargetRunResult, run_for_test_target},
44
};
5+
use crate::scarb::load_package_config;
56
use crate::{
67
TestArgs,
78
block_number_map::BlockNumberMap,
@@ -24,7 +25,6 @@ use crate::{
2425
use anyhow::Result;
2526
use camino::{Utf8Path, Utf8PathBuf};
2627
use cheatnet::runtime_extensions::forge_runtime_extension::contracts_data::ContractsData;
27-
use configuration::load_package_config;
2828
use console::Style;
2929
use forge_runner::{
3030
forge_config::ForgeConfig,

0 commit comments

Comments
 (0)