multiversx_sc_meta/folder_structure/
relevant_directory.rs1use crate::version::FrameworkVersion;
2use multiversx_sc_meta_lib::cargo_toml::{CargoTomlContents, DependencyReference};
3use std::{
4 fs::{self, DirEntry},
5 path::{Path, PathBuf},
6};
7
8pub const FRAMEWORK_CRATE_NAMES: &[&str] = &[
10 "multiversx-sc",
11 "multiversx-sc-meta",
12 "multiversx-sc-meta-lib",
13 "multiversx-sc-scenario",
14 "multiversx-sc-snippets",
15 "multiversx-sc-wasm-adapter",
16 "multiversx-sc-modules",
17 "elrond-wasm",
18 "elrond-wasm-debug",
19 "elrond-wasm-modules",
20 "elrond-wasm-node",
21 "elrond-interact-snippets",
22];
23
24pub const CARGO_TOML_FILE_NAME: &str = "Cargo.toml";
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum DirectoryType {
28 Contract,
29 Lib,
30}
31
32#[derive(Debug, Clone)]
33pub struct RelevantDirectory {
34 pub path: PathBuf,
35 pub version: DependencyReference,
36 pub upgrade_in_progress: Option<(FrameworkVersion, FrameworkVersion)>,
37 pub dir_type: DirectoryType,
38}
39
40impl RelevantDirectory {
41 pub fn dir_name(&self) -> String {
42 self.path.file_name().unwrap().to_str().unwrap().to_string()
43 }
44
45 pub fn dir_name_underscores(&self) -> String {
46 self.dir_name().replace('-', "_")
47 }
48
49 pub fn meta_path(&self) -> PathBuf {
51 self.path.join("meta")
52 }
53
54 pub fn assert_meta_path_exists(&self) {
56 let meta_path = self.meta_path();
57 assert!(
58 meta_path.exists(),
59 "Contract meta crate not found at {}",
60 meta_path.as_path().display()
61 );
62 }
63
64 pub fn output_path(&self) -> PathBuf {
66 self.path.join("output")
67 }
68}
69
70pub struct RelevantDirectories(pub(crate) Vec<RelevantDirectory>);
71
72impl RelevantDirectories {
73 pub fn find_all(path_ref: &Path, ignore: &[String]) -> Self {
74 let canonicalized = fs::canonicalize(path_ref).unwrap_or_else(|err| {
75 panic!(
76 "error canonicalizing input path {}: {}",
77 path_ref.display(),
78 err,
79 )
80 });
81 let mut dirs = Vec::new();
82 populate_directories(canonicalized.as_path(), ignore, &mut dirs);
83 RelevantDirectories(dirs)
84 }
85
86 pub fn len(&self) -> usize {
87 self.0.len()
88 }
89
90 pub fn is_empty(&self) -> bool {
91 self.0.is_empty()
92 }
93
94 #[allow(dead_code)]
95 pub fn iter(&self) -> impl Iterator<Item = &RelevantDirectory> {
96 self.0.iter()
97 }
98
99 pub fn iter_contract_crates(&self) -> impl Iterator<Item = &RelevantDirectory> {
100 self.0
101 .iter()
102 .filter(|dir| dir.dir_type == DirectoryType::Contract)
103 }
104
105 pub fn count_for_version(&self, version: &FrameworkVersion) -> usize {
106 self.0
107 .iter()
108 .filter(|dir| dir.version.eq_framework_version(version))
109 .count()
110 }
111
112 pub fn iter_version(
113 &mut self,
114 version: &'static FrameworkVersion,
115 ) -> impl Iterator<Item = &RelevantDirectory> {
116 self.0
117 .iter()
118 .filter(move |dir| dir.version.eq_framework_version(version))
119 }
120
121 pub fn start_upgrade(&mut self, from_version: FrameworkVersion, to_version: FrameworkVersion) {
123 for dir in self.0.iter_mut() {
124 if dir.version.eq_framework_version(&from_version) {
125 dir.upgrade_in_progress = Some((from_version.clone(), to_version.clone()));
126 }
127 }
128 }
129
130 pub fn finish_upgrade(&mut self) {
133 for dir in self.0.iter_mut() {
134 if let Some((_, to_version)) = &dir.upgrade_in_progress {
135 if let DependencyReference::Version(version_req) = &mut dir.version {
136 version_req.semver = to_version.clone();
137 }
138 dir.upgrade_in_progress = None;
139 }
140 }
141 }
142}
143
144fn populate_directories(path: &Path, ignore: &[String], result: &mut Vec<RelevantDirectory>) {
145 let is_contract = is_marked_contract_crate_dir(path);
146
147 if !is_contract && path.is_dir() {
148 let read_dir = fs::read_dir(path).expect("error reading directory");
149 for child_result in read_dir {
150 let child = child_result.unwrap();
151 if can_continue_recursion(&child, ignore) {
152 populate_directories(child.path().as_path(), ignore, result);
153 }
154 }
155 }
156
157 if let Some(version) = find_framework_dependency(path) {
158 let dir_type = if is_contract {
159 DirectoryType::Contract
160 } else {
161 DirectoryType::Lib
162 };
163 result.push(RelevantDirectory {
164 path: path.to_owned(),
165 version,
166 upgrade_in_progress: None,
167 dir_type,
168 });
169 }
170}
171
172fn is_marked_contract_crate_dir(path: &Path) -> bool {
173 path.join("multiversx.json").is_file() || path.join("elrond.json").is_file()
174}
175
176fn can_continue_recursion(dir_entry: &DirEntry, blacklist: &[String]) -> bool {
177 if !dir_entry.file_type().unwrap().is_dir() {
178 return false;
179 }
180
181 if let Some(dir_name_str) = dir_entry.file_name().to_str() {
182 if blacklist.iter().any(|ignored| ignored == dir_name_str) {
183 return false;
184 }
185
186 !dir_name_str.starts_with('.')
188 } else {
189 false
190 }
191}
192
193fn load_cargo_toml_contents(dir_path: &Path) -> Option<CargoTomlContents> {
194 let cargo_toml_path = dir_path.join(CARGO_TOML_FILE_NAME);
195 if cargo_toml_path.is_file() {
196 Some(CargoTomlContents::load_from_file(cargo_toml_path))
197 } else {
198 None
199 }
200}
201
202impl RelevantDirectory {
203 #[allow(unused)]
204 pub fn cargo_toml_contents(&self) -> Option<CargoTomlContents> {
205 load_cargo_toml_contents(self.path.as_path())
206 }
207}
208
209fn find_framework_dependency(dir_path: &Path) -> Option<DependencyReference> {
210 if let Some(cargo_toml_contents) = load_cargo_toml_contents(dir_path) {
211 for &crate_name in FRAMEWORK_CRATE_NAMES {
212 if let Some(dep_raw) = cargo_toml_contents.dependency_raw_value(crate_name) {
213 return Some(dep_raw.interpret());
214 }
215 }
216 }
217
218 None
219}