qualia_core_db/q42/
yaml_ld_q42.rs1use crate::{q_hash, NQuin};
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug)]
7pub enum YamlToken<'a> {
8 MapStart,
9 MapEnd,
10 ListStart,
11 ListEnd,
12 Key(&'a str),
13 ValueString(&'a str),
14 ValueInt(i64),
15}
16
17pub struct YamlStreamingLexer<'a> {
21 buffer: &'a [u8],
22 cursor: usize,
23}
24
25impl<'a> YamlStreamingLexer<'a> {
26 pub fn new(buffer: &'a [u8]) -> Self {
27 Self { buffer, cursor: 0 }
28 }
29
30 fn skip_ws_and_comments(&mut self) {
31 while self.cursor < self.buffer.len() {
32 let b = self.buffer[self.cursor];
33 if b == b' ' || b == b'\t' || b == b'\r' || b == b'\n' {
34 self.cursor += 1;
35 continue;
36 }
37 if b == b'#' {
38 while self.cursor < self.buffer.len() && self.buffer[self.cursor] != b'\n' {
39 self.cursor += 1;
40 }
41 continue;
42 }
43 break;
44 }
45 }
46
47 pub fn next_token(&mut self) -> Option<YamlToken<'a>> {
49 self.skip_ws_and_comments();
50 if self.cursor >= self.buffer.len() {
51 return None;
52 }
53 if self.buffer[self.cursor] == b'-' {
54 self.cursor += 1;
55 return Some(YamlToken::ListStart);
56 }
57 let start = self.cursor;
58 while self.cursor < self.buffer.len() {
59 let b = self.buffer[self.cursor];
60 if b == b':' || b.is_ascii_whitespace() {
61 break;
62 }
63 self.cursor += 1;
64 }
65 if self.cursor >= self.buffer.len() || self.buffer[self.cursor] != b':' {
66 self.cursor = self.buffer.len();
67 return None;
68 }
69 let key = std::str::from_utf8(&self.buffer[start..self.cursor]).ok()?;
70 self.cursor += 1;
71 Some(YamlToken::Key(key))
72 }
73}
74
75#[derive(Serialize, Deserialize, Debug, Clone)]
77pub struct WebizenWorkspace {
78 pub pages: Vec<Page>,
79}
80
81#[derive(Serialize, Deserialize, Debug, Clone)]
82pub struct Page {
83 pub url_path: String,
84 pub name: String,
85 pub panes: Vec<Pane>,
86}
87
88#[derive(Serialize, Deserialize, Debug, Clone)]
89pub struct Pane {
90 pub component_id: String,
91 pub x: u8,
92 pub y: u8,
93 pub w: u8,
94 pub h: u8,
95}
96
97pub fn compile_yaml_ld_to_quins(
100 yaml_bytes: &[u8],
101 namespace: u64,
102 lamport_clock: u64,
103) -> Result<Vec<NQuin>, &'static str> {
104 let mut lexer = YamlStreamingLexer::new(yaml_bytes);
106 while lexer.next_token().is_some() {}
107
108 let workspace: WebizenWorkspace =
109 serde_yaml::from_slice(yaml_bytes).map_err(|_| "Failed to parse yaml-ld-q42 payload")?;
110
111 let mut quins = Vec::new();
112 let pred_pane = q_hash("q42:SystemPaneState");
113 let pred_page = q_hash("q42:SystemPageDef");
114
115 for page in workspace.pages {
117 let page_hash = q_hash(&page.url_path);
118
119 let page_name_hash = q_hash(&page.name);
121 quins.push(NQuin {
122 subject: page_hash,
123 predicate: pred_page,
124 object: page_name_hash,
125 context: namespace,
126 metadata: lamport_clock << 32,
127 parity: page_hash ^ pred_page ^ page_name_hash ^ namespace,
128 });
129
130 for pane in page.panes {
132 let packed_layout: u64 = ((pane.x as u64) << 24)
134 | ((pane.y as u64) << 16)
135 | ((pane.w as u64) << 8)
136 | (pane.h as u64);
137
138 let metadata = packed_layout | (lamport_clock << 32);
139 let subject = q_hash(&pane.component_id);
140
141 quins.push(NQuin {
149 subject,
150 predicate: pred_pane,
151 object: page_hash, context: namespace,
153 metadata,
154 parity: subject ^ pred_pane ^ page_hash ^ namespace,
155 });
156 }
157 }
158
159 Ok(quins)
160}