ghactions_toolcache/
tool.rs1use glob::{MatchOptions, glob_with};
4use std::{fmt::Display, path::PathBuf};
5
6use super::ToolCacheArch;
7
8#[derive(Debug, Clone)]
10pub struct Tool {
11 name: String,
13 version: String,
15 arch: ToolCacheArch,
17
18 path: PathBuf,
20}
21
22impl Tool {
23 pub fn new(
25 name: impl Into<String>,
26 version: impl Into<String>,
27 arch: impl Into<ToolCacheArch>,
28 path: impl Into<PathBuf>,
29 ) -> Self {
30 Self {
31 name: name.into(),
32 version: version.into(),
33 arch: arch.into(),
34 path: path.into(),
35 }
36 }
37
38 pub fn name(&self) -> &str {
40 &self.name
41 }
42
43 pub fn version(&self) -> &str {
45 &self.version
46 }
47
48 pub fn arch(&self) -> &ToolCacheArch {
50 &self.arch
51 }
52
53 pub fn path(&self) -> &PathBuf {
55 &self.path
56 }
57
58 pub fn join(&self, path: impl Into<PathBuf>) -> PathBuf {
60 self.path.join(path.into())
61 }
62
63 pub(crate) fn find(
65 toolcache_root: impl Into<PathBuf>,
66 tool_name: impl Into<String>,
67 version: impl Into<String>,
68 arch: impl Into<ToolCacheArch>,
69 ) -> Result<Vec<Tool>, crate::ToolCacheError> {
70 let tool_name = tool_name.into();
71 let version = version.into();
72 let arch = arch.into();
73
74 let tool_path = Tool::tool_path(toolcache_root, tool_name.clone(), version.clone(), arch);
75 let tool_path_str = tool_path.to_str().unwrap();
76
77 let mut results: Vec<Tool> = vec![];
78
79 let options = MatchOptions {
80 case_sensitive: false,
81 require_literal_separator: true,
82 require_literal_leading_dot: false,
83 };
84
85 for entry in glob_with(tool_path_str, options).expect("Failed to read tool cache") {
86 let path = entry.expect("Failed to read tool cache");
87
88 if path.is_dir() && path.exists() {
89 match Tool::try_from(path) {
90 Ok(tool) => results.push(tool),
91 Err(e) => {
92 log::debug!("Failed to create Tool from path: {:?}", e);
93 }
94 };
95 }
96 }
97
98 Ok(results)
99 }
100
101 pub(crate) fn tool_path(
103 toolcache_root: impl Into<PathBuf>,
104 tool: impl Into<String>,
105 version: impl Into<String>,
106 arch: impl Into<ToolCacheArch>,
107 ) -> PathBuf {
108 let toolcache_root = toolcache_root.into();
109 let tool = tool.into();
111 let mut version = version.into();
112 if version.contains('x') {
114 version = version.replace("x", "*");
115 }
116 let arch = match arch.into() {
117 ToolCacheArch::X64 => "x64",
118 ToolCacheArch::ARM64 => "arm64",
119 ToolCacheArch::Any => "**",
120 };
121 toolcache_root.join(tool).join(version).join(arch).join("")
123 }
124}
125
126impl TryFrom<PathBuf> for Tool {
127 type Error = crate::ToolCacheError;
128
129 fn try_from(value: PathBuf) -> Result<Self, Self::Error> {
130 let parts: Vec<&str> = value.iter().map(|p| p.to_str().unwrap()).collect();
131
132 let arch = match parts.last() {
133 Some(arch) => arch,
134 None => {
135 return Err(crate::ToolCacheError::GenericError(
136 "Invalid Tool Path".to_string(),
137 ));
138 }
139 };
140 let version = match parts.get(parts.len() - 2) {
141 Some(version) => version,
142 None => {
143 return Err(crate::ToolCacheError::GenericError(
144 "Invalid Tool Path".to_string(),
145 ));
146 }
147 };
148 let name = match parts.get(parts.len() - 3) {
149 Some(name) => name,
150 None => {
151 return Err(crate::ToolCacheError::GenericError(
152 "Invalid Tool Path".to_string(),
153 ));
154 }
155 };
156
157 Ok(Self {
158 name: name.to_string(),
159 version: version.to_string(),
160 arch: ToolCacheArch::from(*arch),
161 path: value,
162 })
163 }
164}
165
166impl Display for Tool {
167 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
168 write!(f, "{:?}", self.path)
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 #[test]
177 fn test_tool_from_path() {
178 let path = PathBuf::from("node/12.7.0/x64");
179 let tool = Tool::try_from(path.clone()).unwrap();
180
181 assert_eq!(tool.path(), &path);
182 assert_eq!(tool.name(), "node");
183 assert_eq!(tool.version(), "12.7.0");
184 assert_eq!(tool.arch(), &ToolCacheArch::X64);
185 }
186
187 #[test]
188 #[cfg(target_os = "linux")]
189 fn test_tool_path() {
190 let cwd = std::env::current_dir()
191 .unwrap()
192 .join("..")
193 .canonicalize()
194 .unwrap();
195
196 let toolcache_root = cwd.clone();
197 let tool_path = Tool::tool_path(&toolcache_root, "node", "12.7.0", ToolCacheArch::X64);
198
199 assert_eq!(tool_path, cwd.join("node/12.7.0/x64/"));
200
201 let tool_path = Tool::tool_path(&toolcache_root, "node", "12.x", ToolCacheArch::X64);
202 assert_eq!(tool_path, cwd.join("node/12.*/x64/"));
203 }
204}