Initial Commit.
This commit is contained in:
261
src/main.rs
Normal file
261
src/main.rs
Normal file
@@ -0,0 +1,261 @@
|
||||
mod cli;
|
||||
mod env;
|
||||
mod fs;
|
||||
mod io;
|
||||
mod java;
|
||||
mod nest;
|
||||
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{LazyLock, Mutex};
|
||||
|
||||
use cli::{CONFIG, Command};
|
||||
use java::{JAVA_EXT_CLASS, JAVA_EXT_SOURCE};
|
||||
use nest::{Class, NEST, NestLock};
|
||||
|
||||
use anyhow::Context;
|
||||
use bytesize::ByteSize;
|
||||
|
||||
const DIR_TARGET: LazyLock<PathBuf> = LazyLock::new(|| PathBuf::from("target/"));
|
||||
const DIR_TEST: LazyLock<PathBuf> = LazyLock::new(|| PathBuf::from("test/"));
|
||||
const DIR_SRC: LazyLock<PathBuf> = LazyLock::new(|| PathBuf::from("src/"));
|
||||
const F_NEST_TOML: LazyLock<PathBuf> = LazyLock::new(|| PathBuf::from("Nest.toml"));
|
||||
const F_NEST_LOCK: LazyLock<PathBuf> = LazyLock::new(|| PathBuf::from("Nest.lock"));
|
||||
|
||||
/// This may not actually be the project root.
|
||||
pub static PROJECT_ROOT: LazyLock<Mutex<PathBuf>> = LazyLock::new(|| {
|
||||
Mutex::new(std::env::current_dir().expect("Failed to get current working directory"))
|
||||
});
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
// Ensure that Nest.toml exists, if the requested command depends upon it.
|
||||
if CONFIG.command.depends_on_nest() && NEST.is_err() {
|
||||
println!("No Nest.toml found in project directory");
|
||||
println!("Aborting...");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match &CONFIG.command {
|
||||
Command::Init => init()?,
|
||||
Command::New { project_name } => {
|
||||
new(project_name.to_owned())?;
|
||||
init()?;
|
||||
}
|
||||
Command::Build => {
|
||||
build()?;
|
||||
}
|
||||
Command::Run {
|
||||
entry_point,
|
||||
assertions,
|
||||
} => {
|
||||
build()?;
|
||||
run(entry_point, assertions.into())?;
|
||||
}
|
||||
Command::Test { assertions } => {
|
||||
test(assertions.into())?;
|
||||
}
|
||||
Command::Clean => clean(),
|
||||
}
|
||||
|
||||
println!("Done.");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init() -> anyhow::Result<()> {
|
||||
let is_empty = std::fs::read_dir(PROJECT_ROOT.lock().expect("Failed to lock mutex").as_path())
|
||||
.is_ok_and(|tree| tree.count() == 0);
|
||||
|
||||
let project_name = PROJECT_ROOT
|
||||
.lock()
|
||||
.expect("Failed to lock mutex")
|
||||
.file_name()
|
||||
.context("Invalid directory name")?
|
||||
.to_str()
|
||||
.context("Unable to convert OsStr to str")?
|
||||
.to_owned();
|
||||
|
||||
// Make src, target, tests
|
||||
for dir in [DIR_SRC, DIR_TARGET, DIR_TEST] {
|
||||
std::fs::create_dir(dir.clone())?;
|
||||
}
|
||||
|
||||
// Make config file.
|
||||
if let Result::Ok(mut f) = OpenOptions::new()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(F_NEST_TOML.as_path())
|
||||
{
|
||||
f.write_all(
|
||||
toml::to_string_pretty(&nest::Nest {
|
||||
package: nest::Package {
|
||||
name: project_name.to_owned(),
|
||||
..Default::default()
|
||||
},
|
||||
})?
|
||||
.as_bytes(),
|
||||
)?;
|
||||
}
|
||||
|
||||
// Make .java-version
|
||||
if let Result::Ok(mut f) = OpenOptions::new()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(PathBuf::from(".java-version"))
|
||||
{
|
||||
f.write_all(
|
||||
{
|
||||
let mut version = crate::env::get_javac_ver()?.major.to_string();
|
||||
version.push('\n');
|
||||
version
|
||||
}
|
||||
.as_bytes(),
|
||||
)?;
|
||||
}
|
||||
|
||||
// Make Main.java
|
||||
if let Result::Ok(mut f) = OpenOptions::new()
|
||||
.write(true)
|
||||
.create_new(is_empty)
|
||||
.open(DIR_SRC.clone().join("Main").with_extension(JAVA_EXT_SOURCE))
|
||||
{
|
||||
f.write_all(include_bytes!("../assets/Main.java"))?;
|
||||
}
|
||||
|
||||
// git init .
|
||||
crate::io::run_process(&["git", "init", "."])?;
|
||||
|
||||
// Append to .gitignore
|
||||
if let Result::Ok(mut f) = OpenOptions::new()
|
||||
.append(true)
|
||||
.create(true)
|
||||
.read(true)
|
||||
.open(".gitignore")
|
||||
{
|
||||
dbg!();
|
||||
let mut buf = String::new();
|
||||
f.read_to_string(&mut buf)?;
|
||||
|
||||
for ignored in [
|
||||
DIR_TARGET.as_path().display().to_string(),
|
||||
format!("*.{}", JAVA_EXT_CLASS),
|
||||
] {
|
||||
if dbg!(!buf.contains(&ignored)) {
|
||||
f.write(format!("{}\n", ignored).as_bytes())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn new(project_name: String) -> anyhow::Result<()> {
|
||||
let cwd = PROJECT_ROOT
|
||||
.lock()
|
||||
.expect("Failed to lock mutex")
|
||||
.join(project_name);
|
||||
|
||||
std::fs::create_dir(&cwd)?;
|
||||
std::env::set_current_dir(&cwd)?;
|
||||
|
||||
// Replace PathBuf within mutex
|
||||
let mut state = PROJECT_ROOT.lock().expect("Failed to lock mutex");
|
||||
*state = std::env::current_dir().expect("Failed to change current working directory");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build() -> anyhow::Result<()> {
|
||||
let mut targets = crate::fs::expand_files(DIR_SRC.as_path())?;
|
||||
let mut nest_lock = NestLock::load().unwrap_or_default();
|
||||
|
||||
nest_lock.update();
|
||||
|
||||
let mut retained_targets = vec![];
|
||||
for path in targets.into_iter() {
|
||||
if let Option::Some((_path, class)) = nest_lock.0.get_key_value(&path)
|
||||
&& class.is_updated()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
retained_targets.push(path);
|
||||
}
|
||||
|
||||
targets = retained_targets;
|
||||
|
||||
let javac = java::CompilerBuilder::new()
|
||||
.class_path(DIR_TARGET.as_path())
|
||||
.destination(DIR_TARGET.as_path())
|
||||
.build();
|
||||
|
||||
for target in targets {
|
||||
println!("Compiling {}", target.display());
|
||||
if javac.clone().compile(target.as_path()).is_ok()
|
||||
&& let Result::Ok(class) = Class::try_from(target.clone())
|
||||
{
|
||||
nest_lock.0.insert(target, class);
|
||||
}
|
||||
}
|
||||
|
||||
OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open(F_NEST_LOCK.as_path())?
|
||||
.write_all(toml::to_string_pretty(&nest_lock)?.as_bytes())
|
||||
.with_context(|| format!("Failed to write {}", F_NEST_LOCK.display()))
|
||||
}
|
||||
|
||||
fn run<P: AsRef<Path>>(
|
||||
entry_point: P,
|
||||
assertions: bool,
|
||||
) -> anyhow::Result<(Option<String>, Option<String>)> {
|
||||
crate::env::set_cwd(DIR_TARGET.as_path())?;
|
||||
|
||||
java::JVMBuilder::new()
|
||||
.assertions(assertions)
|
||||
.monitor(true)
|
||||
.build()
|
||||
.run(entry_point)
|
||||
}
|
||||
|
||||
fn test(assertions: bool) -> anyhow::Result<(Option<String>, Option<String>)> {
|
||||
java::CompilerBuilder::new()
|
||||
.class_path(DIR_TARGET.as_path())
|
||||
.destination(DIR_TARGET.as_path())
|
||||
.build()
|
||||
.compile(DIR_TEST.as_path())?;
|
||||
|
||||
// Change cwd to avoid Java pathing issues.
|
||||
crate::env::set_cwd(
|
||||
PROJECT_ROOT
|
||||
.lock()
|
||||
.expect("Failed to lock mutex")
|
||||
.join(DIR_TARGET.as_path()),
|
||||
)?;
|
||||
|
||||
java::JVMBuilder::new()
|
||||
.assertions(assertions)
|
||||
.ram_min(ByteSize::mib(128))
|
||||
.ram_max(ByteSize::mib(512))
|
||||
.monitor(true)
|
||||
.build()
|
||||
.run(DIR_TEST.as_path())
|
||||
}
|
||||
|
||||
fn clean() {
|
||||
let _ = std::fs::remove_file(
|
||||
PROJECT_ROOT
|
||||
.lock()
|
||||
.expect("Failed to lock mutex")
|
||||
.join(F_NEST_LOCK.as_path()),
|
||||
);
|
||||
let _ = std::fs::remove_dir_all(
|
||||
PROJECT_ROOT
|
||||
.lock()
|
||||
.expect("Failed to lock mutex")
|
||||
.join(DIR_TARGET.as_path()),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user