jaded 0.5.0

Java deserialization for Rust
Documentation
use jaded::{Content, ConversionError, JavaError, ObjectData, Parser, PrimitiveType::*, Value::*};
use std::fs::File;
use std::io::Read;

const STREAM_ERROR: &str = "Couldn't read from stream";

fn load_stream(name: &str) -> Parser<File> {
    let full_file = &format!("res/{name}.obj");
    let source = File::open(full_file).unwrap_or_else(|_| panic!("File '{}' missing", full_file));
    Parser::new(source).expect("Parser failed")
}

/// Read the first item from a stream
fn read_content(name: &str) -> Content {
    load_stream(name).read().expect(STREAM_ERROR)
}

/// Macro to chain string of methods where each returns an Option<T> that should be unwrapped
macro_rules! assume {
    ($stream:ident.$( $method:ident($($args:tt)?) ).+ ) => {
        $stream$(.$method($($args)?).expect(
                concat!(".", stringify!($method), "(", stringify!($($args)?), ") returned None")
        ))+
    };
}

#[test]
fn externalized() {
    let content = read_content("extern");
    let obj = assume!(content.value().object_data());

    assert_eq!(1, obj.annotation_count());
    let mut anno = obj.get_annotation(0).unwrap();
    assert_eq!(17, anno.read_i32().unwrap());
}

#[test]
fn proxy() {
    let content = read_content("proxy");
    let obj = assume!(content.value().object_data());

    assert_eq!("java.lang.reflect.Proxy", obj.class_name());
    assert_eq!(1, obj.field_count());

    let proxied = get_field(obj, "h");
    let proxied_obj = assume!(proxied.object_data());
    assert_eq!(1, proxied_obj.field_count());
    assert_eq!(0, proxied_obj.annotation_count());
    assert_eq!(&Null, get_field(proxied_obj, "foo"));
    assert_eq!("com.example.MeaningOfLife", proxied_obj.class_name());
}

#[test]
fn custom_write() {
    let content = read_content("custom_write");
    let obj = assume!(content.value().object_data());
    assert_eq!("com.example.Custom", obj.class_name());
    let mut anno = obj.get_annotation(0).unwrap();
    assert_eq!(2, anno.read_i32().unwrap());
    println!("read int");
    assert_eq!("helloWorld", assume!(anno.read_object().string()));
    println!("read string");
    assert_eq!(2, anno.read_i32().unwrap());
    println!("read int");
}

#[test]
fn sample_record() {
    let content = read_content("sample_record");
    let obj = assume!(content.value().object_data());
    assert_eq!("com.example.SampleRecord", obj.class_name());
    assert_eq!(42, obj.get_field_as("i").unwrap());
    assert_eq!("helloWorld", assume!(obj.get_field("s").string()));
}

#[test]
fn loop_reference() {
    let content = read_content("loop_reference");
    let data = assume!(content.value().object_data());
    assert_eq!(&Loop(0), get_field(data, "loop"));
}

#[test]
fn arraylist() {
    let content = read_content("arraylist");
    let list = assume!(content.value());
    let data = assume!(list.object_data());

    assert_eq!("java.util.ArrayList", data.class_name());
    assert_eq!(1, data.field_count());
    assert_eq!(3, data.get_field_as("size").unwrap());
    let mut arraylist_anno = data.get_annotation(0).unwrap();
    assert_eq!(3, arraylist_anno.read_i32().unwrap());

    let child = "com.example.Child";
    let c1 = arraylist_anno.read_object().unwrap();
    assert_eq!(child, c1.object_data().unwrap().class_name());
    let c2 = arraylist_anno.read_object().unwrap();
    assert_eq!(child, c2.object_data().unwrap().class_name());
    let c3 = arraylist_anno.read_object().unwrap();
    assert_eq!(child, c3.object_data().unwrap().class_name());
    assert_ne!(c1, c2);
    assert_eq!(c2, c3);
}

#[test]
fn extended_pojo() {
    let content = read_content("extended_pojo");
    let obj = assume!(content.value().object_data());

    assert_eq!("com.example.Child", obj.class_name());
    assert_eq!(0, obj.annotation_count());
    assert_eq!(6, obj.field_count());
    assert_eq!(1, obj.get_field_as("i").unwrap());
    assert_eq!(
        &Enum("com.example.Direction".to_string(), "EAST".to_string()),
        get_field(obj, "d")
    );
    assert_eq!("helloWorld", &obj.get_field_as::<String>("bar").unwrap());
    assert_eq!(
        &PrimitiveArray(vec![Double(1.2), Double(2.3), Double(3.4), Double(4.5)]),
        get_field(obj, "arr")
    );
    assert_eq!("bar", &obj.get_field_as::<String>("foo").unwrap());
    let other = get_field(obj, "other").object_data().unwrap();
    assert_eq!("com.example.Other", other.class_name());
    assert_eq!("def", other.get_field_as::<String>("abc").unwrap());
    assert_eq!(0, other.annotation_count());
}

#[test]
fn pojo() {
    let content = read_content("pojo");
    let pojo = assume!(content.value());
    let obj = assume!(pojo.object_data());
    assert_eq!(obj.class_name(), "com.example.Parent");
    assert_eq!(obj.annotation_count(), 0);
    assert_eq!(obj.field_count(), 2);
    assert_eq!(13, obj.get_field_as("i").unwrap());
    assert_eq!("bar", obj.get_field_as::<String>("foo").unwrap());
}

#[test]
fn strings() {
    let mut p = load_stream("strings");
    assert_eq!(assume!(p.read().value().string()), "helloWorld");
    assert_eq!(assume!(p.read().value().string()), "hello");
    assert_eq!(assume!(p.read().value().string()), "world");
    assert_eq!(assume!(p.read().value().string()), "helloWorld");

    let mut p = load_stream("string_long");
    let long = p.read().expect(STREAM_ERROR);
    let long_string = assume!(long.value().string());
    assert!(long_string.len() > u16::MAX.into());
    assert!(long_string.starts_with("Lorem ipsum"));
    assert!(long_string.ends_with("magna aliqua"));
}

#[test]
fn auto_box_string() {
    let mut p = load_stream("strings");
    assert_eq!(Box::new("helloWorld".to_string()), p.read_as().unwrap());
}

#[test]
fn reset() {
    let mut p = load_stream("reset");
    let s1 = p.read().expect(STREAM_ERROR);
    let s2 = p.read().expect(STREAM_ERROR);
    let s3 = p.read().expect(STREAM_ERROR);
    let s4 = p.read().expect(STREAM_ERROR);

    assert_eq!(assume!(s1.value().string()), "helloWorld");
    assert_eq!(assume!(s2.value().string()), "helloWorld");
    assert_eq!(assume!(s3.value().string()), "foobar");
    assert_eq!(assume!(s4.value().string()), "foobar");
}

#[test]
fn null_reference() {
    assert!(read_content("null").value().unwrap().is_null())
}

#[test]
fn null_as_string() {
    let mut p = load_stream("null");
    match p.read_as::<String>() {
        Err(JavaError::ConvertError(ConversionError::NullPointerException)) => (),
        Err(e) => panic!("Expected NPE but got: {}", e),
        Ok(s) => panic!("Expected NPE but read {}", s),
    }
}

#[test]
fn null_as_optional_string() {
    let mut p = load_stream("null");
    match p.read_as::<Option<String>>() {
        Ok(None) => (),
        Ok(Some(s)) => panic!("Expected None but read {}", s),
        Err(e) => panic!("Expected None but got: {}", e),
    }
}

#[test]
fn enum_constant() {
    match read_content("enum").value().unwrap() {
        Enum(cls, cons) => assert_eq!(
            ("com.example.Direction", "EAST"),
            (cls.as_str(), cons.as_str())
        ),
        _ => panic!("Expected enum constant"),
    }
}

#[test]
fn class() {
    assert_eq!(
        &Class("com.example.Parent".to_string()),
        read_content("class_only").value().unwrap()
    );
}

#[test]
fn block_data() {
    let content = read_content("block");
    let short = content.data().unwrap();
    assert_eq!(
        short,
        vec![
            0, 0, 0, 42, // 42 as int
            0, 0, 0, 0, 0, 0, 0, 84, // 84 as long
            0, 10, 104, 101, 108, 108, 111, 87, 111, 114, 108, 100
        ]
    ); // helloWorld as string (length, data)

    let content = read_content("block_long");
    let long = content.data().unwrap();
    assert_eq!(long, [0; 256]);
}

#[test]
fn nested_array() {
    let content = read_content("array_nested");
    let arrays = content.value().unwrap();
    assert_eq!(
        &Array(vec![
            PrimitiveArray(vec![Boolean(true), Boolean(false)]),
            PrimitiveArray(vec![Boolean(false), Boolean(true)]),
        ]),
        arrays
    );
}

#[test]
fn primitive_arrays() {
    let mut parser = load_stream("array_primitive");

    assert_eq!(
        parser.read().expect(STREAM_ERROR).value().unwrap(),
        &PrimitiveArray(vec![Double(1.2), Double(2.3), Double(3.4), Double(4.5)])
    );
    assert_eq!(
        parser.read().expect(STREAM_ERROR).value().unwrap(),
        &PrimitiveArray(vec![
            Boolean(true),
            Boolean(true),
            Boolean(false),
            Boolean(true)
        ])
    );
}

#[test]
fn primitive_arrays_as_vec() {
    let mut parser = load_stream("array_primitive");
    assert_eq!(
        vec![1.2, 2.3, 3.4, 4.5],
        parser.read_as::<Vec<f64>>().unwrap()
    );
    assert_eq!(
        vec![true, true, false, true],
        parser.read_as::<Vec<bool>>().unwrap()
    );
}

#[test]
fn primitives_from_value() {
    let mut parser = load_stream("primitives");
    assert!(parser.read_as::<bool>().unwrap());
    assert_eq!('A', parser.read_as().unwrap());
    assert_eq!(-42i16, parser.read_as().unwrap());
    assert_eq!(-23, parser.read_as().unwrap());
    assert_eq!(-84i64, parser.read_as().unwrap());
    assert_eq!(1.23f32, parser.read_as().unwrap());
    assert_eq!(4.56, parser.read_as().unwrap());
    assert_eq!(76u8, parser.read_as().unwrap());
    assert!(parser.read_as::<f32>().unwrap().is_nan());
    assert!(parser.read_as::<f64>().unwrap().is_nan());
    assert!(parser.read_as::<f64>().unwrap().is_infinite());
}

#[test]
fn primitives_as_objects() {
    let mut parser = load_stream("primitives");
    assert_eq!(extract_primitive(&mut parser, "Boolean"), Boolean(true));
    assert_eq!(extract_primitive(&mut parser, "Character"), Char('A'));
    assert_eq!(extract_primitive(&mut parser, "Short"), Short(-42));
    assert_eq!(extract_primitive(&mut parser, "Integer"), Int(-23));
    assert_eq!(extract_primitive(&mut parser, "Long"), Long(-84));
    assert_eq!(extract_primitive(&mut parser, "Float"), Float(1.23));
    assert_eq!(extract_primitive(&mut parser, "Double"), Double(4.56));
    assert_eq!(extract_primitive(&mut parser, "Byte"), Byte(76));
    match extract_primitive(&mut parser, "Float") {
        Float(f) => assert!(f.is_nan()),
        _ => panic!("expected NaN"),
    }
    match extract_primitive(&mut parser, "Double") {
        Double(f) => assert!(f.is_nan()),
        _ => panic!("expected NaN"),
    }
    match extract_primitive(&mut parser, "Double") {
        Double(f) => assert!(f.is_infinite() && f.is_sign_positive()),
        _ => panic!("expected NaN"),
    }
}

fn extract_primitive<T: Read>(parser: &mut Parser<T>, class: &str) -> jaded::PrimitiveType {
    match parser.read().expect("Couldn't read from stream").value() {
        Some(Object(obj)) => {
            assert_eq!(format!("java.lang.{class}"), obj.class_name());
            assert_eq!(0, obj.annotation_count());
            *obj.get_field("value").unwrap().primitive().unwrap()
        }
        _ => panic!("Expected instance of java.lang.{}", class),
    }
}

fn get_field<'a>(obj: &'a ObjectData, field: &str) -> &'a jaded::Value {
    obj.get_field(field).unwrap()
}