1use crate::handlers::CodeRequest;
5use std::collections::HashMap;
6use std::fs;
7use std::fs::write;
8use std::process::Command;
9
10#[derive(Debug)]
20pub struct Program {
21 pub filename: String,
22 pub workspace: String,
23 pub cells: HashMap<u32, Cell>,
24}
25
26#[derive(Debug)]
34pub struct Cell {
35 pub fragment: u32,
36 pub index: u32,
37 pub contents: String,
38}
39
40impl Program {
41 pub fn new() -> Program {
44 Program {
45 filename: String::new(),
46 workspace: String::new(),
47 cells: HashMap::new(),
48 }
49 }
50
51 pub fn create_cell(&mut self, cr: &CodeRequest) {
55 let cell = Cell {
56 contents: cr.contents.clone(),
57 fragment: cr.fragment,
58 index: cr.index,
59 };
60 self.cells.insert(cr.fragment, cell);
61 }
62
63 pub fn update_cell(&mut self, cr: &CodeRequest) {
66 if let Some(cell) = self.cells.get_mut(&cr.fragment) {
67 cell.contents = cr.contents.clone();
68 cell.index = cr.index;
69 }
70 }
71
72 pub fn write_to_file(&mut self, fragment: u32) {
76 let mut cells_vec: Vec<&Cell> = self.cells.iter().map(|(_, cell)| (cell)).collect();
78 cells_vec.sort_by(|a, b| a.index.cmp(&b.index));
79
80 let mut crates = String::new();
82 let mut outer_scope = String::new();
83 let mut inner_scope = String::new();
84 let mut contains_main = false;
85 for cell in cells_vec {
86 let lines = cell.contents.split('\n');
87 for line in lines {
88 let line = line.trim();
89 if line.starts_with("fn main()") {
91 contains_main = true;
92 continue;
93 }
94 if line.starts_with("print") && cell.fragment != fragment {
96 continue;
97 }
98
99 if line.starts_with("use") {
100 outer_scope += line;
101 outer_scope += "\n";
102 if !line.starts_with("use std") {
103 let (_, full_path) = line.split_once(' ').unwrap();
104 let (crate_name, _) = full_path.split_once(':').unwrap();
105 let crate_name_fixed = str::replace(crate_name, "_", "-");
106 crates += &crate_name_fixed;
107 crates += "=\"*\"\n";
108 }
109 } else {
110 inner_scope += line;
111 inner_scope += "\n";
112 }
113 }
114 if contains_main {
115 inner_scope = inner_scope.trim_end().to_string();
116 inner_scope.pop();
117 contains_main = false;
118 }
119 }
120
121 let mut output = "#![allow(dead_code)]\n".to_string();
122 output += outer_scope.as_str();
123 output += "fn main() {\n";
124 output += &inner_scope;
125 output += "}";
126
127 let mut dir = self.workspace.to_owned();
130
131 let mut cargo_file = dir.clone();
132 cargo_file += "/Cargo.toml";
133
134 dir += "/src";
135 let mut main_file = dir.to_owned();
136 main_file += "/main.rs";
137
138 if let Err(err) = fs::create_dir_all(&dir) {
139 if err.kind() != std::io::ErrorKind::AlreadyExists {
140 panic!("Can't create dir: {}, err {}", &dir, err)
141 }
142 };
143
144 let cargo_contents = format!(
145 "{}\n{}",
146 r#"
147[package]
148name = 'output'
149version = '0.0.1'
150edition = '2021'
151[dependencies]
152"#,
153 crates
154 );
155
156 write(&main_file, &output).expect("Error writing file");
158 write(&cargo_file, &cargo_contents).expect("Error writing file");
159 }
160
161 pub fn run(&self) -> String {
165 let output = Command::new("cargo")
166 .current_dir(&self.workspace)
167 .arg("run")
168 .output()
169 .expect("Failed to run cargo");
170
171 let err = String::from_utf8(output.stderr).expect("Failed to parse utf8");
172
173 if err.contains("error: ") || err.contains("panicked at") {
174 return format!("1\0{}", err);
176 }
177
178 let output = String::from_utf8(output.stdout).unwrap();
179 format!("0\0{}", output)
180 }
181
182 pub fn fmt(&self) {
183 Command::new("cargo")
184 .current_dir(&self.workspace)
185 .arg("fmt")
186 .output()
187 .expect("Failed to run cargo fmt");
188 }
189}