parent
4dbcd30e23
commit
dad9d5f8c7
@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "lunanode_macros"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
proc_macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = "1.0"
|
||||
quote = "1.0"
|
@ -0,0 +1,88 @@
|
||||
/// This module is designed to automatically add the essential types to a LunaNodeRequest struct, without developer intervention.
|
||||
/// You should apply this to every LunaNodeRequest struct *BEFORE* any other macros, and especially before any derive macros.
|
||||
/// Usages:
|
||||
/// ```rust
|
||||
/// #[lunanode_request(response="ImageListResponse", endpoint="image/list/")]
|
||||
/// #[derive(Serialize, Deserialize, Debug, ...)]
|
||||
/// struct MyStruct {
|
||||
/// ...
|
||||
/// ```
|
||||
///
|
||||
/// TODO: Improve error messages.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::parse::{Parse, ParseStream, Parser, Result};
|
||||
use syn::{parse, parse_macro_input, punctuated::Punctuated, Attribute, AttributeArgs, Expr, Ident, ItemStruct, Local, Lit, LitStr, NestedMeta, Meta, MetaNameValue, Pat, Stmt, Type, Token, DeriveInput};
|
||||
|
||||
#[derive(Debug, Hash, Eq, PartialEq)]
|
||||
enum LunanodeRequestParam {
|
||||
Invalid,
|
||||
Response(Type),
|
||||
EndPoint(String),
|
||||
}
|
||||
impl LunanodeRequestParam {
|
||||
fn from(key: String, val: String) -> Self {
|
||||
match (key.as_str(), val.as_str()) {
|
||||
("response", tp) => Self::Response(syn::parse_str(tp).expect("The value given to the 'response' parameter must be a valid type.")),
|
||||
("endpoint", ep) => Self::EndPoint(ep.to_string()),
|
||||
_ => Self::Invalid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn lunanode_request(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let mut item_struct = parse_macro_input!(input as ItemStruct);
|
||||
let args_parsed: Vec<LunanodeRequestParam> = parse_macro_input!(attr as AttributeArgs)
|
||||
.into_iter()
|
||||
.filter_map(|nm| match nm {
|
||||
NestedMeta::Meta(Meta::NameValue(MetaNameValue { path, eq_token, lit: Lit::Str(lstr) })) => Some(
|
||||
(path.segments
|
||||
.into_iter()
|
||||
.map(|seg| seg.ident.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.swap_remove(0),
|
||||
lstr.value())
|
||||
),
|
||||
_ => None
|
||||
})
|
||||
.map(|(k,v)| LunanodeRequestParam::from(k, v))
|
||||
.collect();
|
||||
let response_type = match args_parsed.get(0).expect("There must be two argument for the macro.") {
|
||||
LunanodeRequestParam::Response(res) => res,
|
||||
_ => panic!("The response parameter must be a type; it must also be first."),
|
||||
};
|
||||
let url_endpoint = match args_parsed.get(1).expect("There must be two arguments for the macro.") {
|
||||
LunanodeRequestParam::EndPoint(ep) => ep,
|
||||
_ => panic!("The endpoint parameter must be a string; it must also be last.")
|
||||
};
|
||||
let name = item_struct.ident.clone();
|
||||
|
||||
if let syn::Fields::Named(ref mut fields) = item_struct.fields {
|
||||
fields.named.push(
|
||||
syn::Field::parse_named
|
||||
.parse2(quote! {
|
||||
#[serde(flatten)]
|
||||
#[clap(flatten)]
|
||||
pub keys: ApiKeys
|
||||
})
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
return quote! {
|
||||
#item_struct
|
||||
impl LunaNodeRequest for #name {
|
||||
type response = #response_type; // TODO: Last section that needs to be dynamic
|
||||
fn get_keys(&self) -> ApiKeys {
|
||||
self.keys.clone()
|
||||
}
|
||||
fn url_endpoint(&self) -> String {
|
||||
#url_endpoint.to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
.into();
|
||||
}
|
Loading…
Reference in new issue