tquest/
persistence.rs

1use crate::questionaire::{QuestionAnswerInput, QuestionEntry, QuestionAnswer} ;
2use anyhow::{anyhow, Result};
3use colored::Colorize;
4use serde::Serialize;
5
6use std::fs::{File, OpenOptions};
7use std::io::Write;
8use std::path::Path;
9use std::io::{BufRead, BufReader};
10
11
12pub trait QuestionairePersistence {
13    fn store_question(&mut self, entry: &QuestionEntry, data: &QuestionAnswerInput) -> Result<()>;
14    fn load(&mut self, source: Option<&str>) -> Result<()>;
15    fn import(&mut self, data_to_import: &Vec<QuestionAnswer>);
16    fn next_answer(&mut self) -> Option<QuestionAnswer>;
17    fn next_answer_id(&mut self) -> Option<String>;
18}
19
20pub struct FileQuestionairePersistence  {
21    file: String,
22    data: Vec<QuestionAnswer>,
23    pub debug: bool,
24    current_pos: usize,
25}
26
27impl FileQuestionairePersistence  {
28    pub fn new(file: &str) -> Result<FileQuestionairePersistence> {
29        let ret = FileQuestionairePersistence {
30            file: file.to_string(),
31            data: vec![],
32            debug: false,
33            current_pos: 0,
34        };
35        Ok(ret)
36    }
37
38    fn store<T: Serialize>(&mut self, id: &str, answer: &T) -> Result<()> {
39        let json_string = serde_json::to_string(answer).unwrap();
40        let txt = format!("{}={}",id, json_string);
41        if self.debug {
42            println!("{}", txt.blue().italic());
43        }
44        self.write_to_file(&txt)
45    }
46
47
48    fn write_to_file(&mut self, txt: &str) -> Result<()> {
49        let p = Path::new(&self.file);
50        let mut file = OpenOptions::new()
51            .append(true)
52            .create(true)
53            .open(p)?;
54        write!(file, "{}\n", txt)?;
55        Ok(())
56    }
57}
58
59impl QuestionairePersistence for FileQuestionairePersistence {
60    fn store_question(&mut self, entry: &QuestionEntry, data: &QuestionAnswerInput) -> Result<()> {
61        // TODO: remove, that mess up the load and fast-forward mode ... and it's not needed
62        //self.data.push((entry.id.to_string(), data.clone()));
63        self.store(&entry.id, data)
64    }
65
66    fn load(&mut self, source: Option<&str>) -> Result<()> {
67        if let Some(file_path) = source {
68            self.data = load_tmp_file(&file_path)?;
69            Ok(())
70        } else {
71            Err(anyhow!("No source for loading given"))
72        }
73    }
74
75    fn import (&mut self, data_to_import: &Vec<QuestionAnswer>) {
76        for i in data_to_import {
77            self.data.push(i.clone());
78        }
79    }
80
81    fn next_answer(&mut self) -> Option<QuestionAnswer> {
82        if self.current_pos < self.data.len() {
83            let e = self.data.get(self.current_pos);
84            self.current_pos += 1;
85            if let Some(a) = e {
86                return Some(a.clone())
87            }
88            None
89        } else {
90            None
91        }
92    }
93
94    fn next_answer_id(&mut self) -> Option<String> {
95        if self.current_pos < self.data.len() {
96            let e = self.data.get(self.current_pos);
97            if let Some(a) = e {
98                Some(a.id.to_string())
99            } else {
100                None
101            }
102        } else {
103            None
104        }
105    }
106
107}
108
109pub struct NoPersistence {
110}
111
112impl NoPersistence {
113    pub fn new() -> Self {
114        NoPersistence{}
115    }
116}
117
118impl QuestionairePersistence for NoPersistence {
119    fn store_question(&mut self, _entry: &QuestionEntry, _data: &QuestionAnswerInput) -> Result<()> {
120        Ok(())
121    }
122
123    fn load(&mut self, _s: Option<&str>) -> Result<()> {
124        Err(anyhow!("Not supported"))
125    }
126
127    fn import(&mut self, _data_to_import: &Vec<QuestionAnswer>) {
128    }
129
130    fn next_answer(&mut self) -> Option<QuestionAnswer> {
131        None
132    }
133
134    fn next_answer_id(&mut self) -> Option<String> {
135        None
136    }
137
138}
139
140pub fn load_tmp_file(file_path: &str) -> Result<Vec<QuestionAnswer>> {
141    let file = File::open(file_path)?;
142    let reader = BufReader::new(file);
143
144    let mut ret: Vec<QuestionAnswer> = Vec::new();
145
146    for line in reader.lines() {
147        let line = line.unwrap();
148
149        let (id, json_str) = if let Some(index) = line.find("=") {
150            (&line[..index], &line[index+1..])
151        } else {
152            continue;
153        };
154        if let Ok(o) = serde_json::from_str::<QuestionAnswerInput>(&json_str) {
155            let qa = QuestionAnswer {
156                id: id.to_string(),
157                answer: o,
158            };
159            ret.push(qa);
160        }
161    }
162    Ok(ret)
163}
164
165#[cfg(test)]
166mod test {
167    use super::*;
168
169    #[test]
170    fn test_load_tmp_file() {
171        if let Ok(v) = load_tmp_file("res/tquest.tmp") {
172            assert_eq!(14, v.len());
173            v.iter().enumerate().for_each(|(index, a)| {
174                match index {
175                    0 => assert_eq!("id01".to_string(), *a.id),
176                    1 => assert_eq!("id02".to_string(), *a.id),
177                    2 => assert_eq!("id03_01_01".to_string(), *a.id),
178                    3 => assert_eq!("id03_01_02".to_string(), *a.id),
179                    4 => assert_eq!("id03_01_01".to_string(), *a.id),
180                    5 => assert_eq!("id03_01_02".to_string(), *a.id),
181                    6 => assert_eq!("id04_01".to_string(), *a.id),
182                    7 => assert_eq!("id04_02".to_string(), *a.id),
183                    8 => assert_eq!("id04_03".to_string(), *a.id),
184                    9 => assert_eq!("id04_04_01".to_string(), *a.id),
185                    10 => assert_eq!("id04_04_02".to_string(), *a.id),
186                    11 => assert_eq!("id04_01".to_string(), *a.id),
187                    12 => assert_eq!("id04_02".to_string(), *a.id),
188                    13 => assert_eq!("id04_03".to_string(), *a.id),
189                    _ => panic!("more elements than expected"),
190                };
191            });
192        } else {
193            panic!("error while loading test file");
194        }
195    }
196
197    #[test]
198    fn test_next() {
199        let mut persistence = FileQuestionairePersistence::new("tmp/tquest.tmp").unwrap();
200        persistence.load(Some("res/tquest.tmp")).expect("error while loading old persistence file");
201        assert_eq!("id01".to_string(), persistence.next_answer_id().unwrap());
202        let _ = persistence.next_answer().unwrap();
203        assert_eq!("id02".to_string(), persistence.next_answer_id().unwrap());
204        let _ = persistence.next_answer().unwrap();
205        assert_eq!("id03_01_01".to_string(), persistence.next_answer_id().unwrap());
206        let _ = persistence.next_answer().unwrap();
207        assert_eq!("id03_01_02".to_string(), persistence.next_answer_id().unwrap());
208        let _ = persistence.next_answer().unwrap();
209        assert_eq!("id03_01_01".to_string(), persistence.next_answer_id().unwrap());
210        let _ = persistence.next_answer().unwrap();
211        assert_eq!("id03_01_02".to_string(), persistence.next_answer_id().unwrap());
212        let _ = persistence.next_answer().unwrap();
213        assert_eq!("id04_01".to_string(), persistence.next_answer_id().unwrap());
214        let _ = persistence.next_answer().unwrap();
215        assert_eq!("id04_02".to_string(), persistence.next_answer_id().unwrap());
216        let _ = persistence.next_answer().unwrap();
217        assert_eq!("id04_03".to_string(), persistence.next_answer_id().unwrap());
218        let _ = persistence.next_answer().unwrap();
219        assert_eq!("id04_04_01".to_string(), persistence.next_answer_id().unwrap());
220        let _ = persistence.next_answer().unwrap();
221        assert_eq!("id04_04_02".to_string(), persistence.next_answer_id().unwrap());
222        let _ = persistence.next_answer().unwrap();
223        assert_eq!("id04_01".to_string(), persistence.next_answer_id().unwrap());
224        let _ = persistence.next_answer().unwrap();
225        assert_eq!("id04_02".to_string(), persistence.next_answer_id().unwrap());
226        let _ = persistence.next_answer().unwrap();
227        assert_eq!("id04_03".to_string(), persistence.next_answer_id().unwrap());
228        let _ = persistence.next_answer().unwrap();
229        assert_eq!(None, persistence.next_answer_id());
230        assert_eq!(None, persistence.next_answer());
231        assert_eq!(None, persistence.next_answer_id());
232        assert_eq!(None, persistence.next_answer());
233    }
234}