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
use std::fs::File;
use std::io::{Write, BufReader, BufRead};

/// ### The databuf store kv data
/// ```rust
/// use iii::BufData;
/// let mut cc = BufData::new();
/// cc.chgvalue("c3", "ccc3");
/// cc.chgvalue("c5", "5c");
/// if let Some(v) = cc.getvalue("c3"){
///     println!("K:{},V:{}","c3",v);
/// }
/// cc.save(None);
/// ```
#[derive(Default)]
pub struct BufData{
    filepath:&'static str,
    data:Vec<DataType>,
}
enum DataType {
    Notes(String),
    Data((String,String)),
}

impl BufData {
    /// Create a empty data squence in memory
    pub fn new()->Self{
        Self { filepath:"default.iii",..Default::default() }
    }
    /// ### load config file
    /// 
    /// ```rust
    /// use iii::BufData;
    /// let mut cc = BufData::loadfromiii("default.iii");
    /// ```
    pub fn loadfromiii(filepath:&'static str)->Result<Self,&'static str>{
        let mut data = Vec::new();
        if let Ok(file)=File::open(filepath){
            for idx in BufReader::new(file).lines(){
                if let Ok(iidx) = idx{
                    if iidx.trim().starts_with("#"){
                        data.push(DataType::Notes(iidx));
                    }else{
                        let cc:Vec<&str> = iidx.trim().split("=").collect();
                        if cc.len() == 2{
                            data.push(DataType::Data((cc[0].trim().to_string(),cc[1].trim().to_string())));
                        }
                    }
                }
            }
            Ok(Self{filepath:filepath,data:data})
        }else{
            Err("load file fail")
        }
        
    }
    
    fn onlywrite(&self,path:&'static str)->Result<(),&'static str>{
        if let Ok(mut f) = File::create(path){
            let mut bufstr = String::new();
            for idx in &self.data{
                match idx {
                    DataType::Data((s1,s2))=>{
                        bufstr.push_str(&format!("{} = {}\r\n",s1,s2));
                    },
                    DataType::Notes(commet)=>{
                        bufstr.push_str(&commet);
                        bufstr.push_str("\r\n");
                    },
                }
            }
            if let Ok(_) = f.write(bufstr.as_bytes()){
                return Ok(());
            }
        }
        Err("write err")

    }
    
    /// ### save config to file 
    /// if newfile is None,use loaded file's path to save 
    /// 
    /// ```rust
    /// use iii::BufData;
    /// if let Ok(mut cc) = BufData::loadfromiii("default.iii"){
    ///     cc.chgvalue("c3", "ccc3");
    ///     cc.chgvalue("c5", "5c");
    ///     if let Ok(_) = cc.save(None){
    ///         println!("file write success")
    ///     };
    ///     if let Ok(_) = cc.save(Some("file1.iii")){
    ///         println!("file1.iii write success")
    ///     }
    /// }
    /// ```
    pub fn save(&self,newfilepath:Option<&'static str>)->Result<(),&'static str>{
        if let Some(pth) = newfilepath{
            self.onlywrite(pth)
        }else{
            self.onlywrite(self.filepath)
        }
    }
    /// ### read value from file by key
    /// ```rust
    /// use iii::BufData;
    /// if let Ok(cc) = BufData::loadfromiii("default.iii"){
    ///     if let Some(v) = cc.getvalue("c2"){
    ///         println!("K:{},V:{}","c2",v);
    ///     }
    ///     if let Some(v) = cc.getvalue("c5"){
    ///         println!("K:{},V:{}","c5",v);
    ///     }
    /// };
    /// ```
    pub fn getvalue(&self,key:&'static str)->Option<String>{
        for idx in &self.data{
            if let DataType::Data((s1,s2))=idx{
                if key == s1{
                    return Some(s2.clone());
                }
            }
        }
        None
    }
    /// ### write value or change value
    /// ```rust
    /// use iii::BufData;
    /// if let Ok(mut cc) = BufData::loadfromiii("default.iii"){
    ///     cc.chgvalue("c3", "ccc3");
    ///     cc.chgvalue("c5", "5c");
    ///     if let Ok(_) = cc.save(None){
    ///         println!("file write success")
    ///     };
    /// };
    /// ```
    pub fn chgvalue(&mut self,key:&'static str,value:&str){
        for idx in &mut self.data{
            if let DataType::Data((s1,s2))=idx{
                if key == s1{
                    s2.clear();
                    s2.push_str(value);
                    return;
                }
            }
        }
        self.data.push(DataType::Data((key.to_string(),value.to_string())));
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn crate1(){
        let mut cc = BufData::new();
        cc.chgvalue("c1", "value");
        cc.chgvalue("c2", "value2");
        cc.chgvalue("c3", "value3");
        if let Ok(_) = cc.save(None){
            println!("file write success")
        };
    }
    #[test]
    fn readwrite(){
        if let Ok(mut cc) = BufData::loadfromiii("default.iii"){
            cc.chgvalue("c3", "ccc3");
            cc.chgvalue("c5", "5c");
            if let Ok(_) = cc.save(None){
                println!("file write success")
            };
        };
    }

    #[test]
    fn readvalue(){
        if let Ok(cc) = BufData::loadfromiii("default.iii"){
            if let Some(v) = cc.getvalue("c2"){
                println!("K:{},V:{}","c2",v);
            }
            if let Some(v) = cc.getvalue("c5"){
                println!("K:{},V:{}","c5",v);
            }
        };
    }
    #[test]
    fn getnumbervalue(){
        let mut cc = BufData::new();
        cc.chgvalue("c2", "33");
        cc.chgvalue("c4", &true.to_string());
        if let Some(v) = cc.getvalue("c2"){
            if let Ok(number) = v.parse::<i32>(){
                println!("number:");
                assert!(33==number);
            }
        };
        if let Some(v) = cc.getvalue("c4"){
            if let Ok(var) = v.parse::<bool>(){
                println!("bool:");
                assert!(true==var);
            }
        }

    }
}