Skip to main content

gen_types/
workspace.rs

1//! Typed [`Workspace`] — multi-package project root.
2//!
3//! Multi-package projects (Cargo `[workspace]`, npm workspaces, pnpm
4//! workspace, Composer monorepo, Bundler `Gemfile`-with-multiple-
5//! gemspecs, …) all reduce to: a root + N member-package paths +
6//! shared workspace-level metadata.
7
8use serde::{Deserialize, Serialize};
9use std::path::PathBuf;
10
11/// Typed workspace root + the IDs of every member package contained
12/// in it.
13#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
14pub struct Workspace {
15    pub root: PathBuf,
16    /// Member packages — relative paths from `root`.
17    pub members: Vec<PathBuf>,
18    /// Adapter that produced this workspace (`"cargo"`, `"npm"`, …).
19    pub adapter: String,
20    /// Shared workspace-level metadata that members inherit when
21    /// they don't override it (Cargo `workspace.package`,
22    /// Composer monorepo, …).
23    #[serde(default)]
24    pub shared_metadata: indexmap::IndexMap<String, String>,
25}
26
27impl Workspace {
28    /// Convenience constructor for the common case (no shared
29    /// metadata).
30    #[must_use]
31    pub fn new(
32        root: impl Into<PathBuf>,
33        members: Vec<PathBuf>,
34        adapter: impl Into<String>,
35    ) -> Self {
36        Self {
37            root: root.into(),
38            members,
39            adapter: adapter.into(),
40            shared_metadata: indexmap::IndexMap::new(),
41        }
42    }
43
44    /// Single-package workspace shape (e.g. a single-crate cargo
45    /// repo). Members is `[root]`.
46    #[must_use]
47    pub fn single_package(root: impl Into<PathBuf>, adapter: impl Into<String>) -> Self {
48        let root = root.into();
49        Self::new(root.clone(), vec![root], adapter)
50    }
51
52    /// True if this workspace has multiple member packages.
53    #[must_use]
54    pub fn is_multi_package(&self) -> bool {
55        self.members.len() > 1
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62
63    #[test]
64    fn single_package_workspace_isnt_multi() {
65        let w = Workspace::single_package("/x", "cargo");
66        assert!(!w.is_multi_package());
67    }
68
69    #[test]
70    fn multi_member_workspace_is_multi() {
71        let w = Workspace::new(
72            "/x",
73            vec![PathBuf::from("a"), PathBuf::from("b")],
74            "cargo",
75        );
76        assert!(w.is_multi_package());
77    }
78
79    #[test]
80    fn round_trip_through_serde() {
81        let w = Workspace::new(
82            "/x",
83            vec![PathBuf::from("a"), PathBuf::from("b")],
84            "cargo",
85        );
86        let j = serde_json::to_string(&w).unwrap();
87        let parsed: Workspace = serde_json::from_str(&j).unwrap();
88        assert_eq!(w, parsed);
89    }
90}