1use crate::schema::{
4 AssetKind, ColorScheme, DesignSystem, Key, MAX_MODIFIER_WORDS, MaterialRole, MessageDuration,
5 Modifier, MotionRole, NotificationImportance, NotificationPermission, NotificationPresentation,
6 Paint, PropertyKind, Selection, ShapeRole, SpaceRole, TYPE_ROLE_COUNT, Theme, WidgetKind,
7 modifier_extra_words,
8};
9use core::fmt;
10
11const TAG_ENVELOPE: u16 = 0;
12const TAG_CREATE: u16 = 1;
13const TAG_SET_PROP: u16 = 2;
14const TAG_SET_MODIFIER: u16 = 3;
15const TAG_INSERT: u16 = 4;
16const TAG_MOVE: u16 = 5;
17const TAG_REMOVE: u16 = 6;
18const TAG_SET_TEXT: u16 = 7;
19const TAG_APPEND_TEXT: u16 = 8;
20const TAG_SET_THEME: u16 = 9;
21const TAG_REGISTER_ASSET: u16 = 10;
22const TAG_RELEASE_ASSET: u16 = 11;
23const TAG_SHOW_MESSAGE: u16 = 12;
24const TAG_SET_WINDOW: u16 = 13;
25const TAG_POST_NOTIFICATION: u16 = 14;
26const TAG_WITHDRAW_NOTIFICATION: u16 = 15;
27const TAG_REQUEST_NOTIFICATION_PERMISSION: u16 = 16;
28const TAG_EDIT_CODE: u16 = 17;
29const TAG_START_ANIMATION: u16 = 18;
30const TAG_CONTROL_ANIMATION: u16 = 19;
31const TAG_SET_SELECTION: u16 = 20;
33const TAG_REVEAL_RANGE: u16 = 21;
34const TAG_SET_FOLDED: u16 = 22;
35const ANIMATION_HEADER_LEN: usize = 52;
37const KEYFRAME_HEAD_LEN: usize = 24;
39const TRANSFORM_FUNCTION_LEN: usize = 28;
41const KEYFRAME_FROM_PRESENTED: u16 = 1;
42const ENVELOPE_LEN: usize = 12;
43pub const THEME_PAYLOAD_LEN: usize = 8 + 4 * TYPE_ROLE_COUNT + 8;
46const MIN_RECORD_LEN: usize = 4;
49
50const VALUE_NONE: u16 = 0;
51const VALUE_STRING: u16 = 1;
52const VALUE_BOOL: u16 = 2;
53const VALUE_I64: u16 = 3;
54const VALUE_F32: u16 = 4;
55const VALUE_BYTES: u16 = 5;
58
59#[derive(Clone, Debug, PartialEq)]
60pub enum PropertyValue<'a> {
61 None,
62 String(&'a str),
63 Bool(bool),
64 Integer(i64),
65 Float(f32),
66 Bytes(&'a [u8]),
67}
68
69#[derive(Clone, Debug, PartialEq)]
70pub enum Mutation<'a> {
71 Create {
72 node_id: u32,
73 widget: WidgetKind,
74 },
75 SetProp {
76 node_id: u32,
77 property: PropertyKind,
78 value: PropertyValue<'a>,
79 },
80 SetModifier {
81 node_id: u32,
82 index: u16,
83 modifier: Modifier,
84 },
85 Insert {
86 parent_id: u32,
87 node_id: u32,
88 index: u32,
89 },
90 Move {
91 parent_id: u32,
92 node_id: u32,
93 index: u32,
94 },
95 Remove {
96 node_id: u32,
97 },
98 SetText {
99 node_id: u32,
100 text: &'a str,
101 selection: Option<Selection>,
102 },
103 AppendText {
105 node_id: u32,
106 text: &'a str,
107 },
108 SetTheme(Theme),
110 SetWindow(crate::schema::Window),
118 RegisterAsset {
122 asset_id: u32,
123 kind: AssetKind,
124 bytes: &'a [u8],
125 },
126 ReleaseAsset {
128 asset_id: u32,
129 },
130 ShowMessage {
138 handler_id: u64,
139 text: &'a str,
140 action: &'a str,
141 duration: MessageDuration,
142 },
143 PostNotification {
151 key: &'a str,
152 title: &'a str,
153 body: &'a str,
154 channel: &'a str,
155 action_1: &'a str,
156 action_2: &'a str,
157 importance: NotificationImportance,
158 presentation: NotificationPresentation,
159 },
160 WithdrawNotification {
162 key: &'a str,
163 },
164 RequestNotificationPermission,
167 EditCode {
176 node_id: u32,
177 request_id: u32,
178 base_version: u32,
179 start_line: u32,
180 start_column: u32,
181 end_line: u32,
182 end_column: u32,
183 text: &'a str,
184 },
185 StartAnimation(crate::schema::Animation<'a>),
188 ControlAnimation {
192 node_id: u32,
193 animation_id: u32,
194 property: crate::schema::AnimatedProperty,
195 slot: u8,
196 op: crate::schema::AnimationControl,
197 at_time_nanos: u64,
199 },
200 SetSelection {
205 node_id: u32,
206 base_version: u32,
207 anchor_line: u32,
208 anchor_column: u32,
209 active_line: u32,
210 active_column: u32,
211 },
212 RevealRange {
215 node_id: u32,
216 base_version: u32,
217 start_line: u32,
218 start_column: u32,
219 end_line: u32,
220 end_column: u32,
221 align: crate::schema::RevealAlign,
222 },
223 SetFolded {
225 node_id: u32,
226 start_line: u32,
227 collapsed: bool,
228 },
229}
230
231#[derive(Clone, Debug, Eq, PartialEq)]
232pub enum ProtocolError {
233 Truncated,
234 InvalidEnvelope,
235 InvalidRecordLength,
236 InvalidTag(u16),
237 InvalidWidget(u16),
238 InvalidProperty(u16),
239 InvalidValueKind(u16),
240 InvalidModifier(u16),
241 InvalidTheme(u16),
242 InvalidAssetKind(u16),
243 InvalidMessageDuration(u16),
244 InvalidNotification(u16),
246 InvalidStringRange,
247 InvalidUtf8,
248 LengthOverflow,
249 MeasureRefused(i32),
252 InvalidAnimation,
257}
258
259#[derive(Clone, Debug, PartialEq)]
260pub struct HostEvent<'a> {
261 pub node_id: u32,
262 pub handler_id: u64,
263 pub payload: crate::schema::EventPayload<'a>,
264}
265
266const EVENT_CLICK: u16 = 1;
267const EVENT_TEXT_CHANGED: u16 = 2;
268const EVENT_TEXT_SUBMITTED: u16 = 3;
269const EVENT_FOCUS_LOST: u16 = 4;
270const EVENT_PROTOCOL_ERROR: u16 = 5;
271const EVENT_KEY_DOWN: u16 = 6;
272const EVENT_RANGE_REQUESTED: u16 = 7;
273const EVENT_POINTER: u16 = 8;
276const POINTER_RECORD_LEN: usize = 72;
277const EVENT_VALUE_CHANGED: u16 = 16;
278const EVENT_WINDOW_SIZE_CHANGED: u16 = 17;
279const EVENT_RESYNC: u16 = 18;
280const EVENT_LIFECYCLE_START: u16 = 19;
281const EVENT_LIFECYCLE_STOP: u16 = 20;
282const EVENT_DESIGN_SYSTEM_RESOLVED: u16 = 21;
283const EVENT_FILES_ENTERED: u16 = 22;
284const EVENT_FILES_DROPPED: u16 = 23;
285const EVENT_NOTIFICATION_ACTIVATED: u16 = 24;
286const EVENT_NOTIFICATION_PERMISSION_CHANGED: u16 = 25;
287const EVENT_CODE_CHANGED: u16 = 26;
288const EVENT_CODE_EDIT_REJECTED: u16 = 27;
289const EVENT_CODE_HOVERED: u16 = 28;
290const EVENT_CODE_SAVE_REQUESTED: u16 = 29;
291const EVENT_DECORATION_ACTIVATED: u16 = 30;
292const EVENT_ANIMATION: u16 = 31;
293const EVENT_REDUCED_MOTION_CHANGED: u16 = 32;
294const EVENT_ZOOM_CHANGED: u16 = 33;
296const EVENT_SELECTION_CHANGED: u16 = 34;
298const EVENT_VISIBLE_RANGE_CHANGED: u16 = 35;
299const EVENT_EDITOR_FOCUS_CHANGED: u16 = 36;
300const EVENT_FOLD_CHANGED: u16 = 37;
301const EVENT_COMPOSITION_CHANGED: u16 = 38;
302
303const MODIFIER_SHIFT: u8 = 1 << 0;
304const MODIFIER_CTRL: u8 = 1 << 1;
305const MODIFIER_ALT: u8 = 1 << 2;
306const MODIFIER_META: u8 = 1 << 3;
307
308fn pointer_tag<T: TryFrom<u16, Error = ()>>(
311 bytes: &[u8],
312 offset: usize,
313) -> Result<T, ProtocolError> {
314 let raw = read_u32(bytes, offset)?;
315 u16::try_from(raw)
316 .ok()
317 .and_then(|tag| T::try_from(tag).ok())
318 .ok_or(ProtocolError::InvalidValueKind(
319 raw.min(u32::from(u16::MAX)) as u16,
320 ))
321}
322
323fn decode_pointer(bytes: &[u8]) -> Result<crate::schema::EventPayload<'_>, ProtocolError> {
325 Ok(crate::schema::EventPayload::Pointer {
326 kind: pointer_tag(bytes, 16)?,
327 pointer_id: read_u32(bytes, 20)?,
328 pointer_type: pointer_tag(bytes, 24)?,
329 x: f32::from_bits(read_u32(bytes, 28)?),
330 y: f32::from_bits(read_u32(bytes, 32)?),
331 buttons: read_u32(bytes, 36)?,
332 button: read_u32(bytes, 40)?,
333 modifiers: read_u32(bytes, 44)?,
334 click_count: read_u32(bytes, 48)?,
335 delta_x: f32::from_bits(read_u32(bytes, 52)?),
336 delta_y: f32::from_bits(read_u32(bytes, 56)?),
337 delta_mode: pointer_tag(bytes, 60)?,
338 timestamp_nanos: read_u64(bytes, 64)?,
339 })
340}
341
342pub fn decode_event(bytes: &[u8]) -> Result<HostEvent<'_>, ProtocolError> {
344 if bytes.len() < 16 {
345 return Err(ProtocolError::Truncated);
346 }
347 let tag = read_u16(bytes, 0)?;
348 let record_len = usize::from(read_u16(bytes, 2)?);
349 if record_len < 16 || record_len % 4 != 0 || record_len > bytes.len() {
350 return Err(ProtocolError::InvalidRecordLength);
351 }
352 let node_id = read_u32(bytes, 4)?;
353 let handler_id = read_u64(bytes, 8)?;
354 let payload = match tag {
355 EVENT_CLICK if record_len == 16 => crate::schema::EventPayload::Clicked,
356 EVENT_TEXT_CHANGED if record_len == 24 => {
357 crate::schema::EventPayload::TextChanged(read_string(bytes, 16, record_len)?)
358 }
359 EVENT_TEXT_SUBMITTED if record_len == 24 => {
360 crate::schema::EventPayload::TextSubmitted(read_string(bytes, 16, record_len)?)
361 }
362 EVENT_FOCUS_LOST if record_len == 16 => crate::schema::EventPayload::FocusLost,
363 EVENT_PROTOCOL_ERROR if record_len == 28 => crate::schema::EventPayload::ProtocolError {
364 code: read_u32(bytes, 16)?,
365 message: read_string(bytes, 20, record_len)?,
366 },
367 EVENT_KEY_DOWN if record_len == 20 => {
368 let raw_key = read_u16(bytes, 16)?;
369 let modifiers = *bytes.get(18).ok_or(ProtocolError::Truncated)?;
370 crate::schema::EventPayload::KeyDown {
371 key: Key::try_from(raw_key)
372 .map_err(|()| ProtocolError::InvalidValueKind(raw_key))?,
373 shift_key: modifiers & MODIFIER_SHIFT != 0,
374 ctrl_key: modifiers & MODIFIER_CTRL != 0,
375 alt_key: modifiers & MODIFIER_ALT != 0,
376 meta_key: modifiers & MODIFIER_META != 0,
377 }
378 }
379 EVENT_RANGE_REQUESTED if record_len == 24 => crate::schema::EventPayload::RangeRequested {
380 start: read_u32(bytes, 16)?,
381 count: read_u32(bytes, 20)?,
382 },
383 EVENT_VALUE_CHANGED if record_len == 24 => {
384 crate::schema::EventPayload::ValueChanged(f64::from_bits(read_u64(bytes, 16)?))
385 }
386 EVENT_WINDOW_SIZE_CHANGED if record_len == 32 => {
387 let raw_class = read_u32(bytes, 24)?;
388 let class = u16::try_from(raw_class)
389 .ok()
390 .and_then(|value| crate::schema::WindowSizeClass::try_from(value).ok())
391 .ok_or(ProtocolError::InvalidValueKind(raw_class as u16))?;
392 let raw_height_class = read_u32(bytes, 28)?;
393 let height_class = u16::try_from(raw_height_class)
394 .ok()
395 .and_then(|value| crate::schema::WindowHeightClass::try_from(value).ok())
396 .ok_or(ProtocolError::InvalidValueKind(raw_height_class as u16))?;
397 crate::schema::EventPayload::WindowSizeChanged {
398 width_dp: f32::from_bits(read_u32(bytes, 16)?),
399 height_dp: f32::from_bits(read_u32(bytes, 20)?),
400 class,
401 height_class,
402 }
403 }
404 EVENT_DESIGN_SYSTEM_RESOLVED if record_len == 20 => {
405 let raw = read_u32(bytes, 16)?;
406 let system = u16::try_from(raw)
407 .ok()
408 .and_then(|tag| crate::schema::DesignSystem::try_from(tag).ok())
409 .ok_or(ProtocolError::InvalidValueKind(raw as u16))?;
410 crate::schema::EventPayload::DesignSystemResolved(system)
411 }
412 EVENT_FILES_ENTERED if record_len == 16 => crate::schema::EventPayload::FilesEntered,
413 EVENT_FILES_DROPPED if record_len == 24 => {
414 crate::schema::EventPayload::FilesDropped(read_string(bytes, 16, record_len)?)
415 }
416 EVENT_NOTIFICATION_ACTIVATED if record_len == 28 => {
417 let action = read_u32(bytes, 16)?;
418 if action > 2 {
421 return Err(ProtocolError::InvalidValueKind(
422 action.min(u32::from(u16::MAX)) as u16,
423 ));
424 }
425 crate::schema::EventPayload::NotificationActivated {
426 action,
427 key: read_string(bytes, 20, record_len)?,
428 }
429 }
430 EVENT_NOTIFICATION_PERMISSION_CHANGED if record_len == 20 => {
431 let raw = read_u32(bytes, 16)?;
432 let state = u16::try_from(raw)
433 .ok()
434 .and_then(|tag| NotificationPermission::try_from(tag).ok())
435 .ok_or(ProtocolError::InvalidValueKind(
436 raw.min(u32::from(u16::MAX)) as u16,
437 ))?;
438 crate::schema::EventPayload::NotificationPermissionChanged(state)
439 }
440 EVENT_ANIMATION if record_len == 40 => {
441 let raw_kind = u16::from(read_u8(bytes, 20)?);
442 let raw_property = u16::from(read_u8(bytes, 21)?);
443 crate::schema::EventPayload::AnimationEvent {
444 animation_id: read_u32(bytes, 16)?,
445 kind: crate::schema::AnimationEventKind::try_from(raw_kind)
446 .map_err(|()| ProtocolError::InvalidValueKind(raw_kind))?,
447 property: crate::schema::AnimatedProperty::try_from(raw_property)
448 .map_err(|()| ProtocolError::InvalidValueKind(raw_property))?,
449 slot: read_u8(bytes, 22)?,
450 iteration: read_u32(bytes, 24)?,
451 elapsed_ms: f32::from_bits(read_u32(bytes, 28)?),
452 time_nanos: read_u64(bytes, 32)?,
453 }
454 }
455 EVENT_POINTER if record_len == POINTER_RECORD_LEN => decode_pointer(bytes)?,
456 EVENT_REDUCED_MOTION_CHANGED if record_len == 20 => {
457 let raw = read_u32(bytes, 16)?;
458 let state = u16::try_from(raw)
459 .ok()
460 .and_then(|tag| crate::schema::ReducedMotion::try_from(tag).ok())
461 .ok_or(ProtocolError::InvalidValueKind(
462 raw.min(u32::from(u16::MAX)) as u16,
463 ))?;
464 crate::schema::EventPayload::ReducedMotionChanged(state)
465 }
466 EVENT_CODE_CHANGED if record_len == 44 => crate::schema::EventPayload::CodeChanged {
467 version: read_u32(bytes, 16)?,
468 start_line: read_u32(bytes, 20)?,
469 start_column: read_u32(bytes, 24)?,
470 end_line: read_u32(bytes, 28)?,
471 end_column: read_u32(bytes, 32)?,
472 text: read_string(bytes, 36, record_len)?,
473 },
474 EVENT_CODE_EDIT_REJECTED if record_len == 44 => {
475 crate::schema::EventPayload::CodeEditRejected {
476 request_id: read_u32(bytes, 16)?,
477 base_version: read_u32(bytes, 20)?,
478 current_version: read_u32(bytes, 24)?,
479 start_line: read_u32(bytes, 28)?,
480 start_column: read_u32(bytes, 32)?,
481 end_line: read_u32(bytes, 36)?,
482 end_column: read_u32(bytes, 40)?,
483 }
484 }
485 EVENT_CODE_HOVERED if record_len == 36 => {
486 let raw_phase = read_u32(bytes, 32)?;
487 let phase = u16::try_from(raw_phase)
488 .ok()
489 .and_then(|tag| crate::schema::HoverPhase::try_from(tag).ok())
490 .ok_or(ProtocolError::InvalidValueKind(raw_phase as u16))?;
491 crate::schema::EventPayload::CodeHovered {
492 decoration: read_u64(bytes, 16)?,
493 line: read_u32(bytes, 24)?,
494 column: read_u32(bytes, 28)?,
495 phase,
496 }
497 }
498 EVENT_CODE_SAVE_REQUESTED if record_len == 24 => {
499 crate::schema::EventPayload::CodeSaveRequested {
500 version: read_u32(bytes, 16)?,
501 }
502 }
503 EVENT_DECORATION_ACTIVATED if record_len == 24 => {
504 crate::schema::EventPayload::DecorationActivated {
505 decoration: read_u64(bytes, 16)?,
506 }
507 }
508 EVENT_CODE_CHANGED..=EVENT_DECORATION_ACTIVATED => {
509 return Err(ProtocolError::InvalidRecordLength);
510 }
511 EVENT_SELECTION_CHANGED if record_len == 36 => {
512 crate::schema::EventPayload::SelectionChanged {
513 version: read_u32(bytes, 16)?,
514 anchor_line: read_u32(bytes, 20)?,
515 anchor_column: read_u32(bytes, 24)?,
516 active_line: read_u32(bytes, 28)?,
517 active_column: read_u32(bytes, 32)?,
518 }
519 }
520 EVENT_VISIBLE_RANGE_CHANGED if record_len == 28 => {
521 crate::schema::EventPayload::VisibleRangeChanged {
522 version: read_u32(bytes, 16)?,
523 first_line: read_u32(bytes, 20)?,
524 last_line: read_u32(bytes, 24)?,
525 }
526 }
527 EVENT_EDITOR_FOCUS_CHANGED if record_len == 24 => {
528 crate::schema::EventPayload::EditorFocusChanged {
529 focused: read_flag(bytes, 16)?,
530 }
531 }
532 EVENT_FOLD_CHANGED if record_len == 24 => crate::schema::EventPayload::FoldChanged {
533 start_line: read_u32(bytes, 16)?,
534 collapsed: read_flag(bytes, 20)?,
535 },
536 EVENT_COMPOSITION_CHANGED if record_len == 24 => {
537 crate::schema::EventPayload::CompositionChanged {
538 composing: read_flag(bytes, 16)?,
539 }
540 }
541 EVENT_SELECTION_CHANGED..=EVENT_COMPOSITION_CHANGED => {
542 return Err(ProtocolError::InvalidRecordLength);
543 }
544 EVENT_ZOOM_CHANGED if record_len == 28 => {
545 let level = read_u32(bytes, 24)? as i32;
546 if !(i32::from(crate::zoom::MIN_ZOOM_LEVEL)..=i32::from(crate::zoom::MAX_ZOOM_LEVEL))
547 .contains(&level)
548 {
549 return Err(ProtocolError::InvalidValueKind(level as u16));
550 }
551 crate::schema::EventPayload::ZoomChanged {
552 k: f32::from_bits(read_u32(bytes, 16)?),
553 os: f32::from_bits(read_u32(bytes, 20)?),
554 level,
555 }
556 }
557 EVENT_RESYNC if record_len == 16 => crate::schema::EventPayload::Resync,
558 EVENT_LIFECYCLE_START if record_len == 16 => crate::schema::EventPayload::LifecycleStart,
559 EVENT_LIFECYCLE_STOP if record_len == 16 => crate::schema::EventPayload::LifecycleStop,
560 EVENT_CLICK..=EVENT_RANGE_REQUESTED
561 | EVENT_VALUE_CHANGED..=EVENT_NOTIFICATION_PERMISSION_CHANGED
562 | EVENT_ANIMATION
563 | EVENT_POINTER
564 | EVENT_REDUCED_MOTION_CHANGED
565 | EVENT_ZOOM_CHANGED => {
566 return Err(ProtocolError::InvalidRecordLength);
567 }
568 other => return Err(ProtocolError::InvalidTag(other)),
569 };
570 Ok(HostEvent {
571 node_id,
572 handler_id,
573 payload,
574 })
575}
576
577pub fn encode_event(event: &HostEvent<'_>, output: &mut Vec<u8>) -> Result<(), ProtocolError> {
579 output.clear();
580 if let crate::schema::EventPayload::KeyDown {
581 key,
582 shift_key,
583 ctrl_key,
584 alt_key,
585 meta_key,
586 } = event.payload
587 {
588 output.extend_from_slice(&EVENT_KEY_DOWN.to_le_bytes());
589 output.extend_from_slice(&20_u16.to_le_bytes());
590 output.extend_from_slice(&event.node_id.to_le_bytes());
591 output.extend_from_slice(&event.handler_id.to_le_bytes());
592 output.extend_from_slice(&(key as u16).to_le_bytes());
593 let modifiers = (u8::from(shift_key) * MODIFIER_SHIFT)
594 | (u8::from(ctrl_key) * MODIFIER_CTRL)
595 | (u8::from(alt_key) * MODIFIER_ALT)
596 | (u8::from(meta_key) * MODIFIER_META);
597 output.push(modifiers);
598 output.push(0);
599 return Ok(());
600 }
601 if let crate::schema::EventPayload::ValueChanged(value) = event.payload {
602 output.extend_from_slice(&EVENT_VALUE_CHANGED.to_le_bytes());
603 output.extend_from_slice(&24_u16.to_le_bytes());
604 output.extend_from_slice(&event.node_id.to_le_bytes());
605 output.extend_from_slice(&event.handler_id.to_le_bytes());
606 output.extend_from_slice(&value.to_bits().to_le_bytes());
607 return Ok(());
608 }
609 if let crate::schema::EventPayload::WindowSizeChanged {
610 width_dp,
611 height_dp,
612 class,
613 height_class,
614 } = event.payload
615 {
616 output.extend_from_slice(&EVENT_WINDOW_SIZE_CHANGED.to_le_bytes());
617 output.extend_from_slice(&32_u16.to_le_bytes());
618 output.extend_from_slice(&event.node_id.to_le_bytes());
619 output.extend_from_slice(&event.handler_id.to_le_bytes());
620 output.extend_from_slice(&width_dp.to_bits().to_le_bytes());
621 output.extend_from_slice(&height_dp.to_bits().to_le_bytes());
622 output.extend_from_slice(&u32::from(u16::from(class)).to_le_bytes());
623 output.extend_from_slice(&u32::from(u16::from(height_class)).to_le_bytes());
624 return Ok(());
625 }
626 if let crate::schema::EventPayload::DesignSystemResolved(system) = event.payload {
627 output.extend_from_slice(&EVENT_DESIGN_SYSTEM_RESOLVED.to_le_bytes());
628 output.extend_from_slice(&20_u16.to_le_bytes());
629 output.extend_from_slice(&event.node_id.to_le_bytes());
630 output.extend_from_slice(&event.handler_id.to_le_bytes());
631 output.extend_from_slice(&u32::from(u16::from(system)).to_le_bytes());
634 return Ok(());
635 }
636 if let crate::schema::EventPayload::NotificationPermissionChanged(state) = event.payload {
637 output.extend_from_slice(&EVENT_NOTIFICATION_PERMISSION_CHANGED.to_le_bytes());
638 output.extend_from_slice(&20_u16.to_le_bytes());
639 output.extend_from_slice(&event.node_id.to_le_bytes());
640 output.extend_from_slice(&event.handler_id.to_le_bytes());
641 output.extend_from_slice(&u32::from(u16::from(state)).to_le_bytes());
642 return Ok(());
643 }
644 if let crate::schema::EventPayload::AnimationEvent {
645 animation_id,
646 kind,
647 property,
648 slot,
649 iteration,
650 elapsed_ms,
651 time_nanos,
652 } = event.payload
653 {
654 output.extend_from_slice(&EVENT_ANIMATION.to_le_bytes());
655 output.extend_from_slice(&40_u16.to_le_bytes());
656 output.extend_from_slice(&event.node_id.to_le_bytes());
657 output.extend_from_slice(&event.handler_id.to_le_bytes());
658 output.extend_from_slice(&animation_id.to_le_bytes());
659 output.push(kind as u8);
660 output.push(property as u8);
661 output.push(slot);
662 output.push(0);
663 output.extend_from_slice(&iteration.to_le_bytes());
664 output.extend_from_slice(&elapsed_ms.to_bits().to_le_bytes());
665 output.extend_from_slice(&time_nanos.to_le_bytes());
666 return Ok(());
667 }
668 if let crate::schema::EventPayload::Pointer {
669 kind,
670 pointer_id,
671 pointer_type,
672 x,
673 y,
674 buttons,
675 button,
676 modifiers,
677 click_count,
678 delta_x,
679 delta_y,
680 delta_mode,
681 timestamp_nanos,
682 } = event.payload
683 {
684 output.extend_from_slice(&EVENT_POINTER.to_le_bytes());
685 output.extend_from_slice(&(POINTER_RECORD_LEN as u16).to_le_bytes());
686 output.extend_from_slice(&event.node_id.to_le_bytes());
687 output.extend_from_slice(&event.handler_id.to_le_bytes());
688 output.extend_from_slice(&u32::from(u16::from(kind)).to_le_bytes());
689 output.extend_from_slice(&pointer_id.to_le_bytes());
690 output.extend_from_slice(&u32::from(u16::from(pointer_type)).to_le_bytes());
691 output.extend_from_slice(&x.to_bits().to_le_bytes());
692 output.extend_from_slice(&y.to_bits().to_le_bytes());
693 output.extend_from_slice(&buttons.to_le_bytes());
694 output.extend_from_slice(&button.to_le_bytes());
695 output.extend_from_slice(&modifiers.to_le_bytes());
696 output.extend_from_slice(&click_count.to_le_bytes());
697 output.extend_from_slice(&delta_x.to_bits().to_le_bytes());
698 output.extend_from_slice(&delta_y.to_bits().to_le_bytes());
699 output.extend_from_slice(&u32::from(u16::from(delta_mode)).to_le_bytes());
700 output.extend_from_slice(×tamp_nanos.to_le_bytes());
701 return Ok(());
702 }
703 if let crate::schema::EventPayload::ReducedMotionChanged(state) = event.payload {
704 output.extend_from_slice(&EVENT_REDUCED_MOTION_CHANGED.to_le_bytes());
705 output.extend_from_slice(&20_u16.to_le_bytes());
706 output.extend_from_slice(&event.node_id.to_le_bytes());
707 output.extend_from_slice(&event.handler_id.to_le_bytes());
708 output.extend_from_slice(&u32::from(u16::from(state)).to_le_bytes());
709 return Ok(());
710 }
711 match event.payload {
712 crate::schema::EventPayload::CodeChanged {
713 version,
714 start_line,
715 start_column,
716 end_line,
717 end_column,
718 text,
719 } => {
720 put_event_header(output, EVENT_CODE_CHANGED, 44, event);
721 for value in [version, start_line, start_column, end_line, end_column] {
722 output.extend_from_slice(&value.to_le_bytes());
723 }
724 let len = u32::try_from(text.len()).map_err(|_| ProtocolError::LengthOverflow)?;
725 output.extend_from_slice(&44_u32.to_le_bytes());
726 output.extend_from_slice(&len.to_le_bytes());
727 output.extend_from_slice(text.as_bytes());
728 return Ok(());
729 }
730 crate::schema::EventPayload::CodeEditRejected {
731 request_id,
732 base_version,
733 current_version,
734 start_line,
735 start_column,
736 end_line,
737 end_column,
738 } => {
739 put_event_header(output, EVENT_CODE_EDIT_REJECTED, 44, event);
740 for value in [
741 request_id,
742 base_version,
743 current_version,
744 start_line,
745 start_column,
746 end_line,
747 end_column,
748 ] {
749 output.extend_from_slice(&value.to_le_bytes());
750 }
751 return Ok(());
752 }
753 crate::schema::EventPayload::CodeHovered {
754 decoration,
755 line,
756 column,
757 phase,
758 } => {
759 put_event_header(output, EVENT_CODE_HOVERED, 36, event);
760 output.extend_from_slice(&decoration.to_le_bytes());
761 output.extend_from_slice(&line.to_le_bytes());
762 output.extend_from_slice(&column.to_le_bytes());
763 output.extend_from_slice(&u32::from(u16::from(phase)).to_le_bytes());
764 return Ok(());
765 }
766 crate::schema::EventPayload::CodeSaveRequested { version } => {
767 put_event_header(output, EVENT_CODE_SAVE_REQUESTED, 24, event);
768 output.extend_from_slice(&version.to_le_bytes());
769 output.extend_from_slice(&0_u32.to_le_bytes());
771 return Ok(());
772 }
773 crate::schema::EventPayload::DecorationActivated { decoration } => {
774 put_event_header(output, EVENT_DECORATION_ACTIVATED, 24, event);
775 output.extend_from_slice(&decoration.to_le_bytes());
776 return Ok(());
777 }
778 crate::schema::EventPayload::SelectionChanged {
779 version,
780 anchor_line,
781 anchor_column,
782 active_line,
783 active_column,
784 } => {
785 put_event_header(output, EVENT_SELECTION_CHANGED, 36, event);
786 for value in [
787 version,
788 anchor_line,
789 anchor_column,
790 active_line,
791 active_column,
792 ] {
793 output.extend_from_slice(&value.to_le_bytes());
794 }
795 return Ok(());
796 }
797 crate::schema::EventPayload::VisibleRangeChanged {
798 version,
799 first_line,
800 last_line,
801 } => {
802 put_event_header(output, EVENT_VISIBLE_RANGE_CHANGED, 28, event);
803 for value in [version, first_line, last_line] {
804 output.extend_from_slice(&value.to_le_bytes());
805 }
806 return Ok(());
807 }
808 crate::schema::EventPayload::EditorFocusChanged { focused } => {
809 put_event_header(output, EVENT_EDITOR_FOCUS_CHANGED, 24, event);
810 output.extend_from_slice(&u32::from(focused).to_le_bytes());
811 output.extend_from_slice(&0_u32.to_le_bytes());
812 return Ok(());
813 }
814 crate::schema::EventPayload::FoldChanged {
815 start_line,
816 collapsed,
817 } => {
818 put_event_header(output, EVENT_FOLD_CHANGED, 24, event);
819 output.extend_from_slice(&start_line.to_le_bytes());
820 output.extend_from_slice(&u32::from(collapsed).to_le_bytes());
821 return Ok(());
822 }
823 crate::schema::EventPayload::CompositionChanged { composing } => {
824 put_event_header(output, EVENT_COMPOSITION_CHANGED, 24, event);
825 output.extend_from_slice(&u32::from(composing).to_le_bytes());
826 output.extend_from_slice(&0_u32.to_le_bytes());
827 return Ok(());
828 }
829 _ => {}
830 }
831 if let crate::schema::EventPayload::ZoomChanged { k, os, level } = event.payload {
832 output.extend_from_slice(&EVENT_ZOOM_CHANGED.to_le_bytes());
833 output.extend_from_slice(&28_u16.to_le_bytes());
834 output.extend_from_slice(&event.node_id.to_le_bytes());
835 output.extend_from_slice(&event.handler_id.to_le_bytes());
836 output.extend_from_slice(&k.to_bits().to_le_bytes());
837 output.extend_from_slice(&os.to_bits().to_le_bytes());
838 output.extend_from_slice(&level.to_le_bytes());
839 return Ok(());
840 }
841 if let crate::schema::EventPayload::RangeRequested { start, count } = event.payload {
842 output.extend_from_slice(&EVENT_RANGE_REQUESTED.to_le_bytes());
843 output.extend_from_slice(&24_u16.to_le_bytes());
844 output.extend_from_slice(&event.node_id.to_le_bytes());
845 output.extend_from_slice(&event.handler_id.to_le_bytes());
846 output.extend_from_slice(&start.to_le_bytes());
847 output.extend_from_slice(&count.to_le_bytes());
848 return Ok(());
849 }
850 let (tag, record_len, text, error_code) = match &event.payload {
851 crate::schema::EventPayload::Clicked => (EVENT_CLICK, 16_u16, None, None),
852 crate::schema::EventPayload::TextChanged(value) => {
853 (EVENT_TEXT_CHANGED, 24, Some(*value), None)
854 }
855 crate::schema::EventPayload::TextSubmitted(value) => {
856 (EVENT_TEXT_SUBMITTED, 24, Some(*value), None)
857 }
858 crate::schema::EventPayload::FocusLost => (EVENT_FOCUS_LOST, 16, None, None),
859 crate::schema::EventPayload::FilesEntered => (EVENT_FILES_ENTERED, 16, None, None),
860 crate::schema::EventPayload::FilesDropped(paths) => {
861 (EVENT_FILES_DROPPED, 24, Some(*paths), None)
862 }
863 crate::schema::EventPayload::Resync => (EVENT_RESYNC, 16, None, None),
864 crate::schema::EventPayload::LifecycleStart => (EVENT_LIFECYCLE_START, 16, None, None),
865 crate::schema::EventPayload::LifecycleStop => (EVENT_LIFECYCLE_STOP, 16, None, None),
866 crate::schema::EventPayload::ProtocolError { code, message } => {
867 (EVENT_PROTOCOL_ERROR, 28, Some(*message), Some(*code))
868 }
869 crate::schema::EventPayload::NotificationActivated { action, key } => {
871 (EVENT_NOTIFICATION_ACTIVATED, 28, Some(*key), Some(*action))
872 }
873 crate::schema::EventPayload::KeyDown { .. }
874 | crate::schema::EventPayload::RangeRequested { .. }
875 | crate::schema::EventPayload::ValueChanged(_)
876 | crate::schema::EventPayload::WindowSizeChanged { .. }
877 | crate::schema::EventPayload::DesignSystemResolved(_)
878 | crate::schema::EventPayload::NotificationPermissionChanged(_)
879 | crate::schema::EventPayload::CodeChanged { .. }
880 | crate::schema::EventPayload::CodeEditRejected { .. }
881 | crate::schema::EventPayload::CodeHovered { .. }
882 | crate::schema::EventPayload::CodeSaveRequested { .. }
883 | crate::schema::EventPayload::DecorationActivated { .. }
884 | crate::schema::EventPayload::SelectionChanged { .. }
885 | crate::schema::EventPayload::VisibleRangeChanged { .. }
886 | crate::schema::EventPayload::EditorFocusChanged { .. }
887 | crate::schema::EventPayload::FoldChanged { .. }
888 | crate::schema::EventPayload::CompositionChanged { .. } => unreachable!(),
889 crate::schema::EventPayload::AnimationEvent { .. }
890 | crate::schema::EventPayload::Pointer { .. }
891 | crate::schema::EventPayload::ReducedMotionChanged(_)
892 | crate::schema::EventPayload::ZoomChanged { .. } => unreachable!(),
893 };
894 output.extend_from_slice(&tag.to_le_bytes());
895 output.extend_from_slice(&record_len.to_le_bytes());
896 output.extend_from_slice(&event.node_id.to_le_bytes());
897 output.extend_from_slice(&event.handler_id.to_le_bytes());
898 if let Some(code) = error_code {
899 output.extend_from_slice(&code.to_le_bytes());
900 }
901 if let Some(text) = text {
902 let offset = u32::from(record_len);
903 let len = u32::try_from(text.len()).map_err(|_| ProtocolError::LengthOverflow)?;
904 output.extend_from_slice(&offset.to_le_bytes());
905 output.extend_from_slice(&len.to_le_bytes());
906 output.extend_from_slice(text.as_bytes());
907 }
908 Ok(())
909}
910
911fn put_event_header(output: &mut Vec<u8>, tag: u16, record_len: u16, event: &HostEvent<'_>) {
914 output.extend_from_slice(&tag.to_le_bytes());
915 output.extend_from_slice(&record_len.to_le_bytes());
916 output.extend_from_slice(&event.node_id.to_le_bytes());
917 output.extend_from_slice(&event.handler_id.to_le_bytes());
918}
919
920impl fmt::Display for ProtocolError {
921 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
922 write!(formatter, "protocol error: {self:?}")
923 }
924}
925
926impl std::error::Error for ProtocolError {}
927
928pub struct BatchEncoder {
930 records: Vec<u8>,
931 strings: Vec<u8>,
932 string_fixups: Vec<usize>,
933 arena: Vec<u8>,
934 record_count: u32,
935}
936
937impl Default for BatchEncoder {
938 fn default() -> Self {
939 Self::with_capacity(4096, 1024, 64)
940 }
941}
942
943impl BatchEncoder {
944 pub fn with_capacity(record_bytes: usize, string_bytes: usize, string_count: usize) -> Self {
945 let mut encoder = Self {
946 records: Vec::with_capacity(record_bytes),
947 strings: Vec::with_capacity(string_bytes),
948 string_fixups: Vec::with_capacity(string_count),
949 arena: Vec::with_capacity(record_bytes + string_bytes),
950 record_count: 0,
951 };
952 encoder.clear();
953 encoder
954 }
955
956 pub fn clear(&mut self) {
957 self.records.clear();
958 self.strings.clear();
959 self.string_fixups.clear();
960 self.arena.clear();
961 self.record_count = 0;
962 self.begin_record(TAG_ENVELOPE, 8);
963 self.put_u32(0);
964 self.put_u32(0);
965 }
966
967 pub fn encode(&mut self, mutation: &Mutation<'_>) -> Result<(), ProtocolError> {
968 match mutation {
969 Mutation::Create { node_id, widget } => {
970 self.begin_record(TAG_CREATE, 8);
971 self.put_u32(*node_id);
972 self.put_u16(*widget as u16);
973 self.put_u16(0);
974 }
975 Mutation::SetProp {
976 node_id,
977 property,
978 value,
979 } => {
980 self.begin_record(TAG_SET_PROP, 20);
981 self.put_u32(*node_id);
982 self.put_u16(*property as u16);
983 match value {
984 PropertyValue::None => {
985 self.put_u16(VALUE_NONE);
986 self.put_u64(0);
987 self.put_u32(0);
988 }
989 PropertyValue::String(value) => {
990 self.put_u16(VALUE_STRING);
991 self.put_string_ref(value)?;
992 self.put_u32(0);
993 }
994 PropertyValue::Bool(value) => {
995 self.put_u16(VALUE_BOOL);
996 self.put_u64(u64::from(*value));
997 self.put_u32(0);
998 }
999 PropertyValue::Integer(value) => {
1000 self.put_u16(VALUE_I64);
1001 self.put_u64(*value as u64);
1002 self.put_u32(0);
1003 }
1004 PropertyValue::Float(value) => {
1005 self.put_u16(VALUE_F32);
1006 self.put_u64(u64::from(value.to_bits()));
1007 self.put_u32(0);
1008 }
1009 PropertyValue::Bytes(value) => {
1010 self.put_u16(VALUE_BYTES);
1011 self.put_bytes_ref(value)?;
1012 self.put_u32(0);
1013 }
1014 }
1015 }
1016 Mutation::SetModifier {
1017 node_id,
1018 index,
1019 modifier,
1020 } => {
1021 let (tag, words) = modifier_fields(modifier);
1022 let extra = modifier_extra_words(tag).ok_or(ProtocolError::InvalidModifier(tag))?;
1026 self.begin_record(TAG_SET_MODIFIER, 24 + 8 * u16::from(extra));
1027 self.put_u32(*node_id);
1028 self.put_u16(*index);
1029 self.put_u16(tag);
1030 for word in &words[..2 + usize::from(extra)] {
1031 self.put_u64(*word);
1032 }
1033 }
1034 Mutation::Insert {
1035 parent_id,
1036 node_id,
1037 index,
1038 } => {
1039 self.begin_record(TAG_INSERT, 12);
1040 self.put_u32(*parent_id);
1041 self.put_u32(*node_id);
1042 self.put_u32(*index);
1043 }
1044 Mutation::Move {
1045 parent_id,
1046 node_id,
1047 index,
1048 } => {
1049 self.begin_record(TAG_MOVE, 12);
1050 self.put_u32(*parent_id);
1051 self.put_u32(*node_id);
1052 self.put_u32(*index);
1053 }
1054 Mutation::Remove { node_id } => {
1055 self.begin_record(TAG_REMOVE, 4);
1056 self.put_u32(*node_id);
1057 }
1058 Mutation::SetText {
1059 node_id,
1060 text,
1061 selection,
1062 } => {
1063 self.begin_record(TAG_SET_TEXT, 20);
1064 self.put_u32(*node_id);
1065 self.put_string_ref(text)?;
1066 let (start, end) =
1067 selection.map_or((u32::MAX, u32::MAX), |value| (value.start, value.end));
1068 self.put_u32(start);
1069 self.put_u32(end);
1070 }
1071 Mutation::AppendText { node_id, text } => {
1072 self.begin_record(TAG_APPEND_TEXT, 12);
1073 self.put_u32(*node_id);
1074 self.put_string_ref(text)?;
1075 }
1076 Mutation::RegisterAsset {
1077 asset_id,
1078 kind,
1079 bytes,
1080 } => {
1081 self.begin_record(TAG_REGISTER_ASSET, 16);
1082 self.put_u32(*asset_id);
1083 self.put_u16(*kind as u16);
1084 self.put_u16(0);
1085 self.put_bytes_ref(bytes)?;
1086 }
1087 Mutation::ReleaseAsset { asset_id } => {
1088 self.begin_record(TAG_RELEASE_ASSET, 4);
1089 self.put_u32(*asset_id);
1090 }
1091 Mutation::ShowMessage {
1092 handler_id,
1093 text,
1094 action,
1095 duration,
1096 } => {
1097 self.begin_record(TAG_SHOW_MESSAGE, 28);
1098 self.put_u64(*handler_id);
1099 self.put_string_ref(text)?;
1100 self.put_string_ref(action)?;
1101 self.put_u16(*duration as u16);
1102 self.put_u16(0);
1103 }
1104 Mutation::EditCode {
1105 node_id,
1106 request_id,
1107 base_version,
1108 start_line,
1109 start_column,
1110 end_line,
1111 end_column,
1112 text,
1113 } => {
1114 self.begin_record(TAG_EDIT_CODE, 36);
1115 self.put_u32(*node_id);
1116 self.put_u32(*request_id);
1117 self.put_u32(*base_version);
1118 self.put_u32(*start_line);
1119 self.put_u32(*start_column);
1120 self.put_u32(*end_line);
1121 self.put_u32(*end_column);
1122 self.put_string_ref(text)?;
1123 }
1124 Mutation::SetSelection {
1125 node_id,
1126 base_version,
1127 anchor_line,
1128 anchor_column,
1129 active_line,
1130 active_column,
1131 } => {
1132 self.begin_record(TAG_SET_SELECTION, 24);
1133 self.put_u32(*node_id);
1134 self.put_u32(*base_version);
1135 self.put_u32(*anchor_line);
1136 self.put_u32(*anchor_column);
1137 self.put_u32(*active_line);
1138 self.put_u32(*active_column);
1139 }
1140 Mutation::RevealRange {
1141 node_id,
1142 base_version,
1143 start_line,
1144 start_column,
1145 end_line,
1146 end_column,
1147 align,
1148 } => {
1149 self.begin_record(TAG_REVEAL_RANGE, 28);
1150 self.put_u32(*node_id);
1151 self.put_u32(*base_version);
1152 self.put_u32(*start_line);
1153 self.put_u32(*start_column);
1154 self.put_u32(*end_line);
1155 self.put_u32(*end_column);
1156 self.put_u32(u32::from(*align as u16));
1157 }
1158 Mutation::SetFolded {
1159 node_id,
1160 start_line,
1161 collapsed,
1162 } => {
1163 self.begin_record(TAG_SET_FOLDED, 12);
1164 self.put_u32(*node_id);
1165 self.put_u32(*start_line);
1166 self.put_u32(u32::from(*collapsed));
1167 }
1168 Mutation::SetTheme(theme) => {
1169 self.begin_record(TAG_SET_THEME, THEME_PAYLOAD_LEN as u16);
1170 self.put_u16(theme.design_system as u16);
1171 self.put_u16(theme.fallback as u16);
1172 self.put_u16(theme.color_scheme as u16);
1173 self.put_u16(u16::from(theme.adaptive));
1174 for asset in theme.fonts {
1178 self.put_u32(asset);
1179 }
1180 self.put_palette_ref(theme.palette)?;
1185 }
1186 Mutation::SetWindow(window) => {
1187 self.begin_record(TAG_SET_WINDOW, 28);
1188 self.put_u16(window.chrome as u16);
1189 self.put_u16(window.title_bar as u16);
1190 self.put_u16(window.width);
1191 self.put_u16(window.height);
1192 self.put_u16(window.min_width);
1193 self.put_u16(window.min_height);
1194 self.put_u16(u16::from(window.resizable));
1195 self.put_u16(match window.zoom_level {
1200 None => 0,
1201 Some(level) => 1 | (u16::from(crate::zoom::clamp_level(level) as u8) << 8),
1202 });
1203 self.put_string_ref(window.title)?;
1204 self.put_u32(window.icon);
1205 }
1206 Mutation::PostNotification {
1207 key,
1208 title,
1209 body,
1210 channel,
1211 action_1,
1212 action_2,
1213 importance,
1214 presentation,
1215 } => {
1216 self.begin_record(TAG_POST_NOTIFICATION, 52);
1217 self.put_string_ref(key)?;
1218 self.put_string_ref(title)?;
1219 self.put_string_ref(body)?;
1220 self.put_string_ref(channel)?;
1221 self.put_string_ref(action_1)?;
1222 self.put_string_ref(action_2)?;
1223 self.put_u16(*importance as u16);
1224 self.put_u16(*presentation as u16);
1225 }
1226 Mutation::WithdrawNotification { key } => {
1227 self.begin_record(TAG_WITHDRAW_NOTIFICATION, 8);
1228 self.put_string_ref(key)?;
1229 }
1230 Mutation::RequestNotificationPermission => {
1231 self.begin_record(TAG_REQUEST_NOTIFICATION_PERMISSION, 0);
1232 }
1233 Mutation::StartAnimation(animation) => self.put_animation(animation)?,
1234 Mutation::ControlAnimation {
1235 node_id,
1236 animation_id,
1237 property,
1238 slot,
1239 op,
1240 at_time_nanos,
1241 } => {
1242 self.begin_record(TAG_CONTROL_ANIMATION, 20);
1243 self.put_u32(*node_id);
1244 self.put_u32(*animation_id);
1245 self.records.push(*property as u8);
1246 self.records.push(*slot);
1247 self.records.push(*op as u8);
1248 self.records.push(0);
1249 self.put_u64(*at_time_nanos);
1250 }
1251 }
1252 self.record_count = self
1253 .record_count
1254 .checked_add(1)
1255 .ok_or(ProtocolError::LengthOverflow)?;
1256 Ok(())
1257 }
1258
1259 pub fn finish(&mut self) -> Result<&[u8], ProtocolError> {
1260 let records_len =
1261 u32::try_from(self.records.len()).map_err(|_| ProtocolError::LengthOverflow)?;
1262 self.records[4..8].copy_from_slice(&records_len.to_le_bytes());
1263 self.records[8..12].copy_from_slice(&self.record_count.to_le_bytes());
1264 let total = self
1265 .records
1266 .len()
1267 .checked_add(self.strings.len())
1268 .ok_or(ProtocolError::LengthOverflow)?;
1269 self.arena.clear();
1270 if self.arena.capacity() < total {
1271 self.arena.reserve(total);
1272 }
1273 self.arena.extend_from_slice(&self.records);
1274 self.arena.extend_from_slice(&self.strings);
1275 for &position in &self.string_fixups {
1276 let relative = read_u32(&self.arena, position)?;
1277 let absolute = records_len
1278 .checked_add(relative)
1279 .ok_or(ProtocolError::LengthOverflow)?;
1280 self.arena[position..position + 4].copy_from_slice(&absolute.to_le_bytes());
1281 }
1282 Ok(&self.arena)
1283 }
1284
1285 pub fn arena(&self) -> (*const u8, usize) {
1292 (self.arena.as_ptr(), self.arena.capacity())
1293 }
1294
1295 pub fn capacity(&self) -> usize {
1296 self.records.capacity() + self.strings.capacity() + self.arena.capacity()
1297 }
1298
1299 fn put_animation(
1305 &mut self,
1306 animation: &crate::schema::Animation<'_>,
1307 ) -> Result<(), ProtocolError> {
1308 use crate::schema::{KeyframeValue, Timing};
1309 let keyframes_len: usize = animation
1310 .keyframes
1311 .iter()
1312 .map(|keyframe| {
1313 KEYFRAME_HEAD_LEN
1314 + match &keyframe.value {
1315 KeyframeValue::Alpha(_) => 4,
1316 KeyframeValue::Paint(_) => 8,
1317 KeyframeValue::Transform(functions) => {
1318 4 + TRANSFORM_FUNCTION_LEN * functions.len()
1319 }
1320 }
1321 })
1322 .sum();
1323 let payload = u16::try_from(ANIMATION_HEADER_LEN - 4 + keyframes_len)
1324 .ok()
1325 .filter(|payload| *payload <= u16::MAX - 4)
1326 .ok_or(ProtocolError::LengthOverflow)?;
1327 let keyframe_count =
1328 u16::try_from(animation.keyframes.len()).map_err(|_| ProtocolError::LengthOverflow)?;
1329 self.begin_record(TAG_START_ANIMATION, payload);
1330 self.put_u32(animation.node_id);
1331 self.put_u32(animation.animation_id);
1332 self.records.push(animation.property as u8);
1333 self.records.push(animation.slot);
1334 self.records.push(animation.direction as u8);
1335 self.records.push(animation.fill as u8);
1336 self.records.push(animation.play_state as u8);
1337 self.records
1338 .push(animation.interpolation.map_or(0, |value| value as u8));
1339 self.put_u16(keyframe_count);
1340 self.put_u64(animation.start_time_nanos);
1341 self.put_u32(animation.delay_ms.to_bits());
1342 self.put_u32(animation.duration_ms.to_bits());
1343 self.put_u32(animation.iterations.to_bits());
1344 self.put_u32(animation.origin_x.to_bits());
1345 self.put_u32(animation.origin_y.to_bits());
1346 self.records.push(animation.events.0);
1347 self.records.extend_from_slice(&[0, 0, 0]);
1348 for keyframe in animation.keyframes.iter() {
1349 self.put_u32(keyframe.offset.to_bits());
1350 let (kind, position, numbers) = match keyframe.timing {
1351 Timing::Linear => (1_u8, 0_u8, [0_u32; 4]),
1352 Timing::CubicBezier { x1, y1, x2, y2 } => (
1353 2,
1354 0,
1355 [x1.to_bits(), y1.to_bits(), x2.to_bits(), y2.to_bits()],
1356 ),
1357 Timing::Steps { count, position } => (3, position as u8, [count, 0, 0, 0]),
1358 };
1359 self.records.push(kind);
1360 self.records.push(position);
1361 self.put_u16(if keyframe.from_presented {
1362 KEYFRAME_FROM_PRESENTED
1363 } else {
1364 0
1365 });
1366 for number in numbers {
1367 self.put_u32(number);
1368 }
1369 match &keyframe.value {
1370 KeyframeValue::Alpha(value) => self.put_u32(value.to_bits()),
1371 KeyframeValue::Paint(paint) => self.put_u64(paint.to_bits()),
1372 KeyframeValue::Transform(functions) => {
1373 let count = u16::try_from(functions.len())
1374 .map_err(|_| ProtocolError::LengthOverflow)?;
1375 self.put_u16(count);
1376 self.put_u16(0);
1377 for function in functions.iter() {
1378 self.put_u32(u32::from(u16::from(function.kind)));
1379 for value in function.values {
1380 self.put_u32(value.to_bits());
1381 }
1382 }
1383 }
1384 }
1385 }
1386 Ok(())
1387 }
1388
1389 fn begin_record(&mut self, tag: u16, payload_len: u16) {
1390 self.put_u16(tag);
1391 self.put_u16(payload_len + 4);
1392 }
1393
1394 fn put_palette_ref(
1397 &mut self,
1398 palette: Option<&crate::palette::Palette>,
1399 ) -> Result<(), ProtocolError> {
1400 let start = self.strings.len();
1401 let offset = u32::try_from(start).map_err(|_| ProtocolError::LengthOverflow)?;
1402 if let Some(palette) = palette {
1403 palette.write_into(&mut self.strings);
1404 }
1405 let len =
1406 u32::try_from(self.strings.len() - start).map_err(|_| ProtocolError::LengthOverflow)?;
1407 self.string_fixups.push(self.records.len());
1408 self.put_u32(offset);
1409 self.put_u32(len);
1410 Ok(())
1411 }
1412
1413 fn put_string_ref(&mut self, value: &str) -> Result<(), ProtocolError> {
1414 self.put_bytes_ref(value.as_bytes())
1415 }
1416
1417 fn put_bytes_ref(&mut self, value: &[u8]) -> Result<(), ProtocolError> {
1420 let offset =
1421 u32::try_from(self.strings.len()).map_err(|_| ProtocolError::LengthOverflow)?;
1422 let len = u32::try_from(value.len()).map_err(|_| ProtocolError::LengthOverflow)?;
1423 self.string_fixups.push(self.records.len());
1424 self.put_u32(offset);
1425 self.put_u32(len);
1426 self.strings.extend_from_slice(value);
1427 Ok(())
1428 }
1429
1430 fn put_u16(&mut self, value: u16) {
1431 self.records.extend_from_slice(&value.to_le_bytes());
1432 }
1433
1434 fn put_u32(&mut self, value: u32) {
1435 self.records.extend_from_slice(&value.to_le_bytes());
1436 }
1437
1438 fn put_u64(&mut self, value: u64) {
1439 self.records.extend_from_slice(&value.to_le_bytes());
1440 }
1441}
1442
1443pub fn decode_batch(bytes: &[u8]) -> Result<Vec<Mutation<'_>>, ProtocolError> {
1444 if bytes.len() < ENVELOPE_LEN
1445 || read_u16(bytes, 0)? != TAG_ENVELOPE
1446 || read_u16(bytes, 2)? != 12
1447 {
1448 return Err(ProtocolError::InvalidEnvelope);
1449 }
1450 let records_len =
1451 usize::try_from(read_u32(bytes, 4)?).map_err(|_| ProtocolError::LengthOverflow)?;
1452 let count = usize::try_from(read_u32(bytes, 8)?).map_err(|_| ProtocolError::LengthOverflow)?;
1453 if records_len < ENVELOPE_LEN || records_len > bytes.len() || records_len % 4 != 0 {
1454 return Err(ProtocolError::InvalidEnvelope);
1455 }
1456 let capacity = count.min((records_len - ENVELOPE_LEN) / MIN_RECORD_LEN);
1461 let mut output = Vec::with_capacity(capacity);
1462 let mut position = ENVELOPE_LEN;
1463 while position < records_len {
1464 let tag = read_u16(bytes, position)?;
1465 let len = usize::from(read_u16(bytes, position + 2)?);
1466 if len < 4 || len % 4 != 0 || position + len > records_len {
1467 return Err(ProtocolError::InvalidRecordLength);
1468 }
1469 let payload = position + 4;
1470 let mutation = match tag {
1471 TAG_CREATE if len == 12 => Mutation::Create {
1472 node_id: read_u32(bytes, payload)?,
1473 widget: WidgetKind::try_from(read_u16(bytes, payload + 4)?).map_err(|()| {
1474 ProtocolError::InvalidWidget(read_u16(bytes, payload + 4).unwrap_or(0))
1475 })?,
1476 },
1477 TAG_SET_PROP if len == 24 => {
1478 let property_raw = read_u16(bytes, payload + 4)?;
1479 let value_kind = read_u16(bytes, payload + 6)?;
1480 let value = match value_kind {
1481 VALUE_NONE => PropertyValue::None,
1482 VALUE_STRING => {
1483 PropertyValue::String(read_string(bytes, payload + 8, records_len)?)
1484 }
1485 VALUE_BOOL => PropertyValue::Bool(read_u64(bytes, payload + 8)? != 0),
1486 VALUE_I64 => PropertyValue::Integer(read_u64(bytes, payload + 8)? as i64),
1487 VALUE_F32 => {
1488 PropertyValue::Float(f32::from_bits(read_u64(bytes, payload + 8)? as u32))
1489 }
1490 VALUE_BYTES => {
1491 PropertyValue::Bytes(read_bytes(bytes, payload + 8, records_len)?)
1492 }
1493 other => return Err(ProtocolError::InvalidValueKind(other)),
1494 };
1495 Mutation::SetProp {
1496 node_id: read_u32(bytes, payload)?,
1497 property: PropertyKind::try_from(property_raw)
1498 .map_err(|()| ProtocolError::InvalidProperty(property_raw))?,
1499 value,
1500 }
1501 }
1502 TAG_SET_MODIFIER if len >= 28 => {
1503 let modifier_tag = read_u16(bytes, payload + 6)?;
1507 let extra = usize::from(
1508 modifier_extra_words(modifier_tag)
1509 .ok_or(ProtocolError::InvalidModifier(modifier_tag))?,
1510 );
1511 if len != 28 + 8 * extra {
1512 return Err(ProtocolError::InvalidRecordLength);
1513 }
1514 let mut words = [0_u64; MAX_MODIFIER_WORDS];
1515 for (word_index, word) in words.iter_mut().take(2 + extra).enumerate() {
1516 *word = read_u64(bytes, payload + 8 + 8 * word_index)?;
1517 }
1518 Mutation::SetModifier {
1519 node_id: read_u32(bytes, payload)?,
1520 index: read_u16(bytes, payload + 4)?,
1521 modifier: decode_modifier(modifier_tag, &words)?,
1522 }
1523 }
1524 TAG_INSERT if len == 16 => Mutation::Insert {
1525 parent_id: read_u32(bytes, payload)?,
1526 node_id: read_u32(bytes, payload + 4)?,
1527 index: read_u32(bytes, payload + 8)?,
1528 },
1529 TAG_MOVE if len == 16 => Mutation::Move {
1530 parent_id: read_u32(bytes, payload)?,
1531 node_id: read_u32(bytes, payload + 4)?,
1532 index: read_u32(bytes, payload + 8)?,
1533 },
1534 TAG_REMOVE if len == 8 => Mutation::Remove {
1535 node_id: read_u32(bytes, payload)?,
1536 },
1537 TAG_SET_TEXT if len == 24 => {
1538 let start = read_u32(bytes, payload + 12)?;
1539 let end = read_u32(bytes, payload + 16)?;
1540 Mutation::SetText {
1541 node_id: read_u32(bytes, payload)?,
1542 text: read_string(bytes, payload + 4, records_len)?,
1543 selection: (start != u32::MAX || end != u32::MAX)
1544 .then_some(Selection { start, end }),
1545 }
1546 }
1547 TAG_APPEND_TEXT if len == 16 => Mutation::AppendText {
1548 node_id: read_u32(bytes, payload)?,
1549 text: read_string(bytes, payload + 4, records_len)?,
1550 },
1551 TAG_SET_THEME if len == 4 + THEME_PAYLOAD_LEN => {
1552 let design_system = read_u16(bytes, payload)?;
1553 let fallback = read_u16(bytes, payload + 2)?;
1554 let color_scheme = read_u16(bytes, payload + 4)?;
1555 let adaptive = read_u16(bytes, payload + 6)?;
1556 if adaptive > 1 {
1557 return Err(ProtocolError::InvalidTheme(adaptive));
1558 }
1559 let mut fonts = [0u32; TYPE_ROLE_COUNT];
1560 for (index, slot) in fonts.iter_mut().enumerate() {
1561 *slot = read_u32(bytes, payload + 8 + 4 * index)?;
1562 }
1563 let palette_bytes =
1568 read_bytes(bytes, payload + 8 + 4 * TYPE_ROLE_COUNT, records_len)?;
1569 let palette = if palette_bytes.is_empty() {
1570 None
1571 } else {
1572 let (palette, _problems) = crate::palette::decode_palette(palette_bytes);
1573 Some(&*Box::leak(Box::new(palette)))
1574 };
1575 Mutation::SetTheme(Theme {
1576 design_system: DesignSystem::try_from(design_system)
1577 .map_err(|()| ProtocolError::InvalidTheme(design_system))?,
1578 fallback: DesignSystem::try_from(fallback)
1579 .map_err(|()| ProtocolError::InvalidTheme(fallback))?,
1580 color_scheme: ColorScheme::try_from(color_scheme)
1581 .map_err(|()| ProtocolError::InvalidTheme(color_scheme))?,
1582 adaptive: adaptive == 1,
1583 fonts,
1584 palette,
1585 })
1586 }
1587 TAG_SET_WINDOW if len == 32 => {
1588 let chrome = read_u16(bytes, payload)?;
1589 let title_bar = read_u16(bytes, payload + 2)?;
1590 let resizable = read_u16(bytes, payload + 12)?;
1591 if resizable > 1 {
1592 return Err(ProtocolError::InvalidValueKind(resizable));
1593 }
1594 let zoom = read_u16(bytes, payload + 14)?;
1595 let zoom_level = match zoom & 0xFF {
1596 0 => None,
1597 1 => {
1598 let level = (zoom >> 8) as u8 as i8;
1599 if !(crate::zoom::MIN_ZOOM_LEVEL..=crate::zoom::MAX_ZOOM_LEVEL)
1600 .contains(&level)
1601 {
1602 return Err(ProtocolError::InvalidValueKind(zoom));
1603 }
1604 Some(level)
1605 }
1606 _ => return Err(ProtocolError::InvalidValueKind(zoom)),
1607 };
1608 Mutation::SetWindow(crate::schema::Window {
1609 chrome: crate::schema::Chrome::try_from(chrome)
1610 .map_err(|()| ProtocolError::InvalidValueKind(chrome))?,
1611 title_bar: crate::schema::TitleBar::try_from(title_bar)
1612 .map_err(|()| ProtocolError::InvalidValueKind(title_bar))?,
1613 width: read_u16(bytes, payload + 4)?,
1614 height: read_u16(bytes, payload + 6)?,
1615 min_width: read_u16(bytes, payload + 8)?,
1616 min_height: read_u16(bytes, payload + 10)?,
1617 resizable: resizable == 1,
1618 zoom_level,
1619 icon: read_u32(bytes, payload + 24)?,
1623 title: Box::leak(
1624 read_string(bytes, payload + 16, records_len)?
1625 .to_owned()
1626 .into_boxed_str(),
1627 ),
1628 })
1629 }
1630 TAG_REGISTER_ASSET if len == 20 => {
1631 let raw_kind = read_u16(bytes, payload + 4)?;
1632 Mutation::RegisterAsset {
1633 asset_id: read_u32(bytes, payload)?,
1634 kind: AssetKind::try_from(raw_kind)
1635 .map_err(|()| ProtocolError::InvalidAssetKind(raw_kind))?,
1636 bytes: read_bytes(bytes, payload + 8, records_len)?,
1637 }
1638 }
1639 TAG_RELEASE_ASSET if len == 8 => Mutation::ReleaseAsset {
1640 asset_id: read_u32(bytes, payload)?,
1641 },
1642 TAG_SHOW_MESSAGE if len == 32 => {
1643 let raw_duration = read_u16(bytes, payload + 24)?;
1644 Mutation::ShowMessage {
1645 handler_id: read_u64(bytes, payload)?,
1646 text: read_string(bytes, payload + 8, records_len)?,
1647 action: read_string(bytes, payload + 16, records_len)?,
1648 duration: MessageDuration::try_from(raw_duration)
1649 .map_err(|()| ProtocolError::InvalidMessageDuration(raw_duration))?,
1650 }
1651 }
1652 TAG_EDIT_CODE if len == 40 => Mutation::EditCode {
1653 node_id: read_u32(bytes, payload)?,
1654 request_id: read_u32(bytes, payload + 4)?,
1655 base_version: read_u32(bytes, payload + 8)?,
1656 start_line: read_u32(bytes, payload + 12)?,
1657 start_column: read_u32(bytes, payload + 16)?,
1658 end_line: read_u32(bytes, payload + 20)?,
1659 end_column: read_u32(bytes, payload + 24)?,
1660 text: read_string(bytes, payload + 28, records_len)?,
1661 },
1662 TAG_POST_NOTIFICATION if len == 56 => {
1663 let raw_importance = read_u16(bytes, payload + 48)?;
1664 let raw_presentation = read_u16(bytes, payload + 50)?;
1665 Mutation::PostNotification {
1666 key: read_string(bytes, payload, records_len)?,
1667 title: read_string(bytes, payload + 8, records_len)?,
1668 body: read_string(bytes, payload + 16, records_len)?,
1669 channel: read_string(bytes, payload + 24, records_len)?,
1670 action_1: read_string(bytes, payload + 32, records_len)?,
1671 action_2: read_string(bytes, payload + 40, records_len)?,
1672 importance: NotificationImportance::try_from(raw_importance)
1673 .map_err(|()| ProtocolError::InvalidNotification(raw_importance))?,
1674 presentation: NotificationPresentation::try_from(raw_presentation)
1675 .map_err(|()| ProtocolError::InvalidNotification(raw_presentation))?,
1676 }
1677 }
1678 TAG_WITHDRAW_NOTIFICATION if len == 12 => Mutation::WithdrawNotification {
1679 key: read_string(bytes, payload, records_len)?,
1680 },
1681 TAG_REQUEST_NOTIFICATION_PERMISSION if len == 4 => {
1682 Mutation::RequestNotificationPermission
1683 }
1684 TAG_START_ANIMATION if len >= ANIMATION_HEADER_LEN => {
1685 Mutation::StartAnimation(
1688 decode_animation(&bytes[position..position + len]).map_err(|error| {
1689 if error == ProtocolError::Truncated {
1690 ProtocolError::InvalidAnimation
1691 } else {
1692 error
1693 }
1694 })?,
1695 )
1696 }
1697 TAG_CONTROL_ANIMATION if len == 24 => {
1698 let raw_property = u16::from(read_u8(bytes, payload + 8)?);
1699 let raw_op = u16::from(read_u8(bytes, payload + 10)?);
1700 Mutation::ControlAnimation {
1701 node_id: read_u32(bytes, payload)?,
1702 animation_id: read_u32(bytes, payload + 4)?,
1703 property: crate::schema::AnimatedProperty::try_from(raw_property)
1704 .map_err(|()| ProtocolError::InvalidAnimation)?,
1705 slot: read_u8(bytes, payload + 9)?,
1706 op: crate::schema::AnimationControl::try_from(raw_op)
1707 .map_err(|()| ProtocolError::InvalidAnimation)?,
1708 at_time_nanos: read_u64(bytes, payload + 12)?,
1709 }
1710 }
1711 TAG_SET_SELECTION if len == 28 => Mutation::SetSelection {
1712 node_id: read_u32(bytes, payload)?,
1713 base_version: read_u32(bytes, payload + 4)?,
1714 anchor_line: read_u32(bytes, payload + 8)?,
1715 anchor_column: read_u32(bytes, payload + 12)?,
1716 active_line: read_u32(bytes, payload + 16)?,
1717 active_column: read_u32(bytes, payload + 20)?,
1718 },
1719 TAG_REVEAL_RANGE if len == 32 => {
1720 let raw_align = read_u32(bytes, payload + 24)?;
1721 Mutation::RevealRange {
1722 node_id: read_u32(bytes, payload)?,
1723 base_version: read_u32(bytes, payload + 4)?,
1724 start_line: read_u32(bytes, payload + 8)?,
1725 start_column: read_u32(bytes, payload + 12)?,
1726 end_line: read_u32(bytes, payload + 16)?,
1727 end_column: read_u32(bytes, payload + 20)?,
1728 align: u16::try_from(raw_align)
1729 .ok()
1730 .and_then(|tag| crate::schema::RevealAlign::try_from(tag).ok())
1731 .ok_or(ProtocolError::InvalidValueKind(
1732 raw_align.min(u32::from(u16::MAX)) as u16,
1733 ))?,
1734 }
1735 }
1736 TAG_SET_FOLDED if len == 16 => Mutation::SetFolded {
1737 node_id: read_u32(bytes, payload)?,
1738 start_line: read_u32(bytes, payload + 4)?,
1739 collapsed: read_flag(bytes, payload + 8)?,
1740 },
1741 TAG_CREATE..=TAG_EDIT_CODE
1742 | TAG_START_ANIMATION
1743 | TAG_CONTROL_ANIMATION
1744 | TAG_SET_SELECTION..=TAG_SET_FOLDED => {
1745 return Err(ProtocolError::InvalidRecordLength);
1746 }
1747 other => return Err(ProtocolError::InvalidTag(other)),
1748 };
1749 output.push(mutation);
1750 position += len;
1751 }
1752 if output.len() != count {
1753 return Err(ProtocolError::InvalidEnvelope);
1754 }
1755 Ok(output)
1756}
1757
1758fn decode_animation(record: &[u8]) -> Result<crate::schema::Animation<'static>, ProtocolError> {
1762 use crate::schema::{
1763 AnimatedProperty, Animation, AnimationEvents, ColorInterpolation, FillMode, Keyframe,
1764 KeyframeValue, PlayState, PlaybackDirection, StepPosition, Timing, TransformFunction,
1765 TransformFunctionKind,
1766 };
1767 let bad = ProtocolError::InvalidAnimation;
1768 fn byte_enum<T: TryFrom<u16, Error = ()>>(
1769 record: &[u8],
1770 at: usize,
1771 ) -> Result<T, ProtocolError> {
1772 T::try_from(u16::from(read_u8(record, at)?)).map_err(|()| ProtocolError::InvalidAnimation)
1773 }
1774 let float = |at: usize| read_u32(record, at).map(f32::from_bits);
1775 let property: AnimatedProperty = byte_enum(record, 12)?;
1776 let direction: PlaybackDirection = byte_enum(record, 14)?;
1777 let fill: FillMode = byte_enum(record, 15)?;
1778 let play_state: PlayState = byte_enum(record, 16)?;
1779 let colour = matches!(
1780 property,
1781 AnimatedProperty::Color | AnimatedProperty::Background | AnimatedProperty::BorderColor
1782 );
1783 let interpolation = match (colour, read_u8(record, 17)?) {
1784 (false, 0) => None,
1785 (true, raw) => Some(
1786 ColorInterpolation::try_from(u16::from(raw))
1787 .map_err(|()| ProtocolError::InvalidAnimation)?,
1788 ),
1789 (false, _) => return Err(bad),
1790 };
1791 let keyframe_count = usize::from(read_u16(record, 18)?);
1792 let delay_ms = float(28)?;
1793 let duration_ms = float(32)?;
1794 let iterations = float(36)?;
1795 let origin_x = float(40)?;
1796 let origin_y = float(44)?;
1797 let events = read_u8(record, 48)?;
1798 if keyframe_count < 2
1799 || !delay_ms.is_finite()
1800 || !(duration_ms.is_finite() && duration_ms >= 0.0)
1801 || iterations.is_nan()
1802 || iterations < 0.0
1803 || !(origin_x.is_finite() && origin_y.is_finite())
1804 || events & !AnimationEvents::ALL.0 != 0
1805 {
1806 return Err(bad);
1807 }
1808 let mut keyframes = Vec::with_capacity(keyframe_count);
1809 let mut at = ANIMATION_HEADER_LEN;
1810 for index in 0..keyframe_count {
1811 let offset = float(at)?;
1812 let raw_timing = read_u8(record, at + 4)?;
1813 let raw_position = read_u8(record, at + 5)?;
1814 let flags = read_u16(record, at + 6)?;
1815 let timing = match raw_timing {
1816 1 => Timing::Linear,
1817 2 => {
1818 let (x1, y1, x2, y2) = (
1819 float(at + 8)?,
1820 float(at + 12)?,
1821 float(at + 16)?,
1822 float(at + 20)?,
1823 );
1824 if ![x1, y1, x2, y2].iter().all(|value| value.is_finite())
1825 || !(0.0..=1.0).contains(&x1)
1826 || !(0.0..=1.0).contains(&x2)
1827 {
1828 return Err(bad);
1829 }
1830 Timing::CubicBezier { x1, y1, x2, y2 }
1831 }
1832 3 => {
1833 let count = read_u32(record, at + 8)?;
1834 let position =
1835 StepPosition::try_from(u16::from(raw_position)).map_err(|()| bad.clone())?;
1836 let least = if position == StepPosition::JumpNone {
1837 2
1838 } else {
1839 1
1840 };
1841 if count < least {
1842 return Err(bad);
1843 }
1844 Timing::Steps { count, position }
1845 }
1846 _ => return Err(bad),
1847 };
1848 if flags & !KEYFRAME_FROM_PRESENTED != 0
1849 || (index > 0 && flags & KEYFRAME_FROM_PRESENTED != 0)
1850 || !(0.0..=1.0).contains(&offset)
1851 {
1852 return Err(bad);
1853 }
1854 let value_at = at + KEYFRAME_HEAD_LEN;
1855 let (value, value_len) = match property {
1856 AnimatedProperty::Alpha => {
1857 let value = float(value_at)?;
1858 if !value.is_finite() {
1859 return Err(bad);
1860 }
1861 (KeyframeValue::Alpha(value), 4)
1862 }
1863 AnimatedProperty::Color
1864 | AnimatedProperty::Background
1865 | AnimatedProperty::BorderColor => {
1866 let paint = Paint::from_bits(read_u64(record, value_at)?)
1868 .filter(|paint| !matches!(paint, Paint::Asset(_)))
1869 .ok_or(bad.clone())?;
1870 (KeyframeValue::Paint(paint), 8)
1871 }
1872 AnimatedProperty::Transform => {
1873 let count = usize::from(read_u16(record, value_at)?);
1874 let mut functions = Vec::with_capacity(count);
1875 for function in 0..count {
1876 let function_at = value_at + 4 + TRANSFORM_FUNCTION_LEN * function;
1877 let raw_kind = read_u32(record, function_at)?;
1878 let kind = u16::try_from(raw_kind)
1879 .ok()
1880 .and_then(|kind| TransformFunctionKind::try_from(kind).ok())
1881 .ok_or(bad.clone())?;
1882 let mut values = [0.0_f32; 6];
1883 for (slot, value) in values.iter_mut().enumerate() {
1884 *value = float(function_at + 4 + 4 * slot)?;
1885 }
1886 if !values.iter().all(|value| value.is_finite()) {
1887 return Err(bad);
1888 }
1889 functions.push(TransformFunction { kind, values });
1890 }
1891 (
1892 KeyframeValue::Transform(std::borrow::Cow::Owned(functions)),
1893 4 + TRANSFORM_FUNCTION_LEN * count,
1894 )
1895 }
1896 };
1897 keyframes.push(Keyframe {
1898 offset,
1899 timing,
1900 from_presented: flags & KEYFRAME_FROM_PRESENTED != 0,
1901 value,
1902 });
1903 at = value_at + value_len;
1904 }
1905 if at != record.len() {
1906 return Err(bad);
1907 }
1908 let first = keyframes.first().map(|keyframe| keyframe.offset);
1912 let last = keyframes.last().map(|keyframe| keyframe.offset);
1913 if first != Some(0.0)
1914 || last != Some(1.0)
1915 || keyframes
1916 .windows(2)
1917 .any(|pair| pair[1].offset < pair[0].offset)
1918 {
1919 return Err(bad);
1920 }
1921 if let Some(KeyframeValue::Transform(shape)) = keyframes.first().map(|keyframe| &keyframe.value)
1922 {
1923 let kinds: Vec<_> = shape.iter().map(|function| function.kind).collect();
1924 let lined_up = keyframes.iter().all(|keyframe| match &keyframe.value {
1925 KeyframeValue::Transform(functions) => functions
1926 .iter()
1927 .map(|function| function.kind)
1928 .eq(kinds.iter().copied()),
1929 _ => false,
1930 });
1931 if !lined_up {
1932 return Err(bad);
1933 }
1934 }
1935 Ok(Animation {
1936 node_id: read_u32(record, 4)?,
1937 animation_id: read_u32(record, 8)?,
1938 property,
1939 slot: read_u8(record, 13)?,
1940 direction,
1941 fill,
1942 play_state,
1943 interpolation,
1944 start_time_nanos: read_u64(record, 20)?,
1945 delay_ms,
1946 duration_ms,
1947 iterations,
1948 origin_x,
1949 origin_y,
1950 events: AnimationEvents(events),
1951 keyframes: std::borrow::Cow::Owned(keyframes),
1952 })
1953}
1954
1955const fn pack_floats(low: f32, high: f32) -> u64 {
1957 (low.to_bits() as u64) | ((high.to_bits() as u64) << 32)
1958}
1959
1960const fn unpack_low(word: u64) -> f32 {
1961 f32::from_bits(word as u32)
1962}
1963
1964const fn unpack_high(word: u64) -> f32 {
1965 f32::from_bits((word >> 32) as u32)
1966}
1967
1968const fn two_words(first: u64, second: u64) -> [u64; MAX_MODIFIER_WORDS] {
1971 [first, second, 0, 0, 0, 0]
1972}
1973
1974fn float_word(value: f32) -> u64 {
1975 u64::from(value.to_bits())
1976}
1977
1978fn modifier_fields(modifier: &Modifier) -> (u16, [u64; MAX_MODIFIER_WORDS]) {
1981 match modifier {
1982 Modifier::Empty => (0, two_words(0, 0)),
1983 Modifier::Padding(value) => (1, two_words(float_word(*value), 0)),
1984 Modifier::FillMaxWidth => (2, two_words(0, 0)),
1985 Modifier::FillMaxHeight => (3, two_words(0, 0)),
1986 Modifier::Width(value) => (4, two_words(float_word(*value), 0)),
1987 Modifier::Height(value) => (5, two_words(float_word(*value), 0)),
1988 Modifier::Size { width, height } => (6, two_words(float_word(*width), float_word(*height))),
1989 Modifier::Background(paint) => (7, two_words(paint.to_bits(), 0)),
1990 Modifier::Clickable { handler_id } => (8, two_words(*handler_id, 0)),
1991 Modifier::PaddingRole(role) => (9, two_words(*role as u64, 0)),
1992 Modifier::PaddingEach {
1993 start,
1994 top,
1995 end,
1996 bottom,
1997 } => (
1998 10,
1999 two_words(pack_floats(*start, *top), pack_floats(*end, *bottom)),
2000 ),
2001 Modifier::Weight(value) => (11, two_words(float_word(*value), 0)),
2002 Modifier::Shape {
2003 top_start,
2004 top_end,
2005 bottom_end,
2006 bottom_start,
2007 } => (
2008 12,
2009 two_words(
2010 pack_floats(*top_start, *top_end),
2011 pack_floats(*bottom_end, *bottom_start),
2012 ),
2013 ),
2014 Modifier::ShapeRole(role) => (13, two_words(*role as u64, 0)),
2015 Modifier::Border { width, paint } => (14, two_words(float_word(*width), paint.to_bits())),
2016 Modifier::Elevation(dp) => (15, two_words(float_word(*dp), 0)),
2017 Modifier::ObserveSize { token } => (16, two_words(u64::from(*token), 0)),
2018 Modifier::Motion(role) => (17, two_words(*role as u64, 0)),
2019 Modifier::Material(role) => (18, two_words(*role as u64, 0)),
2020 Modifier::Offset { x, y } => (19, two_words(float_word(*x), float_word(*y))),
2021 Modifier::RequiredSize { width, height } => {
2022 (20, two_words(float_word(*width), float_word(*height)))
2023 }
2024 Modifier::BorderEach {
2025 top,
2026 right,
2027 bottom,
2028 left,
2029 top_paint,
2030 right_paint,
2031 bottom_paint,
2032 left_paint,
2033 } => (
2034 21,
2035 [
2036 pack_floats(*top, *right),
2037 pack_floats(*bottom, *left),
2038 top_paint.to_bits(),
2039 right_paint.to_bits(),
2040 bottom_paint.to_bits(),
2041 left_paint.to_bits(),
2042 ],
2043 ),
2044 Modifier::CornerEach {
2045 top_left,
2046 top_right,
2047 bottom_right,
2048 bottom_left,
2049 } => (
2050 22,
2051 two_words(
2052 pack_floats(*top_left, *top_right),
2053 pack_floats(*bottom_right, *bottom_left),
2054 ),
2055 ),
2056 Modifier::Shadow {
2057 x,
2058 y,
2059 blur,
2060 spread,
2061 paint,
2062 } => (
2063 23,
2064 [
2065 pack_floats(*x, *y),
2066 pack_floats(*blur, *spread),
2067 paint.to_bits(),
2068 0,
2069 0,
2070 0,
2071 ],
2072 ),
2073 Modifier::Clip(enabled) => (24, two_words(u64::from(*enabled), 0)),
2074 Modifier::Alpha(value) => (25, two_words(float_word(*value), 0)),
2075 Modifier::Transform {
2076 a,
2077 b,
2078 c,
2079 d,
2080 e,
2081 f,
2082 origin_x,
2083 origin_y,
2084 } => (
2085 26,
2086 [
2087 pack_floats(*a, *b),
2088 pack_floats(*c, *d),
2089 pack_floats(*e, *f),
2090 pack_floats(*origin_x, *origin_y),
2091 0,
2092 0,
2093 ],
2094 ),
2095 }
2096}
2097
2098fn decode_role<T: TryFrom<u16, Error = ()>>(first: u64) -> Result<T, ProtocolError> {
2099 let tag = u16::try_from(first).map_err(|_| ProtocolError::InvalidModifier(0))?;
2100 T::try_from(tag).map_err(|()| ProtocolError::InvalidModifier(tag))
2101}
2102
2103fn decode_paint(bits: u64) -> Result<Paint, ProtocolError> {
2104 Paint::from_bits(bits).ok_or(ProtocolError::InvalidModifier(0))
2105}
2106
2107fn decode_modifier(tag: u16, words: &[u64; MAX_MODIFIER_WORDS]) -> Result<Modifier, ProtocolError> {
2108 let [first, second, third, fourth, fifth, sixth] = *words;
2109 match tag {
2110 0 => Ok(Modifier::Empty),
2111 1 => Ok(Modifier::Padding(f32::from_bits(first as u32))),
2112 2 => Ok(Modifier::FillMaxWidth),
2113 3 => Ok(Modifier::FillMaxHeight),
2114 4 => Ok(Modifier::Width(f32::from_bits(first as u32))),
2115 5 => Ok(Modifier::Height(f32::from_bits(first as u32))),
2116 6 => Ok(Modifier::Size {
2117 width: f32::from_bits(first as u32),
2118 height: f32::from_bits(second as u32),
2119 }),
2120 7 => Ok(Modifier::Background(decode_paint(first)?)),
2121 8 => Ok(Modifier::Clickable { handler_id: first }),
2122 9 => Ok(Modifier::PaddingRole(decode_role::<SpaceRole>(first)?)),
2123 10 => Ok(Modifier::PaddingEach {
2124 start: unpack_low(first),
2125 top: unpack_high(first),
2126 end: unpack_low(second),
2127 bottom: unpack_high(second),
2128 }),
2129 11 => Ok(Modifier::Weight(f32::from_bits(first as u32))),
2130 12 => Ok(Modifier::Shape {
2131 top_start: unpack_low(first),
2132 top_end: unpack_high(first),
2133 bottom_end: unpack_low(second),
2134 bottom_start: unpack_high(second),
2135 }),
2136 13 => Ok(Modifier::ShapeRole(decode_role::<ShapeRole>(first)?)),
2137 14 => Ok(Modifier::Border {
2138 width: f32::from_bits(first as u32),
2139 paint: decode_paint(second)?,
2140 }),
2141 15 => Ok(Modifier::Elevation(f32::from_bits(first as u32))),
2142 16 => Ok(Modifier::ObserveSize {
2143 token: first as u32,
2144 }),
2145 17 => Ok(Modifier::Motion(decode_role::<MotionRole>(first)?)),
2146 18 => Ok(Modifier::Material(decode_role::<MaterialRole>(first)?)),
2147 19 => Ok(Modifier::Offset {
2148 x: f32::from_bits(first as u32),
2149 y: f32::from_bits(second as u32),
2150 }),
2151 20 => Ok(Modifier::RequiredSize {
2152 width: f32::from_bits(first as u32),
2153 height: f32::from_bits(second as u32),
2154 }),
2155 21 => Ok(Modifier::BorderEach {
2156 top: unpack_low(first),
2157 right: unpack_high(first),
2158 bottom: unpack_low(second),
2159 left: unpack_high(second),
2160 top_paint: decode_paint(third)?,
2161 right_paint: decode_paint(fourth)?,
2162 bottom_paint: decode_paint(fifth)?,
2163 left_paint: decode_paint(sixth)?,
2164 }),
2165 22 => Ok(Modifier::CornerEach {
2166 top_left: unpack_low(first),
2167 top_right: unpack_high(first),
2168 bottom_right: unpack_low(second),
2169 bottom_left: unpack_high(second),
2170 }),
2171 23 => Ok(Modifier::Shadow {
2172 x: unpack_low(first),
2173 y: unpack_high(first),
2174 blur: unpack_low(second),
2175 spread: unpack_high(second),
2176 paint: decode_paint(third)?,
2177 }),
2178 24 => match first {
2181 0 => Ok(Modifier::Clip(false)),
2182 1 => Ok(Modifier::Clip(true)),
2183 _ => Err(ProtocolError::InvalidModifier(24)),
2184 },
2185 25 => Ok(Modifier::Alpha(f32::from_bits(first as u32))),
2186 26 => {
2187 let values = [
2188 unpack_low(first),
2189 unpack_high(first),
2190 unpack_low(second),
2191 unpack_high(second),
2192 unpack_low(third),
2193 unpack_high(third),
2194 unpack_low(fourth),
2195 unpack_high(fourth),
2196 ];
2197 if values.iter().any(|value| !value.is_finite()) {
2201 return Err(ProtocolError::InvalidModifier(26));
2202 }
2203 let [a, b, c, d, e, f, origin_x, origin_y] = values;
2204 Ok(Modifier::Transform {
2205 a,
2206 b,
2207 c,
2208 d,
2209 e,
2210 f,
2211 origin_x,
2212 origin_y,
2213 })
2214 }
2215 other => Err(ProtocolError::InvalidModifier(other)),
2216 }
2217}
2218
2219fn read_string(bytes: &[u8], position: usize, arena_start: usize) -> Result<&str, ProtocolError> {
2226 let value = read_bytes(bytes, position, arena_start)?;
2227 std::str::from_utf8(value).map_err(|_| ProtocolError::InvalidUtf8)
2228}
2229
2230fn read_bytes(bytes: &[u8], position: usize, arena_start: usize) -> Result<&[u8], ProtocolError> {
2232 let offset =
2233 usize::try_from(read_u32(bytes, position)?).map_err(|_| ProtocolError::LengthOverflow)?;
2234 let len = usize::try_from(read_u32(bytes, position + 4)?)
2235 .map_err(|_| ProtocolError::LengthOverflow)?;
2236 let end = offset
2237 .checked_add(len)
2238 .ok_or(ProtocolError::InvalidStringRange)?;
2239 if offset < arena_start {
2240 return Err(ProtocolError::InvalidStringRange);
2241 }
2242 bytes
2243 .get(offset..end)
2244 .ok_or(ProtocolError::InvalidStringRange)
2245}
2246
2247fn read_u8(bytes: &[u8], position: usize) -> Result<u8, ProtocolError> {
2248 bytes.get(position).copied().ok_or(ProtocolError::Truncated)
2249}
2250
2251fn read_u16(bytes: &[u8], position: usize) -> Result<u16, ProtocolError> {
2252 let value = bytes
2253 .get(position..position + 2)
2254 .ok_or(ProtocolError::Truncated)?;
2255 Ok(u16::from_le_bytes([value[0], value[1]]))
2256}
2257
2258fn read_u32(bytes: &[u8], position: usize) -> Result<u32, ProtocolError> {
2259 let value = bytes
2260 .get(position..position + 4)
2261 .ok_or(ProtocolError::Truncated)?;
2262 Ok(u32::from_le_bytes([value[0], value[1], value[2], value[3]]))
2263}
2264
2265fn read_flag(bytes: &[u8], position: usize) -> Result<bool, ProtocolError> {
2267 match read_u32(bytes, position)? {
2268 0 => Ok(false),
2269 1 => Ok(true),
2270 other => Err(ProtocolError::InvalidValueKind(
2271 other.min(u32::from(u16::MAX)) as u16,
2272 )),
2273 }
2274}
2275
2276fn read_u64(bytes: &[u8], position: usize) -> Result<u64, ProtocolError> {
2277 let value = bytes
2278 .get(position..position + 8)
2279 .ok_or(ProtocolError::Truncated)?;
2280 Ok(u64::from_le_bytes(
2281 value.try_into().map_err(|_| ProtocolError::Truncated)?,
2282 ))
2283}
2284
2285#[cfg(test)]
2286mod tests {
2287 use super::*;
2288 use crate::schema::TypeRole;
2289
2290 #[test]
2291 fn round_trips_all_m0_mutations() {
2292 let mutations = [
2293 Mutation::Create {
2294 node_id: 1,
2295 widget: WidgetKind::Column,
2296 },
2297 Mutation::SetProp {
2298 node_id: 2,
2299 property: PropertyKind::Text,
2300 value: PropertyValue::String("안녕"),
2301 },
2302 Mutation::SetProp {
2303 node_id: 3,
2304 property: PropertyKind::Enabled,
2305 value: PropertyValue::Bool(true),
2306 },
2307 Mutation::SetModifier {
2308 node_id: 1,
2309 index: 0,
2310 modifier: Modifier::Padding(16.0),
2311 },
2312 Mutation::Insert {
2313 parent_id: 1,
2314 node_id: 2,
2315 index: 0,
2316 },
2317 Mutation::Move {
2318 parent_id: 1,
2319 node_id: 2,
2320 index: 1,
2321 },
2322 Mutation::Remove { node_id: 3 },
2323 Mutation::SetText {
2324 node_id: 4,
2325 text: "compose",
2326 selection: Some(Selection { start: 1, end: 4 }),
2327 },
2328 Mutation::AppendText {
2329 node_id: 4,
2330 text: " token",
2331 },
2332 ];
2333 let mut encoder = BatchEncoder::default();
2334 for mutation in &mutations {
2335 encoder.encode(mutation).unwrap();
2336 }
2337 let decoded = decode_batch(encoder.finish().unwrap()).unwrap();
2338 assert_eq!(decoded, mutations);
2339 }
2340
2341 #[test]
2344 fn fr13_paint_role_and_literal_share_one_record_length() {
2345 let encode_one = |modifier| {
2346 let mut encoder = BatchEncoder::default();
2347 encoder
2348 .encode(&Mutation::SetModifier {
2349 node_id: 1,
2350 index: 0,
2351 modifier,
2352 })
2353 .unwrap();
2354 encoder.finish().unwrap().to_vec()
2355 };
2356 let role = encode_one(Modifier::Background(Paint::Role(
2357 crate::schema::ColorRole::Surface,
2358 )));
2359 let literal = encode_one(Modifier::Background(Paint::Literal(
2360 crate::schema::Color::rgb(0x1b1b1f),
2361 )));
2362 assert_eq!(role.len(), literal.len());
2363 assert_eq!(
2364 decode_batch(&role).unwrap(),
2365 [Mutation::SetModifier {
2366 node_id: 1,
2367 index: 0,
2368 modifier: Modifier::Background(Paint::Role(crate::schema::ColorRole::Surface)),
2369 }]
2370 );
2371 assert_eq!(
2372 decode_batch(&literal).unwrap(),
2373 [Mutation::SetModifier {
2374 node_id: 1,
2375 index: 0,
2376 modifier: Modifier::Background(Paint::Literal(crate::schema::Color::rgb(0x1b1b1f))),
2377 }]
2378 );
2379 }
2380
2381 #[test]
2383 fn fr13_design_modifiers_round_trip_in_two_words() {
2384 let modifiers = [
2385 Modifier::PaddingRole(SpaceRole::Md),
2386 Modifier::PaddingEach {
2387 start: 1.0,
2388 top: 2.0,
2389 end: 3.0,
2390 bottom: 4.0,
2391 },
2392 Modifier::Weight(0.25),
2393 Modifier::Shape {
2394 top_start: 4.0,
2395 top_end: 8.0,
2396 bottom_end: 12.0,
2397 bottom_start: 16.0,
2398 },
2399 Modifier::ShapeRole(ShapeRole::Full),
2400 Modifier::Border {
2401 width: 2.0,
2402 paint: Paint::Role(crate::schema::ColorRole::Outline),
2403 },
2404 Modifier::Elevation(6.0),
2405 ];
2406 let mut encoder = BatchEncoder::default();
2407 let expected: Vec<_> = modifiers
2408 .iter()
2409 .enumerate()
2410 .map(|(index, modifier)| Mutation::SetModifier {
2411 node_id: 1,
2412 index: index as u16,
2413 modifier: modifier.clone(),
2414 })
2415 .collect();
2416 for mutation in &expected {
2417 encoder.encode(mutation).unwrap();
2418 }
2419 let bytes = encoder.finish().unwrap();
2420 assert_eq!(bytes.len(), ENVELOPE_LEN + expected.len() * 28);
2422 assert_eq!(decode_batch(bytes).unwrap(), expected);
2423 }
2424
2425 #[test]
2431 fn fr23_set_theme_round_trips_with_a_slot_for_every_type_role() {
2432 let theme = Theme::adaptive(DesignSystem::Cupertino)
2433 .with_color_scheme(ColorScheme::Dark)
2434 .with_font(TypeRole::Display, 7);
2435 let length = 20 + 4 * TYPE_ROLE_COUNT as u16;
2436 let mut encoder = BatchEncoder::default();
2437 encoder.encode(&Mutation::SetTheme(theme)).unwrap();
2438 let bytes = encoder.finish().unwrap();
2439 assert_eq!(bytes.len(), ENVELOPE_LEN + length as usize);
2440 assert_eq!(read_u16(bytes, ENVELOPE_LEN).unwrap(), TAG_SET_THEME);
2441 assert_eq!(read_u16(bytes, ENVELOPE_LEN + 2).unwrap(), length);
2442 assert_eq!(decode_batch(bytes).unwrap(), [Mutation::SetTheme(theme)]);
2443 }
2444
2445 #[test]
2447 fn fr23_a_font_is_named_for_one_role_and_no_others() {
2448 let theme = Theme::unified(DesignSystem::Material3).with_font(TypeRole::Display, 3);
2449 assert_eq!(theme.font(TypeRole::Display), Some(3));
2450 for role in [
2451 TypeRole::Body,
2452 TypeRole::Title,
2453 TypeRole::Mono,
2454 TypeRole::Caption,
2455 ] {
2456 assert_eq!(theme.font(role), None);
2457 }
2458 }
2459
2460 #[test]
2462 fn fr14_rejects_unknown_theme_tags() {
2463 let mut encoder = BatchEncoder::default();
2464 encoder
2465 .encode(&Mutation::SetTheme(Theme::default()))
2466 .unwrap();
2467 let mut bytes = encoder.finish().unwrap().to_vec();
2468 bytes[ENVELOPE_LEN + 4..ENVELOPE_LEN + 6].copy_from_slice(&99_u16.to_le_bytes());
2469 assert_eq!(decode_batch(&bytes), Err(ProtocolError::InvalidTheme(99)));
2470 }
2471
2472 #[test]
2473 fn rejects_unknown_tags_without_panicking() {
2474 let mut bytes = vec![0, 0, 12, 0, 20, 0, 0, 0, 1, 0, 0, 0];
2475 bytes.extend_from_slice(&999_u16.to_le_bytes());
2476 bytes.extend_from_slice(&8_u16.to_le_bytes());
2477 bytes.extend_from_slice(&0_u32.to_le_bytes());
2478 assert_eq!(decode_batch(&bytes), Err(ProtocolError::InvalidTag(999)));
2479 }
2480
2481 #[test]
2482 fn pr4_renderer_events_round_trip_fixed_layout() {
2483 let events = [
2484 HostEvent {
2485 node_id: 7,
2486 handler_id: 11,
2487 payload: crate::EventPayload::Clicked,
2488 },
2489 HostEvent {
2490 node_id: 8,
2491 handler_id: 12,
2492 payload: crate::EventPayload::TextChanged("한글"),
2493 },
2494 HostEvent {
2495 node_id: 8,
2496 handler_id: 13,
2497 payload: crate::EventPayload::TextSubmitted("done"),
2498 },
2499 HostEvent {
2500 node_id: 8,
2501 handler_id: 14,
2502 payload: crate::EventPayload::FocusLost,
2503 },
2504 HostEvent {
2505 node_id: 9,
2506 handler_id: 16,
2507 payload: crate::EventPayload::RangeRequested {
2508 start: 100,
2509 count: 20,
2510 },
2511 },
2512 HostEvent {
2513 node_id: 8,
2514 handler_id: 15,
2515 payload: crate::EventPayload::KeyDown {
2516 key: Key::Enter,
2517 shift_key: true,
2518 ctrl_key: false,
2519 alt_key: true,
2520 meta_key: false,
2521 },
2522 },
2523 ];
2524 let mut bytes = Vec::new();
2525 for event in events {
2526 encode_event(&event, &mut bytes).unwrap();
2527 assert_eq!(decode_event(&bytes).unwrap(), event);
2528 }
2529 }
2530
2531 #[test]
2532 fn fr20_window_size_changed_round_trips_in_thirty_two_bytes() {
2533 let event = HostEvent {
2534 node_id: 0,
2535 handler_id: 0,
2536 payload: crate::EventPayload::WindowSizeChanged {
2537 width_dp: 841.5,
2538 height_dp: 600.25,
2539 class: crate::WindowSizeClass::Expanded,
2540 height_class: crate::WindowHeightClass::Medium,
2541 },
2542 };
2543 let mut bytes = Vec::new();
2544 encode_event(&event, &mut bytes).unwrap();
2545 assert_eq!(bytes.len(), 32);
2546 assert_eq!(decode_event(&bytes).unwrap(), event);
2547 }
2548
2549 #[test]
2550 fn fr20_unknown_window_size_class_is_a_protocol_error() {
2551 let event = HostEvent {
2552 node_id: 0,
2553 handler_id: 0,
2554 payload: crate::EventPayload::WindowSizeChanged {
2555 width_dp: 320.0,
2556 height_dp: 640.0,
2557 class: crate::WindowSizeClass::Compact,
2558 height_class: crate::WindowHeightClass::Medium,
2559 },
2560 };
2561 let mut bytes = Vec::new();
2562 encode_event(&event, &mut bytes).unwrap();
2563 bytes[24..28].copy_from_slice(&99_u32.to_le_bytes());
2564 assert_eq!(
2565 decode_event(&bytes),
2566 Err(ProtocolError::InvalidValueKind(99))
2567 );
2568 }
2569
2570 #[test]
2571 fn fr20_unknown_window_height_class_is_a_protocol_error() {
2572 let event = HostEvent {
2573 node_id: 0,
2574 handler_id: 0,
2575 payload: crate::EventPayload::WindowSizeChanged {
2576 width_dp: 320.0,
2577 height_dp: 640.0,
2578 class: crate::WindowSizeClass::Compact,
2579 height_class: crate::WindowHeightClass::Medium,
2580 },
2581 };
2582 let mut bytes = Vec::new();
2583 encode_event(&event, &mut bytes).unwrap();
2584 bytes[28..32].copy_from_slice(&99_u32.to_le_bytes());
2585 assert_eq!(
2586 decode_event(&bytes),
2587 Err(ProtocolError::InvalidValueKind(99))
2588 );
2589 }
2590
2591 #[test]
2592 fn fr20_window_size_classes_follow_the_material_boundaries() {
2593 use crate::WindowSizeClass;
2594 assert_eq!(
2595 WindowSizeClass::from_width_dp(599.9),
2596 WindowSizeClass::Compact
2597 );
2598 assert_eq!(
2599 WindowSizeClass::from_width_dp(600.0),
2600 WindowSizeClass::Medium
2601 );
2602 assert_eq!(
2603 WindowSizeClass::from_width_dp(839.9),
2604 WindowSizeClass::Medium
2605 );
2606 assert_eq!(
2607 WindowSizeClass::from_width_dp(840.0),
2608 WindowSizeClass::Expanded
2609 );
2610 }
2611
2612 #[test]
2613 fn fr20_window_height_classes_follow_the_material_boundaries() {
2614 use crate::WindowHeightClass;
2615 assert_eq!(
2616 WindowHeightClass::from_height_dp(479.9),
2617 WindowHeightClass::Compact
2618 );
2619 assert_eq!(
2620 WindowHeightClass::from_height_dp(480.0),
2621 WindowHeightClass::Medium
2622 );
2623 assert_eq!(
2624 WindowHeightClass::from_height_dp(899.9),
2625 WindowHeightClass::Medium
2626 );
2627 assert_eq!(
2628 WindowHeightClass::from_height_dp(900.0),
2629 WindowHeightClass::Expanded
2630 );
2631 }
2632
2633 fn post_notification() -> Mutation<'static> {
2634 Mutation::PostNotification {
2635 key: "session/7",
2636 title: "세션이 끝났습니다",
2637 body: "refactor-parser: 테스트 214개 통과",
2638 channel: "세션",
2639 action_1: "열기",
2640 action_2: "",
2641 importance: NotificationImportance::Urgent,
2642 presentation: NotificationPresentation::WhenInactive,
2643 }
2644 }
2645
2646 #[test]
2649 fn fr36_notification_commands_round_trip_at_their_fixed_lengths() {
2650 let mutations = [
2651 post_notification(),
2652 Mutation::WithdrawNotification { key: "session/7" },
2653 Mutation::RequestNotificationPermission,
2654 ];
2655 let mut lengths = Vec::new();
2656 let mut encoder = BatchEncoder::default();
2657 for mutation in &mutations {
2658 encoder.encode(mutation).unwrap();
2659 }
2660 let bytes = encoder.finish().unwrap();
2661 let mut position = ENVELOPE_LEN;
2662 for _ in &mutations {
2663 let length = read_u16(bytes, position + 2).unwrap();
2664 lengths.push((read_u16(bytes, position).unwrap(), length));
2665 position += usize::from(length);
2666 }
2667 assert_eq!(
2668 lengths,
2669 [
2670 (TAG_POST_NOTIFICATION, 56),
2671 (TAG_WITHDRAW_NOTIFICATION, 12),
2672 (TAG_REQUEST_NOTIFICATION_PERMISSION, 4),
2673 ]
2674 );
2675 assert_eq!(decode_batch(bytes).unwrap(), mutations);
2676 }
2677
2678 #[test]
2681 fn fr36_unknown_importance_and_presentation_are_protocol_errors() {
2682 for (offset, expected) in [(52, 9_u16), (54, 7_u16)] {
2683 let mut encoder = BatchEncoder::default();
2684 encoder.encode(&post_notification()).unwrap();
2685 let mut bytes = encoder.finish().unwrap().to_vec();
2686 bytes[ENVELOPE_LEN + offset..ENVELOPE_LEN + offset + 2]
2687 .copy_from_slice(&expected.to_le_bytes());
2688 assert_eq!(
2689 decode_batch(&bytes),
2690 Err(ProtocolError::InvalidNotification(expected))
2691 );
2692 }
2693 }
2694
2695 #[test]
2698 fn fr36_a_notification_record_of_the_wrong_length_is_rejected() {
2699 let mut bytes = vec![0, 0, 12, 0, 20, 0, 0, 0, 1, 0, 0, 0];
2700 bytes.extend_from_slice(&TAG_REQUEST_NOTIFICATION_PERMISSION.to_le_bytes());
2701 bytes.extend_from_slice(&8_u16.to_le_bytes());
2702 bytes.extend_from_slice(&0_u32.to_le_bytes());
2703 assert_eq!(
2704 decode_batch(&bytes),
2705 Err(ProtocolError::InvalidRecordLength)
2706 );
2707 }
2708
2709 #[test]
2712 fn fr36_notification_events_round_trip_fixed_layout() {
2713 let activated = HostEvent {
2714 node_id: 0,
2715 handler_id: 0,
2716 payload: crate::EventPayload::NotificationActivated {
2717 action: 2,
2718 key: "session/7",
2719 },
2720 };
2721 let mut bytes = Vec::new();
2722 encode_event(&activated, &mut bytes).unwrap();
2723 assert_eq!(read_u16(&bytes, 0).unwrap(), EVENT_NOTIFICATION_ACTIVATED);
2724 assert_eq!(read_u16(&bytes, 2).unwrap(), 28);
2725 assert_eq!(bytes.len(), 28 + "session/7".len());
2726 assert_eq!(decode_event(&bytes).unwrap(), activated);
2727
2728 for state in [
2729 NotificationPermission::NotDetermined,
2730 NotificationPermission::Granted,
2731 NotificationPermission::Denied,
2732 NotificationPermission::Unsupported,
2733 ] {
2734 let changed = HostEvent {
2735 node_id: 0,
2736 handler_id: 0,
2737 payload: crate::EventPayload::NotificationPermissionChanged(state),
2738 };
2739 encode_event(&changed, &mut bytes).unwrap();
2740 assert_eq!(bytes.len(), 20);
2741 assert_eq!(decode_event(&bytes).unwrap(), changed);
2742 }
2743 }
2744
2745 #[test]
2747 fn fr36_unknown_permission_state_is_a_protocol_error() {
2748 let mut bytes = Vec::new();
2749 encode_event(
2750 &HostEvent {
2751 node_id: 0,
2752 handler_id: 0,
2753 payload: crate::EventPayload::NotificationPermissionChanged(
2754 NotificationPermission::Granted,
2755 ),
2756 },
2757 &mut bytes,
2758 )
2759 .unwrap();
2760 bytes[16..20].copy_from_slice(&5_u32.to_le_bytes());
2761 assert_eq!(
2762 decode_event(&bytes),
2763 Err(ProtocolError::InvalidValueKind(5))
2764 );
2765 }
2766
2767 #[test]
2770 fn fr36_an_activation_naming_a_third_button_is_a_protocol_error() {
2771 let mut bytes = Vec::new();
2772 encode_event(
2773 &HostEvent {
2774 node_id: 0,
2775 handler_id: 0,
2776 payload: crate::EventPayload::NotificationActivated {
2777 action: 1,
2778 key: "k",
2779 },
2780 },
2781 &mut bytes,
2782 )
2783 .unwrap();
2784 bytes[16..20].copy_from_slice(&3_u32.to_le_bytes());
2785 assert_eq!(
2786 decode_event(&bytes),
2787 Err(ProtocolError::InvalidValueKind(3))
2788 );
2789 }
2790
2791 #[test]
2793 fn fr43_zoom_changed_round_trips_fixed_layout() {
2794 let event = HostEvent {
2795 node_id: 0,
2796 handler_id: 0,
2797 payload: crate::EventPayload::ZoomChanged {
2798 k: 1.25 * 1.44,
2799 os: 1.25,
2800 level: 2,
2801 },
2802 };
2803 let mut bytes = Vec::new();
2804 encode_event(&event, &mut bytes).unwrap();
2805 assert_eq!(read_u16(&bytes, 0).unwrap(), 33);
2806 assert_eq!(read_u16(&bytes, 2).unwrap(), 28);
2807 assert_eq!(bytes.len(), 28);
2808 assert_eq!(decode_event(&bytes).unwrap(), event);
2809
2810 let smaller = HostEvent {
2811 node_id: 0,
2812 handler_id: 0,
2813 payload: crate::EventPayload::ZoomChanged {
2814 k: 0.5,
2815 os: 1.0,
2816 level: -4,
2817 },
2818 };
2819 encode_event(&smaller, &mut bytes).unwrap();
2820 assert_eq!(decode_event(&bytes).unwrap(), smaller);
2821 }
2822
2823 #[test]
2825 fn fr43_a_zoom_level_out_of_range_is_rejected() {
2826 let event = HostEvent {
2827 node_id: 0,
2828 handler_id: 0,
2829 payload: crate::EventPayload::ZoomChanged {
2830 k: 1.0,
2831 os: 1.0,
2832 level: 9,
2833 },
2834 };
2835 let mut bytes = Vec::new();
2836 encode_event(&event, &mut bytes).unwrap();
2837 assert_eq!(
2838 decode_event(&bytes),
2839 Err(ProtocolError::InvalidValueKind(9))
2840 );
2841 bytes[2..4].copy_from_slice(&24_u16.to_le_bytes());
2842 assert_eq!(
2843 decode_event(&bytes),
2844 Err(ProtocolError::InvalidRecordLength)
2845 );
2846 }
2847
2848 #[test]
2851 fn fr43_the_window_record_carries_a_zoom_level() {
2852 for level in [None, Some(-8), Some(0), Some(2), Some(8)] {
2853 let mut window = crate::schema::Window::new().with_title("zoom");
2854 window.zoom_level = level;
2855 let mut encoder = BatchEncoder::default();
2856 encoder.encode(&Mutation::SetWindow(window)).unwrap();
2857 let bytes = encoder.finish().unwrap().to_vec();
2858 if level.is_none() {
2859 assert_eq!(read_u16(&bytes, ENVELOPE_LEN + 18).unwrap(), 0);
2860 }
2861 let decoded = decode_batch(&bytes).unwrap();
2862 assert_eq!(decoded, vec![Mutation::SetWindow(window)]);
2863 }
2864 assert_eq!(
2865 crate::schema::Window::new().with_zoom_level(20).zoom_level,
2866 Some(8)
2867 );
2868 }
2869}