Most of the refactor. Need to switch machines.

This commit is contained in:
Olivia Brooks
2026-02-15 09:36:04 -05:00
parent dda863e512
commit e41d4bcd76
61 changed files with 3390 additions and 618 deletions

10
crates/core/src/class.rs Normal file
View File

@@ -0,0 +1,10 @@
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
/// Data struct
#[derive(Debug, Clone, Deserialize, Serialize, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Class {
pub path: PathBuf,
pub checksum: String,
}

View File

@@ -0,0 +1,43 @@
use semver::Version;
use serde::{Deserialize, Serialize};
use crate::package::PackageHandler;
/// Data struct
#[derive(Debug, Clone, Deserialize, Serialize, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Dependency {
name: String,
version: Version,
pub checksum: String,
#[serde(skip_serializing_if = "<Option<_>>::is_none")]
source: Option<String>, // Path / URL
}
impl Dependency {
/// Returns a path to the dependency in local storage
/// if there is one.
pub fn local_path(&self) -> String {
if self.source.as_ref().is_some_and(|path| !is_url(path)) {
return self.source.clone().unwrap();
}
// TODO: Convert from reverse domain name to path.
return self.name.clone();
}
}
impl From<PackageHandler> for Dependency {
fn from(value: PackageHandler) -> Self {
Dependency {
name: value.name(),
version: value.version(),
checksum: String::new(),
source: None,
}
}
}
/// TODO: This is just a placeholder at present.
fn is_url<S: ToString>(path: S) -> bool {
return false;
}

25
crates/core/src/error.rs Normal file
View File

@@ -0,0 +1,25 @@
use derive_more::{Display, From};
pub type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, From, Display)]
pub enum Error {
#[from]
Io(io::Error),
#[from]
Java(java::Error),
MissingFileName,
#[from]
StdIo(std::io::Error),
#[from]
TomlDeserialize(toml::de::Error),
#[from]
TomlSerialize(toml::ser::Error),
UnknownPackage,
}

13
crates/core/src/lib.rs Normal file
View File

@@ -0,0 +1,13 @@
#![allow(dead_code)]
pub mod class;
pub mod dependency;
pub mod error;
pub mod meta;
pub mod nest;
pub mod package;
pub mod prelude;
pub mod prey;
pub mod workspace;
pub use error::{Error, Result};

40
crates/core/src/meta.rs Normal file
View File

@@ -0,0 +1,40 @@
use std::path::PathBuf;
use semver::Version;
use serde::{Deserialize, Serialize};
/// Data struct
#[derive(Debug, Clone, Deserialize, Serialize, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Meta {
pub name: String,
pub version: Version,
#[serde(skip_serializing_if = "<Option<_>>::is_none")]
pub authors: Option<Vec<String>>,
#[serde(skip_serializing_if = "<Option<_>>::is_none")]
pub repository: Option<String>,
#[serde(skip_serializing_if = "<Option<_>>::is_none")]
pub license: Option<String>,
#[serde(skip_serializing_if = "<Option<_>>::is_none")]
pub license_file: Option<PathBuf>,
}
impl Meta {
pub fn new<S: ToString>(name: S) -> Self {
let mut meta = Self::default();
meta.name = name.to_string();
meta
}
}
impl Default for Meta {
fn default() -> Self {
Meta {
name: String::from("Main"),
version: Version::new(0, 1, 0),
authors: None,
repository: None,
license: None,
license_file: None,
}
}
}

105
crates/core/src/nest.rs Normal file
View File

@@ -0,0 +1,105 @@
use std::collections::HashSet;
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize};
use crate::meta::Meta;
use crate::prelude::Dependency;
use crate::workspace::Workspace;
pub const F_NEST_TOML: &str = "Nest.toml";
pub const F_NEST_LOCK: &str = "Nest.lock";
/// Data struct
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
pub struct Nest {
workspace: Workspace,
meta: Meta,
dependencies: HashSet<Dependency>,
}
impl Nest {
pub fn new<S: ToString>(name: S) -> Self {
Self {
workspace: Workspace::default(),
meta: Meta::new(name),
dependencies: Default::default(),
}
}
pub fn write<P: AsRef<Path>>(&self, path: P) -> crate::Result<()> {
Ok(OpenOptions::new()
.write(true)
.create(true)
.open(path)?
.write_all(toml::to_string_pretty(&self)?.as_bytes())?)
}
pub fn default_package(&self) -> PathBuf {
self.workspace.default_package.clone()
}
pub fn name(&self) -> String {
self.meta.name.clone()
}
pub fn set_default_package<P: AsRef<Path>>(&mut self, package: P) {
self.workspace.default_package = package.as_ref().to_path_buf();
}
}
impl TryFrom<PathBuf> for Nest {
type Error = crate::Error;
fn try_from(value: PathBuf) -> Result<Self, Self::Error> {
let f = OpenOptions::new().read(true).open(value)?;
Self::try_from(f)
}
}
impl TryFrom<File> for Nest {
type Error = crate::Error;
fn try_from(mut value: File) -> Result<Self, Self::Error> {
let mut buf = String::new();
value.read_to_string(&mut buf)?;
Ok(toml::from_str(buf.as_str())?)
}
}
/// Data struct
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
pub struct NestLock {
pub dependencies: Vec<Dependency>,
}
impl NestLock {
pub fn write<P: AsRef<Path>>(&self, path: P) -> crate::Result<()> {
Ok(OpenOptions::new()
.write(true)
.create(true)
.open(path)?
.write_all(toml::to_string_pretty(&self)?.as_bytes())?)
}
}
impl TryFrom<PathBuf> for NestLock {
type Error = crate::Error;
fn try_from(value: PathBuf) -> Result<Self, Self::Error> {
let f = OpenOptions::new().read(true).open(value)?;
Self::try_from(f)
}
}
impl TryFrom<File> for NestLock {
type Error = crate::Error;
fn try_from(mut value: File) -> Result<Self, Self::Error> {
let mut buf = String::new();
value.read_to_string(&mut buf)?;
Ok(toml::from_str(buf.as_str())?)
}
}

View File

@@ -0,0 +1,56 @@
use std::hash::Hash;
use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize};
use crate::prey::{F_PREY_LOCK, F_PREY_TOML, Prey, PreyLock};
/// Hashing is only based off the Prey.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PackageHandler {
prey: Prey,
prey_lock: Option<PreyLock>,
package_root: PathBuf,
target_dir: PathBuf,
}
impl PackageHandler {
pub fn new<P: AsRef<Path>>(package_root: P, target_dir: P) -> crate::Result<Self> {
let package_root = package_root.as_ref().to_path_buf();
Ok(Self {
prey: Prey::try_from(package_root.join(F_PREY_TOML))?,
prey_lock: PreyLock::try_from(package_root.join(F_PREY_LOCK)).ok(),
package_root,
target_dir: target_dir.as_ref().to_path_buf(),
})
}
pub fn update_class_cache(&self) {}
pub fn entry_point(&self) -> PathBuf {
self.prey.entry_point()
}
pub fn name(&self) -> String {
self.prey.name()
}
pub fn version(&self) -> semver::Version {
self.prey.version()
}
}
impl Hash for PackageHandler {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.prey.hash(state);
}
}
/// Data struct
#[derive(Debug, Clone, Deserialize, Serialize, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Package {
pub entry_point: PathBuf,
}
//impl Into<Dependency> for Package {}

View File

@@ -0,0 +1,9 @@
#![allow(unused_imports)]
pub use crate::class::Class;
pub use crate::dependency::Dependency;
pub use crate::meta::Meta;
pub use crate::nest::{F_NEST_LOCK, F_NEST_TOML, Nest, NestLock};
pub use crate::package::{Package, PackageHandler};
pub use crate::prey::{F_PREY_LOCK, F_PREY_TOML, Prey, PreyLock};
pub use crate::workspace::{Workspace, WorkspaceHandler};

78
crates/core/src/prey.rs Normal file
View File

@@ -0,0 +1,78 @@
use std::collections::HashSet;
use std::fs::{File, OpenOptions};
use std::io::Read;
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use crate::class::Class;
use crate::meta::Meta;
use crate::package::Package;
pub const F_PREY_TOML: &str = "Prey.toml";
pub const F_PREY_LOCK: &str = "Prey.lock";
/// Data struct
#[derive(Debug, Clone, Deserialize, Serialize, Hash, PartialEq, Eq)]
pub struct Prey {
package: Package,
meta: Meta,
}
impl Prey {
pub fn entry_point(&self) -> PathBuf {
self.package.entry_point.clone()
}
pub fn name(&self) -> String {
self.meta.name.clone()
}
pub fn version(&self) -> semver::Version {
self.meta.version.clone()
}
}
impl TryFrom<PathBuf> for Prey {
type Error = crate::Error;
fn try_from(value: PathBuf) -> Result<Self, Self::Error> {
let f = OpenOptions::new().read(true).open(value)?;
Self::try_from(f)
}
}
impl TryFrom<File> for Prey {
type Error = crate::Error;
fn try_from(mut value: File) -> Result<Self, Self::Error> {
let mut buf = String::new();
value.read_to_string(&mut buf)?;
Ok(toml::from_str(buf.as_str())?)
}
}
/// Data struct
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct PreyLock {
classes: HashSet<Class>,
}
impl TryFrom<PathBuf> for PreyLock {
type Error = crate::Error;
fn try_from(value: PathBuf) -> Result<Self, Self::Error> {
let f = OpenOptions::new().read(true).open(value)?;
Self::try_from(f)
}
}
impl TryFrom<File> for PreyLock {
type Error = crate::Error;
fn try_from(mut value: File) -> Result<Self, Self::Error> {
let mut buf = String::new();
value.read_to_string(&mut buf)?;
Ok(toml::from_str(buf.as_str())?)
}
}

View File

@@ -0,0 +1,272 @@
use std::collections::HashMap;
use std::fs::{OpenOptions, read_dir};
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use fs::{self, expand_files};
use io::run_process;
use java::{self, JAVA_EXT_CLASS, JAVA_EXT_SOURCE};
use serde::{Deserialize, Serialize};
use crate::Error;
use crate::dependency::Dependency;
use crate::nest::{F_NEST_LOCK, F_NEST_TOML, Nest, NestLock};
use crate::package::PackageHandler;
use crate::prey::F_PREY_TOML;
pub struct WorkspaceHandler {
nest: Nest,
nest_lock: Option<NestLock>,
project_root: PathBuf,
packages: HashMap<PathBuf, PackageHandler>,
}
impl WorkspaceHandler {
const DIR_SRC: &str = "src/";
const DIR_TARGET: &str = "target/";
pub fn new<P: AsRef<Path>>(project_root: P) -> crate::Result<Self> {
let project_root = project_root.as_ref().canonicalize()?;
Ok(Self {
nest: Nest::new(
project_root
.file_name()
.ok_or(Error::MissingFileName)?
.display(),
),
nest_lock: None,
packages: HashMap::new(),
project_root,
})
}
pub fn load<P: AsRef<Path>>(project_root: P) -> crate::Result<Self> {
let project_root = project_root.as_ref().canonicalize()?;
let mut workspace_manager = Self {
nest: Nest::try_from(project_root.join(F_NEST_TOML))?,
nest_lock: NestLock::try_from(project_root.join(F_NEST_LOCK)).ok(),
packages: HashMap::new(),
project_root,
};
workspace_manager.discover_packages()?;
Ok(workspace_manager)
}
pub fn write(&self) -> crate::Result<()> {
self.nest.write(self.project_root.join(F_NEST_TOML))?;
if let Option::Some(lock) = self.nest_lock.clone() {
lock.write(self.project_root.join(F_NEST_LOCK))?;
}
Ok(())
}
//pub fn refresh_packages(&mut self) -> crate::Result<()> {}
/*
/// Future `build` method.
pub fn compile(&self, target: Option<PathBuf>) -> crate::Result<()> {
let mut target = target.unwrap_or(self.nest.default_package());
if !target.is_file() {
// Use is_file to skip messing with pathing for src/
// If target is not a file (explicit entry point), check if it's a known package
// and use that's package's default entry point.
target = target.join(
self.packages
.get(&target)
.ok_or(Error::UnknownPackage)?
.entry_point(),
);
}
//java::Compiler::new();
Ok(())
}
fn make_compiler_job<P: AsRef<Path>>(target: P) {
// Generate dependency tree.
}
*/
pub fn init(&mut self) -> crate::Result<()> {
let is_empty = read_dir(self.project_root.as_path()).is_ok_and(|tree| tree.count() == 0);
// ORDER MATTERS. THIS MUST COME FIRST.
// Make config file.
self.write_nest()?;
// Make .java-version
self.write_java_version()?;
// Make src/, target/, test/
self.write_dir_tree()?;
if !is_empty {
self.write_example_project()?;
self.discover_packages()?;
run_process(&["git", "init", "."])?;
}
// Append to .gitignore
if let Result::Ok(mut f) = OpenOptions::new()
.append(true)
.create(true)
.read(true)
.open(".gitignore")
{
let mut buf = String::new();
f.read_to_string(&mut buf)?;
for ignored in [
"# Automatically added by Raven".to_string(),
Self::DIR_TARGET.to_string(),
format!("*.{}", JAVA_EXT_CLASS),
] {
if !buf.contains(&ignored) {
f.write(format!("{}\n", ignored).as_bytes())?;
}
}
}
Ok(())
}
// This is the naive build
pub fn build(&mut self) -> crate::Result<()> {
Ok(())
}
/// Add any newly created packages.
fn discover_packages(&mut self) -> crate::Result<()> {
// Scan the src/ directory for entries,
// filter out the files,
// then construct PackageManagers for each package
// Promote *not* using reverse domain name tree structures
// by improving the speed of package discovery by using read_dir
// and checking for an immediate Prey.toml before expanding the
// whole subtree.
//
// Yes, I know this looks like shit.
// That's because it is.
for file in read_dir(Self::DIR_SRC)?
// Get directories
.filter_map(|entry| {
if entry.as_ref().is_ok_and(|entry| entry.path().is_dir()) {
Some(entry.unwrap().path())
} else {
None
}
})
// Get Prey.toml files
.filter_map(|dir| {
Some(if dir.join(F_PREY_TOML).exists() {
vec![dir.join(F_PREY_TOML)]
} else {
expand_files(dir)
.ok()?
.iter()
.filter_map(|file| {
if file.ends_with(PathBuf::from(F_PREY_TOML)) {
Some(file.to_owned())
} else {
None
}
})
.collect()
})
})
.flatten()
{
let package_root =
pathsub::sub_paths(file.as_path(), PathBuf::from(Self::DIR_SRC).as_path()).unwrap();
self.packages.insert(
package_root.to_path_buf(),
PackageHandler::new(package_root, PathBuf::from(Self::DIR_TARGET))?,
);
}
Ok(())
}
fn write_nest(&self) -> crate::Result<()> {
if let Result::Ok(mut f) = OpenOptions::new()
.write(true)
.create_new(true)
.open(F_NEST_TOML)
{
f.write_all(toml::to_string_pretty(&self.nest)?.as_bytes())?;
}
Ok(())
}
fn write_java_version(&self) -> crate::Result<()> {
if let Result::Ok(mut f) = OpenOptions::new()
.write(true)
.create_new(true)
.open(java::F_JAVA_VERSION)
{
f.write_all(format!("{}\n", java::get_javac_ver()?.major.to_string()).as_bytes())?;
}
Ok(())
}
fn write_dir_tree(&self) -> std::io::Result<()> {
for dir in [
format!("{}main/java", Self::DIR_SRC),
format!("{}test/java", Self::DIR_SRC),
Self::DIR_TARGET.to_string(),
] {
std::fs::create_dir_all(std::env::current_dir()?.join(dir))?;
}
Ok(())
}
fn write_example_project(&self) -> std::io::Result<()> {
// Make src/main/Main.java
if let Result::Ok(mut f) = OpenOptions::new().write(true).create_new(true).open(
PathBuf::from(Self::DIR_SRC)
.join("main/java/Main")
.with_extension(JAVA_EXT_SOURCE),
) {
f.write_all(include_bytes!("../assets/Main.java"))?;
}
// Make src/test/MainTest.java
if let Result::Ok(mut f) = OpenOptions::new().write(true).create_new(true).open(
PathBuf::from(Self::DIR_SRC)
.join("test/java/MainTest")
.with_extension(JAVA_EXT_SOURCE),
) {
f.write_all(include_bytes!("../assets/MainTest.java"))?;
}
Ok(())
}
}
/// Data struct
#[derive(Debug, Clone, Deserialize, Serialize, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Workspace {
pub default_package: PathBuf,
}
impl Default for Workspace {
fn default() -> Self {
Workspace {
default_package: PathBuf::from("main"),
}
}
}