oracle_nosql_rust_sdk_derive/
lib.rs1extern crate proc_macro;
8extern crate proc_macro2;
9extern crate syn;
10#[macro_use]
11extern crate quote;
12
13use proc_macro::TokenStream;
14use proc_macro2::{TokenStream as TokenStream2, TokenTree};
15use syn::{
16 parse::Parser, parse_macro_input, Data, DeriveInput, GenericArgument, Meta, PathArguments,
17 Type, TypePath,
18};
19
20#[proc_macro_derive(NoSQLRow, attributes(nosql))]
29pub fn to_from_map_value(input: TokenStream) -> TokenStream {
30 let input = parse_macro_input!(input as DeriveInput);
32
33 impl_to_from_map_value(input)
35}
36
37fn impl_to_from_map_value(input: DeriveInput) -> TokenStream {
38 let name = &input.ident;
40 let name_string = name.to_string();
41
42 let ds;
44 if let Data::Struct(d) = input.data {
45 ds = d;
46 } else {
47 panic!("NoSQLRow only supports Struct datatypes");
48 }
49
50 #[derive(Debug)]
51 struct FieldNameType {
52 fname: String,
53 alias: Option<String>,
54 }
56
57 let mut fntypes: Vec<FieldNameType> = Vec::new();
58
59 for field in ds.fields {
60 let mut alias: Option<String> = None;
64 for a in field.attrs {
65 let mut good: bool = false;
66 if let Meta::List(l) = a.meta {
67 for s in l.path.segments {
68 if s.ident == "nosql" {
69 good = true;
70 break;
71 }
72 }
73 if good == false {
74 continue;
75 }
76 let mut is_column: bool = false;
79 for t in l.tokens {
80 match t {
82 TokenTree::Ident(i) => {
83 if is_column {
84 alias = Some(i.to_string());
85 break;
86 }
87 if i.to_string() == "column" {
88 is_column = true;
89 } else {
90 is_column = false;
91 }
92 }
93 _ => (),
95 }
96 }
97 if alias.is_some() {
98 break;
99 }
100 }
101 }
102
103 let fname = if let Some(id) = field.ident {
104 id.to_string()
105 } else {
106 panic!("Field in NoSQLRow is missing ident");
107 };
108
109 let _ftype = if let Type::Path(p) = field.ty {
113 get_path_segment(&p, "")
115 } else {
116 panic!("Field type in NoSQLRow does not have Path element");
117 };
118 fntypes.push(FieldNameType { fname, alias });
119 }
121
122 let mut tbody = TokenStream2::default();
125 let mut fbody = TokenStream2::default();
126 for f in fntypes {
127 let fname = format_ident!("{}", f.fname);
128 let fnameq: String;
129 match f.alias {
130 Some(s) => fnameq = s,
131 None => fnameq = f.fname,
132 }
133 tbody.extend(quote! {
134 m.put(#fnameq, &self.#fname);
135 });
136 fbody.extend(quote! {
137 self.#fname = self.#fname.from_map(#fnameq, value)?;
138 });
139 }
140
141 let expanded = quote! {
142 impl NoSQLRow for #name {
143 fn to_map_value(&self) -> Result<MapValue, oracle_nosql_rust_sdk::NoSQLError> {
144 let mut m = MapValue::new();
145 #tbody
146 Ok(m)
147 }
148
149 fn from_map_value(&mut self, value: &MapValue) -> Result<(), oracle_nosql_rust_sdk::NoSQLError> {
150 #fbody
151 Ok(())
152 }
153 }
154
155 impl NoSQLColumnToFieldValue for #name {
156 fn to_field_value(&self) -> FieldValue {
157 let m = self.to_map_value();
158 if let Ok(mv) = m {
159 return FieldValue::Map(mv);
160 }
161 FieldValue::Null
163 }
164 }
165
166 impl NoSQLColumnFromFieldValue for #name {
167 fn from_field(fv: &FieldValue) -> Result<Self, oracle_nosql_rust_sdk::NoSQLError> {
168 if let FieldValue::Map(v) = fv {
169 let mut s: #name = Default::default();
170 s.from_map_value(v)?;
171 return Ok(s);
172 }
173 Err(oracle_nosql_rust_sdk::NoSQLError::new(
174 oracle_nosql_rust_sdk::NoSQLErrorCode::IllegalArgument,
175 format!("NoSQL: Error converting field into {}: expected FieldValue::Map, actual: {:?}", #name_string, fv).as_str()))
176 }
177 }
178
179 };
180
181 TokenStream::from(expanded)
184}
185
186fn get_path_segment(p: &TypePath, val: &str) -> String {
187 for elem in &p.path.segments {
188 let mut s = elem.ident.to_string();
189 if let PathArguments::AngleBracketed(args) = &elem.arguments {
191 for a in &args.args {
192 if let GenericArgument::Type(tp) = a {
193 if let Type::Path(p1) = tp {
194 s = val.to_string() + &s;
196 return get_path_segment(&p1, &s);
197 }
198 }
199 }
200 return val.to_string();
203 }
204 s = val.to_string() + &s;
206 return s.to_string();
208 }
209 val.to_string()
211}
212
213#[proc_macro_attribute]
215pub fn add_planiter_fields(args: TokenStream, input: TokenStream) -> TokenStream {
216 let mut item_struct = parse_macro_input!(input as syn::ItemStruct);
217 let _ = parse_macro_input!(args as syn::parse::Nothing);
218
219 if let syn::Fields::Named(ref mut fields) = item_struct.fields {
220 fields.named.push(
222 syn::Field::parse_named
223 .parse2(quote! { result_reg: i32 })
224 .unwrap(),
225 );
226 fields.named.push(
227 syn::Field::parse_named
228 .parse2(quote! { loc: Location })
229 .unwrap(),
230 );
231 }
232
233 return quote! {
234 #item_struct
235 }
236 .into();
237}