1#![warn(
8 missing_copy_implementations,
9 missing_debug_implementations,
10 missing_docs,
11 unsafe_code
12)]
13
14use serde::{Deserialize, Serialize};
15use serde_json::Value;
16use std::{collections::BTreeMap, fmt, fs, io::Read, path::Path, str::FromStr};
17
18mod error;
19
20pub use crate::error::Error;
21
22pub type BinSet = BTreeMap<String, String>;
24pub type DepsSet = BTreeMap<String, String>;
26pub type EnginesSet = BTreeMap<String, String>;
28pub type ScriptsSet = BTreeMap<String, String>;
30
31pub type Result<T> = std::result::Result<T, Error>;
33
34#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
36pub struct Bug {
37 pub email: Option<String>,
39 pub url: Option<String>,
41}
42
43impl Bug {
44 pub fn new() -> Self {
46 Self::default()
47 }
48
49 pub fn with(email: Option<String>, url: Option<String>) -> Self {
51 Self { email, url }
52 }
53}
54
55#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
57pub struct Person {
58 pub name: String,
60 pub email: Option<String>,
62 pub url: Option<String>,
64}
65
66impl Person {
67 pub fn new() -> Self {
69 Self::default()
70 }
71
72 pub fn with_name(name: String) -> Self {
74 Self {
75 name,
76 ..Default::default()
77 }
78 }
79
80 pub fn with(name: String, email: Option<String>, url: Option<String>) -> Self {
82 Self { name, email, url }
83 }
84}
85
86impl fmt::Display for Person {
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 match (&self.name, self.email.as_ref(), self.url.as_ref()) {
89 (name, Some(email), None) => write!(f, "{} <{}>", name, email),
90 (name, None, Some(url)) => write!(f, "{} ({})", name, url),
91 (name, None, None) => write!(f, "{}", name),
92 (name, Some(email), Some(url)) => write!(f, "{} <{}> ({})", name, email, url),
93 }
94 }
95}
96
97#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
99#[serde(untagged)]
100pub enum PersonReference {
101 Short(String),
105 Full(Person),
110}
111
112#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
114#[serde(untagged)]
115pub enum ManReference {
116 Single(String),
118 Multiple(Vec<String>),
120}
121
122#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
124pub struct Repository {
125 pub r#type: String,
127 pub url: String,
129 pub directory: Option<String>,
131}
132
133#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
135#[serde(untagged)]
136pub enum RepositoryReference {
137 Short(String),
139 Full(Repository),
141}
142
143#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
145#[serde(rename_all = "camelCase")]
146pub struct Package {
147 pub name: String,
149 pub version: String,
151 pub description: Option<String>,
153 #[serde(default)]
155 pub keywords: Vec<String>,
156 pub homepage: Option<String>,
158 pub bugs: Option<Bug>,
160 pub license: Option<String>,
162 pub author: Option<PersonReference>,
164 #[serde(default)]
166 pub contributors: Vec<PersonReference>,
167 #[serde(default)]
170 pub files: Vec<String>,
171 pub main: Option<String>,
173 pub browser: Option<String>,
179 #[serde(default)]
181 pub bin: BinSet,
182 pub man: Option<ManReference>,
184 pub repository: Option<RepositoryReference>,
187 #[serde(default)]
189 pub scripts: ScriptsSet,
190 #[serde(default)]
192 pub dependencies: DepsSet,
193 #[serde(default)]
195 pub dev_dependencies: DepsSet,
196 #[serde(default)]
198 pub peer_dependencies: DepsSet,
199 #[serde(default)]
201 pub bundled_dependencies: DepsSet,
202 #[serde(default)]
204 pub optional_dependencies: DepsSet,
205 #[serde(default)]
207 pub engines: EnginesSet,
208 #[serde(default)]
210 pub private: bool,
211 #[serde(default)]
213 pub os: Vec<String>,
214 #[serde(default)]
216 pub cpu: Vec<String>,
217 pub config: Option<Value>,
219 #[serde(flatten)]
222 pub others: BTreeMap<String, Value>,
223}
224
225impl Package {
226 pub fn new() -> Self {
228 Self::default()
229 }
230
231 pub fn from_path(path: impl AsRef<Path>) -> Result<Self> {
233 let content = fs::read(path.as_ref())?;
234 Self::from_slice(content.as_slice())
235 }
236
237 pub fn from_reader<R: Read>(r: R) -> Result<Self> {
239 Ok(serde_json::from_reader(r)?)
240 }
241
242 pub fn from_slice(v: &[u8]) -> Result<Self> {
244 Ok(serde_json::from_slice(v)?)
245 }
246}
247
248impl FromStr for Package {
249 type Err = Error;
250
251 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
253 Ok(serde_json::from_str(s)?)
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use super::Person;
260
261 #[test]
262 fn test_person_display() {
263 let person = Person {
264 name: "John Doe".to_string(),
265 email: Some("john@doe.dev".to_string()),
266 url: Some("https://john.doe.dev".to_string()),
267 };
268 let expected = "John Doe <john@doe.dev> (https://john.doe.dev)";
269 assert_eq!(expected, person.to_string());
270 }
271}