Skip to main content

chaste_types/
dependency.rs

1// SPDX-FileCopyrightText: 2024 The Chaste Authors
2// SPDX-License-Identifier: Apache-2.0 OR BSD-2-Clause
3
4use crate::name::{PackageName, PackageNameBorrowed};
5use crate::package::PackageID;
6use crate::svs::SourceVersionSpecifier;
7
8#[derive(Debug, PartialEq, Eq, Clone, Copy)]
9#[non_exhaustive]
10/// The type of a [Dependency].
11pub enum DependencyKind {
12    /// Either defined as a regular dependency (in the `"dependencies"` field of package.json),
13    /// or, in some cases (implementation-dependent), as any other kind that is not [`DependencyKind::DevDependency`].
14    Dependency,
15    /// Defined in `"devDependencies"`.
16    DevDependency,
17    /// Defined in `"peerDependencies"`. If known to be [defined as optional],
18    /// it will be marked as [`DependencyKind::OptionalPeerDependency`] instead.
19    ///
20    /// [defined as optional]: https://docs.npmjs.com/cli/v11/configuring-npm/package-json#peerdependenciesmeta
21    PeerDependency,
22    /// Defined in `"optionalDependencies"`.
23    OptionalDependency,
24    /// Defined in `"peerDependency"` and known to be [defined as optional].
25    ///
26    /// [defined as optional]: https://docs.npmjs.com/cli/v11/configuring-npm/package-json#peerdependenciesmeta
27    OptionalPeerDependency,
28}
29
30impl DependencyKind {
31    pub fn is_prod(self) -> bool {
32        !matches!(self, DependencyKind::DevDependency)
33    }
34    pub fn is_dev(self) -> bool {
35        matches!(self, DependencyKind::DevDependency)
36    }
37    pub fn is_optional(self) -> bool {
38        matches!(
39            self,
40            DependencyKind::OptionalDependency | DependencyKind::OptionalPeerDependency
41        )
42    }
43    pub fn is_peer(self) -> bool {
44        matches!(
45            self,
46            DependencyKind::PeerDependency | DependencyKind::OptionalPeerDependency
47        )
48    }
49}
50
51#[derive(Debug, Clone)]
52/// A relation of dependency between 2 [`crate::Package`]s
53pub struct Dependency {
54    /// Type of dependency
55    pub kind: DependencyKind,
56    /// ID of the package that defined this dependency
57    pub from: PackageID,
58    /// ID of the package that is being depended on
59    pub on: PackageID,
60    alias_name: Option<PackageName>,
61    svs: Option<SourceVersionSpecifier>,
62}
63
64impl Dependency {
65    /// The source and version range chosen by the dependent package.
66    ///
67    /// # Example
68    /// ```
69    /// # use chaste_types::{ChastefileBuilder, DependencyBuilder, DependencyKind, PackageBuilder, PackageName, SourceVersionSpecifier};
70    /// # let mut chastefile_builder = ChastefileBuilder::new();
71    /// # let root_pid = chastefile_builder.add_package(
72    /// #     PackageBuilder::new(None, None).build().unwrap(),
73    /// # ).unwrap();
74    /// # chastefile_builder.set_root_package_id(root_pid);
75    /// # let lodash_pid = chastefile_builder.add_package(
76    /// #     PackageBuilder::new(
77    /// #         Some(PackageName::new("lodash".to_string()).unwrap()),
78    /// #         Some("4.2.1".to_string()),
79    /// #     ).build().unwrap(),
80    /// # ).unwrap();
81    /// # let mut dependency_builder = DependencyBuilder::new(DependencyKind::Dependency, root_pid, lodash_pid);
82    /// # dependency_builder.svs(SourceVersionSpecifier::new("^4.2.0".to_string()).unwrap());
83    /// # chastefile_builder.add_dependency(dependency_builder.build());
84    /// # let chastefile = chastefile_builder.build().unwrap();
85    /// # let dependencies = chastefile.package_dependencies(root_pid);
86    /// # let dependency = dependencies.first().unwrap();
87    /// let svs = dependency.svs().unwrap();
88    /// assert_eq!(svs, "^4.2.0");
89    /// assert!(svs.is_npm());
90    /// ```
91    pub fn svs(&self) -> Option<&SourceVersionSpecifier> {
92        self.svs.as_ref()
93    }
94
95    /// If the dependency is from npm, aliasing a package with a different name,
96    /// this represents the name under which it's aliased, e.g. if package.json defines
97    /// the dependency as `"lodash": "npm:@chastelock/lodash-fork@^4.0.0"`,
98    /// [`crate::Package::name`] will be `@chastelock/lodash-fork`, but [`crate::Dependency::alias_name`]
99    /// will be `lodash`. (If dependency is not from npm, the behavior is undefined.)
100    ///
101    /// # Example
102    /// ```
103    /// # use chaste_types::{ChastefileBuilder, DependencyBuilder, DependencyKind, PackageBuilder, PackageName};
104    /// # let mut chastefile_builder = ChastefileBuilder::new();
105    /// # let root_pid = chastefile_builder.add_package(
106    /// #     PackageBuilder::new(None, None).build().unwrap(),
107    /// # ).unwrap();
108    /// # chastefile_builder.set_root_package_id(root_pid);
109    /// # let lodash_pid = chastefile_builder.add_package(
110    /// #     PackageBuilder::new(
111    /// #         Some(PackageName::new("@chastelock/lodash-fork".to_string()).unwrap()),
112    /// #         Some("4.0.0".to_string()),
113    /// #     ).build().unwrap(),
114    /// # ).unwrap();
115    /// # let mut dependency_builder = DependencyBuilder::new(DependencyKind::Dependency, root_pid, lodash_pid);
116    /// # dependency_builder.alias_name(PackageName::new("lodash".to_string()).unwrap());
117    /// # chastefile_builder.add_dependency(dependency_builder.build());
118    /// # let chastefile = chastefile_builder.build().unwrap();
119    /// let dependencies = chastefile.package_dependencies(root_pid);
120    /// let dependency = dependencies.first().unwrap();
121    /// assert_eq!(chastefile.package(dependency.on).name().unwrap(), "@chastelock/lodash-fork");
122    /// assert_eq!(dependency.alias_name().unwrap(), "lodash");
123    /// ```
124    pub fn alias_name<'a>(&'a self) -> Option<PackageNameBorrowed<'a>> {
125        self.alias_name.as_ref().map(|a| a.as_borrowed())
126    }
127}
128
129pub struct DependencyBuilder {
130    kind: DependencyKind,
131    of: PackageID,
132    on: PackageID,
133    alias_name: Option<PackageName>,
134    svs: Option<SourceVersionSpecifier>,
135}
136
137impl DependencyBuilder {
138    pub fn new(kind: DependencyKind, of: PackageID, on: PackageID) -> DependencyBuilder {
139        DependencyBuilder {
140            kind,
141            of,
142            on,
143            alias_name: None,
144            svs: None,
145        }
146    }
147
148    pub fn alias_name(&mut self, alias_name: PackageName) {
149        self.alias_name = Some(alias_name);
150    }
151
152    pub fn svs(&mut self, svs: SourceVersionSpecifier) {
153        self.svs = Some(svs);
154    }
155
156    pub fn build(self) -> Dependency {
157        Dependency {
158            kind: self.kind,
159            from: self.of,
160            on: self.on,
161            alias_name: self.alias_name,
162            svs: self.svs,
163        }
164    }
165}