Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions cargo-typify/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,5 +204,8 @@ default). Builder output lets you write code like this:
let xy: MyStruct = MyStruct::builder().x_coord(x).y_coord(y).try_into();
```

The `--additional-derive` adds the specified derive macro to all generated
types. This may be specified more than once.
The `--additional-derive` option adds the specified derive macro to all generated
types. This may be specified more than once.

The `--additional-attr` option adds the specified attribute to all generated
types. This may be specified more than once.
17 changes: 16 additions & 1 deletion cargo-typify/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,13 @@ pub struct CliArgs {
pub no_builder: bool,

/// Add an additional derive macro to apply to all defined types.
#[arg(short, long = "additional-derive", value_name = "derive")]
#[arg(short = 'd', long = "additional-derive", value_name = "derive")]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the shorthand for the additional-derive option to resolve the conflict with the new additional-attr option. This is a breaking change.

pub additional_derives: Vec<String>,

/// Add an additional attribute to apply to all defined types.
#[arg(short = 'a', long = "additional-attr", value_name = "attr")]
pub additional_attrs: Vec<String>,

/// The output file to write to. If not specified, the input file name will
/// be used with a `.rs` extension.
///
Expand Down Expand Up @@ -146,6 +150,10 @@ pub fn convert(args: &CliArgs) -> Result<String> {
settings.with_derive(derive.clone());
}

for attr in &args.additional_attrs {
settings.with_attr(attr.clone());
}

for CrateSpec {
name,
version,
Expand Down Expand Up @@ -197,6 +205,7 @@ mod tests {
input: PathBuf::from("input.json"),
builder: false,
additional_derives: vec![],
additional_attrs: vec![],
output: Some(PathBuf::from("-")),
no_builder: false,
crates: vec![],
Expand All @@ -213,6 +222,7 @@ mod tests {
input: PathBuf::from("input.json"),
builder: false,
additional_derives: vec![],
additional_attrs: vec![],
output: Some(PathBuf::from("some_file.rs")),
no_builder: false,
crates: vec![],
Expand All @@ -229,6 +239,7 @@ mod tests {
input: PathBuf::from("input.json"),
builder: false,
additional_derives: vec![],
additional_attrs: vec![],
output: None,
no_builder: false,
crates: vec![],
Expand All @@ -245,6 +256,7 @@ mod tests {
input: PathBuf::from("input.json"),
builder: false,
additional_derives: vec![],
additional_attrs: vec![],
output: None,
no_builder: false,
crates: vec![],
Expand All @@ -264,6 +276,7 @@ mod tests {
input: PathBuf::from("input.json"),
builder: false,
additional_derives: vec![],
additional_attrs: vec![],
output: None,
no_builder: false,
crates: vec![],
Expand All @@ -280,6 +293,7 @@ mod tests {
input: PathBuf::from("input.json"),
builder: false,
additional_derives: vec![],
additional_attrs: vec![],
output: None,
no_builder: true,
crates: vec![],
Expand All @@ -296,6 +310,7 @@ mod tests {
input: PathBuf::from("input.json"),
builder: true,
additional_derives: vec![],
additional_attrs: vec![],
output: None,
no_builder: false,
crates: vec![],
Expand Down
25 changes: 25 additions & 0 deletions cargo-typify/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,31 @@ fn test_derive() {
assert_contents("tests/outputs/derive.rs", &actual);
}

#[test]
fn test_attr() {
let input = concat!(env!("CARGO_MANIFEST_DIR"), "/../example.json");

let temp = TempDir::new("cargo-typify").unwrap();
let output_file = temp.path().join("output.rs");

assert_cmd::cargo::cargo_bin_cmd!()
.args([
"typify",
input,
"--no-builder",
"--additional-attr",
"#[extra_attr]",
"--output",
output_file.to_str().unwrap(),
])
.assert()
.success();

let actual = std::fs::read_to_string(output_file).unwrap();

assert_contents("tests/outputs/attr.rs", &actual);
}

#[test]
fn test_multi_derive() {
let input = concat!(env!("CARGO_MANIFEST_DIR"), "/../example.json");
Expand Down
211 changes: 211 additions & 0 deletions cargo-typify/tests/outputs/attr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
#![allow(clippy::redundant_closure_call)]
#![allow(clippy::needless_lifetimes)]
#![allow(clippy::match_single_binding)]
#![allow(clippy::clone_on_copy)]

#[doc = r" Error types."]
pub mod error {
#[doc = r" Error from a `TryFrom` or `FromStr` implementation."]
pub struct ConversionError(::std::borrow::Cow<'static, str>);
impl ::std::error::Error for ConversionError {}
impl ::std::fmt::Display for ConversionError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> {
::std::fmt::Display::fmt(&self.0, f)
}
}
impl ::std::fmt::Debug for ConversionError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> {
::std::fmt::Debug::fmt(&self.0, f)
}
}
impl From<&'static str> for ConversionError {
fn from(value: &'static str) -> Self {
Self(value.into())
}
}
impl From<String> for ConversionError {
fn from(value: String) -> Self {
Self(value.into())
}
}
}
#[doc = "`Fruit`"]
#[doc = r""]
#[doc = r" <details><summary>JSON schema</summary>"]
#[doc = r""]
#[doc = r" ```json"]
#[doc = "{"]
#[doc = " \"type\": \"object\","]
#[doc = " \"additionalProperties\": {"]
#[doc = " \"type\": \"string\""]
#[doc = " }"]
#[doc = "}"]
#[doc = r" ```"]
#[doc = r" </details>"]
#[extra_attr]
#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)]
#[serde(transparent)]
pub struct Fruit(pub ::std::collections::HashMap<::std::string::String, ::std::string::String>);
impl ::std::ops::Deref for Fruit {
type Target = ::std::collections::HashMap<::std::string::String, ::std::string::String>;
fn deref(&self) -> &::std::collections::HashMap<::std::string::String, ::std::string::String> {
&self.0
}
}
impl ::std::convert::From<Fruit>
for ::std::collections::HashMap<::std::string::String, ::std::string::String>
{
fn from(value: Fruit) -> Self {
value.0
}
}
impl ::std::convert::From<&Fruit> for Fruit {
fn from(value: &Fruit) -> Self {
value.clone()
}
}
impl ::std::convert::From<::std::collections::HashMap<::std::string::String, ::std::string::String>>
for Fruit
{
fn from(
value: ::std::collections::HashMap<::std::string::String, ::std::string::String>,
) -> Self {
Self(value)
}
}
#[doc = "`FruitOrVeg`"]
#[doc = r""]
#[doc = r" <details><summary>JSON schema</summary>"]
#[doc = r""]
#[doc = r" ```json"]
#[doc = "{"]
#[doc = " \"oneOf\": ["]
#[doc = " {"]
#[doc = " \"title\": \"veg\","]
#[doc = " \"anyOf\": ["]
#[doc = " {"]
#[doc = " \"$ref\": \"#/defs/veggie\""]
#[doc = " }"]
#[doc = " ]"]
#[doc = " },"]
#[doc = " {"]
#[doc = " \"title\": \"fruit\","]
#[doc = " \"anyOf\": ["]
#[doc = " {"]
#[doc = " \"$ref\": \"#/defs/fruit\""]
#[doc = " }"]
#[doc = " ]"]
#[doc = " }"]
#[doc = " ]"]
#[doc = "}"]
#[doc = r" ```"]
#[doc = r" </details>"]
#[extra_attr]
#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)]
#[serde(untagged)]
pub enum FruitOrVeg {
Veg(Veggie),
Fruit(Fruit),
}
impl ::std::convert::From<&Self> for FruitOrVeg {
fn from(value: &FruitOrVeg) -> Self {
value.clone()
}
}
impl ::std::convert::From<Veggie> for FruitOrVeg {
fn from(value: Veggie) -> Self {
Self::Veg(value)
}
}
impl ::std::convert::From<Fruit> for FruitOrVeg {
fn from(value: Fruit) -> Self {
Self::Fruit(value)
}
}
#[doc = "`Veggie`"]
#[doc = r""]
#[doc = r" <details><summary>JSON schema</summary>"]
#[doc = r""]
#[doc = r" ```json"]
#[doc = "{"]
#[doc = " \"type\": \"object\","]
#[doc = " \"required\": ["]
#[doc = " \"veggieLike\","]
#[doc = " \"veggieName\""]
#[doc = " ],"]
#[doc = " \"properties\": {"]
#[doc = " \"veggieLike\": {"]
#[doc = " \"description\": \"Do I like this vegetable?\","]
#[doc = " \"type\": \"boolean\""]
#[doc = " },"]
#[doc = " \"veggieName\": {"]
#[doc = " \"description\": \"The name of the vegetable.\","]
#[doc = " \"type\": \"string\""]
#[doc = " }"]
#[doc = " }"]
#[doc = "}"]
#[doc = r" ```"]
#[doc = r" </details>"]
#[extra_attr]
#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)]
pub struct Veggie {
#[doc = "Do I like this vegetable?"]
#[serde(rename = "veggieLike")]
pub veggie_like: bool,
#[doc = "The name of the vegetable."]
#[serde(rename = "veggieName")]
pub veggie_name: ::std::string::String,
}
impl ::std::convert::From<&Veggie> for Veggie {
fn from(value: &Veggie) -> Self {
value.clone()
}
}
#[doc = "A representation of a person, company, organization, or place"]
#[doc = r""]
#[doc = r" <details><summary>JSON schema</summary>"]
#[doc = r""]
#[doc = r" ```json"]
#[doc = "{"]
#[doc = " \"$id\": \"https://example.com/arrays.schema.json\","]
#[doc = " \"title\": \"veggies\","]
#[doc = " \"description\": \"A representation of a person, company, organization, or place\","]
#[doc = " \"type\": \"object\","]
#[doc = " \"properties\": {"]
#[doc = " \"fruits\": {"]
#[doc = " \"type\": \"array\","]
#[doc = " \"items\": {"]
#[doc = " \"type\": \"string\""]
#[doc = " }"]
#[doc = " },"]
#[doc = " \"vegetables\": {"]
#[doc = " \"type\": \"array\","]
#[doc = " \"items\": {"]
#[doc = " \"$ref\": \"#/$defs/veggie\""]
#[doc = " }"]
#[doc = " }"]
#[doc = " }"]
#[doc = "}"]
#[doc = r" ```"]
#[doc = r" </details>"]
#[extra_attr]
#[derive(:: serde :: Deserialize, :: serde :: Serialize, Clone, Debug)]
pub struct Veggies {
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub fruits: ::std::vec::Vec<::std::string::String>,
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub vegetables: ::std::vec::Vec<Veggie>,
}
impl ::std::convert::From<&Veggies> for Veggies {
fn from(value: &Veggies) -> Self {
value.clone()
}
}
impl ::std::default::Default for Veggies {
fn default() -> Self {
Self {
fruits: Default::default(),
vegetables: Default::default(),
}
}
}
5 changes: 4 additions & 1 deletion cargo-typify/tests/outputs/help.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ Options:
-B, --no-builder
Inverse of `--builder`. When set the builder-style interface will not be included

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

-a, --additional-attr <attr>
Add an additional attribute to apply to all defined types

-o, --output <OUTPUT>
The output file to write to. If not specified, the input file name will be used with a `.rs` extension.

Expand Down
1 change: 1 addition & 0 deletions typify-impl/src/defaults.rs
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,7 @@ mod tests {
let type_entry = TypeEntry {
details: crate::type_entry::TypeEntryDetails::Box(type_id),
extra_derives: Default::default(),
extra_attrs: Default::default(),
};

assert!(type_entry
Expand Down
Loading