Skip to main content

blockworx/doc_ng/
command.rs

1//! The log vocabulary: per-kind write enums of `Swap { old, new }` pairs,
2//! monomorphized over entity kinds by the closed [`Command`] enum.
3//! Rationale — the entity-kind typing axis, init totality, the
4//! compound-operation rule, serialization longevity: `docs/doc-ng-design-notes.md`.
5
6use serde::{Deserialize, Serialize};
7
8use crate::doc_ng::{
9    block_model::{Icon, Waypoint},
10    geometry::{FracVal, GridPoint, GridRect, ScreenRect},
11    hash::AssetHash,
12    id::{BlockId, CommentId, ImageId, PinId, RouteId, RouteLabelId, TextId},
13    values::{LabelSide, PinDir, Role},
14};
15
16#[derive(Serialize, Deserialize, Debug, Clone)]
17pub enum OpCode<W, I> {
18    /// Full initial state; fresh random UUID minted by the author, so
19    /// creates never conflict.
20    Create(I),
21    Set(W),
22    /// Tombstone — the inner is retained for restore.
23    Delete,
24    Restore,
25}
26
27impl<W: Invert, I> OpCode<W, I> {
28    pub fn invert(self) -> Option<OpCode<W, I>> {
29        match self {
30            OpCode::Create(_) | OpCode::Restore => Some(OpCode::Delete),
31            OpCode::Set(w) => Some(OpCode::Set(w.invert())),
32            OpCode::Delete => Some(OpCode::Restore),
33        }
34    }
35}
36
37/// Per-variant old/new swap. Property: `fold(fold(s, c), invert(c)) == s`.
38pub trait Invert {
39    #[must_use]
40    fn invert(self) -> Self;
41}
42
43#[derive(Serialize, Deserialize, Debug, Clone)]
44pub struct Swap<T> {
45    pub old: T,
46    pub new: T,
47}
48
49impl<T> Swap<T> {
50    #[must_use]
51    pub fn invert(self) -> Self {
52        Swap {
53            old: self.new,
54            new: self.old,
55        }
56    }
57}
58
59// ---------------------------------------------------------------------------
60// Write vocabularies — mirror Register placement 1:1
61// ---------------------------------------------------------------------------
62
63#[derive(Serialize, Deserialize, Debug, Clone)]
64pub enum DocumentWrite {
65    Name(Swap<String>),
66}
67
68#[derive(Serialize, Deserialize, Debug, Clone)]
69pub enum LabelWrite {
70    Name(Swap<String>),
71    Side(Swap<LabelSide>),
72    Offset(Swap<FracVal>),
73    Hidden(Swap<bool>),
74}
75
76#[derive(Serialize, Deserialize, Debug, Clone)]
77pub enum BlockWrite {
78    Parent(Swap<BlockId>),
79    Rect(Swap<GridRect>),
80    Locked(Swap<bool>),
81    Title(LabelWrite),
82    TypeLabel(LabelWrite),
83    Icon(Swap<Icon>),
84}
85
86#[derive(Serialize, Deserialize, Debug, Clone)]
87pub enum PinWrite {
88    Owner(Swap<BlockId>),
89    Name(Swap<String>),
90    TypeName(Swap<String>),
91    Tag(Swap<String>),
92    TagHidden(Swap<bool>),
93    Rect(Swap<GridRect>),
94    Dir(Swap<PinDir>),
95    PinAccent(Swap<Role>),
96    PortAccent(Swap<Role>),
97    PortPinAccent(Swap<Role>),
98    FlipLR(Swap<bool>),
99}
100
101#[derive(Serialize, Deserialize, Debug, Clone)]
102pub enum RouteWrite {
103    Owner(Swap<BlockId>),
104    Name(Swap<String>),
105    Role(Swap<Role>),
106    Waypoints(Swap<Vec<Waypoint>>),
107}
108
109#[derive(Serialize, Deserialize, Debug, Clone)]
110pub enum RouteLabelWrite {
111    Owner(Swap<RouteId>),
112    Pos(Swap<FracVal>),
113}
114
115#[derive(Serialize, Deserialize, Debug, Clone)]
116pub enum TextWrite {
117    Owner(Swap<BlockId>),
118    Text(Swap<String>),
119    Pos(Swap<GridPoint>),
120    Role(Swap<Role>),
121}
122
123#[derive(Serialize, Deserialize, Debug, Clone)]
124pub enum CommentWrite {
125    Owner(Swap<BlockId>),
126    Rect(Swap<GridRect>),
127    Role(Swap<Role>),
128    Title(LabelWrite),
129}
130
131#[derive(Serialize, Deserialize, Debug, Clone)]
132pub enum ImageWrite {
133    Owner(Swap<BlockId>),
134    Asset(Swap<AssetHash>),
135    Rect(Swap<ScreenRect>),
136}
137
138// ---------------------------------------------------------------------------
139// Init shapes — the full initial value per kind, stamp-free
140// ---------------------------------------------------------------------------
141
142#[derive(Serialize, Deserialize, Debug, Clone)]
143pub struct LabelInit {
144    pub name: String,
145    pub side: LabelSide,
146    pub offset: FracVal,
147    pub hidden: bool,
148}
149
150#[derive(Serialize, Deserialize, Debug, Clone)]
151pub struct BlockInit {
152    /// `Id::NULL` = document level.
153    pub parent: BlockId,
154    pub rect: GridRect,
155    pub locked: bool,
156    pub title: LabelInit,
157    pub type_label: LabelInit,
158    pub icon: Icon,
159}
160
161#[derive(Serialize, Deserialize, Debug, Clone)]
162pub struct PinInit {
163    pub owner: BlockId,
164    pub name: String,
165    pub type_name: String,
166    pub tag: String,
167    pub tag_hidden: bool,
168    pub rect: GridRect,
169    pub dir: PinDir,
170    pub pin_accent: Role,
171    pub port_accent: Role,
172    pub port_pin_accent: Role,
173    pub flip_lr: bool,
174}
175
176#[derive(Serialize, Deserialize, Debug, Clone)]
177pub struct RouteInit {
178    pub owner: BlockId,
179    pub name: String,
180    pub from: PinId,
181    pub to: PinId,
182    pub role: Role,
183    pub waypoints: Vec<Waypoint>,
184}
185
186#[derive(Serialize, Deserialize, Debug, Clone)]
187pub struct RouteLabelInit {
188    pub owner: RouteId,
189    pub pos: FracVal,
190}
191
192#[derive(Serialize, Deserialize, Debug, Clone)]
193pub struct TextInit {
194    pub owner: BlockId,
195    pub text: String,
196    pub pos: GridPoint,
197    pub role: Role,
198}
199
200#[derive(Serialize, Deserialize, Debug, Clone)]
201pub struct CommentInit {
202    pub owner: BlockId,
203    pub rect: GridRect,
204    pub role: Role,
205    pub title: LabelInit,
206}
207
208#[derive(Serialize, Deserialize, Debug, Clone)]
209pub struct ImageInit {
210    pub owner: BlockId,
211    pub asset: AssetHash,
212    pub rect: ScreenRect,
213}
214
215#[derive(Serialize, Deserialize, Debug, Clone)]
216pub enum Command {
217    /// Singleton: no id, no lifecycle — `Set` only.
218    Document(DocumentWrite),
219    Block(BlockId, OpCode<BlockWrite, BlockInit>),
220    Pin(PinId, OpCode<PinWrite, PinInit>),
221    Route(RouteId, OpCode<RouteWrite, RouteInit>),
222    RouteLabel(RouteLabelId, OpCode<RouteLabelWrite, RouteLabelInit>),
223    Text(TextId, OpCode<TextWrite, TextInit>),
224    Comment(CommentId, OpCode<CommentWrite, CommentInit>),
225    Image(ImageId, OpCode<ImageWrite, ImageInit>),
226}