Merge pull request #105 from mist-go/cli-improvements

CLI improvements, module system fixes, project scaffolding, and bug fixes
This commit is contained in:
2026-07-02 21:56:39 +02:00
committed by GitHub
18 changed files with 161 additions and 29 deletions
Generated
+1
View File
@@ -519,6 +519,7 @@ version = "0.3.7-alpha.1"
dependencies = [ dependencies = [
"mist-analyzer", "mist-analyzer",
"mist-api", "mist-api",
"toml",
] ]
[[package]] [[package]]
+1
View File
@@ -18,6 +18,7 @@ include = [
[dependencies] [dependencies]
mist-analyzer = { workspace = true } mist-analyzer = { workspace = true }
mist-api = { workspace = true } mist-api = { workspace = true }
toml = "1.1.2"
[workspace] [workspace]
members = [ members = [
+1 -1
View File
@@ -58,7 +58,7 @@ pub bool build(Vec<String> mut args, PathBuf root)
serde_json::from_str<rev_mapper::Mapping>(&map_str).expect("Failed to parse map file") serde_json::from_str<rev_mapper::Mapping>(&map_str).expect("Failed to parse map file")
}); });
let mist_file = map.mist_path.strip_prefix(&root); let mist_file = map.mist_path.strip_prefix(&root);
let (_, rev_mapper::MistMap(line, column)) = map.find(&rev_mapper::RustMap(span.line_end, span.column_start)).expect("Unable to find mapping"); let (_, rev_mapper::MistMap(line, column)) = map.find(&rev_mapper::RustMap(span.line_end, span.column_start)).unwrap_or_default();
let mist_msg = MistDiagnosticMessage { let mist_msg = MistDiagnosticMessage {
message: format!("{}: {}", message: format!("{}: {}",
msg.message.message, msg.message.message,
+3 -1
View File
@@ -76,7 +76,9 @@ pub class Module
pub PathBuf output_path(&self, PathBuf& parent_dir, Option<(mist_parser::ast::Visibility, mist_parser::ast::Identifier)>& curr_mod_decl) pub PathBuf output_path(&self, PathBuf& parent_dir, Option<(mist_parser::ast::Visibility, mist_parser::ast::Identifier)>& curr_mod_decl)
{ {
let name = if let Some((_, name)) = curr_mod_decl { let name = if self.path.is_dir() || self.is_package() {
&self.name
} else if let Some((_, name)) = curr_mod_decl {
&name.0 &name.0
} else { } else {
&self.name &self.name
+31 -3
View File
@@ -136,12 +136,37 @@ impl modules::Module
} }
(bool, String) write_transpile(&self, String output, PathBuf& parent_dir, bool child_changed) (bool, String) write_transpile(&self, String output, PathBuf& parent_dir, bool child_changed)
{ {
if self.path.is_dir() { if self.path.is_dir() || self.is_package() {
let output_file = self.output_path(parent_dir, &None); let output_file = self.output_path(parent_dir, &None);
let final_output = if self.is_package() {
let source = match fs::read_to_string(&self.path) {
Ok(s) => s,
Err(e) => {
eprintln!("error: failed to read file {}\n {}", self.path.display(), e);
process::exit(1);
}
};
let curr_mod_decl = mist_parser::parse_module(&source).expect("Failed parsing module");
let (result_path, gc, rust_code) = match transpile_code(&self.path, &source, output_file.clone(), &output, &curr_mod_decl) {
Ok(v) => v,
Err(e) => {
decode_err(&e, &self.path);
process::exit(1);
}
};
let map_path = result_path.with_extension("map.json");
if let Some(parent) = map_path.parent() {
let _ = fs::create_dir_all(parent);
}
fs::write(&map_path, serde_json::to_string(&gc.mapping).unwrap()).expect("Failed to write map file");
rust_code
} else {
if output.len() == 0 { if output.len() == 0 {
return (true, String::new()); return (true, String::new());
} }
let res = fs::write(&output_file, output); output
};
let res = fs::write(&output_file, final_output);
if res.is_err() { if res.is_err() {
eprintln!("error: failed to write output {}\n {}", eprintln!("error: failed to write output {}\n {}",
output_file.display(), output_file.display(),
@@ -222,13 +247,16 @@ pub void transpile_file(PathBuf& path, str& source, PathBuf output_file, str& mo
process::exit(1); process::exit(1);
} }
}; };
if let Some(parent) = output_file.parent() {
let _ = fs::create_dir_all(parent);
}
let res = fs::write(&output_file, output); let res = fs::write(&output_file, output);
let output_file_map = output_file.with_extension("map.json"); let output_file_map = output_file.with_extension("map.json");
let res_map = fs::write(&output_file_map, serde_json::to_string(&gc.mapping).unwrap()); let res_map = fs::write(&output_file_map, serde_json::to_string(&gc.mapping).unwrap());
if res_map.is_err() { if res_map.is_err() {
eprintln!("error: failed to write mapping output {}\n {}", eprintln!("error: failed to write mapping output {}\n {}",
output_file_map.display(), output_file_map.display(),
res.as_ref().unwrap_err(),); res_map.as_ref().unwrap_err(),);
} }
if res.is_err() { if res.is_err() {
eprintln!("error: failed to write output {}\n {}", eprintln!("error: failed to write output {}\n {}",
+1
View File
@@ -1,4 +1,5 @@
pub module decl_test; pub module decl_test;
pub module decl_test;
pub void test_let_basic() pub void test_let_basic()
{ {
+1
View File
@@ -1,4 +1,5 @@
pub module expr_test; pub module expr_test;
pub module expr_test;
pub void test_literals() pub void test_literals()
{ {
+1
View File
@@ -1,3 +1,4 @@
pub module package;
pub module common; pub module common;
pub void test_package() {} pub void test_package() {}
+1
View File
@@ -1,4 +1,5 @@
pub module statement_test; pub module statement_test;
pub module statement_test;
pub void test_var_decl() pub void test_var_decl()
{ {
+1
View File
@@ -1,4 +1,5 @@
pub module types_test; pub module types_test;
pub module types_test;
pub void test_integer_type() { pub void test_integer_type() {
i32 x = 42; i32 x = 42;
+1
View File
@@ -1,4 +1,5 @@
pub module attribute_test; pub module attribute_test;
pub module attribute_test;
#[derive(Debug)] #[derive(Debug)]
pub struct Debuggable pub struct Debuggable
+1
View File
@@ -1,4 +1,5 @@
pub module class_test; pub module class_test;
pub module class_test;
pub class Animal pub class Animal
{ {
+1
View File
@@ -1,4 +1,5 @@
pub module enum_test; pub module enum_test;
pub module enum_test;
enum OptionInt enum OptionInt
{ {
+1
View File
@@ -1,4 +1,5 @@
pub module function_test; pub module function_test;
pub module function_test;
void greet() void greet()
{ {
+1
View File
@@ -1,4 +1,5 @@
pub module impl_test; pub module impl_test;
pub module impl_test;
struct Vector2 struct Vector2
{ {
+1
View File
@@ -1,4 +1,5 @@
pub module struct_test; pub module struct_test;
pub module struct_test;
struct Point struct Point
{ {
+1
View File
@@ -1,4 +1,5 @@
pub module trait_test; pub module trait_test;
pub module trait_test;
trait Speak trait Speak
{ {
+109 -21
View File
@@ -10,14 +10,15 @@ use mist_api::transpiler;
void main() void main()
{ {
Vec<String> args = env::args().collect(); Vec<String> mut args = env::args().collect();
if args.len() < 2 { if args.len() < 2 {
print_usage(); print_usage();
process::exit(1); process::exit(1);
} }
match args[1].as_str() { match args[1].as_str() {
"run" | "build" | "check" | "r" | "b" | "c" => { "run" | "build" | "check" | "test" | "publish" | "bench" | "doc" | "fix" | "clippy" | "r" | "b" | "c" => {
let root = env::current_dir().expect("Unable to find project root"); let mut root = env::current_dir().expect("Unable to find project root");
modify_args(&mut args, &mut root);
transpiler::transpile(&root); transpiler::transpile(&root);
println!(""); println!("");
if builder::build(args, root) { if builder::build(args, root) {
@@ -27,12 +28,20 @@ void main()
} }
} }
"transpile" | "t" => { "transpile" | "t" => {
let root = env::current_dir().expect("Unable to find project root"); let mut root = env::current_dir().expect("Unable to find project root");
modify_args(&mut args, &mut root);
transpiler::transpile(&root); transpiler::transpile(&root);
} }
"clean" => {
args.remove(0);
process::Command::new("cargo").args(args).stdout(process::Stdio::inherit()).stderr(process::Stdio::inherit()).stdin(process::Stdio::inherit()).spawn().expect("Failed to run cargo").wait().expect("Unable to wait");
}
"init" => { "init" => {
init_project(args); init_project(args);
} }
"new" => {
new_project(args);
}
"version" | "--version" | "-v" => { "version" | "--version" | "-v" => {
println!("mist {}", env!("CARGO_PKG_VERSION")); println!("mist {}", env!("CARGO_PKG_VERSION"));
} }
@@ -47,44 +56,123 @@ void main()
} }
} }
void modify_args(Vec<String> mut& args, std::path::PathBuf mut& root)
{
if let Some(path_arg) = args.get(2) {
if path_arg == "--path" {
if args.len() > 3 {
let path_arg = args[3].clone();
*root = root.join(&path_arg);
args.remove(2);
args.remove(2);
args.push(format!("--manifest-path={}/Cargo.toml", root.display()));
}
}
}
}
void print_usage() void print_usage()
{ {
println!("Mist - the mist compiler\n"); println!("Mist - the mist compiler\n");
println!("󰖟 View our documentation! \x1b[35mhttps://mist.selimaj.dev\x1b[0m"); println!("󰖟 View our documentation! \x1b[95mhttps://mist.selimaj.dev\x1b[0m");
println!(" Follow us on github! \x1b[35mhttps://github.com/mist-go\x1b[0m\n"); println!(" Follow us on github! \x1b[95mhttps://github.com/mist-go\x1b[0m\n");
println!("usage:"); println!("usage:");
println!(" \x1b[36m mist run\x1b[0m,\x1b[36m r\x1b[0m run the project in the current directory\n"); println!(" \x1b[96m mist run\x1b[0m,\x1b[96m r\x1b[0m run the project\n");
println!(" \x1b[36m mist build\x1b[0m,\x1b[36m b\x1b[0m build the project in the current directory\n"); println!(" \x1b[96m mist build\x1b[0m,\x1b[96m b\x1b[0m build the project\n");
println!(" \x1b[36m mist transpile\x1b[0m,\x1b[36m t\x1b[0m transpile the project in the current directory\n"); println!(" \x1b[96m mist test\x1b[0m test the project\n");
println!(" \x1b[36m mist check\x1b[0m,\x1b[36m c\x1b[0m check the project in the current directory\n"); println!(" \x1b[96m mist publish\x1b[0m publish the project\n");
println!(" \x1b[36m mist init\x1b[0m initialize a project in the current folder\n"); println!(" \x1b[96m mist bench\x1b[0m benchmark the project\n");
println!(" \x1b[36m mist version \x1b[0m print the compiler version\n"); println!(" \x1b[96m mist doc\x1b[0m build documentation\n");
println!(" \x1b[36m mist help\x1b[0m print this message\n"); println!(" \x1b[96m mist fix\x1b[0m automatically fix warnings\n");
println!(" \x1b[96m mist clippy\x1b[0m lint the project\n");
println!(" \x1b[96m mist check\x1b[0m,\x1b[96m c\x1b[0m check the project\n");
println!(" \x1b[96m mist clean\x1b[0m clean build artifacts\n");
println!(" \x1b[96m mist transpile\x1b[0m,\x1b[96m t\x1b[0m transpile the project\n");
println!(" \x1b[96m mist init\x1b[0m initialize a project in the current folder\n");
println!(" \x1b[96m mist new\x1b[0m create a new project\n");
println!(" \x1b[96m mist version \x1b[0m print the compiler version\n");
println!(" \x1b[96m mist help\x1b[0m print this message\n");
} }
void init_project(Vec<String> mut args) void init_project(Vec<String> mut args)
{ {
args.remove(0); args.remove(0);
process::Command::new("cargo").args(args).stdout(process::Stdio::inherit()).stderr(process::Stdio::inherit()).stdin(process::Stdio::inherit()).spawn().expect("Failed to run cargo").wait().expect("Unable to wait"); let status = process::Command::new("cargo").args(args).stdout(process::Stdio::inherit()).stderr(process::Stdio::inherit()).stdin(process::Stdio::inherit()).spawn().expect("Failed to run cargo").wait().expect("Unable to wait");
if !status.success() {
return;
}
bool is_lib = fs::metadata("src/lib.rs").is_ok(); bool is_lib = fs::metadata("src/lib.rs").is_ok();
if is_lib { if is_lib {
fs::remove_file("src/lib.rs").expect("Failed to remove src/lib.rs"); fs::remove_file("src/lib.rs").expect("Failed to remove src/lib.rs");
fs::write("src/lib.mist", "fn test() {\n}").expect("Failed to create src/lib.mist"); fs::write("src/lib.mist", "void test() {\n}").expect("Failed to create src/lib.mist");
} else { } else {
fs::remove_file("src/main.rs").expect("Failed to remove src/main.rs"); fs::remove_file("src/main.rs").expect("Failed to remove src/main.rs");
fs::write("src/main.mist", "fn main() {\n println!(\"Hello, World!\");\n}\n").expect("Failed to create src/main.mist"); fs::write("src/main.mist", "void main() {\n println!(\"Hello, World!\");\n}\n").expect("Failed to create src/main.mist");
} }
inject_mist_target(is_lib); inject_mist_target(is_lib);
println!("\n\x1b[34minfo\x1b[0m: Please open Cargo.toml and adjust `\x1b[35mname\x1b[0m = ` as needed, Thank you for using Mist!\n"); add_mist_toml(is_lib);
add_gitignore();
println!("Thank you for using Mist!\n");
}
void new_project(Vec<String> mut args)
{
args.remove(0);
if args.len() < 2 {
eprintln!("error: missing project name\n");
print_usage();
process::exit(1);
}
String project_name = args[1].clone();
let status = process::Command::new("cargo").args(args).stdout(process::Stdio::inherit()).stderr(process::Stdio::inherit()).stdin(process::Stdio::inherit()).spawn().expect("Failed to run cargo").wait().expect("Unable to wait");
if !status.success() {
return;
}
std::path::PathBuf project_path = env::current_dir().expect("Unable to find project root").join(&project_name);
env::set_current_dir(&project_path).expect("Failed to change to project directory");
bool is_lib = fs::metadata("src/lib.rs").is_ok();
if is_lib {
fs::remove_file("src/lib.rs").expect("Failed to remove src/lib.rs");
fs::write("src/lib.mist", "void test() {\n}").expect("Failed to create src/lib.mist");
} else {
fs::remove_file("src/main.rs").expect("Failed to remove src/main.rs");
fs::write("src/main.mist", "void main() {\n println!(\"Hello, World!\");\n}\n").expect("Failed to create src/main.mist");
}
inject_mist_target(is_lib);
add_mist_toml(is_lib);
add_gitignore();
println!("Thank you for using Mist!\n");
} }
void inject_mist_target(bool is_lib) void inject_mist_target(bool is_lib)
{ {
String toml_content = fs::read_to_string("Cargo.toml").expect("Failed to read Cargo.toml"); String toml_content = fs::read_to_string("Cargo.toml").expect("Failed to read Cargo.toml");
str& inject = if is_lib { toml::Value parsed = toml::from_str(&toml_content).expect("Failed to parse Cargo.toml");
"\n[lib]\nname = \"my_lib\"\npath = \".mist/src/lib.rs\"\n" String name = parsed["package"]["name"].as_str().expect("Missing package.name in Cargo.toml").to_string();
String inject = if is_lib {
format!("\n[lib]\nname = \"{name}\"\npath = \".mist/src/lib.rs\"\n")
} else { } else {
"\n[[bin]]\nname = \"my_bin\"\npath = \".mist/src/main.rs\"\n" format!("\n[[bin]]\nname = \"{name}\"\npath = \".mist/src/main.rs\"\n")
}; };
fs::write("Cargo.toml", toml_content + inject).expect("Failed to inject to Cargo.toml"); fs::write("Cargo.toml", toml_content + &inject).expect("Failed to inject to Cargo.toml");
}
void add_mist_toml(bool is_lib)
{
str& content = if is_lib {
"package = \"lib.mist\"\npackages = []\ninclude = []\n"
} else {
"package = \"main.mist\"\npackages = []\ninclude = []\n"
};
fs::write("Mist.toml", content).expect("Failed to add Mist.toml");
}
void add_gitignore()
{
if fs::metadata(".gitignore").is_ok() {
String content = fs::read_to_string(".gitignore").expect("Failed to read .gitignore");
if !content.contains(".mist") {
fs::write(".gitignore", content + "\n.mist\n").expect("Failed to write .gitignore");
}
}
} }