release_kit/depend/
target.rs1use camino::{Utf8Path, Utf8PathBuf};
10use serde::Serialize;
11
12use super::{Manager, canonical_dir};
13use crate::error::RkError;
14pub use crate::self_depend::manager::{MISE_FILES, ManagerFile, first_mention, manager_files};
15
16#[derive(Debug, Clone, Serialize)]
18pub struct Already {
19 pub manager: Manager,
21 pub file: String,
23 pub line: usize,
25}
26
27#[derive(Debug, Clone)]
29pub struct Target {
30 pub path: Utf8PathBuf,
32 pub tech: Option<&'static str>,
34 pub managers: Vec<ManagerFile>,
36 pub envrc_use_flake: bool,
38 pub already: Vec<Already>,
40}
41
42impl Target {
43 #[must_use]
45 pub fn file_of(&self, manager: Manager) -> Option<&ManagerFile> {
46 self.managers.iter().find(|m| m.manager == manager)
47 }
48
49 #[must_use]
51 pub const fn default_file(manager: Manager) -> &'static str {
52 manager.default_file()
53 }
54}
55
56pub fn observe(path: &Utf8Path, dep_name: Option<&str>) -> Result<Target, RkError> {
63 let path = canonical_dir(path, "target")?;
64 let managers = manager_files(&path, dep_name)?;
65 let envrc_use_flake = match std::fs::read_to_string(path.join(".envrc")) {
66 Ok(text) => text.lines().any(|line| {
67 let mut words = line.split_whitespace();
68 words.next() == Some("use") && words.next() == Some("flake")
69 }),
70 Err(e) if e.kind() == std::io::ErrorKind::NotFound => false,
71 Err(e) => return Err(RkError::Io(e)),
72 };
73 let already = dep_name
74 .map(|name| {
75 managers
76 .iter()
77 .filter_map(|m| {
78 first_mention(&m.text, name).map(|line| Already {
79 manager: m.manager,
80 file: m.file.clone(),
81 line,
82 })
83 })
84 .collect()
85 })
86 .unwrap_or_default();
87 Ok(Target {
88 tech: tech_or_node(&path),
89 path,
90 managers,
91 envrc_use_flake,
92 already,
93 })
94}
95
96#[must_use]
99pub fn tech_or_node(dir: &Utf8Path) -> Option<&'static str> {
100 crate::detect::tech_of(dir.as_std_path())
101 .or_else(|| dir.join("package.json").is_file().then_some("node"))
102}
103
104#[cfg(test)]
105mod tests {
106 use super::{Manager, Target, tech_or_node};
107
108 #[test]
109 fn the_default_file_is_the_managers_own() {
110 for manager in Manager::ALL {
111 assert_eq!(Target::default_file(manager), manager.default_file());
112 }
113 }
114
115 #[test]
116 fn node_is_read_from_package_json_after_the_version_files() {
117 let dir = tempfile::tempdir().expect("a scratch dir");
118 let root = camino::Utf8Path::from_path(dir.path()).expect("utf-8");
119 assert_eq!(tech_or_node(root), None);
120 std::fs::write(root.join("package.json"), "{}").expect("writes");
121 assert_eq!(tech_or_node(root), Some("node"));
122 std::fs::write(root.join("Cargo.toml"), "").expect("writes");
123 assert_eq!(tech_or_node(root), Some("rust"));
124 }
125}