diff --git a/.gitignore b/.gitignore index a039880..49ab8d2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /target -dependencies/ \ No newline at end of file +dependencies/ +foundry.toml +.dependency_reading.toml \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index cf95c4e..3c68b86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1069,6 +1069,7 @@ dependencies = [ name = "soldeer" version = "0.1.0" dependencies = [ + "lazy_static", "reqwest", "serde", "serde_derive", diff --git a/Cargo.toml b/Cargo.toml index a9e77e1..728a2fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,4 +21,5 @@ tokio = "1.29.1" tokio-dl-stream-to-disk = "1.0.0" zip-extract = "0.1.2" reqwest = "0.11.18" -toml_edit = "0.9.1" \ No newline at end of file +toml_edit = "0.9.1" +lazy_static = "1.4.0" \ No newline at end of file diff --git a/README.md b/README.md index c5f71f0..0d858c4 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,10 @@ WARNING! The `[remappings]` must be first then the `[dependencies]`. The `enable The full list of dependencies is available [here](./all_dependencies.toml). +### CAVEATS + +The add to remappings feature just appends to the `remappings.txt`` file, it does not delete old dependencies. So if you want to remove a dependency from remappings you have to do it manually. + ### TODO - Parallel downloads of the dependencies. diff --git a/soldeer.toml b/soldeer.toml index ad7d8b9..7f9f127 100644 --- a/soldeer.toml +++ b/soldeer.toml @@ -1,9 +1,9 @@ -[dependencies] +[sdependencies] "@openzeppelin~v4.9.2" = "https://github.com/OpenZeppelin/openzeppelin-contracts/archive/refs/tags/v4.9.2.zip" "@openzeppelin~v1.0.5" = "https://github.com/OpenZeppelin/openzeppelin-contracts/archive/refs/tags/v1.0.5.zip" -"@solady~v0.0.41" = "https://github.com/Vectorized/solady/archive/refs/tags/v0.0.41.zip" -[remappings] +[foundry] enabled = true +foundry-config = true \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index bd5a71e..a4e1194 100644 --- a/src/config.rs +++ b/src/config.rs @@ -7,12 +7,13 @@ use std::io::{ Write, BufRead, BufReader }; use crate::utils::get_current_working_dir; extern crate toml_edit; use toml_edit::{ Document, value }; +use crate::FOUNDRY; // TODO need to improve this, to propagate the error to main and not exit here. -pub fn read_config(filename: String) -> Vec { +pub fn read_config(filename: String, foundry_setup: &FOUNDRY) -> Vec { let mut filename: String = filename; if filename == "" { - filename = define_config_file(); + filename = define_config_file(foundry_setup); } // Read the contents of the file using a `match` block // to return the `data: Ok(c)` as a `String` @@ -48,7 +49,7 @@ pub fn read_config(filename: String) -> Vec { }; let mut dependencies: Vec = Vec::new(); - data.dependencies.iter().for_each(|(k, v)| { + data.sdependencies.iter().for_each(|(k, v)| { let parts: Vec<&str> = k.split("~").collect::>(); dependencies.push(Dependency { name: parts.get(0).unwrap().to_string(), @@ -60,28 +61,40 @@ pub fn read_config(filename: String) -> Vec { return dependencies; } -pub fn define_config_file() -> String { +pub fn define_config_file(foundry_setup: &FOUNDRY) -> String { // reading the current directory to look for the config file let working_dir: Result = get_current_working_dir(); - let filename: String = - working_dir.unwrap().into_os_string().into_string().unwrap() + "/soldeer.toml"; + let mut filename: String = + working_dir.as_ref().unwrap().clone().into_os_string().into_string().unwrap().to_owned() + + "/soldeer.toml"; + + if foundry_setup.config { + filename = + working_dir.as_ref().unwrap().clone().into_os_string().into_string().unwrap().clone() + + "/foundry.toml"; + } let exists: bool = Path::new(&filename).exists(); if exists { - println!("Config file exists."); + // println!("Config file exists."); } else { eprintln!("Config file does not exist. Program exited."); exit(404); } return filename; } -pub fn add_to_config(dependency_name: &str, dependency_version: &str, dependency_url: &str) { +pub fn add_to_config( + dependency_name: &str, + dependency_version: &str, + dependency_url: &str, + foundry_setup: &FOUNDRY +) { println!("Adding dependency {}-{} to config file", dependency_name, dependency_version); - let filename: String = define_config_file(); + let filename: String = define_config_file(foundry_setup); let contents = read_file_to_string(filename.clone()); let mut doc: Document = contents.parse::().expect("invalid doc"); - if !doc["dependencies"].get(format!("{}~{}", dependency_name, dependency_version)).is_none() { + if !doc["sdependencies"].get(format!("{}~{}", dependency_name, dependency_version)).is_none() { println!( "Dependency {}-{} already exists in the config file", dependency_name, @@ -89,7 +102,7 @@ pub fn add_to_config(dependency_name: &str, dependency_version: &str, dependency ); return; } - doc["dependencies"][format!("{}~{}", dependency_name, dependency_version)] = + doc["sdependencies"][format!("{}~{}", dependency_name, dependency_version)] = value(dependency_url); let mut file: std::fs::File = fs::OpenOptions ::new() @@ -102,31 +115,39 @@ pub fn add_to_config(dependency_name: &str, dependency_version: &str, dependency } } -pub fn remappings() { - if !enable_remappings() { - return; - } - update_foundry(); -} - -fn update_foundry() { +pub fn remappings(foundry_setup: &FOUNDRY) { if !Path::new("remappings.txt").exists() { File::create("remappings.txt").unwrap(); } println!("Updating foundry..."); + let contents = read_file_to_string(String::from("remappings.txt")); + + let existing_remappings: Vec = contents + .split("\n") + .map(|s| s.to_string()) + .collect(); let mut new_remappings: String = String::new(); - let dependencies: Vec = read_config(String::new()); + let dependencies: Vec = read_config(String::new(), foundry_setup); + + let mut existing_remap: Vec = Vec::new(); + existing_remappings.iter().for_each(|remapping| { + let split: Vec<&str> = remapping.split("=").collect::>(); + existing_remap.push(String::from(split[0])); + }); dependencies.iter().for_each(|dependency| { - println!("Adding a new remap {}", &dependency.name); - new_remappings.push_str( - &format!( - "{}=dependencies/{}-{}\n", - &dependency.name, - &dependency.name, - &dependency.version - ) - ); + let index = existing_remap.iter().position(|r| r == &dependency.name); + if index.is_none() { + println!("Adding a new remap {}", &dependency.name); + new_remappings.push_str( + &format!( + "{}=dependencies/{}-{}\n", + &dependency.name, + &dependency.name, + &dependency.version + ) + ); + } }); if new_remappings.len() == 0 { @@ -137,11 +158,10 @@ fn update_foundry() { let mut file: std::fs::File = fs::OpenOptions ::new() .write(true) - .truncate(true) - .append(false) + .append(true) .open(Path::new("remappings.txt")) .unwrap(); - println!("New remappings: {}", &new_remappings); + match write!(file, "{}", &new_remappings) { Ok(_) => {} Err(e) => { @@ -188,15 +208,15 @@ fn remove_empty_lines(filename: String) { } } -fn enable_remappings() -> bool { - let filename = define_config_file(); +pub fn get_foundry_setup() -> Vec { + let filename = define_config_file(&(FOUNDRY { remappings: false, config: false })); let contents: String = read_file_to_string(filename.clone()); // Use a `match` block to return the // file `contents` as a `Data struct: Ok(d)` // or handle any `errors: Err(_)`. - let data: Remmapings = match toml::from_str(&contents) { + let data: Foundry = match toml::from_str(&contents) { // If successful, return data as `Data` struct. // `d` is a local variable. Ok(d) => d, @@ -209,7 +229,11 @@ fn enable_remappings() -> bool { exit(1); } }; - return data.remappings.get("enabled").unwrap().as_bool().unwrap(); + + return vec![ + data.foundry.get("enabled").unwrap().as_bool().unwrap(), + data.foundry.get("foundry-config").unwrap().as_bool().unwrap() + ]; } fn read_file_to_string(filename: String) -> String { @@ -231,7 +255,7 @@ fn read_file_to_string(filename: String) -> String { #[derive(Deserialize)] #[derive(Debug)] struct Data { - dependencies: Table, + sdependencies: Table, } // Dependency object used to store a dependency data @@ -244,6 +268,6 @@ pub struct Dependency { #[derive(Deserialize)] #[derive(Debug)] -struct Remmapings { - remappings: Table, +struct Foundry { + foundry: Table, } diff --git a/src/dependency_downloader.rs b/src/dependency_downloader.rs index 58ef0b8..b051159 100644 --- a/src/dependency_downloader.rs +++ b/src/dependency_downloader.rs @@ -8,6 +8,7 @@ use zip_extract::ZipExtractError; use crate::config::{ Dependency, read_config }; use crate::utils::get_current_working_dir; +use crate::FOUNDRY; // TODOs: // - needs to be downloaded in parallel @@ -51,7 +52,8 @@ pub fn unzip_dependencies(dependencies: &Vec) -> Result<(), ZipExtra pub async fn download_dependency_remote( dependency_name: &String, dependency_version: &String, - remote_url: &String + remote_url: &String, + foundry_setup: &FOUNDRY ) -> Result { let res: Response = get(format!("{}", remote_url)).await.unwrap(); let body: String = res.text().await.unwrap(); @@ -59,7 +61,7 @@ pub async fn download_dependency_remote( .unwrap() .join(".dependency_reading.toml"); fs::write(&tmp_path, body).expect("Unable to write file"); - let dependencies: Vec = read_config((&tmp_path).to_str().unwrap().to_string()); + let dependencies: Vec = read_config((&tmp_path).to_str().unwrap().to_string(), foundry_setup); for dependency in dependencies.iter() { if dependency.name == *dependency_name && dependency.version == *dependency_version { println!("dependency url: {}", dependency.url); diff --git a/src/main.rs b/src/main.rs index 06a3ce1..1e2188b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,23 +6,36 @@ mod janitor; use std::process::exit; use std::env; -use crate::config::{ read_config, remappings, Dependency }; +use crate::config::{ read_config, remappings, Dependency, get_foundry_setup }; use crate::dependency_downloader::{ download_dependencies, unzip_dependencies, unzip_dependency }; use crate::janitor::{ healthcheck_dependencies, cleanup_after }; const REMOTE_REPOSITORY: &str = "https://raw.githubusercontent.com/mario-eth/soldeer/main/all_dependencies.toml"; +#[derive(Debug)] +pub struct FOUNDRY { + remappings: bool, + config: bool, +} #[tokio::main] async fn main() { let args: Vec = env::args().collect(); let command: (String, String, String) = process_args(args).unwrap(); + // setup the foundry setup, in case it's enabled inside the soldeer.toml, then the foundry.toml will be used for + // `sdependencies` + let f_setup_vec: Vec = get_foundry_setup(); + let foundry_setup: FOUNDRY = FOUNDRY { + remappings: f_setup_vec[0], + config: f_setup_vec[1], + }; + if command.0 == "install" && command.1 != "" { let dependency_name: String = command.1.split("~").collect::>()[0].to_string(); let dependency_version: String = command.1.split("~").collect::>()[1].to_string(); let dependency_url: String; - let mut remote_url = REMOTE_REPOSITORY.to_string(); + let mut remote_url: String = REMOTE_REPOSITORY.to_string(); if command.2 != "" { remote_url = command.2; } @@ -30,7 +43,8 @@ async fn main() { dependency_downloader::download_dependency_remote( &dependency_name, &dependency_version, - &remote_url + &remote_url, + &foundry_setup ).await { Ok(url) => { @@ -49,7 +63,12 @@ async fn main() { } } // TODO this is kinda junky written, need to refactor and a better TOML writer - config::add_to_config(&dependency_name, &dependency_version, &dependency_url); + config::add_to_config( + &dependency_name, + &dependency_version, + &dependency_url, + &foundry_setup + ); match janitor::healthcheck_dependency(&dependency_name, &dependency_version) { Ok(_) => {} Err(err) => { @@ -64,9 +83,11 @@ async fn main() { exit(500); } } - remappings(); + if foundry_setup.remappings { + remappings(&foundry_setup); + } } else if command.0 == "update" || (command.0 == "install" && command.1 == "") { - let dependencies: Vec = read_config(String::new()); + let dependencies: Vec = read_config(String::new(), &foundry_setup); if download_dependencies(&dependencies, true).await.is_err() { eprintln!("Error downloading dependencies"); exit(500); @@ -88,7 +109,9 @@ async fn main() { eprintln!("Error cleanup dependencies {:?}", result.err().unwrap().name); exit(500); } - remappings(); + if foundry_setup.remappings { + remappings(&foundry_setup); + } } else if command.0 == "help" { println!( "Usage: soldeer [command] [dependency] Example: dependency~version. the `~` is very important to differentiate between the name and the version that needs to be installed."