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}