Skip to main content

check_updates/
package.rs

1use crate::Purl;
2use semver::VersionReq;
3use std::{
4    borrow::Cow,
5    collections::HashMap,
6    fmt,
7    path::{Path, PathBuf},
8};
9
10/// A unit of package management, such as a project, a workspace, or a global environment
11#[derive(Debug, Clone, PartialEq, Eq, Hash)]
12pub enum Unit {
13    /// A single project manifest (e.g. `crates/foo/Cargo.toml` `[dependencies]`)
14    Project { manifest: PathBuf, name: String },
15    /// The workspace root manifest (e.g. `Cargo.toml` `[workspace.dependencies]`)
16    Workspace { manifest: PathBuf },
17    /// A globally installed package
18    Global,
19}
20
21impl Ord for Unit {
22    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
23        match (self, other) {
24            (Unit::Workspace { .. }, Unit::Workspace { .. }) => std::cmp::Ordering::Equal,
25            (Unit::Workspace { .. }, _) => std::cmp::Ordering::Less,
26            (_, Unit::Workspace { .. }) => std::cmp::Ordering::Greater,
27            (Unit::Global, Unit::Global) => std::cmp::Ordering::Equal,
28            (Unit::Global, _) => std::cmp::Ordering::Greater,
29            (_, Unit::Global) => std::cmp::Ordering::Less,
30            _ => self.name().cmp(&other.name()),
31        }
32    }
33}
34
35impl PartialOrd for Unit {
36    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
37        Some(self.cmp(other))
38    }
39}
40
41impl Unit {
42    /// Returns the path to the manifest file, if this unit has one.
43    pub fn path(&self) -> Option<&Path> {
44        match self {
45            Unit::Project { manifest, .. } | Unit::Workspace { manifest, .. } => Some(manifest),
46            Unit::Global => None,
47        }
48    }
49
50    /// Returns the name of the unit, if it has one.
51    pub fn name(&self) -> Cow<'_, str> {
52        match self {
53            Unit::Project { name, .. } => name.into(),
54            Unit::Workspace { manifest, .. } => manifest
55                .parent()
56                .and_then(|p| p.file_name())
57                .and_then(|n| n.to_str())
58                .map(|n| format!("{n} (workspace)"))
59                .unwrap_or("workspace".into())
60                .into(),
61            Unit::Global => "global".into(),
62        }
63    }
64}
65
66/// The kind of dependency
67#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
68pub enum DepKind {
69    Normal,
70    Dev,
71    Build,
72}
73
74impl fmt::Display for DepKind {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        match self {
77            DepKind::Normal => write!(f, "dependencies"),
78            DepKind::Dev => write!(f, "dev-dependencies"),
79            DepKind::Build => write!(f, "build-dependencies"),
80        }
81    }
82}
83
84#[derive(Debug, Clone)]
85pub struct Usage {
86    pub unit: Unit,
87    pub req: VersionReq,
88    pub kind: DepKind,
89    pub rename: Option<String>,
90}
91
92#[derive(Debug, Clone)]
93pub struct Package {
94    pub purl: Purl,
95    pub usages: Vec<Usage>,
96    pub versions: Vec<PackageVersion>,
97    pub repository: Option<String>,
98    pub homepage: Option<String>,
99}
100
101pub type Packages = HashMap<Unit, Vec<(VersionReq, DepKind, Package)>>;
102
103#[derive(Debug, Clone)]
104pub struct PackageVersion {
105    pub version: semver::Version,
106    pub yanked: bool,
107    pub features: HashMap<String, Vec<String>>,
108    pub rust_version: Option<semver::Version>,
109}