aboutsummaryrefslogtreecommitdiff
path: root/development/tools/cargo-workspace2/src/models
diff options
context:
space:
mode:
Diffstat (limited to 'development/tools/cargo-workspace2/src/models')
-rw-r--r--development/tools/cargo-workspace2/src/models/_crate.rs112
-rw-r--r--development/tools/cargo-workspace2/src/models/cargo.rs132
-rw-r--r--development/tools/cargo-workspace2/src/models/graph.rs78
-rw-r--r--development/tools/cargo-workspace2/src/models/mod.rs50
-rw-r--r--development/tools/cargo-workspace2/src/models/publish.rs20
5 files changed, 392 insertions, 0 deletions
diff --git a/development/tools/cargo-workspace2/src/models/_crate.rs b/development/tools/cargo-workspace2/src/models/_crate.rs
new file mode 100644
index 000000000000..68d2baad2bad
--- /dev/null
+++ b/development/tools/cargo-workspace2/src/models/_crate.rs
@@ -0,0 +1,112 @@
+use crate::models::{CargoCrate, CrateId, DepGraph};
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::{
+ cmp::{self, Eq, Ord, PartialEq, PartialOrd},
+ collections::BTreeSet,
+ path::PathBuf,
+};
+
+static ID_CTR: AtomicUsize = AtomicUsize::new(0);
+
+/// A crate in a cargo workspace
+///
+/// Has a name, path (stored as the offset of the root), and set of
+/// dependencies inside the workspace. To get the dependents of this
+/// crate, query the dependency graph with the set of other crate IDs.
+#[derive(Clone, Debug)]
+pub struct Crate {
+ /// Numeric Id of this crate
+ pub id: CrateId,
+ /// Package name, not the folder name
+ pub name: String,
+ /// Path offset of the workspace root
+ pub cc: CargoCrate,
+ /// List of dependencies this crate has inside this workspace
+ pub dependencies: BTreeSet<CrateId>,
+}
+
+impl PartialEq for Crate {
+ fn eq(&self, other: &Self) -> bool {
+ self.id == other.id
+ }
+}
+
+impl Eq for Crate {}
+
+impl Ord for Crate {
+ fn cmp(&self, other: &Self) -> cmp::Ordering {
+ self.id.cmp(&other.id)
+ }
+}
+
+impl PartialOrd for Crate {
+ fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+/// Increment the monotonicly increasing Id
+fn incr_id() -> usize {
+ ID_CTR.fetch_add(1, Ordering::Relaxed)
+}
+
+impl Crate {
+ pub fn new(cc: CargoCrate) -> Self {
+ Self {
+ id: incr_id(),
+ name: cc.name(),
+ cc,
+ dependencies: BTreeSet::default(),
+ }
+ }
+
+ /// Call this function once all crates have been loaded into scope
+ pub fn process(&mut self, g: &DepGraph) {
+ let deps: Vec<_> = self
+ .cc
+ .dependencies
+ .iter()
+ .filter_map(|d| g.find_crate(&d.name))
+ .collect();
+
+ deps.into_iter().for_each(|cid| self.add_dependency(cid));
+ }
+
+ /// Get the crate name
+ pub fn name(&self) -> &String {
+ &self.name
+ }
+
+ /// Get the crate path
+ pub fn path(&self) -> &PathBuf {
+ &self.cc.path
+ }
+
+ /// Get the current version
+ pub fn version(&self) -> String {
+ self.cc.version()
+ }
+
+ /// Add a dependency of this crate
+ pub fn add_dependency(&mut self, id: CrateId) {
+ self.dependencies.insert(id);
+ }
+
+ /// Check if this crate has a particular dependency
+ pub fn has_dependency(&self, id: CrateId) -> bool {
+ self.dependencies.contains(&id)
+ }
+
+ pub fn change_dependency(&mut self, dep: &String, new_ver: &String) {
+ self.cc.change_dep(dep, new_ver);
+ }
+
+ /// Publish a new version of this crate
+ pub fn publish(&mut self, new_version: String) {
+ self.cc.set_version(new_version);
+ }
+
+ pub fn sync(&mut self) {
+ self.cc.sync();
+ }
+}
diff --git a/development/tools/cargo-workspace2/src/models/cargo.rs b/development/tools/cargo-workspace2/src/models/cargo.rs
new file mode 100644
index 000000000000..b020e82e418d
--- /dev/null
+++ b/development/tools/cargo-workspace2/src/models/cargo.rs
@@ -0,0 +1,132 @@
+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<Dependency>,
+}
+
+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<CargoCrate>,
+}
+
+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<PathBuf>) -> Result<Self> {
+ 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(),
+ })
+ }
+}
diff --git a/development/tools/cargo-workspace2/src/models/graph.rs b/development/tools/cargo-workspace2/src/models/graph.rs
new file mode 100644
index 000000000000..867c463fb1e3
--- /dev/null
+++ b/development/tools/cargo-workspace2/src/models/graph.rs
@@ -0,0 +1,78 @@
+use crate::models::{CargoCrate, Crate, CrateId};
+use std::collections::{BTreeMap, BTreeSet};
+use std::path::PathBuf;
+
+/// Dependency graph in a workspace
+pub struct DepGraph {
+ /// Mapping of crates in the workspace
+ members: BTreeMap<CrateId, Crate>,
+ /// Map of crates and the members that depend on them
+ dependents: BTreeMap<CrateId, BTreeSet<CrateId>>,
+}
+
+impl DepGraph {
+ /// Create a new, empty dependency graph
+ pub fn new() -> Self {
+ Self {
+ members: Default::default(),
+ dependents: Default::default(),
+ }
+ }
+
+ pub fn add_crate(&mut self, cc: CargoCrate) {
+ let cc = Crate::new(cc);
+ self.members.insert(cc.id, cc);
+ }
+
+ /// Cache the dependents graph for all crates
+ pub fn finalise(&mut self) {
+ let mut members = self.members.clone();
+ members.iter_mut().for_each(|(_, c)| c.process(&self));
+
+ members.iter().for_each(|(id, _crate)| {
+ _crate.dependencies.iter().for_each(|dep_id| {
+ self.dependents.entry(*dep_id).or_default().insert(*id);
+ });
+ });
+ let _ = std::mem::replace(&mut self.members, members);
+ }
+
+ /// Get a crate by ID
+ pub fn get_crate(&self, id: CrateId) -> &Crate {
+ self.members.get(&id).as_ref().unwrap()
+ }
+
+ /// Get mutable access to a crate by ID
+ pub fn mut_crate(&mut self, id: CrateId) -> &mut Crate {
+ self.members.get_mut(&id).unwrap()
+ }
+
+ /// Find a crate via it's name
+ pub fn find_crate(&self, name: &String) -> Option<CrateId> {
+ self.members
+ .iter()
+ .find(|(_, c)| c.name() == name)
+ .map(|(id, _)| *id)
+ }
+
+ /// Find a crate by it's path-offset in the workspace
+ pub fn find_crate_by_path(&self, name: &String) -> Option<CrateId> {
+ self.members
+ .iter()
+ .find(|(_, c)| c.path() == &PathBuf::new().join(name))
+ .map(|(id, _)| *id)
+ }
+
+ /// Get a crate's dependents
+ pub fn get_dependents(&self, id: CrateId) -> Vec<CrateId> {
+ self.dependents
+ .get(&id)
+ .as_ref()
+ .map(|set| set.iter().cloned().collect())
+ .unwrap_or(vec![])
+ }
+
+ pub fn get_all(&self) -> Vec<&Crate> {
+ self.members.iter().map(|(_, c)| c).collect()
+ }
+}
diff --git a/development/tools/cargo-workspace2/src/models/mod.rs b/development/tools/cargo-workspace2/src/models/mod.rs
new file mode 100644
index 000000000000..759b2703f9f0
--- /dev/null
+++ b/development/tools/cargo-workspace2/src/models/mod.rs
@@ -0,0 +1,50 @@
+//! Collection of cargo workspace data models.
+//!
+//! To start parsing types, construct a `CargoWorkspace`, which you
+//! can then modify with commands found in [`ops`](../ops/index.html).
+
+mod cargo;
+pub use cargo::{CargoCrate, CargoWorkspace};
+
+mod _crate;
+pub use _crate::Crate;
+
+mod publish;
+pub use publish::{MutationSet, PubMutation};
+
+mod graph;
+pub use graph::DepGraph;
+
+pub type CrateId = usize;
+
+use crate::{ops::Op, query::Query};
+use std::path::PathBuf;
+
+/// A fully parsed workspace
+pub struct Workspace {
+ pub root: PathBuf,
+ dgraph: DepGraph,
+}
+
+impl Workspace {
+ /// Create a parsed workspace by passing in the stage1 parse data
+ pub fn process(cws: CargoWorkspace) -> Self {
+ let CargoWorkspace { root, crates } = cws;
+
+ let mut dgraph = DepGraph::new();
+ crates.into_iter().for_each(|cc| dgraph.add_crate(cc));
+ dgraph.finalise();
+
+ Self { root, dgraph }
+ }
+
+ /// Execute a query on this workspace to find crate IDs
+ pub fn query(&self, q: Query) -> Vec<CrateId> {
+ q.execute(&self.dgraph)
+ }
+
+ /// Execute an operation on a set of crates this in workspace
+ pub fn execute(&mut self, op: Op, target: Vec<CrateId>) {
+ op.execute(target, self.root.clone(), &mut self.dgraph)
+ }
+}
diff --git a/development/tools/cargo-workspace2/src/models/publish.rs b/development/tools/cargo-workspace2/src/models/publish.rs
new file mode 100644
index 000000000000..11984017d49f
--- /dev/null
+++ b/development/tools/cargo-workspace2/src/models/publish.rs
@@ -0,0 +1,20 @@
+use crate::models::{Crate, CrateId};
+
+/// A publishing mutation executed on the graph
+pub struct PubMutation {
+ _crate: CrateId,
+ new_version: String,
+}
+
+impl PubMutation {
+ /// Createa new motation from a crate a version string
+ pub fn new(c: &Crate, new_version: String) -> Self {
+ Self {
+ _crate: c.id,
+ new_version,
+ }
+ }
+}
+
+/// A collection of mutations performed in a batch
+pub struct MutationSet {}