1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
//! Derive macro for automatically implementing jaded::FromJava for Rust types. //! //! This is provided as a `FromJava` macro. //! //! Most implementations of FromJava will be very similar and automatically deriving them makes //! much more sense that writing almost identical impl blocks for every type. //! //! For a 'simple' bean style Java class, eg //! //! ```java //! public class Person implements Serializable { //! private String firstName; //! private String familyName; //! private int age; //! } //! ``` //! The rust equivalent can be automatically derived //! ```ignore //! #[derive(FromJava)] //! struct Person { //! firstName: String, //! familyName: String, //! age: i32, //! } //! ``` //! //! To allow both Java and rust to keep to their respective naming conventions, //! a 'rename' feature is available that allows camelCase java fields to be //! read into snake_case rust fields //! //! ```ignore //! #[derive(FromJava)] //! #[jaded(rename)] //! struct Person { //! first_name: String, //! family_name: String, //! age: i32, //! } //! ``` //! use proc_macro::TokenStream; use syn::parse_macro_input; mod attributes; mod from_value_derive; mod internal; use from_value_derive::expand_from_value; /// # Derive FromJava for custom Rust types /// /// By default each field is read from a field of the same name in the serialized /// Java object. In this simple form, all fields must also implement `FromJava` /// (similar to `derive(Debug)` requiring fields to implement `Debug`). /// /// To modify the standard behaviour a `jaded` attribute is available /// ## On structs /// ### `#[jaded(rename)]` /// Convert all fields to camelCase before looking them up in the Java object. /// This allows a field 'first_name` to be read from a Java field `firstName`. /// /// NB. This requires the `renaming` feature as it requires additional /// dependencies. /// ### `#[jaded(class = "com.example.Demo")]` /// By default, Jaded will try to read from the given fields and use them if /// they're present without first checking the class of the object. This means /// a class `Foo` with a single `String name` field will be treated exactly /// the same as a class `Bar` that also has a single `String name` field. /// This attribute allows a specific class to be given which will be checked /// at conversion time. /// /// ## On Fields /// ### `#[jaded(field = "fieldName")]` /// Customise the name of the Java field being read. /// By default each rust field is read from a Java field of the same name. While /// this is usually fine, it can be useful in some situations whether better /// match domain language or to avoid keyword clashes (eg type is a valid /// Java field name but can't be used in Rust). /// This could also be used to rename fields to match naming conventions if /// the struct level `rename` attribute is not used. /// /// ### `#[jaded(extract(method_name))]` /// Serializable classes in Java can customise the way the builtin Serializer /// writes their state to the output stream. As there is no standard for how this /// may look, the custom data is written to an annotations field in the /// intermediate representation and the field name is not available. /// This data can be read using an interface similar to Java's ObjectInputStream /// but the specifics must be user implemented and can't be derived. /// The `extract` attribute allows a user method to be used to read a field's /// value from the custom data stream. /// /// The value given to the `extract` attribute must be a valid path to a function /// that can be called with a `&mut AnnotationIter` and must return a /// `ConversionResult<T>` where `T` is the type of the field being annotated. /// /// NB. Fields that read from the annotation stream are all passed the same /// instance of the `AnnotationIter`. This means that later fields do /// not have to 'fast-forward' through the previous field's data but does /// mean that the order fields are declared in is important. /// NB. Fields read via a custom extract method do not have to implement `FromJava`. /// /// ### `#[jaded(annotation_number = 2)]` /// Each class in a Java object's hierarchy can write its own custom data. If a /// field should be read from the annotations of an object's superclass, the /// annotation number may be given. This number reflects the number of classes /// up the hierarchy to read with 0 (the default if not specified) being the /// current class. /// /// NB. It is an error to specify the annotation number but not give an extract /// method to use when reading. #[proc_macro_derive(FromJava, attributes(jaded))] pub fn from_java(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input); expand_from_value(input) .unwrap_or_else(syn::Error::into_compile_error) .into() }