use super::{Context, Module, ModuleConfig}; use crate::configs::solidity::SolidityConfig; use crate::formatter::{StringFormatter, VersionFormatter}; use crate::utils::get_command_string_output; pub fn module<'a>(context: &'a Context) -> Option> { let mut module = context.new_module("solidity"); let config = SolidityConfig::try_load(module.config); let is_solidity_project = context .try_begin_scan()? .set_files(&config.detect_files) .set_extensions(&config.detect_extensions) .set_folders(&config.detect_folders) .is_match(); if !is_solidity_project { return None; } let parsed = StringFormatter::new(config.format).and_then(|formatter| { formatter .map_meta(|variable, _| match variable { "symbol" => Some(config.symbol), _ => None, }) .map_style(|variable| match variable { "style" => Some(Ok(config.style)), _ => None, }) .map(|variable| match variable { "version" => { let version = get_solidity_version(context, &config)?; VersionFormatter::format_module_version( module.get_name(), &version, config.version_format, ) .map(Ok) } _ => None, }) .parse(None, Some(context)) }); module.set_segments(match parsed { Ok(segments) => segments, Err(error) => { log::warn!("Error in module 'solidity'\n {}", error); return None; } }); Some(module) } fn get_solidity_version(context: &Context, config: &SolidityConfig) -> Option { let version = config .compiler .0 .iter() .find_map(|compiler_name| context.exec_cmd(compiler_name, &["--version"])) .map(get_command_string_output)?; parse_solidity_version(&version) } fn parse_solidity_version(version: &str) -> Option { /*solc --version output looks like "solc, the solidity compiler commandline interface Version: 0.8.16+commit.07a7930e.Linux.g++" solcjs --version out looks like 0.8.15+commit.e14f2714.Emscripten.clang */ let version_var = match version.split_whitespace().nth(7) { // Will return Some(x) for solc --version and None for solcjs --version Some(c) => c.split_terminator('+').next()?, //Isolates the versioning number e.g "0.8.16" None => version.split_terminator('+').next()?, //Isolates the version number e.g "0.8.15" }; Some(version_var.to_string()) } #[cfg(test)] mod tests { use super::*; use crate::test::ModuleRenderer; use nu_ansi_term::Color; use std::fs::File; use std::io; #[test] fn test_parse_solc_version() { let input = "solc, the solidity compiler commandline interface Version: 0.8.16+commit.07a7930e.Linux.g++"; assert_eq!(parse_solidity_version(input), Some(String::from("0.8.16"))); } #[test] fn test_parse_solcjs_version() { let input = "0.8.15+commit.e14f2714.Emscripten.clang"; assert_eq!(parse_solidity_version(input), Some(String::from("0.8.15"))); } #[test] fn folder_without_solidity_file() -> io::Result<()> { let dir = tempfile::tempdir()?; File::create(dir.path().join("solidity.txt"))?.sync_all()?; let actual = ModuleRenderer::new("solidity").path(dir.path()).collect(); let expected = None; assert_eq!(expected, actual); dir.close() } #[test] fn folder_with_solidity_file() -> io::Result<()> { let tempdir = tempfile::tempdir()?; // Create some file needed to render the module File::create(tempdir.path().join("main.sol"))?.sync_all()?; // The output of the module let actual = ModuleRenderer::new("solidity") // For a custom path .path(tempdir.path()) // Run the module and collect the output .collect(); // The value that should be rendered by the module. let expected = Some(format!("via {}", Color::Blue.bold().paint("S v0.8.16"))); // Assert that the actual and expected values are the same assert_eq!(actual, expected); // Close the tempdir tempdir.close() } #[test] fn testing_for_solcjs_render() -> io::Result<()> { let tempdir = tempfile::tempdir()?; // Create some file needed to render the module File::create(tempdir.path().join("main.sol"))?.sync_all()?; // The output of the module let actual = ModuleRenderer::new("solidity") // For a custom path .path(tempdir.path()) // For a custom config .config(toml::toml! { [solidity] compiler = "solcjs" }) // Run the module and collect the output .collect(); // The value that should be rendered by the module. let expected = Some(format!("via {}", Color::Blue.bold().paint("S v0.8.15"))); // Assert that the actual and expected values are the same assert_eq!(actual, expected); // Close the tempdir tempdir.close() } #[test] fn testing_sol_fallback() -> io::Result<()> { let tempdir = tempfile::tempdir()?; // Create some file needed to render the module File::create(tempdir.path().join("main.sol"))?.sync_all()?; // The output of the module let actual = ModuleRenderer::new("solidity") // For a custom path .path(tempdir.path()) // Make regular solc unavailable .cmd("solc --version", None) // Custom Config .config(toml::toml! { [solidity] compiler = ["solc", "solcjs"] }) // Run the module and collect the output .collect(); // The value that should be rendered by the module. let expected = Some(format!("via {}", Color::Blue.bold().paint("S v0.8.15"))); // Assert that the actual and expected values are the same assert_eq!(actual, expected); // Close the tempdir tempdir.close() } }