diff --git a/.github/config-schema.json b/.github/config-schema.json index 25d7ad9f..7390c98a 100644 --- a/.github/config-schema.json +++ b/.github/config-schema.json @@ -237,6 +237,25 @@ } ] }, + "daml": { + "default": { + "detect_extensions": [], + "detect_files": [ + "daml.yaml" + ], + "detect_folders": [], + "disabled": false, + "format": "via [$symbol($version )]($style)", + "style": "bold cyan", + "symbol": "Λ ", + "version_format": "v${raw}" + }, + "allOf": [ + { + "$ref": "#/definitions/DamlConfig" + } + ] + }, "dart": { "default": { "detect_extensions": [ @@ -2015,6 +2034,54 @@ } } }, + "DamlConfig": { + "type": "object", + "properties": { + "symbol": { + "default": "Λ ", + "type": "string" + }, + "format": { + "default": "via [$symbol($version )]($style)", + "type": "string" + }, + "version_format": { + "default": "v${raw}", + "type": "string" + }, + "style": { + "default": "bold cyan", + "type": "string" + }, + "disabled": { + "default": false, + "type": "boolean" + }, + "detect_extensions": { + "default": [], + "type": "array", + "items": { + "type": "string" + } + }, + "detect_files": { + "default": [ + "daml.yaml" + ], + "type": "array", + "items": { + "type": "string" + } + }, + "detect_folders": { + "default": [], + "type": "array", + "items": { + "type": "string" + } + } + } + }, "DartConfig": { "type": "object", "properties": { diff --git a/docs/.vuepress/public/presets/toml/bracketed-segments.toml b/docs/.vuepress/public/presets/toml/bracketed-segments.toml index f2a13918..9e84c5d1 100644 --- a/docs/.vuepress/public/presets/toml/bracketed-segments.toml +++ b/docs/.vuepress/public/presets/toml/bracketed-segments.toml @@ -19,6 +19,9 @@ format = '\[[$symbol$environment]($style)\]' [crystal] format = '\[[$symbol($version)]($style)\]' +[daml] +format = '\[[$symbol($version)]($style)\]' + [dart] 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 e549f41c..0f28eb80 100644 --- a/docs/.vuepress/public/presets/toml/no-runtime-versions.toml +++ b/docs/.vuepress/public/presets/toml/no-runtime-versions.toml @@ -10,6 +10,9 @@ format = "via [$symbol]($style)" [crystal] format = "via [$symbol]($style)" +[daml] +format = "via [$symbol]($style)" + [dart] 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 550a1b23..0a433339 100644 --- a/docs/.vuepress/public/presets/toml/plain-text-symbols.toml +++ b/docs/.vuepress/public/presets/toml/plain-text-symbols.toml @@ -31,6 +31,9 @@ symbol = "cr " [cmake] symbol = "cmake " +[daml] +symbol = "daml " + [dart] symbol = "dart " diff --git a/docs/config/README.md b/docs/config/README.md index bdc9de28..9fd270c4 100644 --- a/docs/config/README.md +++ b/docs/config/README.md @@ -214,6 +214,7 @@ $c\ $cmake\ $cobol\ $container\ +$daml\ $dart\ $deno\ $dotnet\ @@ -845,6 +846,48 @@ By default the module will be shown if any of the following conditions are met: format = "via [✨ $version](bold blue) " ``` +## Daml + +The `daml` module shows the currently used [Daml](https://www.digitalasset.com/developers) +SDK version when you are in the root directory of your Daml project. The `sdk-version` in +the `daml.yaml` file will be used, unless it's overridden by the `DAML_SDK_VERSION` +environment variable. +By default the module will be shown if any of the following conditions are met: + +- The current directory contains a `daml.yaml` file + +### Options + +| Option | Default | Description | +| ------------------- | ---------------------------------- | ------------------------------------------------------------------------- | +| `format` | `via [$symbol($version )]($style)` | The format for the module. | +| `version_format` | `v${raw}` | The version format. Available vars are `raw`, `major`, `minor`, & `patch` | +| `symbol` | `"Λ "` | A format string representing the symbol of Daml | +| `style` | `"bold cyan"` | The style for the module. | +| `detect_extensions` | `[]` | Which extensions should trigger this module. | +| `detect_files` | `["daml.yaml"]` | Which filenames should trigger this module. | +| `detect_folders` | `[]` | Which folders should trigger this module. | +| `disabled` | `false` | Disables the `daml` module. | + +### Variables + +| Variable | Example | Description | +| -------- | -------- | ------------------------------------ | +| version | `v2.2.0` | The version of `daml` | +| 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 + +[daml] +format = "via [D $version](bold bright-green) " +``` + ## Dart The `dart` module shows the currently installed version of [Dart](https://dart.dev/). @@ -2506,7 +2549,7 @@ symbol = "☁️ " The `package` module is shown when the current directory is the repository for a package, and shows its current version. The module currently supports `npm`, `nimble`, `cargo`, -`poetry`, `python`, `composer`, `gradle`, `julia`, `mix`, `helm`, `shards` and `dart` packages. +`poetry`, `python`, `composer`, `gradle`, `julia`, `mix`, `helm`, `shards`, `daml` and `dart` packages. - [**npm**](https://docs.npmjs.com/cli/commands/npm) – The `npm` package version is extracted from the `package.json` present in the current directory @@ -2526,6 +2569,7 @@ package, and shows its current version. The module currently supports `npm`, `ni - [**Shards**](https://crystal-lang.org/reference/the_shards_command/index.html) - The `shards` package version is extracted from the `shard.yml` present in the current directory - [**V**](https://vlang.io) - The `vlang` package version is extracted from the `v.mod` present in the current directory - [**SBT**](https://scala-sbt.org) - The `sbt` package version is extracted from the `build.sbt` present in the current directory +- [**Daml**](https://www.digitalasset.com/developers) - The `daml` package version is extracted from the `daml.yaml` present in the current directory - [**Dart**](https://pub.dev/) - The `dart` package version is extracted from the `pubspec.yaml` present in the current directory > ⚠️ The version being shown is that of the package whose source code is in your diff --git a/src/configs/daml.rs b/src/configs/daml.rs new file mode 100644 index 00000000..51ea9330 --- /dev/null +++ b/src/configs/daml.rs @@ -0,0 +1,30 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Deserialize, Serialize)] +#[cfg_attr(feature = "config-schema", derive(schemars::JsonSchema))] +#[serde(default)] +pub struct DamlConfig<'a> { + pub symbol: &'a str, + pub format: &'a str, + pub version_format: &'a str, + pub style: &'a str, + pub disabled: bool, + pub detect_extensions: Vec<&'a str>, + pub detect_files: Vec<&'a str>, + pub detect_folders: Vec<&'a str>, +} + +impl<'a> Default for DamlConfig<'a> { + fn default() -> Self { + DamlConfig { + symbol: "Λ ", + format: "via [$symbol($version )]($style)", + version_format: "v${raw}", + style: "bold cyan", + disabled: false, + detect_extensions: vec![], + detect_files: vec!["daml.yaml"], + detect_folders: vec![], + } + } +} diff --git a/src/configs/mod.rs b/src/configs/mod.rs index c34d36e6..04bba7e9 100644 --- a/src/configs/mod.rs +++ b/src/configs/mod.rs @@ -14,6 +14,7 @@ pub mod conda; pub mod container; pub mod crystal; pub mod custom; +pub mod daml; pub mod dart; pub mod deno; pub mod directory; @@ -114,6 +115,8 @@ pub struct FullConfig<'a> { #[serde(borrow)] crystal: crystal::CrystalConfig<'a>, #[serde(borrow)] + daml: daml::DamlConfig<'a>, + #[serde(borrow)] dart: dart::DartConfig<'a>, #[serde(borrow)] deno: deno::DenoConfig<'a>, diff --git a/src/configs/starship_root.rs b/src/configs/starship_root.rs index 63506ef5..9c61aebc 100644 --- a/src/configs/starship_root.rs +++ b/src/configs/starship_root.rs @@ -39,6 +39,7 @@ pub const PROMPT_ORDER: &[&str] = &[ "c", "cmake", "cobol", + "daml", "dart", "deno", "dotnet", diff --git a/src/module.rs b/src/module.rs index 84eca7ad..a5f80538 100644 --- a/src/module.rs +++ b/src/module.rs @@ -22,6 +22,7 @@ pub const ALL_MODULES: &[&str] = &[ "conda", "container", "crystal", + "daml", "dart", "deno", "directory", diff --git a/src/modules/daml.rs b/src/modules/daml.rs new file mode 100644 index 00000000..e9957227 --- /dev/null +++ b/src/modules/daml.rs @@ -0,0 +1,135 @@ +use super::{Context, Module, ModuleConfig}; +use crate::configs::daml::DamlConfig; +use crate::formatter::{StringFormatter, VersionFormatter}; + +const DAML_SDK_VERSION: &str = "sdk-version"; +const DAML_SDK_VERSION_ENV: &str = "DAML_SDK_VERSION"; +const DAML_YAML: &str = "daml.yaml"; + +/// Creates a module with the current Daml version +pub fn module<'a>(context: &'a Context) -> Option> { + let mut module = context.new_module("daml"); + let config: DamlConfig = DamlConfig::try_load(module.config); + + let is_daml_project = context + .try_begin_scan()? + .set_files(&config.detect_files) + .set_extensions(&config.detect_extensions) + .set_folders(&config.detect_folders) + .is_match(); + + if !is_daml_project { + return None; + } + + let parsed = StringFormatter::new(config.format).and_then(|formatter| { + formatter + .map_meta(|var, _| match var { + "symbol" => Some(config.symbol), + _ => None, + }) + .map_style(|variable| match variable { + "style" => Some(Ok(config.style)), + _ => None, + }) + .map(|variable| match variable { + "version" => { + let daml_sdk_version = get_daml_sdk_version(context)?; + VersionFormatter::format_module_version( + module.get_name(), + &daml_sdk_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 `daml`:\n{}", error); + return None; + } + }); + + Some(module) +} + +fn get_daml_sdk_version(context: &Context) -> Option { + context + .get_env(DAML_SDK_VERSION_ENV) + .or_else(|| read_sdk_version_from_daml_yaml(context)) +} + +fn read_sdk_version_from_daml_yaml(context: &Context) -> Option { + let file_contents = context.read_file_from_pwd(DAML_YAML)?; + let daml_yaml = yaml_rust::YamlLoader::load_from_str(&file_contents).ok()?; + let sdk_version = daml_yaml.first()?[DAML_SDK_VERSION].as_str()?; + Some(sdk_version.to_string()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test::ModuleRenderer; + use ansi_term::Color; + use std::fs::File; + use std::io::{Result, Write}; + + #[test] + fn folder_without_daml_yaml() -> Result<()> { + let dir = tempfile::tempdir()?; + let actual = ModuleRenderer::new("daml").path(dir.path()).collect(); + let expected = None; + assert_eq!(expected, actual); + dir.close() + } + + #[test] + fn folder_with_daml_yaml() -> Result<()> { + let dir = tempfile::tempdir()?; + File::create(dir.path().join(DAML_YAML))?.write_all(b"sdk-version: 2.2.0\n")?; + let actual = ModuleRenderer::new("daml").path(dir.path()).collect(); + let expected = Some(format!("via {}", Color::Cyan.bold().paint("Λ v2.2.0 "))); + assert_eq!(expected, actual); + dir.close() + } + + #[test] + fn folder_with_daml_yaml_without_sdk_version_entry() -> Result<()> { + let dir = tempfile::tempdir()?; + File::create(dir.path().join(DAML_YAML))?.write_all(b"version: 1.0.0\n")?; + let actual = ModuleRenderer::new("daml").path(dir.path()).collect(); + let expected = Some(format!("via {}", Color::Cyan.bold().paint("Λ "))); + assert_eq!(expected, actual); + dir.close() + } + + #[test] + fn folder_with_daml_yaml_and_daml_sdk_version() -> Result<()> { + let dir = tempfile::tempdir()?; + File::create(dir.path().join(DAML_YAML))?.write_all(b"sdk-version: 2.2.0\n")?; + let actual = ModuleRenderer::new("daml") + .env(DAML_SDK_VERSION_ENV, "2.0.0") + .path(dir.path()) + .collect(); + let expected = Some(format!("via {}", Color::Cyan.bold().paint("Λ v2.0.0 "))); + assert_eq!(expected, actual); + dir.close() + } + + #[test] + fn folder_without_daml_yaml_with_daml_sdk_version() -> Result<()> { + let dir = tempfile::tempdir()?; + let actual = ModuleRenderer::new("daml") + .env(DAML_SDK_VERSION_ENV, "2.0.0") + .path(dir.path()) + .collect(); + let expected = None; + assert_eq!(expected, actual); + dir.close() + } +} diff --git a/src/modules/mod.rs b/src/modules/mod.rs index d58b73df..5eeace43 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -11,6 +11,7 @@ mod conda; mod container; mod crystal; pub(crate) mod custom; +mod daml; mod dart; mod deno; mod directory; @@ -102,6 +103,7 @@ pub fn handle<'a>(module: &str, context: &'a Context) -> Option> { "cobol" => cobol::module(context), "conda" => conda::module(context), "container" => container::module(context), + "daml" => daml::module(context), "dart" => dart::module(context), "deno" => deno::module(context), "directory" => directory::module(context), @@ -204,6 +206,7 @@ pub fn description(module: &str) -> &'static str { "conda" => "The current conda environment, if $CONDA_DEFAULT_ENV is set", "container" => "The container indicator, if inside a container.", "crystal" => "The currently installed version of Crystal", + "daml" => "The Daml SDK version of your project", "dart" => "The currently installed version of Dart", "deno" => "The currently installed version of Deno", "directory" => "The current working directory", diff --git a/src/modules/package.rs b/src/modules/package.rs index 29ad7aa4..7d707a66 100644 --- a/src/modules/package.rs +++ b/src/modules/package.rs @@ -247,6 +247,15 @@ fn get_shard_version(context: &Context, config: &PackageConfig) -> Option Option { + let file_contents = context.read_file_from_pwd("daml.yaml")?; + + let daml_yaml = yaml_rust::YamlLoader::load_from_str(&file_contents).ok()?; + let raw_version = daml_yaml.first()?["version"].as_str()?; + + format_version(raw_version, config.version_format) +} + fn get_dart_pub_version(context: &Context, config: &PackageConfig) -> Option { let file_contents = context.read_file_from_pwd("pubspec.yaml")?; @@ -274,6 +283,7 @@ fn get_version(context: &Context, config: &PackageConfig) -> Option { get_vmod_version, get_vpkg_version, get_sbt_version, + get_daml_project_version, get_dart_pub_version, ]; @@ -1213,6 +1223,19 @@ scalaVersion := \"2.13.7\" project_dir.close() } + #[test] + fn test_extract_daml_project_version() -> io::Result<()> { + let config_name = "daml.yaml"; + let config_content = " +sdk-version: 2.2.0 +version: 6.8.65 +"; + let project_dir = create_project_dir()?; + fill_config(&project_dir, config_name, Some(config_content))?; + expect_output(&project_dir, Some("v6.8.65"), None); + project_dir.close() + } + #[test] fn test_extract_dart_pub_version() -> io::Result<()> { let config_name = "pubspec.yaml";