Crate ploidy_pointer_derive

Crate ploidy_pointer_derive 

Source
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 are lowercase, UPPERCASE, PascalCase, camelCase, snake_case, SCREAMING_SNAKE_CASE, kebab-case, and SCREAMING-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§

JsonPointee
Derives the JsonPointee trait for JSON Pointer (RFC 6901) traversal.