use std::path::{Path, PathBuf};
use std::process::Command;
use yog_data::{Map, Ref, Res, Value, binary, text};
fn gson() -> PathBuf {
let home = std::env::var_os("HOME")
.map(PathBuf::from)
.unwrap_or_default();
let dir = home.join(".gradle/caches/modules-2/files-2.1/com.google.code.gson/gson");
let mut jars: Vec<PathBuf> = walk(&dir)
.into_iter()
.filter(|p| {
p.extension().is_some_and(|e| e == "jar") && !p.to_string_lossy().contains("sources")
})
.collect();
jars.sort();
jars.pop()
.expect("gson in the Gradle cache (build a host once)")
}
fn walk(dir: &Path) -> Vec<PathBuf> {
let Ok(entries) = std::fs::read_dir(dir) else {
return Vec::new();
};
entries
.flatten()
.flat_map(|e| {
if e.path().is_dir() {
walk(&e.path())
} else {
vec![e.path()]
}
})
.collect()
}
fn jdk_tool(name: &str) -> PathBuf {
let jdk17 = Path::new("/usr/lib/jvm/java-17-openjdk-amd64/bin").join(name);
if jdk17.exists() {
jdk17
} else {
PathBuf::from(name)
}
}
fn every_type() -> Value {
let mut m = Map::new();
m.insert(
"ints",
Value::List(
[0, 1, -1, 63, -64, 64, i64::MAX, i64::MIN]
.map(Value::Int)
.to_vec(),
),
);
m.insert(
"floats",
Value::List(
[0.5, -0.0, 1e300, f64::MIN_POSITIVE]
.map(Value::Float)
.to_vec(),
),
);
m.insert(
"misc",
Value::List(vec![
Value::Null,
Value::Bool(true),
Value::Bool(false),
Value::Str("ünï😀".into()),
]),
);
m.insert("ref", Value::Ref(Ref::new("item", "minecraft:stick")));
m.insert("tag", Value::Tag(Ref::new("block", "minecraft:logs")));
m.insert(
"res",
Value::Res(Res {
set: "Strings".into(),
key: "a.b".into(),
}),
);
m.insert("text", Value::Text("{b|x}".into()));
m.insert("image", Value::Image("sprite:x".into()));
m.insert("color", Value::Color(0x80FF_0000));
m.insert("lang", Value::Lang("gui.done".into()));
m.insert("again", Value::Str("ünï😀".into()));
Value::Map(m)
}
#[test]
#[ignore = "needs a JDK; run with --ignored"]
fn java_reads_and_writes_the_same_bytes() {
let root = Path::new(env!("CARGO_MANIFEST_DIR"));
let dir = std::env::temp_dir().join(format!("yog-data-java-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let javac = Command::new(jdk_tool("javac"))
.args(["--release", "17", "-cp"])
.arg(gson())
.arg("-d")
.arg(&dir)
.arg(root.join("../../../java-common/src/main/java/dev/yog/data/YogData.java"))
.arg(root.join("tests/java/RoundTrip.java"))
.status()
.expect("javac");
assert!(javac.success());
let book = text::parse_value(include_str!("../benches/hexbook.ybook")).unwrap();
for (name, value) in [("types", every_type()), ("book", book)] {
let bytes = binary::encode(&value);
let (input, output) = (
dir.join(format!("{name}.in")),
dir.join(format!("{name}.out")),
);
std::fs::write(&input, &bytes).unwrap();
let run = Command::new(jdk_tool("java"))
.arg("-cp")
.arg(std::env::join_paths([dir.clone(), gson()]).unwrap())
.arg("RoundTrip")
.arg(&input)
.arg(&output)
.status()
.expect("java");
assert!(run.success(), "{name}: Java failed");
assert!(
std::fs::read(&output).unwrap() == bytes,
"{name}: Java wrote different bytes"
);
}
let stream: yog_data::Stream<String> = ["minecraft:stone", "x", "minecraft:stone"]
.map(String::from)
.into_iter()
.collect();
let lists = [
("packed", yog_data::to_bytes(&vec![1u16, 300, 7, u16::MAX])),
("stream", yog_data::to_bytes(&stream)),
(
"floats",
yog_data::to_bytes(&vec![0.5f64, -1e300, f64::MIN_POSITIVE]),
),
("floats32", yog_data::to_bytes(&vec![0.25f32, -3.5])),
("float32", yog_data::to_bytes(&1.5f32)),
];
for (name, bytes) in lists {
let (input, output) = (
dir.join(format!("{name}.in")),
dir.join(format!("{name}.out")),
);
std::fs::write(&input, &bytes).unwrap();
let run = Command::new(jdk_tool("java"))
.arg("-cp")
.arg(std::env::join_paths([dir.clone(), gson()]).unwrap())
.arg("RoundTrip")
.arg(&input)
.arg(&output)
.status()
.expect("java");
assert!(run.success(), "{name}: Java failed");
assert_eq!(
binary::decode(&std::fs::read(&output).unwrap()).unwrap(),
binary::decode(&bytes).unwrap(),
"{name}"
);
}
let _ = std::fs::remove_dir_all(&dir);
}