Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions src/elan-cli/elan_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,13 @@ pub fn list_toolchains(cfg: &Cfg) -> Result<()> {
Ok(())
}

fn get_toolchain_local_target(cfg: &Cfg, t: &ToolchainDesc) -> Result<Option<std::path::PathBuf>> {
let ToolchainDesc::Local { .. } = &t else {
return Ok(None);
};
Ok(cfg.get_toolchain(&t, false)?.symlink_target()?)
}

fn show(cfg: &Cfg) -> Result<()> {
let cwd = &(utils::current_dir()?);
let installed_toolchains = cfg.list_toolchains()?;
Expand All @@ -357,10 +364,20 @@ fn show(cfg: &Cfg) -> Result<()> {
print_header("installed toolchains")
}
for t in installed_toolchains {
println!(
"{}",
mk_toolchain_label(&t, &default_tc, &resolved_default_tc)
);
// If t is a local toolchain, look up the symlink and print
// where it points.
if let Some(path) = get_toolchain_local_target(&cfg, &t)? {
println!(
"{} (linked to local path {})",
&t,
Copy link
Contributor Author

@jcreedcmu jcreedcmu Sep 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be mk_toolchain_label(&t, &default_tc, &resolved_default_tc) instead of just &t? I'm not sure about the potential for different decorations to overlap.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, seems fine

path.display().to_string()
);
} else {
println!(
"{}",
mk_toolchain_label(&t, &default_tc, &resolved_default_tc)
);
}
}
if show_headers {
println!()
Expand Down
6 changes: 6 additions & 0 deletions src/elan-utils/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ error_chain! {
description("could not symlink directory")
display("could not create link from '{}' to '{}'", src.display(), dest.display())
}
ReadingSymlink {
path: PathBuf,
} {
description("could not read symlink")
display("could not read symlink at '{}'", path.display())
}
CopyingDirectory {
src: PathBuf,
dest: PathBuf,
Expand Down
28 changes: 24 additions & 4 deletions src/elan-utils/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::errors::*;
use crate::notifications::Notification;
use dirs;
use json;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is just rustfmt, happy to remove if it's undesired noise

use std::cmp::Ord;
use std::env;
use std::ffi::OsString;
Expand All @@ -11,7 +12,6 @@ use std::process::Command;
use url::Url;
#[cfg(windows)]
use winreg;
use json;

use crate::raw;

Expand Down Expand Up @@ -262,6 +262,25 @@ pub fn symlink_file(src: &Path, dest: &Path) -> Result<()> {
.into())
}

// If we are on unix, we expect reading the symlink to work,
// and so return Some.
#[cfg(unix)]
pub fn read_link(path: &Path) -> Result<Option<PathBuf>> {
Ok(Some(::std::fs::read_link(path).chain_err(|| {
ErrorKind::ReadingSymlink {
path: PathBuf::from(path),
}
})?))
}

// If we are on windows, we don't attempt to read the symlink at all,
// and return None. This is not an error, but merely advice to the
// caller to not even attempt to show the symlink target.
#[cfg(windows)]
pub fn read_link(path: &Path) -> Result<Option<PathBuf>> {
Ok(None)
}

pub fn copy_dir(src: &Path, dest: &Path, notify_handler: &dyn Fn(Notification<'_>)) -> Result<()> {
notify_handler(Notification::CopyingDirectory(src, dest));
raw::copy_dir(src, dest).chain_err(|| ErrorKind::CopyingDirectory {
Expand Down Expand Up @@ -512,9 +531,10 @@ pub fn fetch_latest_release_json(url: &str, channel: &str, no_net: bool) -> Resu
fetch_url(&url)
}?;

let releases = json::parse(&json)
.chain_err(|| format!("failed to parse release data: {}", url))?;
releases[channel][0]["name"].as_str()
let releases =
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also just rustfmt

json::parse(&json).chain_err(|| format!("failed to parse release data: {}", url))?;
releases[channel][0]["name"]
.as_str()
.ok_or_else(|| format!("failed to parse release data: {}", url).into())
.map(|s| s.to_string())
}
Expand Down
10 changes: 7 additions & 3 deletions src/elan/toolchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,10 @@ pub fn resolve_toolchain_desc_ext(
utils::fetch_latest_release_json(uri, release, no_net)
} else {
if release == "beta" {
return Err(Error::from(
format!("channel 'beta' is not supported for custom origin '{}'", origin)
));
return Err(Error::from(format!(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also just rustfmt

"channel 'beta' is not supported for custom origin '{}'",
origin
)));
}
utils::fetch_latest_release_tag(origin, no_net)
};
Expand Down Expand Up @@ -233,6 +234,9 @@ impl<'a> Toolchain<'a> {
.map(|m| m.file_type().is_symlink())
.unwrap_or(false)
}
pub fn symlink_target(&self) -> Result<Option<PathBuf>> {
Ok(utils::read_link(&self.path)?)
}
pub fn exists(&self) -> bool {
// HACK: linked toolchains are symlinks, and, contrary to what std docs
// lead me to believe `fs::metadata`, used by `is_directory` does not
Expand Down
Loading