Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(buf): Add Buf module #3661

Merged
merged 13 commits into from
Mar 12, 2022
40 changes: 40 additions & 0 deletions docs/config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ $git_status\
$hg_branch\
$docker_context\
$package\
$buf\
$cmake\
$cobol\
$container\
Expand Down Expand Up @@ -450,6 +451,45 @@ discharging_symbol = "💦"
# when capacity is over 30%, the battery indicator will not be displayed
```

## Buf

The `buf` module shows the currently installed version of [Buf](https://buf.build). By default, the module is shown if all of the following conditions are met:

- The [`buf`](https://github.com/bufbuild/buf) CLI is installed.
- The current directory contains a [`buf.yaml`](https://docs.buf.build/configuration/v1/buf-yaml), [`buf.gen.yaml`](https://docs.buf.build/configuration/v1/buf-gen-yaml), or [`buf.work.yaml`](https://docs.buf.build/configuration/v1/buf-work-yaml) configuration file.

### Options

| Option | Default | Description |
| ------------------- | ---------------------------------------------------------- | ----------------------------------------------------- |
| `format` | `'with [$symbol($version \(Buf $buf_version\) )]($style)'` | The format for the `buf` module. |
| `version_format` | `"v${raw}"` | The version format. |
| `symbol` | `"🦬 "` | The symbol used before displaying the version of Buf. |
| `detect_extensions` | `[]` | Which extensions should trigger this module. |
| `detect_files` | `["buf.yaml", "buf.gen.yaml", "buf.work.yaml"]` | Which filenames should trigger this module. |
| `detect_folders` | `[]` | Which folders should trigger this modules. |
| `style` | `"bold blue"` | The style for the module. |
| `disabled` | `false` | Disables the `elixir` module. |

### Variables

| Variable | Example | Description |
| ------------- | -------- | ------------------------------------ |
| `buf_version` | `v1.0.0` | The version of `buf` |
| `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

[buf]
symbol = "🦬 "
```

## Character

The `character` module shows a character (usually an arrow) beside where the text
Expand Down
6 changes: 6 additions & 0 deletions docs/presets/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ If emojis aren't your thing, this might catch your eye!
[aws]
symbol = " "

[buf]
symbol = "🦬 "
lucperkins marked this conversation as resolved.
Show resolved Hide resolved

[conda]
symbol = " "

Expand Down Expand Up @@ -112,6 +115,9 @@ After:
[aws]
format = '\[[$symbol($profile)(\($region\))(\[$duration\])]($style)\]'

[buf]
format = '\[[$symbol($version)]($style)\]'

[cmake]
format = '\[[$symbol($version)]($style)\]'

Expand Down
31 changes: 31 additions & 0 deletions src/configs/buf.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use crate::config::ModuleConfig;

use serde::Serialize;
use starship_module_config_derive::ModuleConfig;

#[derive(Clone, ModuleConfig, Serialize)]
pub struct BufConfig<'a> {
pub format: &'a str,
pub version_format: &'a str,
pub symbol: &'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 BufConfig<'a> {
fn default() -> Self {
BufConfig {
format: "with [$symbol ($version)]($style)",
version_format: "v${raw}",
symbol: "🦬",
style: "bold blue",
disabled: false,
detect_extensions: vec![],
detect_files: vec!["buf.yaml", "buf.gen.yaml", "buf.work.yaml"],
detect_folders: vec![],
}
}
}
3 changes: 3 additions & 0 deletions src/configs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use starship_module_config_derive::ModuleConfig;
pub mod aws;
pub mod azure;
pub mod battery;
pub mod buf;
pub mod character;
pub mod cmake;
pub mod cmd_duration;
Expand Down Expand Up @@ -90,6 +91,7 @@ pub struct FullConfig<'a> {
aws: aws::AwsConfig<'a>,
azure: azure::AzureConfig<'a>,
battery: battery::BatteryConfig<'a>,
buf: buf::BufConfig<'a>,
character: character::CharacterConfig<'a>,
cmake: cmake::CMakeConfig<'a>,
cmd_duration: cmd_duration::CmdDurationConfig<'a>,
Expand Down Expand Up @@ -171,6 +173,7 @@ impl<'a> Default for FullConfig<'a> {
aws: Default::default(),
azure: Default::default(),
battery: Default::default(),
buf: Default::default(),
character: Default::default(),
cmake: Default::default(),
cmd_duration: Default::default(),
Expand Down
1 change: 1 addition & 0 deletions src/configs/starship_root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ pub const PROMPT_ORDER: &[&str] = &[
"vagrant",
"zig",
// ↑ Toolchain version modules ↑
"buf",
"nix_shell",
"conda",
"memory_usage",
Expand Down
1 change: 1 addition & 0 deletions src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub const ALL_MODULES: &[&str] = &[
"azure",
#[cfg(feature = "battery")]
"battery",
"buf",
"character",
"cmake",
"cmd_duration",
Expand Down
123 changes: 123 additions & 0 deletions src/modules/buf.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
use super::{Context, Module, RootModuleConfig};

use crate::configs::buf::BufConfig;
use crate::formatter::StringFormatter;
use crate::formatter::VersionFormatter;

pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let mut module = context.new_module("buf");
let config: BufConfig = BufConfig::try_load(module.config);

let is_buf_project = context
.try_begin_scan()?
.set_files(&config.detect_files)
.set_extensions(&config.detect_extensions)
.set_folders(&config.detect_folders)
.is_match();

if !is_buf_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 buf_version =
parse_buf_version(&context.exec_cmd("buf", &["--version"])?.stdout)?;
VersionFormatter::format_module_version(
module.get_name(),
&buf_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 `buf`:\n{}", error);
return None;
}
});

Some(module)
}

fn parse_buf_version(buf_version: &str) -> Option<String> {
Some(buf_version.split_whitespace().next()?.to_string())
}

#[cfg(test)]
mod tests {
use super::parse_buf_version;
use crate::test::ModuleRenderer;
use ansi_term::Color;
use std::fs::File;
use std::io;

#[test]
fn buf_version() {
let ok_versions = ["1.0.0", "1.1.0-dev"];
let not_ok_versions = ["foo", "1.0"];

let all_some = ok_versions.iter().all(|&v| parse_buf_version(v).is_some());
let all_none = not_ok_versions
.iter()
.any(|&v| parse_buf_version(v).is_some());

assert!(all_some);
assert!(all_none);
}

#[test]
fn folder_without_buf_config() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let actual = ModuleRenderer::new("buf").path(dir.path()).collect();
let expected = None;
assert_eq!(expected, actual);
dir.close()
}

#[test]
fn folder_with_buf_config() {
let ok_files = ["buf.yaml", "buf.gen.yaml", "buf.work.yaml"];
let not_ok_files = ["buf.json"];

for file in ok_files {
let dir = tempfile::tempdir().unwrap();
File::create(dir.path().join(file))
.unwrap()
.sync_all()
.unwrap();
let actual = ModuleRenderer::new("buf").path(dir.path()).collect();
let expected = Some(format!("with {}", Color::Blue.bold().paint("🦬 v1.0.0")));
assert_eq!(expected, actual);
dir.close().unwrap();
}

for file in not_ok_files {
let dir = tempfile::tempdir().unwrap();
File::create(dir.path().join(file))
.unwrap()
.sync_all()
.unwrap();
let actual = ModuleRenderer::new("buf").path(dir.path()).collect();
let expected = None;
assert_eq!(expected, actual);
dir.close().unwrap();
}
}
}
3 changes: 3 additions & 0 deletions src/modules/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// While adding out new module add out module to src/module.rs ALL_MODULES const array also.
mod aws;
mod azure;
mod buf;
mod character;
mod cmake;
mod cmd_duration;
Expand Down Expand Up @@ -90,6 +91,7 @@ pub fn handle<'a>(module: &str, context: &'a Context) -> Option<Module<'a>> {
"azure" => azure::module(context),
#[cfg(feature = "battery")]
"battery" => battery::module(context),
"buf" => buf::module(context),
"character" => character::module(context),
"cmake" => cmake::module(context),
"cmd_duration" => cmd_duration::module(context),
Expand Down Expand Up @@ -179,6 +181,7 @@ pub fn description(module: &str) -> &'static str {
"aws" => "The current AWS region and profile",
"azure" => "The current Azure subscription",
"battery" => "The current charge of the device's battery and its current charging status",
"buf" => "The currently installed version of the Buf CLI",
"character" => {
"A character (usually an arrow) beside where the text is entered in your terminal"
}
Expand Down
4 changes: 4 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ pub fn mock_cmd<T: AsRef<OsStr> + Debug, U: AsRef<OsStr> + Debug>(
) -> Option<Option<CommandOutput>> {
let command = display_command(&cmd, args);
let out = match command.as_str() {
"buf --version" => Some(CommandOutput {
stdout: String::from("1.0.0"),
stderr: String::default(),
}),
"cobc -version" => Some(CommandOutput {
stdout: String::from("\
cobc (GnuCOBOL) 3.1.2.0
Expand Down