Skip to main content

gen_types/
package.rs

1//! Typed [`Package`] — one node in the dependency graph.
2
3use crate::{BuildStep, Dependency, Feature, PackageSource, Registry, Version};
4use serde::{Deserialize, Serialize};
5
6/// Canonical identifier: `(name, version, registry)`. Different
7/// registries can host packages with the same name+version, so the
8/// registry is part of identity.
9#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub struct PackageId {
11    pub name: String,
12    pub version: Version,
13    pub registry: Registry,
14}
15
16impl PackageId {
17    /// Stable string form for diagnostics + filesystem-safe cache
18    /// keys: `<registry>/<name>/<version>`.
19    #[must_use]
20    pub fn dotted(&self) -> String {
21        format!("{}/{}/{}", self.registry.as_str(), self.name, self.version)
22    }
23}
24
25/// One package in the parsed manifest. Adapter-agnostic shape;
26/// every language reduces to this.
27#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
28pub struct Package {
29    pub name: String,
30    pub version: Version,
31    pub source: PackageSource,
32    pub registry: Registry,
33    #[serde(default)]
34    pub dependencies: Vec<Dependency>,
35    #[serde(default)]
36    pub features: Vec<Feature>,
37    #[serde(default)]
38    pub build_steps: Vec<BuildStep>,
39    /// SPDX license string when available.
40    #[serde(default)]
41    pub license: Option<String>,
42    /// Free-text description from the manifest.
43    #[serde(default)]
44    pub description: Option<String>,
45    /// Authors / publisher line.
46    #[serde(default)]
47    pub authors: Vec<String>,
48    /// Homepage URL.
49    #[serde(default)]
50    pub homepage: Option<String>,
51    /// Source repository URL.
52    #[serde(default)]
53    pub repository: Option<String>,
54}
55
56impl Package {
57    /// Convenience: canonical identity of this package.
58    #[must_use]
59    pub fn id(&self) -> PackageId {
60        PackageId {
61            name: self.name.clone(),
62            version: self.version.clone(),
63            registry: self.registry.clone(),
64        }
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71    use crate::ConstraintSpec;
72
73    #[test]
74    fn id_dotted_form_is_stable() {
75        let p = Package {
76            name: "serde".into(),
77            version: Version::new(1, 0, 228),
78            source: PackageSource::Registry {
79                registry: Registry::CratesIo,
80                registry_name: "serde".into(),
81                integrity_hash: None,
82            },
83            registry: Registry::CratesIo,
84            dependencies: vec![],
85            features: vec![],
86            build_steps: vec![],
87            license: Some("MIT OR Apache-2.0".into()),
88            description: None,
89            authors: vec![],
90            homepage: None,
91            repository: None,
92        };
93        assert_eq!(p.id().dotted(), "crates-io/serde/1.0.228");
94    }
95
96    #[test]
97    fn package_id_used_as_hash_key() {
98        let id_a = PackageId {
99            name: "serde".into(),
100            version: Version::new(1, 0, 0),
101            registry: Registry::CratesIo,
102        };
103        let id_b = id_a.clone();
104        let mut m = std::collections::HashMap::new();
105        m.insert(id_a, "x");
106        assert_eq!(m.get(&id_b), Some(&"x"));
107    }
108
109    #[test]
110    fn round_trip_through_serde() {
111        let p = Package {
112            name: "serde".into(),
113            version: Version::new(1, 0, 228),
114            source: PackageSource::Registry {
115                registry: Registry::CratesIo,
116                registry_name: "serde".into(),
117                integrity_hash: Some("sha256:abc".into()),
118            },
119            registry: Registry::CratesIo,
120            dependencies: vec![Dependency {
121                name: "serde_derive".into(),
122                constraint: crate::VersionConstraint::from_spec(ConstraintSpec::Caret(
123                    Version::new(1, 0, 228),
124                )),
125                kind: crate::DependencyKind::Direct,
126                features_enabled: vec![],
127                default_features: true,
128                target_predicate: None,
129                source_override: None,
130            }],
131            features: vec![],
132            build_steps: vec![],
133            license: Some("MIT".into()),
134            description: Some("Serialization framework".into()),
135            authors: vec![],
136            homepage: None,
137            repository: Some("https://github.com/serde-rs/serde".into()),
138        };
139        let j = serde_json::to_string(&p).unwrap();
140        let parsed: Package = serde_json::from_str(&j).unwrap();
141        assert_eq!(p, parsed);
142    }
143}