oro_common/
packument.rs

1use derive_builder::Builder;
2use node_semver::Version;
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4use serde_json::Value;
5use std::{collections::HashMap, fmt::Display};
6use url::Url;
7
8use crate::{CorgiManifest, Manifest, PersonField};
9
10/// A serializable representation of a Packument -- the toplevel metadata
11/// object containing information about package versions, dist-tags, etc.
12///
13/// This version is a reduced-size packument that only contains fields from
14/// "Corgi" packuments (or will only (de)serialize those specific fields).
15#[derive(Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)]
16pub struct CorgiPackument {
17    #[serde(default)]
18    pub versions: HashMap<Version, CorgiVersionMetadata>,
19    #[serde(default, rename = "dist-tags")]
20    pub tags: HashMap<String, Version>,
21}
22
23/// A serializable representation of a Packument -- the toplevel metadata
24/// object containing information about package versions, dist-tags, etc.
25#[derive(Builder, Default, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
26pub struct Packument {
27    #[serde(default)]
28    pub versions: HashMap<Version, VersionMetadata>,
29    #[serde(default)]
30    pub time: HashMap<String, String>,
31    #[serde(default, rename = "dist-tags")]
32    pub tags: HashMap<String, Version>,
33    #[serde(flatten)]
34    pub rest: HashMap<String, Value>,
35}
36
37impl From<CorgiPackument> for Packument {
38    fn from(value: CorgiPackument) -> Self {
39        Packument {
40            versions: value
41                .versions
42                .into_iter()
43                .map(|(k, v)| (k, v.into()))
44                .collect(),
45            tags: value.tags,
46            ..Default::default()
47        }
48    }
49}
50
51impl From<Packument> for CorgiPackument {
52    fn from(value: Packument) -> Self {
53        CorgiPackument {
54            versions: value
55                .versions
56                .into_iter()
57                .map(|(k, v)| (k, v.into()))
58                .collect(),
59            tags: value.tags,
60        }
61    }
62}
63
64/// A manifest for an individual package version.
65///
66/// This version is a reduced-size VersionMetadata that only contains fields
67/// from "Corgi" packuments (or will only (de)serialize those specific
68/// fields).
69#[derive(Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)]
70pub struct CorgiVersionMetadata {
71    #[serde(default)]
72    pub dist: CorgiDist,
73    #[serde(rename = "_hasShrinkwrap", skip_serializing_if = "Option::is_none")]
74    pub has_shrinkwrap: Option<bool>,
75    #[serde(flatten)]
76    pub manifest: CorgiManifest,
77    #[serde(
78        default,
79        deserialize_with = "deserialize_deprecation_info",
80        skip_serializing_if = "Option::is_none"
81    )]
82    pub deprecated: Option<DeprecationInfo>,
83}
84
85/// A manifest for an individual package version.
86#[derive(Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)]
87pub struct VersionMetadata {
88    #[serde(default, skip_serializing_if = "Vec::is_empty")]
89    pub maintainers: Vec<PersonField>,
90    #[serde(rename = "_npmUser", skip_serializing_if = "Option::is_none")]
91    pub npm_user: Option<NpmUser>,
92    #[serde(default)]
93    pub dist: Dist,
94    #[serde(rename = "_hasShrinkwrap", skip_serializing_if = "Option::is_none")]
95    pub has_shrinkwrap: Option<bool>,
96    #[serde(
97        default,
98        deserialize_with = "deserialize_deprecation_info",
99        skip_serializing_if = "Option::is_none"
100    )]
101    pub deprecated: Option<DeprecationInfo>,
102
103    #[serde(flatten)]
104    pub manifest: Manifest,
105}
106
107impl From<CorgiVersionMetadata> for VersionMetadata {
108    fn from(value: CorgiVersionMetadata) -> Self {
109        VersionMetadata {
110            dist: value.dist.into(),
111            has_shrinkwrap: value.has_shrinkwrap,
112            manifest: value.manifest.into(),
113            ..Default::default()
114        }
115    }
116}
117
118impl From<VersionMetadata> for CorgiVersionMetadata {
119    fn from(value: VersionMetadata) -> Self {
120        CorgiVersionMetadata {
121            dist: value.dist.into(),
122            has_shrinkwrap: value.has_shrinkwrap,
123            manifest: value.manifest.into(),
124            deprecated: value.deprecated,
125        }
126    }
127}
128
129impl From<CorgiVersionMetadata> for CorgiManifest {
130    fn from(value: CorgiVersionMetadata) -> Self {
131        value.manifest
132    }
133}
134
135impl From<VersionMetadata> for Manifest {
136    fn from(value: VersionMetadata) -> Self {
137        value.manifest
138    }
139}
140
141#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
142#[serde(untagged)]
143enum StringOrBool {
144    String(String),
145    Bool(bool),
146}
147
148/// Representation for the `bin` field in package manifests.
149#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
150#[serde(untagged)]
151pub enum Bin {
152    Str(String),
153    Hash(HashMap<String, String>),
154}
155
156/// Represents a human!
157#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
158pub struct NpmUser {
159    pub name: String,
160    pub email: Option<String>,
161}
162
163/// Represents the deprecation state of a package.
164#[derive(Clone, Debug, PartialEq, Eq)]
165pub enum DeprecationInfo {
166    Reason(String),
167    UnknownReason,
168}
169
170impl Display for DeprecationInfo {
171    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172        match self {
173            Self::Reason(s) => write!(f, "{:?}", s),
174            Self::UnknownReason => write!(f, "Unknown Reason"),
175        }
176    }
177}
178
179impl Serialize for DeprecationInfo {
180    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
181    where
182        S: Serializer,
183    {
184        match self {
185            DeprecationInfo::Reason(s) => serializer.serialize_str(s),
186            DeprecationInfo::UnknownReason => serializer.serialize_bool(true),
187        }
188    }
189}
190
191fn deserialize_deprecation_info<'de, D>(
192    deserializer: D,
193) -> std::result::Result<Option<DeprecationInfo>, D::Error>
194where
195    D: Deserializer<'de>,
196{
197    let val: StringOrBool = Deserialize::deserialize(deserializer)?;
198    Ok(match val {
199        StringOrBool::String(s) => Some(DeprecationInfo::Reason(s)),
200        StringOrBool::Bool(b) => {
201            if b {
202                Some(DeprecationInfo::UnknownReason)
203            } else {
204                None
205            }
206        }
207    })
208}
209
210/// Distribution information for a particular package version.
211///
212/// This version is a reduced-size CorgiDist that only contains fields from
213/// "Corgi" packuments (or will only (de)serialize those specific fields).
214#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
215pub struct CorgiDist {
216    pub shasum: Option<String>,
217    pub tarball: Option<Url>,
218    pub integrity: Option<String>,
219    #[serde(rename = "npm-signature")]
220    pub npm_signature: Option<String>,
221}
222
223/// Distribution information for a particular package version.
224#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
225pub struct Dist {
226    pub shasum: Option<String>,
227    pub tarball: Option<Url>,
228
229    pub integrity: Option<String>,
230    #[serde(rename = "fileCount")]
231    pub file_count: Option<usize>,
232    #[serde(rename = "unpackedSize")]
233    pub unpacked_size: Option<usize>,
234    #[serde(rename = "npm-signature")]
235    pub npm_signature: Option<String>,
236
237    #[serde(flatten)]
238    pub rest: HashMap<String, Value>,
239}
240
241impl From<CorgiDist> for Dist {
242    fn from(value: CorgiDist) -> Self {
243        Dist {
244            shasum: value.shasum,
245            tarball: value.tarball,
246            integrity: value.integrity,
247            npm_signature: value.npm_signature,
248            ..Default::default()
249        }
250    }
251}
252
253impl From<Dist> for CorgiDist {
254    fn from(value: Dist) -> Self {
255        CorgiDist {
256            shasum: value.shasum,
257            tarball: value.tarball,
258            integrity: value.integrity,
259            npm_signature: value.npm_signature,
260        }
261    }
262}