Compare commits

1 Commits

66 changed files with 902 additions and 2784 deletions

0
.gitignore vendored Executable file → Normal file
View File

View File

@@ -1,18 +0,0 @@
// Project-local debug tasks
//
// For more documentation on how to configure debug tasks,
// see: https://zed.dev/docs/debugger
[
{
"label": "Build & Debug raven new",
"build": {
"command": "cargo",
"args": ["run", "--", "new", "JavaProjectTest"],
},
"program": "$ZED_WORKTREE_ROOT/target/debug/binary",
// sourceLanguages is required for CodeLLDB (not GDB) when using Rust
"sourceLanguages": ["rust"],
"request": "launch",
"adapter": "CodeLLDB",
},
]

View File

@@ -1,9 +0,0 @@
// Folder-specific settings
//
// For a full list of overridable settings, and general information on folder-specific settings,
// see the documentation: https://zed.dev/docs/configuring-zed#settings-files
{
"file_types": {
"TOML": ["lock"],
},
}

0
AUTHORS.md Executable file → Normal file
View File

0
CODE_OF_CONDUCT.md Executable file → Normal file
View File

283
Cargo.lock generated Executable file → Normal file
View File

@@ -54,9 +54,9 @@ dependencies = [
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.101" version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]] [[package]]
name = "async-trait" name = "async-trait"
@@ -66,14 +66,17 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.116", "syn",
] ]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.11.0" version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
dependencies = [
"serde_core",
]
[[package]] [[package]]
name = "block-buffer" name = "block-buffer"
@@ -86,9 +89,9 @@ dependencies = [
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.11.1" version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
[[package]] [[package]]
name = "bytesize" name = "bytesize"
@@ -104,9 +107,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.58" version = "4.5.54"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63be97961acde393029492ce0be7a1af7e323e6bae9511ebfac33751be5e6806" checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@@ -114,9 +117,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.58" version = "4.5.54"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f13174bda5dfd69d7e947827e5af4b0f2f94a4a3ee92912fba07a66150f21e2" checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@@ -126,28 +129,21 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.55" version = "4.5.49"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.116", "syn",
] ]
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "1.0.0" version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32"
[[package]]
name = "cli"
version = "0.2.1"
dependencies = [
"clap",
]
[[package]] [[package]]
name = "colorchoice" name = "colorchoice"
@@ -155,31 +151,6 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "convert_case"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "core"
version = "0.1.4"
dependencies = [
"anyhow",
"derive_more",
"fs",
"java",
"pathsub",
"semver",
"serde",
"sha256",
"subprocess",
"toml",
]
[[package]] [[package]]
name = "cpufeatures" name = "cpufeatures"
version = "0.2.17" version = "0.2.17"
@@ -199,29 +170,6 @@ dependencies = [
"typenum", "typenum",
] ]
[[package]]
name = "derive_more"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
dependencies = [
"derive_more-impl",
]
[[package]]
name = "derive_more-impl"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"rustc_version",
"syn 2.0.116",
"unicode-xid",
]
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.10.7" version = "0.10.7"
@@ -238,13 +186,6 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "fs"
version = "0.1.0"
dependencies = [
"derive_more",
]
[[package]] [[package]]
name = "generic-array" name = "generic-array"
version = "0.14.7" version = "0.14.7"
@@ -255,31 +196,6 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "hard-xml"
version = "1.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b07b8ba970e18a03dbb79f6786b6e4d6f198a0ac839aa5182017001bb8dee17"
dependencies = [
"hard-xml-derive",
"jetscii",
"lazy_static",
"memchr",
"xmlparser",
]
[[package]]
name = "hard-xml-derive"
version = "1.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0c43e7c3212bd992c11b6b9796563388170950521ae8487f5cdf6f6e792f1c8"
dependencies = [
"bitflags",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.16.1" version = "0.16.1"
@@ -314,40 +230,17 @@ version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]]
name = "java"
version = "0.2.2"
dependencies = [
"bytesize",
"derive_more",
"fs",
"semver",
"subprocess",
]
[[package]]
name = "jetscii"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47f142fe24a9c9944451e8349de0a56af5f3e7226dc46f3ed4d4ecc0b85af75e"
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.182" version = "0.2.180"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
[[package]] [[package]]
name = "memchr" name = "once_cell"
version = "2.8.0" version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]] [[package]]
name = "once_cell_polyfill" name = "once_cell_polyfill"
@@ -355,29 +248,12 @@ version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "pathsub"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dadd38133bcbe43264410412c48614bab4ef899f0792ffc4530dc19ec000a970"
[[package]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.16" version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "pom"
version = "0.1.0"
dependencies = [
"derive_more",
"hard-xml",
"semver",
"serde",
"strum",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.106" version = "1.0.106"
@@ -389,33 +265,40 @@ dependencies = [
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.44" version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]] [[package]]
name = "raven" name = "raven"
version = "0.2.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytesize", "bytesize",
"cli", "clap",
"core", "ron",
"fs", "semver",
"java", "serde",
"sha256",
"subprocess",
"toml", "toml",
] ]
[[package]] [[package]]
name = "rustc_version" name = "ron"
version = "0.4.1" version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" checksum = "fd490c5b18261893f14449cbd28cb9c0b637aebf161cd77900bfdedaff21ec32"
dependencies = [ dependencies = [
"semver", "bitflags",
"once_cell",
"serde",
"serde_derive",
"typeid",
"unicode-ident",
] ]
[[package]] [[package]]
@@ -455,7 +338,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.116", "syn",
] ]
[[package]] [[package]]
@@ -497,32 +380,11 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "strum"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.116",
]
[[package]] [[package]]
name = "subprocess" name = "subprocess"
version = "1.0.0" version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98e067360c7f5a302e35c9f9cd24cb583c378fbedc73d0237284dbdd0650fcc5" checksum = "f75238edb5be30a9ea3035b945eb9c319dde80e879411cdc9a8978e1ac822960"
dependencies = [ dependencies = [
"libc", "libc",
"winapi", "winapi",
@@ -530,20 +392,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "2.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.116"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -562,9 +413,9 @@ dependencies = [
[[package]] [[package]]
name = "toml" name = "toml"
version = "1.0.1+spec-1.1.0" version = "0.9.11+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe30f93627849fa362d4a602212d41bb237dc2bd0f8ba0b2ce785012e124220" checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"serde_core", "serde_core",
@@ -577,18 +428,18 @@ dependencies = [
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "1.0.0+spec-1.1.0" version = "0.7.5+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
dependencies = [ dependencies = [
"serde_core", "serde_core",
] ]
[[package]] [[package]]
name = "toml_parser" name = "toml_parser"
version = "1.0.8+spec-1.1.0" version = "1.0.6+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0742ff5ff03ea7e67c8ae6c93cac239e0d9784833362da3f9a9c1da8dfefcbdc" checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44"
dependencies = [ dependencies = [
"winnow", "winnow",
] ]
@@ -599,6 +450,12 @@ version = "1.0.6+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607"
[[package]]
name = "typeid"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.19.0" version = "1.19.0"
@@ -607,21 +464,9 @@ checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.23" version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "unicode-segmentation"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
@@ -677,9 +522,3 @@ name = "winnow"
version = "0.7.14" version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
[[package]]
name = "xmlparser"
version = "0.13.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4"

58
Cargo.toml Executable file → Normal file
View File

@@ -1,50 +1,28 @@
[workspace] [package]
resolver = "3" name = "raven"
members = ["crates/*"] version = "0.1.0"
default-members = ["crates/*"]
[workspace.package]
edition = "2024" edition = "2024"
license = "MIT" authors = ["Olivia Brooks", "Adrian Long"]
repository = "https://gitea.cutieguwu.ca/Cutieguwu/raven" repository = "https://gitea.cutieguwu.ca/Cutieguwu/raven"
license = "MIT"
publish = false publish = false
[workspace.dependencies] [dependencies]
#
# Workspace member crates
#
cli = { path = "crates/cli" }
fs = { path = "crates/fs" }
java = { path = "crates/java" }
core = { path = "crates/core" }
pom = { path = "crates/pom" }
raven = { path = "crates/raven" }
#
# External crates
#
anyhow = "1.0" anyhow = "1.0"
bytesize = "2.3" bytesize = "2.3"
const_format = "0.2.35" ron = "0.12"
hard-xml = "1.41"
lenient_semver = "0.4.2"
pathsub = "0.1.1"
ron = "0.12.0"
sha256 = "1.6" sha256 = "1.6"
subprocess = "1.0" subprocess = "0.2"
toml = "1.0" toml = "0.9"
tree-sitter = "0.22"
tree-sitter-java = "0.21.0"
derive_more = { version = "2.1", features = ["display", "from"] } [dependencies.clap]
semver = { version = "1.0",features = ["serde"] } version = "4.5"
serde = { version = "1.0",features = ["derive"] } features = ["cargo", "derive"]
strum = { version = "0.27.2", features = ["derive"] }
[workspace.lints.rust] [dependencies.semver]
unsafe_code = "forbid" version = "1.0"
features = ["serde"]
[profile.dev] [dependencies.serde]
incremental = true version = "1.0"
features =["derive"]

0
LICENSE Executable file → Normal file
View File

27
README.md Executable file → Normal file
View File

@@ -29,35 +29,18 @@ WSL *may* affect the behaviour of raven. Quirks to be worked out.
```bash ```bash
raven new demo raven new demo
cd demo cd demo
raven run main raven run main.Main
```
## Raven commands
```bash
Usage: raven <COMMAND>
Commands:
new Create a new raven project
init Create a new raven project in an existing directory
build Compile the current project
run Run the current project
test !!! BORKED !!! Run the tests
clean Remove the target directory and caching
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
-V, --version Print version
``` ```
## Future plans ## Future plans
- [ ] Make `raven run` fall back to an entry point specified in Nest.toml, when - [ ] Fix `raven test`. Totally borked.
- [ ] Make `nest run` fall back to an entry point specified in Nest.toml, when
none provided. none provided.
- [ ] Fix project structure, eliminate inter-dependencies. - [ ] Fix project structure, eliminate inter-dependencies.
- [ ] Separate out `java.rs` into a dedicated wrapper library. - [ ] Separate out `java.rs` into a dedicated wrapper library.
- [ ] Possibly add support for pulling remote packages via `raven add`. This - [ ] Possibly add support for pulling remote packages via `nest add`. This
will be implemented should there arise a need. will be implemented should there arise a need.
- [ ] Wrap errors properly, instead of hap-hazardly using `anyhow` - [ ] Wrap errors properly, instead of hap-hazardly using `anyhow`
- [ ] Maybe proper logging? - [ ] Maybe proper logging?
- [ ] Fix using hashes to avoid unnesscesary recompilation.

3
Test1/.gitignore vendored
View File

@@ -1,3 +0,0 @@
# Automatically added by Raven
target/
*.class

10
Test1/.idea/.gitignore generated vendored
View File

@@ -1,10 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="app" />
<module name="core" />
</profile>
</annotationProcessing>
</component>
</project>

View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
</component>
</project>

12
Test1/.idea/misc.xml generated
View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="25" project-jdk-type="JavaSDK" />
</project>

6
Test1/.idea/vcs.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

View File

@@ -1 +0,0 @@
25

View File

@@ -1,4 +0,0 @@
[package]
name = "Test1"
version = "0.1.0"
entry_point = "app"

View File

@@ -1,2 +0,0 @@
target/
*.class

View File

@@ -1,7 +0,0 @@
[package]
name = "app"
version = "0.1.0"
entry_point = "com/raven/Main.java"
[dependencies]
code = {path = "../core"}

View File

@@ -1,23 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.raven</groupId>
<artifactId>multi-module-demo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>app</artifactId>
<dependencies>
<dependency>
<groupId>com.raven</groupId>
<artifactId>core</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>

View File

@@ -1,7 +0,0 @@
package com.raven;
public class Main {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}

View File

@@ -1,5 +0,0 @@
public class Test {
public static void main(String[] args) {
System.out.println("This is a test.");
}
}

View File

@@ -1,2 +0,0 @@
target/
*.class

View File

@@ -1,4 +0,0 @@
[package]
name = "core"
version = "0.1.0"
entry_point = "com/raven/Main.java"

View File

@@ -1,15 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.raven</groupId>
<artifactId>multi-module-demo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>core</artifactId>
</project>

View File

@@ -1,7 +0,0 @@
package com.raven;
public class Main {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}

View File

@@ -1,5 +0,0 @@
public class Test {
public static void main(String[] args) {
System.out.println("This is a test.");
}
}

View File

@@ -1,18 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.raven</groupId>
<artifactId>multi-module-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>core</module>
<module>app</module>
</modules>
</project>

View File

@@ -1,3 +1,5 @@
package main;
public class Main { public class Main {
public static void main(String[] args) { public static void main(String[] args) {

View File

@@ -1,10 +1,12 @@
public class MainTest { package test;
public class Main {
public static void main(String[] args) { public static void main(String[] args) {
testAdd(); testAdd();
} }
public static void testAdd() { public static void testAdd() {
assert Main.add(2, 2) == 4; assert main.Main.add(2, 2) == 4;
} }
} }

View File

@@ -1,14 +0,0 @@
[package]
name = "cli"
version = "0.2.1"
edition.workspace = true
license.workspace = true
description = "Raven's CLI"
repository.workspace = true
publish.workspace = true
[dependencies.clap]
version = "4.5"
features = ["cargo", "derive"]

View File

@@ -1,152 +0,0 @@
use std::sync::LazyLock;
use std::{fmt::Display, path::PathBuf};
use clap::{ArgAction, Args, Parser, Subcommand, ValueEnum};
pub static CLI_ARGS: LazyLock<CliArgs> = LazyLock::new(|| CliArgs::parse());
#[derive(Debug, Parser)]
#[clap(name = "Raven", version = "0.2.0")]
pub struct CliArgs {
#[command(subcommand)]
pub command: Command,
}
#[derive(Debug, Clone, Subcommand)]
pub enum Command {
/// Create a new raven project
New {
#[clap(flatten)]
type_: ProjectFlag,
project_name: String,
},
/// Create a new raven project in an existing directory
Init,
/// Compile the current project
Build,
/// Run the current project
Run {
#[clap(value_hint = clap::ValueHint::DirPath)]
entry_point: Option<PathBuf>,
#[clap(flatten)]
assertions: Assertions,
},
/// !!! BORKED !!! Run the tests
Test {
#[clap(flatten)]
assertions: Assertions,
},
/// Remove the target directory and caching
Clean,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Args)]
pub struct Assertions {
/// Disable assertions.
#[arg(short, long = "no-assert", action = ArgAction::SetFalse)]
assertions: bool,
}
impl Into<bool> for &Assertions {
fn into(self) -> bool {
self.assertions
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Args)]
#[group(multiple = false)]
pub struct ProjectFlag {
#[arg(long, action = ArgAction::SetTrue)]
_nest: (),
#[arg(long, action = ArgAction::SetTrue)]
_package: (),
#[arg(
hide = true,
required = false,
short,
long,
default_value_ifs = [
("_nest", "true", "nest"),
("_package", "true", "package"),
("_nest", "false", "nest"),
],
)]
pub project_type: ProjectType,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, ValueEnum)]
#[value(rename_all = "kebab-case")]
pub enum ProjectType {
#[default]
Nest,
Package,
}
impl Display for ProjectType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", Self::to_string(&self))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn args_new_w_arg() {
let args: CliArgs = Parser::try_parse_from(["raven", "new", "demo"]).unwrap();
let type_ = match args.command {
Command::New { type_, .. } => type_,
_ => unreachable!(),
};
assert!(type_.project_type == ProjectType::Nest);
}
#[test]
fn args_new_nest() {
let args: CliArgs = Parser::try_parse_from(["raven", "new", "--nest", "demo"]).unwrap();
let type_ = match args.command {
Command::New { type_, .. } => type_,
_ => unreachable!(),
};
assert!(type_.project_type == ProjectType::Nest);
}
#[test]
fn args_new_package() {
let args: CliArgs = Parser::try_parse_from(["raven", "new", "--package", "demo"]).unwrap();
let type_ = match args.command {
Command::New { type_, .. } => type_,
_ => unreachable!(),
};
assert!(type_.project_type == ProjectType::Package);
}
#[test]
fn args_run_assert() {
let args: CliArgs = Parser::try_parse_from(["raven", "run", "Main"]).unwrap();
let assertions: bool = match args.command {
Command::Run { assertions, .. } => assertions.assertions,
_ => unreachable!(),
};
assert!(assertions == true)
}
#[test]
fn args_run_no_assert() {
let args: CliArgs =
Parser::try_parse_from(["raven", "run", "--no-assert", "Main"]).unwrap();
let assertions: bool = match args.command {
Command::Run { assertions, .. } => assertions.assertions,
_ => unreachable!(),
};
assert!(assertions == false)
}
}

View File

@@ -1,28 +0,0 @@
[package]
name = "core"
version = "0.1.4"
edition.workspace = true
license.workspace = true
description = "Raven's core, including metadata tooling and resources"
repository.workspace = true
publish.workspace = true
[dependencies]
derive_more.workspace = true
fs.workspace = true
java.workspace = true
pathsub.workspace = true
semver.workspace = true
serde.workspace = true
sha256.workspace = true
subprocess.workspace = true
toml.workspace = true
[dependencies.anyhow]
workspace = true
optional = true
[features]
into_anyhow = ["dep:anyhow"]

View File

@@ -1,49 +0,0 @@
use std::path::{Path, PathBuf};
use java::{JAVA_EXT_CLASS, JAVA_EXT_SOURCE};
use serde::{Deserialize, Serialize};
use crate::{Error, Result};
/// Data struct
#[derive(Debug, Clone, Deserialize, Serialize, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Class {
/// Path relative to PACKAGE/java, without file extension.
pub path: PathBuf,
pub checksum: String,
}
impl Class {
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() {
file_path = pathsub::sub_paths(file_path.as_path(), package_src_root.as_ref())
.ok_or(Error::MismatchedPackage)?;
}
Ok(Self {
path: dbg!(file_path.with_extension("")),
checksum: sha256::try_digest(package_src_root.as_ref().join(file_path))?,
})
}
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
.as_ref()
.join(self.path.as_path())
.with_extension(JAVA_EXT_CLASS)
.exists()
&& self.checksum == sha256::try_digest(self.path.clone())?)
}
pub fn update<P: AsRef<Path>>(&mut self, package_src_root: P) -> Result<()> {
self.checksum = sha256::try_digest(dbg!(
package_src_root
.as_ref()
.join(self.path.as_path())
.with_extension(JAVA_EXT_SOURCE),
))?;
Ok(())
}
}

View File

@@ -1,51 +0,0 @@
use semver::Version;
use serde::{Deserialize, Serialize};
use crate::Result;
use crate::package::PackageHandler;
/// Data struct
#[derive(Debug, Clone, Deserialize, Serialize, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Dependency {
name: String,
#[serde(skip_serializing_if = "<Option<_>>::is_none")]
version: Option<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();
}
pub fn is_updated(&self) -> Result<bool> {
// If the path is local and that file has not been updated.
Ok(self.source.as_ref().is_some_and(|path| is_url(path))
&& self.checksum == sha256::try_digest(self.source.as_ref().unwrap())?)
}
}
impl From<PackageHandler> for Dependency {
fn from(value: PackageHandler) -> Self {
Dependency {
name: value.name(),
version: Some(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;
}

View File

@@ -1,30 +0,0 @@
use derive_more::{Display, From};
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, From, Display)]
pub enum Error {
/// Attempted to replace a value in a hash collection,
/// but there was no prime present when one was expected.
AbsentPrimeHashingError,
#[from]
Io(std::io::Error),
#[from]
Java(java::Error),
MissingFileName,
MismatchedPackage,
#[from]
TomlDeserialize(toml::de::Error),
#[from]
TomlSerialize(toml::ser::Error),
UndefinedEntryPoint,
UnknownPackage,
}

View File

@@ -1,9 +0,0 @@
pub mod class;
pub mod dependency;
pub mod error;
pub mod nest;
pub mod package;
pub mod prelude;
pub mod workspace;
pub use error::{Error, Result};

View File

@@ -1,166 +0,0 @@
use std::collections::{BTreeMap, HashSet};
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use serde::{Deserialize, Serialize};
use fs::expand_files;
use crate::class::Class;
use crate::dependency::Dependency;
use crate::package::Package;
//use crate::workspace::Workspace;
pub const F_NEST_TOML: &str = "Nest.toml";
pub const F_NEST_LOCK: &str = "Nest.lock";
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetailedDependency {
pub version: Option<String>,
pub path: Option<String>,
pub git: Option<String>,
pub optional: Option<bool>,
pub features: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum DependencySpec {
Simple(String), // serde = "1.0"
Detailed(DetailedDependency), // serde = { version = "...", ... }
}
/// Data struct
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct Nest {
package: Package,
dependencies: Option<BTreeMap<String, DependencySpec>>,
}
impl Nest {
pub fn new<S: ToString>(name: S) -> Self {
Self {
package: Package::new(name),
dependencies: Default::default(),
}
}
pub fn write<P: AsRef<Path>>(&self, project_root: P) -> crate::Result<()> {
let mut project_root = project_root.as_ref().to_path_buf();
if project_root.is_dir() {
project_root = project_root.join(F_NEST_TOML);
}
Ok(OpenOptions::new()
.write(true)
.create(true)
.open(project_root)?
.write_all(toml::to_string_pretty(&self)?.as_bytes())?)
}
pub fn with_entry_point<P: AsRef<Path>>(&mut self, entry_point: P) -> &mut Self {
self.package.entry_point = Some(entry_point.as_ref().to_path_buf());
self
}
pub fn entry_point(&self) -> Option<PathBuf> {
self.package.entry_point.clone()
}
pub fn name(&self) -> String {
self.package.name.clone()
}
pub fn version(&self) -> semver::Version {
self.package.version.clone()
}
}
impl TryFrom<PathBuf> for Nest {
type Error = crate::Error;
fn try_from(value: PathBuf) -> Result<Self, Self::Error> {
println!("{}", value.to_string_lossy());
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 {
classes: HashSet<Class>,
pub dependencies: Vec<Dependency>,
}
impl NestLock {
pub fn new<P: AsRef<Path>>(package_root: P, package_src_root: P) -> crate::Result<Self> {
Ok(Self::from_paths(
expand_files(package_root)?,
package_src_root,
))
}
pub fn write<P: AsRef<Path>>(&self, path: P) -> crate::Result<()> {
let mut path = path.as_ref().to_path_buf();
if path.is_dir() {
path = path.join(F_NEST_LOCK);
}
Ok(OpenOptions::new()
.write(true)
.create(true)
.open(path)?
.write_all(toml::to_string_pretty(&self)?.as_bytes())?)
}
pub fn from_paths<P: AsRef<Path>>(paths: Vec<PathBuf>, package_src_root: P) -> Self {
let mut lock = Self::default();
lock.classes = paths
.iter()
.filter_map(|f| {
let dep = Class::new(package_src_root.as_ref().to_path_buf(), f.to_owned());
if dep.is_ok() {
Some(dep.unwrap())
} else {
None
}
})
.collect();
lock
}
pub fn with_class(&mut self, class: Class) -> &mut Self {
self.classes.insert(class);
self
}
pub fn classes(&mut self) -> &mut HashSet<Class> {
&mut self.classes
}
}
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

@@ -1,115 +0,0 @@
use std::hash::Hash;
use std::path::{Path, PathBuf};
use semver::Version;
use serde::{Deserialize, Serialize};
use crate::class::Class;
use crate::nest::{F_NEST_LOCK, Nest, NestLock};
pub const DIR_JAVA: &str = "java/";
/// Hashing is only based off the Prey.
#[derive(Debug, Clone)]
pub struct PackageHandler {
prey: Nest,
prey_lock: NestLock,
/// Path relative to WORKSPACE/src
package_root: PathBuf,
target_dir: PathBuf,
}
impl PackageHandler {
pub fn new<P: AsRef<Path>>(src_dir: P, package_root: P, target_dir: P) -> crate::Result<Self> {
let package_root = src_dir.as_ref().join(package_root.as_ref());
let prey_lock = if let Ok(prey_lock) = NestLock::try_from(package_root.join(F_NEST_LOCK)) {
prey_lock
} else {
NestLock::new(package_root.clone(), package_root.join(DIR_JAVA))?
};
Ok(Self {
prey: Nest::try_from(package_root.join(F_NEST_LOCK))?,
prey_lock,
package_root,
target_dir: target_dir.as_ref().to_path_buf(),
})
}
pub fn entry_point(&self) -> Option<PathBuf> {
self.prey.entry_point()
}
pub fn name(&self) -> String {
self.prey.name()
}
pub fn version(&self) -> semver::Version {
self.prey.version()
}
pub fn prey_lock(&mut self) -> &mut NestLock {
&mut self.prey_lock
}
pub fn get_outdated<P: AsRef<Path>>(&self, class_path: P) -> Vec<Class> {
self.prey_lock
.clone()
.classes()
.iter()
.filter(|class| {
class
.is_updated(class_path.as_ref())
.is_ok_and(|b| b == false)
})
.cloned()
.collect()
}
pub fn write_lock(&self) -> crate::Result<()> {
self.prey_lock.write(self.package_root.as_path())
}
}
// 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 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>,
pub entry_point: Option<PathBuf>,
}
impl Package {
pub fn new<S: ToString>(name: S) -> Self {
let mut package = Self::default();
package.name = name.to_string();
package
}
}
impl Default for Package {
fn default() -> Self {
Package {
name: String::from("Main"),
version: Version::new(0, 1, 0),
authors: None,
repository: None,
license: None,
license_file: None,
entry_point: None,
}
}
}

View File

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

View File

@@ -1,387 +0,0 @@
use std::collections::HashMap;
use std::fs::{OpenOptions, read_dir};
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::time::Duration;
use fs::expand_files;
use java::{JAVA_EXT_CLASS, JAVA_EXT_SOURCE};
use serde::{Deserialize, Serialize};
use subprocess::Exec;
use crate::nest::{F_NEST_LOCK, F_NEST_TOML, Nest, NestLock};
use crate::package::PackageHandler;
use crate::{Error, package};
#[derive(Debug)]
pub struct WorkspaceHandler {
nest: Nest,
nest_lock: Option<NestLock>,
project_root: PathBuf,
packages: HashMap<PathBuf, PackageHandler>,
}
impl WorkspaceHandler {
const DIR_SRC: &'static str = "src/";
const DIR_TARGET: &'static 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()?;
println!("{}", project_root.display());
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_locks(&self) -> crate::Result<()> {
if let Option::Some(lock) = self.nest_lock.clone() {
lock.write(self.project_root.join(F_NEST_LOCK))?;
}
for handler in self.packages.values() {
handler.write_lock()?;
}
Ok(())
}
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);
// Make config file.
self.write_nest()?;
// Make .java-version
self.write_java_version()?;
if is_empty {
self.write_example_project()?;
self.discover_packages()?;
Exec::cmd("git")
.arg("init")
.arg(".")
.start()?
.wait_timeout(Duration::from_secs(10))?;
}
// 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(self)
}
// 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)
.destination(Self::DIR_TARGET)
.build();
// cyclic dependencies across packages are not supported.
// construct a hashmap of parent child relationships between all files across all packages.
for handler in self.packages.values_mut() {
let package_src_root = self
.project_root
.join(Self::DIR_SRC)
.join(handler.name())
.join(package::DIR_JAVA);
let targets = handler.get_outdated(self.project_root.join(Self::DIR_TARGET));
for mut target in targets {
dbg!(&target);
if let Ok(true) = compiler
.clone()
.compile(vec![package_src_root.join(target.path.as_path())])
{
target.update(package_src_root.as_path())?;
handler
.prey_lock()
.classes()
.replace(target)
.ok_or(Error::AbsentPrimeHashingError)?;
}
}
}
self.write_locks()?;
Ok(self)
}
pub fn run<P: AsRef<Path>>(
&mut self,
entry_point: Option<P>,
assertions: bool,
) -> crate::Result<&mut Self> {
let mut entry_point = entry_point.expect("No entry point found").as_ref().to_path_buf();
while entry_point.is_file() {
entry_point = self
.packages
.get(&entry_point)
.ok_or(Error::UnknownPackage)?
.entry_point()
.expect("No entry point found");
}
// let mut entry_point = if entry_point.is_none() {
// self.nest.entry_point()
// } else {
// //entry_point.unwrap().as_ref().to_path_buf()
// };
//
// if !entry_point.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.
// entry_point = self
// .packages
// .get(&entry_point)
// .ok_or(Error::UnknownPackage)?
// .entry_point();
// }
//
if entry_point.file_name().is_none() {
return Err(Error::UndefinedEntryPoint);
}
//
java::runtime::JVMBuilder::new(Self::DIR_TARGET)
.assertions(assertions)
.monitor(true)
.build()
.run(entry_point)?;
Ok(self)
}
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)) {
if err.kind() != std::io::ErrorKind::NotFound {
return Err(Error::from(err));
}
}
// Clear src/**/Prey.lock
for handler in self.packages.values() {
if let Err(err) = std::fs::remove_file(
self.project_root
.join(Self::DIR_SRC)
.join(handler.name())
.join(F_NEST_LOCK),
) {
if err.kind() != std::io::ErrorKind::NotFound {
return Err(Error::from(err));
}
}
}
// Clear target/
let _ = std::fs::remove_dir_all(Self::DIR_TARGET);
Ok(self)
}
/// Add any newly created packages.
/// Prey.toml files designate packages.
/// discover_packages() iterates the filesystem recursively to discover all such packages.
/// discovered packages are stored as a hashmap<PathBuf, PackageHandler> in the WorkspaceHandler.
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. CON-FUCKING-FIRMED THIS SHIT IS NOT DEBUGGABLE
for file in read_dir(self.project_root.join(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_NEST_TOML).exists() {
vec![dir.join(F_NEST_TOML)]
} else {
expand_files(dir)
.ok()?
.iter()
.filter_map(|file| {
if file.ends_with(PathBuf::from(F_NEST_TOML)) {
Some(file.to_owned())
} else {
None
}
})
.collect()
})
})
.flatten()
{
let package_root = pathsub::sub_paths(
file.as_path(),
self.project_root.join(Self::DIR_SRC).as_path(),
)
.unwrap()
.parent()
.unwrap()
.to_path_buf();
self.packages.insert(
package_root.to_path_buf(),
PackageHandler::new(
self.project_root.join(Self::DIR_SRC),
package_root,
self.project_root.join(Self::DIR_TARGET),
)?,
);
}
Ok(())
}
fn write_nest(&self) -> crate::Result<()> {
Ok(self.nest.write(self.project_root.clone())?)
}
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_version()?.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) -> crate::Result<()> {
let main: PathBuf = PathBuf::from(Self::DIR_SRC).join("main/");
let test: PathBuf = PathBuf::from(Self::DIR_SRC).join("test/");
// Make src/, target/, test/
self.write_dir_tree()?;
// Make src/main/Prey.toml
if let Result::Ok(mut f) = OpenOptions::new()
.write(true)
.create_new(true)
.open(main.join(F_NEST_TOML))
{
f.write_all(
toml::to_string_pretty(&Nest::new("main").with_entry_point("Main"))?.as_bytes(),
)?;
}
// Make src/main/Main.java
if let Result::Ok(mut f) = OpenOptions::new()
.write(true)
.create_new(true)
.open(main.join("java/Main").with_extension(JAVA_EXT_SOURCE))
{
f.write_all(include_bytes!("../assets/Main.java"))?;
}
// Make src/test/Prey.toml
if let Result::Ok(mut f) = OpenOptions::new()
.write(true)
.create_new(true)
.open(test.join(F_NEST_TOML))
{
f.write_all(
toml::to_string_pretty(&Nest::new("test").with_entry_point("MainTest"))?.as_bytes(),
)?;
}
// Make src/test/MainTest.java
if let Result::Ok(mut f) = OpenOptions::new()
.write(true)
.create_new(true)
.open(test.join("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"),
}
}
}

View File

@@ -1,13 +0,0 @@
[package]
name = "fs"
version = "0.1.0"
edition.workspace = true
license.workspace = true
description = "Raven's FS utilities"
repository.workspace = true
publish.workspace = true
[dependencies]
derive_more.workspace = true

View File

@@ -1,20 +0,0 @@
# May want to find a better name, more reflective of the JDK part
# than the entire Java language.
[package]
name = "java"
version = "0.2.2"
edition.workspace = true
license.workspace = true
description = "Tools for interfacing with the Java Development Kit"
repository.workspace = true
publish.workspace = true
[dependencies]
bytesize.workspace = true
derive_more.workspace = true
fs.workspace = true
semver.workspace = true
subprocess.workspace = true

View File

@@ -1,96 +0,0 @@
use std::fmt::Display;
use std::path::{Path, PathBuf};
use subprocess::Exec;
use subprocess::Redirection;
use crate::JAVA_BIN_COMPILER;
use crate::JAVA_EXT_SOURCE;
use crate::Result;
#[derive(Debug, Default, Clone)]
pub struct CompilerBuilder {
class_path: Option<PathBuf>,
destination: Option<PathBuf>,
}
impl CompilerBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn class_path<S: AsRef<Path>>(&mut self, class_path: S) -> &mut Self {
self.class_path = Some(class_path.as_ref().to_path_buf());
self
}
pub fn destination<S: AsRef<Path>>(&mut self, destination: S) -> &mut Self {
self.destination = Some(destination.as_ref().to_path_buf());
self
}
pub fn build(&self) -> Compiler {
let mut flags = vec![];
if let Option::Some(path) = self.destination.to_owned() {
flags.push(CompilerFlag::Destination { path });
}
if let Option::Some(path) = self.class_path.to_owned() {
flags.push(CompilerFlag::Classpath { path });
}
Compiler { flags }
}
}
#[derive(Debug, Clone)]
pub struct Compiler {
flags: Vec<CompilerFlag>,
}
impl Compiler {
pub fn compile<P: AsRef<Path>>(self, paths: Vec<P>) -> Result<bool> {
Ok(Exec::cmd(JAVA_BIN_COMPILER)
.args(
self.flags
.clone()
.into_iter()
.flat_map(|f| Into::<Vec<String>>::into(f)),
)
.args(paths.into_iter().map(|p| p.as_ref().with_extension(JAVA_EXT_SOURCE)))
.stdout(Redirection::Pipe)
.detached()
.start()?
.wait()?
.success())
}
}
#[derive(Debug, Clone)]
pub enum CompilerFlag {
Classpath { path: PathBuf },
Destination { path: PathBuf },
Version,
}
impl Into<Vec<String>> for CompilerFlag {
fn into(self) -> Vec<String> {
match self {
Self::Classpath { path } => vec!["-classpath".to_string(), path.display().to_string()],
Self::Destination { path } => vec!["-d".to_string(), path.display().to_string()],
Self::Version => vec!["--version".to_string()],
}
}
}
// TODO: Clean this up a bit?
impl Display for CompilerFlag {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Classpath { path } => write!(f, "-classpath {}", path.display()),
Self::Destination { path } => write!(f, "-d {}", path.display()),
Self::Version => write!(f, "--version"),
}
}
}

View File

@@ -1,16 +0,0 @@
use derive_more::{Display, From};
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, From, Display)]
pub enum Error {
EmptyStdout,
#[from]
Io(std::io::Error),
NthOutOfBounds,
#[from]
Semver(semver::Error),
}

View File

@@ -1,56 +0,0 @@
pub mod compiler;
pub mod error;
pub mod runtime;
mod compilation_scheduler;
use std::str::FromStr;
pub use error::{Error, Result};
use runtime::VMFlag;
use subprocess::Exec;
use crate::compiler::CompilerFlag;
pub const JAVA_BIN_VM: &str = "java";
pub const JAVA_BIN_COMPILER: &str = "javac";
pub const JAVA_EXT_SOURCE: &str = "java";
pub const JAVA_EXT_CLASS: &str = "class";
pub const F_JAVA_VERSION: &str = ".java-version";
/// Uses the javac binary to get information about the install compiler.
pub fn get_javac_version() -> Result<semver::Version> {
// Try to pull from javac first.
if let Ok(version) = semver::Version::from_str(
Exec::cmd(JAVA_BIN_COMPILER)
.arg(CompilerFlag::Version.to_string())
.capture()?
.stdout_str()
.replace("javac", "")
.trim(),
) {
return Ok(version);
}
Ok(semver::Version::from_str(
get_java_version_info()?
.split_ascii_whitespace()
.nth(1)
.ok_or(Error::NthOutOfBounds)?,
)?)
}
/// Calls the java binary, returning the complete stdout version information.
fn get_java_version_info() -> Result<String> {
/*
* $ java --version
* openjdk 21.0.9 2025-10-21
* OpenJDK Runtime Environment (build 21.0.9+10)
* OpenJDK 64-Bit Server VM (build 21.0.9+10, mixed mode, sharing)
*/
Ok(Exec::cmd(JAVA_BIN_VM)
.arg(VMFlag::Version.to_string())
.capture()?
.stdout_str())
}

View File

@@ -1,141 +0,0 @@
use std::fmt;
use std::path::{Path, PathBuf};
use crate::JAVA_BIN_VM;
use crate::Result;
use bytesize::ByteSize;
use subprocess::Exec;
#[derive(Debug, Default, Clone)]
pub struct JVMBuilder {
assertions: bool,
monitor: bool,
ram_min: Option<ByteSize>,
ram_max: Option<ByteSize>,
class_path: PathBuf,
}
impl JVMBuilder {
pub fn new<P: AsRef<Path>>(class_path: P) -> Self {
Self {
class_path: class_path.as_ref().to_path_buf(),
..Default::default()
}
}
pub fn assertions(&mut self, assertions: bool) -> &mut Self {
self.assertions = assertions;
self
}
pub fn ram_min(&mut self, ram_min: ByteSize) -> &mut Self {
self.ram_min = Some(ram_min);
self
}
pub fn ram_max(&mut self, ram_max: ByteSize) -> &mut Self {
self.ram_max = Some(ram_max);
self
}
/// Monitor stdout and stderr in raven's process.
pub fn monitor(&mut self, monitor: bool) -> &mut Self {
self.monitor = monitor;
self
}
pub fn build(&self) -> JVM {
let mut flags = vec![VMFlag::Classpath {
path: self.class_path.to_owned(),
}];
if self.assertions {
flags.push(VMFlag::EnableAssert);
}
if let Option::Some(size) = self.ram_min {
flags.push(VMFlag::HeapMin { size });
}
if let Option::Some(size) = self.ram_max {
flags.push(VMFlag::HeapMax { size });
}
JVM {
monitor: self.monitor,
flags,
}
}
}
pub struct JVM {
monitor: bool,
flags: Vec<VMFlag>,
}
impl JVM {
pub fn run<P: AsRef<Path>>(self, entry_point: P) -> Result<()> {
let capture = Exec::cmd(JAVA_BIN_VM)
.args(
self.flags
.clone()
.into_iter()
.flat_map(|f| Into::<Vec<String>>::into(f)),
)
.arg(entry_point.as_ref())
.capture()?;
if !self.monitor {
return Ok(());
}
let stdout = capture.stdout_str();
if stdout.len() > 0 {
println!("{stdout}");
}
let stderr = capture.stderr_str();
if stderr.len() > 0 {
eprintln!("{stderr}");
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub enum VMFlag {
Classpath { path: PathBuf },
EnableAssert,
HeapMax { size: ByteSize },
HeapMin { size: ByteSize },
Version,
}
impl Into<Vec<String>> for VMFlag {
fn into(self) -> Vec<String> {
self.to_string()
.split_ascii_whitespace()
.map(|s| s.to_string())
.collect()
}
}
// Currently being kept around because it's fine for the current branches,
// and is currently serving a to_string() method for the Java version fetch.
impl fmt::Display for VMFlag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"-{}",
match self {
Self::Classpath { path } => format!("classpath {}", path.display()),
Self::EnableAssert => String::from("ea"),
Self::HeapMax { size } => format!("Xmx{}", size.as_u64()),
Self::HeapMin { size } => format!("Xms{}", size.as_u64()),
Self::Version => String::from("-version"), // --version
}
)
}
}

View File

@@ -1,17 +0,0 @@
[package]
name = "pom"
version = "0.1.0"
edition.workspace = true
license.workspace = true
description = "Library for serializing and deserializing Maven's POM"
repository.workspace = true
publish.workspace = true
[dependencies]
derive_more.workspace = true
hard-xml.workspace = true
serde.workspace = true
semver.workspace = true
strum.workspace = true

View File

@@ -1,12 +0,0 @@
use derive_more::{Display, From};
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, From, Display)]
pub enum Error {
#[from]
Io(std::io::Error),
#[from]
Xml(hard_xml::XmlError),
}

View File

@@ -1,25 +0,0 @@
pub mod error;
pub mod xml;
pub use error::{Error, Result};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct Pom {
model_version: semver::Version,
}
impl Pom {
pub fn is_empty(&self) -> bool {
self == &Self::default()
}
}
impl Default for Pom {
fn default() -> Self {
Self {
model_version: semver::Version::new(4, 0, 0),
}
}
}

View File

@@ -1,118 +0,0 @@
use std::{fs::File, io::Read};
use crate::Error;
use hard_xml::{XmlRead, XmlWrite};
use serde::{Deserialize, Serialize};
use strum::EnumString;
// start with the super POM idiot.
#[derive(Debug, Clone, Deserialize, Serialize, XmlRead, XmlWrite)]
#[xml(tag = "project")]
pub struct Project {
#[xml(attr = "xmlns")]
xmlns: String,
#[xml(attr = "xlmns:xsi")]
xmlns_xsi: String,
#[xml(attr = "xsi:schemaLocation")]
xsi_schema_location: String,
#[xml(flatten_text = "modelVersion")]
model_version: semver::Version,
#[xml(flatten_text = "groupId")]
group_id: String,
#[xml(flatten_text = "artifactId")]
artifact_id: String,
#[xml(flatten_text = "version")]
version: String,
#[xml(flatten_text = "packaging")]
packaging: String,
#[xml(child = "properties")]
properties: Properties,
#[xml(child = "dependencyManagement")]
dependency_management: DependencyManagement,
#[xml(child = "dependencies")]
dependencies: Vec<Dependency>,
}
#[derive(Debug, Clone, Deserialize, Serialize, XmlRead, XmlWrite)]
#[xml(tag = "properties")]
pub struct Properties {
#[xml(flatten_text = "maven.compiler.source")]
source: String,
#[xml(flatten_text = "maven.compiler.target")]
target: String,
// lwjgl.version
// lwjgl.natives
}
#[derive(Debug, Clone, Deserialize, Serialize, XmlRead, XmlWrite)]
#[xml(tag = "dependencyManagement")]
pub struct DependencyManagement {
#[xml(child = "dependencies")]
dependencies: Vec<Dependency>,
}
#[derive(Debug, Clone, Deserialize, Serialize, XmlRead, XmlWrite)]
#[xml(tag = "dependency")]
pub struct Dependency {
#[xml(flatten_text = "groupId")]
group_id: String,
#[xml(flatten_text = "artifactId")]
artifact_id: String,
#[xml(flatten_text = "version")]
version: semver::Version,
#[xml(flatten_text = "scope")]
scope: String,
#[xml(flatten_text = "type")]
type_: String,
#[xml(flatten_text = "optional")]
optional: bool,
#[xml(child = "exclusions")]
exclusions: Vec<Exclusion>,
}
#[derive(Debug, Clone, Deserialize, Serialize, XmlRead, XmlWrite)]
#[xml(tag = "exclusion")]
pub struct Exclusion {
#[xml(flatten_text = "groupId")]
group_id: String,
#[xml(flatten_text = "artifactId")]
artifact_id: String,
}
#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, EnumString)]
#[strum(serialize_all = "camelCase")]
pub enum Packaging {
#[default]
Pom,
Jar,
}
impl TryFrom<File> for Project {
type Error = Error;
fn try_from(value: File) -> Result<Self, Self::Error> {
let mut value = value;
let mut buf = String::new();
value.read_to_string(&mut buf)?;
Ok(Project::from_str(&buf)?)
}
}

671
crates/raven/Cargo.lock generated
View File

@@ -1,671 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "anstream"
version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
[[package]]
name = "anstyle-parse"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys",
]
[[package]]
name = "anyhow"
version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]]
name = "async-trait"
version = "0.1.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "bitflags"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
dependencies = [
"serde_core",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "bytes"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
[[package]]
name = "bytesize"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3"
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "clap"
version = "4.5.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.5.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "clap_lex"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32"
[[package]]
name = "colorchoice"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "const_format"
version = "0.2.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad"
dependencies = [
"const_format_proc_macros",
]
[[package]]
name = "const_format_proc_macros"
version = "0.2.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "hard-xml"
version = "1.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b07b8ba970e18a03dbb79f6786b6e4d6f198a0ac839aa5182017001bb8dee17"
dependencies = [
"hard-xml-derive",
"jetscii",
"lazy_static",
"memchr",
"xmlparser",
]
[[package]]
name = "hard-xml-derive"
version = "1.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0c43e7c3212bd992c11b6b9796563388170950521ae8487f5cdf6f6e792f1c8"
dependencies = [
"bitflags",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "indexmap"
version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]]
name = "jetscii"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47f142fe24a9c9944451e8349de0a56af5f3e7226dc46f3ed4d4ecc0b85af75e"
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "lenient_semver"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de8de3f4f3754c280ce1c8c42ed8dd26a9c8385c2e5ad4ec5a77e774cea9c1ec"
dependencies = [
"lenient_semver_parser",
"semver",
]
[[package]]
name = "lenient_semver_parser"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f650c1d024ddc26b4bb79c3076b30030f2cf2b18292af698c81f7337a64d7d6"
dependencies = [
"lenient_semver_version_builder",
"semver",
]
[[package]]
name = "lenient_semver_version_builder"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9049f8ff49f75b946f95557148e70230499c8a642bf2d6528246afc7d0282d17"
dependencies = [
"semver",
]
[[package]]
name = "libc"
version = "0.2.180"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
[[package]]
name = "memchr"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "once_cell_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "pathsub"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dadd38133bcbe43264410412c48614bab4ef899f0792ffc4530dc19ec000a970"
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a"
dependencies = [
"proc-macro2",
]
[[package]]
name = "raven"
version = "0.1.0"
dependencies = [
"anyhow",
"bytesize",
"clap",
"const_format",
"hard-xml",
"lenient_semver",
"pathsub",
"ron",
"semver",
"serde",
"sha256",
"strum",
"subprocess",
"toml",
]
[[package]]
name = "ron"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd490c5b18261893f14449cbd28cb9c0b637aebf161cd77900bfdedaff21ec32"
dependencies = [
"bitflags",
"once_cell",
"serde",
"serde_derive",
"typeid",
"unicode-ident",
]
[[package]]
name = "semver"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
dependencies = [
"serde",
"serde_core",
]
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "serde_spanned"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776"
dependencies = [
"serde_core",
]
[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "sha256"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f880fc8562bdeb709793f00eb42a2ad0e672c4f883bbe59122b926eca935c8f6"
dependencies = [
"async-trait",
"bytes",
"hex",
"sha2",
"tokio",
]
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "strum"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.114",
]
[[package]]
name = "subprocess"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f75238edb5be30a9ea3035b945eb9c319dde80e879411cdc9a8978e1ac822960"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tokio"
version = "1.49.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86"
dependencies = [
"bytes",
"pin-project-lite",
]
[[package]]
name = "toml"
version = "0.9.11+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46"
dependencies = [
"indexmap",
"serde_core",
"serde_spanned",
"toml_datetime",
"toml_parser",
"toml_writer",
"winnow",
]
[[package]]
name = "toml_datetime"
version = "0.7.5+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_parser"
version = "1.0.6+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44"
dependencies = [
"winnow",
]
[[package]]
name = "toml_writer"
version = "1.0.6+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607"
[[package]]
name = "typeid"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
[[package]]
name = "typenum"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
[[package]]
name = "unicode-ident"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "winnow"
version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
[[package]]
name = "xmlparser"
version = "0.13.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4"

View File

@@ -1,21 +0,0 @@
[package]
name = "raven"
version = "0.2.0"
edition.workspace = true
license.workspace = true
description = "A simple build tool for Java"
keywords = ["java", "tool"]
categories = ["development-tools::build-utils"]
repository.workspace = true
publish.workspace = true
[dependencies]
anyhow.workspace = true
bytesize.workspace = true
cli.workspace = true
core.workspace = true
fs.workspace = true
java.workspace = true
toml.workspace = true

View File

@@ -1,49 +0,0 @@
use core::prelude::WorkspaceHandler;
use anyhow::anyhow;
use cli::{CLI_ARGS, Command};
fn main() -> anyhow::Result<()> {
// type_ is yet unused.
if let Command::New { project_name, .. } = &CLI_ARGS.command {
new(project_name.to_owned())?;
}
let project_root = std::env::current_dir()?;
println!("{}", project_root.display());
let mut wh: WorkspaceHandler = match &CLI_ARGS.command {
Command::New { .. } | Command::Init => WorkspaceHandler::new(project_root),
_ => WorkspaceHandler::load(project_root),
}
.map_err(|err| anyhow!(err))?;
match &CLI_ARGS.command {
Command::New { .. } | Command::Init => wh.init(),
Command::Build => wh.build(),
Command::Run {
entry_point,
assertions,
} => wh
.build()
.map_err(|err| anyhow!(err))?
.run(entry_point.to_owned(), assertions.into()),
Command::Clean => wh.clean(),
Command::Test { .. } => unimplemented!(),
}
.map_err(|err| anyhow!(err))?;
Ok(())
}
fn new(project_name: String) -> anyhow::Result<()> {
let cwd = std::env::current_dir()?.join(project_name);
std::fs::create_dir(&cwd)?;
std::env::set_current_dir(&cwd)?;
Ok(())
}

61
src/cli.rs Normal file
View File

@@ -0,0 +1,61 @@
use std::sync::LazyLock;
use clap::{ArgAction, Parser, Subcommand};
pub static CONFIG: LazyLock<Args> = LazyLock::new(|| Args::parse());
#[derive(Debug, Parser)]
#[clap(author, version)]
#[command(help_template = "{author-section}\n{usage-heading} {usage}\n\n{all-args}")]
pub struct Args {
#[command(subcommand)]
pub command: Command,
}
#[derive(Debug, Subcommand)]
pub enum Command {
/// Create a new raven project
New { project_name: String },
/// Create a new raven project in an existing directory
Init,
/// Compile the current project
Build,
/// Run the current project
#[command(arg_required_else_help = true)]
Run {
#[arg(required = true)]
entry_point: String,
#[clap(flatten)]
assertions: Assertions,
},
/// !!! BORKED !!! Run the tests
Test {
#[clap(flatten)]
assertions: Assertions,
},
/// Remove the target directory and caching
Clean,
}
impl Command {
pub fn depends_on_nest(&self) -> bool {
match self {
Self::Init | Self::New { .. } => false,
_ => true,
}
}
}
#[derive(Debug, clap::Args)]
pub struct Assertions {
/// Disable assertions.
#[arg(short, long = "no-assert", action = ArgAction::SetFalse)]
disable_assert: bool,
}
impl Into<bool> for &Assertions {
fn into(self) -> bool {
!self.disable_assert
}
}

41
src/env.rs Normal file
View File

@@ -0,0 +1,41 @@
use std::io;
use std::path::Path;
use std::str::FromStr;
use std::sync::LazyLock;
use crate::java::{JAVA_VM, VMFlag};
use anyhow::Context;
static JAVA_VERSION: LazyLock<String> = LazyLock::new(|| {
crate::io::run_process(&[JAVA_VM, VMFlag::Version.to_string().as_str()])
.expect("Failed to call java")
.0
.expect("Failed to read java version from stdout")
});
pub fn get_javac_ver() -> anyhow::Result<semver::Version> {
/*
* $ java --version
* openjdk 21.0.9 2025-10-21
* OpenJDK Runtime Environment (build 21.0.9+10)
* OpenJDK 64-Bit Server VM (build 21.0.9+10, mixed mode, sharing)
*/
semver::Version::from_str(
JAVA_VERSION
.lines()
.nth(0)
.context("stdout from Java is all f-cked up")?
.split_ascii_whitespace()
.nth(1)
.context("Java version position is all f-cked up")?,
)
.context("Failed to produce a version struct from string")
}
pub fn set_cwd<P: AsRef<Path>>(path: P) -> io::Result<()> {
let mut cwd = crate::PROJECT_ROOT.clone();
cwd.push(path.as_ref());
std::env::set_current_dir(cwd.as_path())
}

5
crates/fs/src/lib.rs → src/fs.rs Executable file → Normal file
View File

@@ -1,9 +1,6 @@
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
pub const EXT_TOML: &str = ".toml"; pub fn expand_files<P: AsRef<Path>>(path: P) -> anyhow::Result<Vec<PathBuf>> {
pub const EXT_LOCK: &str = ".lock";
pub fn expand_files<P: AsRef<Path>>(path: P) -> std::io::Result<Vec<PathBuf>> {
let path = path.as_ref(); let path = path.as_ref();
if path.is_file() { if path.is_file() {

30
src/io.rs Normal file
View File

@@ -0,0 +1,30 @@
use std::ffi;
use anyhow::Context;
pub fn run_process<S>(argv: &[S]) -> anyhow::Result<(Option<String>, Option<String>)>
where
S: AsRef<ffi::OsStr>,
{
let mut process = subprocess::Popen::create(
argv,
subprocess::PopenConfig {
stdout: subprocess::Redirection::Pipe,
..Default::default()
},
)?;
let result = process
.communicate(None)
.context("Failed to communicate with subprocess");
if process
.wait_timeout(std::time::Duration::from_secs(5))
.is_err()
|| process.exit_status().is_none()
{
process.terminate()?;
}
result
}

227
src/java.rs Normal file
View File

@@ -0,0 +1,227 @@
use std::fmt;
use std::path::{Path, PathBuf};
use anyhow::Context;
use bytesize::ByteSize;
pub const JAVA_VM: &str = "java";
pub const JAVA_COMPILER: &str = "javac";
pub const JAVA_EXT_SOURCE: &str = "java";
pub const JAVA_EXT_CLASS: &str = "class";
// TODO: Builder-pattern JVM wrapper.
#[derive(Debug, Default, Clone)]
pub struct JVMBuilder {
assertions: bool,
monitor: bool,
ram_min: Option<ByteSize>,
ram_max: Option<ByteSize>,
}
impl JVMBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn assertions(&mut self, assertions: bool) -> &mut Self {
self.assertions = assertions;
self
}
pub fn ram_min(&mut self, ram_min: ByteSize) -> &mut Self {
self.ram_min = Some(ram_min);
self
}
pub fn ram_max(&mut self, ram_max: ByteSize) -> &mut Self {
self.ram_max = Some(ram_max);
self
}
/// Monitor stdout and stderr in raven's process.
pub fn monitor(&mut self, monitor: bool) -> &mut Self {
self.monitor = monitor;
self
}
pub fn build(&self) -> JVM {
let mut flags = vec![];
if self.assertions {
flags.push(VMFlag::EnableAssert);
}
if let Option::Some(size) = self.ram_min {
flags.push(VMFlag::HeapMin { size });
}
if let Option::Some(size) = self.ram_max {
flags.push(VMFlag::HeapMax { size });
}
JVM {
monitor: self.monitor,
flags,
}
}
}
pub struct JVM {
monitor: bool,
flags: Vec<VMFlag>,
}
impl JVM {
pub fn run<P: AsRef<Path>>(
self,
entry_point: P,
) -> anyhow::Result<(Option<String>, Option<String>)> {
let mut cmd = vec![JAVA_VM.to_string()];
cmd.extend(
self.flags
.clone()
.into_iter()
.flat_map(|f| Into::<Vec<String>>::into(f)),
);
cmd.push(entry_point.as_ref().display().to_string());
let result = crate::io::run_process(cmd.as_slice());
if self.monitor
&& let Result::Ok((stdout, stderr)) = &result
{
if let Option::Some(stdout) = stdout
&& stdout.len() > 0
{
print!("{stdout}");
}
if let Option::Some(stderr) = stderr
&& stderr.len() > 0
{
eprintln!("{stderr}");
}
}
result
}
}
#[derive(Debug, Clone)]
pub enum VMFlag {
EnableAssert,
HeapMax { size: ByteSize },
HeapMin { size: ByteSize },
Version,
}
impl Into<Vec<String>> for VMFlag {
fn into(self) -> Vec<String> {
match self {
Self::EnableAssert => vec![String::from("-ea")],
Self::HeapMax { size } => vec![format!("Xmx{}", size)],
Self::HeapMin { size } => vec![format!("Xms{}", size)],
Self::Version => vec![String::from("--version")],
}
}
}
// Currently being kept around because it's fine for the current branches,
// and is currently serving a to_string() method for the Java version fetch.
impl fmt::Display for VMFlag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"-{}",
match self {
Self::EnableAssert => String::from("ea"),
Self::HeapMax { size } => format!("Xmx{}", size),
Self::HeapMin { size } => format!("Xms{}", size),
Self::Version => String::from("-version"), // --version
}
)
}
}
#[derive(Debug, Default, Clone)]
pub struct CompilerBuilder {
class_path: Option<PathBuf>,
destination: Option<PathBuf>,
}
impl CompilerBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn class_path<S: AsRef<Path>>(&mut self, class_path: S) -> &mut Self {
self.class_path = Some(class_path.as_ref().to_path_buf());
self
}
pub fn destination<S: AsRef<Path>>(&mut self, destination: S) -> &mut Self {
self.destination = Some(destination.as_ref().to_path_buf());
self
}
pub fn build(&self) -> Compiler {
let mut flags = vec![];
if let Option::Some(path) = self.destination.to_owned() {
flags.push(CompilerFlag::Destination { path });
}
if let Option::Some(path) = self.class_path.to_owned() {
flags.push(CompilerFlag::Classpath { path });
}
Compiler { flags }
}
}
#[derive(Debug, Clone)]
pub struct Compiler {
flags: Vec<CompilerFlag>,
}
impl Compiler {
pub fn compile<P: AsRef<Path>>(
self,
path: P,
) -> anyhow::Result<(Option<String>, Option<String>)> {
let mut cmd: Vec<String> = vec![JAVA_COMPILER.to_string()];
cmd.extend(
self.flags
.clone()
.into_iter()
.flat_map(|f| Into::<Vec<String>>::into(f)),
);
cmd.extend(crate::fs::expand_files(path)?.into_iter().filter_map(|f| {
Some(
f.to_str()
.context("Failed to cast PathBuf to &str")
.ok()?
.to_string(),
)
}));
crate::io::run_process(cmd.as_slice())
}
}
#[derive(Debug, Clone)]
pub enum CompilerFlag {
Destination { path: PathBuf },
Classpath { path: PathBuf },
}
impl Into<Vec<String>> for CompilerFlag {
fn into(self) -> Vec<String> {
match self {
Self::Classpath { path } => vec!["-cp".to_string(), path.display().to_string()],
Self::Destination { path } => vec!["-d".to_string(), path.display().to_string()],
}
}
}

272
src/main.rs Normal file
View File

@@ -0,0 +1,272 @@
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;
use cli::{CONFIG, Command};
use java::{JAVA_EXT_CLASS, JAVA_EXT_SOURCE};
use nest::{Class, NEST, NestLock};
use anyhow::Context;
use bytesize::ByteSize;
pub static PROJECT_ROOT: LazyLock<PathBuf> = LazyLock::new(|| {
// Start from CWD
let cwd = std::env::current_dir().expect("Failed to get current working directory");
let mut probe = cwd.clone();
while !probe.join(F_NEST_TOML.as_path()).exists() {
if !probe.pop() {
// This is easier than having a Result everywhere. For now.
panic!(
"No {} found in current directory or any ancestor. (cwd: {})",
F_NEST_TOML.display(),
cwd.display()
)
}
}
probe
});
const DIR_TARGET: LazyLock<PathBuf> = LazyLock::new(|| PROJECT_ROOT.join("target/"));
const DIR_SRC: LazyLock<PathBuf> = LazyLock::new(|| PROJECT_ROOT.join("src/"));
const DIR_MAIN: LazyLock<PathBuf> = LazyLock::new(|| DIR_SRC.join("main/"));
const DIR_TEST: LazyLock<PathBuf> = LazyLock::new(|| DIR_SRC.join("test/"));
const F_NEST_TOML: LazyLock<PathBuf> = LazyLock::new(|| PathBuf::from("Nest.toml"));
const F_NEST_LOCK: LazyLock<PathBuf> = LazyLock::new(|| PathBuf::from("Nest.lock"));
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 cwd = std::env::current_dir()?;
let is_empty = std::fs::read_dir(cwd.as_path()).is_ok_and(|tree| tree.count() == 0);
let project_name = cwd
.file_name()
.context("Invalid directory name")?
.to_str()
.context("Unable to convert OsStr to str")?
.to_owned();
// ORDER MATTERS. THIS MUST COME FIRST.
// 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 src, target, tests
for dir in [DIR_SRC, DIR_MAIN, DIR_TARGET, DIR_TEST] {
std::fs::create_dir_all(dir.clone())?;
}
// Make src/main/Main.java
if let Result::Ok(mut f) = OpenOptions::new().write(true).create_new(is_empty).open(
DIR_MAIN
.clone()
.join("Main")
.with_extension(JAVA_EXT_SOURCE),
) {
f.write_all(include_bytes!("../assets/src/main/Main.java"))?;
}
// Make src/test/Main.java
if let Result::Ok(mut f) = OpenOptions::new().write(true).create_new(is_empty).open(
DIR_TEST
.clone()
.join("Main")
.with_extension(JAVA_EXT_SOURCE),
) {
f.write_all(include_bytes!("../assets/src/test/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")
{
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 !buf.contains(&ignored) {
f.write(format!("{}\n", ignored).as_bytes())?;
}
}
}
Ok(())
}
fn new(project_name: String) -> anyhow::Result<()> {
let cwd = std::env::current_dir()?.join(project_name);
std::fs::create_dir(&cwd)?;
std::env::set_current_dir(&cwd)?;
Ok(())
}
fn build() -> anyhow::Result<()> {
let mut targets: Vec<PathBuf> = crate::fs::expand_files(PROJECT_ROOT.clone().join(DIR_SRC.as_path()))?
.into_iter()
.filter(|f| {
f.extension()
.is_some_and(|ext| ext.to_str().is_some_and(|ext| ext == JAVA_EXT_SOURCE))
})
.collect();
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(dbg!(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>)> {
// JRE pathing will be messed up without this.
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(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.join(F_NEST_LOCK.as_path()));
let _ = std::fs::remove_dir_all(DIR_TARGET.join("/*").as_path());
}

163
src/nest.rs Normal file
View File

@@ -0,0 +1,163 @@
use std::collections::HashMap;
use std::fs::{File, OpenOptions};
use std::io::Read;
use std::path::PathBuf;
use std::sync::LazyLock;
use crate::java::{JAVA_EXT_CLASS, JAVA_EXT_SOURCE};
use crate::{DIR_SRC, DIR_TARGET, DIR_TEST, F_NEST_LOCK, F_NEST_TOML, PROJECT_ROOT};
use anyhow::Context;
use semver::Version;
use serde::{Deserialize, Serialize};
pub static NEST: LazyLock<anyhow::Result<Nest>> =
LazyLock::new(|| Nest::try_from(PROJECT_ROOT.join(F_NEST_TOML.as_path())));
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Nest {
pub package: Package,
}
impl TryFrom<File> for Nest {
type Error = anyhow::Error;
fn try_from(value: File) -> Result<Self, Self::Error> {
let mut value = value;
let mut buf = String::new();
value
.read_to_string(&mut buf)
.context("Failed to read Nest")?;
toml::from_str(buf.as_str()).context("Failed to deserialize Nest")
}
}
impl TryFrom<PathBuf> for Nest {
type Error = anyhow::Error;
fn try_from(value: PathBuf) -> Result<Self, Self::Error> {
Nest::try_from(
OpenOptions::new()
.read(true)
.open(value)
.expect("Failed to load Nest.toml"),
)
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Package {
pub name: String,
pub version: semver::Version,
}
impl Default for Package {
fn default() -> Self {
Self {
name: String::from("MyPackage"),
version: Version::new(0, 1, 0),
}
}
}
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct NestLock(pub HashMap<PathBuf, Class>);
impl NestLock {
pub fn load() -> anyhow::Result<NestLock> {
NestLock::try_from(F_NEST_LOCK.clone())
}
/// Update, retaining all classes that still exist and whose paths are still files, rather than
/// being shifted into a package.
pub fn update(&mut self) {
self.0 = self
.0
.clone()
.into_iter()
.filter_map(|(path, class)| {
if path.exists() && path.is_file() {
return Some((path, class));
}
None
})
.collect();
}
}
impl TryFrom<File> for NestLock {
type Error = anyhow::Error;
fn try_from(value: File) -> Result<Self, Self::Error> {
let mut value = value;
let mut buf = String::new();
value.read_to_string(&mut buf)?;
toml::from_str(buf.as_str()).context("Failed to deserialize NestLock")
}
}
impl TryFrom<PathBuf> for NestLock {
type Error = anyhow::Error;
fn try_from(value: PathBuf) -> Result<Self, Self::Error> {
NestLock::try_from(
OpenOptions::new()
.read(true)
.open(&value)
.with_context(|| format!("Failed to open {}", value.display()))?,
)
.context("")
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Class {
pub name: PathBuf,
pub checksum: String,
pub test: bool,
}
impl Class {
/// Returns true if the class needs updating.
/// This may also cautionarily return true if it cannot digest the file.
pub fn is_updated(&self) -> bool {
// If the source file exists, hasn't been moved to a package (path is a file) and
// the associated compiled class file exists in DIR_TARGET
PROJECT_ROOT
.join(DIR_TARGET.as_path())
.join(self.name.as_path())
.with_extension(JAVA_EXT_CLASS)
.exists()
&& sha256::try_digest(
PROJECT_ROOT
.join(if self.test {
DIR_TEST.clone()
} else {
DIR_SRC.clone()
})
.join(self.name.as_path())
.with_extension(JAVA_EXT_SOURCE),
)
.is_ok_and(|hash| self.checksum == hash)
}
}
impl TryFrom<PathBuf> for Class {
type Error = anyhow::Error;
fn try_from(value: PathBuf) -> Result<Self, Self::Error> {
Ok(Self {
name: PathBuf::from(
value
.file_name()
.context("Failed to get file name from PathBuf for class")?,
)
.with_extension(""),
checksum: sha256::try_digest(&value)?,
test: value.is_relative() && value.starts_with(DIR_TEST.as_path()),
})
}
}

View File

@@ -1,26 +0,0 @@
[workspace]
default_package = "main" # PathBuf || String ?
[meta]
name = "demo" # String
version = "0.1.0" # semver::Version
authors = ["Olivia Brooks", "Adrian Long"] # Option<Vec<String>>
repository = "https://gitea.cutieguwu.ca/Cutieguwu/raven" # Option<URL> struct?
license = "MIT" # Option<enum License>
license-file = "LICENSE"
[dependencies]
anyhow = "1.0" # semver::VersionReq
bytesize = "2.3"
pathsub = "0.1.1"
ron = "0.12"
sha256 = "1.6"
subprocess = "0.2"
toml = "0.9"
[dependencies.clap]
version = "4.5"
#features = ["cargo", "derive"] # Is this a concept in POM?
[pom] # Only POM-specific data
model_version = "4.0.0" # semver::Version

View File

@@ -1,7 +0,0 @@
[[classes]]
path = "Main" # PathBuf to .class
checksum = "24dffb40073ff21878cf879bf8c67d189ad600115f9a8ecead11a3ca6c086767"
[[classes]]
path = "Cli"
checksum = "24dffb40073ff21878cf879bf8c67d189ad600115f9a8ecead11a3ca6c086767"

View File

@@ -1,6 +0,0 @@
[package]
entry_point = "Main" # PathBuf
[meta]
name = "main"
version = "0.1.0"

17
templates/imports.rs Normal file
View File

@@ -0,0 +1,17 @@
// Acknowledge sister/child
mod module;
// std
use std::*;
// sister/child
use module1::*;
// parent
use super::*;
// ancestor of parent
use crate::*;
// external
use external::*;