From b2ebd5b50c62fe5eb1cf8f5b0f79deaff2edd059 Mon Sep 17 00:00:00 2001 From: AnirbanHalder654322 <92542059+AnirbanHalder654322@users.noreply.github.com> Date: Fri, 14 Apr 2023 00:35:12 +0530 Subject: [PATCH] feat: Add Solidity Module (#5047) * Adding documentation * Documentation and schema addition * Creating solidity config * Module for solidity lang * Updating all the files * Changing according to clippy * Fixing misspellings * Changes suggested by clippy * Updating schema , maybe fixing docs workflow error * Updating schema * Removing solcjs from default compiler list * Fallback test added and test string fixed * Fixing docs * Updating schema * Updating schema * Fixing docs * Updating schema * Updating schema * Typo fix * Update docs/config/README.md Co-authored-by: David Knaack * Update src/utils.rs Co-authored-by: David Knaack * Fix build commit --------- Co-authored-by: Anirban Halder Co-authored-by: David Knaack --- .github/config-schema.json | 81 ++++++++ .../presets/toml/bracketed-segments.toml | 3 + .../presets/toml/no-runtime-versions.toml | 3 + .../presets/toml/plain-text-symbols.toml | 3 + docs/config/README.md | 40 ++++ src/configs/mod.rs | 3 + src/configs/solidity.rs | 39 ++++ src/configs/starship_root.rs | 1 + src/module.rs | 1 + src/modules/mod.rs | 3 + src/modules/solidity.rs | 184 ++++++++++++++++++ src/utils.rs | 8 + 12 files changed, 369 insertions(+) create mode 100644 src/configs/solidity.rs create mode 100644 src/modules/solidity.rs diff --git a/.github/config-schema.json b/.github/config-schema.json index 0ddb428e..d7e18cf7 100644 --- a/.github/config-schema.json +++ b/.github/config-schema.json @@ -1520,6 +1520,28 @@ } ] }, + "solidity": { + "default": { + "compiler": [ + "solc" + ], + "detect_extensions": [ + "sol" + ], + "detect_files": [], + "detect_folders": [], + "disabled": false, + "format": "via [$symbol($version)]($style)", + "style": "bold blue", + "symbol": "S ", + "version_format": "v${major}.${minor}.${patch}" + }, + "allOf": [ + { + "$ref": "#/definitions/SolidityConfig" + } + ] + }, "spack": { "default": { "disabled": false, @@ -5241,6 +5263,65 @@ }, "additionalProperties": false }, + "SolidityConfig": { + "type": "object", + "properties": { + "format": { + "default": "via [$symbol($version)]($style)", + "type": "string" + }, + "version_format": { + "default": "v${major}.${minor}.${patch}", + "type": "string" + }, + "disabled": { + "default": false, + "type": "boolean" + }, + "style": { + "default": "bold blue", + "type": "string" + }, + "symbol": { + "default": "S ", + "type": "string" + }, + "compiler": { + "default": [ + "solc" + ], + "allOf": [ + { + "$ref": "#/definitions/Either_for_String_and_Array_of_String" + } + ] + }, + "detect_extensions": { + "default": [ + "sol" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "detect_files": { + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "detect_folders": { + "default": [], + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, "SpackConfig": { "type": "object", "properties": { diff --git a/docs/.vuepress/public/presets/toml/bracketed-segments.toml b/docs/.vuepress/public/presets/toml/bracketed-segments.toml index c75fc3a7..f97b9c12 100644 --- a/docs/.vuepress/public/presets/toml/bracketed-segments.toml +++ b/docs/.vuepress/public/presets/toml/bracketed-segments.toml @@ -186,3 +186,6 @@ format = '\[[$symbol($version)]($style)\]' [zig] format = '\[[$symbol($version)]($style)\]' + +[solidity] +format = '\[[$symbol($version)]($style)\]' diff --git a/docs/.vuepress/public/presets/toml/no-runtime-versions.toml b/docs/.vuepress/public/presets/toml/no-runtime-versions.toml index 9778f285..dc573954 100644 --- a/docs/.vuepress/public/presets/toml/no-runtime-versions.toml +++ b/docs/.vuepress/public/presets/toml/no-runtime-versions.toml @@ -106,6 +106,9 @@ format = 'via [$symbol]($style)' [rust] format = 'via [$symbol]($style)' +[solidity] +format = 'via [$symbol]($style)' + [swift] format = 'via [$symbol]($style)' diff --git a/docs/.vuepress/public/presets/toml/plain-text-symbols.toml b/docs/.vuepress/public/presets/toml/plain-text-symbols.toml index 1bbc1d89..32126a01 100644 --- a/docs/.vuepress/public/presets/toml/plain-text-symbols.toml +++ b/docs/.vuepress/public/presets/toml/plain-text-symbols.toml @@ -197,6 +197,9 @@ symbol = "scala " [spack] symbol = "spack " +[solidity] +symbol = "solidity " + [status] symbol = "[x](bold red) " diff --git a/docs/config/README.md b/docs/config/README.md index a6f4de02..a889e1e1 100644 --- a/docs/config/README.md +++ b/docs/config/README.md @@ -311,6 +311,7 @@ $red\ $ruby\ $rust\ $scala\ +$solidity\ $swift\ $terraform\ $vlang\ @@ -3817,6 +3818,45 @@ and `$SINGULARITY_NAME` is set. format = '[📦 \[$env\]]($style) ' ``` +## Solidity + +The `solidity` module shows the currently installed version of [Solidity](https://soliditylang.org/) +The module will be shown if any of the following conditions are met: + +- The current directory contains a file with the `.sol` extension + +### Options + +| Option | Default | Description | +| ------------------- | ------------------------------------ | ------------------------------------------------------------------------- | +| `format` | `"via [$symbol($version )]($style)"` | The format for the module. | +| `version_format` | `"v${major}.${minor}.${patch}"` | The version format. Available vars are `raw`, `major`, `minor`, & `patch` | +| `symbol` | `"S "` | A format string representing the symbol of Solidity | +| `compiler | ["solc"] | The default compiler for Solidity. | +| `detect_extensions` | `["sol"]` | Which extensions should trigger this module. | +| `detect_files` | `[]` | Which filenames should trigger this module. | +| `detect_folders` | `[]` | Which folders should trigger this module. | +| `style` | `"bold blue"` | The style for the module. | +| `disabled` | `false` | Disables this module. | + +### Variables + +| Variable | Example | Description | +| -------- | -------- | ------------------------------------ | +| version | `v0.8.1` | The version of `solidity` | +| symbol | | Mirrors the value of option `symbol` | +| style\* | | Mirrors the value of option `style` | + +*: This variable can only be used as a part of a style string + +### Example + +```toml +# ~/.config/starship.toml +[solidity] +format = "via [S $version](blue bold)" +``` + ## Spack The `spack` module shows the current [Spack](https://spack.readthedocs.io/en/latest/) environment, if `$SPACK_ENV` is set. diff --git a/src/configs/mod.rs b/src/configs/mod.rs index a897e915..e218dd40 100644 --- a/src/configs/mod.rs +++ b/src/configs/mod.rs @@ -75,6 +75,7 @@ pub mod scala; pub mod shell; pub mod shlvl; pub mod singularity; +pub mod solidity; pub mod spack; mod starship_root; pub mod status; @@ -251,6 +252,8 @@ pub struct FullConfig<'a> { #[serde(borrow)] singularity: singularity::SingularityConfig<'a>, #[serde(borrow)] + solidity: solidity::SolidityConfig<'a>, + #[serde(borrow)] spack: spack::SpackConfig<'a>, #[serde(borrow)] status: status::StatusConfig<'a>, diff --git a/src/configs/solidity.rs b/src/configs/solidity.rs new file mode 100644 index 00000000..4d5bf84d --- /dev/null +++ b/src/configs/solidity.rs @@ -0,0 +1,39 @@ +use serde::{Deserialize, Serialize}; + +use crate::config::VecOr; + +#[derive(Clone, Deserialize, Serialize)] +#[cfg_attr( + feature = "config-schema", + derive(schemars::JsonSchema), + schemars(deny_unknown_fields) +)] +#[serde(default)] + +pub struct SolidityConfig<'a> { + pub format: &'a str, + pub version_format: &'a str, + pub disabled: bool, + pub style: &'a str, + pub symbol: &'a str, + pub compiler: VecOr<&'a str>, + pub detect_extensions: Vec<&'a str>, + pub detect_files: Vec<&'a str>, + pub detect_folders: Vec<&'a str>, +} + +impl<'a> Default for SolidityConfig<'a> { + fn default() -> Self { + SolidityConfig { + format: "via [$symbol($version)]($style)", + symbol: "S ", + style: "bold blue", + compiler: VecOr(vec!["solc"]), + version_format: "v${major}.${minor}.${patch}", + disabled: false, + detect_extensions: vec!["sol"], + detect_files: vec![], + detect_folders: vec![], + } + } +} diff --git a/src/configs/starship_root.rs b/src/configs/starship_root.rs index 717b1ae1..7d8cae12 100644 --- a/src/configs/starship_root.rs +++ b/src/configs/starship_root.rs @@ -86,6 +86,7 @@ pub const PROMPT_ORDER: &[&str] = &[ "ruby", "rust", "scala", + "solidity", "swift", "terraform", "vlang", diff --git a/src/module.rs b/src/module.rs index 4e1caf2a..b2434a25 100644 --- a/src/module.rs +++ b/src/module.rs @@ -82,6 +82,7 @@ pub const ALL_MODULES: &[&str] = &[ "shell", "shlvl", "singularity", + "solidity", "spack", "status", "sudo", diff --git a/src/modules/mod.rs b/src/modules/mod.rs index 28534e6b..d60cdc50 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -72,6 +72,7 @@ mod scala; mod shell; mod shlvl; mod singularity; +mod solidity; mod spack; mod status; mod sudo; @@ -175,6 +176,7 @@ pub fn handle<'a>(module: &str, context: &'a Context) -> Option> { "shell" => shell::module(context), "shlvl" => shlvl::module(context), "singularity" => singularity::module(context), + "solidity" => solidity::module(context), "spack" => spack::module(context), "swift" => swift::module(context), "status" => status::module(context), @@ -291,6 +293,7 @@ pub fn description(module: &str) -> &'static str { "shell" => "The currently used shell indicator", "shlvl" => "The current value of SHLVL", "singularity" => "The currently used Singularity image", + "solidity" => "The current installed version of Solidity", "spack" => "The current spack environment, if $SPACK_ENV is set", "status" => "The status of the last command", "sudo" => "The sudo credentials are currently cached", diff --git a/src/modules/solidity.rs b/src/modules/solidity.rs new file mode 100644 index 00000000..f76d1739 --- /dev/null +++ b/src/modules/solidity.rs @@ -0,0 +1,184 @@ +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() + } +} diff --git a/src/utils.rs b/src/utils.rs index aad9f297..1409db80 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -413,6 +413,14 @@ Built on MoarVM version 2021.12.\n", stdout: String::from("ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-gnu]\n"), stderr: String::default(), }), + "solc --version" => Some(CommandOutput { + stdout: String::from("solc, the solidity compiler commandline interface +Version: 0.8.16+commit.07a7930e.Linux.g++"), + stderr: String::default(), + }), + "solcjs --version" => Some(CommandOutput { + stdout: String::from("0.8.15+commit.e14f2714.Emscripten.clang"), + stderr: String::default() }), "swift --version" => Some(CommandOutput { stdout: String::from( "\