human_language_toolkit_chatbot/
lib.rs1pub 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}