Use structs instead of enum structs, add first implementations of other subcommands

This commit is contained in:
Moritz Hölting 2024-04-02 19:29:58 +02:00
parent d7a3f19967
commit 54e536d11f
8 changed files with 211 additions and 43 deletions

View File

@ -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}

View File

@ -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<String>,
/// The description of the project.
#[clap(short, long)]
description: Option<String>,
/// The pack format version.
#[clap(short, long)]
pack_format: Option<u8>,
/// 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(())

View File

@ -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<T> = std::result::Result<T, Error>;

View File

@ -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::<ProjectConfig>(&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(())
}

View File

@ -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<String>,
/// The description of the project.
#[clap(short, long)]
description: Option<String>,
/// The pack format version.
#[clap(short, long)]
pack_format: Option<u8>,
/// 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()))

View File

@ -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(())
}

View File

@ -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};

View File

@ -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(())
}