mv_command_line_common/
files.rs1use anyhow::{anyhow, bail, *};
5use serde::{Deserialize, Serialize};
6use sha2::Digest;
7use std::{collections::BTreeMap, convert::TryInto, path::Path};
8
9#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
11pub struct FileHash(pub [u8; 32]);
12
13impl FileHash {
14    pub fn new(file_contents: &str) -> Self {
15        Self(
16            sha2::Sha256::digest(file_contents.as_bytes())
17                .try_into()
18                .expect("Length of sha256 digest must always be 32 bytes"),
19        )
20    }
21
22    pub const fn empty() -> Self {
23        Self([0; 32])
24    }
25}
26
27impl std::fmt::Display for FileHash {
28    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29        hex::encode(self.0).fmt(f)
30    }
31}
32
33impl std::fmt::Debug for FileHash {
34    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35        hex::encode(self.0).fmt(f)
36    }
37}
38
39pub const MOVE_EXTENSION: &str = "move";
41pub const MOVE_IR_EXTENSION: &str = "mvir";
43pub const MOVE_COMPILED_EXTENSION: &str = "mv";
45pub const SOURCE_MAP_EXTENSION: &str = "mvsm";
47pub const MOVE_ERROR_DESC_EXTENSION: &str = "errmap";
49pub const MOVE_COVERAGE_MAP_EXTENSION: &str = "mvcov";
51
52pub fn find_filenames<Predicate: FnMut(&Path) -> bool>(
56    paths: &[impl AsRef<Path>],
57    mut is_file_desired: Predicate,
58) -> anyhow::Result<Vec<String>> {
59    let mut result = vec![];
60
61    for s in paths {
62        let path = s.as_ref();
63        if !path.exists() {
64            bail!("No such file or directory '{}'", path.to_string_lossy())
65        }
66        if path.is_file() && is_file_desired(path) {
67            result.push(path_to_string(path)?);
68            continue;
69        }
70        if !path.is_dir() {
71            continue;
72        }
73        for entry in walkdir::WalkDir::new(path)
74            .follow_links(true)
75            .into_iter()
76            .filter_map(|e| e.ok())
77        {
78            let entry_path = entry.path();
79            if !entry.file_type().is_file() || !is_file_desired(entry_path) {
80                continue;
81            }
82
83            result.push(path_to_string(entry_path)?);
84        }
85    }
86    Ok(result)
87}
88
89pub fn find_move_filenames(
94    paths: &[impl AsRef<Path>],
95    keep_specified_files: bool,
96) -> anyhow::Result<Vec<String>> {
97    if keep_specified_files {
98        let (file_paths, other_paths): (Vec<&Path>, Vec<&Path>) =
99            paths.iter().map(|p| p.as_ref()).partition(|s| s.is_file());
100        let mut files = file_paths
101            .into_iter()
102            .map(path_to_string)
103            .collect::<anyhow::Result<Vec<String>>>()?;
104        files.extend(find_filenames(&other_paths, |path| {
105            extension_equals(path, MOVE_EXTENSION)
106        })?);
107        Ok(files)
108    } else {
109        find_filenames(paths, |path| extension_equals(path, MOVE_EXTENSION))
110    }
111}
112
113pub fn path_to_string(path: &Path) -> anyhow::Result<String> {
114    match path.to_str() {
115        Some(p) => Ok(p.to_string()),
116        None => Err(anyhow!("non-Unicode file name")),
117    }
118}
119
120pub fn extension_equals(path: &Path, target_ext: &str) -> bool {
121    match path.extension().and_then(|s| s.to_str()) {
122        Some(extension) => extension == target_ext,
123        None => false,
124    }
125}
126
127pub fn verify_and_create_named_address_mapping<T: Copy + std::fmt::Display + Eq>(
128    named_addresses: Vec<(String, T)>,
129) -> anyhow::Result<BTreeMap<String, T>> {
130    let mut mapping = BTreeMap::new();
131    let mut invalid_mappings = BTreeMap::new();
132    for (name, addr_bytes) in named_addresses {
133        match mapping.insert(name.clone(), addr_bytes) {
134            Some(other_addr) if other_addr != addr_bytes => {
135                invalid_mappings
136                    .entry(name)
137                    .or_insert_with(Vec::new)
138                    .push(other_addr);
139            }
140            None | Some(_) => (),
141        }
142    }
143
144    if !invalid_mappings.is_empty() {
145        let redefinitions = invalid_mappings
146            .into_iter()
147            .map(|(name, addr_bytes)| {
148                format!(
149                    "{} is assigned differing values {} and {}",
150                    name,
151                    addr_bytes
152                        .iter()
153                        .map(|x| format!("{}", x))
154                        .collect::<Vec<_>>()
155                        .join(","),
156                    mapping[&name]
157                )
158            })
159            .collect::<Vec<_>>();
160
161        anyhow::bail!(
162            "Redefinition of named addresses found in arguments to compiler: {}",
163            redefinitions.join(", ")
164        )
165    }
166
167    Ok(mapping)
168}