1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
use crate::{IResult, JsonV, JsonVExentd};
use std::fs::File;
use std::io::Read;

use hotwatch::{Event, Hotwatch};
use serde_json::json;
use std::ops::{Deref, DerefMut};
use std::path::Path;
use std::sync::RwLock;

pub type JsonMap = serde_json::Map<String, JsonV>;

/// 配置文件保存
pub static _CONFIG: once_cell::sync::Lazy<RwLock<JsonMap>> = once_cell::sync::Lazy::new(|| RwLock::new(JsonMap::new()));

/// 获得配置
#[allow(dead_code)]
pub(crate) fn get_config_all() -> JsonV {
    _CONFIG.read().map(|v| serde_json::json!(v.deref().clone())).unwrap_or_else(|e| {
        eprintln!("CONFIG.read: {:?}", e);
        JsonV::Null
    })
}

/// 获得配置,使用方式传参key: "api.port"
#[allow(dead_code)]
pub(crate) fn get_config_keys(keys: &str) -> JsonV {
    get_config(keys.split(".").collect::<Vec<&str>>())
}

/// 获得配置,使用方式传参key: vec!["api","port"]
pub fn get_config(keys: Vec<&str>) -> JsonV {
    _CONFIG
        .read()
        .map(|v| {
            let v = v.deref();
            let mut r = JsonV::Null;
            for i in 0..keys.len() {
                let k = keys[i];
                if i == 0 {
                    r = serde_json::json!(v)[k].clone();
                } else {
                    r = serde_json::json!(r)[k].to_owned();
                }
            }
            r
        })
        .unwrap_or_else(|e| {
            eprintln!("CONFIG.read: {:?}", e);
            JsonV::Null
        })
}


/// 监听配置文件修改进行实时读取
///     let ref toml_file_path_str = format!("config-{}.toml", crate::log4rs_mod::EXE_FILE_NAME.to_string());

pub fn run_watch(toml_file_path_str: &str, map: &mut Option<JsonMap>) -> IResult<(Hotwatch, crossbeam::channel::Receiver<String>)> {
    load_file(toml_file_path_str)?;

    let (send, rev) = crossbeam::channel::bounded(10);

    let toml_file_path = toml_file_path_str.to_string();
    let mut hotwatch = Hotwatch::new().expect("Hotwatch failed to initialize.");
    hotwatch.watch(toml_file_path, move |event: Event| {
        if let Event::Write(path) = event {
            let path = path.to_str().unwrap_or_else(|| "");
            if let Err(e) = load_file(path) {
                error!("file reload error: {:?}", e);
            }else{
                info!("file reload: {}", path);
            _ = send.try_send(path.to_string());
            }
            
        }
    })?;

    {
        // 监听的配置文件
        let file_name = get_file_name(toml_file_path_str)?;

        let write_data = |map: &mut JsonMap| {
            let k = "_watch_mode".to_owned();
            if let Some(JsonV::String(v)) = map.get_mut(&k) {
                v.push_str(&format!("{},", file_name));
            } else {
                map.insert(k, json!(file_name));
            }
        };

        if let Some(v) = map{
            write_data(v);
        }else if let Ok(lock) = _CONFIG.write().as_deref_mut() {
            write_data(lock);
        }
    }

    Ok((hotwatch, rev))
}

#[test]
fn test_read_file() {
    let mut b = vec![];
    File::open(file!()).unwrap().read_to_end(&mut b).unwrap();
    let s = String::from_utf8(b).unwrap();
    println!("{}", s);
}

/// 读取配置文件: config.toml
/// F:  std::env::set_var(k, v); |k, v| std::env::set_var(k, v)
fn read_file(toml_file_path: &str) -> IResult<String> {
    let mut file_temp = match File::open(&toml_file_path) {
        Ok(v) => v,
        Err(e) => {
            let msg = format!("打开[{}]文件失败: {:?}", &toml_file_path, e);
            Err(msg)?
        }
    };

    let mut b = vec![];
    let _size = match file_temp.read_to_end(&mut b) {
        Err(e) => {
            let msg = format!("read_to_string[{}]文件失败: {:?}", &toml_file_path, e);
            Err(msg)?
        }
        Ok(_v) => _v,
    };

    let toml_str = match String::from_utf8(b) {
        Err(e) => {
            let msg = format!("解析toml文件失败: {:?}", e);
            Err(msg)?
        }
        Ok(v) => v,
    };

    Ok(toml_str)
}

pub fn get_file_name(path: &str) -> IResult<String> {
    let r = Path::new(path)
        .file_name()
        .and_then(|v| v.to_str().map(|v| v.to_string()))
        .ok_or_else(|| format!("[{}]路径中不存在配置文件名", path))?
        .trim_end_matches(".toml")
        .trim_end_matches("config")
        .trim_end_matches('_')
        .trim_start_matches("config")
        .trim_start_matches("_")
        .to_string();
    Ok(r)
}

pub fn load_file(path: &str) -> IResult<()> {
    let file_name = get_file_name(path)?;
    let str_v = read_file(path)?;
    load_str(file_name, str_v)
}

pub fn load_str(file_name: String, toml_str: String) -> IResult<()> {
    let value = toml_str.parse::<toml::Value>().map_err(|e| format!("解析toml字符串失败: {:?}", e))?;

    let data = serde_json::json!(value).as_object2().ok_or_else(|| "配置文件格式不是一个键值对对象")?;

    if let Ok(mut lock) = _CONFIG.write() {
        if lock.deref().get("mode").is_none() {
            let mode = data
                .get("mode")
                .map(|v| v.clone().as_string2())
                .ok_or_else(|| "读取的第一个配置文件中没有定义mode属性")?
                .ok_or_else(|| "读取的第一个配置文件中定义的mode属性不是一个字符串")?;
            lock.deref_mut().insert("mode".to_owned(), JsonV::String(mode));
        }

        if lock.deref().get("_default_name").is_none() {
            lock.deref_mut().insert("_default_name".to_owned(), JsonV::String(file_name.clone()));
        }

        // 读取后直接赋盖
        lock.deref_mut().insert(file_name, JsonV::Object(data));

        // append 追加 map 数据
        // if let Some(JsonV::Object(map)) = lock.get_mut(file_name.as_str()) {
        //     map.append(&mut data);
        // } else {
        //     lock.deref_mut().insert(file_name, JsonV::Object(data));
        // }

        debug!("config: {}", serde_json::json!(lock.deref()).to_string());
    }
    Ok(())
}

/*
/// 读取配置
/// std::env::set_var(k, v);
fn set_etax_value<F: FnMut(String, String) -> ()>(
    value: &toml::Value,
    k: &str,
    callback: &mut F,
) -> HashMap<String, String> {
    let mut map = HashMap::new();
    if let Some(v) = value.as_str() {
        // println!("Read Config File, set_var: {}={}", &k, v);
        callback(k.to_string(), v.to_string());
        map.insert(k.to_string(), v.to_string());
    } else if let Some(v) = value.as_integer() {
        // println!("Read Config File, set_var: {}={}", &k, v);
        callback(k.to_string(), v.to_string());
        map.insert(k.to_string(), v.to_string());
    } else if let Some(v) = value.as_float() {
        // println!("Read Config File, set_var: {}={}", &k, v);
        callback(k.to_string(), v.to_string());
        map.insert(k.to_string(), v.to_string());
    } else if let Some(v) = value.as_bool() {
        // println!("Read Config File, set_var: {}={}", &k, v);
        callback(k.to_string(), v.to_string());
        map.insert(k.to_string(), v.to_string());
    } else if let Some(arr) = value.as_array() {
        for i in 0..arr.len() {
            let v = &arr[i];
            let k = match k {
                "" => i.to_string(),
                _ => format!("{}[{}]", k, i),
            };
            let m = set_etax_value(v, &k, callback);
            map.extend(m);
        }
    } else if let Some(table) = value.as_table() {
        for (k1, value) in table.iter() {
            let k3 = match k {
                "" => k1.to_string(),
                _ => format!("{}.{}", k, k1),
            };
            let m = set_etax_value(value, &k3, callback);
            map.extend(m);
        }
    };
    map
}*/

/* 
#[test]
fn test_read() {
    use crate::arg::get_arg;
    load_str("dev".to_owned(), include_str!("../config/default.toml").to_string()).unwrap();
    let _watch = run_watch("./config/prod.toml");

    let config = get_arg("mail.enable");
    println!("get_arg mail.enable: {:?}", config);

    std::thread::sleep(std::time::Duration::from_secs(2));
    let config = get_config(vec!["mail", "enable"]);
    println!("enable: {:?}", config);

    let config = get_config(vec!["log", "filters"]);
    println!("filters: {:?}", config);

    let config = get_config(vec!["feishu_url"]);
    println!("feishu_url: {:?}", config);

    let config = get_config(vec!["log"]);
    println!("log: {:?}", config);

    std::thread::sleep(std::time::Duration::from_secs(20));
}
 */

/// 功能
///  把配置文件打包到 bin 文件
///  修改一个mode参数即可切换配置文件
///  在命令行加 ./xxx.exe mode=prod 即可运行正式环境配置
///
/// 模式 1: 输入多个配置文件: load_tomls!("./config/default.toml", "./config/prod.toml");
/// 以第一个配置文件作为默认的配置, 在定义的配置文件中找不到的值, 将在此配置寻找
/// 第一个配置文件中必须有一个 mode = "xxx" 的值, 此值可以是 "prod"
///
/// 模式 2: 输入运行环境需要加载的配置, 输入多个配置文件: load_tomls!("prod"; "./config/default.toml", "./config/prod.toml");
/// 以第一个配置文件作为默认的配置, 其它文件中找不到的值, 将在此配置寻找
/// 运行环境将被设置为 prod, 在 prod 中找不到的配置, 将在第一个配置文件中寻找
///
///
#[macro_export]
macro_rules! load_tomls {

    // 加载配置文件
    ($($file:expr), + $(,)?) => {{
        $(
            let file_name = $crate::toml_read::get_file_name($file).unwrap();
            let s = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/", $file));
            $crate::toml_read::load_str(file_name, s.to_string()).map_err(|e| e.to_string()).unwrap();
        )*
    }};

    // 加载带模式的配置文件
    ($mode:expr; $($file:expr), + $(,)?) => {{
        $crate::toml_read::_CONFIG
        .write()
        .unwrap()
        .insert("mode".to_owned(), JsonV::String($mode.to_string()));
        load_tomls! ($($file),*)
    }};
}
/* 
#[test]
fn test_loadfiles() {
    println!("path: {}", concat!(env!("CARGO_MANIFEST_DIR"), file!()));

    load_tomls!("./config/default.toml", "./config/prod.toml");
    let _watch = run_watch("./config/test.toml").unwrap();
    println!("config: {:?}", _CONFIG.read().unwrap().deref());
    let r = crate::get_arg("log.filters");
    println!("loadfiles!: {:?}", r);
    let r = crate::get_arg("log.level");
    println!("loadfiles!: {:?}", r);

    let r = crate::get_arg("read_value");
    println!("read_value: {:?}", r);
} */

/*
#[test]
fn test_dir() {
    let dir = std::fs::read_dir("./").unwrap();
    for x in dir {
        let f = x.unwrap().path();
        if !f.is_file() {
            continue;
        }
        let file_name = f
            .file_name()
            .map(|v| v.to_str().map(|v| v.to_string()))
            .unwrap()
            .unwrap();
        if !file_name.to_lowercase().ends_with(".toml") {
            continue;
        }
        let file_name = file_name.split(".").collect::<Vec<&str>>()[0].to_string();
        if file_name == "" {
            continue;
        }
        let abs_path = f
            .canonicalize()
            .map(|v| v.to_str().map(|v| v.to_string()))
            .unwrap()
            .unwrap();

        let s = format_args!("{}", abs_path);
        read_str(s.to_string());

        println!("{}, {}", file_name, abs_path);
        // let abs_path = include_str!(abs_path);
        let str = "";
    }
}
*/