Skip to content

Commit f856450

Browse files
author
Eric Szentiványi
authored
feat!: introduce attrs setting (#949)
1 parent f0bcf00 commit f856450

File tree

11 files changed

+373
-35
lines changed

11 files changed

+373
-35
lines changed

cargo-typify/README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,5 +194,8 @@ default). Builder output lets you write code like this:
194194
let xy: MyStruct = MyStruct::builder().x_coord(x).y_coord(y).try_into();
195195
```
196196

197-
The `--additional-derive` adds the specified derive macro to all generated
198-
types. This may be specified more than once.
197+
The `--additional-derive` option adds the specified derive macro to all generated
198+
types. This may be specified more than once.
199+
200+
The `--additional-attr` option adds the specified attribute to all generated
201+
types. This may be specified more than once.

cargo-typify/src/lib.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,13 @@ pub struct CliArgs {
3131
pub no_builder: bool,
3232

3333
/// Add an additional derive macro to apply to all defined types.
34-
#[arg(short, long = "additional-derive", value_name = "derive")]
34+
#[arg(short = 'd', long = "additional-derive", value_name = "derive")]
3535
pub additional_derives: Vec<String>,
3636

37+
/// Add an additional attribute to apply to all defined types.
38+
#[arg(short = 'a', long = "additional-attr", value_name = "attr")]
39+
pub additional_attrs: Vec<String>,
40+
3741
/// The output file to write to. If not specified, the input file name will
3842
/// be used with a `.rs` extension.
3943
///
@@ -146,6 +150,10 @@ pub fn convert(args: &CliArgs) -> Result<String> {
146150
settings.with_derive(derive.clone());
147151
}
148152

153+
for attr in &args.additional_attrs {
154+
settings.with_attr(attr.clone());
155+
}
156+
149157
for CrateSpec {
150158
name,
151159
version,
@@ -197,6 +205,7 @@ mod tests {
197205
input: PathBuf::from("input.json"),
198206
builder: false,
199207
additional_derives: vec![],
208+
additional_attrs: vec![],
200209
output: Some(PathBuf::from("-")),
201210
no_builder: false,
202211
crates: vec![],
@@ -213,6 +222,7 @@ mod tests {
213222
input: PathBuf::from("input.json"),
214223
builder: false,
215224
additional_derives: vec![],
225+
additional_attrs: vec![],
216226
output: Some(PathBuf::from("some_file.rs")),
217227
no_builder: false,
218228
crates: vec![],
@@ -229,6 +239,7 @@ mod tests {
229239
input: PathBuf::from("input.json"),
230240
builder: false,
231241
additional_derives: vec![],
242+
additional_attrs: vec![],
232243
output: None,
233244
no_builder: false,
234245
crates: vec![],
@@ -245,6 +256,7 @@ mod tests {
245256
input: PathBuf::from("input.json"),
246257
builder: false,
247258
additional_derives: vec![],
259+
additional_attrs: vec![],
248260
output: None,
249261
no_builder: false,
250262
crates: vec![],
@@ -264,6 +276,7 @@ mod tests {
264276
input: PathBuf::from("input.json"),
265277
builder: false,
266278
additional_derives: vec![],
279+
additional_attrs: vec![],
267280
output: None,
268281
no_builder: false,
269282
crates: vec![],
@@ -280,6 +293,7 @@ mod tests {
280293
input: PathBuf::from("input.json"),
281294
builder: false,
282295
additional_derives: vec![],
296+
additional_attrs: vec![],
283297
output: None,
284298
no_builder: true,
285299
crates: vec![],
@@ -296,6 +310,7 @@ mod tests {
296310
input: PathBuf::from("input.json"),
297311
builder: true,
298312
additional_derives: vec![],
313+
additional_attrs: vec![],
299314
output: None,
300315
no_builder: false,
301316
crates: vec![],

cargo-typify/tests/integration.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,31 @@ fn test_derive() {
103103
assert_contents("tests/outputs/derive.rs", &actual);
104104
}
105105

106+
#[test]
107+
fn test_attr() {
108+
let input = concat!(env!("CARGO_MANIFEST_DIR"), "/../example.json");
109+
110+
let temp = TempDir::new("cargo-typify").unwrap();
111+
let output_file = temp.path().join("output.rs");
112+
113+
assert_cmd::cargo::cargo_bin_cmd!()
114+
.args([
115+
"typify",
116+
input,
117+
"--no-builder",
118+
"--additional-attr",
119+
"#[extra_attr]",
120+
"--output",
121+
output_file.to_str().unwrap(),
122+
])
123+
.assert()
124+
.success();
125+
126+
let actual = std::fs::read_to_string(output_file).unwrap();
127+
128+
assert_contents("tests/outputs/attr.rs", &actual);
129+
}
130+
106131
#[test]
107132
fn test_multi_derive() {
108133
let input = concat!(env!("CARGO_MANIFEST_DIR"), "/../example.json");

cargo-typify/tests/outputs/attr.rs

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
#![allow(clippy::redundant_closure_call)]
2+
#![allow(clippy::needless_lifetimes)]
3+
#![allow(clippy::match_single_binding)]
4+
#![allow(clippy::clone_on_copy)]
5+
6+
#[doc = r" Error types."]
7+
pub mod error {
8+
#[doc = r" Error from a `TryFrom` or `FromStr` implementation."]
9+
pub struct ConversionError(::std::borrow::Cow<'static, str>);
10+
impl ::std::error::Error for ConversionError {}
11+
impl ::std::fmt::Display for ConversionError {
12+
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> {
13+
::std::fmt::Display::fmt(&self.0, f)
14+
}
15+
}
16+
impl ::std::fmt::Debug for ConversionError {
17+
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> {
18+
::std::fmt::Debug::fmt(&self.0, f)
19+
}
20+
}
21+
impl From<&'static str> for ConversionError {
22+
fn from(value: &'static str) -> Self {
23+
Self(value.into())
24+
}
25+
}
26+
impl From<String> for ConversionError {
27+
fn from(value: String) -> Self {
28+
Self(value.into())
29+
}
30+
}
31+
}
32+
#[doc = "`Fruit`"]
33+
#[doc = r""]
34+
#[doc = r" <details><summary>JSON schema</summary>"]
35+
#[doc = r""]
36+
#[doc = r" ```json"]
37+
#[doc = "{"]
38+
#[doc = " \"type\": \"object\","]
39+
#[doc = " \"additionalProperties\": {"]
40+
#[doc = " \"type\": \"string\""]
41+
#[doc = " }"]
42+
#[doc = "}"]
43+
#[doc = r" ```"]
44+
#[doc = r" </details>"]
45+
#[extra_attr]
46+
#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)]
47+
#[serde(transparent)]
48+
pub struct Fruit(pub ::std::collections::HashMap<::std::string::String, ::std::string::String>);
49+
impl ::std::ops::Deref for Fruit {
50+
type Target = ::std::collections::HashMap<::std::string::String, ::std::string::String>;
51+
fn deref(&self) -> &::std::collections::HashMap<::std::string::String, ::std::string::String> {
52+
&self.0
53+
}
54+
}
55+
impl ::std::convert::From<Fruit>
56+
for ::std::collections::HashMap<::std::string::String, ::std::string::String>
57+
{
58+
fn from(value: Fruit) -> Self {
59+
value.0
60+
}
61+
}
62+
impl ::std::convert::From<&Fruit> for Fruit {
63+
fn from(value: &Fruit) -> Self {
64+
value.clone()
65+
}
66+
}
67+
impl ::std::convert::From<::std::collections::HashMap<::std::string::String, ::std::string::String>>
68+
for Fruit
69+
{
70+
fn from(
71+
value: ::std::collections::HashMap<::std::string::String, ::std::string::String>,
72+
) -> Self {
73+
Self(value)
74+
}
75+
}
76+
#[doc = "`FruitOrVeg`"]
77+
#[doc = r""]
78+
#[doc = r" <details><summary>JSON schema</summary>"]
79+
#[doc = r""]
80+
#[doc = r" ```json"]
81+
#[doc = "{"]
82+
#[doc = " \"oneOf\": ["]
83+
#[doc = " {"]
84+
#[doc = " \"title\": \"veg\","]
85+
#[doc = " \"anyOf\": ["]
86+
#[doc = " {"]
87+
#[doc = " \"$ref\": \"#/defs/veggie\""]
88+
#[doc = " }"]
89+
#[doc = " ]"]
90+
#[doc = " },"]
91+
#[doc = " {"]
92+
#[doc = " \"title\": \"fruit\","]
93+
#[doc = " \"anyOf\": ["]
94+
#[doc = " {"]
95+
#[doc = " \"$ref\": \"#/defs/fruit\""]
96+
#[doc = " }"]
97+
#[doc = " ]"]
98+
#[doc = " }"]
99+
#[doc = " ]"]
100+
#[doc = "}"]
101+
#[doc = r" ```"]
102+
#[doc = r" </details>"]
103+
#[extra_attr]
104+
#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)]
105+
#[serde(untagged)]
106+
pub enum FruitOrVeg {
107+
Veg(Veggie),
108+
Fruit(Fruit),
109+
}
110+
impl ::std::convert::From<&Self> for FruitOrVeg {
111+
fn from(value: &FruitOrVeg) -> Self {
112+
value.clone()
113+
}
114+
}
115+
impl ::std::convert::From<Veggie> for FruitOrVeg {
116+
fn from(value: Veggie) -> Self {
117+
Self::Veg(value)
118+
}
119+
}
120+
impl ::std::convert::From<Fruit> for FruitOrVeg {
121+
fn from(value: Fruit) -> Self {
122+
Self::Fruit(value)
123+
}
124+
}
125+
#[doc = "`Veggie`"]
126+
#[doc = r""]
127+
#[doc = r" <details><summary>JSON schema</summary>"]
128+
#[doc = r""]
129+
#[doc = r" ```json"]
130+
#[doc = "{"]
131+
#[doc = " \"type\": \"object\","]
132+
#[doc = " \"required\": ["]
133+
#[doc = " \"veggieLike\","]
134+
#[doc = " \"veggieName\""]
135+
#[doc = " ],"]
136+
#[doc = " \"properties\": {"]
137+
#[doc = " \"veggieLike\": {"]
138+
#[doc = " \"description\": \"Do I like this vegetable?\","]
139+
#[doc = " \"type\": \"boolean\""]
140+
#[doc = " },"]
141+
#[doc = " \"veggieName\": {"]
142+
#[doc = " \"description\": \"The name of the vegetable.\","]
143+
#[doc = " \"type\": \"string\""]
144+
#[doc = " }"]
145+
#[doc = " }"]
146+
#[doc = "}"]
147+
#[doc = r" ```"]
148+
#[doc = r" </details>"]
149+
#[extra_attr]
150+
#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)]
151+
pub struct Veggie {
152+
#[doc = "Do I like this vegetable?"]
153+
#[serde(rename = "veggieLike")]
154+
pub veggie_like: bool,
155+
#[doc = "The name of the vegetable."]
156+
#[serde(rename = "veggieName")]
157+
pub veggie_name: ::std::string::String,
158+
}
159+
impl ::std::convert::From<&Veggie> for Veggie {
160+
fn from(value: &Veggie) -> Self {
161+
value.clone()
162+
}
163+
}
164+
#[doc = "A representation of a person, company, organization, or place"]
165+
#[doc = r""]
166+
#[doc = r" <details><summary>JSON schema</summary>"]
167+
#[doc = r""]
168+
#[doc = r" ```json"]
169+
#[doc = "{"]
170+
#[doc = " \"$id\": \"https://example.com/arrays.schema.json\","]
171+
#[doc = " \"title\": \"veggies\","]
172+
#[doc = " \"description\": \"A representation of a person, company, organization, or place\","]
173+
#[doc = " \"type\": \"object\","]
174+
#[doc = " \"properties\": {"]
175+
#[doc = " \"fruits\": {"]
176+
#[doc = " \"type\": \"array\","]
177+
#[doc = " \"items\": {"]
178+
#[doc = " \"type\": \"string\""]
179+
#[doc = " }"]
180+
#[doc = " },"]
181+
#[doc = " \"vegetables\": {"]
182+
#[doc = " \"type\": \"array\","]
183+
#[doc = " \"items\": {"]
184+
#[doc = " \"$ref\": \"#/$defs/veggie\""]
185+
#[doc = " }"]
186+
#[doc = " }"]
187+
#[doc = " }"]
188+
#[doc = "}"]
189+
#[doc = r" ```"]
190+
#[doc = r" </details>"]
191+
#[extra_attr]
192+
#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)]
193+
pub struct Veggies {
194+
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
195+
pub fruits: ::std::vec::Vec<::std::string::String>,
196+
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
197+
pub vegetables: ::std::vec::Vec<Veggie>,
198+
}
199+
impl ::std::convert::From<&Veggies> for Veggies {
200+
fn from(value: &Veggies) -> Self {
201+
value.clone()
202+
}
203+
}
204+
impl ::std::default::Default for Veggies {
205+
fn default() -> Self {
206+
Self {
207+
fruits: Default::default(),
208+
vegetables: Default::default(),
209+
}
210+
}
211+
}

cargo-typify/tests/outputs/help.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ Options:
1313
-B, --no-builder
1414
Inverse of `--builder`. When set the builder-style interface will not be included
1515

16-
-a, --additional-derive <derive>
16+
-d, --additional-derive <derive>
1717
Add an additional derive macro to apply to all defined types
1818

19+
-a, --additional-attr <attr>
20+
Add an additional attribute to apply to all defined types
21+
1922
-o, --output <OUTPUT>
2023
The output file to write to. If not specified, the input file name will be used with a `.rs` extension.
2124

typify-impl/src/defaults.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,7 @@ mod tests {
669669
let type_entry = TypeEntry {
670670
details: crate::type_entry::TypeEntryDetails::Box(type_id),
671671
extra_derives: Default::default(),
672+
extra_attrs: Default::default(),
672673
};
673674

674675
assert!(type_entry

0 commit comments

Comments
 (0)