multisol_writer/
lib.rs

1//! This is a crate that writes the collected contracts to a directory named "multisol-contract_name".
2
3use std::fs;
4use std::path::PathBuf;
5
6use anyhow::{Context, Result};
7use multisol_structs::Contract;
8
9/// Entry point to the crate.
10pub fn run(contracts: Vec<Contract>) -> Result<()> {
11    // The last contract is the one provided by the user since that is the first contract that
12    // gets inserted in the vector.
13    let user_provided_contract = contracts.last().with_context(|| "No contracts found")?;
14
15    // Delete the output directory if it exists already.
16    let output_dir = get_output_dir(&user_provided_contract)?;
17    if output_dir.is_dir() {
18        fs::remove_dir_all(&output_dir)?;
19    }
20    fs::create_dir(&output_dir)?;
21
22    for contract in contracts.iter().rev() {
23        let output_file_path = output_dir.join(contract.file_name());
24        fs::write(output_file_path, contract.source_code())?;
25    }
26
27    println!("✨ contracts written to {:?} folder", output_dir);
28
29    Ok(())
30}
31
32/// Appends the contract name to "multisol-" to generate the name of the output directory.
33fn get_output_dir(user_provided_contract: &Contract) -> Result<PathBuf> {
34    let contract_file_stem = user_provided_contract.full_path().file_stem().with_context(|| {
35        format!(
36            "Could not get file stem out of contract path: {:?}",
37            user_provided_contract.full_path()
38        )
39    })?;
40
41    let contract_name = contract_file_stem.to_str().with_context(|| {
42        format!(
43            "Contract name contains invalid UTF-8: {:?}",
44            user_provided_contract.file_name()
45        )
46    })?;
47    let contract_name_lowercase = contract_name.to_lowercase();
48    let output_dir_str = ["multisol", &contract_name_lowercase].join("-");
49
50    Ok(PathBuf::from(&output_dir_str))
51}
52
53#[cfg(test)]
54mod tests {
55    use super::{get_output_dir, Contract};
56    use std::ffi::OsString;
57    use std::path::PathBuf;
58
59    #[test]
60    fn gets_correct_output_dir() {
61        let directory = PathBuf::from("/tmp/contracts");
62        let full_path = PathBuf::from("/tmp/contracts/Foo.sol");
63        let contract = Contract::new(directory, false, OsString::from("Foo.sol"), full_path, String::from(""));
64        let output_dir = get_output_dir(&contract).unwrap();
65        assert_eq!(output_dir, PathBuf::from("multisol-foo"));
66    }
67
68    /// Technically this edge case should never occur, because the contract path is sanity checked on program entry.
69    /// But let's be comprehensive.
70    #[test]
71    fn cant_get_file_stem_out_of_contract_path() {
72        let directory = PathBuf::from("/tmp/contracts");
73        let full_path = PathBuf::from("/tmp/contracts/Foo.sol/..");
74        let contract = Contract::new(directory, false, OsString::from("Foo.sol"), full_path, String::from(""));
75        let err = get_output_dir(&contract).unwrap_err();
76        assert_eq!(
77            err.to_string(),
78            format!(
79                "Could not get file stem out of contract path: {:?}",
80                contract.full_path()
81            )
82        );
83    }
84}