hwpforge_core/control/metadata.rs
1//! Annotation metadata types for memo and dutmal controls.
2//!
3//! Houses [`MemoMetadata`], [`DutmalMetadata`], [`DutmalPosition`], and
4//! [`DutmalAlign`] — the wire-mirrored metadata carried by the memo and
5//! dutmal variants of [`Control`](super::Control).
6
7use schemars::JsonSchema;
8use serde::{Deserialize, Serialize};
9
10/// Metadata associated with a memo annotation (HWPX `<hp:parameters>`).
11///
12/// Carries the seven HWPX memo parameters (`Prop`, `Command`, `ID`, `Number`,
13/// `Author`, `MemoShapeIDRef`, `CreateDateTime`). Empty / default values are
14/// rendered by encoders as sensible defaults: `Prop` is always `0` on
15/// 한컴-authored fixtures so the field is omitted on the Core side, and
16/// `CreateDateTime` is auto-generated at encode time if blank (a 한컴-native
17/// memo always has a timestamp).
18///
19/// Other format encoders (Markdown, future ODT) can ignore the
20/// HWPX-specific fields and just use `author` if useful.
21#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
22#[non_exhaustive]
23pub struct MemoMetadata {
24 /// Reference to the `MemoShape` table entry. 한컴 default is `65535`.
25 pub shape_id_ref: u32,
26 /// Memo number (`<hp:integerParam name="Number">`). Equals the HWP5
27 /// `memo_id`. Encoders normally also derive the HWPX `ID` parameter
28 /// from this value (`"memo{number}"`).
29 pub number: u32,
30 /// HWPX `ID` parameter (typically `"memo{number}"`). Encoders may
31 /// auto-derive from `number` when blank.
32 pub id: String,
33 /// Memo author (`Author` parameter). Empty when unknown.
34 pub author: String,
35 /// HWPX `CreateDateTime` parameter (ISO 8601 UTC). Empty triggers
36 /// encoder-side auto-generation at write time.
37 pub create_datetime: String,
38 /// Raw wire `Command` parameter (`"MEMO/{shape}/{number}/…"` from HWP5).
39 /// Empty for HwpForge-authored memos — encoders synthesise a minimum
40 /// `MEMO/{shape}/{number}/` form in that case.
41 pub command: String,
42}
43
44impl Default for MemoMetadata {
45 fn default() -> Self {
46 Self {
47 shape_id_ref: 65535,
48 number: 0,
49 id: String::new(),
50 author: String::new(),
51 create_datetime: String::new(),
52 command: String::new(),
53 }
54 }
55}
56
57impl MemoMetadata {
58 /// Returns the HWPX `ID` parameter, deriving `"memo{number}"` when the
59 /// explicit field is blank.
60 pub fn hwpx_id(&self) -> String {
61 if self.id.is_empty() {
62 format!("memo{}", self.number)
63 } else {
64 self.id.clone()
65 }
66 }
67}
68
69/// Wire-mirrored metadata attached to a `Control::Dutmal`.
70///
71/// Carries HWPX `<hp:dutmal>` attributes that HwpForge does not yet
72/// model as typed fields — currently just `option`. Field is mirrored
73/// verbatim from HWP5 wire / HWPX `option=` attribute and emitted
74/// verbatim on encode so round-trips preserve the value even when the
75/// semantics aren't pinned down (see
76/// `.docs/algorithms/2026-06-01_dutmal_carry.md`).
77///
78/// `#[non_exhaustive]` — additions like `style_id_ref` or the two
79/// reserved tail words are additive and don't break existing
80/// destructure / pattern-match sites.
81#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)]
82#[non_exhaustive]
83pub struct DutmalMetadata {
84 /// `<hp:dutmal option=…>` value mirrored verbatim. Likely a
85 /// bit-field or enum on the HWPX side; HwpForge treats it as an
86 /// opaque u32 until the spec is confirmed.
87 pub option: u32,
88}
89
90/// Position of dutmal annotation text relative to the main text.
91#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)]
92#[non_exhaustive]
93pub enum DutmalPosition {
94 /// Annotation above main text (default).
95 #[default]
96 Top,
97 /// Annotation below main text.
98 Bottom,
99 /// Annotation to the right.
100 Right,
101 /// Annotation to the left.
102 Left,
103}
104
105/// Alignment of dutmal annotation text.
106#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)]
107#[non_exhaustive]
108pub enum DutmalAlign {
109 /// Center-aligned (default).
110 #[default]
111 Center,
112 /// Left-aligned.
113 Left,
114 /// Right-aligned.
115 Right,
116}