refactor: determine_ethers_crates
This commit is contained in:
parent
10310ce3ad
commit
51c2818263
|
@ -1,123 +1,108 @@
|
||||||
|
//! N.B.:
|
||||||
|
//! - crate names must not be [global paths](https://doc.rust-lang.org/reference/paths.html#path-qualifiers)
|
||||||
|
//! since we must be able to override them internally, like in Multicall.
|
||||||
|
//!
|
||||||
|
//! - [`ETHERS_CRATE_NAMES`] cannot hold [`syn::Path`] because it is not [`Sync`], so the names must
|
||||||
|
//! be parsed at every call.
|
||||||
|
|
||||||
use cargo_metadata::MetadataCommand;
|
use cargo_metadata::MetadataCommand;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use syn::Path;
|
/// Crate names to use in Path resolution.
|
||||||
|
|
||||||
/// See `determine_ethers_crates`
|
|
||||||
///
|
///
|
||||||
/// This ensures that the `MetadataCommand` is only run once
|
/// `(core, contract, providers)`
|
||||||
static ETHERS_CRATES: Lazy<(&'static str, &'static str, &'static str)> =
|
type CrateNames = (&'static str, &'static str, &'static str);
|
||||||
Lazy::new(determine_ethers_crates);
|
|
||||||
|
|
||||||
/// Convenience function to turn the `ethers_core` name in `ETHERS_CRATE` into a `Path`
|
const DEFAULT_CRATE_NAMES: CrateNames = ("ethers::core", "ethers::contract", "ethers::providers");
|
||||||
pub fn ethers_core_crate() -> Path {
|
const SUB_CRATE_NAMES: CrateNames = ("ethers_core", "ethers_contract", "ethers_providers");
|
||||||
syn::parse_str(ETHERS_CRATES.0).expect("valid path; qed")
|
|
||||||
}
|
/// See [`determine_ethers_crates`].
|
||||||
/// Convenience function to turn the `ethers_contract` name in `ETHERS_CRATE` into an `Path`
|
///
|
||||||
pub fn ethers_contract_crate() -> Path {
|
/// This ensures that the `MetadataCommand` is ran only once.
|
||||||
syn::parse_str(ETHERS_CRATES.1).expect("valid path; qed")
|
static ETHERS_CRATE_NAMES: Lazy<CrateNames> = Lazy::new(determine_ethers_crates);
|
||||||
}
|
|
||||||
pub fn ethers_providers_crate() -> Path {
|
/// Returns the `core` crate's [`Path`][syn::Path].
|
||||||
syn::parse_str(ETHERS_CRATES.2).expect("valid path; qed")
|
pub fn ethers_core_crate() -> syn::Path {
|
||||||
|
syn::parse_str(ETHERS_CRATE_NAMES.0).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The crates name to use when deriving macros: (`core`, `contract`)
|
/// Returns the `contract` crate's [`Path`][syn::Path].
|
||||||
|
pub fn ethers_contract_crate() -> syn::Path {
|
||||||
|
syn::parse_str(ETHERS_CRATE_NAMES.1).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the `providers` crate's [`Path`][syn::Path].
|
||||||
|
pub fn ethers_providers_crate() -> syn::Path {
|
||||||
|
syn::parse_str(ETHERS_CRATE_NAMES.2).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines which crate paths to use by looking at the [metadata][cargo_metadata] of the project.
|
||||||
///
|
///
|
||||||
/// We try to determine which crate ident to use based on the dependencies of
|
/// Returns `ethers_*` if *all* necessary dependencies are present, otherwise `ethers::*`.
|
||||||
/// the project in which the macro is used. This is useful because the macros,
|
fn determine_ethers_crates() -> CrateNames {
|
||||||
/// like `EthEvent` are provided by the `ethers-contract` crate which depends on
|
// always defined in Cargo projects
|
||||||
/// `ethers_core`. Most commonly `ethers` will be used as dependency which
|
let manifest_dir: PathBuf =
|
||||||
/// reexports all the different crates, essentially `ethers::core` is
|
std::env::var_os("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR is not defined").into();
|
||||||
/// `ethers_core` So depending on the dependency used `ethers` ors `ethers_core
|
|
||||||
/// | ethers_contract`, we need to use the fitting crate ident when expand the
|
|
||||||
/// macros This will attempt to parse the current `Cargo.toml` and check the
|
|
||||||
/// ethers related dependencies.
|
|
||||||
///
|
|
||||||
/// This determines
|
|
||||||
/// - `ethers_*` idents if `ethers-core`, `ethers-contract`, `ethers-providers` are present in
|
|
||||||
/// the manifest or the `ethers` is _not_ present
|
|
||||||
/// - `ethers::*` otherwise
|
|
||||||
///
|
|
||||||
/// This process is a bit hacky, we run `cargo metadata` internally which
|
|
||||||
/// resolves the current package but creates a new `Cargo.lock` file in the
|
|
||||||
/// process. This is not a problem for regular workspaces but becomes an issue
|
|
||||||
/// during publishing with `cargo publish` if the project does not ignore
|
|
||||||
/// `Cargo.lock` in `.gitignore`, because then cargo can't proceed with
|
|
||||||
/// publishing the crate because the created `Cargo.lock` leads to a modified
|
|
||||||
/// workspace, not the `CARGO_MANIFEST_DIR` but the workspace `cargo publish`
|
|
||||||
/// created in `./target/package/..`. Therefore we check prior to executing
|
|
||||||
/// `cargo metadata` if a `Cargo.lock` file exists and delete it afterwards if
|
|
||||||
/// it was created by `cargo metadata`.
|
|
||||||
pub fn determine_ethers_crates() -> (&'static str, &'static str, &'static str) {
|
|
||||||
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR");
|
|
||||||
|
|
||||||
// if there is no cargo manifest, default to `ethers::`-style imports.
|
let lock_file = manifest_dir.join("Cargo.lock");
|
||||||
let manifest_dir = if let Ok(manifest_dir) = manifest_dir {
|
let lock_file_existed = lock_file.exists();
|
||||||
manifest_dir
|
|
||||||
} else {
|
|
||||||
return ("ethers::core", "ethers::contract", "ethers::providers")
|
|
||||||
};
|
|
||||||
|
|
||||||
// check if the lock file exists, if it's missing we need to clean up afterward
|
let names = crate_names_from_metadata(manifest_dir).unwrap_or(DEFAULT_CRATE_NAMES);
|
||||||
let lock_file = format!("{manifest_dir}/Cargo.lock");
|
|
||||||
let needs_lock_file_cleanup = !std::path::Path::new(&lock_file).exists();
|
|
||||||
|
|
||||||
let res = MetadataCommand::new()
|
// remove the lock file created from running the command
|
||||||
.manifest_path(&format!("{manifest_dir}/Cargo.toml"))
|
if !lock_file_existed && lock_file.exists() {
|
||||||
.exec()
|
|
||||||
.ok()
|
|
||||||
.and_then(|metadata| {
|
|
||||||
metadata.root_package().and_then(|pkg| {
|
|
||||||
let sub_crates = Some(("ethers_core", "ethers_contract", "ethers_providers"));
|
|
||||||
|
|
||||||
// Note(mattsse): this is super hacky but required in order to compile and test
|
|
||||||
// ethers' internal crates
|
|
||||||
if [
|
|
||||||
"ethers-contract",
|
|
||||||
"ethers-derive-eip712",
|
|
||||||
"ethers-signers",
|
|
||||||
"ethers-middleware",
|
|
||||||
"ethers-solc",
|
|
||||||
]
|
|
||||||
.contains(&pkg.name.as_str())
|
|
||||||
{
|
|
||||||
return sub_crates
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut has_ethers_core = false;
|
|
||||||
let mut has_ethers_contract = false;
|
|
||||||
let mut has_ethers_providers = false;
|
|
||||||
|
|
||||||
for dep in pkg.dependencies.iter() {
|
|
||||||
match dep.name.as_str() {
|
|
||||||
"ethers-core" => {
|
|
||||||
has_ethers_core = true;
|
|
||||||
}
|
|
||||||
"ethers-contract" => {
|
|
||||||
has_ethers_contract = true;
|
|
||||||
}
|
|
||||||
"ethers-providers" => {
|
|
||||||
has_ethers_providers = true;
|
|
||||||
}
|
|
||||||
"ethers" => return None,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if has_ethers_core && has_ethers_contract && has_ethers_providers {
|
|
||||||
return sub_crates
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.unwrap_or(("ethers::core", "ethers::contract", "ethers::providers"));
|
|
||||||
|
|
||||||
if needs_lock_file_cleanup {
|
|
||||||
// delete the `Cargo.lock` file that was created by `cargo metadata`
|
|
||||||
// if the package is not part of a workspace
|
|
||||||
let _ = std::fs::remove_file(lock_file);
|
let _ = std::fs::remove_file(lock_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
res
|
names
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs [`cargo metadata`][MetadataCommand] from `manifest_dir` and determines the crate names to
|
||||||
|
/// use.
|
||||||
|
///
|
||||||
|
/// Returns `None` on any error or if no dependencies are found.
|
||||||
|
#[inline]
|
||||||
|
fn crate_names_from_metadata(manifest_dir: PathBuf) -> Option<CrateNames> {
|
||||||
|
let metadata = MetadataCommand::new().current_dir(manifest_dir).exec().ok()?;
|
||||||
|
let pkg = metadata.root_package()?;
|
||||||
|
|
||||||
|
// HACK(mattsse): this is required in order to compile and test ethers' internal crates
|
||||||
|
const INTERNAL_CRATES: [&str; 5] = [
|
||||||
|
"ethers-contract",
|
||||||
|
"ethers-derive-eip712",
|
||||||
|
"ethers-signers",
|
||||||
|
"ethers-middleware",
|
||||||
|
"ethers-solc",
|
||||||
|
];
|
||||||
|
let pkg_name = pkg.name.as_str();
|
||||||
|
if INTERNAL_CRATES.contains(&pkg_name) {
|
||||||
|
return Some(SUB_CRATE_NAMES)
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut has_ethers_core = false;
|
||||||
|
let mut has_ethers_contract = false;
|
||||||
|
let mut has_ethers_providers = false;
|
||||||
|
|
||||||
|
for dep in pkg.dependencies.iter() {
|
||||||
|
match dep.name.as_str() {
|
||||||
|
"ethers-core" => {
|
||||||
|
has_ethers_core = true;
|
||||||
|
}
|
||||||
|
"ethers-contract" => {
|
||||||
|
has_ethers_contract = true;
|
||||||
|
}
|
||||||
|
"ethers-providers" => {
|
||||||
|
has_ethers_providers = true;
|
||||||
|
}
|
||||||
|
"ethers" => return None,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if has_ethers_core && has_ethers_contract && has_ethers_providers {
|
||||||
|
Some(SUB_CRATE_NAMES)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue