From 92a2beae8145fc9c8d6e397bfcbccca74c16a819 Mon Sep 17 00:00:00 2001 From: Mario Date: Sat, 5 Aug 2023 17:15:53 +0300 Subject: [PATCH] added a way better toml handling --- Cargo.lock | 49 +++++++++++++++++- Cargo.toml | 3 +- README.md | 1 - soldeer.toml | 10 ++-- src/config.rs | 98 +++++++++++++++--------------------- src/dependency_downloader.rs | 23 +++------ src/janitor.rs | 6 +-- src/main.rs | 34 ++++++++----- 8 files changed, 126 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 986435f..90360ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,6 +149,16 @@ dependencies = [ "inout", ] +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -249,6 +259,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + [[package]] name = "encoding_rs" version = "0.8.32" @@ -593,6 +609,15 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.8" @@ -617,6 +642,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kstring" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b310ccceade8121d7d77fee406160e457c2f4e7c7982d589da3499bc7ea4526" +dependencies = [ + "serde", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -1135,6 +1169,7 @@ dependencies = [ "tokio", "tokio-dl-stream-to-disk", "toml", + "toml_edit 0.9.1", "zip-extract", ] @@ -1297,7 +1332,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.19.12", ] [[package]] @@ -1309,6 +1344,18 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_edit" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b26d63d75583ce572323a963b850d2121cf47a03b1f6c5fc96d641d3b0412b3" +dependencies = [ + "combine", + "indexmap 1.9.3", + "itertools", + "kstring", +] + [[package]] name = "toml_edit" version = "0.19.12" diff --git a/Cargo.toml b/Cargo.toml index 10041a9..087a2f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,4 +15,5 @@ tokio = "1.29.1" tokio-dl-stream-to-disk = "1.0.0" zip-extract = "0.1.2" curl = "0.4.44" -reqwest = "0.11.18" \ No newline at end of file +reqwest = "0.11.18" +toml_edit = "0.9.1" \ No newline at end of file diff --git a/README.md b/README.md index 1ddc0e4..775bccf 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,6 @@ The full list of dependencies is available [here](./all_dependencies.toml). ### TODO -- A better way to write to the TOML file. - Parallel downloads of the dependencies. - A better way to handle the dependencies. - Error handling. diff --git a/soldeer.toml b/soldeer.toml index 181393c..e0c2287 100644 --- a/soldeer.toml +++ b/soldeer.toml @@ -1,8 +1,8 @@ -[remappings] -enabled = true + [dependencies] "openzeppelin~v4.9.2" = "https://github.com/OpenZeppelin/openzeppelin-contracts/archive/refs/tags/v4.9.2.zip" -"solady~v0.0.107" = "https://github.com/Vectorized/solady/archive/refs/tags/v0.0.107.zip" -"uniswap-v3-periphery~v1.0.0" = "https://github.com/Uniswap/v3-periphery/archive/refs/tags/v1.0.0.zip" -"solady~v0.0.49" = "https://github.com/Vectorized/solady/archive/refs/tags/v0.0.49.zip" +"openzeppelin~v1.0.5" = "https://github.com/OpenZeppelin/openzeppelin-contracts/archive/refs/tags/v1.0.5.zip" + +[remappings] +enabled = true diff --git a/src/config.rs b/src/config.rs index cac8efd..49c190e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,14 +1,12 @@ -use std::fs; -use std::path::PathBuf; -use std::path::Path; +use std::fs::{ self, File }; +use std::path::{ PathBuf, Path }; use std::process::exit; use serde_derive::Deserialize; -use toml; -use toml::Table; -use std::io::Write; -use std::fs::File; -use std::io::{ BufRead, BufReader }; +use toml::{ self, Table }; +use std::io::{ Write, BufRead, BufReader }; use crate::utils::get_current_working_dir; +extern crate toml_edit; +use toml_edit::{ Document, value }; // TODO need to improve this, to propagate the error to main and not exit here. pub fn read_config(filename: String) -> Vec { @@ -80,14 +78,10 @@ pub fn define_config_file() -> String { pub fn add_to_config(dependency_name: &str, dependency_version: &str, dependency_url: &str) { println!("Adding dependency {}-{} to config file", dependency_name, dependency_version); let filename: String = define_config_file(); - let dependencies: Vec = read_config(filename.clone()); - let mut dependency_exists: bool = false; - for dependency in dependencies.iter() { - if dependency.name == dependency_name && dependency.version == dependency_version { - dependency_exists = true; - } - } - if dependency_exists { + 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() { println!( "Dependency {}-{} already exists in the config file", dependency_name, @@ -95,21 +89,15 @@ pub fn add_to_config(dependency_name: &str, dependency_version: &str, dependency ); return; } + doc["dependencies"][format!("{}~{}", dependency_name, dependency_version)] = + value(dependency_url); let mut file: std::fs::File = fs::OpenOptions ::new() .write(true) - .append(true) + .append(false) .open(filename) .unwrap(); - if - let Err(e) = writeln!( - file, - "\n\"{}~{}\" = \"{}\"", - dependency_name, - dependency_version, - dependency_url - ) - { + if let Err(e) = write!(file, "{}", doc.to_string()) { eprintln!("Couldn't write to file: {}", e); } } @@ -122,26 +110,12 @@ pub fn remappings() { } fn update_foundry() { - //TODO need to create the remappings file if it does not exists. if !Path::new("remappings.txt").exists() { File::create("remappings.txt").unwrap(); } println!("Updating foundry..."); - // Read the contents of the file using a `match` block - // to return the `data: Ok(c)` as a `String` - // or handle any `errors: Err(_)`. - let contents: String = match fs::read_to_string("remappings.txt") { - // If successful return the files text as `contents`. - // `c` is a local variable. - Ok(c) => c, - // Handle the `error` case. - Err(_) => { - // Write `msg` to `stderr`. - eprintln!("Could not read file `{}`", "remappings.txt"); - // Exit the program with exit code `1`. - exit(1); - } - }; + let contents = read_file_to_string(String::from("remappings.txt")); + let existing_remappings: Vec = contents .split("\n") .map(|s| s.to_string()) @@ -160,7 +134,12 @@ fn update_foundry() { if index.is_none() { println!("Adding a new remap {}", &dependency.name); new_remappings.push_str( - &format!("{}=dependencies/{}-{}\n", &dependency.name, &dependency.name, &dependency.version) + &format!( + "{}=dependencies/{}-{}\n", + &dependency.name, + &dependency.name, + &dependency.version + ) ); } }); @@ -225,21 +204,8 @@ fn remove_empty_lines(filename: String) { fn enable_remappings() -> bool { let filename = define_config_file(); - // Read the contents of the file using a `match` block - // to return the `data: Ok(c)` as a `String` - // or handle any `errors: Err(_)`. - let contents: String = match fs::read_to_string(&filename) { - // If successful return the files text as `contents`. - // `c` is a local variable. - Ok(c) => c, - // Handle the `error` case. - Err(_) => { - // Write `msg` to `stderr`. - eprintln!("Could not read file `{}`", &filename); - // Exit the program with exit code `1`. - exit(1); - } - }; + + let contents: String = read_file_to_string(filename.clone()); // Use a `match` block to return the // file `contents` as a `Data struct: Ok(d)` @@ -259,6 +225,22 @@ fn enable_remappings() -> bool { }; return data.remappings.get("enabled").unwrap().as_bool().unwrap(); } + +fn read_file_to_string(filename: String) -> String { + let contents: String = match fs::read_to_string(&filename) { + // If successful return the files text as `contents`. + // `c` is a local variable. + Ok(c) => c, + // Handle the `error` case. + Err(_) => { + // Write `msg` to `stderr`. + eprintln!("Could not read file `{}`", &filename); + // Exit the program with exit code `1`. + exit(1); + } + }; + return contents; +} // Top level struct to hold the TOML data. #[derive(Deserialize)] #[derive(Debug)] diff --git a/src/dependency_downloader.rs b/src/dependency_downloader.rs index 8d85c60..6894866 100644 --- a/src/dependency_downloader.rs +++ b/src/dependency_downloader.rs @@ -1,22 +1,14 @@ use std::path::Path; -use std::io::Cursor; -use std::io::Read; -use std::io::BufReader; -use std::fs::File; +use std::io::{ Cursor, Read, BufReader }; +use reqwest::{ get, Response }; +use std::fs::{ self, remove_file, File }; use std::fmt; use tokio_dl_stream_to_disk::AsyncDownload; use zip_extract::ZipExtractError; -use reqwest::get; -use reqwest::Response; -use std::fs; -use std::fs::remove_file; -use crate::config::Dependency; -use crate::config::read_config; +use crate::config::{ Dependency, read_config }; use crate::utils::get_current_working_dir; -const REMOTE_REPOSITORY: &str = "https://raw.githubusercontent.com/mario-eth/soldeer/main/all_dependencies.toml"; - // TODOs: // - needs to be downloaded in parallel pub async fn download_dependencies(dependencies: &Vec) -> Result<(), DownloadError> { @@ -50,11 +42,10 @@ pub fn unzip_dependencies(dependencies: &Vec) -> Result<(), ZipExtra pub async fn download_dependency_remote( dependency_name: &String, - dependency_version: &String + dependency_version: &String, + remote_url: &String ) -> Result { - let res: Response = get( - format!("{}/{}~{}.zip", REMOTE_REPOSITORY, dependency_name, dependency_version) - ).await.unwrap(); + let res: Response = get(format!("{}", remote_url)).await.unwrap(); let body: String = res.text().await.unwrap(); let tmp_path: std::path::PathBuf = get_current_working_dir() .unwrap() diff --git a/src/janitor.rs b/src/janitor.rs index 9e3e48f..2ec2d48 100644 --- a/src/janitor.rs +++ b/src/janitor.rs @@ -1,4 +1,4 @@ -use std::fs; +use std::fs::{ metadata, remove_file }; use crate::config::Dependency; use crate::utils::get_current_working_dir; @@ -51,7 +51,7 @@ pub fn healthcheck_dependency( println!("Health-checking dependency {}-{}", dependency_name, dependency_version); let file_name: String = format!("{}-{}", &dependency_name, &dependency_version); let new_path: std::path::PathBuf = get_current_working_dir().unwrap().join("dependencies"); - match fs::metadata(new_path.join(file_name)) { + match metadata(new_path.join(file_name)) { Ok(_) => { Ok(()) } Err(_) => { return Err(MissingDependencies::new(&dependency_name)); @@ -66,7 +66,7 @@ pub fn cleanup_dependency( println!("Cleaning up dependency {}-{}", dependency_name, dependency_version); let file_name: String = format!("{}-{}.zip", dependency_name, dependency_version); let new_path: std::path::PathBuf = get_current_working_dir().unwrap().join("dependencies"); - match fs::remove_file(new_path.join(file_name)) { + match remove_file(new_path.join(file_name)) { Ok(_) => { Ok(()) } Err(_) => { return Err(MissingDependencies::new(&dependency_name)); diff --git a/src/main.rs b/src/main.rs index 24c4878..8d80ec5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,28 +6,31 @@ mod janitor; use std::process::exit; use std::env; -use crate::config::read_config; -use crate::config::remappings; -use crate::config::Dependency; -use crate::dependency_downloader::download_dependencies; -use crate::dependency_downloader::unzip_dependencies; -use crate::dependency_downloader::unzip_dependency; -use crate::janitor::healthcheck_dependencies; -use crate::janitor::cleanup_after; +use crate::config::{ read_config, remappings, Dependency }; +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"; #[tokio::main] async fn main() { let args: Vec = env::args().collect(); - let command: (String, String) = process_args(args).unwrap(); + let command: (String, String, String) = process_args(args).unwrap(); 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(); + if command.2 != "" { + remote_url = command.2; + } match dependency_downloader::download_dependency_remote( &dependency_name, - &dependency_version + &dependency_version, + &remote_url ).await { Ok(url) => { @@ -91,17 +94,22 @@ async fn main() { "Usage: soldeer [command] [dependency] Example: dependency~version. the `~` is very important to differentiate between the name and the version that needs to be installed." ); println!("Commands:"); - println!(" install [dependency] - install a dependency"); + println!( + " install [dependency] (remote_url) - install a dependency, the `remote_url` is optional and defaults to the soldeer repository" + ); println!(" update - update all dependencies"); println!(" help - show this help"); } } -fn process_args(args: Vec) -> Result<(String, String), ()> { +fn process_args(args: Vec) -> Result<(String, String, String), ()> { let command: String = String::from(&args[1]); let mut dependency: String = String::new(); if args.len() > 2 { dependency = String::from(&args[2]); } - return Ok((command, dependency)); + if args.len() > 3 { + return Ok((command, dependency, String::from(&args[3]))); + } + return Ok((command, dependency, String::new())); }