multisol_structs/
contract.rs

1use std::env;
2use std::ffi::OsString;
3use std::path::PathBuf;
4
5use anyhow::{Context, Result};
6
7#[derive(Debug)]
8pub struct Contract {
9    /// Absolute path to the directory in which the contract is located on disk
10    directory: PathBuf,
11    /// Whether the contract is imported from "node_modules" or not
12    external: bool,
13    /// Name of the contract file
14    file_name: OsString,
15    /// Absolute path to the contract on disk
16    full_path: PathBuf,
17    /// Source code of the Solidity contract
18    source_code: String,
19}
20
21impl Contract {
22    pub fn directory(&self) -> &PathBuf {
23        &self.directory
24    }
25
26    pub fn external(&self) -> bool {
27        self.external
28    }
29
30    pub fn file_name(&self) -> &OsString {
31        &self.file_name
32    }
33
34    pub fn full_path(&self) -> &PathBuf {
35        &self.full_path
36    }
37
38    pub fn source_code(&self) -> &str {
39        &self.source_code
40    }
41}
42
43impl Contract {
44    pub fn set_source_code(&mut self, source_code: String) {
45        self.source_code = source_code;
46    }
47}
48
49impl Contract {
50    pub fn new(
51        directory: PathBuf,
52        external: bool,
53        file_name: OsString,
54        full_path: PathBuf,
55        source_code: String,
56    ) -> Contract {
57        Contract {
58            directory,
59            external,
60            file_name,
61            full_path,
62            source_code,
63        }
64    }
65
66    /// Generates the struct instance starting from the user provided path to the contract.
67    pub fn from_cli(contract_path: &PathBuf) -> Result<Contract> {
68        let full_path = contract_path
69            .canonicalize()
70            .with_context(|| "Couldn't get an absolute path to the contract path")?;
71
72        // Get the curent working directory by "popping" the file name out of the path.
73        let mut directory = PathBuf::from(&full_path);
74        directory.pop();
75
76        // Gets the file name out of the path provided by the user. It is safe to unwrap because the
77        // contract path is sanity checked on program entry.
78        let file_name = contract_path.file_name().unwrap().to_os_string();
79
80        let contract = Contract {
81            directory,
82            file_name,
83            external: false,
84            full_path,
85            source_code: String::from(""),
86        };
87        Ok(contract)
88    }
89
90    /// Generates the struct instance starting from import path captured in another contract.
91    pub fn from_import_path(ancestor_contract: &Contract, import_path: &PathBuf) -> Result<Contract> {
92        let external: bool;
93        let full_path: PathBuf;
94
95        // Safe to unwrap because the regex can't match a path ending in "..".
96        let file_name = import_path.file_name().unwrap().to_os_string();
97
98        // If the path starts with "." or "..", the import is local. Otherwise, the paths are constructed as if
99        // the contract was defined in "node_modules" in the current working directory.
100        if import_path.starts_with(".") || import_path.starts_with("..") {
101            // The contract could be external even if the import isn't relative. That's because one of the ancestors
102            // may be external.
103            external = ancestor_contract.external;
104
105            // Join the ancestor contract's directory with the current import path to the full path.
106            full_path = ancestor_contract
107                .directory()
108                .join(import_path)
109                .canonicalize()
110                .with_context(|| format!("Couldn't get an absolute path to the contract: {:?}", file_name))?;
111        } else {
112            // The contract must be external if the import isn't relative. Lest the Solidity program wouldn't compile.
113            external = true;
114
115            // Construct the path assuming that "node_modules" is in the current working directory.
116            let cwd = env::current_dir().unwrap();
117            let cwd_str = cwd.as_os_str().to_str().unwrap();
118            let import_path_str = import_path.to_str().unwrap();
119            full_path = [cwd_str, "node_modules", import_path_str].iter().collect();
120        }
121
122        // Get the curent working directory by popping the file name out of the path.
123        let mut directory: PathBuf;
124        directory = PathBuf::from(&full_path);
125        directory.pop();
126
127        let contract = Contract {
128            directory,
129            external,
130            file_name,
131            full_path,
132            source_code: String::from(""),
133        };
134        Ok(contract)
135    }
136}