Expand description
Derive macro for the JsonPointee trait.
This crate provides a derive macro to generate a JsonPointee
implementation for a Rust data structure. The macro can generate implementations for
structs and enums, and supports Serde-like attributes.
§Container attributes
Container-level attributes apply to structs and enums:
#[ploidy(tag = "field")]- Use the internally tagged enum representation, with the given field name for the tag. Supported on enums only.#[ploidy(tag = "t", content = "c")]- Use the adjacently tagged enum representation, with the given field names for the tag and contents. Supported on enums only.#[ploidy(untagged)]- Use the untagged enum representation. Supported on enums only.#[ploidy(rename_all = "case")]- Rename all struct fields or enum variants according to the given case. The supported cases arelowercase,UPPERCASE,PascalCase,camelCase,snake_case,SCREAMING_SNAKE_CASE,kebab-case, andSCREAMING-KEBAB-CASE.
§Variant Attributes
Variant-level attributes apply to enum variants:
#[ploidy(rename = "name")]- Access this variant using the given name, instead of its Rust name.#[ploidy(skip)]- Make this variant inaccessible, except for the tag field if using the internally or adjacently tagged enum representation.
§Field Attributes
Field-level attributes apply to struct and enum variant fields:
#[ploidy(rename = "name")]- Access this variant using the given name, instead of its Rust name.#[ploidy(flatten)]- Remove one layer of structure between the container and field. Supported on named fields only.#[ploidy(skip)]- Exclude the field from pointer access.
§Examples
§Struct flattening
#[derive(JsonPointee)]
struct User {
name: String,
#[ploidy(flatten)]
contact: ContactInfo,
}
#[derive(JsonPointee)]
struct ContactInfo {
email: String,
phone: String,
}
let user = User {
name: "Alice".to_owned(),
contact: ContactInfo {
email: "a@example.com".to_owned(),
phone: "555-1234".to_owned(),
},
};
assert_eq!(
user.resolve(JsonPointer::parse("/name")?)?.downcast_ref::<String>(),
Some(&"Alice".to_owned()),
);
assert_eq!(
user.resolve(JsonPointer::parse("/email")?)?.downcast_ref::<String>(),
Some(&"a@example.com".to_owned()),
);
assert_eq!(
user.resolve(JsonPointer::parse("/phone")?)?.downcast_ref::<String>(),
Some(&"555-1234".to_owned()),
);§Renaming fields
#[derive(JsonPointee)]
#[ploidy(rename_all = "snake_case")]
enum ApiResponse {
SuccessResponse { data: String },
#[ploidy(rename = "error")]
ErrorResponse { message: String },
}
let success = ApiResponse::SuccessResponse {
data: "ok".to_owned(),
};
assert_eq!(
success.resolve(JsonPointer::parse("/success_response/data")?)?.downcast_ref::<String>(),
Some(&"ok".to_owned()),
);
let error = ApiResponse::ErrorResponse {
message: "failed".to_owned(),
};
assert_eq!(
error.resolve(JsonPointer::parse("/error/message")?)?.downcast_ref::<String>(),
Some(&"failed".to_owned()),
);§Enum representations
Like Serde, #[derive(JsonPointee)] supports externally tagged,
internally tagged, adjacently tagged, and untagged enum representations.
§Externally tagged
This is the default enum representation. The variant’s tag wraps the contents.
#[derive(JsonPointee)]
enum Message {
Text { content: String },
Image { url: String },
}
let message = Message::Text {
content: "hello".to_owned(),
};
assert_eq!(
message.resolve(JsonPointer::parse("/Text/content")?)?.downcast_ref::<String>(),
Some(&"hello".to_owned()),
);§Internally tagged
In this representation, the tag that specifies the variant name is next to the variant’s fields.
#[derive(JsonPointee)]
#[ploidy(tag = "type")]
enum Message {
Text { content: String },
Image { url: String },
}
let message = Message::Text {
content: "hello".to_owned(),
};
assert_eq!(
message.resolve(JsonPointer::parse("/type")?)?.downcast_ref::<&str>(),
Some(&"Text"),
);
assert_eq!(
message.resolve(JsonPointer::parse("/content")?)?.downcast_ref::<String>(),
Some(&"hello".to_owned()),
);§Adjacently tagged
In this representation, the variant’s tag and contents are adjacent to each other, as two fields in the same object.
#[derive(JsonPointee)]
#[ploidy(tag = "type", content = "value")]
enum Message {
Text { content: String },
Image { url: String },
}
let message = Message::Text {
content: "hello".to_owned(),
};
assert_eq!(
message.resolve(JsonPointer::parse("/type")?)?.downcast_ref::<&str>(),
Some(&"Text"),
);
assert_eq!(
message.resolve(JsonPointer::parse("/value/content")?)?.downcast_ref::<String>(),
Some(&"hello".to_owned()),
);§Untagged
In this representation, the variant’s name is completely ignored, and pointers are resolved against the variant’s contents.
#[derive(JsonPointee)]
#[ploidy(untagged)]
enum Message {
Text { content: String },
Image { url: String },
}
let message = Message::Text {
content: "hello".to_owned(),
};
assert_eq!(
message.resolve(JsonPointer::parse("/content")?)?.downcast_ref::<String>(),
Some(&"hello".to_owned()),
);Derive Macros§
- Json
Pointee - Derives the
JsonPointeetrait for JSON Pointer (RFC 6901) traversal.