diff --git a/README.md b/README.md index 8de4201b..0621d808 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,7 @@ The prompt shows information you need while you're working, while staying sleek - Current Ruby version (`💎`) - Current Rust version (`🦀`) - Current .NET version (`•NET`) +- Current Crystal version (`🔮 `). - Current version of package in current directory (`📦`) - npm (Node.js) - cargo (Rust) diff --git a/docs/config/README.md b/docs/config/README.md index 1c2ca272..d40e09da 100644 --- a/docs/config/README.md +++ b/docs/config/README.md @@ -116,6 +116,7 @@ prompt_order = [ "memory_usage", "aws", "env_var", + "crystal", "cmd_duration", "line_break", "jobs", @@ -858,6 +859,33 @@ separator = "/" style = "bold dimmed green" ``` +## Crystal + +The `crystal` module shows the currently installed version of Crystal. +The module will be shown if any of the following conditions are met: + +- The current directory contains a `shard.yml` file +- The current directory contains a `.cr` file + +### Options + +| Variable | Default | Description | +| ---------- | ------------ | ------------------------------------------------------------ | +| `symbol` | `"🔮 "` | The symbol used before displaying the version of crystal. | +| `style` | `"bold red"` | The style for the module. | +| `disabled` | `false` | Disables the `crystal` module. | + +### Example + +```toml +# ~/.config/starship.toml + +[crystal] +symbol = "🔮 " +style = "bold red" +disabled = false +``` + ## NodeJS The `nodejs` module shows the currently installed version of NodeJS. diff --git a/src/configs/crystal.rs b/src/configs/crystal.rs new file mode 100644 index 00000000..58455e8e --- /dev/null +++ b/src/configs/crystal.rs @@ -0,0 +1,21 @@ +use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig}; + +use ansi_term::{Color, Style}; +use starship_module_config_derive::ModuleConfig; + +#[derive(Clone, ModuleConfig)] +pub struct CrystalConfig<'a> { + pub symbol: SegmentConfig<'a>, + pub style: Style, + pub disabled: bool, +} + +impl<'a> RootModuleConfig<'a> for CrystalConfig<'a> { + fn new() -> Self { + CrystalConfig { + symbol: SegmentConfig::new("🔮 "), + style: Color::Red.bold(), + disabled: false, + } + } +} diff --git a/src/configs/mod.rs b/src/configs/mod.rs index 02ee00ef..b3950b50 100644 --- a/src/configs/mod.rs +++ b/src/configs/mod.rs @@ -3,6 +3,7 @@ pub mod battery; pub mod character; pub mod cmd_duration; pub mod conda; +pub mod crystal; pub mod directory; pub mod dotnet; pub mod env_var; diff --git a/src/module.rs b/src/module.rs index 1768d6b2..c3d283d8 100644 --- a/src/module.rs +++ b/src/module.rs @@ -36,6 +36,7 @@ pub const ALL_MODULES: &[&str] = &[ "package", "python", "ruby", + "crystal", "rust", "php", "terraform", diff --git a/src/modules/crystal.rs b/src/modules/crystal.rs new file mode 100644 index 00000000..a215369c --- /dev/null +++ b/src/modules/crystal.rs @@ -0,0 +1,86 @@ +use super::{Context, Module, RootModuleConfig, SegmentConfig}; + +use crate::configs::crystal::CrystalConfig; +use crate::utils; + +/// Creates a module with the current Crystal version +/// +/// Will display the Crystal version if any of the following criteria are met: +/// - Current directory contains a `.cr` file +/// - Current directory contains a `shard.yml` file +pub fn module<'a>(context: &'a Context) -> Option> { + let is_crystal_project = context + .try_begin_scan()? + .set_files(&["shard.yml"]) + .set_extensions(&["cr"]) + .is_match(); + + if !is_crystal_project { + return None; + } + + let crystal_version = utils::exec_cmd("crystal", &["--version"])?.stdout; + let formatted_version = format_crystal_version(&crystal_version)?; + + let mut module = context.new_module("crystal"); + let config: CrystalConfig = CrystalConfig::try_load(module.config); + module.set_style(config.style); + + module.create_segment("symbol", &config.symbol); + module.create_segment("version", &SegmentConfig::new(&formatted_version)); + + Some(module) +} + +fn format_crystal_version(crystal_version: &str) -> Option { + let version = crystal_version + // split into ["Crystal", "0.32.1", ...] + .split_whitespace() + // return "0.32.1" + .nth(1)?; + + let mut formatted_version = String::with_capacity(version.len() + 1); + formatted_version.push('v'); + formatted_version.push_str(version); + Some(formatted_version) +} + +#[cfg(test)] +mod tests { + use crate::modules::utils::test::render_module; + use ansi_term::Color; + use std::fs::File; + use std::io; + use tempfile; + + #[test] + fn folder_without_crystal_files() -> io::Result<()> { + let dir = tempfile::tempdir()?; + let actual = render_module("crystal", dir.path()); + let expected = None; + assert_eq!(expected, actual); + Ok(()) + } + + #[test] + fn folder_with_shard_file() -> io::Result<()> { + let dir = tempfile::tempdir()?; + File::create(dir.path().join("shard.yml"))?.sync_all()?; + + let actual = render_module("crystal", dir.path()); + let expected = Some(format!("via {} ", Color::Red.bold().paint("🔮 v0.32.1"))); + assert_eq!(expected, actual); + Ok(()) + } + + #[test] + fn folder_with_cr_file() -> io::Result<()> { + let dir = tempfile::tempdir()?; + File::create(dir.path().join("main.cr"))?.sync_all()?; + + let actual = render_module("crystal", dir.path()); + let expected = Some(format!("via {} ", Color::Red.bold().paint("🔮 v0.32.1"))); + assert_eq!(expected, actual); + Ok(()) + } +} diff --git a/src/modules/mod.rs b/src/modules/mod.rs index 74b11b94..2852d169 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -3,6 +3,7 @@ mod aws; mod character; mod cmd_duration; mod conda; +mod crystal; mod directory; mod dotnet; mod env_var; @@ -73,6 +74,7 @@ pub fn handle<'a>(module: &str, context: &'a Context) -> Option> { "rust" => rust::module(context), "terraform" => terraform::module(context), "time" => time::module(context), + "crystal" => crystal::module(context), "username" => username::module(context), _ => { eprintln!("Error: Unknown module {}. Use starship module --list to list out all supported modules.", module); diff --git a/src/utils.rs b/src/utils.rs index 0aae93da..01039093 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -41,6 +41,10 @@ pub fn exec_cmd(cmd: &str, args: &[&str]) -> Option { stdout: String::from("v12.0.0"), stderr: String::default(), }), + "crystal --version" => Some(CommandOutput { + stdout: String::from("Crystal 0.32.1 (2019-12-18)"), + stderr: String::default(), + }), "dummy_command" => Some(CommandOutput { stdout: String::from("stdout ok!"), stderr: String::from("stderr ok!"),