1
0
mirror of https://github.com/Llewellynvdm/starship.git synced 2024-12-14 06:48:21 +00:00
starship/src/modules/rust.rs

529 lines
17 KiB
Rust
Raw Normal View History

use std::fs;
use std::path::Path;
use std::process::{Command, Output};
2019-04-21 23:37:34 +00:00
use serde::Deserialize;
use super::{Context, Module, RootModuleConfig};
2019-05-01 20:34:24 +00:00
use crate::configs::rust::RustConfig;
use crate::formatter::{StringFormatter, VersionFormatter};
/// Creates a module with the current Rust version
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let mut module = context.new_module("rust");
let config = RustConfig::try_load(module.config);
2019-05-12 17:37:23 +00:00
let is_rs_project = context
.try_begin_scan()?
.set_files(&config.detect_files)
.set_extensions(&config.detect_extensions)
.set_folders(&config.detect_folders)
.is_match();
2019-05-12 17:37:23 +00:00
2019-04-21 23:37:34 +00:00
if !is_rs_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 {
// This may result in multiple calls to `get_module_version` when a user have
// multiple `$version` variables defined in `format`.
"version" => get_module_version(context, &config).map(Ok),
_ => None,
})
.parse(None)
});
module.set_segments(match parsed {
Ok(segments) => segments,
Err(error) => {
log::warn!("Error in module `rust`:\n{}", error);
return None;
}
});
Some(module)
}
fn get_module_version(context: &Context, config: &RustConfig) -> Option<String> {
// `$CARGO_HOME/bin/rustc(.exe) --version` may attempt installing a rustup toolchain.
// https://github.com/starship/starship/issues/417
//
// To display appropriate versions preventing `rustc` from downloading toolchains, we have to
// check
// 1. `$RUSTUP_TOOLCHAIN`
// 2. `rustup override list`
// 3. `rust-toolchain` or `rust-toolchain.toml` in `.` or parent directories
// as `rustup` does.
// https://github.com/rust-lang/rustup.rs/tree/eb694fcada7becc5d9d160bf7c623abe84f8971d#override-precedence
//
// Probably we have no other way to know whether any toolchain override is specified for the
// current directory. The following commands also cause toolchain installations.
// - `rustup show`
// - `rustup show active-toolchain`
// - `rustup which`
if let Some(toolchain) = env_rustup_toolchain(context)
.or_else(|| execute_rustup_override_list(&context.current_dir))
.or_else(|| find_rust_toolchain_file(&context))
{
match execute_rustup_run_rustc_version(&toolchain) {
RustupRunRustcVersionOutcome::RustcVersion(rustc_version) => {
format_rustc_version(&rustc_version, config.version_format)
}
RustupRunRustcVersionOutcome::ToolchainName(toolchain) => Some(toolchain),
RustupRunRustcVersionOutcome::RustupNotWorking => {
// If `rustup` is not in `$PATH` or cannot be executed for other reasons, we can
// safely execute `rustc --version`.
format_rustc_version(&execute_rustc_version()?, config.version_format)
}
RustupRunRustcVersionOutcome::Err => None,
}
} else {
format_rustc_version(&execute_rustc_version()?, config.version_format)
}
}
fn env_rustup_toolchain(context: &Context) -> Option<String> {
let val = context.get_env("RUSTUP_TOOLCHAIN")?;
Some(val.trim().to_owned())
}
fn execute_rustup_override_list(cwd: &Path) -> Option<String> {
let Output { stdout, .. } = Command::new("rustup")
.args(&["override", "list"])
.output()
.ok()?;
let stdout = String::from_utf8(stdout).ok()?;
extract_toolchain_from_rustup_override_list(&stdout, cwd)
}
fn extract_toolchain_from_rustup_override_list(stdout: &str, cwd: &Path) -> Option<String> {
if stdout == "no overrides\n" {
return None;
}
stdout
.lines()
.flat_map(|line| {
let mut words = line.split_whitespace();
let dir = words.next()?;
let toolchain = words.next()?;
Some((dir, toolchain))
})
.find(|(dir, _)| cwd.starts_with(dir))
.map(|(_, toolchain)| toolchain.to_owned())
}
fn find_rust_toolchain_file(context: &Context) -> Option<String> {
// Look for 'rust-toolchain' or 'rust-toolchain.toml' as rustup does.
// for more information:
// https://rust-lang.github.io/rustup/overrides.html#the-toolchain-file
// for the implementation in 'rustup':
// https://github.com/rust-lang/rustup/blob/a45e4cd21748b04472fce51ba29999ee4b62bdec/src/config.rs#L631
#[derive(Deserialize)]
struct OverrideFile {
toolchain: ToolchainSection,
}
#[derive(Deserialize)]
struct ToolchainSection {
channel: Option<String>,
}
fn read_channel(path: &Path, only_toml: bool) -> Option<String> {
let contents = fs::read_to_string(path).ok()?;
match contents.lines().count() {
0 => None,
1 if !only_toml => Some(contents),
_ => {
toml::from_str::<OverrideFile>(&contents)
.ok()?
.toolchain
.channel
}
}
.filter(|c| !c.trim().is_empty())
.map(|c| c.trim().to_owned())
}
if context
.dir_contents()
.map_or(false, |dir| dir.has_file("rust-toolchain"))
{
if let Some(toolchain) = read_channel(Path::new("rust-toolchain"), false) {
return Some(toolchain);
}
}
if context
.dir_contents()
.map_or(false, |dir| dir.has_file("rust-toolchain.toml"))
{
if let Some(toolchain) = read_channel(Path::new("rust-toolchain.toml"), true) {
return Some(toolchain);
}
}
2019-04-21 23:37:34 +00:00
let mut dir = &*context.current_dir;
loop {
if let Some(toolchain) = read_channel(&dir.join("rust-toolchain"), false) {
return Some(toolchain);
}
if let Some(toolchain) = read_channel(&dir.join("rust-toolchain.toml"), true) {
return Some(toolchain);
}
dir = dir.parent()?;
}
}
2019-04-21 23:37:34 +00:00
fn execute_rustup_run_rustc_version(toolchain: &str) -> RustupRunRustcVersionOutcome {
Command::new("rustup")
.args(&["run", toolchain, "rustc", "--version"])
.output()
.map(extract_toolchain_from_rustup_run_rustc_version)
.unwrap_or(RustupRunRustcVersionOutcome::RustupNotWorking)
}
2019-04-21 23:37:34 +00:00
fn extract_toolchain_from_rustup_run_rustc_version(output: Output) -> RustupRunRustcVersionOutcome {
if output.status.success() {
if let Ok(output) = String::from_utf8(output.stdout) {
return RustupRunRustcVersionOutcome::RustcVersion(output);
}
} else if let Ok(stderr) = String::from_utf8(output.stderr) {
if stderr.starts_with("error: toolchain '") && stderr.ends_with("' is not installed\n") {
let stderr = stderr
["error: toolchain '".len()..stderr.len() - "' is not installed\n".len()]
.to_owned();
return RustupRunRustcVersionOutcome::ToolchainName(stderr);
2019-04-21 23:37:34 +00:00
}
}
RustupRunRustcVersionOutcome::Err
2019-04-21 23:37:34 +00:00
}
fn execute_rustc_version() -> Option<String> {
match Command::new("rustc").arg("--version").output() {
2019-04-21 23:37:34 +00:00
Ok(output) => Some(String::from_utf8(output.stdout).unwrap()),
Err(_) => None,
}
}
fn format_rustc_version(rustc_version: &str, version_format: &str) -> Option<String> {
let version = rustc_version
// split into ["rustc", "1.34.0", ...]
.split_whitespace()
// get down to "1.34.0"
.nth(1)?;
match VersionFormatter::format_version(version, version_format) {
Ok(formatted) => Some(formatted),
Err(error) => {
feat: Add version formating for modules (#2611) * format crystal version with VersionFormatter * update crystal dosc * format crystal module * fix typos * format dart version with VersionFormatter * fix dart malformed test * update dart docs * format cmake version with VersionFormatter * update cmake docs * format deno version with VersionFormatter * update deno docs * remove Version type * format dotnet version with VersionFormatter * update dotnet docs * format erlang version with VersionFormatter * update erlang docs * format golang version with VersionFormatter * refactor formatting in my modules * format helm version with VersionFormatter * format julia version with VersionFormatter * format kotlin version with VersionFormatter * format lua version with VersionFormatter * format nim version with VersionFormatter * format perl version with VersionFormatter * format php version with VersionFormatter * format purescript version with VersionFormatter * format scala version with VersionFormatter * format swift version with VersionFormatter * format terraform version with VersionFormatter * format vagrant version with VersionFormatter * format zig version with VersionFormatter * format elixir version with VersionFormatter * format ocaml version with VersionFormatter * update elixir docs * update golang docs * update helm docs * update julia docs * update kotlin docs * update lua docs * update nim docs * update ocaml docs * update perl docs * update php docs * update purescript docs * update scala docs * update swift docs * update terraform docs * update vagrant docs * update zig docs * format elm version with VersionFormatter * update elm docs * pass module_name as &str to format_module_version
2021-04-29 21:22:20 +00:00
log::warn!("Error formatting `rust` version:\n{}", error);
Some(format!("v{}", version))
}
}
2019-04-21 23:37:34 +00:00
}
#[derive(Debug, PartialEq)]
enum RustupRunRustcVersionOutcome {
RustcVersion(String),
ToolchainName(String),
RustupNotWorking,
Err,
}
2019-04-21 23:37:34 +00:00
#[cfg(test)]
mod tests {
refactor(directory): Introduce `logical-path` argument which allows a shell to explicitly specify both a logical and physical filesystem path (#2104) * refactor(directory): Introduce `logical-path` argument which allows a shell to explicitly specify both a logical and physical filesystem path Fix `directory::module` to consume both path and logical-path (if provided). The "logical" path is preferred when rendering the "display path", while the "physical" path is used to resolve the "read only" flag. Repo- and home-directory contraction behavior is maintained, based on the logical path if it is set, or the physical path if it is not. The custom "get_current_dir" logic has been removed entirely, and the `directory` module now relies on `context.current_dir` / `context.logical_dir` entirely. Changes have been made to `init/starship.ps1` to work with this new flag: - Calculate and pass "physical" and "logical" paths explicitly (as other shells do not pass `--logical-path` that they fall back to rendering the physical path) - Moved the "powershell provider prefix" cleanup code to the PowerShell script - this code _should_ now support any kind of powershell path prefix. * fix(powershell): Fix an issue with trailing backslashes on file paths causing command line parsing issues. This is a bit of a footgun! The work-around chosen is to append a trailing space when a path string ends with a backslash, and then trim any extra whitespace away in the Context constructor. Other alternatives considered and rejected: 1. Always trim trailing backslashes as the filesystem generally doesn't need them. 2. Escape trailing backslashes with another backslash. This proved complex as PS only quotes string args when the string includes some whitespace, and other backslashes within the string apparently don't need to be escaped. * fix(powershell): Use Invoke-Native pattern for safely invoking native executables with strings which may contain characters which need to be escaped carefully. * fix(context): Remove superfluous argument trims These were in place to clean up extra whitespace sometimes injected by starship.ps1::prompt, and are no longer required with the new Invoke-Native helper in place. * refactor(directory): Clean up the semantics of `logical_dir` defaulting it to `current_dir` but overridable by the `--logical-dir` flag. - Restore `use_logical_path` config flag. - Always attempt to contract repo paths from the `current_dir`. * fix(directory) :Use logical_dir for contracting the home directory This keeps the two calls to contract_path in sync. * fix(directory): Remove test script * refactor(directory): Convert current_dir to canonical filesystem path when use_logical_path = false - This requires some clean-up to remove the extended-path prefix on Windows - The configured logical_dir is ignored entirely in this mode - we calculate a new logical_dir by cleaning up the physical_dir path for display. - Test coverage * fix(directory): Use AsRef style for passing Path arguments * fix(directory): Strip the windows extended-path prefix from the display string later in the render process * fix(docs): Update docs/config/README.md for use_logical_path * refactor(context): Populate `current_dir` from `--path` or `std::env::current_dir`, populate `logical_dir` from `--logical-path` or the `PWD` env var - `current_dir` is always canonicalized - On Windows, `current_dir` will have an extended-path prefix - `logical_dir` is now always set - `directory::module` now just selects between `current_dir` and `logical_dir` when picking which path to render - Test coverage * fix(directory): Fix path comparison operations in directory to ignore differences between path prefixes - Added PathExt extension trait which adds `normalised_equals`, `normalised_starts_with` and `without_prefix` * fix(path): Add test coverage for PathExt on *nix * fix(directory): Test coverage for `contract_repo_path`, `contract_path` with variations of verbatim and non-verbatim paths * fix(directory): Update path-slash to latest This fixes the issue with the trailing character of some Windows paths being truncated, e.g. `\\server\share` and `C:` * fix(powershell): Improve UTF8 output handling, argument encoding - Use `ProcessStartInfo` to launch native executable, replacing manual UTF8 output encoding handling - If we detect we're on PWSH6+ use the new `System.Diagnostics.ProcessStartInfo.ArgumentList` parameter, otherwise manually escape the argument string - Move `Get-Cwd` and `Invoke-Native` into the prompt function scope so that they don't leak into the user's shell scope * fix(path): Make PathExt methods no-ops on *nix * fix(path): Cargo fmt * fix(powershell): Remove typo ';'. Fix variable assignment lint.
2021-02-08 14:14:59 +00:00
use crate::context::Shell;
use once_cell::sync::Lazy;
use std::io;
use std::process::{ExitStatus, Output};
2019-04-21 23:37:34 +00:00
use super::*;
#[test]
fn test_extract_toolchain_from_rustup_override_list() {
static NO_OVERRIDES_INPUT: &str = "no overrides\n";
static NO_OVERRIDES_CWD: &str = "";
assert_eq!(
extract_toolchain_from_rustup_override_list(
NO_OVERRIDES_INPUT,
NO_OVERRIDES_CWD.as_ref(),
),
None,
);
static OVERRIDES_INPUT: &str =
"/home/user/src/a beta-x86_64-unknown-linux-gnu\n\
/home/user/src/b nightly-x86_64-unknown-linux-gnu\n";
static OVERRIDES_CWD_A: &str = "/home/user/src/a/src";
static OVERRIDES_CWD_B: &str = "/home/user/src/b/tests";
static OVERRIDES_CWD_C: &str = "/home/user/src/c/examples";
assert_eq!(
extract_toolchain_from_rustup_override_list(OVERRIDES_INPUT, OVERRIDES_CWD_A.as_ref()),
Some("beta-x86_64-unknown-linux-gnu".to_owned()),
);
assert_eq!(
extract_toolchain_from_rustup_override_list(OVERRIDES_INPUT, OVERRIDES_CWD_B.as_ref()),
Some("nightly-x86_64-unknown-linux-gnu".to_owned()),
);
assert_eq!(
extract_toolchain_from_rustup_override_list(OVERRIDES_INPUT, OVERRIDES_CWD_C.as_ref()),
None,
);
}
#[cfg(any(unix, windows))]
#[test]
fn test_extract_toolchain_from_rustup_run_rustc_version() {
#[cfg(unix)]
use std::os::unix::process::ExitStatusExt as _;
#[cfg(windows)]
use std::os::windows::process::ExitStatusExt as _;
static RUSTC_VERSION: Lazy<Output> = Lazy::new(|| Output {
status: ExitStatus::from_raw(0),
stdout: b"rustc 1.34.0\n"[..].to_owned(),
stderr: vec![],
});
assert_eq!(
extract_toolchain_from_rustup_run_rustc_version(RUSTC_VERSION.clone()),
RustupRunRustcVersionOutcome::RustcVersion("rustc 1.34.0\n".to_owned()),
);
static TOOLCHAIN_NAME: Lazy<Output> = Lazy::new(|| Output {
status: ExitStatus::from_raw(1),
stdout: vec![],
stderr: b"error: toolchain 'channel-triple' is not installed\n"[..].to_owned(),
});
assert_eq!(
extract_toolchain_from_rustup_run_rustc_version(TOOLCHAIN_NAME.clone()),
RustupRunRustcVersionOutcome::ToolchainName("channel-triple".to_owned()),
);
static INVALID_STDOUT: Lazy<Output> = Lazy::new(|| Output {
status: ExitStatus::from_raw(0),
stdout: b"\xc3\x28"[..].to_owned(),
stderr: vec![],
});
assert_eq!(
extract_toolchain_from_rustup_run_rustc_version(INVALID_STDOUT.clone()),
RustupRunRustcVersionOutcome::Err,
);
static INVALID_STDERR: Lazy<Output> = Lazy::new(|| Output {
status: ExitStatus::from_raw(1),
stdout: vec![],
stderr: b"\xc3\x28"[..].to_owned(),
});
assert_eq!(
extract_toolchain_from_rustup_run_rustc_version(INVALID_STDERR.clone()),
RustupRunRustcVersionOutcome::Err,
);
static UNEXPECTED_FORMAT_OF_ERROR: Lazy<Output> = Lazy::new(|| Output {
status: ExitStatus::from_raw(1),
stdout: vec![],
stderr: b"error:"[..].to_owned(),
});
assert_eq!(
extract_toolchain_from_rustup_run_rustc_version(UNEXPECTED_FORMAT_OF_ERROR.clone()),
RustupRunRustcVersionOutcome::Err,
);
}
2019-04-21 23:37:34 +00:00
#[test]
fn test_format_rustc_version() {
let config = RustConfig::default();
let rustc_stable = "rustc 1.34.0 (91856ed52 2019-04-10)";
let rustc_beta = "rustc 1.34.0-beta.1 (2bc1d406d 2019-04-10)";
let rustc_nightly = "rustc 1.34.0-nightly (b139669f3 2019-04-10)";
assert_eq!(
format_rustc_version(rustc_nightly, config.version_format),
Some("v1.34.0-nightly".to_string())
);
assert_eq!(
format_rustc_version(rustc_beta, config.version_format),
Some("v1.34.0-beta.1".to_string())
);
assert_eq!(
format_rustc_version(rustc_stable, config.version_format),
Some("v1.34.0".to_string())
);
assert_eq!(
format_rustc_version("rustc 1.34.0", config.version_format),
Some("v1.34.0".to_string())
);
2019-04-21 23:37:34 +00:00
}
#[test]
fn test_find_rust_toolchain_file() -> io::Result<()> {
// `rust-toolchain` with toolchain in one line
let dir = tempfile::tempdir()?;
fs::write(dir.path().join("rust-toolchain"), "1.34.0")?;
refactor(directory): Introduce `logical-path` argument which allows a shell to explicitly specify both a logical and physical filesystem path (#2104) * refactor(directory): Introduce `logical-path` argument which allows a shell to explicitly specify both a logical and physical filesystem path Fix `directory::module` to consume both path and logical-path (if provided). The "logical" path is preferred when rendering the "display path", while the "physical" path is used to resolve the "read only" flag. Repo- and home-directory contraction behavior is maintained, based on the logical path if it is set, or the physical path if it is not. The custom "get_current_dir" logic has been removed entirely, and the `directory` module now relies on `context.current_dir` / `context.logical_dir` entirely. Changes have been made to `init/starship.ps1` to work with this new flag: - Calculate and pass "physical" and "logical" paths explicitly (as other shells do not pass `--logical-path` that they fall back to rendering the physical path) - Moved the "powershell provider prefix" cleanup code to the PowerShell script - this code _should_ now support any kind of powershell path prefix. * fix(powershell): Fix an issue with trailing backslashes on file paths causing command line parsing issues. This is a bit of a footgun! The work-around chosen is to append a trailing space when a path string ends with a backslash, and then trim any extra whitespace away in the Context constructor. Other alternatives considered and rejected: 1. Always trim trailing backslashes as the filesystem generally doesn't need them. 2. Escape trailing backslashes with another backslash. This proved complex as PS only quotes string args when the string includes some whitespace, and other backslashes within the string apparently don't need to be escaped. * fix(powershell): Use Invoke-Native pattern for safely invoking native executables with strings which may contain characters which need to be escaped carefully. * fix(context): Remove superfluous argument trims These were in place to clean up extra whitespace sometimes injected by starship.ps1::prompt, and are no longer required with the new Invoke-Native helper in place. * refactor(directory): Clean up the semantics of `logical_dir` defaulting it to `current_dir` but overridable by the `--logical-dir` flag. - Restore `use_logical_path` config flag. - Always attempt to contract repo paths from the `current_dir`. * fix(directory) :Use logical_dir for contracting the home directory This keeps the two calls to contract_path in sync. * fix(directory): Remove test script * refactor(directory): Convert current_dir to canonical filesystem path when use_logical_path = false - This requires some clean-up to remove the extended-path prefix on Windows - The configured logical_dir is ignored entirely in this mode - we calculate a new logical_dir by cleaning up the physical_dir path for display. - Test coverage * fix(directory): Use AsRef style for passing Path arguments * fix(directory): Strip the windows extended-path prefix from the display string later in the render process * fix(docs): Update docs/config/README.md for use_logical_path * refactor(context): Populate `current_dir` from `--path` or `std::env::current_dir`, populate `logical_dir` from `--logical-path` or the `PWD` env var - `current_dir` is always canonicalized - On Windows, `current_dir` will have an extended-path prefix - `logical_dir` is now always set - `directory::module` now just selects between `current_dir` and `logical_dir` when picking which path to render - Test coverage * fix(directory): Fix path comparison operations in directory to ignore differences between path prefixes - Added PathExt extension trait which adds `normalised_equals`, `normalised_starts_with` and `without_prefix` * fix(path): Add test coverage for PathExt on *nix * fix(directory): Test coverage for `contract_repo_path`, `contract_path` with variations of verbatim and non-verbatim paths * fix(directory): Update path-slash to latest This fixes the issue with the trailing character of some Windows paths being truncated, e.g. `\\server\share` and `C:` * fix(powershell): Improve UTF8 output handling, argument encoding - Use `ProcessStartInfo` to launch native executable, replacing manual UTF8 output encoding handling - If we detect we're on PWSH6+ use the new `System.Diagnostics.ProcessStartInfo.ArgumentList` parameter, otherwise manually escape the argument string - Move `Get-Cwd` and `Invoke-Native` into the prompt function scope so that they don't leak into the user's shell scope * fix(path): Make PathExt methods no-ops on *nix * fix(path): Cargo fmt * fix(powershell): Remove typo ';'. Fix variable assignment lint.
2021-02-08 14:14:59 +00:00
let context = Context::new_with_shell_and_path(
Default::default(),
Shell::Unknown,
dir.path().into(),
dir.path().into(),
);
assert_eq!(
find_rust_toolchain_file(&context),
Some("1.34.0".to_owned())
);
dir.close()?;
// `rust-toolchain` in toml format
let dir = tempfile::tempdir()?;
fs::write(
dir.path().join("rust-toolchain"),
"[toolchain]\nchannel = \"1.34.0\"",
)?;
refactor(directory): Introduce `logical-path` argument which allows a shell to explicitly specify both a logical and physical filesystem path (#2104) * refactor(directory): Introduce `logical-path` argument which allows a shell to explicitly specify both a logical and physical filesystem path Fix `directory::module` to consume both path and logical-path (if provided). The "logical" path is preferred when rendering the "display path", while the "physical" path is used to resolve the "read only" flag. Repo- and home-directory contraction behavior is maintained, based on the logical path if it is set, or the physical path if it is not. The custom "get_current_dir" logic has been removed entirely, and the `directory` module now relies on `context.current_dir` / `context.logical_dir` entirely. Changes have been made to `init/starship.ps1` to work with this new flag: - Calculate and pass "physical" and "logical" paths explicitly (as other shells do not pass `--logical-path` that they fall back to rendering the physical path) - Moved the "powershell provider prefix" cleanup code to the PowerShell script - this code _should_ now support any kind of powershell path prefix. * fix(powershell): Fix an issue with trailing backslashes on file paths causing command line parsing issues. This is a bit of a footgun! The work-around chosen is to append a trailing space when a path string ends with a backslash, and then trim any extra whitespace away in the Context constructor. Other alternatives considered and rejected: 1. Always trim trailing backslashes as the filesystem generally doesn't need them. 2. Escape trailing backslashes with another backslash. This proved complex as PS only quotes string args when the string includes some whitespace, and other backslashes within the string apparently don't need to be escaped. * fix(powershell): Use Invoke-Native pattern for safely invoking native executables with strings which may contain characters which need to be escaped carefully. * fix(context): Remove superfluous argument trims These were in place to clean up extra whitespace sometimes injected by starship.ps1::prompt, and are no longer required with the new Invoke-Native helper in place. * refactor(directory): Clean up the semantics of `logical_dir` defaulting it to `current_dir` but overridable by the `--logical-dir` flag. - Restore `use_logical_path` config flag. - Always attempt to contract repo paths from the `current_dir`. * fix(directory) :Use logical_dir for contracting the home directory This keeps the two calls to contract_path in sync. * fix(directory): Remove test script * refactor(directory): Convert current_dir to canonical filesystem path when use_logical_path = false - This requires some clean-up to remove the extended-path prefix on Windows - The configured logical_dir is ignored entirely in this mode - we calculate a new logical_dir by cleaning up the physical_dir path for display. - Test coverage * fix(directory): Use AsRef style for passing Path arguments * fix(directory): Strip the windows extended-path prefix from the display string later in the render process * fix(docs): Update docs/config/README.md for use_logical_path * refactor(context): Populate `current_dir` from `--path` or `std::env::current_dir`, populate `logical_dir` from `--logical-path` or the `PWD` env var - `current_dir` is always canonicalized - On Windows, `current_dir` will have an extended-path prefix - `logical_dir` is now always set - `directory::module` now just selects between `current_dir` and `logical_dir` when picking which path to render - Test coverage * fix(directory): Fix path comparison operations in directory to ignore differences between path prefixes - Added PathExt extension trait which adds `normalised_equals`, `normalised_starts_with` and `without_prefix` * fix(path): Add test coverage for PathExt on *nix * fix(directory): Test coverage for `contract_repo_path`, `contract_path` with variations of verbatim and non-verbatim paths * fix(directory): Update path-slash to latest This fixes the issue with the trailing character of some Windows paths being truncated, e.g. `\\server\share` and `C:` * fix(powershell): Improve UTF8 output handling, argument encoding - Use `ProcessStartInfo` to launch native executable, replacing manual UTF8 output encoding handling - If we detect we're on PWSH6+ use the new `System.Diagnostics.ProcessStartInfo.ArgumentList` parameter, otherwise manually escape the argument string - Move `Get-Cwd` and `Invoke-Native` into the prompt function scope so that they don't leak into the user's shell scope * fix(path): Make PathExt methods no-ops on *nix * fix(path): Cargo fmt * fix(powershell): Remove typo ';'. Fix variable assignment lint.
2021-02-08 14:14:59 +00:00
let context = Context::new_with_shell_and_path(
Default::default(),
Shell::Unknown,
dir.path().into(),
dir.path().into(),
);
assert_eq!(
find_rust_toolchain_file(&context),
Some("1.34.0".to_owned())
);
dir.close()?;
// `rust-toolchain` in toml format with new lines
let dir = tempfile::tempdir()?;
fs::write(
dir.path().join("rust-toolchain"),
"\n\n[toolchain]\n\n\nchannel = \"1.34.0\"",
)?;
let context = Context::new_with_shell_and_path(
Default::default(),
Shell::Unknown,
dir.path().into(),
dir.path().into(),
);
assert_eq!(
find_rust_toolchain_file(&context),
Some("1.34.0".to_owned())
);
dir.close()?;
// `rust-toolchain` in parent directory.
let dir = tempfile::tempdir()?;
let child_dir_path = dir.path().join("child");
fs::create_dir(&child_dir_path)?;
fs::write(
dir.path().join("rust-toolchain"),
"\n\n[toolchain]\n\n\nchannel = \"1.34.0\"",
)?;
let context = Context::new_with_shell_and_path(
Default::default(),
Shell::Unknown,
child_dir_path.clone(),
child_dir_path,
);
assert_eq!(
find_rust_toolchain_file(&context),
Some("1.34.0".to_owned())
);
dir.close()?;
// `rust-toolchain.toml` with toolchain in one line
// This should not work!
// See https://rust-lang.github.io/rustup/overrides.html#the-toolchain-file
let dir = tempfile::tempdir()?;
fs::write(dir.path().join("rust-toolchain.toml"), "1.34.0")?;
let context = Context::new_with_shell_and_path(
Default::default(),
Shell::Unknown,
dir.path().into(),
dir.path().into(),
);
assert_eq!(find_rust_toolchain_file(&context), None);
dir.close()?;
// `rust-toolchain.toml` in toml format
let dir = tempfile::tempdir()?;
fs::write(
dir.path().join("rust-toolchain.toml"),
"[toolchain]\nchannel = \"1.34.0\"",
)?;
let context = Context::new_with_shell_and_path(
Default::default(),
Shell::Unknown,
dir.path().into(),
dir.path().into(),
);
assert_eq!(
find_rust_toolchain_file(&context),
Some("1.34.0".to_owned())
);
dir.close()?;
// `rust-toolchain.toml` in toml format with new lines
let dir = tempfile::tempdir()?;
fs::write(
dir.path().join("rust-toolchain.toml"),
"\n\n[toolchain]\n\n\nchannel = \"1.34.0\"",
)?;
refactor(directory): Introduce `logical-path` argument which allows a shell to explicitly specify both a logical and physical filesystem path (#2104) * refactor(directory): Introduce `logical-path` argument which allows a shell to explicitly specify both a logical and physical filesystem path Fix `directory::module` to consume both path and logical-path (if provided). The "logical" path is preferred when rendering the "display path", while the "physical" path is used to resolve the "read only" flag. Repo- and home-directory contraction behavior is maintained, based on the logical path if it is set, or the physical path if it is not. The custom "get_current_dir" logic has been removed entirely, and the `directory` module now relies on `context.current_dir` / `context.logical_dir` entirely. Changes have been made to `init/starship.ps1` to work with this new flag: - Calculate and pass "physical" and "logical" paths explicitly (as other shells do not pass `--logical-path` that they fall back to rendering the physical path) - Moved the "powershell provider prefix" cleanup code to the PowerShell script - this code _should_ now support any kind of powershell path prefix. * fix(powershell): Fix an issue with trailing backslashes on file paths causing command line parsing issues. This is a bit of a footgun! The work-around chosen is to append a trailing space when a path string ends with a backslash, and then trim any extra whitespace away in the Context constructor. Other alternatives considered and rejected: 1. Always trim trailing backslashes as the filesystem generally doesn't need them. 2. Escape trailing backslashes with another backslash. This proved complex as PS only quotes string args when the string includes some whitespace, and other backslashes within the string apparently don't need to be escaped. * fix(powershell): Use Invoke-Native pattern for safely invoking native executables with strings which may contain characters which need to be escaped carefully. * fix(context): Remove superfluous argument trims These were in place to clean up extra whitespace sometimes injected by starship.ps1::prompt, and are no longer required with the new Invoke-Native helper in place. * refactor(directory): Clean up the semantics of `logical_dir` defaulting it to `current_dir` but overridable by the `--logical-dir` flag. - Restore `use_logical_path` config flag. - Always attempt to contract repo paths from the `current_dir`. * fix(directory) :Use logical_dir for contracting the home directory This keeps the two calls to contract_path in sync. * fix(directory): Remove test script * refactor(directory): Convert current_dir to canonical filesystem path when use_logical_path = false - This requires some clean-up to remove the extended-path prefix on Windows - The configured logical_dir is ignored entirely in this mode - we calculate a new logical_dir by cleaning up the physical_dir path for display. - Test coverage * fix(directory): Use AsRef style for passing Path arguments * fix(directory): Strip the windows extended-path prefix from the display string later in the render process * fix(docs): Update docs/config/README.md for use_logical_path * refactor(context): Populate `current_dir` from `--path` or `std::env::current_dir`, populate `logical_dir` from `--logical-path` or the `PWD` env var - `current_dir` is always canonicalized - On Windows, `current_dir` will have an extended-path prefix - `logical_dir` is now always set - `directory::module` now just selects between `current_dir` and `logical_dir` when picking which path to render - Test coverage * fix(directory): Fix path comparison operations in directory to ignore differences between path prefixes - Added PathExt extension trait which adds `normalised_equals`, `normalised_starts_with` and `without_prefix` * fix(path): Add test coverage for PathExt on *nix * fix(directory): Test coverage for `contract_repo_path`, `contract_path` with variations of verbatim and non-verbatim paths * fix(directory): Update path-slash to latest This fixes the issue with the trailing character of some Windows paths being truncated, e.g. `\\server\share` and `C:` * fix(powershell): Improve UTF8 output handling, argument encoding - Use `ProcessStartInfo` to launch native executable, replacing manual UTF8 output encoding handling - If we detect we're on PWSH6+ use the new `System.Diagnostics.ProcessStartInfo.ArgumentList` parameter, otherwise manually escape the argument string - Move `Get-Cwd` and `Invoke-Native` into the prompt function scope so that they don't leak into the user's shell scope * fix(path): Make PathExt methods no-ops on *nix * fix(path): Cargo fmt * fix(powershell): Remove typo ';'. Fix variable assignment lint.
2021-02-08 14:14:59 +00:00
let context = Context::new_with_shell_and_path(
Default::default(),
Shell::Unknown,
dir.path().into(),
dir.path().into(),
);
assert_eq!(
find_rust_toolchain_file(&context),
Some("1.34.0".to_owned())
);
dir.close()?;
// `rust-toolchain.toml` in parent directory.
let dir = tempfile::tempdir()?;
let child_dir_path = dir.path().join("child");
fs::create_dir(&child_dir_path)?;
fs::write(
dir.path().join("rust-toolchain.toml"),
"\n\n[toolchain]\n\n\nchannel = \"1.34.0\"",
)?;
let context = Context::new_with_shell_and_path(
Default::default(),
Shell::Unknown,
child_dir_path.clone(),
child_dir_path,
);
assert_eq!(
find_rust_toolchain_file(&context),
Some("1.34.0".to_owned())
);
dir.close()
}
2019-04-21 23:37:34 +00:00
}