zng_view_api/lib.rs
1#![doc(html_favicon_url = "https://zng-ui.github.io/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://zng-ui.github.io/res/zng-logo.png")]
3//!
4//! The View Process API.
5//!
6//! Zng isolates all render and windowing related code to a different process (the view-process), this crate
7//! provides the API that must be implemented to create a view-process backend, plus the [`Controller`] that
8//! can be used from an app-process to spawn and communicate with a view-process.
9//!
10//! # VERSION
11//!
12//! The [`VERSION`] of this crate must match exactly in both *App-Process* and *View-Process*, otherwise a runtime
13//! panic error is generated.
14//!
15//! # Same Process Patch
16//!
17//! Dynamically loaded same process implementers must propagate a [`StaticPatch`], otherwise the view will not connect.
18//!
19//! # Crate
20//!
21#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
22#![warn(missing_docs)]
23#![warn(unused_extern_crates)]
24
25use drag_drop::{DragDropData, DragDropEffect, DragDropError};
26#[cfg(ipc)]
27use serde::{Deserialize, Serialize};
28
29/// The *App Process* and *View Process* must be build using the same exact version and this is
30/// validated during run-time, causing a panic if the versions don't match.
31pub const VERSION: &str = env!("CARGO_PKG_VERSION");
32
33pub mod access;
34pub mod api_extension;
35pub mod audio;
36pub mod clipboard;
37pub mod config;
38pub mod dialog;
39pub mod display_list;
40pub mod drag_drop;
41pub mod font;
42pub mod image;
43pub mod ipc;
44pub mod keyboard;
45pub mod mouse;
46pub mod raw_input;
47pub mod touch;
48pub mod window;
49
50mod types;
51pub use types::*;
52
53mod app_process;
54pub use app_process::*;
55
56mod view_process;
57pub use view_process::*;
58use zng_txt::Txt;
59
60use std::fmt;
61
62use api_extension::{ApiExtensionId, ApiExtensionPayload};
63use clipboard::{ClipboardData, ClipboardError};
64use dialog::DialogId;
65use font::{FontFaceId, FontId, FontOptions, FontVariationName};
66use image::{ImageId, ImageMaskMode, ImageRequest, ImageTextureId};
67use ipc::{IpcBytes, IpcBytesReceiver};
68use window::WindowId;
69use zng_unit::{DipPoint, DipRect, DipSize, Factor, Px, PxRect};
70
71/// Packaged API request.
72#[derive(Debug)]
73#[cfg_attr(ipc, derive(Serialize, Deserialize))]
74pub struct Request(RequestData);
75impl Request {
76 /// Returns `true` if the request can only be made after the *init* event.
77 pub fn must_be_connected(&self) -> bool {
78 !matches!(&self.0, RequestData::init { .. })
79 }
80
81 /// Returns `true` if the request represents a new frame or frame update for the window with the same wait ID.
82 pub fn is_frame(&self, window_id: WindowId, wait_id: Option<window::FrameWaitId>) -> bool {
83 match &self.0 {
84 RequestData::render { id, frame } if *id == window_id && frame.wait_id == wait_id => true,
85 RequestData::render_update { id, frame } if *id == window_id && frame.wait_id == wait_id => true,
86 _ => false,
87 }
88 }
89
90 /// Returns `true` if the request affects position or size of the window.
91 pub fn affects_window_rect(&self, window_id: WindowId) -> bool {
92 matches!(
93 &self.0,
94 RequestData::set_state { id, .. }
95 if *id == window_id
96 )
97 }
98
99 /// Returns `true` if this request will receive a response. Only [`Api`] methods
100 /// that have a return value send back a response.
101 pub fn expect_response(&self) -> bool {
102 self.0.expect_response()
103 }
104}
105
106/// Packaged API response.
107#[derive(Debug)]
108#[cfg_attr(ipc, derive(Serialize, Deserialize))]
109pub struct Response(ResponseData);
110impl Response {
111 /// If this response must be send back to the app process. Only [`Api`] methods
112 /// that have a return value send back a response.
113 pub fn must_be_send(&self) -> bool {
114 self.0.must_be_send()
115 }
116}
117
118macro_rules! TypeOrNil {
119 ($T:ty) => {
120 $T
121 };
122 () => {
123 ()
124 };
125}
126
127macro_rules! type_is_some {
128 (if $T:ty { $($t_true:tt)* } else { $($t_false:tt)* }) => {
129 $($t_true)*
130 };
131 (if { $($t_true:tt)* } else { $($t_false:tt)* }) => {
132 $($t_false)*
133 };
134}
135
136/// Declares the internal `Request` and `Response` enums, public methods in `Controller` and the public trait `ViewApp`, in the
137/// controller it packs and sends the request and receives and unpacks the response. In the view it implements
138/// the method.
139macro_rules! declare_api {
140 (
141 $(
142 $(#[$meta:meta])*
143 $vis:vis fn $method:ident(
144 &mut $self:ident
145 $(, $input:ident : $RequestType:ty)* $(,)?
146 ) $(-> $ResponseType:ty)?;
147 )*
148 ) => {
149 #[cfg_attr(ipc, derive(Serialize, Deserialize))]
150 #[allow(non_camel_case_types)]
151 #[allow(clippy::large_enum_variant)]
152 #[repr(u32)]
153 enum RequestData {
154 $(
155 $(#[$meta])*
156 $method { $($input: $RequestType),* },
157 )*
158 }
159 impl RequestData {
160 #[allow(unused_doc_comments)]
161 pub fn expect_response(&self) -> bool {
162 match self {
163 $(
164 $(#[$meta])*
165 Self::$method { .. } => type_is_some! {
166 if $($ResponseType)? {
167 true
168 } else {
169 false
170 }
171 },
172 )*
173 }
174 }
175 }
176 impl fmt::Debug for RequestData {
177 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
178 #[allow(unused_doc_comments)]
179 if f.alternate() {
180 match self {
181 $(
182 $(#[$meta])*
183 RequestData::$method { $($input),* } => write!(f, "{}{:#?}", stringify!($method), ($($input),*)),
184 )+
185 }
186 } else {
187 match self {
188 $(
189 $(#[$meta])*
190 RequestData::$method { .. } => write!(f, "{}(..)", stringify!($method)),
191 )+
192 }
193 }
194 }
195 }
196
197 #[derive(Debug)]
198 #[cfg_attr(ipc, derive(Serialize, Deserialize))]
199 #[allow(non_camel_case_types)]
200 #[repr(u32)]
201 enum ResponseData {
202 $(
203 $(#[$meta])*
204 $method(TypeOrNil![$($ResponseType)?]),
205 )*
206 }
207 impl ResponseData {
208 #[allow(unused_doc_comments)]
209 pub fn must_be_send(&self) -> bool {
210 match self {
211 $(
212 $(#[$meta])*
213 Self::$method(_) => type_is_some! {
214 if $($ResponseType)? {
215 true
216 } else {
217 false
218 }
219 },
220 )*
221 }
222 }
223 }
224
225 #[allow(unused_parens)]
226 impl Controller {
227 $(
228 $(#[$meta])*
229 #[allow(clippy::too_many_arguments)]
230 $vis fn $method(&mut self $(, $input: $RequestType)*) -> VpResult<TypeOrNil![$($ResponseType)?]> {
231 let req = Request(RequestData::$method { $($input),* });
232 type_is_some! {
233 if $($ResponseType)? {
234 match self.talk(req)?.0 {
235 ResponseData::$method(r) => Ok(r),
236 r => panic!("view-process did not respond correctly for `{}`, {r:?}", stringify!($method))
237 }
238 } else {
239 self.command(req)
240 }
241 }
242 }
243 )*
244 }
245
246 /// The view-process API.
247 pub trait Api {
248 /// Already implemented, matches a request, calls the corresponding method and packages the response.
249 fn respond(&mut self, request: Request) -> Response {
250 match request.0 {
251 $(
252 #[allow(unused_doc_comments)]
253 $(#[$meta])* // for the cfg
254 RequestData::$method { $($input),* } => {
255 let r = self.$method($($input),*);
256 Response(ResponseData::$method(r))
257 }
258 )*
259 }
260 }
261
262 $(
263 $(#[$meta])*
264 #[allow(clippy::too_many_arguments)]
265 fn $method(&mut self, $($input: $RequestType),*) $(-> $ResponseType)?;
266 )*
267 }
268 };
269}
270declare_api! {
271 /// Called once on init.
272 ///
273 /// Sends an [`Event::Inited`] once the view is completely connected.
274 /// Other methods may only be called after this event.
275 fn init(&mut self, vp_gen: ViewProcessGen, is_respawn: bool, headless: bool);
276
277 /// Called once after exit, if running in a managed external process it will be killed after this call.
278 fn exit(&mut self);
279
280 /// Enable/disable global device events.
281 ///
282 /// This filter affects device events not targeted at windows, such as mouse move outside windows or
283 /// key presses when the app has no focused window.
284 pub fn set_device_events_filter(&mut self, filter: DeviceEventsFilter);
285
286 /// Open a window.
287 ///
288 /// Sends an [`Event::WindowOpened`] once the window, context and renderer have finished initializing or a
289 /// [`Event::WindowOrHeadlessOpenError`] if it failed.
290 pub fn open_window(&mut self, request: window::WindowRequest);
291
292 /// Open a headless surface.
293 ///
294 /// This is a real renderer but not connected to any window, you can requests pixels to get the
295 /// rendered frames.
296 ///
297 /// Sends an [`Event::HeadlessOpened`] once the context and renderer have finished initializing or a
298 /// [`Event::WindowOrHeadlessOpenError`] if it failed.
299 pub fn open_headless(&mut self, request: window::HeadlessRequest);
300
301 /// Close the window or headless surface.
302 ///
303 /// All documents associated with the window or surface are also closed.
304 pub fn close(&mut self, id: WindowId);
305
306 /// Set window title.
307 pub fn set_title(&mut self, id: WindowId, title: Txt);
308
309 /// Set window visible.
310 pub fn set_visible(&mut self, id: WindowId, visible: bool);
311
312 /// Set if the window is "top-most".
313 pub fn set_always_on_top(&mut self, id: WindowId, always_on_top: bool);
314
315 /// Set if the user can drag-move the window when it is in `Normal` mode.
316 pub fn set_movable(&mut self, id: WindowId, movable: bool);
317
318 /// Set if the user can resize the window when it is in `Normal` mode.
319 pub fn set_resizable(&mut self, id: WindowId, resizable: bool);
320
321 /// Set the window taskbar icon visibility.
322 pub fn set_taskbar_visible(&mut self, id: WindowId, visible: bool);
323
324 /// Bring the window to the Z top, without focusing it.
325 pub fn bring_to_top(&mut self, id: WindowId);
326
327 /// Set the window state, position, size.
328 pub fn set_state(&mut self, id: WindowId, state: window::WindowStateAll);
329
330 /// Set the headless surface or document area size (viewport size).
331 pub fn set_headless_size(&mut self, id: WindowId, size: DipSize, scale_factor: Factor);
332
333 /// Set the window icon, the icon image must be loaded.
334 pub fn set_icon(&mut self, id: WindowId, icon: Option<ImageId>);
335
336 /// Set the window cursor icon and visibility.
337 pub fn set_cursor(&mut self, id: WindowId, cursor: Option<window::CursorIcon>);
338
339 /// Set the window cursor to a custom image.
340 ///
341 /// Falls back to cursor icon if not supported or if set to `None`.
342 pub fn set_cursor_image(&mut self, id: WindowId, cursor: Option<window::CursorImage>);
343
344 /// Sets the user attention request indicator, the indicator is cleared when the window is focused or
345 /// if canceled by setting to `None`.
346 pub fn set_focus_indicator(&mut self, id: WindowId, indicator: Option<window::FocusIndicator>);
347
348 /// Set enabled window chrome buttons.
349 pub fn set_enabled_buttons(&mut self, id: WindowId, buttons: window::WindowButton);
350
351 /// Brings the window to the front and sets input focus.
352 ///
353 /// Sends an [`Event::FocusChanged`] if the window is focused, the request can be ignored by the window manager, or if the
354 /// window is not visible, minimized or already focused.
355 ///
356 /// This request can steal focus from other apps disrupting the user, be careful with it.
357 pub fn focus(&mut self, id: WindowId) -> FocusResult;
358
359 /// Moves the window with the left mouse button until the button is released.
360 ///
361 /// There's no guarantee that this will work unless the left mouse button was pressed immediately before this function is called.
362 pub fn drag_move(&mut self, id: WindowId);
363
364 /// Resizes the window with the left mouse button until the button is released.
365 ///
366 /// There's no guarantee that this will work unless the left mouse button was pressed immediately before this function is called.
367 pub fn drag_resize(&mut self, id: WindowId, direction: window::ResizeDirection);
368
369 /// Open the system title bar context menu.
370 pub fn open_title_bar_context_menu(&mut self, id: WindowId, position: DipPoint);
371
372 /// Cache an image resource.
373 ///
374 /// The image is decoded asynchronously, the events [`Event::ImageMetadataLoaded`], [`Event::ImageLoaded`]
375 /// or [`Event::ImageLoadError`] will be send when the image is ready for use or failed.
376 ///
377 /// The [`ImageRequest::data`] handle must contain the full image data already, it will be dropped after the image finishes decoding.
378 ///
379 /// Images are shared between renderers, to use an image in a window you must first call [`use_image`]
380 /// this will register the image data with the renderer.
381 ///
382 /// [`use_image`]: Api::use_image
383 pub fn add_image(&mut self, request: ImageRequest<IpcBytes>) -> ImageId;
384
385 /// Cache an image from data that has not fully loaded.
386 ///
387 /// If the view-process implementation supports **progressive decoding** it will start decoding the image
388 /// as more data is received, otherwise it will collect all data first and then [`add_image`]. Each
389 /// [`ImageRequest::`data`] package is the continuation of the previous call, send an empty package to indicate finish.
390 ///
391 /// The events [`Event::ImageMetadataLoaded`], [`Event::ImageLoaded`] or [`Event::ImageLoadError`] will
392 /// be send while decoding.
393 ///
394 /// [`add_image`]: Api::add_image
395 pub fn add_image_pro(&mut self, request: ImageRequest<IpcBytesReceiver>) -> ImageId;
396
397 /// Remove an image from cache.
398 ///
399 /// Note that if the image is in use in a renderer it will remain in memory until [`delete_image_use`] is
400 /// called or the renderer is deinited by closing the window.
401 ///
402 /// [`delete_image_use`]: Api::delete_image_use
403 pub fn forget_image(&mut self, id: ImageId);
404
405 /// Add an image resource to the window renderer.
406 ///
407 /// Returns the new image texture ID. If the `image_id` is not loaded returns the [`INVALID`] image ID.
408 ///
409 /// [`INVALID`]: ImageTextureId::INVALID
410 pub fn use_image(&mut self, id: WindowId, image_id: ImageId) -> ImageTextureId;
411
412 /// Replace the image resource in the window renderer.
413 ///
414 /// The [`ImageTextureId`] will be associated with the new [`ImageId`].
415 pub fn update_image_use(&mut self, id: WindowId, texture_id: ImageTextureId, image_id: ImageId);
416
417 /// Delete the image resource in the window renderer.
418 pub fn delete_image_use(&mut self, id: WindowId, texture_id: ImageTextureId);
419
420 /// Returns a list of image decoders supported by this implementation.
421 ///
422 /// Each text is the lower-case file extension, without the dot.
423 pub fn image_decoders(&mut self) -> Vec<Txt>;
424
425 /// Returns a list of image encoders supported by this implementation.
426 ///
427 /// Each text is the lower-case file extension, without the dot.
428 pub fn image_encoders(&mut self) -> Vec<Txt>;
429
430 /// Encode the image into the `format`.
431 ///
432 /// The format must be one of the values returned by [`image_encoders`].
433 ///
434 /// Returns immediately. The encoded data will be send as the event
435 /// [`Event::ImageEncoded`] or [`Event::ImageEncodeError`].
436 ///
437 /// [`image_encoders`]: Api::image_encoders
438 pub fn encode_image(&mut self, id: ImageId, format: Txt);
439
440 /// Cache an audio resource.
441 ///
442 /// # Unimplemented
443 ///
444 /// This method is a stub for a future API, it is not implemented by app-process nor the default view-process.
445 pub fn add_audio(&mut self, request: audio::AudioRequest<IpcBytes>) -> audio::AudioId;
446
447 /// Cache an streaming audio resource.
448 ///
449 /// The audio is decoded as bytes are buffered in.
450 ///
451 /// # Unimplemented
452 ///
453 /// This method is a stub for a future API, it is not implemented by app-process nor the default view-process.
454 pub fn add_audio_pro(&mut self, request: audio::AudioRequest<IpcBytesReceiver>) -> audio::AudioId;
455
456 /// Remove an audio from cache.
457 ///
458 /// Note that if the audio is in use by a playback it will remain in memory until the playback ends.
459 ///
460 /// # Unimplemented
461 ///
462 /// This method is a stub for a future API, it is not implemented by app-process nor the default view-process.
463 pub fn forget_audio(&mut self, id: audio::AudioId);
464
465 /// Returns a list of image decoders supported by this implementation.
466 ///
467 /// Each text is the lower-case file extension, without the dot.
468 ///
469 /// # Unimplemented
470 ///
471 /// This method is a stub for a future API, it is not implemented by app-process nor the default view-process.
472 pub fn audio_decoders(&mut self) -> Vec<Txt>;
473
474 /// Start playing audio.
475 ///
476 /// # Unimplemented
477 ///
478 /// This method is a stub for a future API, it is not implemented by app-process nor the default view-process.
479 pub fn playback(&mut self, request: audio::PlaybackRequest) -> audio::PlaybackId;
480
481 /// Update an existing playback.
482 ///
483 /// # Unimplemented
484 ///
485 /// This method is a stub for a future API, it is not implemented by app-process nor the default view-process.
486 pub fn playback_update(&mut self, id: audio::PlaybackId, request: audio::PlaybackUpdateRequest);
487
488 /// Add a raw font resource to the window renderer.
489 ///
490 /// Returns the new font key.
491 pub fn add_font_face(&mut self, id: WindowId, bytes: font::IpcFontBytes, index: u32) -> FontFaceId;
492
493 /// Delete the font resource in the window renderer.
494 pub fn delete_font_face(&mut self, id: WindowId, font_face_id: FontFaceId);
495
496 /// Add a sized font to the window renderer.
497 ///
498 /// Returns the new fond ID.
499 pub fn add_font(
500 &mut self,
501 id: WindowId,
502 font_face_id: FontFaceId,
503 glyph_size: Px,
504 options: FontOptions,
505 variations: Vec<(FontVariationName, f32)>,
506 ) -> FontId;
507
508 /// Delete a font instance.
509 pub fn delete_font(&mut self, id: WindowId, font_id: FontId);
510
511 /// Sets if the headed window is in *capture-mode*. If `true` the resources used to capture
512 /// a screenshot may be kept in memory to be reused in the next screenshot capture.
513 ///
514 /// Note that capture must still be requested in each frame request.
515 pub fn set_capture_mode(&mut self, id: WindowId, enable: bool);
516
517 /// Create a new image resource from the current rendered frame.
518 ///
519 /// If `mask` is set captures an A8 mask, otherwise captures a full BGRA8 image.
520 ///
521 /// Returns immediately if an [`Event::FrameImageReady`] will be send when the image is ready.
522 /// Returns `0` if the window is not found.
523 pub fn frame_image(&mut self, id: WindowId, mask: Option<ImageMaskMode>) -> ImageId;
524
525 /// Create a new image from a selection of the current rendered frame.
526 ///
527 /// If `mask` is set captures an A8 mask, otherwise captures a full BGRA8 image.
528 ///
529 /// Returns immediately if an [`Event::FrameImageReady`] will be send when the image is ready.
530 /// Returns `0` if the window is not found.
531 pub fn frame_image_rect(&mut self, id: WindowId, rect: PxRect, mask: Option<ImageMaskMode>) -> ImageId;
532
533 /// Set the video mode used when the window is in exclusive fullscreen.
534 pub fn set_video_mode(&mut self, id: WindowId, mode: window::VideoMode);
535
536 /// Render a new frame.
537 pub fn render(&mut self, id: WindowId, frame: window::FrameRequest);
538
539 /// Update the current frame and re-render it.
540 pub fn render_update(&mut self, id: WindowId, frame: window::FrameUpdateRequest);
541
542 /// Update the window's accessibility info tree.
543 pub fn access_update(&mut self, id: WindowId, update: access::AccessTreeUpdate);
544
545 /// Shows a native message dialog for the window.
546 ///
547 /// Returns an ID that identifies the response event.
548 pub fn message_dialog(&mut self, id: WindowId, dialog: dialog::MsgDialog) -> DialogId;
549
550 /// Shows a native file/folder picker for the window.
551 ///
552 /// Returns the ID that identifies the response event.
553 pub fn file_dialog(&mut self, id: WindowId, dialog: dialog::FileDialog) -> DialogId;
554
555 /// Get the clipboard content that matches the `data_type`.
556 pub fn read_clipboard(&mut self, data_type: clipboard::ClipboardType) -> Result<ClipboardData, ClipboardError>;
557
558 /// Set the clipboard content.
559 pub fn write_clipboard(&mut self, data: ClipboardData) -> Result<(), ClipboardError>;
560
561 /// Start a drag and drop operation, if the window is pressed.
562 pub fn start_drag_drop(
563 &mut self,
564 id: WindowId,
565 data: Vec<DragDropData>,
566 allowed_effects: DragDropEffect,
567 ) -> Result<DragDropId, DragDropError>;
568
569 /// Cancel a drag and drop operation.
570 pub fn cancel_drag_drop(&mut self, id: WindowId, drag_id: DragDropId);
571
572 /// Notify the drag source of what effect was applied for a received drag&drop.
573 pub fn drag_dropped(&mut self, id: WindowId, drop_id: DragDropId, applied: DragDropEffect);
574
575 /// Enable or disable IME by setting a cursor area.
576 ///
577 /// In mobile platforms also shows the software keyboard for `Some(_)` and hides it for `None`.
578 pub fn set_ime_area(&mut self, id: WindowId, area: Option<DipRect>);
579
580 /// Attempt to set a system wide shutdown warning associated with the window.
581 ///
582 /// Operating systems that support this show the `reason` in a warning for the user, it must be a short text
583 /// that identifies the critical operation that cannot be cancelled.
584 ///
585 /// Note that there is no guarantee that the view-process or operating system will actually set a block, there
586 /// is no error result because operating systems can silently ignore block requests at any moment, even after
587 /// an initial successful block.
588 ///
589 /// Set to an empty text to remove the warning.
590 pub fn set_system_shutdown_warn(&mut self, id: WindowId, reason: Txt);
591
592 /// Licenses that may be required to be displayed in the app about screen.
593 ///
594 /// This is specially important for prebuilt view users, as the tools that scrap licenses
595 /// may not find the prebuilt dependencies.
596 pub fn third_party_licenses(&mut self) -> Vec<zng_tp_licenses::LicenseUsed>;
597
598 /// Call the API extension.
599 ///
600 /// The `extension_id` is the index of an extension in the extensions list provided by the view-process on init.
601 /// The `extension_request` is any data required by the extension.
602 ///
603 /// Returns the extension response or [`ApiExtensionPayload::unknown_extension`] if the `extension_id` is
604 /// not on the list, or [`ApiExtensionPayload::invalid_request`] if the `extension_request` is not in a
605 /// format expected by the extension.
606 pub fn app_extension(&mut self, extension_id: ApiExtensionId, extension_request: ApiExtensionPayload) -> ApiExtensionPayload;
607
608 /// Call the API extension.
609 ///
610 /// This is similar to [`Api::app_extension`], but is targeting the instance of an extension associated
611 /// with the `id` window or headless surface.
612 pub fn window_extension(
613 &mut self,
614 id: WindowId,
615 extension_id: ApiExtensionId,
616 extension_request: ApiExtensionPayload,
617 ) -> ApiExtensionPayload;
618
619 /// Call the API extension.
620 ///
621 /// This is similar to [`Api::app_extension`], but is targeting the instance of an extension associated
622 /// with the `id` renderer.
623 pub fn render_extension(
624 &mut self,
625 id: WindowId,
626 extension_id: ApiExtensionId,
627 extension_request: ApiExtensionPayload,
628 ) -> ApiExtensionPayload;
629
630 /// Returns the `count` and notifies [`Event::Pong`] after ensuring the view-process is responsive.
631 ///
632 /// The app-process and view-process automatically monitor message frequency to detect when the paired process
633 /// is stuck. View-process implementers must only ensure the response event goes through its *main loop* to get an
634 /// accurate read of it is stuck.
635 pub fn ping(&mut self, count: u16) -> u16;
636}
637
638pub(crate) type AnyResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;