Skip to main content

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}