glory_cli/ext/
cargo.rs

1use std::collections::HashSet;
2
3use super::anyhow::Result;
4use super::{PathBufExt, PathExt};
5use camino::{Utf8Path, Utf8PathBuf};
6use cargo_metadata::{Metadata, MetadataCommand, Package, PackageId, Resolve, Target};
7
8pub trait PackageExt {
9    fn has_bin_target(&self) -> bool;
10    fn bin_targets(&self) -> Box<dyn Iterator<Item = &Target> + '_>;
11    fn cdylib_target(&self) -> Option<&Target>;
12    fn target_list(&self) -> String;
13    fn path_dependencies(&self) -> Vec<Utf8PathBuf>;
14}
15
16impl PackageExt for Package {
17    fn has_bin_target(&self) -> bool {
18        self.targets.iter().any(|t| t.is_bin())
19    }
20
21    fn bin_targets(&self) -> Box<dyn Iterator<Item = &Target> + '_> {
22        Box::new(self.targets.iter().filter(|t| t.is_bin()))
23    }
24    fn cdylib_target(&self) -> Option<&Target> {
25        let cdylib: String = "cdylib".to_string();
26        self.targets.iter().find(|t| t.crate_types.contains(&cdylib))
27    }
28    fn target_list(&self) -> String {
29        self.targets
30            .iter()
31            .map(|t| format!("{} ({})", t.name, t.crate_types.join(", ")))
32            .collect::<Vec<_>>()
33            .join(", ")
34    }
35
36    fn path_dependencies(&self) -> Vec<Utf8PathBuf> {
37        let mut found = Vec::new();
38        for dep in &self.dependencies {
39            if let Some(path) = &dep.path {
40                found.push(path.clone());
41            }
42        }
43        found
44    }
45}
46
47pub trait MetadataExt {
48    fn load_cleaned(manifest_path: &Utf8Path) -> Result<Metadata>;
49    fn rel_target_dir(&self) -> Utf8PathBuf;
50    fn package_for(&self, id: &PackageId) -> Option<&Package>;
51    fn path_dependencies(&self, id: &PackageId) -> Vec<Utf8PathBuf>;
52    fn src_path_dependencies(&self, id: &PackageId) -> Vec<Utf8PathBuf>;
53}
54
55impl MetadataExt for Metadata {
56    fn load_cleaned(manifest_path: &Utf8Path) -> Result<Metadata> {
57        let mut metadata = MetadataCommand::new().manifest_path(manifest_path).exec()?;
58        metadata.workspace_root.clean_windows_path();
59        metadata.target_directory.clean_windows_path();
60        for package in &mut metadata.packages {
61            package.manifest_path.clean_windows_path();
62            for dependency in &mut package.dependencies {
63                if let Some(p) = dependency.path.as_mut() {
64                    p.clean_windows_path()
65                }
66            }
67        }
68        Ok(metadata)
69    }
70
71    fn rel_target_dir(&self) -> Utf8PathBuf {
72        self.target_directory.clone().unbase(&self.workspace_root).unwrap()
73    }
74
75    fn package_for(&self, id: &PackageId) -> Option<&Package> {
76        self.packages.iter().find(|p| p.id == *id)
77    }
78
79    fn path_dependencies(&self, id: &PackageId) -> Vec<Utf8PathBuf> {
80        let Some(resolve) = &self.resolve else { return vec![] };
81        let mut found = vec![];
82
83        let mut set = HashSet::new();
84        resolve.deps_for(id, &mut set);
85
86        for pck in &self.packages {
87            if set.contains(&pck.id) {
88                found.extend(pck.path_dependencies())
89            }
90        }
91
92        found
93    }
94
95    fn src_path_dependencies(&self, id: &PackageId) -> Vec<Utf8PathBuf> {
96        let root = &self.workspace_root;
97        self.path_dependencies(id)
98            .iter()
99            .map(|p| p.unbase(root).unwrap_or_else(|_| p.to_path_buf()).join("src"))
100            .collect()
101    }
102}
103
104pub trait ResolveExt {
105    fn deps_for(&self, id: &PackageId, set: &mut HashSet<PackageId>);
106}
107
108impl ResolveExt for Resolve {
109    fn deps_for(&self, id: &PackageId, set: &mut HashSet<PackageId>) {
110        if let Some(node) = self.nodes.iter().find(|n| n.id == *id) {
111            if set.insert(node.id.clone()) {
112                for dep in &node.deps {
113                    self.deps_for(&dep.pkg, set);
114                }
115            }
116        }
117    }
118}