j2s 0.1.0

json字符串转结构体
Documentation
use std::collections::HashMap;

use caisin::set;
use heck::{ToSnakeCase, ToUpperCamelCase};
use serde::{Deserialize, Serialize};
use serde_json::Value;

use crate::GenCodeByValue;

pub fn parse_value(name: &str, v: Value) -> Obj {
    let mut obj = Obj::default();
    obj.name = name.to_string();
    obj.typ_name = name.to_upper_camel_case();
    match v {
        Value::Null => {
            obj.is_opt = true;
            obj.typ_name = "Value".to_string();
        }
        Value::Bool(_) => {
            obj.typ_name = "bool".to_string();
        }
        Value::Number(v) => {
            if v.is_u64() {
                obj.typ_name = "u64".to_string();
            }
            if v.is_i64() {
                obj.typ_name = "i64".to_string();
            }
            if v.is_f64() {
                obj.typ_name = "f64".to_string();
            }
        }
        Value::String(_) => {
            obj.typ_name = "String".to_string();
        }
        Value::Array(vec) => {
            obj.is_arr = true;
            let mut its = vec![];
            for ele in vec {
                let item = parse_value(name, ele.clone());
                if ele.is_object() {
                    obj.is_obj = true;
                    for (_, v) in item.fileds {
                        its.push(v);
                    }
                } else {
                    obj.typ_name = item.typ_name;
                    break;
                }
            }
            obj.combine(its);
        }
        Value::Object(v) => {
            obj.is_obj = true;
            for (k, v) in v {
                let item = parse_value(&k, v.clone());
                obj.add_filed(item);
            }
        }
    }
    obj
}

#[derive(Serialize, Deserialize, Debug, Default)]
pub struct Obj {
    //字段名
    name: String,
    //类型名
    typ_name: String,
    is_opt: bool,
    is_arr: bool,
    is_obj: bool,
    //字段名称+字段类型
    fileds: HashMap<String, Obj>,
}
impl GenCodeByValue for Obj {
    fn gen(&self) -> String {
        let (mut a, b) = self.to_str();
        a.push('\n');
        a.push_str(&b);
        a
    }
}

impl Obj {
    fn exists(&mut self, f: &Obj) -> bool {
        self.fileds.contains_key(&f.name)
    }
    fn combine(&mut self, fs: Vec<Obj>) {
        let mut exists_name = set!();
        for ele in fs {
            if self.exists(&ele) {
                exists_name.insert(ele.name.clone());
            }
            self.add_filed(ele);
        }
        for (n, obj) in &mut self.fileds {
            if exists_name.contains(n) {
                continue;
            }
            obj.is_opt = true;
        }
    }
    fn add_filed(&mut self, f: Obj) {
        match self.fileds.get_mut(&f.name) {
            Some(o) => {
                if f.is_opt {
                    o.is_opt = true;
                }
            }
            None => {
                self.fileds.insert(f.name.clone(), f);
            }
        }
    }

    fn type_str(&self) -> String {
        let mut str = String::new();
        if self.is_opt {
            str.push_str("Option<");
        }
        if self.is_arr {
            str.push_str("Vec<");
        }
        str.push_str(&self.typ_name);
        if self.is_arr {
            str.push_str(">");
        }
        if self.is_opt {
            str.push_str(">");
        }
        str
    }

    fn to_str(&self) -> (String, String) {
        let mut slf = String::new();
        slf.push_str(&format!(
            "#[derive(Serialize, Deserialize, Debug, Default)]\npub struct {} {{\n",
            self.typ_name
        ));
        let mut subs = String::new();
        let mut fileds_vec = vec![];
        for (name, obj) in &self.fileds {
            let lc = name.to_snake_case();
            if !name.eq(&lc) || obj.is_opt {
                let mut serde_str = String::new();
                serde_str.push_str("    #[serde(");
                let mut attrs = vec![];
                if !name.eq(&lc) || obj.is_opt {
                    attrs.push(format!(r#"rename = "{}""#, name));
                }
                if obj.is_opt {
                    attrs.push(r#"skip_serializing_if = "Option::is_none""#.to_string());
                }
                fileds_vec.push(format!("    #[serde({})]", attrs.join(", ")));
            }
            fileds_vec.push(format!("    pub {}: {},", lc, obj.type_str()));
            if obj.is_obj {
                let (sl, sub) = obj.to_str();
                subs.push_str(&sl);
                subs.push_str(&sub);
            }
        }
        slf.push_str(&fileds_vec.join("\n"));
        slf.push_str("\n}\n");
        subs.push_str("\n");
        (slf, subs)
    }
}