textlane 0.3.0

Rust-native text processing pipeline — a unified replacement with native and JavaScript plugins
Documentation
use serde_json::{Value, json};
use textlane_core::{Error, File};

use crate::models::pipeline::{Api, Pipeline};
use crate::models::run::{Outcome, Pause, Progress};

fn pipeline(plugins: Value) -> Result<Pipeline, Error> {
    let mut pipeline = Pipeline::new();
    pipeline.use_value(&plugins, None)?;
    Ok(pipeline)
}

fn paused(progress: Progress) -> Result<Pause, Error> {
    match progress {
        Progress::Paused(pause) => Ok(pause),
        Progress::Finished(_) => Err(Error::new("test", "expected a pause")),
    }
}

fn text(progress: Progress) -> Result<String, Error> {
    match progress {
        Progress::Finished(finished) => match finished.output {
            Outcome::Text(text) => Ok(text),
            Outcome::Tree(tree) => Ok(tree.to_string()),
        },
        Progress::Paused(_) => Err(Error::new("test", "expected the end")),
    }
}

#[test]
fn pauses_before_host_stages_and_resumes_with_their_tree() -> Result<(), Error> {
    let mut pipeline = pipeline(json!([
        "md.parse",
        { "host": 7 },
        "md.toHtml",
        { "host": 8 },
        "html.stringify",
    ]))?;
    let pause = paused(pipeline.start_process("*a*", File::new(), Api::Async)?)?;
    assert_eq!(pause.hosts(), &[7]);
    assert_eq!(pause.tree()["type"], "root");
    assert_eq!(pause.tree()["position"]["end"]["offset"], 3);
    let mut tree = pause.tree().clone();
    tree["children"][0]["children"][0]["type"] = json!("strong");
    let pause = paused(pause.resume(&tree)?)?;
    assert_eq!(pause.hosts(), &[8]);
    assert_eq!(pause.tree()["children"][0]["tagName"], "p");
    let tree = pause.tree().clone();
    assert_eq!(text(pause.resume(&tree)?)?, "<p><strong>a</strong></p>");
    Ok(())
}

#[test]
fn batches_consecutive_host_stages() -> Result<(), Error> {
    let mut pipeline =
        pipeline(json!(["md.parse", { "host": 1 }, { "host": 2 }, "md.stringify"]))?;
    let pause = paused(pipeline.start_process("a", File::new(), Api::Sync)?)?;
    assert_eq!(pause.hosts(), &[1, 2]);
    let tree = pause.tree().clone();
    assert_eq!(text(pause.resume(&tree)?)?, "a\n");
    Ok(())
}

#[test]
fn skips_disabled_host_plugins() -> Result<(), Error> {
    let mut pipeline =
        pipeline(json!(["md.parse", { "host": 1 }, { "host": 2 }, "md.stringify"]))?;
    pipeline.use_value(&json!({ "host": 1 }), Some(json!(false)))?;
    assert_eq!(pipeline.hosts(), vec![2]);
    let pause = paused(pipeline.start_process("a", File::new(), Api::Async)?)?;
    assert_eq!(pause.hosts(), &[2]);
    Ok(())
}

#[test]
fn keeps_messages_host_stages_report() -> Result<(), Error> {
    let mut pipeline = pipeline(json!(["md.parse", { "host": 1 }, "md.stringify"]))?;
    let mut pause = paused(pipeline.start_process("a", File::new(), Api::Async)?)?;
    pause.file_mut().warn("from a host", Some("rule"));
    let tree = pause.tree().clone();
    match pause.resume(&tree)? {
        Progress::Finished(finished) => assert_eq!(finished.file.messages.len(), 1),
        Progress::Paused(_) => return Err(Error::new("test", "expected the end")),
    }
    Ok(())
}

#[test]
fn runs_a_tree_up_to_a_host_stage() -> Result<(), Error> {
    let mut pipeline = pipeline(json!(["md.toHtml", { "host": 1 }]))?;
    let tree = json!({ "type": "root", "children": [{ "type": "text", "value": "a" }] });
    let pause = paused(pipeline.start_run(&tree, File::new(), Api::Async)?)?;
    assert_eq!(
        pause.tree()["children"][0],
        json!({ "type": "text", "value": "a" })
    );
    let tree = pause.tree().clone();
    match pause.resume(&tree)? {
        Progress::Finished(finished) => match finished.output {
            Outcome::Tree(tree) => assert_eq!(tree["type"], "root"),
            Outcome::Text(_) => return Err(Error::new("test", "expected a tree")),
        },
        Progress::Paused(_) => return Err(Error::new("test", "expected the end")),
    }
    Ok(())
}

#[test]
fn fails_to_process_host_stages_without_a_binding() -> Result<(), Error> {
    let mut pipeline = pipeline(json!(["md.parse", { "host": 3 }, "md.stringify"]))?;
    let error = pipeline.process("a", &mut File::new(), Api::Async).err();
    assert_eq!(
        error.map(|error| error.code),
        Some("missing-host".to_string())
    );
    Ok(())
}

#[test]
fn still_checks_the_kinds_around_host_stages() -> Result<(), Error> {
    let mut pipeline = pipeline(json!(["md.parse", { "host": 3 }, "html.stringify"]))?;
    let error = pipeline.freeze().err();
    assert_eq!(
        error.map(|error| error.code),
        Some("invalid-stage".to_string())
    );
    Ok(())
}