Skip to main content

distrib_ruby/
gemspec.rs

1// This is free and unencumbered software released into the public domain.
2
3use alloc::{
4    collections::BTreeMap,
5    string::{String, ToString},
6    vec,
7    vec::Vec,
8};
9use indexmap::IndexMap;
10
11pub type Gemspec = Specification;
12
13#[cfg(all(feature = "alloc", feature = "serde"))]
14pub type Map<K, V = serde_json::Value> = IndexMap<K, V>;
15
16/// The package information for a gem, typically defined in a `.gemspec` file.
17///
18/// This is the tag `!ruby/object:Gem::Specification` in YAML.
19///
20/// Note that we (pedantically) distinguish between `Option<Vec<T>` and
21/// `Vec<T>` so as to indicate whether the property was unset or set to empty
22/// the input specification file.
23///
24/// See: <https://guides.rubygems.org/specification-reference/>
25#[cfg_attr(feature = "serde", serde_with::serde_as)]
26#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
27#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
28#[cfg_attr(feature = "serde", serde(default))]
29pub struct Specification<T = Metadata> {
30    /// See: <https://guides.rubygems.org/specification-reference/#authors=>
31    #[cfg_attr(feature = "serde", serde(alias = "author"))]
32    #[cfg_attr(feature = "serde", serde_as(as = "OneOrMany<_, PreferMany>"))]
33    pub authors: Vec<String>,
34
35    /// See: <https://guides.rubygems.org/specification-reference/#bindir>
36    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
37    pub bindir: Option<String>,
38
39    /// See: <https://guides.rubygems.org/specification-reference/#cert_chain>
40    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
41    pub cert_chain: Option<Vec<String>>,
42
43    /// See: <https://guides.rubygems.org/specification-reference/#add_dependency>
44    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
45    pub dependencies: Option<Vec<Dependency>>,
46
47    /// See: <https://guides.rubygems.org/specification-reference/#description>
48    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
49    pub description: Option<String>,
50
51    /// See: <https://guides.rubygems.org/specification-reference/#email>
52    #[cfg_attr(
53        feature = "serde",
54        serde(alias = "emails", skip_serializing_if = "Vec::is_empty")
55    )]
56    #[cfg_attr(feature = "serde", serde_as(as = "OneOrMany<_, PreferOne>"))]
57    pub email: Vec<String>, // TODO: Option<Vec<String>>
58
59    /// See: <https://guides.rubygems.org/specification-reference/#executables>
60    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
61    pub executables: Option<Vec<String>>,
62
63    /// See: <https://guides.rubygems.org/specification-reference/#extensions>
64    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
65    pub extensions: Option<Vec<String>>,
66
67    /// See: <https://guides.rubygems.org/specification-reference/#extensions_dir>
68    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
69    pub extensions_dir: Option<String>,
70
71    /// See: <https://guides.rubygems.org/specification-reference/#extra_rdoc_files>
72    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
73    pub extra_rdoc_files: Option<Vec<String>>,
74
75    /// See: <https://guides.rubygems.org/specification-reference/#files>
76    pub files: Vec<String>,
77
78    /// See: <https://guides.rubygems.org/specification-reference/#homepage>
79    pub homepage: Option<String>,
80
81    /// See: <https://guides.rubygems.org/specification-reference/#licenses=>
82    #[cfg_attr(
83        feature = "serde",
84        serde(alias = "license", skip_serializing_if = "Vec::is_empty")
85    )]
86    #[cfg_attr(feature = "serde", serde_as(as = "OneOrMany<_, PreferMany>"))]
87    pub licenses: Vec<String>, // TODO: Option<Vec<String>>
88
89    /// See: <https://guides.rubygems.org/specification-reference/#metadata>
90    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
91    pub metadata: Option<T>,
92
93    /// See: <https://guides.rubygems.org/specification-reference/#name>
94    pub name: String,
95
96    /// See: <https://guides.rubygems.org/specification-reference/#platform=>
97    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
98    pub platform: Option<Platform>,
99
100    /// See: <https://guides.rubygems.org/specification-reference/#post_install_message>
101    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
102    pub post_install_message: Option<String>,
103
104    /// See: <https://guides.rubygems.org/specification-reference/#rdoc_options>
105    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
106    pub rdoc_options: Option<Vec<String>>,
107
108    /// See: <https://guides.rubygems.org/specification-reference/#require_paths=>
109    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
110    pub require_paths: Option<Vec<String>>,
111
112    /// See: <https://guides.rubygems.org/specification-reference/#required_ruby_version>
113    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
114    pub required_ruby_version: Option<Requirement>,
115
116    /// See: <https://guides.rubygems.org/specification-reference/#required_rubygems_version>
117    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
118    pub required_rubygems_version: Option<Requirement>,
119
120    /// See: <https://guides.rubygems.org/specification-reference/#requirements>
121    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
122    pub requirements: Option<Vec<String>>,
123
124    /// See: <https://guides.rubygems.org/specification-reference/#rubygems_version>
125    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
126    pub rubygems_version: Option<String>,
127
128    /// See: <https://guides.rubygems.org/specification-reference/#signing_key>
129    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
130    pub signing_key: Option<String>,
131
132    /// See: <https://guides.rubygems.org/specification-reference/#summary>
133    pub summary: String,
134
135    /// See: <https://guides.rubygems.org/specification-reference/#version>
136    pub version: Version,
137}
138
139#[cfg(all(feature = "serde"))]
140impl<T: serde::Serialize> Specification<T> {
141    pub fn try_to_string(&self) -> Result<String, serde_norway::Error> {
142        serde_norway::to_string(self)
143    }
144}
145
146/// !ruby/object:Gem::Dependency
147#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
148#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
149#[cfg_attr(feature = "serde", serde(default))]
150pub struct Dependency {
151    pub name: String,
152    pub r#type: String,
153    pub prerelease: bool,
154    pub requirement: Requirement,
155    pub version_requirements: Requirement,
156}
157
158impl Dependency {
159    pub fn development(name: impl Into<String>, requirement: (&str, &str)) -> Self {
160        let requirement = Requirement::from(requirement);
161        Self::new(name, "development", false, requirement.clone(), requirement)
162    }
163
164    pub fn new(
165        name: impl Into<String>,
166        r#type: impl Into<String>,
167        prerelease: bool,
168        requirement: Requirement,
169        version_requirements: Requirement,
170    ) -> Self {
171        Self {
172            name: name.into(),
173            r#type: r#type.into(),
174            prerelease,
175            requirement,
176            version_requirements,
177        }
178    }
179}
180
181/// `!ruby/object:Gem::Requirement`
182#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
183#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
184#[cfg_attr(feature = "serde", serde(default))]
185pub struct Requirement {
186    pub requirements: Vec<(String, Version)>,
187}
188
189impl From<(&str, &str)> for Requirement {
190    fn from((input0, input1): (&str, &str)) -> Self {
191        From::from((input0.to_string(), input1.to_string()))
192    }
193}
194
195impl From<(String, String)> for Requirement {
196    fn from((input0, input1): (String, String)) -> Self {
197        From::from((input0, Version::from(input1)))
198    }
199}
200
201impl From<(String, Version)> for Requirement {
202    fn from((input0, input1): (String, Version)) -> Self {
203        Self {
204            requirements: vec![(input0, input1)],
205        }
206    }
207}
208
209/// `!ruby/object:Gem::Version`
210#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
211#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
212#[cfg_attr(feature = "serde", serde(default))]
213pub struct Version {
214    pub version: String,
215}
216
217impl From<String> for Version {
218    fn from(version: String) -> Self {
219        Self { version }
220    }
221}
222
223#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
224#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
225#[cfg_attr(
226    feature = "serde",
227    serde(rename_all(serialize = "lowercase", deserialize = "lowercase"))
228)]
229pub enum Platform {
230    #[default]
231    Ruby,
232    Current,
233    #[cfg_attr(feature = "serde", serde(untagged))]
234    Other(String),
235}
236
237/// See: <https://guides.rubygems.org/specification-reference/#metadata>
238#[derive(Clone, Debug, Default, Eq, PartialEq)]
239#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
240#[cfg_attr(feature = "serde", serde(default))]
241pub struct Metadata {
242    /// See: <https://guides.rubygems.org/specification-reference/#metadata>
243    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
244    pub bug_tracker_uri: Option<String>,
245
246    /// See: <https://guides.rubygems.org/specification-reference/#metadata>
247    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
248    pub changelog_uri: Option<String>,
249
250    /// See: <https://guides.rubygems.org/specification-reference/#metadata>
251    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
252    pub documentation_uri: Option<String>,
253
254    /// See: <https://guides.rubygems.org/specification-reference/#metadata>
255    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
256    pub funding_uri: Option<String>,
257
258    /// See: <https://guides.rubygems.org/specification-reference/#metadata>
259    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
260    pub homepage_uri: Option<String>,
261
262    /// See: <https://guides.rubygems.org/specification-reference/#metadata>
263    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
264    pub mailing_list_uri: Option<String>,
265
266    /// See: <https://guides.rubygems.org/specification-reference/#metadata>
267    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
268    pub source_code_uri: Option<String>,
269
270    /// See: <https://guides.rubygems.org/specification-reference/#metadata>
271    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
272    pub wiki_uri: Option<String>,
273
274    #[cfg(all(feature = "alloc", feature = "serde"))]
275    #[cfg_attr(
276        feature = "serde",
277        serde(flatten, skip_serializing_if = "Map::is_empty")
278    )]
279    pub other: Map<String>,
280}