tnt/
tnt.rs

1use std::fs::{self, File, OpenOptions};
2use std::io::{BufRead, BufReader, Write};
3
4pub struct TNT {
5    filename: String,
6}
7
8impl TNT {
9    /// Создаёт новый экземпляр TNT, ассоциированный с указанным файлом.
10    /// Если файл не существует, он будет создан.
11    ///
12    /// # Аргументы
13    /// * `filename` - Имя файла для хранения данных.
14    ///
15    /// # Пример
16    /// ```
17    /// let tnt = TNT::connect("data.txt");
18    /// ```
19    ///
20    /// Creates a new TNT instance associated with the specified file.
21    /// If the file does not exist, it will be created.
22    ///
23    /// # Arguments
24    /// * `filename` - The name of the file to store data.
25    ///
26    /// # Example
27    /// ```
28    /// let tnt = TNT::connect("data.txt");
29    /// ```
30    pub fn connect(filename: &str) -> Self {
31
32        if fs::metadata(filename).is_err() {
33            File::create(filename).expect("File creation error!");
34        }
35
36        Self { filename: filename.to_string()}
37    }
38
39    fn is_ccf(&self) -> std::io::Result<bool> {
40        let file = File::open(&self.filename)?;
41        let reader = BufReader::new(&file);
42        let mut is_good = false;
43        let mut brack = 0;
44        let mut rev_brack = 0;
45        let mut braces = 0;
46        let mut rev_braces = 0;
47
48        for line in reader.lines() {
49            let line = line?;
50
51            if line.find('(').is_some() {
52                brack += 1;
53            }
54
55            if line.find(')').is_some() {
56                rev_brack += 1;
57            }
58
59            if line.find('{').is_some() {
60                braces += 1;
61            }
62
63            if line.find('}').is_some() {
64                rev_braces += 1;
65            }
66        }
67
68        if brack == rev_brack && braces == rev_braces {
69            is_good = true;
70        }
71
72        Ok(is_good)
73    }
74
75    fn get_var_line(&self, key: &str, var: &str) -> std::io::Result<i32> {
76        let file = File::open(&self.filename)?;
77        let reader = BufReader::new(&file);
78        let mut is_key = false;
79        let mut var_line: i32 = -1;
80
81        for (i, line) in reader.lines().enumerate() {
82            let line = line?;
83
84            if line.find('(').is_some() {
85                let cleaned_line = line.replace(['(', ')', '{'], "");
86                let k = cleaned_line.trim();
87
88                if k == key {
89                    is_key = true;
90                    continue;
91                }
92            }
93
94            if is_key && line.find('}').is_some() {
95                break;
96            }
97
98
99            if is_key {
100                let fmt_line = line.trim();
101                let var_from_line = fmt_line.split('=').next().unwrap_or(fmt_line);
102
103                if var_from_line == var.trim() {
104                    var_line = i as i32;
105                    break;
106
107                }
108            }
109
110        }
111
112        Ok(var_line)
113    }
114
115    fn is_var(&self, key: &str, var: &str) -> std::io::Result<bool> {
116        let file = File::open(&self.filename)?;
117        let reader = BufReader::new(&file);
118        let mut is_varible = false;
119        let mut is_found_key = false;
120        let mut var_val = String::new();
121
122        for line in reader.lines() {
123            let line = line?;
124
125            if line.find('(').is_some() {
126                let cleaned_line = line.replace(['(', ')', '{'], "");
127                let k = cleaned_line.trim();
128
129                if k == key.trim() {
130                    is_found_key = true;
131                    continue;
132                }
133            }
134
135            if is_found_key && line.find('}').is_some() {
136                break;
137            }
138
139            if is_found_key {
140                let fmt_line = line.trim();
141                var_val.push_str(&format!("{},", fmt_line));
142            }
143        }
144
145        let old_var: Vec<&str > = var_val.split(',').filter(|s| !s.is_empty()).collect();
146        
147        for v in old_var {
148            let v_v = v.split('=').next().unwrap_or(v);
149
150            if v_v == var.trim() {
151                is_varible = true;
152                break;
153            }
154        }
155
156        Ok(is_varible)
157
158    }
159
160    /// Добавляет новую переменную с указанным значением в секцию ключа.
161    /// Если ключа нет, он будет создан. Если переменная уже существует, операция не выполнится.
162    ///
163    /// # Аргументы
164    /// * `key` - Имя секции (ключа)
165    /// * `var` - Имя переменной
166    /// * `val` - Значение переменной
167    ///
168    /// Adds a new variable with the specified value to the key section.
169    /// If the key does not exist, it will be created. If the variable already exists, the operation will not be performed.
170    ///
171    /// # Arguments
172    /// * `key` - Section (key) name
173    /// * `var` - Variable name
174    /// * `val` - Variable value
175    pub fn add<T: std::fmt::Display, V: std::fmt::Display>(&self, key: &str, var: T, val: V) -> std::io::Result<()> {
176        let var_str = var.to_string();
177        let val_str = val.to_string();
178
179        if !self.is_ccf()? {
180            println!("File integrity error!");
181            return Ok(());
182        }
183        
184        let file = File::open(&self.filename)?;
185        let reader = BufReader::new(&file);
186        let mut is_found = false;
187        let mut is_not_key = true;
188        let mut new_text: String = String::new();
189
190        for line in reader.lines() {
191            let line = line?;
192            
193            if line.find('(').is_some() {
194                let cleaned_line = line.replace('(', "").replace(')', "").replace('{', "");
195                let k = cleaned_line.trim();
196                
197                if k == key {
198                    is_found = true;
199                    is_not_key = false;
200                }
201            }
202
203            if line.find('}').is_some() && is_found {
204                let is_val = self.is_var(key, var_str.as_str())?;
205
206                if is_val {
207                    println!("A variable named '{}' already exists!", var_str);
208                    return Ok(());
209                } 
210
211                new_text.push_str(&format!("\t{}={}\n", var_str, val_str));
212                is_found = false;
213            }
214
215            new_text.push_str(&line);
216            new_text.push_str("\n");
217            
218        }
219
220        if is_not_key {
221            new_text.push_str(&format!("({}) {{\n\t{}={}\n}}", key, var_str, val_str));
222        }
223       
224
225        let mut file = fs::OpenOptions::new().write(true).truncate(true).create(true).open(&self.filename)?;
226        write!(file, "{}", new_text)?;
227
228        Ok(())
229    }
230
231    /// Получает значение переменной по ключу и имени переменной.
232    /// Если переменная не найдена, возвращает "NONE_VAL".
233    ///
234    /// # Аргументы
235    /// * `key` - Имя секции (ключа)
236    /// * `var` - Имя переменной
237    ///
238    /// Gets the value of a variable by key and variable name.
239    /// If the variable is not found, returns "NONE_VAL".
240    ///
241    /// # Arguments
242    /// * `key` - Section (key) name
243    /// * `var` - Variable name
244    pub fn get<T: std::fmt::Display>(&self, key: &str, var: T) -> std::io::Result<String> {
245        let var_str = var.to_string();
246        
247        if !self.is_ccf()? {
248            println!("File integrity error!");
249            return Ok("Error".to_string());
250        }
251
252        let file = fs::File::open(&self.filename)?;
253        let reader = BufReader::new(&file);
254        let mut is_key = false;
255        let mut fmt_val = String::new();
256
257
258        for line in reader.lines() {
259            let line = line?;
260
261            if line.find('(').is_some() {
262                let cleaned_line = line.replace(['(', ')', '{'], "");
263                let k = cleaned_line.trim();
264                
265                if k == key {
266                    is_key = true;
267                    continue;
268                }
269            }
270
271            if is_key && line.find('}').is_some() {
272                println!("The variable was not found!");
273                break;
274            }
275
276
277            if is_key {
278                if let Some((name, val)) = line.trim().split_once("=") {
279                    if name == var_str.trim() {
280                        fmt_val.push_str(val);
281                        break;
282                    }
283                }
284            }
285
286        }
287
288
289        if fmt_val.is_empty() {
290            fmt_val.push_str("NONE_VAL");
291        }
292
293        Ok(fmt_val)
294
295    }
296
297    /// Изменяет значение переменной в секции ключа.
298    /// Если переменная не найдена, операция не выполнится.
299    ///
300    /// # Аргументы
301    /// * `key` - Имя секции (ключа)
302    /// * `var` - Имя переменной
303    /// * `new_val` - Новое значение переменной
304    ///
305    /// Edits the value of a variable in the key section.
306    /// If the variable is not found, the operation will not be performed.
307    ///
308    /// # Arguments
309    /// * `key` - Section (key) name
310    /// * `var` - Variable name
311    /// * `new_val` - New value for the variable
312    pub fn edit<T: std::fmt::Display, V: std::fmt::Display>(&self, key: &str, var: T, new_val: V) -> std::io::Result<()> {
313        let var_str = var.to_string();
314        let val_str = new_val.to_string();
315
316        if !self.is_ccf()? {
317            println!("File integrity error!");
318            return Ok(());
319        }
320
321        let file = File::open(&self.filename)?;
322        let reader = BufReader::new(&file);
323        let pos = self.get_var_line(key, var_str.as_str())?;
324        let mut txt = String::new();
325
326        if pos == -1 {
327            println!("The variable was not found!");
328            return Ok(());
329        }
330
331        for (i, line) in reader.lines().enumerate() {
332            let line = line?;
333
334            if i as i32 == pos {
335                txt.push_str(&format!("\t{}={}\n", var_str, val_str));
336                continue;
337            }
338
339            txt.push_str(&line);
340            txt.push_str("\n");
341        }
342
343        let mut file = fs::OpenOptions::new().write(true).truncate(true).create(true).open(&self.filename)?;
344        write!(file, "{}", txt)?;
345
346        Ok(())
347    }
348
349    /// Удаляет переменную из секции ключа.
350    /// Если переменная не найдена, операция не выполнится.
351    ///
352    /// # Аргументы
353    /// * `key` - Имя секции (ключа)
354    /// * `var` - Имя переменной
355    ///
356    /// Deletes a variable from the key section.
357    /// If the variable is not found, the operation will not be performed.
358    ///
359    /// # Arguments
360    /// * `key` - Section (key) name
361    /// * `var` - Variable name
362    pub fn delete_var<T: std::fmt::Display>(&self, key: &str, var: T) -> std::io::Result<()> {
363        let var_str = var.to_string();
364
365        if !self.is_ccf()? {
366            println!("File integrity error!");
367            return Ok(());
368        }
369
370        let file = File::open(&self.filename)?;
371        let reader = BufReader::new(&file);
372        let mut txt = String::new();
373        let var_del_line = self.get_var_line(key, var_str.as_str())?;
374
375        if var_del_line == -1 {
376            println!("The variable was not found!");
377            return Ok(());
378        }
379
380        for (i, line) in reader.lines().enumerate() {
381            let line = line?;
382
383            if i == var_del_line as usize {
384                continue;
385            }
386
387            txt.push_str(&line);
388            txt.push_str("\n");
389        }
390
391        let mut file = fs::OpenOptions::new().write(true).truncate(true).create(true).open(&self.filename)?;
392        write!(file, "{}", txt)?;
393
394        Ok(())            
395    }
396
397    /// Удаляет секцию (ключ) и все переменные внутри неё.
398    /// Если ключ не найден, операция не выполнится.
399    ///
400    /// # Аргументы
401    /// * `key` - Имя секции (ключа)
402    ///
403    /// Deletes a section (key) and all variables inside it.
404    /// If the key is not found, the operation will not be performed.
405    ///
406    /// # Arguments
407    /// * `key` - Section (key) name
408    pub fn delete_key(&self, key: &str) -> std::io::Result<()> {
409
410        if !self.is_ccf()? {
411            println!("File integrity error!");
412            return Ok(());
413        }
414
415        let file = File::open(&self.filename)?;
416        let reader = BufReader::new(&file);
417        let mut txt = String::new();
418        let mut is_found_key = false;
419        let mut is_key = false;
420
421        for line in reader.lines() {
422            let line = line?;
423
424            if line.find('(').is_some() {
425                let cleaned_line = line.replace(['(', ')', '{'], "");
426                let k = cleaned_line.trim();
427
428                if k == key.trim() {
429                    is_key = true;
430                    is_found_key = true;
431                    continue;
432                }
433            }
434
435            if is_key && line.find('}').is_some() {
436                is_key = false;
437                continue;
438            }
439
440            if is_key {
441                continue;
442            }
443
444            txt.push_str(&line);
445            txt.push_str("\n");
446        }
447
448        if is_found_key {
449            let mut file = fs::OpenOptions::new().write(true).truncate(true).create(true).open(&self.filename)?;
450            write!(file, "{}", txt)?;
451        }
452
453        Ok(())
454    }
455
456    /// Очищает весь файл, удаляя все данные.
457    ///
458    /// Clears the entire file, removing all data.
459    pub fn clear(&self) -> std::io::Result<()> {
460        File::create(&self.filename)?;
461        Ok(())
462    }
463
464    /// Получает все значения переменных в секции ключа.
465    ///
466    /// # Аргументы
467    /// * `key` - Имя секции (ключа)
468    ///
469    /// Gets all variable values in the key section.
470    ///
471    /// # Arguments
472    /// * `key` - Section (key) name
473    pub fn get_all(&self, key: &str) -> std::io::Result<Vec<String>> {
474
475        if !self.is_ccf()? {
476            println!("File integrity error!");
477            return Ok(vec!["".to_string()]);
478        }
479
480        let file = File::open(&self.filename)?;
481        let reader = BufReader::new(&file);
482        let mut all_val: Vec<String> = Vec::new();
483        let mut is_key = false;
484
485        for line in reader.lines() {
486            let line = line?;
487
488            if line.find('(').is_some() {
489                let cleaned_line = line.replace(['(', ')', '{'], "");
490                let k = cleaned_line.trim();
491
492                if k == key.trim() {
493                    is_key = true;
494                    continue;
495                }
496            }
497
498            if is_key && line.find('}').is_some() {
499                break;
500            }
501
502            if is_key {
503                let fmt_line = line.split('=').nth(1).unwrap_or("");
504                let val_fmt = fmt_line.trim().to_string();
505                all_val.push(val_fmt);
506            }
507        }
508
509        Ok(all_val)
510
511    }
512
513    /// Экспортирует данные в формате TOML в указанный файл.
514    ///
515    /// # Аргументы
516    /// * `filename` - Имя файла (без расширения), куда будет сохранён TOML.
517    ///
518    /// Exports data in TOML format to the specified file.
519    ///
520    /// # Arguments
521    /// * `filename` - File name (without extension) where TOML will be saved.
522    pub fn to_toml(&self, filename: &str) -> std::io::Result<()> {
523
524        if !self.is_ccf()? {
525            println!("File integrity error!");
526            return Ok(());
527        }
528
529        let file = File::open(&self.filename)?;
530        let reader = BufReader::new(&file);
531        let mut is_key = false;
532        let mut txt: String = String::new();
533
534        for line in reader.lines() {
535            let line = line?;
536
537            if line.find('(').is_some() {
538                let cleaned_line = line.replace(['(', ')', '{'], "");
539                let key = cleaned_line.trim();
540                txt.push_str(&format!("[{}]\n", key));
541                is_key = true;
542                continue;
543            }
544
545            if is_key && line.find('}').is_some() {
546                is_key = false;
547                txt.push_str("\n");
548                continue;
549            }
550
551            if is_key {
552                let fmt_val = line.trim();
553                let var = fmt_val.split('=').next().unwrap_or(fmt_val);
554                let val = fmt_val.split('=').nth(1).unwrap_or("");
555                txt.push_str(&format!("{}=\"{}\"\n", var, val));
556            }
557
558        }
559
560        let full_filename = String::from(&format!("{}.toml", filename));
561        let mut file = fs::OpenOptions::new().write(true).truncate(true).create(true).open(full_filename)?;
562        write!(file, "{}", txt)?;
563        
564        Ok(())
565    }
566
567    /// Экспортирует данные в формате JSON в указанный файл.
568    ///
569    /// # Аргументы
570    /// * `filename` - Имя файла (без расширения), куда будет сохранён JSON.
571    ///
572    /// Exports data in JSON format to the specified file.
573    ///
574    /// # Arguments
575    /// * `filename` - File name (without extension) where JSON will be saved.
576    pub fn to_json(&self, filename: &str) -> std::io::Result<()> {
577
578        if !self.is_ccf()? {
579            println!("File integrity error!");
580            return Ok(());
581        }
582
583        let file = File::open(&self.filename)?;
584        let reader = BufReader::new(&file);
585        let mut txt: String = String::new();
586        let mut is_key: bool = false;
587        let mut val_str = String::new();
588
589        txt.push_str("{\n");
590
591        for line in reader.lines() {
592            let line = line?;
593
594            if line.find('(').is_some() {
595                let cleaned_line = line.replace(['(', ')', '{'], "");
596                let key = cleaned_line.trim();
597                txt.push_str(&format!("\t\"{}\": {{\n", key));
598                is_key = true;
599                continue;
600            }
601
602            if is_key && line.find('}').is_some() {
603                is_key = false;
604                let mut new_len = val_str.len() - 2;
605
606                while !val_str.is_char_boundary(new_len) && new_len > 0 {
607                    new_len -= 1;
608                }
609
610                val_str.truncate(new_len);
611
612                txt.push_str(&val_str);
613                txt.push_str("\n\t},\n\n");
614                val_str.clear();
615                continue;
616            }
617
618            if is_key {
619                let fmt_line = line.trim();
620                let var = fmt_line.split('=').next().unwrap_or(fmt_line);
621                let val = fmt_line.split('=').nth(1).unwrap_or("");
622                val_str.push_str(&format!("\t\t\"{}\": \"{}\",\n", val, var));
623            }
624
625        }
626
627        let mut new_len = txt.len() - 3;
628
629        while !txt.is_char_boundary(new_len) && new_len > 0 {
630            new_len -= 1;
631        }
632
633        txt.truncate(new_len);
634        txt.push_str("\n}");
635
636        let full_filename = String::from(&format!("{}.json", filename));
637        let mut file = OpenOptions::new().write(true).truncate(true).create(true).open(full_filename)?;
638        writeln!(file, "{}", txt)?;
639
640        Ok(())
641    }
642
643    /// Импортирует данные из TOML-файла в основной файл.
644    ///
645    /// # Аргументы
646    /// * `toml` - Имя TOML-файла (без расширения), из которого будут импортированы данные.
647    ///
648    /// Imports data from a TOML file into the main file.
649    ///
650    /// # Arguments
651    /// * `toml` - TOML file name (without extension) from which data will be imported.
652    pub fn from_toml(&self, toml: &str) -> std::io::Result<()> {
653        let full_toml = String::from(&format!("{}.toml", toml));
654        let file = File::open(full_toml)?;
655        let reader = BufReader::new(&file);
656        let mut txt = String::new();
657        let mut is_one = 0;
658
659        for line in reader.lines() {
660            let line = line?;
661
662            if line.find('[').is_some() && line.find(']').is_some() {
663                let cleaned_line = line.replace(['[', ']'], "");
664                let key = cleaned_line.trim();
665                if is_one > 0 {
666                    txt.push_str(&format!("}}\n({}) {{\n", key));
667                } else {
668                    txt.push_str(&format!("({}) {{\n", key));
669                }
670                is_one += 1;
671                continue;
672            }
673
674            if !line.is_empty() {
675                let fmt_line = line.replace(['\"', ' '], "");
676                let fmt_line_2 = fmt_line.trim();
677                let var = fmt_line_2.split('=').next().unwrap_or(fmt_line_2);
678                let val = fmt_line_2.split('=').nth(1).unwrap_or("");
679
680                txt.push_str(&format!("\t{}={}\n", var, val));
681            }
682        }
683
684        txt.push_str("}");
685
686        let mut file = OpenOptions::new().write(true).truncate(true).create(true).open(&self.filename)?;
687        writeln!(file, "{}", txt)?;
688
689        Ok(())
690    }
691
692    /// Импортирует данные из JSON-файла в основной файл.
693    ///
694    /// # Аргументы
695    /// * `json` - Имя JSON-файла (без расширения), из которого будут импортированы данные.
696    ///
697    /// Imports data from a JSON file into the main file.
698    ///
699    /// # Arguments
700    /// * `json` - JSON file name (without extension) from which data will be imported.
701    pub fn from_json(&self, json: &str) -> std::io::Result<()> {
702        let full_json = String::from(&format!("{}.json", json));
703        let file = File::open(full_json)?;
704        let reader = BufReader::new(&file);
705        let mut txt = String::new();
706        let mut is_key = false;
707
708
709        for line in reader.lines() {
710            let line = line?;
711
712            if line.find('\"').is_some() && line.find("{").is_some() && line.find(':').is_some() {
713                let cleaned_line = line.replace(['{', ':', '\"'], "");
714                let key = cleaned_line.trim();
715                txt.push_str(&format!("({}) {{\n", key));
716                is_key = true;
717                continue;
718            }
719
720            if is_key && line.find('}').is_some() {
721                is_key = false;
722                txt.push_str("}\n");
723                continue;
724            }
725
726            if is_key && !line.is_empty() {
727                let cleaned_line = line.replace(['\"', ',', ' '], "");
728                let var_and_val = cleaned_line.trim();
729                let var = var_and_val.split(':').next().unwrap_or(var_and_val);
730                let val = var_and_val.split(':').nth(1).unwrap_or("");
731                txt.push_str(&format!("\t{}={}\n", var, val));
732            }
733        }
734
735        let mut file = OpenOptions::new().write(true).truncate(true).create(true).open(&self.filename)?;
736        writeln!(file, "{}", txt)?;
737
738        Ok(())
739    }
740}