Skip to main content
Glama
canonical_command.rs3.52 kB
use std::{ borrow::Cow, env, ffi::{ OsStr, OsString, }, io, path::{ Path, PathBuf, }, str::FromStr, }; use thiserror::Error; #[remain::sorted] #[derive(Debug, Error)] pub enum CanonicalCommandError { #[error("failed to canonicalize: {1}")] Canonicalize(#[source] io::Error, PathBuf), #[error("program not found on PATH: {0}")] NotFound(String), } #[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct CanonicalCommand(PathBuf); impl CanonicalCommand { #[must_use] pub fn as_path(&self) -> &Path { self.0.as_path() } } impl AsRef<Path> for CanonicalCommand { fn as_ref(&self) -> &Path { self.0.as_path() } } impl AsRef<OsStr> for CanonicalCommand { fn as_ref(&self) -> &OsStr { self.0.as_os_str() } } impl From<CanonicalCommand> for PathBuf { fn from(value: CanonicalCommand) -> Self { value.0 } } impl TryFrom<Box<Path>> for CanonicalCommand { type Error = CanonicalCommandError; fn try_from(value: Box<Path>) -> Result<Self, Self::Error> { let command = canonicalize_command(value.as_ref())?; Ok(Self(command)) } } impl TryFrom<PathBuf> for CanonicalCommand { type Error = CanonicalCommandError; fn try_from(value: PathBuf) -> Result<Self, Self::Error> { let command = canonicalize_command(value)?; Ok(Self(command)) } } impl TryFrom<OsString> for CanonicalCommand { type Error = CanonicalCommandError; fn try_from(value: OsString) -> Result<Self, Self::Error> { let command = canonicalize_command(value)?; Ok(Self(command)) } } impl TryFrom<String> for CanonicalCommand { type Error = CanonicalCommandError; fn try_from(value: String) -> Result<Self, Self::Error> { let command = canonicalize_command(value)?; Ok(Self(command)) } } impl TryFrom<&str> for CanonicalCommand { type Error = CanonicalCommandError; fn try_from(value: &str) -> Result<Self, Self::Error> { let command = canonicalize_command(value)?; Ok(Self(command)) } } impl<'a> TryFrom<Cow<'a, Path>> for CanonicalCommand { type Error = CanonicalCommandError; fn try_from(value: Cow<'a, Path>) -> Result<Self, Self::Error> { let command = canonicalize_command(value.as_ref())?; Ok(Self(command)) } } impl FromStr for CanonicalCommand { type Err = CanonicalCommandError; fn from_str(s: &str) -> Result<Self, Self::Err> { let command = canonicalize_command(s)?; Ok(Self(command)) } } fn canonicalize_command(program: impl AsRef<OsStr>) -> Result<PathBuf, CanonicalCommandError> { let found = find_command(program)?; found .canonicalize() .map_err(|err| CanonicalCommandError::Canonicalize(err, found.clone())) } fn find_command(program: impl AsRef<OsStr>) -> Result<PathBuf, CanonicalCommandError> { let path = Path::new(program.as_ref()); if path.is_file() { Ok(path.to_path_buf()) } else { #[allow(clippy::disallowed_methods)] // We use `$PATH` lookup to find the command env::split_paths(&env::var("PATH").unwrap_or_else(|_| "".to_string())) .map(|path| path.join(program.as_ref())) .find(|candidate| candidate.is_file()) .ok_or_else(|| { CanonicalCommandError::NotFound(program.as_ref().to_string_lossy().to_string()) }) } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server