Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

feat(solc): relative remappings #786

Merged
merged 1 commit into from
Jan 13, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions ethers-solc/src/remappings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
struct Candidate {
/// dir that opened the window
Expand Down Expand Up @@ -358,6 +476,27 @@ mod tests {
use super::*;
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]
fn serde() {
let remapping = "oz=../b/c/d";
Expand Down