feat(solc): relative remappings (#786)

This commit is contained in:
Matthias Seitz 2022-01-13 01:59:53 +01:00 committed by GitHub
parent d2b59d7097
commit b619a5522f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 139 additions and 0 deletions

View File

@ -190,6 +190,124 @@ impl Remapping {
} }
} }
/// A relative [`Remapping`] that's aware of the current location
///
/// See [`RelativeRemappingPathBuf`]
#[derive(Clone, Debug, PartialEq)]
pub struct RelativeRemapping {
pub name: String,
pub path: RelativeRemappingPathBuf,
}
impl RelativeRemapping {
/// Creates a new `RelativeRemapping` starting prefixed with `root`
pub fn new(remapping: Remapping, root: impl AsRef<Path>) -> Self {
Self {
name: remapping.name,
path: RelativeRemappingPathBuf::with_root(root, remapping.path),
}
}
/// Converts this relative remapping into an absolute remapping
///
/// This sets to root of the remapping to the given `root` path
pub fn to_remapping(mut self, root: PathBuf) -> Remapping {
self.path.parent = Some(root);
self.into()
}
}
// Remappings are printed as `prefix=target`
impl fmt::Display for RelativeRemapping {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}={}", self.name, self.path.original().display())
}
}
impl From<RelativeRemapping> for Remapping {
fn from(r: RelativeRemapping) -> Self {
Remapping { name: r.name, path: r.path.relative().to_string_lossy().to_string() }
}
}
impl From<Remapping> for RelativeRemapping {
fn from(r: Remapping) -> Self {
Self { name: r.name, path: r.path.into() }
}
}
/// The path part of the [`Remapping`] that knows the path of the file it was configured in, if any.
///
/// A [`Remapping`] is intended to be absolute, but paths in configuration files are often desired
/// to be relative to the configuration file itself. For example, a path of
/// `weird-erc20/=lib/weird-erc20/src/` configured in a file `/var/foundry.toml` might be desired to
/// resolve as a `weird-erc20/=/var/lib/weird-erc20/src/` remapping.
#[derive(Debug, Clone, PartialEq)]
pub struct RelativeRemappingPathBuf {
parent: Option<PathBuf>,
path: PathBuf,
}
impl RelativeRemappingPathBuf {
/// Creates a new `RelativeRemappingPathBuf` that checks if the `path` is a child path of
/// `parent`.
pub fn with_root(parent: impl AsRef<Path>, path: impl AsRef<Path>) -> Self {
let parent = parent.as_ref();
let path = path.as_ref();
if let Ok(path) = path.strip_prefix(parent) {
Self { parent: Some(parent.to_path_buf()), path: path.to_path_buf() }
} else if path.has_root() {
Self { parent: None, path: path.to_path_buf() }
} else {
Self { parent: Some(parent.to_path_buf()), path: path.to_path_buf() }
}
}
/// Returns the path as it was declared, without modification.
pub fn original(&self) -> &Path {
&self.path
}
/// Returns this path relative to the file it was delcared in, if any.
/// Returns the original if this path was not declared in a file or if the
/// path has a root.
pub fn relative(&self) -> PathBuf {
if self.original().has_root() {
return self.original().into()
}
self.parent
.as_ref()
.map(|p| p.join(self.original()))
.unwrap_or_else(|| self.original().into())
}
}
impl<P: AsRef<Path>> From<P> for RelativeRemappingPathBuf {
fn from(path: P) -> RelativeRemappingPathBuf {
Self { parent: None, path: path.as_ref().to_path_buf() }
}
}
impl Serialize for RelativeRemapping {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for RelativeRemapping {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let remapping = String::deserialize(deserializer)?;
let remapping = Remapping::from_str(&remapping).map_err(serde::de::Error::custom)?;
Ok(RelativeRemapping { name: remapping.name, path: remapping.path.into() })
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct Candidate { struct Candidate {
/// dir that opened the window /// dir that opened the window
@ -358,6 +476,27 @@ mod tests {
use super::*; use super::*;
use crate::utils::tempdir; use crate::utils::tempdir;
#[test]
fn relative_remapping() {
let remapping = "oz=a/b/c/d";
let remapping = Remapping::from_str(remapping).unwrap();
let relative = RelativeRemapping::new(remapping.clone(), "a/b/c");
assert_eq!(relative.path.relative(), Path::new(&remapping.path));
assert_eq!(relative.path.original(), Path::new("d"));
let relative = RelativeRemapping::new(remapping.clone(), "x/y");
assert_eq!(relative.path.relative(), Path::new("x/y/a/b/c/d"));
assert_eq!(relative.path.original(), Path::new(&remapping.path));
let remapping = "oz=/a/b/c/d";
let remapping = Remapping::from_str(remapping).unwrap();
let relative = RelativeRemapping::new(remapping.clone(), "a/b");
assert_eq!(relative.path.relative(), Path::new(&remapping.path));
assert_eq!(relative.path.original(), Path::new(&remapping.path));
assert!(relative.path.parent.is_none());
}
#[test] #[test]
fn serde() { fn serde() {
let remapping = "oz=../b/c/d"; let remapping = "oz=../b/c/d";