1use {
4 super::{DriverError, physical_device::PhysicalDevice},
5 ash::{
6 ext, khr,
7 vk::{self, Handle},
8 },
9 derive_builder::Builder,
10 log::{debug, error, trace, warn},
11 raw_window_handle::{HasDisplayHandle, RawDisplayHandle},
12 std::{
13 collections::HashSet,
14 error::Error,
15 ffi::CStr,
16 fmt::{Debug, Display, Formatter},
17 ops::Deref,
18 sync::Arc,
19 thread::panicking,
20 },
21};
22
23#[cfg(any(not(target_os = "macos"), feature = "loaded"))]
24use {
25 log::{Level, Metadata, info, logger},
26 std::{
27 env::var,
28 ffi::c_void,
29 io::{IsTerminal, stderr},
30 process::{abort, id},
31 thread::{current, park},
32 },
33};
34
35#[cfg(any(not(target_os = "macos"), feature = "loaded"))]
36const SKIP_VALIDATION_PARK_ENV: &str = "VK_GRAPH_SKIP_VALIDATION_PARK";
37
38#[cfg(target_os = "macos")]
39use std::env::set_var;
40
41#[cfg(any(not(target_os = "macos"), feature = "loaded"))]
42unsafe extern "system" fn debug_callback(
43 message_severity: vk::DebugUtilsMessageSeverityFlagsEXT,
44 _message_types: vk::DebugUtilsMessageTypeFlagsEXT,
45 callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT<'_>,
46 _user_data: *mut c_void,
47) -> vk::Bool32 {
48 if panicking() {
49 return vk::FALSE;
50 }
51
52 assert!(!callback_data.is_null());
53
54 let callback_data = unsafe { &*callback_data };
55 let message = if callback_data.p_message.is_null() {
56 "<missing Vulkan validation message>"
57 } else {
58 unsafe { CStr::from_ptr(callback_data.p_message) }
59 .to_str()
60 .unwrap_or("<invalid Vulkan validation message>")
61 };
62
63 if !callback_data.p_message_id_name.is_null() {
64 let vuid = unsafe { CStr::from_ptr(callback_data.p_message_id_name) }
65 .to_str()
66 .unwrap_or("<invalid Vulkan validation message ID name>");
67 if vuid != "Loader Message" {
68 debug!("{vuid}");
69 }
70 };
71
72 let is_error = message_severity.contains(vk::DebugUtilsMessageSeverityFlagsEXT::ERROR);
73
74 if is_error {
75 error!("{message}");
76 } else if message_severity.contains(vk::DebugUtilsMessageSeverityFlagsEXT::WARNING)
77 && !var("VK_GRAPH_DEBUG_IGNORE_WARNING")
78 .map(var_value_is_set)
79 .unwrap_or_default()
80 {
81 warn!("{message}");
82 } else if message_severity.contains(vk::DebugUtilsMessageSeverityFlagsEXT::INFO)
83 && !var("VK_GRAPH_DEBUG_IGNORE_INFO")
84 .map(var_value_is_set)
85 .unwrap_or_default()
86 {
87 info!("{message}");
88 } else if message_severity.contains(vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE)
89 && !var("VK_GRAPH_DEBUG_IGNORE_VERBOSE")
90 .map(var_value_is_set)
91 .unwrap_or_default()
92 {
93 debug!("{message}");
94 }
95
96 if !is_error {
97 return vk::FALSE;
98 }
99
100 if !logger().enabled(&Metadata::builder().level(Level::Debug).build())
101 || var("RUST_LOG")
102 .map(|rust_log| rust_log.is_empty())
103 .unwrap_or(true)
104 {
105 eprintln!(
106 "note: run with `RUST_LOG=trace` environment variable to display more information"
107 );
108 eprintln!("note: see https://github.com/rust-lang/log#in-executables");
109 abort()
110 }
111
112 if current().name() != Some("main") {
113 warn!("invalid validation callback thread: child thread")
114 }
115
116 if var(SKIP_VALIDATION_PARK_ENV)
117 .map(var_value_is_set)
118 .unwrap_or_default()
119 {
120 warn!("validation callback park skipped; execution will continue");
121
122 return vk::FALSE;
123 }
124
125 if !stderr().is_terminal() {
126 warn!("validation callback park skipped; stderr is not an interactive terminal");
127
128 return vk::FALSE;
129 }
130
131 debug!(
132 "parking validation callback thread `{}` for debugger attach to pid {}",
133 current().name().unwrap_or_default(),
134 id()
135 );
136
137 logger().flush();
138 park();
139
140 vk::FALSE
141}
142
143fn debug_extension_names() -> &'static [&'static CStr] {
144 #[cfg(any(not(target_os = "macos"), feature = "loaded"))]
145 return &[ext::debug_utils::NAME];
146
147 #[cfg(all(target_os = "macos", not(feature = "loaded")))]
148 return &[];
149}
150
151fn debug_layer_names() -> &'static [&'static CStr] {
152 #[cfg(any(not(target_os = "macos"), feature = "loaded"))]
153 return &[c"VK_LAYER_KHRONOS_validation"];
154
155 #[cfg(all(target_os = "macos", not(feature = "loaded")))]
156 return &[];
157}
158
159fn display_extension_names(
161 display_handle: RawDisplayHandle,
162) -> Result<&'static [&'static CStr], DriverError> {
163 let extensions = match display_handle {
164 RawDisplayHandle::Windows(_) => &[khr::surface::NAME, khr::win32_surface::NAME],
165 RawDisplayHandle::Wayland(_) => &[khr::surface::NAME, khr::wayland_surface::NAME],
166 RawDisplayHandle::Xlib(_) => &[khr::surface::NAME, khr::xlib_surface::NAME],
167 RawDisplayHandle::Xcb(_) => &[khr::surface::NAME, khr::xcb_surface::NAME],
168 RawDisplayHandle::Android(_) => &[khr::surface::NAME, khr::android_surface::NAME],
169 RawDisplayHandle::AppKit(_) | RawDisplayHandle::UiKit(_) => {
170 &[khr::surface::NAME, ext::metal_surface::NAME]
171 }
172 _ => {
173 warn!("unsupported display handle type: {display_handle:?}");
174
175 return Err(DriverError::Unsupported);
176 }
177 };
178
179 Ok(extensions)
180}
181
182fn has_vk_khr_surface(entry: &ash::Entry, instance: vk::Instance) -> bool {
189 [
190 c"vkGetPhysicalDeviceSurfaceCapabilitiesKHR",
191 c"vkGetPhysicalDeviceSurfaceFormatsKHR",
192 c"vkGetPhysicalDeviceSurfacePresentModesKHR",
193 c"vkGetPhysicalDeviceSurfaceSupportKHR",
194 c"vkDestroySurfaceKHR",
195 ]
196 .into_iter()
197 .all(|name| unsafe {
198 entry
199 .get_instance_proc_addr(instance, name.as_ptr())
200 .is_some()
201 })
202}
203
204fn var_value_is_set(val: String) -> bool {
205 !matches!(val.as_str(), "" | "0" | "false" | "False" | "FALSE")
206}
207
208#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, PartialOrd, Ord)]
212pub enum ApiVersion {
213 Vulkan12,
215
216 #[default]
220 Vulkan13,
221}
222
223impl ApiVersion {
224 pub fn try_parse_vk_api_version(version: u32) -> Result<Self, ParseApiVersionError> {
226 Self::try_from(version)
227 }
228
229 pub fn major(self) -> u32 {
233 1
234 }
235
236 pub fn minor(self) -> u32 {
238 match self {
239 Self::Vulkan12 => 2,
240 Self::Vulkan13 => 3,
241 }
242 }
243
244 pub fn patch(self) -> u32 {
248 0
249 }
250
251 pub fn to_vk_api_version(self) -> u32 {
253 self.into()
254 }
255
256 pub fn variant(self) -> u32 {
260 0
261 }
262}
263
264impl From<ApiVersion> for u32 {
265 fn from(val: ApiVersion) -> Self {
266 vk::make_api_version(val.variant(), val.major(), val.minor(), val.patch())
267 }
268}
269
270impl TryFrom<u32> for ApiVersion {
271 type Error = ParseApiVersionError;
272
273 fn try_from(val: u32) -> Result<Self, Self::Error> {
274 let major = vk::api_version_major(val);
275 let minor = vk::api_version_minor(val);
276 let patch = vk::api_version_patch(val);
277 let variant = vk::api_version_variant(val);
278
279 if variant != 0 || major != 1 || minor < 2 {
280 return Err(ParseApiVersionError {
281 major,
282 minor,
283 patch,
284 variant,
285 });
286 }
287
288 Ok(match minor {
289 2 => ApiVersion::Vulkan12,
290 _ => ApiVersion::Vulkan13,
291 })
292 }
293}
294
295#[read_only::embed]
303#[allow(private_interfaces)]
304pub struct Instance {
305 #[readonly]
309 pub info: InstanceInfo,
310
311 #[readonly]
312 pub(self) inner: Arc<InstanceInner>,
313
314 #[readonly]
318 pub khr_surface: bool,
319}
320
321impl Clone for Instance {
322 fn clone(&self) -> Self {
323 Self {
324 read_only: ReadOnlyInstance {
325 info: self.info,
326 inner: self.inner.clone(),
327 khr_surface: self.khr_surface,
328 },
329 }
330 }
331}
332
333impl Instance {
334 pub const DEFAULT_API_VERSION: ApiVersion = ApiVersion::Vulkan13;
336
337 #[profiling::function]
345 pub fn create(info: impl Into<InstanceInfo>) -> Result<Self, DriverError> {
346 Self::create_with_extension_names(info.into(), &[])
347 }
348
349 fn create_with_extension_names(
350 info: InstanceInfo,
351 extra_extension_names: &[&CStr],
352 ) -> Result<Self, DriverError> {
353 if info.debug && debug_extension_names().is_empty() {
354 error!("debug mode requires VK_EXT_debug_utils support");
355
356 return Err(DriverError::Unsupported);
357 }
358
359 #[cfg(target_os = "macos")]
361 unsafe {
362 set_var("MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS", "1");
363 }
364
365 #[cfg(feature = "loaded")]
367 let entry = unsafe {
368 ash::Entry::load().map_err(|err| {
369 error!("unable to load Vulkan driver: {err}");
370
371 DriverError::Unsupported
372 })?
373 };
374
375 #[cfg(not(feature = "loaded"))]
377 let entry = {
378 #[cfg(not(target_os = "macos"))]
379 let entry = ash::Entry::linked();
380
381 #[cfg(target_os = "macos")]
383 let entry = ash_molten::load();
384 };
385
386 let mut extension_names = info
387 .extension_names
388 .iter()
389 .chain(extra_extension_names)
390 .copied()
391 .collect::<HashSet<_>>();
392
393 if info.debug {
394 extension_names.extend(debug_extension_names());
395 }
396
397 #[cfg(all(target_os = "macos", feature = "loaded"))]
403 {
404 extension_names.extend(&[
405 ash::khr::get_physical_device_properties2::NAME,
406 ash::khr::portability_enumeration::NAME,
407 ]);
408 }
409
410 let khr_surface = extension_names.contains(&khr::surface::NAME);
411
412 let extension_name_ptrs = extension_names
413 .iter()
414 .copied()
415 .map(CStr::as_ptr)
416 .collect::<Box<_>>();
417
418 let mut layer_names = Vec::with_capacity(info.debug as _);
419
420 if info.debug {
421 layer_names.extend(debug_layer_names());
422 }
423
424 let layer_name_ptrs = layer_names
425 .iter()
426 .copied()
427 .map(CStr::as_ptr)
428 .collect::<Box<_>>();
429
430 let app_desc =
431 vk::ApplicationInfo::default().api_version(info.api_version.to_vk_api_version());
432 let instance_desc = vk::InstanceCreateInfo::default()
433 .application_info(&app_desc)
434 .enabled_layer_names(&layer_name_ptrs)
435 .enabled_extension_names(&extension_name_ptrs);
436
437 #[cfg(all(target_os = "macos", feature = "loaded"))]
442 let instance_desc = instance_desc.flags(vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR);
443
444 #[cfg(any(not(target_os = "macos"), feature = "loaded"))]
445 let mut debug_create_info = vk::DebugUtilsMessengerCreateInfoEXT::default()
446 .message_severity(
447 vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE
448 | vk::DebugUtilsMessageSeverityFlagsEXT::INFO
449 | vk::DebugUtilsMessageSeverityFlagsEXT::WARNING
450 | vk::DebugUtilsMessageSeverityFlagsEXT::ERROR,
451 )
452 .message_type(
453 vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
454 | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION
455 | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE,
456 )
457 .pfn_user_callback(Some(debug_callback));
458
459 #[cfg(any(not(target_os = "macos"), feature = "loaded"))]
460 let instance_desc = if info.debug {
461 instance_desc.push_next(&mut debug_create_info)
462 } else {
463 instance_desc
464 };
465
466 let instance = unsafe {
467 entry.create_instance(&instance_desc, None).map_err(|_| {
468 if info.debug {
469 warn!("debug may only be enabled with a valid Vulkan SDK installation");
470 }
471
472 error!(
473 "Vulkan driver does not support API v{}",
474 match info.api_version {
475 ApiVersion::Vulkan12 => "1.2",
476 ApiVersion::Vulkan13 => "1.3",
477 }
478 );
479
480 for layer_name in &layer_names {
481 debug!("Layer: {:?}", layer_name);
482 }
483
484 for extension_name in extension_names {
485 debug!("Extension: {:?}", extension_name);
486 }
487
488 DriverError::Unsupported
489 })?
490 };
491
492 trace!("created a Vulkan instance");
493
494 #[cfg(all(target_os = "macos", not(feature = "loaded")))]
495 let debug_utils = None;
496
497 #[cfg(any(not(target_os = "macos"), feature = "loaded"))]
498 let debug_utils = if info.debug {
499 let debug_utils = ext::debug_utils::Instance::new(&entry, &instance);
500 let debug_messenger =
501 unsafe { debug_utils.create_debug_utils_messenger(&debug_create_info, None) }
502 .map_err(|err| {
503 unsafe {
504 instance.destroy_instance(None);
505 }
506
507 error!("unable to create debug utils messenger: {err}");
508
509 DriverError::Unsupported
510 })?;
511
512 Some((debug_utils, debug_messenger))
513 } else {
514 None
515 };
516
517 Ok(Self {
518 read_only: ReadOnlyInstance {
519 info,
520 inner: Arc::new(InstanceInner {
521 debug_utils,
522 entry,
523 instance,
524 instance_created: true,
525 }),
526 khr_surface,
527 },
528 })
529 }
530
531 pub fn entry(this: &Self) -> &ash::Entry {
533 &this.inner.entry
534 }
535
536 pub(crate) fn supports_debug_utils(this: &Self) -> bool {
537 this.inner.debug_utils.is_some()
538 }
539
540 #[profiling::function]
544 pub fn physical_devices(
545 this: &Self,
546 ) -> Result<impl IntoIterator<Item = PhysicalDevice>, DriverError> {
547 let physical_devices = unsafe { this.enumerate_physical_devices() }.map_err(|err| {
548 error!("unable to enumerate physical devices: {err}");
549
550 match err {
551 vk::Result::ERROR_INITIALIZATION_FAILED => DriverError::Unsupported,
552 vk::Result::ERROR_OUT_OF_DEVICE_MEMORY | vk::Result::ERROR_OUT_OF_HOST_MEMORY => {
553 DriverError::OutOfMemory
554 }
555 vk::Result::ERROR_VALIDATION_FAILED_EXT => DriverError::InvalidData,
556 _ => {
557 warn!("unexpected enumerate_physical_devices error: {err}");
558
559 DriverError::Unsupported
560 }
561 }
562 })?;
563
564 Ok(physical_devices
565 .into_iter()
566 .enumerate()
567 .filter_map(|(idx, physical_device)| {
568 let physical_device = unsafe {
569 PhysicalDevice::try_from_ash(this, physical_device)
570 .inspect_err(|err| warn!("unsupported physical device #{idx}: {err}"))
571 .ok()?
572 };
573
574 let api_version = ApiVersion::try_parse_vk_api_version(
575 physical_device.properties_v1_0.api_version,
576 )
577 .inspect_err(|err| {
578 warn!(
579 "unsupported physical device #{idx} {}: {err}",
580 physical_device.properties_v1_0.device_name
581 );
582 })
583 .ok()?;
584
585 if api_version < this.info.api_version {
586 return None;
587 }
588
589 if this.info.debug && !physical_device.vk_ext_private_data {
590 warn!(
591 "unsupported physical device #{idx} {}: missing VK_EXT_private_data",
592 physical_device.properties_v1_0.device_name
593 );
594
595 return None;
596 }
597
598 Some(physical_device)
599 }))
600 }
601
602 #[profiling::function]
607 pub fn try_from_display(
608 display: impl HasDisplayHandle,
609 info: impl Into<InstanceInfo>,
610 ) -> Result<Self, DriverError> {
611 let display_handle = display.display_handle().map_err(|err| {
612 warn!("unable to get display handle: {err}");
613
614 DriverError::Unsupported
615 })?;
616 let display_extension_names =
617 display_extension_names(display_handle.as_raw()).map_err(|err| {
618 warn!("unable to enumerate display extensions: {err}");
619
620 DriverError::Unsupported
621 })?;
622
623 Self::create_with_extension_names(info.into(), display_extension_names)
624 }
625
626 #[profiling::function]
633 pub fn try_from_entry(entry: ash::Entry, instance: vk::Instance) -> Result<Self, DriverError> {
634 if instance == vk::Instance::null() {
635 warn!("invalid VkInstance handle: null");
636
637 return Err(DriverError::InvalidData);
638 }
639
640 let api_version = unsafe { entry.try_enumerate_instance_version() }
641 .map_err(|err| match err {
642 vk::Result::ERROR_OUT_OF_HOST_MEMORY => DriverError::OutOfMemory,
643 vk::Result::ERROR_VALIDATION_FAILED_EXT => DriverError::InvalidData,
644 err => {
645 error!("unable to enumerate instance version: {err}");
646
647 DriverError::Unsupported
648 }
649 })?
650 .unwrap_or_else(|| {
651 Self::DEFAULT_API_VERSION.to_vk_api_version()
655 })
656 .try_into()
657 .map_err(|err| {
658 warn!("unsupported instance: {err}");
659
660 DriverError::Unsupported
661 })?;
662 let khr_surface = has_vk_khr_surface(&entry, instance);
663
664 let instance = unsafe { ash::Instance::load(entry.static_fn(), instance) };
665
666 Ok(Self {
667 read_only: ReadOnlyInstance {
668 info: InstanceInfo {
669 api_version,
670 ..Default::default()
671 },
672 inner: Arc::new(InstanceInner {
673 debug_utils: None,
674 entry,
675 instance,
676 instance_created: false,
677 }),
678 khr_surface,
679 },
680 })
681 }
682}
683
684impl Debug for Instance {
685 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
686 f.debug_struct(stringify!(Instance))
687 .field("handle", &self.inner.instance.handle())
688 .field("info", &self.info)
689 .field("khr_surface", &self.khr_surface)
690 .field("debug_utils", &self.inner.debug_utils.is_some())
691 .finish_non_exhaustive()
692 }
693}
694
695#[derive(Builder, Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
697#[builder(
698 build_fn(private, name = "fallible_build"),
699 derive(Clone, Copy, Debug),
700 pattern = "owned"
701)]
702pub struct InstanceInfo {
703 #[builder(default = "Instance::DEFAULT_API_VERSION")]
707 pub api_version: ApiVersion,
708
709 #[builder(default)]
729 pub debug: bool,
730
731 #[builder(default)]
733 pub extension_names: &'static [&'static CStr],
734}
735
736impl InstanceInfo {
737 pub fn builder() -> InstanceInfoBuilder {
739 Default::default()
740 }
741
742 pub fn into_builder(self) -> InstanceInfoBuilder {
744 InstanceInfoBuilder {
745 api_version: Some(self.api_version),
746 debug: Some(self.debug),
747 extension_names: Some(self.extension_names),
748 }
749 }
750}
751
752impl InstanceInfoBuilder {
753 #[inline(always)]
755 pub fn build(self) -> InstanceInfo {
756 self.fallible_build().expect("invalid instance info")
757 }
758}
759
760impl From<InstanceInfoBuilder> for InstanceInfo {
761 fn from(info: InstanceInfoBuilder) -> Self {
762 info.build()
763 }
764}
765
766struct InstanceInner {
767 debug_utils: Option<(ext::debug_utils::Instance, vk::DebugUtilsMessengerEXT)>,
768 entry: ash::Entry,
769 instance: ash::Instance,
770 instance_created: bool,
771}
772
773impl Drop for InstanceInner {
774 #[profiling::function]
775 fn drop(&mut self) {
776 if panicking() {
777 return;
778 }
779
780 unsafe {
781 if let Some((debug_utils, debug_messenger)) = self.debug_utils.take() {
782 trace!("destroy debug_utils_messenger {}", debug_messenger.as_raw());
783 debug_utils.destroy_debug_utils_messenger(debug_messenger, None);
784 trace!(
785 "destroy debug_utils_messenger {} DONE",
786 debug_messenger.as_raw()
787 );
788 }
789
790 if self.instance_created {
791 trace!("destroy instance {}", self.instance.handle().as_raw());
792 self.instance.destroy_instance(None);
793 self.instance_created = false;
794 }
795 }
796 }
797}
798
799#[derive(Clone, Copy, Debug)]
801pub struct ParseApiVersionError {
802 pub major: u32,
805
806 pub minor: u32,
809
810 pub patch: u32,
813
814 pub variant: u32,
817}
818
819impl Display for ParseApiVersionError {
820 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
821 f.write_fmt(format_args!(
822 "v{}.{}.{}-{}",
823 self.major, self.minor, self.patch, self.variant
824 ))
825 }
826}
827
828impl Error for ParseApiVersionError {}
829
830#[doc(hidden)]
831impl Deref for ReadOnlyInstance {
832 type Target = ash::Instance;
833
834 fn deref(&self) -> &Self::Target {
835 &self.inner.instance
836 }
837}
838
839#[cfg(test)]
840mod test {
841 use super::*;
842
843 #[test]
844 pub fn api_versions_match() {
845 assert_eq!(
846 ApiVersion::Vulkan12.to_vk_api_version(),
847 vk::API_VERSION_1_2
848 );
849 assert_eq!(
850 ApiVersion::Vulkan13.to_vk_api_version(),
851 vk::API_VERSION_1_3
852 );
853 }
854
855 #[test]
856 pub fn api_versions_from() {
857 assert_eq!(
858 ApiVersion::try_parse_vk_api_version(vk::API_VERSION_1_2).unwrap(),
859 ApiVersion::Vulkan12
860 );
861 assert_eq!(
862 ApiVersion::try_parse_vk_api_version(vk::API_VERSION_1_3).unwrap(),
863 ApiVersion::Vulkan13
864 );
865 }
866
867 #[test]
868 pub fn default_api_version_matches_instance_info_default() {
869 assert_eq!(
870 Instance::DEFAULT_API_VERSION,
871 InstanceInfo::default().api_version
872 );
873 }
874
875 #[test]
876 pub fn default_api_version_matches_api_version_default() {
877 assert_eq!(Instance::DEFAULT_API_VERSION, ApiVersion::default());
878 }
879
880 #[test]
881 pub fn invalid_api_versions_are_rejected() {
882 assert!(ApiVersion::try_parse_vk_api_version(vk::API_VERSION_1_1).is_err());
883 assert!(ApiVersion::try_parse_vk_api_version(vk::make_api_version(0, 2, 0, 0)).is_err());
884 assert!(ApiVersion::try_parse_vk_api_version(vk::make_api_version(1, 1, 9, 0)).is_err());
885 assert!(ApiVersion::try_parse_vk_api_version(vk::make_api_version(1, 4, 2, 0)).is_err());
886 assert!(ApiVersion::try_parse_vk_api_version(vk::make_api_version(1, 2, 0, 1)).is_err());
887 assert!(ApiVersion::try_parse_vk_api_version(vk::make_api_version(1, 3, 0, 1)).is_err());
888 }
889}