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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
use doc::{Identifier, Object};
use error::Error;
pub trait Resource: Sized {
fn ident(&self) -> Result<Identifier, Error>;
fn object(&self) -> Result<Object, Error>;
}
#[macro_export]
macro_rules! resource {
($target:ident, |&$ctx:ident| { $($rest:tt)* }) => {
impl $crate::Resource for $target {
fn ident(&$ctx) -> Result<$crate::doc::Identifier, $crate::error::Error> {
let mut ident = $crate::doc::Identifier::build();
expand_resource_impl!(@id $ctx, ident, { $($rest)* });
expand_resource_impl!(@kind $ctx, ident, { $($rest)* });
expand_resource_impl!(@meta $ctx, ident, { $($rest)* });
ident.finalize()
}
fn object(&$ctx) -> Result<$crate::doc::Object, $crate::error::Error> {
let mut object = $crate::doc::Object::build();
expand_resource_impl!(@attrs $ctx, object, { $($rest)* });
expand_resource_impl!(@id $ctx, object, { $($rest)* });
expand_resource_impl!(@kind $ctx, object, { $($rest)* });
expand_resource_impl!(@links $ctx, object, { $($rest)* });
expand_resource_impl!(@meta $ctx, object, { $($rest)* });
expand_resource_impl!(@rel $ctx, object, { $($rest)* });
object.finalize()
}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! expand_resource_impl {
(@id $ctx:ident, $builder:ident, { id $value:block $($rest:tt)* }) => {
$builder.id($value);
};
(@kind $ctx:ident, $builder:ident, { kind $value:block $($rest:tt)* }) => {
$builder.kind($value);
};
(@attrs $ctx:ident, $builder:ident, { attr $key:expr, $value:block $($rest:tt)* }) => {
$builder.attribute($key, $crate::to_value($value)?);
expand_resource_impl!(@attrs $ctx, $builder, { $($rest)* });
};
(@attrs $ctx:ident, $builder:ident, { attr $field:ident; $($rest:tt)* }) => {
expand_resource_impl!(@attrs $ctx, $builder, {
attr stringify!($field), &$ctx.$field;
$($rest)*
});
};
(@attrs $ctx:ident, $builder:ident, { attrs $($field:ident),+; $($rest:tt)* }) => {
expand_resource_impl!(@attrs $ctx, $builder, {
$(attr $field;)+
$($rest)*
});
};
(@rel $ctx:ident, $builder:ident, { has_many $key:expr, { $($body:tt)* } $($rest:tt)* }) => {
$builder.relationship($key, {
let mut relationship = $crate::doc::Relationship::build();
expand_resource_impl!(@has_many $ctx, relationship, { $($body)* });
relationship.finalize()?
});
expand_resource_impl!(@rel $ctx, $builder, { $($rest)* });
};
(@rel $ctx:ident, $builder:ident, { has_one $key:expr, { $($body:tt)* } $($rest:tt)* }) => {
$builder.relationship($key, {
let mut relationship = $crate::doc::Relationship::build();
expand_resource_impl!(@has_one $ctx, relationship, { $($body)* });
relationship.finalize()?
});
expand_resource_impl!(@rel $ctx, $builder, { $($rest)* });
};
(@rel $ctx:ident, $builder:ident, { has_many $($field:ident),*; $($rest:tt)* }) => {
expand_resource_impl!(@rel $ctx, $builder, {
$(has_many stringify!($field), { data $ctx.$field.iter(); })*
$($rest)*
});
};
(@rel $ctx:ident, $builder:ident, { has_one $($field:ident),*; $($rest:tt)* }) => {
expand_resource_impl!(@rel $ctx, $builder, {
$(has_one stringify!($field), { data $ctx.$field.as_ref(); })*
$($rest)*
});
};
(@has_many $ctx:ident, $builder:ident, { data $value:block $($rest:tt)* }) => {
$builder.data({
let value = ($value).map($crate::Resource::ident).collect::<Result<_, _>>()?;
$crate::doc::Data::Collection(value)
});
expand_resource_impl!(@meta $ctx, $builder, { $($rest)* });
expand_resource_impl!(@links $ctx, $builder, { $($rest)* });
};
(@has_one $ctx:ident, $builder:ident, { data $value:block $($rest:tt)* }) => {
if let Some(value) = $value {
let ident = $crate::Resource::ident(value)?;
$builder.data($crate::doc::Data::Member(Box::new(Some(ident))));
}
expand_resource_impl!(@meta $ctx, $builder, { $($rest)* });
expand_resource_impl!(@links $ctx, $builder, { $($rest)* });
};
(@links $ctx:ident, $builder:ident, { link $key:expr, { $($body:tt)* } $($rest:tt)* }) => {
$builder.link($key, { expand_resource_impl!(@link $ctx, $builder, { $($body)* }) });
expand_resource_impl!(@links $ctx, $builder, { $($rest)* });
};
(@links $($args:ident),+, { link $key:expr, $value:expr; $($rest:tt)* }) => {
expand_resource_impl!(@links $($args),+, { link $key, { href { $value } } $($rest)* });
};
(@link $ctx:ident, $builder:ident, { href $value:block $($rest:tt)* }) => {{
let mut link = $crate::doc::Link::build();
link.href($value);
expand_resource_impl!(@meta $ctx, link, { $($rest)* });
link.finalize()?
}};
(@meta $ctx:ident, $builder:ident, { meta $key:expr, $value:block $($rest:tt)* }) => {
$builder.meta($key, $crate::to_value($value)?);
expand_resource_impl!(@meta $ctx, $builder, { $($rest)* });
};
(@$scope:tt $($args:ident),+, { has_many $key:expr, { $($body:tt)* } $($rest:tt)* }) => {
expand_resource_impl!(@$scope $($args),+, { $($rest)* });
};
(@$scope:tt $($args:ident),+, { has_one $key:expr, { $($body:tt)* } $($rest:tt)* }) => {
expand_resource_impl!(@$scope $($args),+, { $($rest)* });
};
(@$scope:tt $($args:ident),+, { link $key:expr, { $($body:tt)* } $($rest:tt)* }) => {
expand_resource_impl!(@$scope $($args),+, { $($rest)* });
};
(@$scope:tt $($args:ident),+, { $kwd:ident $value:expr; $($rest:tt)* }) => {
expand_resource_impl!(@$scope $($args),+, { $kwd { $value } $($rest)* });
};
(@$scope:tt $($args:ident),+, { has_many $key:expr, $value:block $($rest:tt)* }) => {
expand_resource_impl!(@$scope $($args),+, { $($rest)* });
};
(@$scope:tt $($args:ident),+, { has_one $key:expr, $value:block $($rest:tt)* }) => {
expand_resource_impl!(@$scope $($args),+, { $($rest)* });
};
(@$scope:tt $($args:ident),+, { link $key:expr, $value:block $($rest:tt)* }) => {
expand_resource_impl!(@$scope $($args),+, { $($rest)* });
};
(@$scope:tt $($args:ident),+, { $kwd:ident $key:expr, $value:expr; $($rest:tt)* }) => {
expand_resource_impl!(@$scope $($args),+, { $kwd $key, { $value } $($rest)* });
};
(@$scope:tt $($args:ident),+, { $skip:tt $($rest:tt)* }) => {
expand_resource_impl!(@$scope $($args),+, { $($rest)* });
};
($($rest:tt)*) => ();
}