blob: c2d97da2c950ab6337685ebd0a9861e524d49e5c [file] [edit]
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)
}
}