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