af_path 0.2.3

path查询语言库
Documentation
use syn::__private::TokenStream2;
use crate::json;
use crate::json::{Table, Field};
use std::collections::HashMap;

// 关系类型
enum AssocialType {
    OneToOne,
    OneToMany,
    ManyToOne,
}

/// 产生数据库定义
/// @param json:全局配置
pub fn tables(map: &HashMap<String, json::Table>) -> TokenStream2 {
    // 一遍过滤产生数据库 (表定义,所有表的初始化,表的加载函数) 三项内容。由于无法对quote的初始值进行判断,依靠计数器判读是否第一次做。
    let (gen, tables_gen, load_gen, _i) = map.iter().fold( (quote!{}, quote!{}, quote!{}, 0), |(acc_gen, acc_tables, acc_load, acc), (k, v)| {
        // 产生数据库结构体内容
        let name: syn::Ident = syn::parse_str(&k).expect(&format!("数据库表名不是合法变量:{:?}", &k));
        // 获得主键类型,以便产生表描述
        let key_type = get_key_type(&v);

        let table_def = quote! { 
            pub #name: Table<#key_type, entity::#name>, 
        };

        let gen = quote! {
            #acc_gen #table_def
        };

        // 产生数据库结构体初始化过程
        let table_gen = quote! { 
            #name:Table{data:BTreeMap::new()} 
        };
        let tables_gen = match acc {
            0 => quote! { #table_gen },
            _ => quote! { #acc_tables, #table_gen },
        };

        // 产生数据库的加载函数
        let load_gen = quote! { #acc_load self.#name.load_data(db_time, #k, &json[#k], conn, &f).await?; };

        (gen, tables_gen, load_gen, acc + 1)
    });

    // 产生加载后续数据的过程
    let load_next_gen: TokenStream2 = map.iter().map(|(k, _v)| {
        load_next(&k)
    }).collect();

    // 产生清空表内容函数
    let clear_gen = clear_gen(map);

    // 一次性取得创建一对多关系函数以及一对一关系函数
    let create_relation_gen = create_relation(map, |k, slave_table, slave_key, field_key, _slave_id| {
        create_one(k, slave_table, slave_key, field_key, AssocialType::OneToOne)
    }, |k, slave_table, slave_key, field_key, _slave_id| {
        create_one(k, slave_table, slave_key, field_key, AssocialType::OneToMany)
    }, |k, slave_table, slave_key, field_key, _slave_id| {
        create_one(k, slave_table, slave_key, field_key, AssocialType::ManyToOne)
    });

    // 取消关系的代码
    let remove_relation_gen = create_relation(map, |k, slave_table, slave_key, field_key, slave_id| {
        remove_one(k, slave_table, slave_key, field_key, slave_id, AssocialType::OneToOne)
    }, |k, slave_table, slave_key, field_key, slave_id| {
        remove_one(k, slave_table, slave_key, field_key, slave_id, AssocialType::OneToMany)
    }, |k, slave_table, slave_key, field_key, slave_id| {
        remove_one(k, slave_table, slave_key, field_key, slave_id, AssocialType::ManyToOne)
    });

    // 重建关系代码
    let create_next_gen = create_relation(map, |k, slave_table, slave_key, field_key, _slave_id| {
        create_next(k, slave_table, slave_key, field_key, AssocialType::OneToOne)
    }, |k, slave_table, slave_key, field_key, _slave_id| {
        create_next(k, slave_table, slave_key, field_key, AssocialType::OneToMany)
    }, |k, slave_table, slave_key, field_key, _slave_id| {
        create_next(k, slave_table, slave_key, field_key, AssocialType::ManyToOne)
    });

    // 建立一个全局数据库对象
    quote! {
        pub struct DataBase { #gen }
        // 定义一个全局的数据库对象
        pub static mut DATABASE: DataBase = DataBase{ #tables_gen };
        // 实现数据库的加载函数
        impl DataBase {
            // 加载函数
            pub async fn load<F>(&self, db_time: &Update, json: &HashMap<String, json::Table>, conn: &mut MssqlConnection, f: F) -> Result<(), sqlx::Error> 
            where F: Fn(ClosureType) {
                #load_gen 
                Ok(())
            }

            // 加载后续数据,并建立关系
            pub async fn load_next<F>(db_time: &Update, json: &HashMap<String, json::Table>, conn: &mut MssqlConnection, f: F) -> Result<(), sqlx::Error> 
            where F: Fn(ClosureType) {
                unsafe {
                    #load_next_gen
                    #remove_relation_gen
                    #create_next_gen
                }
                Ok(())
            }

            // 清空表内容函数
            pub fn clear(&mut self) { 
                #clear_gen 
            }

            // 创建一对多关系函数
            pub fn create_relation<F>(f: F) where F: Fn(ClosureType) { 
                #create_relation_gen 
            }
        }
    }
}

// 产生清空所有表内容的程序
fn clear_gen(map: &HashMap<String, json::Table>) -> TokenStream2 {
    // 对于每一个表
    map.iter().fold(quote!{}, |acc, (table_name, _table)| {
        let name: syn::Ident = syn::parse_str(&table_name).expect(&format!("数据库表名不是合法变量:{:?}", &table_name));
        let gen = quote! {
            self.#name.clear();
        };
        quote! { #acc #gen }
    })
}

// 获取主键类型,如果没有主键,返回() 
// 返回:主键类型声明的rust代码
fn get_key_type(json: &Table) -> TokenStream2 {
    // 获得主键名
    let name = &json.key;
    // 根据主键名获取类型
    let key_type = &json.fields[&name.to_string()];
    match key_type {
        Field::Int => quote! { i32 },
        Field::String => quote! { String },
        _ => panic!("这种类型不能做主键,{:?}", key_type)
    }
}

// 产生创建一对多及一对一关系的代码
// @f: 找到关系后的处理过程,可以是第一次创建关系,也可以是移除关系,也可以是后续创建关系
// @return 一次产生一对多关系及一对一关系,(一对多关系代码,一对一关系代码)
fn create_relation<FOne, FMany, FToOne>(map: &HashMap<String, json::Table>, f_one: FOne, f_many: FMany, f_to_one: FToOne) -> TokenStream2 where 
FOne: Fn(&str, &str, &str, &str, &str) -> TokenStream2, 
FMany: Fn(&str, &str, &str, &str, &str) -> TokenStream2,
FToOne: Fn(&str, &str, &str, &str, &str) -> TokenStream2 {
    // 对于每一个表的一对多关系声名,创建一次关系
    // all_acc:一对多关系累加,one_acc:一对一关系累加
    let result: TokenStream2 = map.iter().map(|(k, v)| {
        let fields = &v.fields;
        // 对表中字段进行循环,一次性产生一对多及一对一关系,(一对多关系,一对一关系)
        let result_in: TokenStream2 = fields.iter().map(|(field_key, field_attr)| {
            // 根据类型处理
            match field_attr {
                // 一对多关系,需要根据主表及从表的各项描述,建立一次关系
                Field::OneToMany(slave_table, slave_key) => {
                    // 获取从表主键
                    let slave_id = &map.get(slave_table).expect("从表不存在").key;
                    f_many(&k, &slave_table, &slave_key, &field_key, slave_id)
                },
                // 一对一关系,需要根据主表及从表的各项描述,建立一次关系
                Field::OneToOne(slave_table, slave_key) => {
                    // 获取从表主键
                    let slave_id = &map.get(slave_table).expect("从表不存在").key;
                    f_one(&k, &slave_table, &slave_key, &field_key, slave_id)
                },
                // 多对一关系建立,表对应的是主表
                Field::ManyToOne(slave_table, slave_key) => {
                    let slave_id = &map.get(slave_table).expect("从表不存在").key;
                    f_to_one(&slave_table, &k, &slave_key, &field_key, slave_id)
                }
                // 非一对多关系字段,不用处理
                Field::Int | Field::Double | Field::String | Field::Datetime => quote!{},
            }
        }).collect();
        result_in
    }).collect();
    result
}

// 产生建立一个一对多关系的代码
// @param main_table: 主表名
// @param slave_table: 从表名
// @param slave_key_str: 从表外键名
// @param main_field_str: 主表的字段名
// @param ty: 关系类型,包括:一对一,一对多,多对一
fn create_one(main_table: &str, slave_table: &str, slave_key_str: &str, main_field_str: &str, ty: AssocialType) -> TokenStream2 {

    let main_name: syn::Ident = syn::parse_str(&main_table).expect(&format!("表名不是合法变量,{:?}", main_table));
    let slave_name: syn::Ident = syn::parse_str(&slave_table).expect(&format!("表名不是合法变量,{:?}", slave_table));
    let slave_key: syn::Ident = syn::parse_str(&slave_key_str).expect(&format!("外键不是合法变量,{:?}", slave_key_str));
    let main_field: syn::Ident = syn::parse_str(&main_field_str).expect(&format!("主表字段不是合法变量,{:?}", main_field_str));

    // 根据是否一对一,确定建立关系的语句
    let create_statement = match ty {
        AssocialType::OneToOne => quote! { v.#main_field = Some(&slave_v) },
        AssocialType::OneToMany => quote! { v.#main_field.push(&slave_v) },
        AssocialType::ManyToOne => quote! { slave_v.#main_field = Some(&v) },
    };

    // 多对一属于反方向操作,从表遍历及主表获取可变性刚好相反
    let iter_gen = if let AssocialType::ManyToOne = ty {
        quote! { iter_mut }
    } else {
        quote! { iter }
    };

    // 多对一主表不改,从表改
    let get_gen = if let AssocialType::ManyToOne = ty {
        quote! { get }
    } else {
        quote! { get_mut }
    };

    quote! {
        // 按从表数据进行遍历,根据外键在主表中找到数据,建立关系
        for (_, slave_v) in unsafe{DATABASE.#slave_name.data.#iter_gen()} {
            let t_main = unsafe{&mut DATABASE.#main_name.data};
            if let Some(v) = t_main.#get_gen(&slave_v.#slave_key) {
                let update: ClosureType = Box::new(move || {#create_statement;});
                f(update);
            }
        }
    }
}

// 产生后续建立关系的代码,后续关系的建立,依据变更数据
// @param main_table: 主表名
// @param slave_table: 从表名
// @param slave_key_str: 从表外键名
// @param main_field_str: 主表的字段名
// @param is_to_one: 是否一对一关系,不是,则按一对多关系处理
fn create_next(main_table: &str, slave_table: &str, slave_key_str: &str, main_field_str: &str, ty: AssocialType) -> TokenStream2 {

    let main_name: syn::Ident = syn::parse_str(&main_table).expect(&format!("表名不是合法变量,{:?}", main_table));
    let slave_name: syn::Ident = syn::parse_str(&slave_table).expect(&format!("表名不是合法变量,{:?}", slave_table));
    let slave_key: syn::Ident = syn::parse_str(&slave_key_str).expect(&format!("外键不是合法变量,{:?}", slave_key_str));
    let main_field: syn::Ident = syn::parse_str(&main_field_str).expect(&format!("主表字段不是合法变量,{:?}", main_field_str));

    // 从表新加实体id号
    let slave_keys: syn::Ident = syn::parse_str(&format!("{}_keys", slave_table)).expect(&format!("主表字段不是合法变量,{:?}", slave_table));

    // 根据是否一对一,确定建立关系的语句
    let create_statement = match ty {
        AssocialType::OneToOne => quote! { v.#main_field = Some(&slave_v) },
        AssocialType::OneToMany => quote! { v.#main_field.push(&slave_v) },
        AssocialType::ManyToOne => quote! { 
            //slave_v.#main_field = Some(&v)
        },
    };

    quote! {
        // 按从表数据进行遍历,根据外键在主表中找到数据,建立关系
        for slave_id in &#slave_keys {
            // 根据主键获取数据
            let slave_v = unsafe{DATABASE.#slave_name.data.get(&slave_id).unwrap()};
            let t_main = unsafe{&mut DATABASE.#main_name.data};
            if let Some(v) = t_main.get_mut(&slave_v.#slave_key) {
                let update: ClosureType = Box::new(move || {#create_statement;});
                f(update);
            }
        }  
    }
}

// 产生移走一对多关系的代码
// @param main_table: 主表名
// @param slave_table: 从表名
// @param slave_key_str: 从表外键名
// @param main_field_str: 主表的字段名
// @param is_to_one: 是否一对一关系,不是,则按一对多关系处理
fn remove_one(main_table: &str, slave_table: &str, slave_key_str: &str, main_field_str: &str, slave_id_str: &str, ty: AssocialType) -> TokenStream2 {

    let main_name: syn::Ident = syn::parse_str(&main_table).expect(&format!("表名不是合法变量,{:?}", main_table));
    let slave_key: syn::Ident = syn::parse_str(&slave_key_str).expect(&format!("外键不是合法变量,{:?}", slave_key_str));
    let slave_id: syn::Ident = syn::parse_str(&slave_id_str).expect(&format!("外键不是合法变量,{:?}", slave_id_str));
    let main_field: syn::Ident = syn::parse_str(&main_field_str).expect(&format!("主表字段不是合法变量,{:?}", main_field_str));

    // 从表的变更实体名
    let slave_entity: syn::Ident = syn::parse_str(&format!("{}_entity", slave_table)).expect(&format!("主表字段不是合法变量,{:?}", slave_table));

    // 根据是否一对一,确定建立关系的语句
    let create_statement = match ty {
        AssocialType::OneToOne => quote! { 
            // 主表的关联部分内容设置成空
            let update: ClosureType = Box::new(move || {v.#main_field = None;});
            f(update);
        },
        AssocialType::OneToMany => quote! { 
            // 找到要删除的对象
            if let Some(pos) = v.#main_field.iter().position(|x| x.#slave_id == slave_v.#slave_id) {
                // 从关系中删除
                let update: ClosureType = Box::new(move || {v.#main_field.remove(pos);});
                f(update);
            }
        },
        AssocialType::ManyToOne => quote! {
            // 从表多关联部分设置为空
            let update: ClosureType = Box::new(move || {
                //slave_v.#main_field = None;
            });
            f(update);
        }
    };

    quote! {
        // 按从表数据进行遍历,根据外键在主表中找到数据,建立关系
        for slave_v in &#slave_entity {
            let t_main = unsafe{&mut DATABASE.#main_name.data};
            if let Some(v) = t_main.get_mut(&slave_v.#slave_key) {
                #create_statement
            }
        }
    }
}

// 产生加载后续数据的代码
fn load_next(k: &str) -> TokenStream2 {
    // 产生名字,主键变量名,旧对象变量名
    let name: syn::Ident = syn::parse_str(k).expect(&format!("数据库表名不是合法变量:{:?}", k));
    let key_name: syn::Ident = syn::parse_str(&format!("{}_keys", k)).expect(&format!("数据库表名不是合法变量:{:?}", k));
    let entity_name: syn::Ident = syn::parse_str(&format!("{}_entity", k)).expect(&format!("数据库表名不是合法变量:{:?}", k));

    quote! {
        // 新加载数据的主键,加载完成后,要根据这些主键找到数据,建立关系
        let mut #key_name = Vec::new();
        // 替换出来的旧数据,加载完成后,要取消旧数据建立的关系
        let mut #entity_name = Vec::new();
        DATABASE.#name.load_next(db_time, #k, &json[#k], conn, |x, key, entity_op| {
            // 执行x,把主键保存下来,把变化的实体保存下来
            f(x);
            #key_name.push(key);
            // 把变化的实体保存下来
            if let Some(entity) = entity_op {
                #entity_name.push(entity);
            }
        }).await?;
    }
}