From 54e536d11fb7f402987395df1bf4f54fa7549d5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20H=C3=B6lting?= <87192362+moritz-hoelting@users.noreply.github.com> Date: Tue, 2 Apr 2024 19:29:58 +0200 Subject: [PATCH] Use structs instead of enum structs, add first implementations of other subcommands --- Cargo.toml | 7 ++++ src/cli.rs | 55 +++++++++++----------------- src/error.rs | 4 +++ src/subcommands/compile.rs | 68 +++++++++++++++++++++++++++++++++++ src/subcommands/init.rs | 35 ++++++++++++++---- src/subcommands/lang_debug.rs | 55 ++++++++++++++++++++++++++++ src/subcommands/mod.rs | 15 +++++++- src/subcommands/package.rs | 15 ++++++++ 8 files changed, 211 insertions(+), 43 deletions(-) create mode 100644 src/subcommands/compile.rs create mode 100644 src/subcommands/lang_debug.rs create mode 100644 src/subcommands/package.rs diff --git a/Cargo.toml b/Cargo.toml index 26f04f4..1bc604f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,9 +5,16 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +zip = ["shulkerbox/zip"] +lang-debug = [] + [dependencies] clap = { version = "4.5.4", features = ["derive"] } colored = "2.1.0" serde = { version = "1.0.197", features = ["derive"] } thiserror = "1.0.58" toml = "0.8.12" +shulkerscript-lang = {path = "../shulkerscript-lang"} +shulkerbox = {path = "../shulkerbox", default-features = false} + diff --git a/src/cli.rs b/src/cli.rs index 1f2c3cd..48332a8 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,6 +1,7 @@ -use std::path::PathBuf; - -use crate::{error::Result, subcommands}; +use crate::{ + error::Result, + subcommands::{self, CompileArgs, InitArgs}, +}; use clap::{Parser, Subcommand}; #[derive(Debug, Parser)] @@ -15,43 +16,27 @@ pub struct Args { #[derive(Debug, Clone, Subcommand)] pub enum Command { - /// Initialize a new project in the current directory. - Init { - /// The path of the folder to initialize in. - #[clap(default_value = ".")] - path: PathBuf, - /// The name of the project. - #[clap(short, long)] - name: Option, - /// The description of the project. - #[clap(short, long)] - description: Option, - /// The pack format version. - #[clap(short, long)] - pack_format: Option, - /// Force initialization even if the directory is not empty. - #[clap(short, long)] - force: bool, - }, + /// Initialize a new project. + Init(InitArgs), + /// Compile the project. + Compile(CompileArgs), + #[cfg(feature = "zip")] + /// Compile and package the project. + Package(subcommands::PackageArgs), + #[cfg(feature = "lang-debug")] + /// Compile the project and dump the intermediate state. + LangDebug(subcommands::LangDebugArgs), } impl Args { pub fn run(&self) -> Result<()> { match &self.cmd { - Command::Init { - path, - name, - description, - pack_format, - force, - } => subcommands::init( - self.verbose, - path, - name.as_deref(), - description.as_deref(), - *pack_format, - *force, - )?, + Command::Init(args) => subcommands::init(self.verbose, args)?, + Command::Compile(args) => subcommands::compile(self.verbose, args)?, + #[cfg(feature = "zip")] + Command::Package(args) => subcommands::package(self.verbose, args)?, + #[cfg(feature = "lang-debug")] + Command::LangDebug(args) => subcommands::lang_debug(args)?, } Ok(()) diff --git a/src/error.rs b/src/error.rs index 8ea1c37..4869264 100644 --- a/src/error.rs +++ b/src/error.rs @@ -14,6 +14,10 @@ pub enum Error { NonEmptyDirectoryError(PathBuf), #[error("An error occured because the path {0} is not a directory.")] NotDirectoryError(PathBuf), + #[error("An error occured because the path is neither a pack directory or a pack.toml file.")] + InvalidPackPathError(PathBuf), + #[error("An error occured while compiling the project.")] + ShulkerScriptError(#[from] shulkerscript_lang::base::Error), } pub type Result = std::result::Result; diff --git a/src/subcommands/compile.rs b/src/subcommands/compile.rs new file mode 100644 index 0000000..8fca0fe --- /dev/null +++ b/src/subcommands/compile.rs @@ -0,0 +1,68 @@ +use crate::{ + config::ProjectConfig, + error::{Error, Result}, + terminal_output::{print_error, print_info}, +}; +use std::{fs, path::PathBuf}; + +use crate::util; + +#[derive(Debug, clap::Args, Clone)] +pub struct CompileArgs { + /// The path of the project to compile. + #[clap(default_value = ".")] + path: PathBuf, +} + +pub fn compile(_verbose: bool, args: &CompileArgs) -> Result<()> { + let path = args.path.as_path(); + + let str_path = util::to_absolute_path(path)?; + print_info(&format!("Compiling project at {}", str_path)); + + let toml_path = if !path.exists() { + print_error("The specified path does not exist."); + return Err(Error::PathNotFoundError(path.to_path_buf())); + } else if path.is_dir() { + let toml_path = path.join("pack.toml"); + if !toml_path.exists() { + print_error("The specified directory does not contain a pack.toml file."); + return Err(Error::InvalidPackPathError(path.to_path_buf())); + } + toml_path + } else if path.is_file() + && path + .file_name() + .ok_or(Error::InvalidPackPathError(path.to_path_buf()))? + == "pack.toml" + { + path.to_path_buf() + } else { + print_error("The specified path is neither a directory nor a pack.toml file."); + return Err(Error::InvalidPackPathError(path.to_path_buf())); + }; + + let toml_content = fs::read_to_string(&toml_path)?; + let project_config = toml::from_str::(&toml_content)?; + + let main_path = toml_path + .parent() + .ok_or(Error::InvalidPackPathError(path.to_path_buf()))? + .join("src/main.shu"); + let compiled = shulkerscript_lang::compile(&main_path)?; + + let dist_path = toml_path + .parent() + .expect("Failed to get parent directory of pack.toml") + .join("dist") + .join(project_config.pack.name); + + compiled.place(&dist_path)?; + + print_info(&format!( + "Finished compiling project to {}", + util::to_absolute_path(&dist_path)? + )); + + Ok(()) +} diff --git a/src/subcommands/init.rs b/src/subcommands/init.rs index b3f1f19..c1d53e6 100644 --- a/src/subcommands/init.rs +++ b/src/subcommands/init.rs @@ -1,4 +1,7 @@ -use std::{fs, path::Path}; +use std::{ + fs, + path::{Path, PathBuf}, +}; use crate::{ config::ProjectConfig, @@ -7,14 +10,32 @@ use crate::{ util::to_absolute_path, }; -pub fn init( - verbose: bool, - path: &Path, - name: Option<&str>, - description: Option<&str>, +#[derive(Debug, clap::Args, Clone)] +pub struct InitArgs { + /// The path of the folder to initialize in. + #[clap(default_value = ".")] + path: PathBuf, + /// The name of the project. + #[clap(short, long)] + name: Option, + /// The description of the project. + #[clap(short, long)] + description: Option, + /// The pack format version. + #[clap(short, long)] pack_format: Option, + /// Force initialization even if the directory is not empty. + #[clap(short, long)] force: bool, -) -> Result<()> { +} + +pub fn init(verbose: bool, args: &InitArgs) -> Result<()> { + let path = args.path.as_path(); + let name = args.name.as_deref(); + let description = args.description.as_deref(); + let pack_format = args.pack_format; + let force = args.force; + if !path.exists() { print_error("The specified path does not exist."); Err(Error::PathNotFoundError(path.to_path_buf())) diff --git a/src/subcommands/lang_debug.rs b/src/subcommands/lang_debug.rs new file mode 100644 index 0000000..06f37df --- /dev/null +++ b/src/subcommands/lang_debug.rs @@ -0,0 +1,55 @@ +use clap::ValueEnum; + +use crate::error::Result; +use std::path::PathBuf; + +#[derive(Debug, clap::Args, Clone)] +pub struct LangDebugArgs { + /// The path of the project to compile. + #[clap(default_value = ".")] + path: PathBuf, + /// The state to dump. + #[clap(short, long, default_value = "ast")] + dump: DumpState, + /// Pretty-print the output. + #[clap(short, long)] + pretty: bool, +} + +#[derive(ValueEnum, Debug, Clone, Copy, Default)] +pub enum DumpState { + Tokens, + #[default] + Ast, + Datapack, +} + +pub fn lang_debug(args: &LangDebugArgs) -> Result<()> { + match args.dump { + DumpState::Tokens => { + let tokens = shulkerscript_lang::tokenize(&args.path)?; + if args.pretty { + println!("{:#?}", tokens); + } else { + println!("{:?}", tokens); + } + } + DumpState::Ast => { + let ast = shulkerscript_lang::parse(&args.path)?; + if args.pretty { + println!("{:#?}", ast); + } else { + println!("{:?}", ast); + } + } + DumpState::Datapack => { + let datapack = shulkerscript_lang::transpile(&args.path)?; + if args.pretty { + println!("{:#?}", datapack); + } else { + println!("{:?}", datapack); + } + } + } + Ok(()) +} diff --git a/src/subcommands/mod.rs b/src/subcommands/mod.rs index f771f37..695a256 100644 --- a/src/subcommands/mod.rs +++ b/src/subcommands/mod.rs @@ -1,2 +1,15 @@ mod init; -pub use init::init; +pub use init::{init, InitArgs}; + +mod compile; +pub use compile::{compile, CompileArgs}; + +#[cfg(feature = "zip")] +mod package; +#[cfg(feature = "zip")] +pub use package::{package, PackageArgs}; + +#[cfg(feature = "lang-debug")] +mod lang_debug; +#[cfg(feature = "lang-debug")] +pub use lang_debug::{lang_debug, LangDebugArgs}; diff --git a/src/subcommands/package.rs b/src/subcommands/package.rs new file mode 100644 index 0000000..5a59cb1 --- /dev/null +++ b/src/subcommands/package.rs @@ -0,0 +1,15 @@ +use crate::error::Result; +use std::path::PathBuf; + +#[derive(Debug, clap::Args, Clone)] +pub struct PackageArgs { + /// The path of the project to package. + #[clap(default_value = ".")] + path: PathBuf, +} + +pub fn package(_verbose: bool, _args: &PackageArgs) -> Result<()> { + println!("PACKAGE"); + + Ok(()) +}