use std::{any::Any, collections::HashMap, sync::Mutex};
use nidrs_extern::datasets::{self, get_meta_key, get_meta_key_by_ref, MetaKey};
use once_cell::sync::Lazy;
use proc_macro::{token_stream, TokenStream};
use proc_macro2::TokenStream as TokenStream2;
use quote::ToTokens;
use syn::{ext, parse::Parse, punctuated::Punctuated, spanned::Spanned, Expr, ExprCall, PatPath};
use crate::{
app_parse::{get_current_app_path, parse_main_macro_args},
current_module,
};
static CMETA_STACK: Lazy<Mutex<Option<CMeta>>> = Lazy::new(|| Mutex::new(None));
#[derive(Debug, Clone)]
pub struct MetaData {
expr: String,
value: Box<CMetaValue>,
key: CMetaKey,
}
impl MetaData {
pub fn key(&self) -> String {
if let CMetaKey::String(k) = self.key.clone() {
return k;
} else {
eprintln!("[cmeta.MetaData.key] unknown");
return "".to_string();
}
}
pub fn value(&self) -> CMetaValue {
*self.value.clone()
}
}
impl From<Expr> for MetaData {
fn from(expr_value: Expr) -> Self {
let expr = expr_value.to_token_stream().to_string();
let mut key = CMetaKey::None;
let mut value = CMetaValue::None;
match expr_value {
Expr::Path(path) => {
let paths = path.path.segments.iter().map(|s| s.ident.to_string()).collect::<Vec<String>>();
let k = paths.join("::").replace(" ", "");
key = CMetaKey::String(k);
}
Expr::Call(call_path) => {
if let Expr::Path(path) = *call_path.func.clone() {
let paths = path.path.segments.iter().map(|s| s.ident.to_string()).collect::<Vec<String>>();
let k1 = paths[paths.len() - 1].clone();
if k1 == "from" {
let k = paths[paths.len() - 2].clone();
key = CMetaKey::String(k);
} else {
key = CMetaKey::String(k1);
}
if let Some(args) = call_path.args.first() {
value = CMetaValue::from(args.clone());
} else {
value = CMetaValue::None;
}
}
}
_ => (),
};
MetaData { expr, value: Box::new(value), key }
}
}
impl Into<Expr> for MetaData {
fn into(self) -> Expr {
syn::parse_str(&self.expr).expect(&format!("[cmeta.MetaData.into] parse expr error, expr:`{}`", &self.expr))
}
}
#[derive(Debug, Clone)]
pub enum CMetaLevel {
Global(String),
Module(String),
Service(String),
Handler(String),
}
#[derive(Debug, Clone)]
pub enum CMetaKey {
String(String),
None,
}
#[derive(Debug, Clone)]
pub enum CMetaValue {
String(String),
Bool(bool),
Int(i64),
Float(f64),
Array(Vec<CMetaValue>),
Object(HashMap<String, CMetaValue>),
MetaData(MetaData),
None,
}
impl Into<bool> for CMetaValue {
fn into(self) -> bool {
if let CMetaValue::Bool(b) = self {
return b;
}
return false;
}
}
impl Into<i64> for CMetaValue {
fn into(self) -> i64 {
if let CMetaValue::Int(i) = self {
return i;
}
return 0;
}
}
impl Into<f64> for CMetaValue {
fn into(self) -> f64 {
if let CMetaValue::Float(f) = self {
return f;
}
return 0.0;
}
}
impl From<Expr> for CMetaValue {
fn from(exp: Expr) -> Self {
match &exp {
Expr::Lit(lit) => {
let lit = lit.lit.clone();
if let syn::Lit::Str(s) = lit {
CMetaValue::String(s.value())
} else if let syn::Lit::Bool(b) = lit {
CMetaValue::Bool(b.value)
} else if let syn::Lit::Int(i) = lit {
CMetaValue::Int(i.base10_parse().unwrap())
} else if let syn::Lit::Float(f) = lit {
CMetaValue::Float(f.base10_parse().unwrap())
} else {
CMetaValue::String(lit.to_token_stream().to_string())
}
}
Expr::Closure(closure) => CMetaValue::None,
Expr::Array(array) => {
let mut arr = Vec::new();
for item in array.elems.iter() {
arr.push(CMetaValue::from(item.clone()));
}
CMetaValue::Array(arr)
}
_ => CMetaValue::MetaData(exp.into()),
}
}
}
impl Into<Expr> for CMetaValue {
fn into(self) -> Expr {
match self {
CMetaValue::String(s) => syn::parse_str(&format!("\"{}\"", s)).expect("[cmeta.CMetaValue.into] parse string error"),
CMetaValue::Bool(b) => syn::parse_str(&format!("{}", b)).expect("[cmeta.CMetaValue.into] parse bool error"),
CMetaValue::Int(i) => syn::parse_str(&format!("{}", i)).expect("[cmeta.CMetaValue.into] parse int error"),
CMetaValue::Float(f) => syn::parse_str(&format!("{}", f)).expect("[cmeta.CMetaValue.into] parse float error"),
CMetaValue::Array(arr) => {
let mut items: Vec<Expr> = Vec::new();
for item in arr.iter() {
items.push(item.clone().into());
}
syn::parse_str(&format!("[{}]", items.iter().map(|i| i.to_token_stream().to_string()).collect::<Vec<String>>().join(", ")))
.expect("[cmeta.CMetaValue.into] parse array error")
}
CMetaValue::Object(obj) => {
let mut items = Vec::new();
for (k, v) in obj.iter() {
let k = syn::Ident::new(&k, proc_macro2::Span::call_site());
let v: Expr = v.clone().into();
items.push(quote::quote! {
#k: #v
});
}
syn::parse_str(&format!("{{ {} }}", items.iter().map(|i| i.to_token_stream().to_string()).collect::<Vec<String>>().join(", ")))
.expect("[cmeta.CMetaValue.into] parse object error")
}
CMetaValue::MetaData(meta) => meta.into(),
CMetaValue::None => syn::parse_str("").expect("[cmeta.CMetaValue.into] parse none error"),
}
}
}
impl From<CMetaValue> for String {
fn from(v: CMetaValue) -> String {
if let CMetaValue::String(s) = v {
return s;
}
panic!("Invalid type");
}
}
#[derive(Debug)]
pub struct CMeta {
data: HashMap<String, CMetaValue>,
extends: Option<Box<CMeta>>,
}
impl CMeta {
pub fn new() -> CMeta {
return CMeta { data: HashMap::new(), extends: None };
}
pub fn collect(mut cmeta: CMeta) {
println!("// CMETA: {:?}", cmeta.keys());
let mut current = CMETA_STACK.lock().unwrap();
if let Some(mut current) = current.as_mut() {
current.merge(cmeta);
} else {
*current = Some(cmeta);
}
}
pub fn get_level() -> Option<CMetaLevel> {
let stack = CMETA_STACK.lock().unwrap();
if let Some(cmeta) = stack.as_ref() {
return cmeta.level();
}
return None;
}
pub fn get_deep() -> usize {
let stack = CMETA_STACK.lock().unwrap();
if let Some(cmeta) = stack.as_ref() {
return cmeta.deep();
}
return 0;
}
pub fn get_stack<K: Into<String>>(key: K) -> Option<CMetaValue> {
let stack = CMETA_STACK.lock().unwrap();
if let Some(cmeta) = stack.as_ref() {
return cmeta.get(key).cloned();
}
return None;
}
pub fn get_stack_data<K: Into<String>, R>(key: K) -> Option<R>
where
R: From<CMetaValue>,
{
if let Some(CMetaValue::MetaData(v)) = CMeta::get_stack(key) {
Some(v.value().into())
} else {
None
}
}
pub fn push(level: CMetaLevel) {
println!("// >>Push: {:?} -- [{:?}]", level, CMeta::get_stack("module"));
let mut cmeta = CMeta::new();
cmeta.set(
match level {
CMetaLevel::Global(_) => "global",
CMetaLevel::Module(_) => "module",
CMetaLevel::Service(_) => "service",
CMetaLevel::Handler(_) => "handler",
},
match level {
CMetaLevel::Global(name) => CMetaValue::String(name),
CMetaLevel::Module(name) => CMetaValue::String(name),
CMetaLevel::Service(name) => CMetaValue::String(name),
CMetaLevel::Handler(name) => CMetaValue::String(name),
},
);
let mut stack = CMETA_STACK.lock().unwrap();
let mut opt_cm: Option<CMeta> = stack.take();
if let Some(mut cm) = opt_cm {
cmeta.extends(cm);
}
*stack = Some(cmeta);
}
pub fn pop() {
let mut opt_cm: Option<CMeta> = CMETA_STACK.lock().unwrap().take();
if let Some(mut cm) = opt_cm {
println!("// << Pop: {:?} {:?}\n", cm.level(), cm.keys());
let mut tail = cm.tail();
if let Some(mut t) = tail {
*CMETA_STACK.lock().unwrap() = Some(*t);
}
}
}
pub fn build_tokens() -> TokenStream2 {
let stack = CMETA_STACK.lock().unwrap();
if let Some(cmeta) = stack.as_ref() {
let tokens = cmeta.to_tokens();
return quote::quote! {
let mut meta = nidrs::InnerMeta::new();
#tokens
meta
};
}
return quote::quote! {};
}
pub fn merge(&mut self, cmeta: CMeta) {
for (k, v) in cmeta.data.iter() {
self.data.insert(k.clone(), v.clone());
}
}
pub fn extends(&mut self, cmeta: CMeta) {
self.extends = Some(Box::new(cmeta));
}
pub fn tail(&mut self) -> Option<Box<CMeta>> {
return self.extends.take();
}
pub fn set<K: Into<String>, V: Into<CMetaValue>>(&mut self, key: K, value: V) {
self.data.insert(key.into(), value.into());
}
pub fn set_data<V: Into<CMetaValue>>(&mut self, value: V) {
let cmv = value.into();
if let CMetaValue::MetaData(v) = &cmv {
self.data.insert(v.key(), cmv);
} else {
panic!("[cmeta.CMeta.set_data] unknown");
}
}
pub fn get<K: Into<String>>(&self, key: K) -> Option<&CMetaValue> {
let key = key.into();
let c = self.data.get(&key);
if let Some(c) = c {
return Some(c);
}
if let Some(extends) = &self.extends {
return extends.get(&key);
}
return None;
}
pub fn keys(&self) -> Vec<String> {
let mut ret_keys = self.data.keys().map(|k| k.clone()).collect::<Vec<String>>();
if let Some(extends) = &self.extends {
let res = extends.keys();
for k in res.iter() {
if !ret_keys.contains(k) {
ret_keys.push(k.clone());
}
}
}
ret_keys
}
pub fn level(&self) -> Option<CMetaLevel> {
if let Some(CMetaValue::String(name)) = self.get("handler") {
return Some(CMetaLevel::Handler(name.clone()));
} else if let Some(CMetaValue::String(name)) = self.get("service") {
return Some(CMetaLevel::Service(name.clone()));
} else if let Some(CMetaValue::String(name)) = self.get("module") {
return Some(CMetaLevel::Module(name.clone()));
} else if let Some(CMetaValue::String(name)) = self.get("global") {
return Some(CMetaLevel::Global(name.clone()));
}
return None;
}
pub fn deep(&self) -> usize {
let mut deep = 1;
if let Some(extends) = &self.extends {
deep += extends.deep();
}
return deep;
}
pub fn to_tokens(&self) -> TokenStream2 {
let mut items = Vec::new();
let keys = self.keys();
for k in keys.iter() {
let v = self.get(k).expect("[cmeta.CMeta.to_tokens] get value error");
let tokens: Expr = v.clone().into();
if let CMetaValue::MetaData(_) = &v {
items.push(quote::quote! {
meta.set_data(#tokens);
});
} else {
items.push(quote::quote! {
meta.set(#k, #tokens);
});
}
}
let cmeta = quote::quote! {
#(#items)*
};
cmeta
}
}
impl Parse for CMeta {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let items: Punctuated<Expr, syn::Token![,]> = Punctuated::parse_terminated(input)?;
let mut cmeta = CMeta::new();
for item in items.iter() {
if let syn::Expr::Assign(assign) = item {
if let syn::Expr::Path(path) = *assign.left.clone() {
let k = path.path.segments.first().expect("[cmeta.CMeta.parse] path.segments.first").ident.to_string();
let v = *assign.right.clone();
cmeta.set(k, v);
}
} else if let syn::Expr::Path(_) = item {
cmeta.set_data(item.clone());
} else if let syn::Expr::Call(_) = item {
cmeta.set_data(item.clone());
} else {
return Err(syn::Error::new(item.span(), "unknown"));
}
}
Ok(cmeta)
}
}
pub fn init_app_meta() {
CMeta::push(CMetaLevel::Global("app".to_string()));
let cmeta = CMeta::new();
if let Some(app_path) = get_current_app_path() {
if app_path.exists() {
let app = std::fs::read_to_string(app_path.clone()).expect(&format!("[init_app_meta.read_to_string] read {app_path:?} file error"));
let app_ast = syn::parse_file(&app).expect(&format!("[init_app_meta.parse_file] parse {app:?} file error"));
for item in app_ast.items.iter() {
if let syn::Item::Fn(item_fn) = item {
if let Some(args) = parse_main_macro_args(item_fn) {
}
}
}
}
}
CMeta::collect(cmeta)
}
pub fn init_module_meta() {
let mod_opts = current_module::get();
if let Some(mod_opts) = mod_opts {
CMeta::push(CMetaLevel::Module(mod_opts.name));
}
}