Skip to content

Commit e8d196c

Browse files
authored
Merge pull request #100 from pulseengine/feat/sysml2-roundtrip-phase4-90
feat(spar-sysml2): roundtrip generation — rivet YAML to SysML v2 (Phase 4 of #90)
2 parents b8852a8 + 6839d4b commit e8d196c

3 files changed

Lines changed: 569 additions & 0 deletions

File tree

crates/spar-cli/src/main.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ fn main() {
3939
"codegen" => cmd_codegen(&args[2..]),
4040
"sysml2" => cmd_sysml2(&args[2..]),
4141
"extract" => cmd_sysml2_extract(&args[2..]),
42+
"generate" => cmd_sysml2_generate(&args[2..]),
4243
"lsp" => cmd_lsp(),
4344
other => {
4445
eprintln!("Unknown command: {other}");
@@ -1830,6 +1831,78 @@ fn cmd_sysml2_extract(args: &[String]) {
18301831
}
18311832
}
18321833

1834+
fn cmd_sysml2_generate(args: &[String]) {
1835+
let mut output_path: Option<String> = None;
1836+
let mut files = Vec::new();
1837+
let mut from_rivet = false;
1838+
1839+
let mut i = 0;
1840+
while i < args.len() {
1841+
match args[i].as_str() {
1842+
"--from-rivet" => {
1843+
from_rivet = true;
1844+
}
1845+
"-o" | "--output" => {
1846+
i += 1;
1847+
if i < args.len() {
1848+
output_path = Some(args[i].clone());
1849+
} else {
1850+
eprintln!("-o requires a value");
1851+
process::exit(1);
1852+
}
1853+
}
1854+
s if s.starts_with('-') => {
1855+
eprintln!("Unknown option: {s}");
1856+
process::exit(1);
1857+
}
1858+
s => files.push(s.to_string()),
1859+
}
1860+
i += 1;
1861+
}
1862+
1863+
if !from_rivet {
1864+
eprintln!("Usage: spar sysml2 generate --from-rivet <rivet-yaml-file> [-o output.sysml]");
1865+
process::exit(1);
1866+
}
1867+
1868+
if files.is_empty() {
1869+
eprintln!("Missing rivet YAML file argument");
1870+
process::exit(1);
1871+
}
1872+
1873+
// Read and parse rivet YAML
1874+
let mut all_yaml = String::new();
1875+
for file_path in &files {
1876+
let source = read_file(file_path);
1877+
all_yaml.push_str(&source);
1878+
all_yaml.push('\n');
1879+
}
1880+
1881+
let artifacts = spar_sysml2::generate::parse_rivet_yaml(&all_yaml);
1882+
if artifacts.is_empty() {
1883+
eprintln!("No artifacts found in rivet YAML");
1884+
process::exit(1);
1885+
}
1886+
1887+
let sysml = spar_sysml2::generate::generate_sysml2(&artifacts);
1888+
1889+
match output_path {
1890+
Some(path) => {
1891+
fs::write(&path, &sysml).unwrap_or_else(|e| {
1892+
eprintln!("Cannot write {path}: {e}");
1893+
process::exit(1);
1894+
});
1895+
eprintln!(
1896+
"Generated SysML v2 from {} artifacts → {path}",
1897+
artifacts.len()
1898+
);
1899+
}
1900+
None => {
1901+
print!("{sysml}");
1902+
}
1903+
}
1904+
}
1905+
18331906
fn cmd_sysml2(args: &[String]) {
18341907
if args.is_empty() {
18351908
eprintln!("Usage: spar sysml2 <subcommand> [options] <file>");
@@ -1838,13 +1911,15 @@ fn cmd_sysml2(args: &[String]) {
18381911
eprintln!(" parse Parse a SysML v2 file and show the syntax tree");
18391912
eprintln!(" lower Lower SysML v2 to AADL");
18401913
eprintln!(" extract Extract requirements to rivet YAML");
1914+
eprintln!(" generate Generate SysML v2 from rivet YAML (--from-rivet)");
18411915
process::exit(1);
18421916
}
18431917

18441918
match args[0].as_str() {
18451919
"parse" => cmd_sysml2_parse(&args[1..]),
18461920
"lower" => cmd_sysml2_lower(&args[1..]),
18471921
"extract" => cmd_sysml2_extract(&args[1..]),
1922+
"generate" => cmd_sysml2_generate(&args[1..]),
18481923
other => {
18491924
eprintln!("Unknown sysml2 subcommand: {other}");
18501925
process::exit(1);

0 commit comments

Comments
 (0)