1use crate::engine::{parse_code, write_std_for_type_checking};
10use crate::io::execute_r_with_path;
11use std::fs;
12use std::fs::File;
13use std::fs::OpenOptions;
14use std::io::Write;
15use std::path::Path;
16use std::path::PathBuf;
17use std::process::Command;
18use typr_core::components::context::config::Environment;
19use typr_core::components::context::Context;
20use typr_core::processes::type_checking::type_checker::TypeChecker;
21use typr_core::typing;
22
23pub fn write_header(context: Context, output_dir: &PathBuf, environment: Environment) -> () {
24 let type_anotations = context.get_type_anotations();
25 let mut app = match environment {
26 Environment::Repl => OpenOptions::new()
27 .append(true)
28 .create(true)
29 .write(true)
30 .open(output_dir.join(".repl.R")),
31 _ => OpenOptions::new().create(true).write(true).open(
32 output_dir
33 .join(context.get_environment().to_base_path())
34 .join("c_types.R"),
35 ),
36 }
37 .unwrap();
38
39 app.write_all(type_anotations.as_bytes()).unwrap();
40
41 let generic_functions = context
42 .get_all_generic_functions()
43 .iter()
44 .map(|(var, _)| var.get_name())
45 .filter(|x| !x.contains("<-"))
46 .map(|fn_name| {
47 format!(
48 "#' @export\n{} <- function(x, ...) UseMethod('{}', x)",
49 fn_name,
50 fn_name.replace("`", "")
51 )
52 })
53 .collect::<Vec<_>>()
54 .join("\n");
55 let mut app = match environment {
56 Environment::Repl => OpenOptions::new()
57 .append(true)
58 .create(true)
59 .write(true)
60 .open(output_dir.join(".repl.R")),
61 _ => OpenOptions::new().create(true).write(true).open(
62 output_dir
63 .join(context.get_environment().to_string())
64 .join("b_generic_functions.R"),
65 ),
66 }
67 .unwrap();
68 app.write_all((generic_functions + "\n").as_bytes())
69 .unwrap();
70}
71
72pub fn write_to_r_lang(
73 content: String,
74 output_dir: &PathBuf,
75 file_name: &str,
76 environment: Environment,
77) -> () {
78 let rstd = include_str!("../configs/src/std.R");
79 let std_path = output_dir.join("a_std.R");
80 let mut rstd_file = File::create(std_path).unwrap();
81 rstd_file.write_all(rstd.as_bytes()).unwrap();
82
83 let app_path = output_dir.join(file_name);
84 let mut app = match environment {
85 Environment::Repl => OpenOptions::new()
86 .append(true)
87 .write(true)
88 .create(true)
89 .open(app_path),
90 _ => File::create(app_path),
91 }
92 .unwrap();
93 let source = match environment {
94 Environment::Project | Environment::Repl | Environment::Wasm => "",
95 Environment::StandAlone => "source('b_generic_functions.R')\nsource('c_types.R')",
96 };
97 app.write_all(format!("{}\n{}", source, content).as_bytes())
98 .unwrap();
99}
100
101pub fn new(name: &str) {
102 println!("Creating the R package '{}'...", name);
103
104 let current_dir = match std::env::current_dir() {
105 Ok(dir) => dir,
106 Err(e) => {
107 eprintln!("Error obtaining current directory: {}", e);
108 std::process::exit(1);
109 }
110 };
111
112 let project_path = current_dir.join(name);
113
114 if let Err(e) = fs::create_dir(&project_path) {
115 eprintln!("Error creating project directory: {}", e);
116 std::process::exit(1);
117 }
118
119 let package_folders = vec![
121 "R", "TypR", "man", "tests", "data", "inst", "src", "vignettes", ];
130
131 for folder in package_folders {
132 let folder_path = project_path.join(folder);
133 if let Err(e) = fs::create_dir(&folder_path) {
134 eprintln!(
135 "Warning: Unable to create the folder {}: {}",
136 folder_path.display(),
137 e
138 );
139 }
140 }
141
142 let tests_testthat = project_path.join("tests/testthat");
143 if let Err(e) = fs::create_dir(&tests_testthat) {
144 eprintln!("Warning: Unable to create the tests/testthat folder: {}", e);
145 }
146
147 let package_files = vec![
148 (
149 "DESCRIPTION",
150 include_str!("../configs/DESCRIPTION").replace("{{PACKAGE_NAME}}", name),
151 ),
152 (
153 "NAMESPACE",
154 include_str!("../configs/NAMESPACE").replace("{{PACKAGE_NAME}}", name),
155 ),
156 (
157 ".Rbuildignore",
158 include_str!("../configs/.Rbuildignore").replace("{{PACKAGE_NAME}}", name),
159 ),
160 (
161 ".gitignore",
162 include_str!("../configs/.gitignore").replace("{{PACKAGE_NAME}}", name),
163 ),
164 (
165 "TypR/main.ty",
166 include_str!("../configs/main.ty").replace("{{PACKAGE_NAME}}", name),
167 ),
168 (
169 "R/.gitkeep",
170 include_str!("../configs/.gitkeep").replace("{{PACKAGE_NAME}}", name),
171 ),
172 (
173 "tests/testthat.R",
174 include_str!("../configs/testthat.R").replace("{{PACKAGE_NAME}}", name),
175 ),
176 (
177 "man/.gitkeep",
178 include_str!("../configs/.gitkeep2").replace("{{PACKAGE_NAME}}", name),
179 ),
180 (
181 "README.md",
182 include_str!("../configs/README.md").replace("{{PACKAGE_NAME}}", name),
183 ),
184 (
185 "rproj.Rproj",
186 include_str!("../configs/rproj.Rproj").to_string(),
187 ),
188 ];
189
190 for (file_path, content) in package_files {
191 let full_path = project_path.join(file_path);
192 if let Some(parent) = full_path.parent() {
193 if let Err(e) = fs::create_dir_all(parent) {
194 eprintln!(
195 "Warning: Unable to create parent directory {}: {}",
196 parent.display(),
197 e
198 );
199 continue;
200 }
201 }
202 println!("Writing {} in '{:?}'", content.len(), full_path);
203 if let Err(e) = fs::write(&full_path, content) {
204 eprintln!(
205 "Warning: Unable to create parent directory {}: {}",
206 full_path.display(),
207 e
208 );
209 }
210 }
211
212 println!("Package R '{}' successfully created!", name);
213 let package_structure =
214 include_str!("../configs/package_structure.md").replace("{{PACKAGE_NAME}}", name);
215 println!("{}", package_structure);
216
217 let instructions = include_str!("../configs/instructions.md").replace("{{PACKAGE_NAME}}", name);
218 println!("{}", instructions);
219}
220
221pub fn check_project() {
222 let context = Context::default().set_environment(Environment::Project);
223 let lang = parse_code(&PathBuf::from("TypR/main.ty"), context.get_environment());
224 let _ = typing(&context, &lang);
225 println!("Code verification successful!");
226}
227
228pub fn check_file(path: &PathBuf) {
229 let context = Context::default().set_environment(Environment::Project);
230 let lang = parse_code(path, context.get_environment());
231 let dir = PathBuf::from(".");
232 write_std_for_type_checking(&dir);
233 let type_checker = TypeChecker::new(context.clone()).typing(&lang);
234 if type_checker.has_errors() {
235 std::process::exit(1);
236 }
237 println!("File verification {:?} successful!", path);
238}
239
240pub fn build_project() {
241 let dir = PathBuf::from(".");
242 let context = Context::default().set_environment(Environment::Project);
243 let lang = parse_code(&PathBuf::from("TypR/main.ty"), context.get_environment());
244 let type_checker = TypeChecker::new(context.clone()).typing(&lang);
245
246 let content = type_checker.clone().transpile();
247 write_header(type_checker.get_context(), &dir, Environment::Project);
248 write_to_r_lang(
249 content,
250 &PathBuf::from("R"),
251 "d_main.R",
252 context.get_environment(),
253 );
254 document();
255 println!("R code successfully generated in the R/ folder");
256}
257
258pub fn build_file(path: &PathBuf) {
259 let lang = parse_code(path, Environment::StandAlone);
260 let dir = PathBuf::from(".");
261
262 write_std_for_type_checking(&dir);
263 let context = Context::default();
264 let type_checker = TypeChecker::new(context.clone()).typing(&lang);
265 let r_file_name = path
266 .file_name()
267 .unwrap()
268 .to_str()
269 .unwrap()
270 .replace(".ty", ".R");
271 let content = type_checker.clone().transpile();
272 write_header(type_checker.get_context(), &dir, Environment::StandAlone);
273 write_to_r_lang(content, &dir, &r_file_name, context.get_environment());
274 println!("Generated R code: {:?}", dir.join(&r_file_name));
275}
276
277pub fn run_project() {
278 build_project();
279 execute_r_with_path(&PathBuf::from("R"), "main.R");
280}
281
282pub fn run_file(path: &PathBuf) {
283 let lang = parse_code(path, Environment::StandAlone);
284 let dir = PathBuf::from(".");
285
286 write_std_for_type_checking(&dir);
287 let context = Context::default();
288 let type_checker = TypeChecker::new(context.clone()).typing(&lang);
289 let r_file_name = path
290 .file_name()
291 .unwrap()
292 .to_str()
293 .unwrap()
294 .replace(".ty", ".R");
295 let content = type_checker.clone().transpile();
296 write_header(type_checker.get_context(), &dir, Environment::StandAlone);
297 write_to_r_lang(content, &dir, &r_file_name, context.get_environment());
298 execute_r_with_path(&dir, &r_file_name);
299}
300
301pub fn test() {
302 build_project();
303 let r_command = "devtools::test()".to_string();
304
305 println!("Execution of: R -e \"{}\"", r_command);
306
307 let output = Command::new("R").arg("-e").arg(&r_command).output();
308
309 match output {
310 Ok(output) => {
311 if output.status.success() {
312 if !output.stdout.is_empty() {
313 println!("\n{}", String::from_utf8_lossy(&output.stdout));
314 }
315 } else {
316 eprintln!("Error while running tests");
317 if !output.stderr.is_empty() {
318 eprintln!("\n{}", String::from_utf8_lossy(&output.stderr));
319 }
320 std::process::exit(1);
321 }
322 }
323 Err(e) => {
324 eprintln!("Error while executing R command: {}", e);
325 eprintln!("Make sure devtools is installed");
326 std::process::exit(1);
327 }
328 }
329}
330
331pub fn get_package_name() -> Result<String, String> {
332 let description_path = PathBuf::from("DESCRIPTION");
333
334 if !description_path.exists() {
335 return Err("DESCRIPTION file not found. Are you at the project root?".to_string());
336 }
337
338 let content = fs::read_to_string(&description_path)
339 .map_err(|e| format!("Error reading file DESCRIPTION: {}", e))?;
340
341 for line in content.lines() {
342 if line.starts_with("Package:") {
343 let package_name = line.replace("Package:", "").trim().to_string();
344 return Ok(package_name);
345 }
346 }
347
348 Err("Package name not found in the DESCRIPTION file".to_string())
349}
350
351pub fn pkg_install() {
352 println!("Installing the package...");
353
354 let current_dir = match std::env::current_dir() {
355 Ok(dir) => dir,
356 Err(e) => {
357 eprintln!("Error obtaining current directory: {}", e);
358 std::process::exit(1);
359 }
360 };
361
362 let project_path = current_dir.to_str().unwrap();
363 let r_command = format!("devtools::install_local('{}')", project_path);
364 println!("Executing: R -e \"{}\"", r_command);
365
366 let output = Command::new("R").arg("-e").arg(&r_command).output();
367
368 match output {
369 Ok(output) => {
370 if output.status.success() {
371 println!("Package installed successfully!");
372
373 if !output.stdout.is_empty() {
374 println!("\n{}", String::from_utf8_lossy(&output.stdout));
375 }
376 } else {
377 eprintln!("Error during package installation");
378 if !output.stderr.is_empty() {
379 eprintln!("\n{}", String::from_utf8_lossy(&output.stderr));
380 }
381
382 std::process::exit(1);
383 }
384 }
385 Err(e) => {
386 eprintln!("Error executing command R: {}", e);
387 eprintln!("Make sure that R and devtools are installed.");
388 std::process::exit(1);
389 }
390 }
391}
392
393pub fn pkg_uninstall() {
394 println!("Uninstalling the package...");
395
396 let package_name = match get_package_name() {
397 Ok(name) => name,
398 Err(e) => {
399 eprintln!("Error: {}", e);
400 std::process::exit(1);
401 }
402 };
403
404 println!("Uninstalling the package '{}'...", package_name);
405 let r_command = format!("remove.packages('{}')", package_name);
406 println!("Executing: R -e \"{}\"", r_command);
407
408 let output = Command::new("R").arg("-e").arg(&r_command).output();
409
410 match output {
411 Ok(output) => {
412 if output.status.success() {
413 println!("Package '{}' successfully uninstalled!", package_name);
414
415 if !output.stdout.is_empty() {
416 println!("\n{}", String::from_utf8_lossy(&output.stdout));
417 }
418 } else {
419 eprintln!(
420 "Note: The package '{}' may not have been installed or an error may have occurred",
421 package_name
422 );
423
424 if !output.stderr.is_empty() {
425 eprintln!("\n{}", String::from_utf8_lossy(&output.stderr));
426 }
427 }
428 }
429 Err(e) => {
430 eprintln!("Error executing command R: {}", e);
431 eprintln!("Make sure that R is installed.");
432 std::process::exit(1);
433 }
434 }
435}
436
437pub fn document() {
438 println!("Generating package documentation...");
439
440 let current_dir = match std::env::current_dir() {
441 Ok(dir) => dir,
442 Err(e) => {
443 eprintln!("Error obtaining current directory: {}", e);
444 std::process::exit(1);
445 }
446 };
447
448 let project_path = current_dir.to_str().unwrap();
449 let r_command = format!("devtools::document('{}')", project_path);
450
451 let output = Command::new("R").arg("-e").arg(&r_command).output();
452
453 match output {
454 Ok(output) => {
455 if output.status.success() {
456 println!("Documentation successfully generated!");
457
458 if !output.stdout.is_empty() {
459 println!("")
460 }
461 } else {
462 eprintln!("Error while generating documentation");
463
464 if !output.stderr.is_empty() {
465 eprintln!("\n{}", String::from_utf8_lossy(&output.stderr));
466 }
467
468 std::process::exit(1);
469 }
470 }
471 Err(e) => {
472 eprintln!("Error while executing the R command : {}", e);
473 eprintln!("Be sure that R et devtools are installed.");
474 std::process::exit(1);
475 }
476 }
477}
478
479pub fn use_package(package_name: &str) {
480 println!("Adding the package '{}' as a dependency...", package_name);
481 let r_command = format!("devtools::use_package('{}')", package_name);
482 println!("Execution of: R -e \"{}\"", r_command);
483
484 let output = Command::new("R").arg("-e").arg(&r_command).output();
485
486 match output {
487 Ok(output) => {
488 if output.status.success() {
489 println!(
490 "Package '{}' successfully added to dependencies!",
491 package_name
492 );
493
494 if !output.stdout.is_empty() {
495 println!("\n{}", String::from_utf8_lossy(&output.stdout));
496 }
497 } else {
498 eprintln!("Error adding package '{}'", package_name);
499
500 if !output.stderr.is_empty() {
501 eprintln!("\n{}", String::from_utf8_lossy(&output.stderr));
502 }
503
504 std::process::exit(1);
505 }
506 }
507 Err(e) => {
508 eprintln!("Error executing command R: {}", e);
509 eprintln!("Make sure that R and devtools are installed.");
510 std::process::exit(1);
511 }
512 }
513}
514
515pub fn load() {
516 let r_command = "devtools::load_all('.')".to_string();
517
518 println!("Execution of: R -e \"{}\"", r_command);
519
520 let output = Command::new("R").arg("-e").arg(&r_command).output();
521
522 match output {
523 Ok(output) => {
524 if output.status.success() {
525 println!("Elements loaded with success!");
526 if !output.stdout.is_empty() {
527 println!("\n{}", String::from_utf8_lossy(&output.stdout));
528 }
529 } else {
530 eprintln!("Error while loading elements");
531 if !output.stderr.is_empty() {
532 eprintln!("\n{}", String::from_utf8_lossy(&output.stderr));
533 }
534
535 std::process::exit(1);
536 }
537 }
538 Err(e) => {
539 eprintln!("Error while executing R command: {}", e);
540 eprintln!("Make sure devtools is installed");
541 std::process::exit(1);
542 }
543 }
544}
545
546pub fn cran() {
547 let r_command = "devtools::check()".to_string();
548 println!("Execution of: R -e \"{}\"", r_command);
549
550 let output = Command::new("R").arg("-e").arg(&r_command).output();
551
552 match output {
553 Ok(output) => {
554 if output.status.success() {
555 println!("Checks passed with success!");
556 if !output.stdout.is_empty() {
557 println!("\n{}", String::from_utf8_lossy(&output.stdout));
558 }
559 } else {
560 eprintln!("Error while checking the project");
561 if !output.stderr.is_empty() {
562 eprintln!("\n{}", String::from_utf8_lossy(&output.stderr));
563 }
564
565 std::process::exit(1);
566 }
567 }
568 Err(e) => {
569 eprintln!("Error while executing R command: {}", e);
570 eprintln!("Make sure devtools is installed");
571 std::process::exit(1);
572 }
573 }
574}
575
576pub fn clean() {
577 let folder = Path::new(".");
578 if folder.is_dir() {
579 for entry_result in fs::read_dir(folder).unwrap() {
580 let entry = entry_result.unwrap();
581 let path = entry.path();
582 if let Some(file_name) = path.file_name() {
583 if let Some(str_name) = file_name.to_str() {
584 if str_name.starts_with(".") {
585 if path.is_file() {
586 let _ = fs::remove_file(&path);
587 }
588 }
589 }
590 }
591 }
592 };
593}