human_language_toolkit_chatbot/
lib.rs

1pub mod bots;
2
3extern crate custom_error;
4use custom_error::custom_error;
5use rand::prelude::*;
6use regex::Regex;
7use serde::{Deserialize, Serialize};
8use serde_json;
9use std::collections::HashMap;
10use std::io::prelude::*;
11use std::{fs, result::Result};
12
13custom_error! { pub ChatbotError
14    File{source: std::io::Error} = "File error {source}",
15    Json{source: serde_json::Error} = "Json parse error {source}",
16    Regex{source: regex::Error} = "Regex error {}",
17}
18
19pub struct CompiledChatbot {
20    pairs: Vec<(Regex, Vec<String>)>,
21    fallback: Vec<String>,
22    reflections: Vec<(String, String)>,
23}
24
25#[derive(Serialize, Deserialize, Debug)]
26pub struct Chatbot {
27    pairs: Vec<(String, Vec<String>)>,
28    fallback: Vec<String>,
29    reflections: HashMap<String, String>,
30}
31
32impl Chatbot {
33    pub fn from_file(path: String) -> Result<Chatbot, ChatbotError> {
34        let content = match fs::read_to_string(path) {
35            Ok(content) => content,
36            Err(e) => return Err(ChatbotError::File { source: e }),
37        };
38
39        let ser: serde_json::Result<Chatbot> = serde_json::from_str(&content.as_str());
40        let chatbot = match ser {
41            Ok(x) => x,
42            Err(e) => return Err(ChatbotError::Json { source: e }),
43        };
44
45        return Ok(chatbot);
46    }
47
48    pub fn to_file(&self, path: String) -> Result<(), ChatbotError> {
49        let json_chatbot = match serde_json::to_string(&self) {
50            Ok(x) => x,
51            Err(e) => return Err(ChatbotError::Json { source: e }),
52        };
53
54        let mut file = match fs::File::create(path.as_str()) {
55            Ok(file) => file,
56            Err(e) => return Err(ChatbotError::File { source: e }),
57        };
58
59        match file.write_all(json_chatbot.as_bytes()) {
60            Ok(_) => (),
61            Err(e) => return Err(ChatbotError::File { source: e }),
62        };
63
64        return Ok(());
65    }
66
67    pub fn compile(&self) -> Result<CompiledChatbot, ChatbotError> {
68        let mut new_pairs: Vec<(Regex, Vec<String>)> = Vec::new();
69
70        for pair in &self.pairs {
71            match Regex::new(&pair.0) {
72                Ok(re) => new_pairs.push((re, pair.1.clone())),
73                Err(e) => return Err(ChatbotError::Regex { source: e }),
74            }
75        }
76
77        let mut new_reflections: Vec<(String, String)> = Vec::new();
78
79        for reflection in &self.reflections {
80            new_reflections.push((reflection.0.clone().to_lowercase(), reflection.1.clone()));
81        }
82
83        new_reflections.sort_by(|a, b| b.0.len().cmp(&a.0.len()));
84
85        return Ok(CompiledChatbot {
86            pairs: new_pairs,
87            fallback: self.fallback.clone(),
88            reflections: new_reflections,
89        });
90    }
91
92    pub fn default_reflections() -> HashMap<String, String> {
93        let mut reflections: HashMap<String, String> = HashMap::new();
94
95        reflections.insert("i am".to_string(), "you are".to_string());
96        reflections.insert("i was".to_string(), "you were".to_string());
97        reflections.insert("i".to_string(), "you".to_string());
98        reflections.insert("i'm".to_string(), "you are".to_string());
99        reflections.insert("i'd".to_string(), "you would".to_string());
100        reflections.insert("i've".to_string(), "you have".to_string());
101        reflections.insert("i'll".to_string(), "you will".to_string());
102        reflections.insert("my".to_string(), "your".to_string());
103        reflections.insert("you are".to_string(), "I am".to_string());
104        reflections.insert("you were".to_string(), "I was".to_string());
105        reflections.insert("you've".to_string(), "I have".to_string());
106        reflections.insert("you'll".to_string(), "I will".to_string());
107        reflections.insert("your".to_string(), "my".to_string());
108        reflections.insert("yours".to_string(), "mine".to_string());
109        reflections.insert("you".to_string(), "me".to_string());
110        reflections.insert("me".to_string(), "you".to_string());
111
112        return reflections;
113    }
114}
115
116impl CompiledChatbot {
117    fn get_random_response(responses: &Vec<String>) -> String {
118        let range = responses.len();
119        let num = rand::thread_rng().gen_range(0..range);
120
121        return responses[num].clone();
122    }
123
124    fn reflect(&self, bit: &String) -> String {
125        let mut new_bit = bit.clone().to_lowercase();
126
127        for reflection in &self.reflections {
128            if !new_bit.contains(&reflection.0) {
129                continue;
130            }
131            new_bit = new_bit.replace(&reflection.0, &reflection.1);
132        }
133        return new_bit;
134    }
135
136    fn format(&self, re: &Regex, response: &String, data: &String) -> String {
137        let mut response = response.clone();
138
139        for capture in re.captures_iter(data) {
140            let mut indx = 0;
141            for bit in capture.iter() {
142                let indx_str = format!("%{}", indx);
143                let bit = match bit {
144                    Some(x) => self.reflect(&x.as_str().to_string()),
145                    None => continue,
146                };
147                response = response.replace(&indx_str, bit.as_str());
148                indx += 1;
149            }
150        }
151
152        return response;
153    }
154
155    pub fn respond(&self, data: &String) -> String {
156        let data = data.trim().to_string();
157        for (re, responses) in &self.pairs {
158            if re.is_match(&data) {
159                let response = CompiledChatbot::get_random_response(&responses);
160                return self.format(re, &response, &data);
161            }
162        }
163
164        return CompiledChatbot::get_random_response(&self.fallback);
165    }
166
167    pub fn converse(&self) {
168        use std::io::{stdin, stdout};
169        loop {
170            let mut data = String::new();
171            print!("> ");
172            let _ = stdout().flush();
173            stdin()
174                .read_line(&mut data)
175                .expect("Did not enter a correct string");
176
177            let response = self.respond(&data);
178            println!("> {}", response);
179        }
180    }
181}