diff --git a/Cargo.lock b/Cargo.lock index 7cf815a..e5ee426 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -367,6 +367,17 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +[[package]] +name = "clipboard-win" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" +dependencies = [ + "error-code", + "str-buf", + "winapi", +] + [[package]] name = "colorchoice" version = "1.0.0" @@ -545,6 +556,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + [[package]] name = "env_logger" version = "0.9.3" @@ -607,12 +624,33 @@ dependencies = [ "libc", ] +[[package]] +name = "error-code" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21" +dependencies = [ + "libc", + "str-buf", +] + [[package]] name = "fastrand" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" +[[package]] +name = "fd-lock" +version = "3.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" +dependencies = [ + "cfg-if", + "rustix", + "windows-sys 0.48.0", +] + [[package]] name = "flate2" version = "1.0.26" @@ -793,6 +831,15 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys 0.48.0", +] + [[package]] name = "http" version = "0.2.9" @@ -1042,6 +1089,7 @@ dependencies = [ "env_logger 0.10.0", "futures-util", "glam", + "home", "hyper", "hyper-tungstenite", "include_dir", @@ -1060,6 +1108,7 @@ dependencies = [ "rbx_xml", "regex", "reqwest", + "rustyline", "serde", "serde_json", "serde_yaml", @@ -1159,6 +1208,27 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "nix" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "static_assertions", +] + [[package]] name = "num-traits" version = "0.2.16" @@ -1362,6 +1432,16 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + [[package]] name = "rand" version = "0.8.5" @@ -1718,6 +1798,29 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustyline" +version = "12.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "994eca4bca05c87e86e15d90fc7a91d1be64b4482b38cb2d27474568fe7c9db9" +dependencies = [ + "bitflags 2.3.3", + "cfg-if", + "clipboard-win", + "fd-lock", + "home", + "libc", + "log", + "memchr", + "nix", + "radix_trie", + "scopeguard", + "unicode-segmentation", + "unicode-width", + "utf8parse", + "winapi", +] + [[package]] name = "ryu" version = "1.0.15" @@ -1973,6 +2076,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" +[[package]] +name = "str-buf" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" + [[package]] name = "strsim" version = "0.8.0" @@ -2338,6 +2447,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + [[package]] name = "unicode-width" version = "0.1.10" diff --git a/Cargo.toml b/Cargo.toml index ac79a8d..bfd0e77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,6 +74,7 @@ lz4_flex = "0.11" pin-project = "1.0" os_str_bytes = "6.4" urlencoding = "2.1" +home = "0.5.5" ### RUNTIME @@ -119,6 +120,7 @@ regex = { optional = true, version = "1.7", default-features = false, features = "std", "unicode-perl", ] } +rustyline = "12.0.0" ### ROBLOX diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 0a97c6f..bbdad07 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,7 +1,13 @@ -use std::{fmt::Write as _, process::ExitCode}; +use std::{ + fmt::Write as _, + io::ErrorKind, + path::PathBuf, + process::{exit, ExitCode}, +}; use anyhow::{Context, Result}; use clap::{CommandFactory, Parser}; +use rustyline::{error::ReadlineError, DefaultEditor}; use lune::Lune; use tokio::{ @@ -148,10 +154,92 @@ impl Cli { } // HACK: We know that we didn't get any arguments here but since // script_path is optional clap will not error on its own, to fix - // we will duplicate the cli command and make arguments required, - // which will then fail and print out the normal help message + // we will duplicate the CLI command and fetch the version of + // lune to display let cmd = Cli::command(); - cmd.arg_required_else_help(true).get_matches(); + let lune_version = cmd.get_version(); + + // The version is mandatory and will always exist + println!("Lune v{}", lune_version.unwrap()); + + let lune_instance = Lune::new(); + + let mut repl = DefaultEditor::new()?; + + match repl.load_history(&(|| -> PathBuf { + let dir_opt = home::home_dir(); + + if let Some(dir) = dir_opt { + dir.join(".lune_history") + } else { + eprintln!("Failed to find user home directory, abort!"); + // Doesn't feel right to exit directly with a exit code of 1 + // Lmk if there is a better way of doing this + exit(1); + } + })()) { + Ok(_) => (), + Err(err) => { + match err { + err => { + if let ReadlineError::Io(io_err) = err { + if io_err.kind() == ErrorKind::NotFound { + std::fs::write( + // We know for sure that the home dir already exists + home::home_dir().unwrap().join(".lune_history"), + String::new(), + )?; + } + } + } + } + + eprintln!("WARN: Failed to load REPL history") + } + }; + + let mut interrupt_counter = 0u32; + + loop { + let mut source_code = String::new(); + + match repl.readline("> ") { + Ok(code) => { + source_code = code; + + // If source code eval was requested, we reset the counter + interrupt_counter = 0; + } + + Err(ReadlineError::Interrupted) => { + // HACK: We actually want the user to do ^C twice to exit, + // but the user would need to ^C one more time even after + // the check passes, so we check for 1 instead of 2 + if interrupt_counter != 1 { + println!("Interrupt: ^C again to exit"); + + // Increment the counter + interrupt_counter += 1; + } else { + break; + } + } + Err(ReadlineError::Eof) => break, + Err(err) => { + eprintln!("REPL ERROR: {}", err.to_string()); + break; + } + }; + + let eval_result = lune_instance.run("REPL", source_code).await; + + match eval_result { + Ok(_) => (), + Err(err) => eprintln!("{}", err), + } + } + + return Ok(ExitCode::SUCCESS); } // Figure out if we should read from stdin or from a file, // reading from stdin is marked by passing a single "-"