cargo_airbender/commands/new/
deps.rs1use crate::error::{CliError, Result};
2use std::path::{Path, PathBuf};
3
4const DEFAULT_SDK_GIT_REPOSITORY: &str = "https://github.com/matter-labs/airbender-platform";
5const DEFAULT_SDK_GIT_BRANCH: &str = "main";
6
7pub(super) fn resolve_crate_dependency(
8 destination_crate_dir: &Path,
9 sdk_path: Option<&Path>,
10 sdk_version: Option<&str>,
11 crate_name: &str,
12) -> Result<String> {
13 if let Some(version) = sdk_version {
14 if version.is_empty() {
15 return Err(CliError::new("`--sdk-version` cannot be empty"));
16 }
17
18 return Ok(format!("version = \"{version}\""));
19 }
20
21 if let Some(sdk_path) = sdk_path {
22 if !sdk_path.exists() {
23 return Err(
24 CliError::new(format!("SDK path `{}` does not exist", sdk_path.display()))
25 .with_hint(
26 "pass `--sdk-path` pointing to an existing airbender-platform checkout",
27 ),
28 );
29 }
30
31 let crate_path = resolve_dependency_crate_path(sdk_path, crate_name)?;
32 let sdk_relative = relative_path(destination_crate_dir, &crate_path)?;
33 return Ok(format!("path = \"{}\"", sdk_relative.to_string_lossy()));
34 }
35
36 Ok(format!(
37 "git = \"{DEFAULT_SDK_GIT_REPOSITORY}\", branch = \"{DEFAULT_SDK_GIT_BRANCH}\""
38 ))
39}
40
41fn resolve_dependency_crate_path(sdk_path: &Path, crate_name: &str) -> Result<PathBuf> {
42 let mut candidates = Vec::new();
43
44 if sdk_path
45 .file_name()
46 .map(|name| name == crate_name)
47 .unwrap_or(false)
48 {
49 candidates.push(sdk_path.to_path_buf());
50 }
51 candidates.push(sdk_path.join(crate_name));
52 candidates.push(sdk_path.join("crates").join(crate_name));
53 if let Some(parent) = sdk_path.parent() {
54 candidates.push(parent.join(crate_name));
55 }
56
57 for candidate in candidates {
58 if candidate.join("Cargo.toml").exists() {
59 return candidate.canonicalize().map_err(|err| {
60 CliError::with_source(
61 format!("failed to canonicalize `{}`", candidate.display()),
62 err,
63 )
64 });
65 }
66 }
67
68 Err(CliError::new(format!(
69 "failed to locate `{crate_name}` under `{}`",
70 sdk_path.display()
71 ))
72 .with_hint("point `--sdk-path` to the workspace root or crate directory"))
73}
74
75fn relative_path(from: &Path, to: &Path) -> Result<PathBuf> {
76 let from = from.canonicalize().map_err(|err| {
77 CliError::with_source(format!("failed to canonicalize `{}`", from.display()), err)
78 })?;
79 let to = to.canonicalize().map_err(|err| {
80 CliError::with_source(format!("failed to canonicalize `{}`", to.display()), err)
81 })?;
82
83 let from_components: Vec<_> = from.components().collect();
84 let to_components: Vec<_> = to.components().collect();
85
86 let mut common_len = 0usize;
87 while common_len < from_components.len()
88 && common_len < to_components.len()
89 && from_components[common_len] == to_components[common_len]
90 {
91 common_len += 1;
92 }
93
94 let mut result = PathBuf::new();
95 for _ in common_len..from_components.len() {
96 result.push("..");
97 }
98 for component in &to_components[common_len..] {
99 result.push(component.as_os_str());
100 }
101
102 Ok(result)
103}