Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c99c40d
refactor(core)!: add more details to errors
andrius-puksta-sensmetry Oct 21, 2025
cccbb0b
adapt bindings, fix warnings, use PathBuf instead of String where app…
andrius-puksta-sensmetry Oct 27, 2025
9002982
box large errors
andrius-puksta-sensmetry Oct 27, 2025
bad18ce
fix UTF-8 path conversion on Windows
andrius-puksta-sensmetry Oct 27, 2025
edbdcab
remove obsolete code
andrius-puksta-sensmetry Oct 27, 2025
92b987e
remove unused enum variant
andrius-puksta-sensmetry Oct 27, 2025
ddc8ba0
more details for MemoryReadError
andrius-puksta-sensmetry Oct 27, 2025
e516c7a
clarify
andrius-puksta-sensmetry Oct 28, 2025
9267448
optimize; usually PathBuf is used for paths in errors, but in this ca…
andrius-puksta-sensmetry Oct 28, 2025
ca62813
simplify
andrius-puksta-sensmetry Oct 28, 2025
d0e2ee3
better lexer errors
andrius-puksta-sensmetry Oct 28, 2025
9cef60e
improve consistency
andrius-puksta-sensmetry Oct 28, 2025
122eb77
cleanup
andrius-puksta-sensmetry Oct 28, 2025
5769fe4
improve wording, optimize tests
andrius-puksta-sensmetry Oct 28, 2025
e1cb589
improve wording
andrius-puksta-sensmetry Oct 28, 2025
78f18c2
improve wording
andrius-puksta-sensmetry Oct 28, 2025
433629f
indicate line and byte position on lexing failure
andrius-puksta-sensmetry Oct 28, 2025
2c9af5a
improve parser/lexer errors
andrius-puksta-sensmetry Oct 29, 2025
4c1ef6b
fix lints
andrius-puksta-sensmetry Oct 29, 2025
ff904c4
bump pyo3 to 0.27
andrius-puksta-sensmetry Oct 29, 2025
3fe8bce
add details to remaining errors
andrius-puksta-sensmetry Oct 31, 2025
cac7130
update dependencies
andrius-puksta-sensmetry Oct 31, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
638 changes: 284 additions & 354 deletions Cargo.lock

Large diffs are not rendered by default.

18 changes: 17 additions & 1 deletion bindings/java/java-test/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@ SPDX-License-Identifier: MIT OR Apache-2.0
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<profiles>
<!-- maven.compiler.release is only available in Java 9+, but it is required
to disallow usage of Java >8 APIs when building with Java >8 -->
<profile>
<id>java-8-api</id>
<activation>
<jdk>[9,)</jdk>
</activation>
<properties>
<maven.compiler.release>8</maven.compiler.release>
</properties>
</profile>
</profiles>
<repositories>
<repository>
<!-- local repository (autoupdated) -->
Expand Down Expand Up @@ -61,7 +74,10 @@ SPDX-License-Identifier: MIT OR Apache-2.0
<!-- test runner -->
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.4</version>
<configuration>
<argLine>--enable-native-access=ALL-UNNAMED</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>
</project>
19 changes: 12 additions & 7 deletions bindings/java/java-test/src/test/java/org/sysand/BasicTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import static org.junit.jupiter.api.Assertions.*;

import java.util.regex.Pattern;
import java.nio.file.Files;

public class BasicTest {

Expand All @@ -21,13 +22,16 @@ public void testBasicInit() {
org.sysand.Sysand.init("test", "1.0.0", tempDir);

// Add basic assertions to verify project creation
assertTrue(java.nio.file.Files.exists(tempDir.resolve(".project.json")), "Project file should exist");
assertTrue(java.nio.file.Files.exists(tempDir.resolve(".meta.json")), "Metadata file should exist");
assertTrue(Files.exists(tempDir.resolve(".project.json")), "Project file should exist");
assertTrue(Files.exists(tempDir.resolve(".meta.json")), "Metadata file should exist");

String projectJson = java.nio.file.Files.readString(tempDir.resolve(".project.json"));
// java.nio.file.Files.readString is available in Java 11+
// String projectJson = java.nio.file.Files.readString(tempDir.resolve(".project.json"));
String projectJson = new String(Files.readAllBytes(tempDir.resolve(".project.json")));
assertEquals("{\n \"name\": \"test\",\n \"version\": \"1.0.0\",\n \"usage\": []\n}", projectJson);

String metaJson = java.nio.file.Files.readString(tempDir.resolve(".meta.json"));
// String metaJson = Files.readString(tempDir.resolve(".meta.json"));
String metaJson = new String(Files.readAllBytes(tempDir.resolve(".meta.json")));
Pattern regex = Pattern.compile(
"\\{\\s*\"index\":\\s*\\{\\},\\s*\"created\":\\s*\"\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}.\\d{6,9}Z\"\\s*\\}",
Pattern.DOTALL);
Expand All @@ -42,12 +46,13 @@ public void testBasicInit() {
@Test
public void testBasicEnv() {
try {
java.nio.file.Path tempDir = java.nio.file.Files.createTempDirectory("sysand-test-env");
java.nio.file.Path tempDir = Files.createTempDirectory("sysand-test-env");
java.nio.file.Path envPath = tempDir.resolve(org.sysand.Sysand.defaultEnvName());
org.sysand.Sysand.env(envPath);

assertTrue(java.nio.file.Files.exists(envPath.resolve("entries.txt")), "Entries file should exist");
String entries = java.nio.file.Files.readString(envPath.resolve("entries.txt"));
assertTrue(Files.exists(envPath.resolve("entries.txt")), "Entries file should exist");
// String entries = java.nio.file.Files.readString(envPath.resolve("entries.txt"));
String entries = new String(Files.readAllBytes(envPath.resolve("entries.txt")));
assertEquals("", entries);
} catch (java.io.IOException e) {
fail("Failed during temporary directory operations or Sysand.env: " + e.getMessage());
Expand Down
5 changes: 2 additions & 3 deletions bindings/java/java/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ SPDX-License-Identifier: MIT OR Apache-2.0
<version>VERSION</version>
<name>sysand</name>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
Expand All @@ -42,6 +40,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.1</version>
<configuration>
<release>8</release>
<compilerArgs>
<arg>-h</arg>
<arg>target/headers</arg>
Expand All @@ -51,4 +50,4 @@ SPDX-License-Identifier: MIT OR Apache-2.0
<!-- -->
</plugins>
</build>
</project>
</project>
6 changes: 2 additions & 4 deletions bindings/java/src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,8 @@ impl ToJObject for bool {
.find_class("java/lang/Boolean")
.expect("Failed to find Boolean class");
let boolean_value: jni::sys::jboolean = if *self { 1 } else { 0 };
let boolean_object = env
.new_object(boolean_class, "(Z)V", &[JValue::from(boolean_value)])
.expect("Failed to create Boolean");
boolean_object
env.new_object(boolean_class, "(Z)V", &[JValue::from(boolean_value)])
.expect("Failed to create Boolean")
}
}

Expand Down
41 changes: 35 additions & 6 deletions bindings/java/src/exceptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,41 @@
//
// SPDX-License-Identifier: MIT OR Apache-2.0

use std::fmt;

use jni::JNIEnv;

pub(crate) fn throw_exception(env: &mut JNIEnv<'_>, exception_kind: &str, message: String) {
let exception_class = env
.find_class(format!("org/sysand/exceptions/{}", exception_kind))
.unwrap_or_else(|_| panic!("Failed to find {} class", exception_kind));
env.throw_new(exception_class, &message)
.expect("Failed to throw the exception");
pub(crate) trait Throw {
fn throw_exception(&mut self, exception_kind: ExceptionKind, message: String);
}

impl Throw for JNIEnv<'_> {
fn throw_exception(&mut self, exception_kind: ExceptionKind, message: String) {
let exception_class = self
.find_class(format!("org/sysand/exceptions/{}", exception_kind))
.unwrap_or_else(|_| panic!("Failed to find {} class", exception_kind));
self.throw_new(exception_class, &message)
.expect("Failed to throw the exception");
}
}

/// Lists all possible exceptions to be thrown, i.e.
/// all exception types defined in
/// `java/src/main/java/org/sysand/exceptions/*.java`
#[derive(Debug)]
pub(crate) enum ExceptionKind {
IOError,
PathError,
ProjectAlreadyExists,
InvalidSemanticVersion,
InvalidValue,
SerializationError,
ResolutionError,
}

impl fmt::Display for ExceptionKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// derive(Debug) prints enum variant name, which is exactly what we need
fmt::Debug::fmt(self, f)
}
}
73 changes: 42 additions & 31 deletions bindings/java/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ use jni::{
objects::{JClass, JObject, JObjectArray, JString},
};
use sysand_core::{
commands,
env::local_directory::LocalWriteError,
info::InfoError,
new::NewError,
project::local_src::{LocalSrcError, LocalSrcProject},
resolve::standard::standard_resolver,
};

use crate::{
conversion::{ToJObject, ToJObjectArray},
exceptions::throw_exception,
exceptions::{ExceptionKind, Throw},
};

mod conversion;
Expand All @@ -36,28 +39,28 @@ pub extern "system" fn Java_org_sysand_Sysand_init__Ljava_lang_String_2Ljava_lan
.expect("Failed to get version")
.into();
let path: String = env.get_string(&path).expect("Failed to get path").into();
let command_result = sysand_core::commands::new::do_new_local_file(name, version, path);
let command_result = commands::new::do_new_local_file(name, version, path);
match command_result {
Ok(_) => {}
Err(error) => match error {
sysand_core::new::NewError::AlreadyExists(msg) => {
throw_exception(&mut env, "ProjectAlreadyExists", msg)
NewError::SemVerParse(..) => {
env.throw_exception(ExceptionKind::InvalidSemanticVersion, error.to_string())
}
sysand_core::new::NewError::SemVerError(suberror) => {
throw_exception(&mut env, "InvalidSemanticVersion", suberror.to_string())
}
sysand_core::new::NewError::ProjectError(suberror) => match suberror {
NewError::Project(suberror) => match suberror {
LocalSrcError::AlreadyExists(msg) => {
throw_exception(&mut env, "ProjectAlreadyExists", msg)
env.throw_exception(ExceptionKind::ProjectAlreadyExists, msg)
}
LocalSrcError::Serde(subsuberror) => {
throw_exception(&mut env, "InvalidValue", subsuberror.to_string())
LocalSrcError::Deserialize(subsuberror) => {
env.throw_exception(ExceptionKind::InvalidValue, subsuberror.to_string())
}
LocalSrcError::Io(subsuberror) => {
throw_exception(&mut env, "IOError", subsuberror.to_string())
env.throw_exception(ExceptionKind::IOError, subsuberror.to_string())
}
LocalSrcError::Path(subsuberror) => {
throw_exception(&mut env, "PathError", subsuberror.to_string())
env.throw_exception(ExceptionKind::PathError, subsuberror.to_string())
}
LocalSrcError::Serialize(subsuberror) => {
env.throw_exception(ExceptionKind::SerializationError, subsuberror.to_string())
}
},
},
Expand All @@ -80,27 +83,35 @@ pub extern "system" fn Java_org_sysand_Sysand_env__Ljava_lang_String_2<'local>(
path: JString<'local>,
) {
let path: String = env.get_string(&path).expect("Failed to get path").into();
let command_result = sysand_core::commands::env::do_env_local_dir(path);
let command_result = commands::env::do_env_local_dir(path);
match command_result {
Ok(_) => {}
Err(error) => match error {
sysand_core::commands::env::EnvError::AlreadyExists(msg) => throw_exception(
&mut env,
"EnvironmentAlreadyExists",
commands::env::EnvError::AlreadyExists(msg) => env.throw_exception(
ExceptionKind::PathError,
format!("Path already exists: {}", msg.display()),
),
sysand_core::commands::env::EnvError::WriteError(suberror) => match suberror {
sysand_core::env::local_directory::LocalWriteError::IOError(subsuberror) => {
throw_exception(&mut env, "IOError", subsuberror.to_string())
commands::env::EnvError::Write(suberror) => match suberror {
LocalWriteError::Io(subsuberror) => {
env.throw_exception(ExceptionKind::IOError, subsuberror.to_string())
}
LocalWriteError::Deserialize(subsuberror) => {
env.throw_exception(ExceptionKind::InvalidValue, subsuberror.to_string())
}
LocalWriteError::Path(subsuberror) => {
env.throw_exception(ExceptionKind::PathError, subsuberror.to_string())
}
LocalWriteError::AlreadyExists(msg) => {
env.throw_exception(ExceptionKind::IOError, msg)
}
LocalWriteError::Serialize(subsuberror) => {
env.throw_exception(ExceptionKind::SerializationError, subsuberror.to_string())
}
sysand_core::env::local_directory::LocalWriteError::SerialisationError(
subsuberror,
) => throw_exception(&mut env, "SerialisationError", subsuberror.to_string()),
sysand_core::env::local_directory::LocalWriteError::PathError(subsuberror) => {
throw_exception(&mut env, "PathError", subsuberror.to_string())
LocalWriteError::TryMove(subsuberror) => {
env.throw_exception(ExceptionKind::IOError, subsuberror.to_string())
}
sysand_core::env::local_directory::LocalWriteError::AlreadyExists(msg) => {
throw_exception(&mut env, "IOError", msg)
LocalWriteError::LocalRead(subsuberror) => {
env.throw_exception(ExceptionKind::IOError, subsuberror.to_string())
}
},
},
Expand All @@ -118,7 +129,7 @@ pub extern "system" fn Java_org_sysand_Sysand_info_1path__Ljava_lang_String_2<'l
project_path: std::path::PathBuf::from(&path),
};

let command_result = sysand_core::commands::info::do_info_project(&project);
let command_result = commands::info::do_info_project(&project);
match command_result {
Some(info_metadata) => info_metadata.to_jobject(&mut env),
None => JObject::null(),
Expand Down Expand Up @@ -173,11 +184,11 @@ pub extern "system" fn Java_org_sysand_Sysand_info<'local>(
index_base_url.map(|x| vec![x]),
);

let results = match sysand_core::commands::info::do_info(&uri, &combined_resolver) {
let results = match commands::info::do_info(&uri, &combined_resolver) {
Ok(matches) => matches,
Err(InfoError::NoResolve(_)) => Vec::new(),
Err(InfoError::ResolutionError(error)) => {
throw_exception(&mut env, "ResolutionError", error.to_string());
Err(e @ InfoError::Resolution(_)) => {
env.throw_exception(ExceptionKind::ResolutionError, e.to_string());
return JObjectArray::default();
}
};
Expand Down
6 changes: 3 additions & 3 deletions bindings/js/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ crate-type = ["rlib", "cdylib"]
sysand-core = { path = "../../core", features = ["js"] }
serde_json = { version = "1.0.140", default-features = false }
thiserror = "2.0.12"
wasm-bindgen = "0.2.100"
web-sys = { version = "0.3.77", default-features = false, optional = true, features = ["Window", "Storage"] }
wasm-bindgen = "0.2.105"
web-sys = { version = "0.3.82", default-features = false, optional = true, features = ["Window", "Storage"] }
console_error_panic_hook = { version = "0.1.7" }
serde = { version = "1.0.219", default-features = false }
sha2 = { version = "0.10.9", default-features = false }
Expand All @@ -28,7 +28,7 @@ typed-path = { version = "0.12.0", default-features = false }
[dev-dependencies]
regex = { version = "1.11.1", default-features = false, features = ["unicode-perl"] }
semver = { version = "1.0.26", default-features = false }
wasm-bindgen-test = "0.3.50"
wasm-bindgen-test = "0.3.55"

[features]
default = ["browser"]
Expand Down
Loading
Loading