forge_doc/preprocessor/
deployments.rs

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