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
//! Proc macro to cache associate function result on strut field. An analogy to python's @cached_property class decorator.

mod field;
mod utils;
use field::cached_field_impl;
use proc_macro::TokenStream;

/// Attribute macro decorates on function implementations of struct;
///
/// Function must receive a &self as its first args; Struct must have a corresponding optional field to store the computation result;  
///
/// ### Params
///
/// **field**: must be a string literal, to indicate the struct field for caching result, default to the ident of function it decorates on;
///
/// **borrow**: bool, indicate whether the transformed function return a reference or copy of the stored value;
///             if false, the return type should implement the Copy trait; Default true;
///
/// It receive either positional arguments in order of (field, borrow) or named value arguments;
///
/// # Example
///
/// ```
/// use cached_field::cached_field;
/// struct SomeStruct{
///     some_value: Option<u8>
/// }
///
/// impl SomeStruct{
///     #[cached_field]
///     fn some_value(&self) -> u8 {
///         100
///     }
///     #[cached_field("some_value")]
///     fn same_value(&self) -> u8 {
///         101
///     }
/// }
///
/// // s has to be mut 
/// // since some_value requires a mutable reference to SomeStruct to mutate the some_value field inside;   
/// let mut s = SomeStruct{some_value:None};
///
/// assert_eq!(100, s.some_value());
/// assert_eq!(100, s.same_value());
///
/// ```
/// ### trait implementation
/// - the function defination in trait block should be the final form of the transformation. 
///   i.e. &mut self for receiver parameter and &T ReturnType if borrow;
/// - The impl function signature should take immutable form; i.e. &self and T without & (if trait signature has &T as ReturnType)
/// - General Idea is that the impl function is only for computation purpose, 
///   so it should not mutate the struct and it should return the original result;    
/// ```
/// 
/// use cached_field::cached_field;
/// 
/// struct AnotherStruct{ 
///     another_value: Option<Vec<String>>
/// }
/// 
/// trait Another {
///     fn another(&mut self)->&Vec<String>;
/// }
/// 
/// impl Another for AnotherStruct {
///     #[cached_field(field = "another_value", borrow = true)]
///     fn another(&self)->Vec<String>{
///         vec![1.to_string(),3.to_string(),5.to_string()]
///     }
/// }
/// 
/// assert_eq!(vec![1.to_string(),3.to_string(),5.to_string()], *AnotherStruct{another_value:None}.another());
///
/// ```
/// 
/// # Feature
/// By default, if borrow is true, the macro will check the computation function return type, if it found a &, it will panic;
/// 
/// Modify the behavior when borrow is true and existing & reference found using these features:
/// 
/// **carry**: keep the return type as is when transform.
/// 
/// **prepend**: prepend & to original type, e.g. transform &T to &&T;
/// 
/// *features are meant to be **multually exclusive**;    
#[proc_macro_attribute]
pub fn cached_field(args: TokenStream, item: TokenStream) -> TokenStream {
    cached_field_impl(args, item)
}