Compare commits

1 Commits

Author SHA1 Message Date
Cutieguwu b20a77c9a5 Update README.md 2026-04-30 08:46:53 -04:00
4 changed files with 142 additions and 124 deletions
+139 -1
View File
@@ -9,6 +9,11 @@ from the command line with sanity.
It takes inspiration from Cargo, and essentially serves as a sanity-preserving
wrapper for `javac` and `java`.
Because of the Rust-originating concepts, Raven *is opinionated*. If you have a
major problem with Rust, look away. If you genuinely intend on using Raven,
probably still look away. This likely won't enter any form of mainstream or
active long-term support.
## Installing
Installing with `cargo`:
@@ -22,7 +27,7 @@ worked out over time.
`raven` is only tested under linux. NT and Darwin support is **not** guaranteed.
WSL *may* affect the behaviour of raven. Quirks to be worked out.
WSL *may* affect the behaviour of Raven. Quirks to be worked out.
## Getting Started
@@ -61,3 +66,136 @@ Options:
will be implemented should there arise a need.
- [ ] Wrap errors properly, instead of hap-hazardly using `anyhow`
- [ ] Maybe proper logging?
## Project Structure
It helps if you're starting from an understanding of a Cargo Workspace.
- **Workspace:** The top level of a project. A workspace contains a series of
*packages*.
- **Package:** An individual library or binary which possesses its own
`Nest.toml` or `pom.xml`. This should be equivalent to Maven's concept of a
module.
- **Module:** Any organization of classes and modules which don't possess a
`Nest.toml` or `pom.xml`.
If you're unfamiliar with a cargo workspace manifest,
An example workspace Nest:
```toml
[workspace]
default_package = "app"
members = [
"app",
"cli",
"core",
"installer",
"io",
]
[meta]
name = "myProject"
version = "0.1.0"
[dependencies]
cli = { path = "cli" }
core = { path = "core" }
io = { path = "io" }
```
### Inheritance
Again, this takes from cargo's functionality.
To inherit from the workspace Nest (given the workspace example above):
```toml
[package]
entry_point = "Main.java"
[meta]
name = "app"
version = "0.1.0"
[dependnencies]
cli.workspace = true
io.workspace = true
```
### Reverse Domain Name Notation
Raven prefers this be trashed, burned, and ground into the earth with your boot.
It is possible to work around this with workspace members and default entry
points.
Raven feels little need to respect Java's standards.
### Sub-modules
Given the structure:
`WORKSPACE/packageA/packageB`
If you want to have subpackages like maven permits (via submodules), it is
technically possible by listing the submodule like so in the workspace members:
```toml
[workspace]
members = [
"packageA",
"packageA/packageB",
]
[dependencies]
packageA = { path = "packageA" }
[dependencies.packageA]
path = "packageA"
dependencies = { packageB = "packageA/packageB" }
```
Obviously, this is a total disaster, and is considered bad practise under Raven.
The project should be restructured to either:
1. Have all packages immediately within the workspace (remove all nesting), or
2. Move the complex package with subpackages out of tree into its own workspace
and add it as a dependency with a relative path in the workspace Nest. The
reason being, that packageA is so complex that it should be maintained as
its own thing, and is realistically a fairly universal library.
#### For Option 1
Example workspace Nest:
```toml
[workspace]
members = [
"packageA",
"packageB",
]
[dependencies]
packageA = { path = "packageA" }
packageB = { path = "packageB" }
```
#### For Option 2
Example workspace Nest:
```toml
[dependencies]
packageA = { path = "external/packageA" }
```
Where packageA's workspace Nest:
```toml
[workspace]
members = [
"core",
"packageB"
]
```
+1 -26
View File
@@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
use crate::{Error, Result};
/// [`Class`] represents a source file. It is a handler.
/// Data struct
#[derive(Debug, Clone, Deserialize, Serialize, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Class {
/// Path relative to PACKAGE/java, without file extension.
@@ -14,13 +14,6 @@ pub struct Class {
}
impl Class {
/// Creates a new [`Class`].
///
/// # Error
///
/// * [`Error::MismatchedPackage`] if the provided `file_path` is absolute, and cannot be
/// subtracted to just its relative path within its parent package.
/// * [`Error::Io`] if the source file cannot be loaded and digested.
pub fn new<P: AsRef<Path>>(package_src_root: P, file_path: P) -> Result<Self> {
let mut file_path = file_path.as_ref().to_path_buf();
if file_path.is_absolute() {
@@ -34,17 +27,6 @@ impl Class {
})
}
/// Returns a boolean representing if the class is up to date.
///
/// # Criteria
///
/// * The class path is local.
/// * The class has been compiled to `target/`
/// * The class' source file has not been updated.
///
/// # Errors
///
/// * [`Error::Io`]
pub fn is_updated<P: AsRef<Path>>(&self, class_path: P) -> Result<bool> {
// If the path is local and that file has not been updated.
Ok(class_path
@@ -55,13 +37,6 @@ impl Class {
&& self.checksum == sha256::try_digest(self.path.clone())?)
}
/// Updates the class' checksum.
///
/// Only call this if you know that the class has been compiled successfully.
///
/// # Errors
///
/// * [`Error::Io`] if the source file cannot be loaded and digested.
pub fn update<P: AsRef<Path>>(&mut self, package_src_root: P) -> Result<()> {
self.checksum = sha256::try_digest(dbg!(
package_src_root
+2 -89
View File
@@ -14,10 +14,6 @@ use crate::package::PackageHandler;
use crate::prey::{F_PREY_LOCK, F_PREY_TOML, Prey};
use crate::{Error, package};
/// Represents a raven workspace.
///
/// [`WorkspaceHandler`] manages project compilation, cache writing,
/// and tracking of all packages that make up the workspace.
#[derive(Debug)]
pub struct WorkspaceHandler {
nest: Nest,
@@ -30,13 +26,6 @@ impl WorkspaceHandler {
const DIR_SRC: &str = "src/";
const DIR_TARGET: &str = "target/";
/// Creates a new [`WorkspaceHandler`] instance at the provided path.
///
/// # Errors
///
/// * [`Error::Io`] if the `project_root` cannot be canonicalized.
/// * [`Error::MissingFileName`] if a name for the workspace cannot be derived
/// from the `project_root`
pub fn new<P: AsRef<Path>>(project_root: P) -> crate::Result<Self> {
let project_root = project_root.as_ref().canonicalize()?;
@@ -53,14 +42,6 @@ impl WorkspaceHandler {
})
}
/// Loads an existing workspace from the provided path.
///
/// # Errors
///
/// * [`Error::Io`] for any number of IO-related errors.
/// Unwrap the [`std::io::Error`] for further information.
/// * [`Error::MissingFileName`] if a name for the workspace cannot be derived
/// from the `project_root`
pub fn load<P: AsRef<Path>>(project_root: P) -> crate::Result<Self> {
let project_root = project_root.as_ref().canonicalize()?;
@@ -76,7 +57,6 @@ impl WorkspaceHandler {
Ok(workspace_manager)
}
/// Writes all lock files (Nest.toml, src/**/Prey.toml) to the disk.
pub fn write_locks(&self) -> crate::Result<()> {
if let Option::Some(lock) = self.nest_lock.clone() {
lock.write(self.project_root.join(F_NEST_LOCK))?;
@@ -89,26 +69,6 @@ impl WorkspaceHandler {
Ok(())
}
/// Initializes a new workspace.
///
/// # Behaviour
///
/// Init will avoid overwriting or adding an example project if raven is being integrated into
/// an existing workspace codebase.
///
/// Init will always write:
///
/// * Nest.toml
/// * .java-version
/// * Attempt to append to .gitignore
///
/// If this is an empty workspace, init will include:
///
/// * Calling `git init`
/// * Writing an example project.
/// * Automatic package discovery.
///
/// # Errors
pub fn init(&mut self) -> crate::Result<&mut Self> {
// ORDER MATTERS.
let is_empty = read_dir(self.project_root.as_path()).is_ok_and(|tree| tree.count() == 0);
@@ -154,18 +114,7 @@ impl WorkspaceHandler {
Ok(self)
}
/// This is a naive build; it attempts to build every source file individually.
///
/// It calls [`PackageHandler::get_outdated()`] on all known packages to determine
/// compile targets.
///
/// It will then attempt to write all locks to update the caches and avoid attempting to
/// needlessly recompile targets.
///
/// # Errors
///
/// * [`Error::AbsentPrimeError`]
/// * [`Error::Io`]
// This is the naive build
pub fn build(&mut self) -> crate::Result<&mut Self> {
let compiler = java::compiler::CompilerBuilder::new()
.class_path(Self::DIR_TARGET)
@@ -203,20 +152,6 @@ impl WorkspaceHandler {
Ok(self)
}
/// Attempts to call the JVM to run, in order of precedence:
///
/// 1. Provided entry point which includes a specific source file.
/// 2. Provided entry point to a package, using that package's default entry point
/// defined in it's `Prey.toml`
/// 3. The workspace's default package defined in `Nest.toml`, and that package's default
/// entry point as defined in it's `Prey.toml`.
///
/// # Errors
///
/// * [`Error::UnknownPackage`] if the package cannot be found.
/// * [`Error::UndefinedEntryPoint`] if no fallback entry point can be found.
/// * [`Error::Io`]
/// * [`Error::Java`]
pub fn run<P: AsRef<Path>>(
&mut self,
entry_point: Option<P>,
@@ -252,11 +187,6 @@ impl WorkspaceHandler {
Ok(self)
}
/// Cleans the workspace of it's caches:
///
/// * `target/`
/// * Nest.lock
/// * Prey.lock
pub fn clean(&mut self) -> crate::Result<&mut Self> {
// Clear Nest.lock
if let Err(err) = std::fs::remove_file(self.project_root.join(F_NEST_LOCK)) {
@@ -285,14 +215,7 @@ impl WorkspaceHandler {
Ok(self)
}
/// Scan for newly created packages, wrapping them in [`PackageHandler`]s
/// and adding them to the [`WorkspaceHandler`]'s package map.
///
/// Packages are identified by their `Prey.toml` manifests.
///
/// # Errors
///
/// * [`Error::Io`]
/// Add any newly created packages.
fn discover_packages(&mut self) -> crate::Result<()> {
// Scan the src/ directory for entries,
// filter out the files,
@@ -357,18 +280,10 @@ impl WorkspaceHandler {
Ok(())
}
/// Writes the [`WorkspaceHandler`]'s `Nest.toml` to disk.
fn write_nest(&self) -> crate::Result<()> {
Ok(self.nest.write(self.project_root.clone())?)
}
/// Writes a `.java-version` file for `jenv` to the disk with an automatically parsed
/// java version from [`java::get_javac_version()`].
///
/// # Errors
///
/// * [`Error::Io`]
/// * [`Error::Java`]
fn write_java_version(&self) -> crate::Result<()> {
if let Result::Ok(mut f) = OpenOptions::new()
.write(true)
@@ -381,7 +296,6 @@ impl WorkspaceHandler {
Ok(())
}
/// Writes an example project tree to disk.
fn write_dir_tree(&self) -> std::io::Result<()> {
for dir in [
format!("{}main/java", Self::DIR_SRC),
@@ -394,7 +308,6 @@ impl WorkspaceHandler {
Ok(())
}
/// Writes an example project to disk.
fn write_example_project(&self) -> crate::Result<()> {
let main: PathBuf = PathBuf::from(Self::DIR_SRC).join("main/");
let test: PathBuf = PathBuf::from(Self::DIR_SRC).join("test/");
-8
View File
@@ -5,11 +5,6 @@ use cli::{CLI_ARGS, Command};
fn main() -> anyhow::Result<()> {
// type_ is yet unused.
// `raven new` requires special behaviour.
// This will call the "new" part of the command,
// changing the cwd, before calling WorkspaceHandler.init()
// on the actual workspace directory.
if let Command::New { project_name, .. } = &CLI_ARGS.command {
new(project_name.to_owned())?;
}
@@ -40,10 +35,7 @@ fn main() -> anyhow::Result<()> {
Ok(())
}
/// Creates a new project directory and enters it.
fn new(project_name: String) -> anyhow::Result<()> {
// Because this messes around with the CWD, this should live external to any
// instance of a `WorkspaceHandler`.
let cwd = std::env::current_dir()?.join(project_name);
std::fs::create_dir(&cwd)?;