forge_doc/preprocessor/deployments.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
use super::{Preprocessor, PreprocessorId};
use crate::{Document, PreprocessorOutput};
use alloy_primitives::Address;
use std::{
fs,
path::{Path, PathBuf},
};
/// [Deployments] preprocessor id.
pub const DEPLOYMENTS_ID: PreprocessorId = PreprocessorId("deployments");
/// The deployments preprocessor.
///
/// This preprocessor writes to [Document]'s context.
#[derive(Debug)]
pub struct Deployments {
/// The project root.
pub root: PathBuf,
/// The deployments directory.
pub deployments: Option<PathBuf>,
}
/// A contract deployment.
#[derive(Clone, Debug, serde::Deserialize)]
pub struct Deployment {
/// The contract address
pub address: Address,
/// The network name
pub network: Option<String>,
}
impl Preprocessor for Deployments {
fn id(&self) -> PreprocessorId {
DEPLOYMENTS_ID
}
fn preprocess(&self, documents: Vec<Document>) -> Result<Vec<Document>, eyre::Error> {
let deployments_dir =
self.root.join(self.deployments.as_deref().unwrap_or(Path::new("deployments")));
// Gather all networks from the deployments directory.
let networks = fs::read_dir(&deployments_dir)?
.map(|entry| {
let entry = entry?;
let path = entry.path();
if entry.file_type()?.is_dir() {
entry
.file_name()
.into_string()
.map_err(|e| eyre::eyre!("failed to extract directory name: {e:?}"))
} else {
eyre::bail!("not a directory: {}", path.display())
}
})
.collect::<Result<Vec<_>, _>>()?;
// Iterate over all documents to find any deployments.
for document in documents.iter() {
let mut deployments = Vec::default();
// Iterate over all networks and check if there is a deployment for the given contract.
for network in &networks {
// Clone the item path of the document and change it from ".sol" -> ".json"
let mut item_path_clone = document.item_path.clone();
item_path_clone.set_extension("json");
// Determine the path of the deployment artifact relative to the root directory.
let deployment_path =
deployments_dir.join(network).join(item_path_clone.file_name().ok_or_else(
|| eyre::eyre!("Failed to extract file name from item path"),
)?);
// If the deployment file for the given contract is found, add the deployment
// address to the document context.
let mut deployment: Deployment =
serde_json::from_str(&fs::read_to_string(deployment_path)?)?;
deployment.network = Some(network.clone());
deployments.push(deployment);
}
// If there are any deployments for the given contract, add them to the document
// context.
if !deployments.is_empty() {
document.add_context(self.id(), PreprocessorOutput::Deployments(deployments));
}
}
Ok(documents)
}
}