1mod api;
10mod character;
11mod emotion;
12mod encryption;
13mod function;
14mod image;
15mod lifecycle;
16mod orchestration;
17mod text;
18mod todo;
19
20pub use api::{ApiBlock, ApiEndpoint};
21pub use character::CharacterBlock;
22pub use emotion::{EmotionBlock, EmotionState};
23pub use encryption::{EncryptionAlgorithm, EncryptionBlock};
24pub use function::{FunctionBlock, FunctionDef, FunctionKind};
25pub use image::{ImageBlock, SpriteAtlas};
26pub use lifecycle::{LifecycleBlock, LifecycleState, LifecycleTransition};
27pub use orchestration::{DirectiveKind, OrchestrationBlock, OrchestrationDirective};
28pub use text::TextBlock;
29pub use todo::{TodoBlock, TodoItem, TodoStatus};
30
31use serde::{Deserialize, Serialize};
32
33use crate::error::Result;
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
39#[serde(rename_all = "lowercase")]
40pub enum BlockTag {
41 Txt,
43 Img,
45 Tdo,
47 Fnc,
49 Api,
51 Lfc,
53 Chr,
55 Emo,
57 Enc,
59 Orc,
61}
62
63impl BlockTag {
64 pub const ALL: [BlockTag; 10] = [
66 BlockTag::Txt,
67 BlockTag::Img,
68 BlockTag::Tdo,
69 BlockTag::Fnc,
70 BlockTag::Api,
71 BlockTag::Lfc,
72 BlockTag::Chr,
73 BlockTag::Emo,
74 BlockTag::Enc,
75 BlockTag::Orc,
76 ];
77
78 #[must_use]
80 pub fn tag_bytes(self) -> [u8; 3] {
81 match self {
82 BlockTag::Txt => *b"txt",
83 BlockTag::Img => *b"img",
84 BlockTag::Tdo => *b"tdo",
85 BlockTag::Fnc => *b"fnc",
86 BlockTag::Api => *b"api",
87 BlockTag::Lfc => *b"lfc",
88 BlockTag::Chr => *b"chr",
89 BlockTag::Emo => *b"emo",
90 BlockTag::Enc => *b"enc",
91 BlockTag::Orc => *b"orc",
92 }
93 }
94
95 #[must_use]
97 pub fn index(self) -> u8 {
98 match self {
99 BlockTag::Txt => 0,
100 BlockTag::Img => 1,
101 BlockTag::Tdo => 2,
102 BlockTag::Fnc => 3,
103 BlockTag::Api => 4,
104 BlockTag::Lfc => 5,
105 BlockTag::Chr => 6,
106 BlockTag::Emo => 7,
107 BlockTag::Enc => 8,
108 BlockTag::Orc => 9,
109 }
110 }
111
112 #[must_use]
114 pub fn from_index(index: u8) -> Option<Self> {
115 BlockTag::ALL.iter().copied().find(|t| t.index() == index)
116 }
117
118 #[must_use]
120 pub fn from_tag_bytes(tag: &[u8; 3]) -> Option<Self> {
121 BlockTag::ALL.iter().copied().find(|t| &t.tag_bytes() == tag)
122 }
123}
124
125#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
127#[serde(tag = "type", rename_all = "lowercase")]
128pub enum Block {
129 Txt(TextBlock),
131 Img(ImageBlock),
133 Tdo(TodoBlock),
135 Fnc(FunctionBlock),
137 Api(ApiBlock),
139 Lfc(LifecycleBlock),
141 Chr(CharacterBlock),
143 Emo(EmotionBlock),
145 Enc(EncryptionBlock),
147 Orc(OrchestrationBlock),
149}
150
151impl Block {
152 #[must_use]
154 pub fn tag(&self) -> BlockTag {
155 match self {
156 Block::Txt(_) => BlockTag::Txt,
157 Block::Img(_) => BlockTag::Img,
158 Block::Tdo(_) => BlockTag::Tdo,
159 Block::Fnc(_) => BlockTag::Fnc,
160 Block::Api(_) => BlockTag::Api,
161 Block::Lfc(_) => BlockTag::Lfc,
162 Block::Chr(_) => BlockTag::Chr,
163 Block::Emo(_) => BlockTag::Emo,
164 Block::Enc(_) => BlockTag::Enc,
165 Block::Orc(_) => BlockTag::Orc,
166 }
167 }
168
169 pub fn payload(&self) -> Result<Vec<u8>> {
172 let json = match self {
173 Block::Txt(b) => serde_json::to_vec(b),
174 Block::Img(b) => serde_json::to_vec(b),
175 Block::Tdo(b) => serde_json::to_vec(b),
176 Block::Fnc(b) => serde_json::to_vec(b),
177 Block::Api(b) => serde_json::to_vec(b),
178 Block::Lfc(b) => serde_json::to_vec(b),
179 Block::Chr(b) => serde_json::to_vec(b),
180 Block::Emo(b) => serde_json::to_vec(b),
181 Block::Enc(b) => serde_json::to_vec(b),
182 Block::Orc(b) => serde_json::to_vec(b),
183 }?;
184 Ok(json)
185 }
186
187 pub fn from_payload(tag: BlockTag, bytes: &[u8]) -> Result<Block> {
189 let block = match tag {
190 BlockTag::Txt => Block::Txt(serde_json::from_slice(bytes)?),
191 BlockTag::Img => Block::Img(serde_json::from_slice(bytes)?),
192 BlockTag::Tdo => Block::Tdo(serde_json::from_slice(bytes)?),
193 BlockTag::Fnc => Block::Fnc(serde_json::from_slice(bytes)?),
194 BlockTag::Api => Block::Api(serde_json::from_slice(bytes)?),
195 BlockTag::Lfc => Block::Lfc(serde_json::from_slice(bytes)?),
196 BlockTag::Chr => Block::Chr(serde_json::from_slice(bytes)?),
197 BlockTag::Emo => Block::Emo(serde_json::from_slice(bytes)?),
198 BlockTag::Enc => Block::Enc(serde_json::from_slice(bytes)?),
199 BlockTag::Orc => Block::Orc(serde_json::from_slice(bytes)?),
200 };
201 Ok(block)
202 }
203
204 #[must_use]
206 pub fn as_encryption(&self) -> Option<&EncryptionBlock> {
207 match self {
208 Block::Enc(b) => Some(b),
209 _ => None,
210 }
211 }
212}
213
214pub(crate) fn validate_block(block: &Block) -> Result<()> {
216 if let Block::Img(img) = block {
217 img.atlas.validate()?;
218 }
219 Ok(())
220}