rulemorph 0.3.4

YAML-based declarative data transformation engine for CSV/JSON to JSON
Documentation
#[test]
fn html_attr_value_requires_attr_name() {
    let yaml = r#"
version: 2
input:
  format: html
  html:
    records_selector: ".item"
    fields:
      url:
        selector: "a"
        value: attr
mappings:
  - target: "url"
    source: "url"
"#;
    let rule = parse_rule_file(yaml).expect("parse rule");
    let errors = validate_rule_file(&rule).expect_err("missing attr should fail");
    assert!(
        errors
            .iter()
            .any(|err| err.path.as_deref() == Some("input.html.fields.url.attr"))
    );
}

#[test]
fn excel_without_headers_requires_columns() {
    let yaml = r#"
version: 2
input:
  format: excel
  excel:
    has_header: false
mappings:
  - target: "id"
    source: "id"
"#;
    let rule = parse_rule_file(yaml).expect("parse rule");
    let errors = validate_rule_file(&rule).expect_err("missing columns should fail");
    assert!(
        errors
            .iter()
            .any(|err| err.code.as_str() == "MissingExcelColumns"
                && err.path.as_deref() == Some("input.excel.columns"))
    );
}

#[test]
fn excel_rejects_invalid_range_syntax_during_validation() {
    let yaml = r#"
version: 2
input:
  format: excel
  excel:
    range: "B3"
mappings:
  - target: "id"
    source: "id"
"#;
    let rule = parse_rule_file(yaml).expect("parse rule");
    let errors = validate_rule_file(&rule).expect_err("invalid range should fail validation");
    assert!(errors.iter().any(|err| {
        err.code == ErrorCode::InvalidInputOption
            && err.path.as_deref() == Some("input.excel.range")
            && err.message.contains("A:D or A1:D100")
    }));
}

#[test]
fn excel_rejects_numeric_column_reference_during_validation() {
    let yaml = r#"
version: 2
input:
  format: excel
  excel:
    has_header: false
    columns:
      - { name: id, column: "1" }
mappings:
  - target: "id"
    source: "id"
"#;
    let rule = parse_rule_file(yaml).expect("parse rule");
    let errors = validate_rule_file(&rule).expect_err("numeric column should fail validation");
    assert!(errors.iter().any(|err| {
        err.code == ErrorCode::InvalidInputOption
            && err.path.as_deref() == Some("input.excel.columns[0].column")
            && err.message.contains("Excel column reference is invalid")
    }));
}

#[test]
fn excel_rejects_header_row_before_selected_range_during_validation() {
    let yaml = r#"
version: 2
input:
  format: excel
  excel:
    range: "B3:F100"
    header_row: 1
mappings:
  - target: "id"
    source: "id"
"#;
    let rule = parse_rule_file(yaml).expect("parse rule");
    let errors = validate_rule_file(&rule).expect_err("header before range should fail validation");
    assert!(errors.iter().any(|err| {
        err.code == ErrorCode::InvalidInputOption
            && err.path.as_deref() == Some("input.excel.header_row")
            && err.message.contains("before selected range")
    }));
}