Skip to main content

cargo_workspace2/models/
cargo.rs

1use crate::cargo::{self, Dependency, Result};
2use std::{fmt, path::PathBuf};
3use toml_edit::{value, Document, Item, Value};
4
5/// Initial representation of a crate, before being parsed
6#[derive(Clone)]
7pub struct CargoCrate {
8    pub doc: Document,
9    pub path: PathBuf,
10    pub dependencies: Vec<Dependency>,
11}
12
13impl fmt::Debug for CargoCrate {
14    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
15        write!(f, "{}", self.path.as_path().display())
16    }
17}
18
19impl CargoCrate {
20    /// Get the crate name from the inner document
21    pub fn name(&self) -> String {
22        match &self.doc["package"]["name"] {
23            Item::Value(Value::String(ref name)) => {
24                name.to_string().replace("\"", "").as_str().trim().into()
25            }
26            _ => panic!(format!("Invalid Cargo.toml: {:?}", self.path)),
27        }
28    }
29
30    /// Get the current version
31    pub fn version(&self) -> String {
32        match &self.doc["package"]["version"] {
33            Item::Value(Value::String(ref name)) => {
34                name.to_string().replace("\"", "").as_str().trim().into()
35            }
36            _ => panic!(format!("Invalid Cargo.toml: {:?}", self.path)),
37        }
38    }
39
40    /// Find a cargo dependency by name
41    pub fn dep_by_name(&self, name: &String) -> &Dependency {
42        self.dependencies
43            .iter()
44            .find(|c| &c.name == name)
45            .as_ref()
46            .unwrap()
47    }
48
49    pub fn change_dep(&mut self, dep: &String, ver: &String) {
50        let dep = self
51            .dep_by_name(dep)
52            .alias()
53            .unwrap_or(dep.to_string())
54            .clone();
55        cargo::update_dependency(&mut self.doc, &dep, ver);
56    }
57
58    pub fn all_deps_mut(&mut self) -> Vec<&mut Dependency> {
59        self.dependencies.iter_mut().collect()
60    }
61
62    /// Check if this crate depends on a specific version of another
63    pub fn has_version(&self, name: &String) -> bool {
64        self.dep_by_name(name).has_version()
65    }
66
67    /// Check if this crate depends on a specific path of another
68    pub fn has_path(&self, name: &String) -> bool {
69        self.dep_by_name(name).has_version()
70    }
71
72    /// Set a new version for this crate
73    pub fn set_version(&mut self, version: String) {
74        self.doc["package"]["version"] = value(version);
75    }
76
77    /// Sync any changes made to the document to disk
78    pub fn sync(&mut self) {
79        cargo::sync(&mut self.doc, self.path.join("Cargo.toml")).unwrap();
80    }
81}
82
83/// Initial representation of the workspate, before getting parsed
84pub struct CargoWorkspace {
85    pub root: PathBuf,
86    pub crates: Vec<CargoCrate>,
87}
88
89impl CargoWorkspace {
90    /// Open a workspace and parse dependency graph
91    ///
92    /// Point this to the root of the workspace, do the root
93    /// `Cargo.toml` file.
94    pub fn open(p: impl Into<PathBuf>) -> Result<Self> {
95        let path = p.into();
96
97        let root_cfg = cargo::parse_root_toml(path.join("Cargo.toml"))?;
98        let members = cargo::get_members(&root_cfg)?;
99
100        let m_cfg: Vec<_> = members
101            .into_iter()
102            .filter_map(
103                |name| match cargo::parse_toml(path.join(&name).join("Cargo.toml")) {
104                    Ok(doc) => Some((
105                        PathBuf::new().join(name),
106                        cargo::parse_dependencies(&doc),
107                        doc,
108                    )),
109                    Err(e) => {
110                        eprintln!(
111                            "Error occured while parsing member `{}`/`Cargo.toml`: {:?}",
112                            name, e
113                        );
114                        None
115                    }
116                },
117            )
118            .collect();
119
120        Ok(Self {
121            root: path,
122            crates: m_cfg
123                .into_iter()
124                .map(|(path, dependencies, doc)| CargoCrate {
125                    path,
126                    dependencies,
127                    doc,
128                })
129                .collect(),
130        })
131    }
132}