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#[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#[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#[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#[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#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
150#[serde(untagged)]
151pub enum Bin {
152 Str(String),
153 Hash(HashMap<String, String>),
154}
155
156#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
158pub struct NpmUser {
159 pub name: String,
160 pub email: Option<String>,
161}
162
163#[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#[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#[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}