cmajor 0.7.0

Rust bindings for the Cmajor JIT engine.
Documentation
use cmajor::{
    endpoint::EndpointDirection,
    engine::{Engine, Error, Externals, Loaded},
    performer::{OutputValue, Performer},
    value::{
        types::{Primitive, Type},
        Complex32, ValueRef,
    },
    Cmajor,
};

#[test]
fn program_details() {
    let source_code = r#"
        processor Test {
            input value int in [[ hello: "world" ]];

            void main() {
                advance();
            }
        }
    "#;

    let cmajor = Cmajor::new();
    let program = cmajor.parse(source_code).unwrap();
    let engine = cmajor
        .create_default_engine()
        .with_sample_rate(48_000.0)
        .build();

    let engine = engine.load(&program).unwrap();

    let program_details = engine.program_details();

    assert_eq!(program_details.main_processor(), "Test");

    let endpoints = program_details.endpoints().collect::<Vec<_>>();
    assert_eq!(endpoints.len(), 1);

    let input_endpoint = &endpoints[0].as_value().unwrap();
    assert_eq!(input_endpoint.id(), "in");
    assert_eq!(input_endpoint.direction(), EndpointDirection::Input);
    assert_eq!(
        input_endpoint.annotation().keys().collect::<Vec<_>>(),
        vec!["hello"]
    );
    assert_eq!(input_endpoint.annotation().get("hello").unwrap(), "world");
    assert!(matches!(
        input_endpoint.ty(),
        Type::Primitive(Primitive::Int32)
    ));
}

fn setup<E>(
    source_code: impl AsRef<str>,
    externals: Externals,
    endpoints: impl FnOnce(&mut Engine<Loaded>) -> E,
) -> Result<(Performer, E), Error> {
    let cmajor = Cmajor::new();
    let program = cmajor.parse(source_code).unwrap();
    let engine = cmajor
        .create_default_engine()
        .with_sample_rate(48_000.0)
        .build();

    let mut engine = engine.load_with_externals(&program, externals)?;

    let endpoints = endpoints(&mut engine);

    let mut performer = engine.link()?.performer();
    performer.set_block_size(1);
    Ok((performer, endpoints))
}

#[test]
fn loading_external_variables_i32() {
    let source_code = r#"
        processor Test
        {
            output value int32 out;
            external int32 in;

            void main()
            {
                out <- in;
                advance();
            }
        }
    "#;

    let (mut performer, out) = setup(
        source_code,
        Externals::default().with_variable("Test::in", 42),
        |engine| engine.endpoint("out").unwrap(),
    )
    .unwrap();

    performer.advance();
    assert_eq!(performer.get::<i32>(out), 42);
}

#[test]
fn loading_external_variables_i64() {
    let source_code = r#"
        processor Test
        {
            output value int64 out;
            external int64 in;

            void main()
            {
                out <- in;
                advance();
            }
        }
    "#;

    let (mut performer, out) = setup(
        source_code,
        Externals::default().with_variable("Test::in", 42_i64),
        |engine| engine.endpoint("out").unwrap(),
    )
    .unwrap();

    performer.advance();
    assert_eq!(performer.get::<i64>(out), 42);
}

#[test]
fn loading_external_variables_f32() {
    let source_code = r#"
        processor Test
        {
            output value float32 out;
            external float32 in;

            void main()
            {
                out <- in;
                advance();
            }
        }
    "#;

    let (mut performer, out) = setup(
        source_code,
        Externals::default().with_variable("Test::in", 42_f32),
        |engine| engine.endpoint("out").unwrap(),
    )
    .unwrap();

    performer.advance();
    assert_eq!(performer.get::<f32>(out), 42_f32);
}

#[test]
fn loading_external_variables_f64() {
    let source_code = r#"
        processor Test
        {
            output value float64 out;
            external float64 in;

            void main()
            {
                out <- in;
                advance();
            }
        }
    "#;

    let (mut performer, out) = setup(
        source_code,
        Externals::default().with_variable("Test::in", 42_f64),
        |engine| engine.endpoint("out").unwrap(),
    )
    .unwrap();

    performer.advance();
    assert_eq!(performer.get::<f64>(out), 42_f64);
}

#[test]
fn loading_external_variables_bool() {
    let source_code = r#"
        processor Test
        {
            output value bool out;
            external bool in;

            void main()
            {
                out <- in;
                advance();
            }
        }
    "#;

    let (mut performer, out) = setup(
        source_code,
        Externals::default().with_variable("Test::in", true),
        |engine| engine.endpoint("out").unwrap(),
    )
    .unwrap();

    performer.advance();
    assert!(performer.get::<bool>(out));
}

#[test]
fn loading_external_variables_struct() {
    let source_code = r#"
        processor Test
        {
            output value complex32 out;
            external complex32 in;

            void main()
            {
                out <- in;
                advance();
            }
        }
    "#;

    let (mut performer, out) = setup(
        source_code,
        Externals::default().with_variable(
            "Test::in",
            Complex32 {
                real: 42.0,
                imag: 21.0,
            },
        ),
        |engine| engine.endpoint::<OutputValue>("out").unwrap(),
    )
    .unwrap();

    performer.advance();

    let result: Complex32 = performer.get(out).unwrap().try_into().unwrap();
    assert_eq!(result.real, 42.0);
    assert_eq!(result.imag, 21.0);
}

#[test]
fn loading_external_variables_array() {
    let source_code = r#"
        processor Test
        {
            output value int[4] out;
            external int[4] in;

            void main()
            {
                out <- in;
                advance();
            }
        }
    "#;

    let (mut performer, out) = setup(
        source_code,
        Externals::default().with_variable("Test::in", [1, 2, 3, 4]),
        |engine| engine.endpoint::<OutputValue>("out").unwrap(),
    )
    .unwrap();

    performer.advance();

    let value = performer.get(out).unwrap();
    let array = value.as_array().unwrap();

    assert_eq!(array.get(0), Some(ValueRef::Int32(1)));
    assert_eq!(array.get(1), Some(ValueRef::Int32(2)));
    assert_eq!(array.get(2), Some(ValueRef::Int32(3)));
    assert_eq!(array.get(3), Some(ValueRef::Int32(4)));
}

#[test]
fn loading_external_variables_are_type_checked() {
    let source_code = r#"
        processor Test
        {
            output value int32 out;
            external int32 in;

            void main()
            {
                out <- in;
                advance();
            }
        }
    "#;

    let result = setup(
        source_code,
        Externals::default().with_variable(
            "Test::in",
            Complex32 {
                real: 42.0,
                imag: 21.0,
            },
        ),
        |_| {},
    );

    assert!(result.is_err());
}

#[test]
#[should_panic(
    expected = "assertion `left == right` failed: cmajor assertion failed\n  left: 4\n right: 5"
)]
fn loading_external_functions() {
    let source_code = r#"
        namespace rust
        {
            namespace debug
            {
                external void print (bool value);
                external void print (int32 value);
                external void print (int64 value);
                external void print (float32 value);
                external void print (float64 value);
            }

            namespace test
            {
                external void assert (bool condition);
                external void assertEqual (int32 a, int32 b);
                external void assertEqual (int64 a, int64 b);
                external void assertEqual (float32 a, float32 b);
                external void assertEqual (float64 a, float64 b);
            }
        }

        processor Test
        {
            output stream float32 out;

            void main()
            {
                rust::debug::print (true);
                rust::debug::print (2147483647_i32);
                rust::debug::print (9223372036854775807_i64);
                rust::debug::print (pi);
                rust::debug::print (float32 (pi));

                rust::test::assert (true);
                rust::test::assertEqual (2 + 2, 5);

                advance();
            }
        }
    "#;

    let (mut performer, _) = setup(source_code, Externals::default(), |_| {}).unwrap();
    performer.advance();
}