Skip to main content

typr_cli/
cli.rs

1//! Command-line interface for TypR
2//!
3//! Provides the main CLI commands:
4//! - `typr new <name>`: Create a new TypR project
5//! - `typr check [file]`: Type-check a file or project
6//! - `typr build [file]`: Transpile to R
7//! - `typr run [file]`: Build and execute
8//! - `typr test`: Run tests
9//! - `typr repl`: Start interactive REPL
10//! - `typr lsp`: Start Language Server Protocol server
11
12use crate::project::{
13    build_file, build_project, check_file, check_project, clean, cran, document, load, new,
14    pkg_install, pkg_uninstall, run_file, run_project, test, use_package,
15};
16use crate::repl;
17use crate::standard_library::standard_library;
18use clap::{Parser, Subcommand};
19use std::path::PathBuf;
20
21#[derive(Parser)]
22#[command(author, version, about, long_about = None)]
23struct Cli {
24    #[arg(value_name = "FILE")]
25    file: Option<PathBuf>,
26
27    #[arg(short, long, value_name = "TARGET", default_value = "r")]
28    target: Option<String>,
29
30    #[command(subcommand)]
31    command: Option<Commands>,
32}
33
34#[derive(Subcommand, Debug)]
35enum Commands {
36    New {
37        name: String,
38    },
39    Check {
40        #[arg(value_name = "FILE")]
41        file: Option<PathBuf>,
42    },
43    Build {
44        #[arg(value_name = "FILE")]
45        file: Option<PathBuf>,
46    },
47    Run {
48        #[arg(value_name = "FILE")]
49        file: Option<PathBuf>,
50    },
51    Test,
52    Pkg {
53        #[command(subcommand)]
54        pkg_command: PkgCommands,
55    },
56    Document,
57    Use {
58        package_name: String,
59    },
60    Load,
61    Cran,
62    Std,
63    Clean,
64    Repl,
65    Lsp,
66}
67
68#[derive(Subcommand, Debug)]
69enum PkgCommands {
70    Install,
71    Uninstall,
72}
73
74/// Main entry point for the CLI
75pub fn start() {
76    let cli = Cli::parse();
77    if let Some(path) = cli.file {
78        if cli.command.is_none() {
79            run_file(&path);
80            return;
81        }
82    }
83
84    match cli.command {
85        Some(Commands::New { name }) => new(&name),
86        Some(Commands::Check { file }) => match file {
87            Some(path) => check_file(&path),
88            _ => check_project(),
89        },
90        Some(Commands::Build { file }) => match file {
91            Some(path) => build_file(&path),
92            _ => build_project(),
93        },
94        Some(Commands::Run { file }) => match file {
95            Some(path) => run_file(&path),
96            _ => run_project(),
97        },
98        Some(Commands::Test) => test(),
99        Some(Commands::Pkg { pkg_command }) => match pkg_command {
100            PkgCommands::Install => pkg_install(),
101            PkgCommands::Uninstall => pkg_uninstall(),
102        },
103        Some(Commands::Document) => document(),
104        Some(Commands::Use { package_name }) => use_package(&package_name),
105        Some(Commands::Load) => load(),
106        Some(Commands::Cran) => cran(),
107        Some(Commands::Std) => standard_library(),
108        Some(Commands::Clean) => clean(),
109        Some(Commands::Lsp) => {
110            // Use a larger stack size (8MB) to avoid stack overflow
111            // during deep recursive parsing/type-checking operations
112            let rt = tokio::runtime::Builder::new_multi_thread()
113                .thread_stack_size(8 * 1024 * 1024)
114                .enable_all()
115                .build()
116                .unwrap();
117            rt.block_on(crate::lsp::run_lsp());
118        }
119        Some(Commands::Repl) => repl::start(),
120        _ => {
121            println!("Please specify a subcommand or file to execute");
122            std::process::exit(1);
123        }
124    }
125}