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()
}