Skip to content

Commit 61d13e0

Browse files
committed
Add SequenceSerializer
1 parent 0d083ea commit 61d13e0

7 files changed

Lines changed: 303 additions & 0 deletions

File tree

plantuml/parser/puml_serializer/BUILD

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ rust_library(
1919
deps = [
2020
":class_serializer",
2121
":component_serializer",
22+
":sequence_serializer",
2223
],
2324
)
2425

@@ -33,3 +34,9 @@ alias(
3334
actual = "//plantuml/parser/puml_serializer/src/serialize:puml_serialize_class",
3435
visibility = ["//plantuml/parser:__subpackages__"],
3536
)
37+
38+
alias(
39+
name = "sequence_serializer",
40+
actual = "//plantuml/parser/puml_serializer/src/serialize:puml_serialize_sequence",
41+
visibility = ["//plantuml/parser:__subpackages__"],
42+
)

plantuml/parser/puml_serializer/src/fbs/BUILD

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,35 @@ rust_library(
7777
"@crates//:flatbuffers",
7878
],
7979
)
80+
81+
flatbuffer_library_public(
82+
name = "sequence_fbs_codegen",
83+
srcs = [
84+
"//tools/metamodel:sequence_schemas",
85+
],
86+
outs = [
87+
"sequence_diagram_generated.rs",
88+
],
89+
flatc_args = [],
90+
language_flag = "--rust",
91+
visibility = ["//plantuml/parser:__subpackages__"],
92+
)
93+
94+
rust_library(
95+
name = "sequence_fbs",
96+
srcs = [
97+
":sequence_fbs_codegen",
98+
],
99+
rustc_flags = [
100+
"--allow=unused_imports",
101+
"--allow=clippy::extra-unused-lifetimes",
102+
"--allow=clippy::missing-safety-doc",
103+
"--allow=clippy::needless-lifetimes",
104+
],
105+
visibility = [
106+
"//plantuml/parser:__subpackages__",
107+
],
108+
deps = [
109+
"@crates//:flatbuffers",
110+
],
111+
)

plantuml/parser/puml_serializer/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@
1313

1414
pub use class_serializer::ClassSerializer;
1515
pub use component_serializer::ComponentSerializer;
16+
pub use sequence_serializer::SequenceSerializer;

plantuml/parser/puml_serializer/src/serialize/BUILD

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,18 @@ rust_library(
4141
"@crates//:flatbuffers",
4242
],
4343
)
44+
45+
rust_library(
46+
name = "puml_serialize_sequence",
47+
srcs = [
48+
"sequence_serializer.rs",
49+
],
50+
crate_name = "sequence_serializer",
51+
crate_root = "sequence_serializer.rs",
52+
visibility = ["//plantuml/parser:__subpackages__"],
53+
deps = [
54+
"//plantuml/parser/puml_serializer/src/fbs:sequence_fbs",
55+
"//tools/metamodel:sequence_diagram",
56+
"@crates//:flatbuffers",
57+
],
58+
)
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// *******************************************************************************
2+
// Copyright (c) 2026 Contributors to the Eclipse Foundation
3+
//
4+
// See the NOTICE file(s) distributed with this work for additional
5+
// information regarding copyright ownership.
6+
//
7+
// This program and the accompanying materials are made available under the
8+
// terms of the Apache License Version 2.0 which is available at
9+
// <https://www.apache.org/licenses/LICENSE-2.0>
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
// *******************************************************************************
13+
14+
use flatbuffers::FlatBufferBuilder;
15+
use sequence_fbs::sequence_metamodel as fb;
16+
use sequence_logic::{ConditionType, Event, SequenceNode};
17+
18+
pub struct SequenceSerializer;
19+
20+
impl SequenceSerializer {
21+
pub fn serialize(
22+
nodes: &[SequenceNode],
23+
name: Option<&str>,
24+
source_files: &[String],
25+
version: Option<&str>,
26+
) -> Vec<u8> {
27+
let mut builder = FlatBufferBuilder::new();
28+
29+
let name_offset = name.map(|n| builder.create_string(n));
30+
31+
let node_offsets: Vec<_> = nodes
32+
.iter()
33+
.map(|node| Self::serialize_node(&mut builder, node))
34+
.collect();
35+
let nodes_offset = builder.create_vector(&node_offsets);
36+
37+
let source_offsets: Vec<_> = source_files
38+
.iter()
39+
.map(|s| builder.create_string(s))
40+
.collect();
41+
let source_files_offset = builder.create_vector(&source_offsets);
42+
43+
let version_offset = version.map(|v| builder.create_string(v));
44+
45+
let root = fb::SequenceDiagram::create(
46+
&mut builder,
47+
&fb::SequenceDiagramArgs {
48+
name: name_offset,
49+
root_interactions: Some(nodes_offset),
50+
source_files: Some(source_files_offset),
51+
version: version_offset,
52+
},
53+
);
54+
55+
builder.finish(root, Some("SEQD"));
56+
builder.finished_data().to_vec()
57+
}
58+
59+
fn serialize_node<'a>(
60+
builder: &mut FlatBufferBuilder<'a>,
61+
node: &SequenceNode,
62+
) -> flatbuffers::WIPOffset<fb::SequenceNode<'a>> {
63+
// Recursively serialize child nodes first (depth-first).
64+
let branch_offsets: Vec<_> = node
65+
.branches_node
66+
.iter()
67+
.map(|child| Self::serialize_node(builder, child))
68+
.collect();
69+
let branches_offset = builder.create_vector(&branch_offsets);
70+
71+
// Serialize the event union.
72+
let (event_type, event_offset) = Self::serialize_event(builder, &node.event);
73+
74+
fb::SequenceNode::create(
75+
builder,
76+
&fb::SequenceNodeArgs {
77+
event_type,
78+
event: Some(event_offset),
79+
branches_node: Some(branches_offset),
80+
},
81+
)
82+
}
83+
84+
fn serialize_event<'a>(
85+
builder: &mut FlatBufferBuilder<'a>,
86+
event: &Event,
87+
) -> (fb::Event, flatbuffers::WIPOffset<flatbuffers::UnionWIPOffset>) {
88+
match event {
89+
Event::Interaction(interaction) => {
90+
let caller = builder.create_string(&interaction.caller);
91+
let callee = builder.create_string(&interaction.callee);
92+
let method = builder.create_string(&interaction.method);
93+
let offset = fb::Interaction::create(
94+
builder,
95+
&fb::InteractionArgs {
96+
caller: Some(caller),
97+
callee: Some(callee),
98+
method: Some(method),
99+
},
100+
);
101+
(fb::Event::Interaction, offset.as_union_value())
102+
}
103+
Event::Return(ret) => {
104+
let caller = builder.create_string(&ret.caller);
105+
let callee = builder.create_string(&ret.callee);
106+
let return_content = builder.create_string(&ret.return_content);
107+
let offset = fb::Return::create(
108+
builder,
109+
&fb::ReturnArgs {
110+
caller: Some(caller),
111+
callee: Some(callee),
112+
return_content: Some(return_content),
113+
},
114+
);
115+
(fb::Event::Return, offset.as_union_value())
116+
}
117+
Event::Condition(cond) => {
118+
let condition_value = builder.create_string(&cond.condition_value);
119+
let offset = fb::Condition::create(
120+
builder,
121+
&fb::ConditionArgs {
122+
condition_type: Self::map_condition_type(cond.condition_type.clone()),
123+
condition_value: Some(condition_value),
124+
},
125+
);
126+
(fb::Event::Condition, offset.as_union_value())
127+
}
128+
}
129+
}
130+
131+
fn map_condition_type(ct: ConditionType) -> fb::ConditionType {
132+
match ct {
133+
ConditionType::Opt => fb::ConditionType::Opt,
134+
ConditionType::Alt => fb::ConditionType::Alt,
135+
ConditionType::Loop => fb::ConditionType::Loop,
136+
ConditionType::Par => fb::ConditionType::Par,
137+
ConditionType::Par2 => fb::ConditionType::Par2,
138+
ConditionType::Break => fb::ConditionType::Break,
139+
ConditionType::Critical => fb::ConditionType::Critical,
140+
ConditionType::Else => fb::ConditionType::Else,
141+
ConditionType::Also => fb::ConditionType::Also,
142+
ConditionType::End => fb::ConditionType::End,
143+
ConditionType::Group => fb::ConditionType::Group,
144+
}
145+
}
146+
}

tools/metamodel/BUILD

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,8 @@ filegroup(
5353
name = "schemas",
5454
srcs = ["schema/class_diagram.fbs"],
5555
)
56+
57+
filegroup(
58+
name = "sequence_schemas",
59+
srcs = ["schema/sequence_diagram.fbs"],
60+
)
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
///////////////////////////////////////////////////////////////////////////////////
2+
// Copyright (c) 2026 Contributors to the Eclipse Foundation
3+
//
4+
// See the NOTICE file(s) distributed with this work for additional
5+
// information regarding copyright ownership.
6+
//
7+
// This program and the accompanying materials are made available under the
8+
// terms of the Apache License Version 2.0 which is available at
9+
// https://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
////////////////////////////////////////////////////////////////////////////////////
13+
14+
namespace sequence_metamodel;
15+
16+
/// The kind of condition / group block in a sequence diagram.
17+
enum ConditionType : byte {
18+
Opt = 0,
19+
Alt = 1,
20+
Loop = 2,
21+
Par = 3,
22+
Par2 = 4,
23+
Break = 5,
24+
Critical = 6,
25+
Else = 7,
26+
Also = 8,
27+
End = 9,
28+
Group = 10
29+
}
30+
31+
/// The discriminator for the event union.
32+
enum EventType : byte {
33+
NONE = 0,
34+
Interaction = 1,
35+
Return = 2,
36+
Condition = 3
37+
}
38+
39+
/// A method-call interaction between two participants.
40+
table Interaction {
41+
/// Sending participant
42+
caller: string (required);
43+
/// Receiving participant
44+
callee: string (required);
45+
/// Method or message label
46+
method: string;
47+
}
48+
49+
/// A return message between two participants.
50+
table Return {
51+
/// Original caller (receiver of the return)
52+
caller: string (required);
53+
/// Original callee (sender of the return)
54+
callee: string (required);
55+
/// Return value / label
56+
return_content: string;
57+
}
58+
59+
/// A condition / group block header.
60+
table Condition {
61+
/// Kind of block (alt, loop, opt, …)
62+
condition_type: ConditionType = Alt;
63+
/// Guard expression or label text
64+
condition_value: string;
65+
}
66+
67+
/// Union of possible events carried by a sequence node.
68+
union Event {
69+
Interaction,
70+
Return,
71+
Condition
72+
}
73+
74+
/// A node in the hierarchical sequence-diagram logic tree.
75+
table SequenceNode {
76+
/// The event at this node
77+
event: Event;
78+
/// Child nodes (nested calls, branches, …)
79+
branches_node: [SequenceNode];
80+
}
81+
82+
/// Root container for a sequence-diagram logic tree.
83+
table SequenceDiagram {
84+
/// Diagram name (usually the source file)
85+
name: string;
86+
/// Top-level sequence nodes
87+
root_interactions: [SequenceNode];
88+
/// Source files parsed
89+
source_files: [string];
90+
/// Parser/writer version
91+
version: string;
92+
}
93+
94+
root_type SequenceDiagram;
95+
96+
file_identifier "SEQD";
97+
file_extension "seqd";

0 commit comments

Comments
 (0)