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}