Skip to main content
Glama
find.rs5.04 kB
use std::{ env, ffi::OsStr, path::{ Path, PathBuf, }, str::FromStr, }; use directories::ProjectDirs; use tracing::{ debug, trace, }; use crate::{ ConfigFileError, FileFormat, ToFileFormats, }; pub fn find( app_name: impl AsRef<str>, file_formats: impl ToFileFormats, env_var: &Option<impl AsRef<OsStr>>, ) -> Result<Option<(PathBuf, FileFormat)>, ConfigFileError> { let app_name = app_name.as_ref(); let (candidate, file_format) = match find_first_file_candidate(app_name, file_formats, env_var)? { Some(candidate) => candidate, None => return Ok(None), }; let target = candidate .canonicalize() .map_err(|err| ConfigFileError::Canonicalize(err, candidate))?; Ok(Some((target, file_format))) } fn find_first_file_candidate( app_name: impl AsRef<str>, file_formats: impl ToFileFormats, env_var: &Option<impl AsRef<OsStr>>, ) -> Result<Option<(PathBuf, FileFormat)>, ConfigFileError> { let app_name = app_name.as_ref(); let goals: Vec<_> = file_formats .to_file_formats()? .map(|f| (goal_file_name(app_name, f), f)) .collect(); // Return candidate if environment variable is asked for with a value if let Some(env_var) = env_var { trace!( "checking environment variable; var={}", env_var.as_ref().to_string_lossy().as_ref() ); #[allow(clippy::disallowed_methods)] // This method explicitly checks env vars as its // strategy if let Ok(value) = env::var(env_var) { let env_candidate = Path::new(&value); if env_candidate.is_file() { trace!( "candidate found in environment and is a file; var={}, candidate={}", env_var.as_ref().to_string_lossy().as_ref(), env_candidate.display() ); let extension = env_candidate .extension() .ok_or_else(|| { ConfigFileError::UnknownFileFormat( env_candidate.to_string_lossy().to_string(), ) })? .to_string_lossy(); let file_format = FileFormat::from_str(extension.as_ref())?; let found = PathBuf::from(value); trace!(path = %found.display(), "found candidate from environment"); return Ok(Some((found, file_format))); } // File doesn't exist, but we asked for it explicitly, so we fail return Err(ConfigFileError::EnvNotFound( app_name.to_string(), env_var.as_ref().to_string_lossy().to_string(), value, )); } } // Return candidate if found in the current directory for (goal, file_format) in &goals { let current_dir_candidate = env::current_dir() .map_err(ConfigFileError::CurrentDirectory)? .join(goal); trace!(path = %current_dir_candidate.display(), "checking candidate in current directory"); if current_dir_candidate.is_file() { debug!(path = %current_dir_candidate.display(), "found candidate in current directory"); return Ok(Some((current_dir_candidate, *file_format))); } } // Return candidate if found in the user's local config directory for (goal, file_format) in &goals { let user_candidate = ProjectDirs::from("", "", app_name) .ok_or(ConfigFileError::HomeDirectory)? .config_dir() .join(goal); trace!(path = %user_candidate.display(), "checking candidate in user directory"); if user_candidate.is_file() { debug!(path = %user_candidate.display(), "found candidate in user directory"); return Ok(Some((user_candidate, *file_format))); } } // Return candidate if found in root location for (goal, file_format) in &goals { for prefix in ["/usr/local/etc", "/etc"] { let mut root_candidate = Path::new(prefix).join(app_name).join(goal); #[allow(clippy::disallowed_methods)] // This is a supported env var which we must check if let Ok(fs_root) = env::var("FS_ROOT") { root_candidate = Path::new(&fs_root).join(root_candidate); } trace!(path = %root_candidate.display(), "checking candidate in root"); if root_candidate.is_file() { debug!(path = %root_candidate.display(), "found candidate in root"); return Ok(Some((root_candidate, *file_format))); } } } // We tried, but couldn't find a successful candidate debug!("no config file candidates found"); Ok(None) } fn goal_file_name(app_name: impl AsRef<str>, file_format: FileFormat) -> String { format!("{}.{}", app_name.as_ref(), file_format.as_str()) }

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