| use std::borrow::Cow; |
| use std::ops::Range; |
| use std::path::Path; |
| |
| use cargo_util::paths::normalize_path; |
| use pathdiff::diff_paths; |
| |
| use crate::GlobalContext; |
| use crate::workspace::Workspace; |
| |
| /// Display path, generally relative to the workspace |
| /// |
| /// Mirrors [`crate::util::path_args`] |
| pub fn workspace_rel_path(ws: &Workspace<'_>, path: &Path) -> String { |
| // Determine which path we make this relative to: usually it's the workspace root, |
| // but this can be overwritten with a `-Z` flag. |
| let root = match &ws.gctx().cli_unstable().root_dir { |
| None => ws.root().to_owned(), |
| Some(root_dir) => normalize_path(&ws.gctx().cwd().join(root_dir)), |
| }; |
| if let Ok(path) = path.strip_prefix(&root) { |
| path |
| } else { |
| path |
| } |
| .display() |
| .to_string() |
| } |
| |
| /// Display path, generally relative to cwd |
| /// |
| /// Prefer [`workspace_rel_path`]. |
| /// This is for when there is no workspace available. |
| pub fn cwd_rel_path(path: &Path, gctx: &GlobalContext) -> String { |
| diff_paths(path, gctx.cwd()) |
| .unwrap_or_else(|| path.to_path_buf()) |
| .display() |
| .to_string() |
| } |
| |
| pub fn get_key_value<'doc, 'i>( |
| document: &'doc toml::Spanned<toml::de::DeTable<'static>>, |
| path: &[impl AsIndex], |
| ) -> Option<( |
| &'doc toml::Spanned<Cow<'doc, str>>, |
| &'doc toml::Spanned<toml::de::DeValue<'static>>, |
| )> { |
| let table = document.get_ref(); |
| let mut iter = path.into_iter(); |
| let index0 = iter.next()?.as_index(); |
| let key0 = index0.as_key()?; |
| let (mut current_key, mut current_item) = table.get_key_value(key0)?; |
| |
| while let Some(index) = iter.next() { |
| match index.as_index() { |
| TomlIndex::Key(key) => { |
| if let Some(table) = current_item.get_ref().as_table() { |
| (current_key, current_item) = table.get_key_value(key)?; |
| } else if let Some(array) = current_item.get_ref().as_array() { |
| current_item = array.iter().find(|item| match item.get_ref() { |
| toml::de::DeValue::String(s) => s == key, |
| _ => false, |
| })?; |
| } else { |
| return None; |
| } |
| } |
| TomlIndex::Offset(offset) => { |
| let array = current_item.get_ref().as_array()?; |
| current_item = array.get(offset)?; |
| } |
| } |
| } |
| Some((current_key, current_item)) |
| } |
| |
| pub fn get_key_value_span<'i>( |
| document: &toml::Spanned<toml::de::DeTable<'static>>, |
| path: &[impl AsIndex], |
| ) -> Option<TomlSpan> { |
| get_key_value(document, path).map(|(k, v)| TomlSpan { |
| key: k.span(), |
| value: v.span(), |
| }) |
| } |
| |
| #[derive(Clone)] |
| pub struct TomlSpan { |
| pub key: Range<usize>, |
| pub value: Range<usize>, |
| } |
| |
| #[derive(Copy, Clone)] |
| pub enum TomlIndex<'i> { |
| Key(&'i str), |
| Offset(usize), |
| } |
| |
| impl<'i> TomlIndex<'i> { |
| fn as_key(&self) -> Option<&'i str> { |
| match self { |
| TomlIndex::Key(key) => Some(key), |
| TomlIndex::Offset(_) => None, |
| } |
| } |
| } |
| |
| pub trait AsIndex { |
| fn as_index<'i>(&'i self) -> TomlIndex<'i>; |
| } |
| |
| impl AsIndex for TomlIndex<'_> { |
| fn as_index<'i>(&'i self) -> TomlIndex<'i> { |
| match self { |
| TomlIndex::Key(key) => TomlIndex::Key(key), |
| TomlIndex::Offset(offset) => TomlIndex::Offset(*offset), |
| } |
| } |
| } |
| |
| impl AsIndex for &str { |
| fn as_index<'i>(&'i self) -> TomlIndex<'i> { |
| TomlIndex::Key(self) |
| } |
| } |
| |
| impl AsIndex for String { |
| fn as_index<'i>(&'i self) -> TomlIndex<'i> { |
| TomlIndex::Key(self.as_str()) |
| } |
| } |
| |
| impl AsIndex for usize { |
| fn as_index<'i>(&'i self) -> TomlIndex<'i> { |
| TomlIndex::Offset(*self) |
| } |
| } |