diff --git a/docs/config/README.md b/docs/config/README.md index 75ffad41..61704656 100644 --- a/docs/config/README.md +++ b/docs/config/README.md @@ -116,6 +116,7 @@ prompt_order = [ "java", "julia", "nodejs", + "ocaml", "php", "python", "ruby", @@ -1098,6 +1099,35 @@ package, and shows its current version. The module currently supports `npm`, `ca symbol = "🎁 " ``` +## OCaml + +The `ocaml` module shows the currently installed version of OCaml. +The module will be shown if any of the following conditions are met: + +- The current directory contains a file with `.opam` extension or `_opam` directory +- The current directory contains a `esy.lock` directory +- The current directory contains a `dune` or `dune-project` file +- The current directory contains a `jbuild` or `jbuild-ignore` file +- The current directory contains a `.merlin` file +- The current directory contains a file with `.ml`, `.mli`, `.re` or `.rei` extension + +### Options + +| Variable | Default | Description | +| ---------- | --------------- | ------------------------------------------------------- | +| `symbol` | `"🐫 "` | The symbol used before displaying the version of OCaml. | +| `style` | `"bold yellow"` | The style for the module. | +| `disabled` | `false` | Disables the `ocaml` module. | + +### Example + +```toml +# ~/.config/starship.toml + +[ocaml] +symbol = "🐪 " +``` + ## PHP The `php` module shows the currently installed version of PHP. diff --git a/src/configs/mod.rs b/src/configs/mod.rs index 26abbe0c..5ee945ee 100644 --- a/src/configs/mod.rs +++ b/src/configs/mod.rs @@ -27,6 +27,7 @@ pub mod kubernetes; pub mod memory_usage; pub mod nix_shell; pub mod nodejs; +pub mod ocaml; pub mod package; pub mod php; pub mod python; diff --git a/src/configs/ocaml.rs b/src/configs/ocaml.rs new file mode 100644 index 00000000..b944f534 --- /dev/null +++ b/src/configs/ocaml.rs @@ -0,0 +1,23 @@ +use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig}; + +use ansi_term::{Color, Style}; +use starship_module_config_derive::ModuleConfig; + +#[derive(Clone, ModuleConfig)] +pub struct OCamlConfig<'a> { + pub symbol: SegmentConfig<'a>, + pub version: SegmentConfig<'a>, + pub style: Style, + pub disabled: bool, +} + +impl<'a> RootModuleConfig<'a> for OCamlConfig<'a> { + fn new() -> Self { + OCamlConfig { + symbol: SegmentConfig::new("🐫 "), + version: SegmentConfig::default(), + style: Color::Yellow.bold(), + disabled: false, + } + } +} diff --git a/src/configs/starship_root.rs b/src/configs/starship_root.rs index edcc3147..d6ebdaa4 100644 --- a/src/configs/starship_root.rs +++ b/src/configs/starship_root.rs @@ -40,6 +40,7 @@ impl<'a> RootModuleConfig<'a> for StarshipRootConfig<'a> { "java", "julia", "nodejs", + "ocaml", "php", "python", "ruby", diff --git a/src/module.rs b/src/module.rs index bf08427a..8ea18024 100644 --- a/src/module.rs +++ b/src/module.rs @@ -39,6 +39,7 @@ pub const ALL_MODULES: &[&str] = &[ "memory_usage", "nix_shell", "nodejs", + "ocaml", "package", "python", "ruby", diff --git a/src/modules/mod.rs b/src/modules/mod.rs index 1e4ac6f4..23f12bb2 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -28,6 +28,7 @@ mod line_break; mod memory_usage; mod nix_shell; mod nodejs; +mod ocaml; mod package; mod php; mod python; @@ -79,6 +80,7 @@ pub fn handle<'a>(module: &str, context: &'a Context) -> Option> { "memory_usage" => memory_usage::module(context), "nix_shell" => nix_shell::module(context), "nodejs" => nodejs::module(context), + "ocaml" => ocaml::module(context), "package" => package::module(context), "php" => php::module(context), "python" => python::module(context), @@ -127,6 +129,7 @@ pub fn description(module: &str) -> &'static str { "memory_usage" => "Current system memory and swap usage", "nix_shell" => "The nix-shell environment", "nodejs" => "The currently installed version of NodeJS", + "ocaml" => "The currently installed version of OCaml", "package" => "The package version of the current directory's project", "php" => "The currently installed version of PHP", "python" => "The currently installed version of Python", diff --git a/src/modules/ocaml.rs b/src/modules/ocaml.rs new file mode 100644 index 00000000..ad61305c --- /dev/null +++ b/src/modules/ocaml.rs @@ -0,0 +1,187 @@ +use super::{Context, Module, RootModuleConfig, SegmentConfig}; + +use crate::configs::ocaml::OCamlConfig; +use crate::utils; + +/// Creates a module with the current OCaml version +/// +/// Will display the OCaml version if any of the following criteria are met: +/// - Current directory contains a file with `.opam` extension or `_opam` directory +/// - Current directory contains a `esy.lock` directory +/// - Current directory contains a `dune` or `dune-project` file +/// - Current directory contains a `jbuild` or `jbuild-ignore` file +/// - Current directory contains a `.merlin` file +/// - Current directory contains a file with `.ml`, `.mli`, `.re` or `.rei` extension +pub fn module<'a>(context: &'a Context) -> Option> { + let is_ocaml_project = context + .try_begin_scan()? + .set_files(&["dune", "dune-project", "jbuild", "jbuild-ignore", ".merlin"]) + .set_folders(&["_opam", "esy.lock"]) + .set_extensions(&["opam", "ml", "mli", "re", "rei"]) + .is_match(); + + if !is_ocaml_project { + return None; + } + + let ocaml_version = utils::exec_cmd("ocaml", &["-vnum"])?.stdout; + let formatted_version = format!("v{}", &ocaml_version); + + let mut module = context.new_module("ocaml"); + let config = OCamlConfig::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) +} + +#[cfg(test)] +mod tests { + use crate::modules::utils::test::render_module; + use ansi_term::Color; + use std::fs::{self, File}; + use std::io; + + #[test] + fn folder_without_ocaml_file() -> io::Result<()> { + let dir = tempfile::tempdir()?; + let actual = render_module("ocaml", dir.path(), None); + let expected = None; + assert_eq!(expected, actual); + dir.close() + } + + #[test] + fn folder_with_opam_file() -> io::Result<()> { + let dir = tempfile::tempdir()?; + File::create(dir.path().join("any.opam"))?.sync_all()?; + + let actual = render_module("ocaml", dir.path(), None); + let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0"))); + assert_eq!(expected, actual); + dir.close() + } + + #[test] + fn folder_with_opam_directory() -> io::Result<()> { + let dir = tempfile::tempdir()?; + fs::create_dir_all(dir.path().join("_opam"))?; + + let actual = render_module("ocaml", dir.path(), None); + let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0"))); + assert_eq!(expected, actual); + dir.close() + } + + #[test] + fn folder_with_esy_lock_directory() -> io::Result<()> { + let dir = tempfile::tempdir()?; + fs::create_dir_all(dir.path().join("esy.lock"))?; + + let actual = render_module("ocaml", dir.path(), None); + let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0"))); + assert_eq!(expected, actual); + dir.close() + } + + #[test] + fn folder_with_dune() -> io::Result<()> { + let dir = tempfile::tempdir()?; + File::create(dir.path().join("dune"))?.sync_all()?; + + let actual = render_module("ocaml", dir.path(), None); + let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0"))); + assert_eq!(expected, actual); + dir.close() + } + + #[test] + fn folder_with_dune_project() -> io::Result<()> { + let dir = tempfile::tempdir()?; + File::create(dir.path().join("dune-project"))?.sync_all()?; + + let actual = render_module("ocaml", dir.path(), None); + let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0"))); + assert_eq!(expected, actual); + dir.close() + } + + #[test] + fn folder_with_jbuild() -> io::Result<()> { + let dir = tempfile::tempdir()?; + File::create(dir.path().join("jbuild"))?.sync_all()?; + + let actual = render_module("ocaml", dir.path(), None); + let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0"))); + assert_eq!(expected, actual); + dir.close() + } + + #[test] + fn folder_with_jbuild_ignore() -> io::Result<()> { + let dir = tempfile::tempdir()?; + File::create(dir.path().join("jbuild-ignore"))?.sync_all()?; + + let actual = render_module("ocaml", dir.path(), None); + let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0"))); + assert_eq!(expected, actual); + dir.close() + } + + #[test] + fn folder_with_merlin_file() -> io::Result<()> { + let dir = tempfile::tempdir()?; + File::create(dir.path().join(".merlin"))?.sync_all()?; + + let actual = render_module("ocaml", dir.path(), None); + let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0"))); + assert_eq!(expected, actual); + dir.close() + } + + #[test] + fn folder_with_ml_file() -> io::Result<()> { + let dir = tempfile::tempdir()?; + File::create(dir.path().join("any.ml"))?.sync_all()?; + + let actual = render_module("ocaml", dir.path(), None); + let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0"))); + assert_eq!(expected, actual); + dir.close() + } + + #[test] + fn folder_with_mli_file() -> io::Result<()> { + let dir = tempfile::tempdir()?; + File::create(dir.path().join("any.mli"))?.sync_all()?; + + let actual = render_module("ocaml", dir.path(), None); + let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0"))); + assert_eq!(expected, actual); + dir.close() + } + + #[test] + fn folder_with_re_file() -> io::Result<()> { + let dir = tempfile::tempdir()?; + File::create(dir.path().join("any.re"))?.sync_all()?; + + let actual = render_module("ocaml", dir.path(), None); + let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0"))); + assert_eq!(expected, actual); + dir.close() + } + + #[test] + fn folder_with_rei_file() -> io::Result<()> { + let dir = tempfile::tempdir()?; + File::create(dir.path().join("any.rei"))?.sync_all()?; + + let actual = render_module("ocaml", dir.path(), None); + let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0"))); + assert_eq!(expected, actual); + dir.close() + } +} diff --git a/src/utils.rs b/src/utils.rs index b4aa1808..fc5d81a2 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -63,6 +63,10 @@ pub fn exec_cmd(cmd: &str, args: &[&str]) -> Option { stdout: String::from("v12.0.0"), stderr: String::default(), }), + "ocaml -vnum" => Some(CommandOutput { + stdout: String::from("4.10.0"), + stderr: String::default(), + }), "php -r echo PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION.'.'.PHP_RELEASE_VERSION;" => { Some(CommandOutput { stdout: String::from("7.3.8"),