use crate::cargo::{self, Dependency, Result}; use std::{fmt, path::PathBuf}; use toml_edit::{value, Document, Item, Value}; /// Initial representation of a crate, before being parsed #[derive(Clone)] pub struct CargoCrate { pub doc: Document, pub path: PathBuf, pub dependencies: Vec, } impl fmt::Debug for CargoCrate { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.path.as_path().display()) } } impl CargoCrate { /// Get the crate name from the inner document pub fn name(&self) -> String { match &self.doc["package"]["name"] { Item::Value(Value::String(ref name)) => { name.to_string().replace("\"", "").as_str().trim().into() } _ => panic!(format!("Invalid Cargo.toml: {:?}", self.path)), } } /// Get the current version pub fn version(&self) -> String { match &self.doc["package"]["version"] { Item::Value(Value::String(ref name)) => { name.to_string().replace("\"", "").as_str().trim().into() } _ => panic!(format!("Invalid Cargo.toml: {:?}", self.path)), } } /// Find a cargo dependency by name pub fn dep_by_name(&self, name: &String) -> &Dependency { self.dependencies .iter() .find(|c| &c.name == name) .as_ref() .unwrap() } pub fn change_dep(&mut self, dep: &String, ver: &String) { let dep = self .dep_by_name(dep) .alias() .unwrap_or(dep.to_string()) .clone(); cargo::update_dependency(&mut self.doc, &dep, ver); } pub fn all_deps_mut(&mut self) -> Vec<&mut Dependency> { self.dependencies.iter_mut().collect() } /// Check if this crate depends on a specific version of another pub fn has_version(&self, name: &String) -> bool { self.dep_by_name(name).has_version() } /// Check if this crate depends on a specific path of another pub fn has_path(&self, name: &String) -> bool { self.dep_by_name(name).has_version() } /// Set a new version for this crate pub fn set_version(&mut self, version: String) { self.doc["package"]["version"] = value(version); } /// Sync any changes made to the document to disk pub fn sync(&mut self) { cargo::sync(&mut self.doc, self.path.join("Cargo.toml")).unwrap(); } } /// Initial representation of the workspate, before getting parsed pub struct CargoWorkspace { pub root: PathBuf, pub crates: Vec, } impl CargoWorkspace { /// Open a workspace and parse dependency graph /// /// Point this to the root of the workspace, do the root /// `Cargo.toml` file. pub fn open(p: impl Into) -> Result { let path = p.into(); let root_cfg = cargo::parse_root_toml(path.join("Cargo.toml"))?; let members = cargo::get_members(&root_cfg)?; let m_cfg: Vec<_> = members .into_iter() .filter_map( |name| match cargo::parse_toml(path.join(&name).join("Cargo.toml")) { Ok(doc) => Some(( PathBuf::new().join(name), cargo::parse_dependencies(&doc), doc, )), Err(e) => { eprintln!( "Error occured while parsing member `{}`/`Cargo.toml`: {:?}", name, e ); None } }, ) .collect(); Ok(Self { root: path, crates: m_cfg .into_iter() .map(|(path, dependencies, doc)| CargoCrate { path, dependencies, doc, }) .collect(), }) } }