Skip to content

Commit

Permalink
feat: Add interactive prompt to default and pin commands
Browse files Browse the repository at this point in the history
Allow to interactively select a version to pin or set as default

Signed-off-by: Chawye Hsu <su+git@chawyehsu.com>
  • Loading branch information
chawyehsu committed Jul 17, 2024
1 parent 21284b0 commit e8c6e1c
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 60 deletions.
20 changes: 20 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ chrono = "0.4.38"
clap = { version = "4.5", features = ["derive"] }
clap-verbosity-flag = "2.2.0"
console = "0.15.8"
dialoguer = "0.11.0"
dirs = "5.0.1"
flate2 = "1.0.30"
futures-util = "0.3.30"
Expand Down
35 changes: 32 additions & 3 deletions src/cli/default.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,45 @@
use clap::Parser;
use dialoguer::theme::ColorfulTheme;
use miette::IntoDiagnostic;

/// Set the default toolchain
#[derive(Parser, Debug)]
#[clap(arg_required_else_help = true)]
pub struct Args {
/// Toolchain name, can be 'latest' or a specific version number
toolchain: String,
toolchain: Option<String>,
}

pub async fn execute(args: Args) -> miette::Result<()> {
let version = args.toolchain.as_str();
let version = args.toolchain.unwrap_or_else(|| {
if let Ok(toolchains) = crate::toolchain::installed_toolchains() {
let selections = toolchains
.iter()
.map(|t| {
if t.latest {
"latest".to_string()
} else {
t.version.clone()
}
})
.rev()
.collect::<Vec<_>>();

if !selections.is_empty() {
let selection = dialoguer::Select::with_theme(&ColorfulTheme::default())
.with_prompt("Pick a installed version")
.items(&selections)
.default(0)
.interact()
.into_diagnostic()
.expect("can't select a toolchain version");

return selections[selection].clone();
}
}

eprintln!("Please provide a toolchain version to set as default");
std::process::exit(1);
});

// TODO: validate only installed toolchains can be set as default
let deafult_file = crate::moonup_home().join("default");
Expand Down
36 changes: 33 additions & 3 deletions src/cli/pin.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,50 @@
use std::env;

use clap::Parser;
use dialoguer::theme::ColorfulTheme;
use miette::IntoDiagnostic;

use crate::utils::detect_toolchain_file;

/// Pin the MoonBit toolchain to a specific version
#[derive(Parser, Debug)]
#[clap(arg_required_else_help = true)]
// #[clap(arg_required_else_help = true)]
pub struct Args {
/// Toolchain name, can be 'latest' or a specific version number
toolchain: String,
toolchain: Option<String>,
}

pub async fn execute(args: Args) -> miette::Result<()> {
let version = args.toolchain.as_str();
let version = args.toolchain.unwrap_or_else(|| {
if let Ok(toolchains) = crate::toolchain::installed_toolchains() {
let selections = toolchains
.iter()
.map(|t| {
if t.latest {
"latest".to_string()
} else {
t.version.clone()
}
})
.rev()
.collect::<Vec<_>>();

if !selections.is_empty() {
let selection = dialoguer::Select::with_theme(&ColorfulTheme::default())
.with_prompt("Pick a installed version")
.items(&selections)
.default(0)
.interact()
.into_diagnostic()
.expect("can't select a toolchain version");

return selections[selection].clone();
}
}

eprintln!("Please provide a toolchain version to pin");
std::process::exit(1);
});

let toolchain_file = detect_toolchain_file().unwrap_or_else(|| {
let current_dir = env::current_dir().expect("can't access current directory");
Expand Down
63 changes: 9 additions & 54 deletions src/cli/show.rs
Original file line number Diff line number Diff line change
@@ -1,71 +1,26 @@
use clap::Parser;
use miette::IntoDiagnostic;

/// Show installed and currently active toolchains
#[derive(Parser, Debug)]
pub struct Args {}

pub async fn execute(_: Args) -> miette::Result<()> {
let toolchains_dir = crate::moonup_home().join("toolchains");

let default_file = crate::moonup_home().join("default");
let default_version = std::fs::read_to_string(default_file).ok().and_then(|s| {
let v = s.trim().to_string();
if v.is_empty() {
None
} else {
Some(v)
}
});

let toolchains = toolchains_dir
.read_dir()
.into_diagnostic()?
.filter_map(std::io::Result::ok)
.filter_map(|e| {
let path = e.path();
let version = path.file_name().map(|n| {
let n = n.to_ascii_lowercase();
let is_default = match default_version.as_deref() {
Some(v) => v == n,
None => false,
};

if n == "latest" {
match std::fs::read_to_string(path.join("version"))
.ok()
.map(|s| s.trim().to_string())
{
Some(v) => {
if is_default {
format!("{} (latest, {})", v, console::style("default").blue())
} else {
format!("{} (latest)", v)
}
}
None => "latest".to_string(),
}
} else if is_default {
format!(
"{} ({})",
n.to_string_lossy(),
console::style("default").blue()
)
} else {
n.to_string_lossy().to_string()
}
});
version
})
.collect::<Vec<_>>();
let toolchains = crate::toolchain::installed_toolchains()?;

println!("Moonup home: {}\n", crate::moonup_home().display());
if toolchains.is_empty() {
println!("No toolchains installed");
} else {
println!("Installed toolchains:");
for toolchain in toolchains {
println!(" {}", toolchain);
let tag = match (toolchain.default, toolchain.latest) {
(true, true) => Some(format!(" (latest, {})", console::style("default").blue())),
(true, false) => Some(format!(" ({})", console::style("default").blue())),
(false, true) => Some(" (latest)".to_string()),
(false, false) => None,
};

println!(" {}{}", toolchain.version, tag.unwrap_or_default());
}
}

Expand Down
65 changes: 65 additions & 0 deletions src/toolchain/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,67 @@
use miette::IntoDiagnostic;

pub mod index;
pub mod package;

pub struct InstalledToolchain {
/// Whether the installed toolchain is marked as default
pub default: bool,

/// Whether this toolchain is installed as 'latest'
pub latest: bool,

/// The actual version of the installed toolchain
pub version: String,
}

pub fn installed_toolchains() -> miette::Result<Vec<InstalledToolchain>> {
let toolchains_dir = crate::moonup_home().join("toolchains");
let default_version = default_toolchain();

let toolchains = toolchains_dir
.read_dir()
.into_diagnostic()?
.filter_map(std::io::Result::ok)
.filter_map(|e| {
let path = e.path();
let version = path.file_name().map(|n| {
let n = n.to_ascii_lowercase();
let latest = n == "latest";
let default = match default_version.as_deref() {
Some(v) => v == n,
None => false,
};
let version = match latest {
false => n.to_string_lossy().to_string(),
true => std::fs::read_to_string(path.join("version"))
.ok()
.map(|s| s.trim().to_string())
.unwrap_or_else(|| "latest".to_string()),
};

InstalledToolchain {
default,
latest,
version,
}
});

version
})
.collect::<Vec<_>>();

Ok(toolchains)
}

fn default_toolchain() -> Option<String> {
let default_file = crate::moonup_home().join("default");

std::fs::read_to_string(default_file).ok().and_then(|s| {
let v = s.trim().to_string();
if v.is_empty() {
None
} else {
Some(v)
}
})
}

0 comments on commit e8c6e1c

Please sign in to comment.