Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions plantuml/parser/puml_cli/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ rust_binary(
visibility = ["//visibility:public"],
deps = [
"//plantuml/parser/puml_fta",
"//plantuml/parser/puml_idmap",
"//plantuml/parser/puml_lobster",
"//plantuml/parser/puml_parser",
"//plantuml/parser/puml_resolver",
Expand All @@ -45,6 +46,7 @@ rust_test(
crate_root = "src/main.rs",
deps = [
"//plantuml/parser/puml_fta",
"//plantuml/parser/puml_idmap",
"//plantuml/parser/puml_lobster",
"//plantuml/parser/puml_parser",
"//plantuml/parser/puml_resolver",
Expand Down
111 changes: 98 additions & 13 deletions plantuml/parser/puml_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use component_serializer::ComponentSerializer;
use sequence_serializer::SequenceSerializer;

use puml_fta::{lobster_document, FtaChain, FtaModel};
use puml_idmap::{write_empty_idmap_to_file, write_idmap_to_file, IdMapModel};
use puml_lobster::{write_lobster_to_file, LobsterModel};
use puml_parser::{
DiagramParser, ErrorLocation, Preprocessor, ProcedureParserService, PumlActivityParser,
Expand Down Expand Up @@ -109,6 +110,13 @@ struct Args {
/// processing is performed in this mode.
#[arg(long)]
fta_output_dir: Option<String>,

/// Output directory for generated idmap sidecar files (optional).
/// When set, a <stem>.idmap.json is written for each resolved diagram,
/// recording the defines/references used by the clickable_plantuml
/// Sphinx extension to resolve cross-diagram links.
#[arg(long)]
idmap_output_dir: Option<String>,
}

#[derive(Copy, Clone, ValueEnum, Debug)]
Expand Down Expand Up @@ -167,13 +175,20 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
None
};

let lobster_output_dir: Option<PathBuf> = match &args.lobster_output_dir {
Some(dir) => {
let p = PathBuf::from(dir);
fs::create_dir_all(&p)?;
Some(p)
}
None => None,
let lobster_output_dir: Option<PathBuf> = if let Some(dir) = &args.lobster_output_dir {
let p = PathBuf::from(dir);
fs::create_dir_all(&p)?;
Some(p)
} else {
None
};

let idmap_output_dir: Option<PathBuf> = if let Some(dir) = &args.idmap_output_dir {
let p = PathBuf::from(dir);
fs::create_dir_all(&p)?;
Some(p)
} else {
None
};

let file_list = collect_files_from_args(&args)?;
Expand All @@ -197,6 +212,13 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
}
}

// Capture the @startuml <name> before resolve_parsed_diagram
// consumes parsed_content — the resolver discards it from the map.
let diagram_name: Option<String> = match &parsed_content {
ParsedDiagram::Component(doc) => doc.name.clone(),
_ => None,
};

match resolve_parsed_diagram(parsed_content) {
Ok(logic_result) => {
debug!(
Expand All @@ -209,11 +231,8 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
}
}

let source_file = path
.file_name()
.and_then(|n| n.to_str())
.unwrap_or_default();
let fbs_buffer = serialize_resolved_diagram(&logic_result, source_file);
let source_file = source_path_for_output(path);
let fbs_buffer = serialize_resolved_diagram(&logic_result, &source_file);
if let Some(ref dir) = fbs_output_dir {
write_fbs_to_file(&fbs_buffer, path, dir)?;
}
Expand All @@ -225,7 +244,42 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
ResolvedDiagram::Activity(_) => LobsterModel::Empty,
ResolvedDiagram::Sequence(_) => LobsterModel::Empty,
};
write_lobster_to_file(lobster_model, path, ldir)?;
write_lobster_to_file(lobster_model, path, &source_file, ldir)?;
}

if let Some(idir) = &idmap_output_dir {
match &logic_result {
ResolvedDiagram::Component(model) => {
write_idmap_to_file(
IdMapModel::Component(model),
path,
Some(&source_file),
diagram_name.as_deref(),
idir,
)?;
}
ResolvedDiagram::Class(model) => {
write_idmap_to_file(
IdMapModel::Class(model),
path,
Some(&source_file),
diagram_name.as_deref(),
idir,
)?;
}
ResolvedDiagram::Sequence(model) => {
write_idmap_to_file(
IdMapModel::Sequence(model),
path,
Some(&source_file),
diagram_name.as_deref(),
idir,
)?;
}
ResolvedDiagram::Activity(_) => {
write_empty_idmap_to_file(path, Some(&source_file), idir)?;
}
}
}
}
Err(e) => {
Expand Down Expand Up @@ -295,6 +349,20 @@ fn run_fta(
let model = FtaModel::from_procedure_file(&parsed)?;
all_items.extend(model.lobster_items(basename));
all_chains.extend(model.chains(basename));

if let Some(ref idir_str) = args.idmap_output_dir {
let idir = PathBuf::from(idir_str);
fs::create_dir_all(&idir)?;
let source_file = source_path_for_output(file);
write_idmap_to_file(
IdMapModel::Fta(&model),
file,
Some(&source_file),
None,
&idir,
)?;
}

debug!("Processed FTA diagram: {}", file.display());
}

Expand Down Expand Up @@ -552,6 +620,23 @@ fn resolve_path(path: &Path) -> PathBuf {
}
}

fn source_path_for_output(path: &Path) -> String {
let workspace_root = std::env::var("BUILD_WORKSPACE_DIRECTORY")
.ok()
.map(PathBuf::from)
.or_else(|| std::env::current_dir().ok());

match workspace_root {
Some(root) => path
.strip_prefix(root)
.ok()
.and_then(Path::to_str)
.map(str::to_owned)
.unwrap_or_else(|| path.to_string_lossy().into_owned()),
None => path.to_string_lossy().into_owned(),
}
}

fn add_single_file(
path: &Path,
file_list: &mut HashSet<Rc<PathBuf>>,
Expand Down
39 changes: 39 additions & 0 deletions plantuml/parser/puml_idmap/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# *******************************************************************************
# Copyright (c) 2026 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************

load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test")

rust_library(
name = "puml_idmap",
srcs = ["src/lib.rs"],
visibility = ["//plantuml/parser:__subpackages__"],
deps = [
"//plantuml/parser/puml_fta",
"//tools/metamodel/class:class_diagram",
"//tools/metamodel/component:component_diagram",
"//tools/metamodel/sequence:sequence_diagram",
"@crates//:log",
"@crates//:serde",
"@crates//:serde_json",
],
)

rust_test(
name = "puml_idmap_test",
crate = ":puml_idmap",
deps = [
"//tools/metamodel/class:class_diagram",
"//tools/metamodel/component:component_diagram",
"//tools/metamodel/sequence:sequence_diagram",
],
)
Loading
Loading