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