feat: add pijul_channel module (#4765)
* feat: Pijul VCS support * Extra bits needed for new module. * Format Markdown table. * Fix lint. * Don't test Pijul module so thoroughly. Installing from source is too expensive, and compiled binaries are only available for Windows (and unofficially as well). Perhaps once Pijul 1.0.0 comes out of beta there will be more binaries available in package repos. * Format! * Bad rebase, remove Pijul install from workflow. * Mock Pijul commands for code coverage. * Make fake .pijul directory in fixture. * Truly mock `pijul` command. * Rename module from `pijul` to `pijul_channel`. * Format! * Fix config-schema.json. * Missed changing module name in docs/ folder.
This commit is contained in:
parent
8d2256ab1d
commit
67b6376e2e
|
@ -1215,6 +1215,21 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"pijul_channel": {
|
||||
"default": {
|
||||
"disabled": true,
|
||||
"format": "on [$symbol$channel]($style) ",
|
||||
"style": "bold purple",
|
||||
"symbol": " ",
|
||||
"truncation_length": 9223372036854775807,
|
||||
"truncation_symbol": "…"
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/PijulConfig"
|
||||
}
|
||||
]
|
||||
},
|
||||
"pulumi": {
|
||||
"default": {
|
||||
"disabled": false,
|
||||
|
@ -4495,6 +4510,37 @@
|
|||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"PijulConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"symbol": {
|
||||
"default": " ",
|
||||
"type": "string"
|
||||
},
|
||||
"style": {
|
||||
"default": "bold purple",
|
||||
"type": "string"
|
||||
},
|
||||
"format": {
|
||||
"default": "on [$symbol$channel]($style) ",
|
||||
"type": "string"
|
||||
},
|
||||
"truncation_length": {
|
||||
"default": 9223372036854775807,
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"truncation_symbol": {
|
||||
"default": "…",
|
||||
"type": "string"
|
||||
},
|
||||
"disabled": {
|
||||
"default": true,
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"PulumiConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
|
@ -130,6 +130,9 @@ format = '\[[$symbol($version)]($style)\]'
|
|||
[php]
|
||||
format = '\[[$symbol($version)]($style)\]'
|
||||
|
||||
[pijul_channel]
|
||||
format = '\[[$symbol$channel]($style)\]'
|
||||
|
||||
[pulumi]
|
||||
format = '\[[$symbol$stack]($style)\]'
|
||||
|
||||
|
|
|
@ -108,6 +108,9 @@ Windows = " "
|
|||
[package]
|
||||
symbol = " "
|
||||
|
||||
[pijul_channel]
|
||||
symbol = "🪺 "
|
||||
|
||||
[python]
|
||||
symbol = " "
|
||||
|
||||
|
|
|
@ -156,6 +156,9 @@ symbol = "pl "
|
|||
[php]
|
||||
symbol = "php "
|
||||
|
||||
[pijul_channel]
|
||||
symbol = "pijul "
|
||||
|
||||
[pulumi]
|
||||
symbol = "pulumi "
|
||||
|
||||
|
|
|
@ -271,6 +271,7 @@ $git_state\
|
|||
$git_metrics\
|
||||
$git_status\
|
||||
$hg_branch\
|
||||
$pijul_channel\
|
||||
$docker_context\
|
||||
$package\
|
||||
$c\
|
||||
|
@ -3175,6 +3176,21 @@ By default the module will be shown if any of the following conditions are met:
|
|||
format = 'via [🔹 $version](147 bold) '
|
||||
```
|
||||
|
||||
## Pijul Channel
|
||||
|
||||
The `pijul_channel` module shows the active channel of the repo in your current directory.
|
||||
|
||||
### Options
|
||||
|
||||
| Option | Default | Description |
|
||||
| ------------------- | --------------------------------- | ------------------------------------------------------------------------------------ |
|
||||
| `symbol` | `' '` | The symbol used before the pijul channel name of the repo in your current directory. |
|
||||
| `style` | `'bold purple'` | The style for the module. |
|
||||
| `format` | `'on [$symbol$channel]($style) '` | The format for the module. |
|
||||
| `truncation_length` | `2^63 - 1` | Truncates the pijul channel name to `N` graphemes |
|
||||
| `truncation_symbol` | `'…'` | The symbol used to indicate a branch name was truncated. |
|
||||
| `disabled` | `true` | Disables the `pijul` module. |
|
||||
|
||||
## Pulumi
|
||||
|
||||
The `pulumi` module shows the current username, selected [Pulumi Stack](https://www.pulumi.com/docs/intro/concepts/stack/), and version.
|
||||
|
|
|
@ -61,6 +61,7 @@ pub mod os;
|
|||
pub mod package;
|
||||
pub mod perl;
|
||||
pub mod php;
|
||||
pub mod pijul_channel;
|
||||
pub mod pulumi;
|
||||
pub mod purescript;
|
||||
pub mod python;
|
||||
|
@ -221,6 +222,8 @@ pub struct FullConfig<'a> {
|
|||
#[serde(borrow)]
|
||||
php: php::PhpConfig<'a>,
|
||||
#[serde(borrow)]
|
||||
pijul_channel: pijul_channel::PijulConfig<'a>,
|
||||
#[serde(borrow)]
|
||||
pulumi: pulumi::PulumiConfig<'a>,
|
||||
#[serde(borrow)]
|
||||
purescript: purescript::PureScriptConfig<'a>,
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
#[cfg_attr(
|
||||
feature = "config-schema",
|
||||
derive(schemars::JsonSchema),
|
||||
schemars(deny_unknown_fields)
|
||||
)]
|
||||
#[serde(default)]
|
||||
pub struct PijulConfig<'a> {
|
||||
pub symbol: &'a str,
|
||||
pub style: &'a str,
|
||||
pub format: &'a str,
|
||||
pub truncation_length: i64,
|
||||
pub truncation_symbol: &'a str,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl<'a> Default for PijulConfig<'a> {
|
||||
fn default() -> Self {
|
||||
PijulConfig {
|
||||
symbol: " ",
|
||||
style: "bold purple",
|
||||
format: "on [$symbol$channel]($style) ",
|
||||
truncation_length: std::i64::MAX,
|
||||
truncation_symbol: "…",
|
||||
disabled: true,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -42,6 +42,7 @@ pub const PROMPT_ORDER: &[&str] = &[
|
|||
"git_metrics",
|
||||
"git_status",
|
||||
"hg_branch",
|
||||
"pijul_channel",
|
||||
"docker_context",
|
||||
"package",
|
||||
// ↓ Toolchain version modules ↓
|
||||
|
|
|
@ -68,6 +68,7 @@ pub const ALL_MODULES: &[&str] = &[
|
|||
"package",
|
||||
"perl",
|
||||
"php",
|
||||
"pijul_channel",
|
||||
"pulumi",
|
||||
"purescript",
|
||||
"python",
|
||||
|
|
|
@ -58,6 +58,7 @@ mod os;
|
|||
mod package;
|
||||
mod perl;
|
||||
mod php;
|
||||
mod pijul_channel;
|
||||
mod pulumi;
|
||||
mod purescript;
|
||||
mod python;
|
||||
|
@ -159,6 +160,7 @@ pub fn handle<'a>(module: &str, context: &'a Context) -> Option<Module<'a>> {
|
|||
"package" => package::module(context),
|
||||
"perl" => perl::module(context),
|
||||
"php" => php::module(context),
|
||||
"pijul_channel" => pijul_channel::module(context),
|
||||
"pulumi" => pulumi::module(context),
|
||||
"purescript" => purescript::module(context),
|
||||
"python" => python::module(context),
|
||||
|
@ -273,6 +275,7 @@ pub fn description(module: &str) -> &'static str {
|
|||
"package" => "The package version of the current directory's project",
|
||||
"perl" => "The currently installed version of Perl",
|
||||
"php" => "The currently installed version of PHP",
|
||||
"pijul_channel" => "The current channel of the repo in the current directory",
|
||||
"pulumi" => "The current username, stack, and installed version of Pulumi",
|
||||
"purescript" => "The currently installed version of PureScript",
|
||||
"python" => "The currently installed version of Python",
|
||||
|
|
|
@ -0,0 +1,220 @@
|
|||
use super::utils::truncate::truncate_text;
|
||||
use super::{Context, Module, ModuleConfig};
|
||||
|
||||
use crate::configs::pijul_channel::PijulConfig;
|
||||
use crate::formatter::StringFormatter;
|
||||
|
||||
/// Creates a module with the Pijul channel in the current directory
|
||||
///
|
||||
/// Will display the channel lame if the current directory is a pijul repo
|
||||
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
||||
let is_repo = context
|
||||
.try_begin_scan()?
|
||||
.set_folders(&[".pijul"])
|
||||
.is_match();
|
||||
|
||||
if !is_repo {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut module = context.new_module("pijul_channel");
|
||||
let config: PijulConfig = PijulConfig::try_load(module.config);
|
||||
|
||||
// We default to disabled=true, so we have to check after loading our config module.
|
||||
if config.disabled {
|
||||
return None;
|
||||
};
|
||||
|
||||
let channel_name = get_pijul_current_channel(context)?;
|
||||
|
||||
let truncated_text = truncate_text(
|
||||
&channel_name,
|
||||
config.truncation_length as usize,
|
||||
config.truncation_symbol,
|
||||
);
|
||||
|
||||
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 {
|
||||
"channel" => Some(Ok(&truncated_text)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
Err(error) => {
|
||||
log::warn!("Error in module `pijul_channel`:\n{}", error);
|
||||
return None;
|
||||
}
|
||||
});
|
||||
|
||||
Some(module)
|
||||
}
|
||||
|
||||
fn get_pijul_current_channel(ctx: &Context) -> Option<String> {
|
||||
let output = ctx.exec_cmd("pijul", &["channel"])?.stdout;
|
||||
|
||||
output
|
||||
.lines()
|
||||
.find_map(|l| l.strip_prefix("* "))
|
||||
.map(str::to_owned)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use nu_ansi_term::{Color, Style};
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::test::{fixture_repo, FixtureProvider, ModuleRenderer};
|
||||
|
||||
enum Expect<'a> {
|
||||
ChannelName(&'a str),
|
||||
Empty,
|
||||
NoTruncation,
|
||||
Symbol(&'a str),
|
||||
Style(Style),
|
||||
TruncationSymbol(&'a str),
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn show_nothing_on_empty_dir() -> io::Result<()> {
|
||||
let repo_dir = tempfile::tempdir()?;
|
||||
|
||||
let actual = ModuleRenderer::new("pijul_channel")
|
||||
.path(repo_dir.path())
|
||||
.collect();
|
||||
|
||||
let expected = None;
|
||||
assert_eq!(expected, actual);
|
||||
repo_dir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pijul_disabled_per_default() -> io::Result<()> {
|
||||
let tempdir = fixture_repo(FixtureProvider::Pijul)?;
|
||||
let repo_dir = tempdir.path();
|
||||
expect_pijul_with_config(
|
||||
repo_dir,
|
||||
Some(toml::toml! {
|
||||
[pijul_channel]
|
||||
truncation_length = 14
|
||||
}),
|
||||
&[Expect::Empty],
|
||||
);
|
||||
tempdir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pijul_autodisabled() -> io::Result<()> {
|
||||
let tempdir = tempfile::tempdir()?;
|
||||
expect_pijul_with_config(tempdir.path(), None, &[Expect::Empty]);
|
||||
tempdir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pijul_channel() -> io::Result<()> {
|
||||
let tempdir = fixture_repo(FixtureProvider::Pijul)?;
|
||||
let repo_dir = tempdir.path();
|
||||
run_pijul(&["channel", "new", "tributary-48198"], repo_dir)?;
|
||||
run_pijul(&["channel", "switch", "tributary-48198"], repo_dir)?;
|
||||
expect_pijul_with_config(
|
||||
repo_dir,
|
||||
None,
|
||||
&[Expect::ChannelName("tributary-48198"), Expect::NoTruncation],
|
||||
);
|
||||
tempdir.close()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pijul_configured() -> io::Result<()> {
|
||||
let tempdir = fixture_repo(FixtureProvider::Pijul)?;
|
||||
let repo_dir = tempdir.path();
|
||||
run_pijul(&["channel", "new", "tributary-48198"], repo_dir)?;
|
||||
run_pijul(&["channel", "switch", "tributary-48198"], repo_dir)?;
|
||||
expect_pijul_with_config(
|
||||
repo_dir,
|
||||
Some(toml::toml! {
|
||||
[pijul_channel]
|
||||
style = "underline blue"
|
||||
symbol = "P "
|
||||
truncation_length = 14
|
||||
truncation_symbol = "%"
|
||||
disabled = false
|
||||
}),
|
||||
&[
|
||||
Expect::ChannelName("tributary-4819"),
|
||||
Expect::Style(Color::Blue.underline()),
|
||||
Expect::Symbol("P"),
|
||||
Expect::TruncationSymbol("%"),
|
||||
],
|
||||
);
|
||||
tempdir.close()
|
||||
}
|
||||
|
||||
fn expect_pijul_with_config(
|
||||
repo_dir: &Path,
|
||||
config: Option<toml::Value>,
|
||||
expectations: &[Expect],
|
||||
) {
|
||||
let actual = ModuleRenderer::new("pijul_channel")
|
||||
.path(repo_dir.to_str().unwrap())
|
||||
.config(config.unwrap_or_else(|| {
|
||||
toml::toml! {
|
||||
[pijul_channel]
|
||||
disabled = false
|
||||
}
|
||||
}))
|
||||
.collect();
|
||||
|
||||
let mut expect_channel_name = "main";
|
||||
let mut expect_style = Color::Purple.bold();
|
||||
let mut expect_symbol = "\u{e0a0}";
|
||||
let mut expect_truncation_symbol = "…";
|
||||
|
||||
for expect in expectations {
|
||||
match expect {
|
||||
Expect::Empty => {
|
||||
assert_eq!(None, actual);
|
||||
return;
|
||||
}
|
||||
Expect::Symbol(symbol) => {
|
||||
expect_symbol = symbol;
|
||||
}
|
||||
Expect::TruncationSymbol(truncation_symbol) => {
|
||||
expect_truncation_symbol = truncation_symbol;
|
||||
}
|
||||
Expect::NoTruncation => {
|
||||
expect_truncation_symbol = "";
|
||||
}
|
||||
Expect::ChannelName(channel_name) => {
|
||||
expect_channel_name = channel_name;
|
||||
}
|
||||
Expect::Style(style) => expect_style = *style,
|
||||
}
|
||||
}
|
||||
|
||||
let expected = Some(format!(
|
||||
"on {} ",
|
||||
expect_style.paint(format!(
|
||||
"{expect_symbol} {expect_channel_name}{expect_truncation_symbol}"
|
||||
)),
|
||||
));
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
fn run_pijul(args: &[&str], _repo_dir: &Path) -> io::Result<()> {
|
||||
crate::utils::mock_cmd("pijul", args).ok_or(io::ErrorKind::Unsupported)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ use crate::{
|
|||
};
|
||||
use log::{Level, LevelFilter};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use tempfile::TempDir;
|
||||
|
@ -165,6 +166,7 @@ impl<'a> ModuleRenderer<'a> {
|
|||
pub enum FixtureProvider {
|
||||
Git,
|
||||
Hg,
|
||||
Pijul,
|
||||
}
|
||||
|
||||
pub fn fixture_repo(provider: FixtureProvider) -> io::Result<TempDir> {
|
||||
|
@ -223,5 +225,10 @@ pub fn fixture_repo(provider: FixtureProvider) -> io::Result<TempDir> {
|
|||
|
||||
Ok(path)
|
||||
}
|
||||
FixtureProvider::Pijul => {
|
||||
let path = tempfile::tempdir()?;
|
||||
fs::create_dir(path.path().join(".pijul"))?;
|
||||
Ok(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
12
src/utils.rs
12
src/utils.rs
|
@ -337,6 +337,18 @@ WebAssembly: unavailable
|
|||
stderr: String::default(),
|
||||
})
|
||||
},
|
||||
"pijul channel" => Some(CommandOutput{
|
||||
stdout: String::from(" main\n* tributary-48198"),
|
||||
stderr: String::default(),
|
||||
}),
|
||||
"pijul channel new tributary-48198" => Some(CommandOutput{
|
||||
stdout: String::default(),
|
||||
stderr: String::default(),
|
||||
}),
|
||||
"pijul channel switch tributary-48198" => Some(CommandOutput{
|
||||
stdout: String::from("Outputting repository ↖"),
|
||||
stderr: String::default(),
|
||||
}),
|
||||
"pulumi version" => Some(CommandOutput{
|
||||
stdout: String::from("1.2.3-ver.1631311768+e696fb6c"),
|
||||
stderr: String::default(),
|
||||
|
|
Loading…
Reference in New Issue