nextest_runner/config/
helpers.rsuse camino::{Utf8Component, Utf8PathBuf};
use serde::{
de::{Error, Unexpected},
Deserialize,
};
pub(super) fn deserialize_relative_path<'de, D>(deserializer: D) -> Result<Utf8PathBuf, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = Utf8PathBuf::deserialize(deserializer)?;
for component in s.components() {
match component {
Utf8Component::Normal(_) | Utf8Component::CurDir => {}
Utf8Component::RootDir | Utf8Component::Prefix(_) | Utf8Component::ParentDir => {
return Err(D::Error::invalid_value(
Unexpected::Str(s.as_str()),
&"a relative path with no parent components",
));
}
}
}
Ok(s)
}
#[cfg(test)]
mod tests {
use super::*;
use color_eyre::eyre::{bail, Context, Result};
use serde::de::IntoDeserializer;
#[test]
fn test_deserialize_relative_path() -> Result<()> {
let valid = &["foo", "foo/bar", "foo/./bar", "./foo/bar", "."];
let invalid = &[
"/foo/bar",
"foo/../bar",
"../foo/bar",
#[cfg(windows)]
"C:\\foo\\bar",
#[cfg(windows)]
"C:foo",
#[cfg(windows)]
"\\\\?\\C:\\foo\\bar",
];
for &input in valid {
let path = de_relative_path(input.into_deserializer())
.wrap_err_with(|| format!("error deserializing valid path {input:?}: error"))?;
assert_eq!(path, Utf8PathBuf::from(input), "path matches: {path:?}");
}
for &input in invalid {
let error = match de_relative_path(input.into_deserializer()) {
Ok(path) => bail!("successfully deserialized an invalid path: {:?}", path),
Err(error) => error,
};
assert_eq!(
error.to_string(),
format!(
"invalid value: string {input:?}, expected a relative path with no parent components"
)
);
}
Ok(())
}
fn de_relative_path<'de, D>(deserializer: D) -> Result<Utf8PathBuf, D::Error>
where
D: serde::Deserializer<'de, Error = serde::de::value::Error>,
{
deserialize_relative_path(deserializer)
}
}