hara-native 0.1.21

HAL-free native host runtime and package launcher for Hara
Documentation
use hara_native::Runtime;

#[test]
fn direct_native_conforms_to_the_portable_catch_shape() {
    let mut runtime = Runtime::core();
    runtime
        .set_execution_backend("direct-native")
        .expect("native builds must expose the direct-native backend");
    assert_eq!(
        runtime
            .eval_direct_native("(try (throw (ex :test/failure {})) (catch error :caught))")
            .unwrap(),
        ":caught"
    );
    let error = runtime
        .eval_direct_native(
            "(try (throw (ex :test/failure {})) (catch Throwable error :caught))",
        )
        .expect_err("typed host catch syntax must not compile as Hara");
    assert!(
        error.contains(
            "catch expects a binding symbol and one handler form; typed host catch clauses are not part of Hara"
        ),
        "{error}"
    );
}

#[test]
fn direct_native_reads_spanned_forms_without_evaluation() {
    let mut runtime = Runtime::core();
    runtime
        .set_execution_backend("direct-native")
        .expect("native builds must expose the direct-native backend");
    assert_eq!(
        runtime
            .eval_direct_native(
                r#"(let [nodes (std.native.Edn/read-forms-spanned "; comment\n(defn sample [x]\n  (inc x))")
                         node (std.protocol.ilookup.ILookup/lookup nodes 0)
                         call (std.protocol.ilookup.ILookup/lookup (:children node) 3)
                         metadata (std.protocol.ilookup.ILookup/lookup
                                   (std.native.Edn/read-forms-spanned "^{:refer demo.subject/run}\n(def run [] 1)")
                                   0)
                         unevaluated (std.protocol.ilookup.ILookup/lookup
                                      (std.native.Edn/read-forms-spanned "(throw (ex :host {} :ex/message \"not evaluated\"))")
                                      0)]
                     [(= (:form node) (quote (defn sample [x] (inc x))))
                      (= [(:offset (:start node)) (:line (:start node)) (:column (:start node))
                          (:offset (:end node)) (:line (:end node)) (:column (:end node))]
                         [10 2 1 37 3 11])
                      (= (:form call) (quote (inc x)))
                      (= [(:offset (:start call)) (:line (:start call)) (:column (:start call))
                          (:offset (:end call)) (:line (:end call)) (:column (:end call))]
                         [29 3 3 36 3 10])
                      (= (:refer (std.protocol.iobjtype.IObjType/meta (:form metadata)))
                         (quote demo.subject/run))
                      (= (:form unevaluated) (quote (throw (ex :host {} :ex/message "not evaluated"))))])"#,
            )
            .unwrap(),
        "[true true true true true true]"
    );
}

#[test]
fn direct_native_spanned_reader_reports_parse_positions() {
    let mut runtime = Runtime::core();
    runtime
        .set_execution_backend("direct-native")
        .expect("native builds must expose the direct-native backend");
    let error = runtime
        .eval_direct_native("(std.native.Edn/read-forms-spanned \"[1\\n2\")")
        .expect_err("the reader must reject an unterminated form");
    assert!(
        error.contains("read-forms-spanned failed: EOF while reading vector [line 2, column 2]"),
        "{error}"
    );
}

#[test]
fn lang_values_are_qualified_and_library_snapshots_restore_the_baseline() {
    let mut runtime = Runtime::core();
    let display = runtime
        .eval_native(
            "(let [book (std.lang.Book/create {:coordinate (quote demo/book)}) \
                   library (std.lang.Library/create {}) \
                   _ (std.lang.Library/install library book) \
                   snapshot (std.lang.Library/snapshot library) \
                   _ (std.lang.Library/remove library (quote demo/book)) \
                   _ (std.lang.Library/restore library snapshot) \
                   harness (std.lang.Harness/create {}) \
                   _ (std.lang.Harness/close harness) \
                   _ (std.lang.Harness/reset harness)] \
               [(std.native.Base/instance? std.lang.Book book) \
                (std.protocol.ilookup.ILookup/lookup \
                  (std.lang.Library/state library) :book-count) \
                (std.lang.Harness/closed? harness)])",
        )
        .unwrap();

    assert_eq!(display, "[true 1 false]");
}

#[test]
fn lang_runtime_close_and_reset_are_idempotent() {
    let mut runtime = Runtime::core();
    let display = runtime
        .eval_native(
            "(let [runtime (std.lang.Runtime/create {})] \
               (std.lang.Runtime/close runtime) \
               (std.lang.Runtime/close runtime) \
               (std.lang.Runtime/reset runtime) \
               [(std.native.Base/instance? std.lang.Runtime runtime) \
                (std.lang.Runtime/closed? runtime) \
                (std.protocol.ilookup.ILookup/lookup \
                  (std.lang.Runtime/state runtime) :state)])",
        )
        .unwrap();

    assert_eq!(display, "[true false :ready]");
}

#[test]
fn library_rejects_books_without_an_explicit_coordinate() {
    let mut runtime = Runtime::core();
    let display = runtime
        .eval_native(
            "(let [library (std.lang.Library/create {}) \
                   book (std.lang.Book/create {:id (quote fixture/book)})] \
               (try \
                 (std.lang.Library/install library book) \
                 false \
                 (catch _ true)))",
        )
        .unwrap();

    assert_eq!(display, "true");
}