1use std::io::Write;
10use std::process::Command;
11use std::str::FromStr;
12use std::{
13 fs::{remove_file, File},
14 path::Path,
15};
16
17macro_rules! some {
18 ($result_value: expr) => {
19 if let Ok(ok) = $result_value {
20 Some(ok)
21 } else {
22 None
23 }
24 };
25}
26
27#[derive(Clone)]
30pub struct Rumya {
31 rumya_path: String,
32 lamuta_path: String,
33}
34
35impl Rumya {
36 pub fn new() -> Self {
39 Self {
40 rumya_path: "rumya.lm".to_string(),
41 lamuta_path: "lamuta".to_string(),
42 }
43 }
44
45 pub fn set_rumya(&self, path: &str) -> Self {
48 let path = Path::new(path);
49 Self {
50 rumya_path: path.display().to_string(),
51 ..self.clone()
52 }
53 }
54
55 pub fn set_lamuta(&self, path: &str) -> Self {
58 let path = Path::new(path);
59 Self {
60 lamuta_path: path.display().to_string(),
61 ..self.clone()
62 }
63 }
64
65 pub fn eval<T: Sized + FromStr>(&self, code: &str) -> Option<T> {
69 const TEMP_FILE_NAME: &str = "Rumya-binding.temp.lm";
70 let mut temp_file = some!(File::create(TEMP_FILE_NAME))?;
71 let code = format!("print\nbegin\n{code}\nend\n");
72 some!(temp_file.write_all(code.as_bytes()))?;
73
74 let output = some!(Command::new(&self.lamuta_path)
75 .args([&self.rumya_path, TEMP_FILE_NAME])
76 .output())?;
77
78 if output.status.success() {
79 let stdout = String::from_utf8_lossy(&output.stdout);
80 some!(remove_file(TEMP_FILE_NAME))?;
81 some!(T::from_str(stdout.lines().last()?))
82 } else {
83 return None;
84 }
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn it_works() {
94 const PATH: &str = "/Users/kajizukataichi/Desktop/repositories/Rumya/rumya.lm";
96 let rumya = Rumya::new().set_rumya(PATH);
97 let result = rumya.eval::<i32>("let x = 0. for i in 1 ~ 10 do x += i. x");
98 assert_eq!(result, Some(45));
99 }
100}