combs_mesh/engine/
builder.rs1use crate::blocks::{
15 Block, EncryptionBlock, ImageBlock, LifecycleBlock, LifecycleState, LifecycleTransition,
16 SpriteAtlas, TextBlock, TodoBlock, TodoItem, TodoStatus,
17};
18
19#[derive(Debug, Clone, PartialEq)]
24pub struct Emoji {
25 pub name: String,
27 pub blocks: Vec<Block>,
29}
30
31impl Emoji {
32 #[must_use]
34 pub fn get_text(&self) -> Option<&TextBlock> {
35 self.blocks.iter().find_map(|b| match b {
36 Block::Txt(t) => Some(t),
37 _ => None,
38 })
39 }
40
41 #[must_use]
43 pub fn get_image(&self) -> Option<&ImageBlock> {
44 self.blocks.iter().find_map(|b| match b {
45 Block::Img(i) => Some(i),
46 _ => None,
47 })
48 }
49
50 #[must_use]
52 pub fn get_encryption(&self) -> Option<&EncryptionBlock> {
53 self.blocks.iter().find_map(Block::as_encryption)
54 }
55
56 pub fn iter(&self) -> impl Iterator<Item = &Block> {
58 self.blocks.iter()
59 }
60
61 pub(crate) fn from_blocks(blocks: Vec<Block>) -> Emoji {
64 let name = blocks
65 .iter()
66 .find_map(|b| match b {
67 Block::Txt(t) => Some(t.name.clone()),
68 _ => None,
69 })
70 .unwrap_or_default();
71 Emoji { name, blocks }
72 }
73}
74
75#[derive(Debug, Clone)]
79pub struct EmojiBuilder {
80 emoji: Emoji,
81}
82
83impl EmojiBuilder {
84 #[must_use]
87 pub fn new(name: &str) -> Self {
88 EmojiBuilder {
89 emoji: Emoji {
90 name: name.to_string(),
91 blocks: vec![Block::Txt(TextBlock {
92 name: name.to_string(),
93 description: String::new(),
94 specs: Vec::new(),
95 })],
96 },
97 }
98 }
99
100 #[must_use]
102 pub fn description(mut self, description: &str) -> Self {
103 if let Some(Block::Txt(t)) = self
104 .emoji
105 .blocks
106 .iter_mut()
107 .find(|b| matches!(b, Block::Txt(_)))
108 {
109 t.description = description.to_string();
110 }
111 self
112 }
113
114 #[must_use]
117 pub fn add_todo(mut self, key: &str, value: &str) -> Self {
118 let item = TodoItem {
119 key: key.to_string(),
120 value: value.to_string(),
121 status: TodoStatus::Pending,
122 depends_on: Vec::new(),
123 };
124 if let Some(Block::Tdo(t)) = self
125 .emoji
126 .blocks
127 .iter_mut()
128 .find(|b| matches!(b, Block::Tdo(_)))
129 {
130 t.items.push(item);
131 } else {
132 self.emoji.blocks.push(Block::Tdo(TodoBlock {
133 items: vec![item],
134 }));
135 }
136 self
137 }
138
139 #[must_use]
144 pub fn add_image_rgba(mut self, width: u32, height: u32, mut rgba: Vec<u8>) -> Self {
145 rgba.resize(width as usize * height as usize * 4, 0);
146 self.emoji.blocks.push(Block::Img(ImageBlock {
147 name: String::new(),
148 atlas: SpriteAtlas {
149 width,
150 height,
151 frame_width: width.max(1),
152 frame_height: height.max(1),
153 frame_count: 1,
154 rgba,
155 },
156 }));
157 self
158 }
159
160 #[must_use]
163 pub fn with_agent_lifecycle(mut self) -> Self {
164 self.emoji.blocks.push(Block::Lfc(LifecycleBlock {
165 states: vec![
166 LifecycleState {
167 name: "idle".into(),
168 initial: true,
169 },
170 LifecycleState {
171 name: "active".into(),
172 initial: false,
173 },
174 LifecycleState {
175 name: "sleeping".into(),
176 initial: false,
177 },
178 ],
179 transitions: vec![
180 LifecycleTransition {
181 from: "idle".into(),
182 to: "active".into(),
183 event: "activate".into(),
184 },
185 LifecycleTransition {
186 from: "active".into(),
187 to: "idle".into(),
188 event: "deactivate".into(),
189 },
190 LifecycleTransition {
191 from: "active".into(),
192 to: "sleeping".into(),
193 event: "sleep".into(),
194 },
195 LifecycleTransition {
196 from: "sleeping".into(),
197 to: "idle".into(),
198 event: "wake".into(),
199 },
200 ],
201 }));
202 self
203 }
204
205 #[must_use]
208 pub fn encryption(mut self, encryption: EncryptionBlock) -> Self {
209 self.emoji.blocks.push(Block::Enc(encryption));
210 self
211 }
212
213 #[must_use]
215 pub fn add_block(mut self, block: Block) -> Self {
216 self.emoji.blocks.push(block);
217 self
218 }
219
220 #[must_use]
222 pub fn build(self) -> Emoji {
223 self.emoji
224 }
225}