sysml-v2-parser 0.23.0

SysML v2 textual notation parser for Rust
Documentation
fn brace_errors(input: &str) -> Vec<(Option<String>, String)> {
    let result = sysml_v2_parser::parse_for_editor(input);
    result
        .errors
        .iter()
        .filter(|d| {
            d.code.as_deref().is_some_and(|c| c.contains("brace"))
                || d.message.contains("brace")
        })
        .map(|d| (d.code.clone(), d.message.clone()))
        .collect()
}

#[test]
fn vacuuming_types_debris_port_legacy_shape() {
    let input = r#"package P {
  port def DebrisPort {
    inout item debris{
      attribute vol:> SI::'m³';
      attribute mass :> SI::kg;
    }
  }
}"#;
    assert!(brace_errors(input).is_empty(), "{:?}", brace_errors(input));
}

#[test]
fn vacuuming_types_suction_and_rotation_ports() {
    let input = r#"package P {
  port def SuctionLevelPort :> BatterySignals::VacuumSystemPowerOutPort {
    out attribute redefines suctionPower :> ISQMechanics::power;
  }
  port def RotationLevelPort :> BatterySignals::VacuumSystemPowerOutPort {
    out attribute redefines brushPower :> ISQMechanics::power;
  }
}"#;
    assert!(brace_errors(input).is_empty(), "{:?}", brace_errors(input));
}

#[test]
fn vacuuming_types_all_ports_without_block_comment() {
    let input = r#"package P {
  package PortDefinitions {
    port def SuctionLevelPort :> BatterySignals::VacuumSystemPowerOutPort {
      out attribute redefines suctionPower :> ISQMechanics::power;
    }
    port def RotationLevelPort :> BatterySignals::VacuumSystemPowerOutPort {
      out attribute redefines brushPower :> ISQMechanics::power;
    }
    port def PowerInOutVac :> Signals::BatterySignals::PowerInOutPort;
    port def AirPort {
      out volume :> ISQSpaceTime::volume;
    }
    port def DebrisPort {
      inout item debris{
        attribute vol:> SI::'m³';
        attribute mass :> SI::kg;
      }
    }
    port def FillStatePort :> NumericSignal;
  }
}"#;
    assert!(
        brace_errors(input).is_empty(),
        "{:?}",
        brace_errors(input)
    );
}

#[test]
fn vacuuming_types_block_comment_with_nested_braces() {
    let input = r#"package P {
  package PortDefinitions {
    port def AirPort {
      out volume :> ISQSpaceTime::volume;
    }
    /*	port def ExternalAirPort {
      in item externalAir{
        attribute volume :> ISQSpaceTime::volume;
      }
    }
    port def InternalAirPort {
      out item internalAir{
        attribute volume :> ISQSpaceTime::volume;
      }
    }*/
    port def DebrisPort {
      inout item debris{
        attribute vol:> SI::'m³';
      }
    }
  }
}"#;
    assert!(
        brace_errors(input).is_empty(),
        "{:?}",
        brace_errors(input)
    );
}

#[test]
fn vacuuming_types_line_comments_with_braces_do_not_break_parse() {
    let input = r#"package P {
  package PortDefinitions {
    port def SuctionLevelPort :> Base {
      out attribute redefines suctionPower :> ISQ::power;
    }
    // {
    //	out item suctionLevel {
    port def AirPort {
      out volume :> ISQSpaceTime::volume;
    }
  }
}"#;
    assert!(brace_errors(input).is_empty(), "{:?}", brace_errors(input));
}

fn vacuuming_types_sysml_path() -> Option<std::path::PathBuf> {
    let root = std::env::var_os("MBSE_VACUUM_EXAMPLE_DIR")?;
    let path = std::path::PathBuf::from(root).join(
        "Functions/legacy/VacuumingSystem/VacuumingTypes.sysml",
    );
    path.exists().then_some(path)
}

#[test]
#[ignore = "requires MBSE_VACUUM_EXAMPLE_DIR pointing at the public example checkout"]
fn vacuuming_types_port_definitions_package_without_block_comment() {
    let Some(path) = vacuuming_types_sysml_path() else {
        return;
    };
    let input = std::fs::read_to_string(&path).expect("read");
    let port_defs_start = input.find("package PortDefinitions").expect("port defs");
    let port_defs_end = input.find("// Interfaces between").expect("interfaces");
    let mut chunk = input[port_defs_start..port_defs_end].to_string();
    while let (Some(start), Some(end)) = (chunk.find("/*"), chunk.find("*/")) {
        chunk.replace_range(start..end + 2, "");
    }
    let wrapped = format!("package VacuumingTypes {{\n{chunk}\n}}");
    let errors = brace_errors(&wrapped);
    assert!(
        errors.is_empty(),
        "port definitions (no block comment) brace errors: {errors:?}"
    );
}

#[test]
#[ignore = "requires MBSE_VACUUM_EXAMPLE_DIR pointing at the public example checkout"]
fn vacuuming_types_port_definitions_package() {
    let Some(path) = vacuuming_types_sysml_path() else {
        return;
    };
    let input = std::fs::read_to_string(&path).expect("read");
    let port_defs_start = input.find("package PortDefinitions").expect("port defs");
    let port_defs_end = input.find("// Interfaces between").expect("interfaces");
    let chunk = format!(
        "package VacuumingTypes {{\n{}\n}}",
        &input[port_defs_start..port_defs_end]
    );
    let errors = brace_errors(&chunk);
    assert!(errors.is_empty(), "port definitions brace errors: {errors:?}");
}