canic_host/release_set/
paths.rs1use crate::workspace_discovery::{
2 discover_canister_manifest_from_metadata, discover_icp_root_from, discover_workspace_root_from,
3 normalize_workspace_path,
4};
5use std::{
6 fs,
7 path::{Path, PathBuf},
8};
9use toml::Value as TomlValue;
10
11use super::{
12 CANISTERS_ROOT_RELATIVE, ROOT_CONFIG_FILE, ROOT_RELEASE_SET_MANIFEST_FILE,
13 WORKSPACE_MANIFEST_RELATIVE,
14};
15
16pub fn workspace_root() -> Result<PathBuf, Box<dyn std::error::Error>> {
19 if let Ok(path) = std::env::var("CANIC_WORKSPACE_ROOT") {
20 return Ok(PathBuf::from(path).canonicalize()?);
21 }
22
23 if let Some(root) = std::env::var_os("CANIC_WORKSPACE_MANIFEST_PATH")
24 .map(PathBuf::from)
25 .and_then(|path| discover_workspace_root_from(&path))
26 {
27 return Ok(root);
28 }
29
30 if let Some(root) = std::env::var_os("CANIC_CONFIG_PATH")
31 .map(PathBuf::from)
32 .and_then(|path| discover_workspace_root_from(&path))
33 {
34 return Ok(root);
35 }
36
37 if let Some(root) = discover_workspace_root_from(&std::env::current_dir()?) {
38 return Ok(root);
39 }
40
41 Ok(std::env::current_dir()?.canonicalize()?)
42}
43
44pub fn icp_root() -> Result<PathBuf, Box<dyn std::error::Error>> {
47 if let Ok(path) = std::env::var("CANIC_ICP_ROOT") {
48 return Ok(PathBuf::from(path).canonicalize()?);
49 }
50
51 let current_dir = std::env::current_dir()?.canonicalize()?;
52 if let Some(root) = discover_icp_root_from(¤t_dir) {
53 return Ok(root);
54 }
55
56 if let Ok(path) = std::env::var("CANIC_WORKSPACE_ROOT") {
57 let workspace_root = PathBuf::from(path).canonicalize()?;
58 if let Some(root) = discover_icp_root_from(&workspace_root) {
59 return Ok(root);
60 }
61 return Ok(workspace_root);
62 }
63
64 Ok(current_dir)
65}
66
67#[must_use]
69pub fn config_path(workspace_root: &Path) -> PathBuf {
70 std::env::var_os("CANIC_CONFIG_PATH").map_or_else(
71 || canisters_root(workspace_root).join(ROOT_CONFIG_FILE),
72 |path| normalize_workspace_path(workspace_root, PathBuf::from(path)),
73 )
74}
75
76#[must_use]
78pub fn canisters_root(workspace_root: &Path) -> PathBuf {
79 if let Some(path) = std::env::var_os("CANIC_CANISTERS_ROOT") {
80 return normalize_workspace_path(workspace_root, PathBuf::from(path));
81 }
82
83 if let Some(path) = std::env::var_os("CANIC_CONFIG_PATH") {
84 let config_path = normalize_workspace_path(workspace_root, PathBuf::from(path));
85 if let Some(parent) = config_path.parent() {
86 return parent.to_path_buf();
87 }
88 }
89
90 if let Some(manifest_path) = discover_canister_manifest_from_metadata(workspace_root, "root")
91 && let Some(parent) = manifest_path.parent().and_then(Path::parent)
92 {
93 return parent.to_path_buf();
94 }
95
96 workspace_root.join(CANISTERS_ROOT_RELATIVE)
97}
98
99#[must_use]
101pub fn root_manifest_path(workspace_root: &Path) -> PathBuf {
102 std::env::var_os("CANIC_ROOT_MANIFEST_PATH").map_or_else(
103 || {
104 discover_canister_manifest_from_metadata(workspace_root, "root").unwrap_or_else(|| {
105 canisters_root(workspace_root)
106 .join("root")
107 .join("Cargo.toml")
108 })
109 },
110 |path| normalize_workspace_path(workspace_root, PathBuf::from(path)),
111 )
112}
113
114#[must_use]
116pub fn canister_manifest_path(workspace_root: &Path, canister_name: &str) -> PathBuf {
117 discover_canister_manifest_from_metadata(workspace_root, canister_name).unwrap_or_else(|| {
118 canisters_root(workspace_root)
119 .join(canister_name)
120 .join("Cargo.toml")
121 })
122}
123
124#[must_use]
126pub fn workspace_manifest_path(workspace_root: &Path) -> PathBuf {
127 std::env::var_os("CANIC_WORKSPACE_MANIFEST_PATH").map_or_else(
128 || workspace_root.join(WORKSPACE_MANIFEST_RELATIVE),
129 |path| normalize_workspace_path(workspace_root, PathBuf::from(path)),
130 )
131}
132
133#[must_use]
135pub fn display_workspace_path(workspace_root: &Path, path: &Path) -> String {
136 path.strip_prefix(workspace_root)
137 .unwrap_or(path)
138 .display()
139 .to_string()
140}
141
142pub fn resolve_artifact_root(
144 icp_root: &Path,
145 network: &str,
146) -> Result<PathBuf, Box<dyn std::error::Error>> {
147 let preferred = icp_root.join(".icp").join(network).join("canisters");
148 if preferred.is_dir() {
149 return Ok(preferred);
150 }
151
152 let local_artifact_root = icp_root.join(".icp/local/canisters");
153 if local_artifact_root.is_dir() {
154 return Ok(local_artifact_root);
155 }
156
157 Err(format!(
158 "missing built ICP artifacts under {} or {}",
159 preferred.display(),
160 local_artifact_root.display()
161 )
162 .into())
163}
164
165pub fn root_release_set_manifest_path(
167 artifact_root: &Path,
168) -> Result<PathBuf, Box<dyn std::error::Error>> {
169 let manifest_path = artifact_root
170 .join("root")
171 .join(ROOT_RELEASE_SET_MANIFEST_FILE);
172
173 if let Some(parent) = manifest_path.parent() {
174 fs::create_dir_all(parent)?;
175 }
176
177 Ok(manifest_path)
178}
179
180pub fn load_root_package_version(
182 root_manifest_path: &Path,
183 workspace_manifest_path: &Path,
184) -> Result<String, Box<dyn std::error::Error>> {
185 let manifest_source = fs::read_to_string(root_manifest_path)?;
186 let manifest = toml::from_str::<TomlValue>(&manifest_source)?;
187 let version_value = manifest
188 .get("package")
189 .and_then(TomlValue::as_table)
190 .and_then(|package| package.get("version"))
191 .ok_or_else(|| {
192 format!(
193 "missing package.version in {}",
194 root_manifest_path.display()
195 )
196 })?;
197
198 if let Some(version) = version_value.as_str() {
199 return Ok(version.to_string());
200 }
201
202 if version_value
203 .as_table()
204 .and_then(|value| value.get("workspace"))
205 .and_then(TomlValue::as_bool)
206 == Some(true)
207 {
208 return load_workspace_package_version(workspace_manifest_path);
209 }
210
211 Err(format!(
212 "unsupported package.version format in {}",
213 root_manifest_path.display()
214 )
215 .into())
216}
217
218pub fn load_workspace_package_version(
220 workspace_manifest_path: &Path,
221) -> Result<String, Box<dyn std::error::Error>> {
222 let manifest_source = fs::read_to_string(workspace_manifest_path)?;
223 let manifest = toml::from_str::<TomlValue>(&manifest_source)?;
224 let version = manifest
225 .get("workspace")
226 .and_then(TomlValue::as_table)
227 .and_then(|workspace| workspace.get("package"))
228 .and_then(TomlValue::as_table)
229 .and_then(|package| package.get("version"))
230 .and_then(TomlValue::as_str)
231 .ok_or_else(|| {
232 format!(
233 "missing workspace.package.version in {}",
234 workspace_manifest_path.display()
235 )
236 })?;
237
238 Ok(version.to_string())
239}