suitcase/internal/utils/
dart.rs1use crate::{exec_on, internal::shell::Shell};
2use anyhow::Context;
3use log::debug;
4use std::{env, path::PathBuf};
5
6const IGNORED_FOLDERS: [&str; 12] = [
7 "ios",
8 "android",
9 "windows",
10 "linux",
11 "macos",
12 ".symlinks",
13 ".plugin_symlinks",
14 ".dart_tool",
15 "build",
16 ".fvm",
17 "flutter_sdk",
18 "flutter_gen",
19];
20
21pub struct DartShell<'a> {
22 shell: &'a Shell,
23}
24
25impl<'a> DartShell<'a> {
26 pub fn new(shell: &'a Shell) -> Self {
27 Self { shell }
28 }
29
30 pub fn find_dart_projects(
31 &self,
32 path: Option<&PathBuf>,
33 ) -> anyhow::Result<Vec<DartProjectMetadata>> {
34 let cwd = env::current_dir().context("trying to get current directory")?;
35 let path = path.unwrap_or(&cwd);
36 debug!("finding Dart projects recursively in path: {:?}", path);
37
38 let path_str = path.to_str().context(format!(
39 "trying to convert path '{}' to a string",
40 path.display()
41 ))?;
42
43 let output = exec_on!(
44 self.shell,
45 "find",
46 path_str,
47 "-type",
48 "f",
49 "-name",
50 "pubspec.yaml",
51 "-exec",
52 "dirname",
53 "{}",
54 ";"
55 )
56 .context(format!(
57 "trying to find Dart projects in path '{}'",
58 path.display()
59 ))?;
60
61 let projects = output
62 .stdout
63 .lines()
64 .map(|line| PathBuf::try_from(line.trim()))
65 .filter_map(Result::ok)
66 .map(|path| path.canonicalize())
67 .filter_map(Result::ok)
68 .filter(|path| {
69 let Some(last_component) = path.components().last() else {
70 return true;
71 };
72
73 let Some(dir_name) = last_component.as_os_str().to_str() else {
74 return true;
75 };
76
77 !IGNORED_FOLDERS.contains(&dir_name)
78 })
79 .map(|path| self.get_dart_project_metadata(path))
80 .collect::<Result<Vec<_>, _>>()?;
81
82 debug!("found {} Dart projects", projects.len());
83
84 Ok(projects)
85 }
86
87 pub fn get_dart_project_metadata(&self, path: PathBuf) -> anyhow::Result<DartProjectMetadata> {
88 let name = path
89 .file_name()
90 .context(format!(
91 "trying to get file name from path '{}'",
92 path.display()
93 ))?
94 .to_str()
95 .context(format!(
96 "trying to convert file name from path '{}' to a string",
97 path.display()
98 ))?
99 .to_string();
100
101 let pubspec_file_path = path.join("pubspec.yaml");
102
103 let pubspec_file = std::fs::File::open(&pubspec_file_path).context(format!(
104 "trying to open pubspec.yaml file at path '{}'",
105 pubspec_file_path.display()
106 ))?;
107 let pubspec_yaml: serde_yaml::Value =
108 serde_yaml::from_reader(pubspec_file).context(format!(
109 "trying to parse pubspec.yaml file at path '{}'",
110 pubspec_file_path.display()
111 ))?;
112
113 let is_flutter_project = !pubspec_yaml["dependencies"]["flutter"].is_null();
114
115 Ok(DartProjectMetadata {
116 path,
117 name,
118 is_flutter_project,
119 })
120 }
121}
122
123#[derive(Debug)]
124pub struct DartProjectMetadata {
125 pub path: PathBuf,
126 pub name: String,
127 pub is_flutter_project: bool,
128}