1#![deny(missing_docs)]
8
9pub mod admin;
10mod reachability;
11
12pub use reachability::{is_globally_routable, to_reachability_stream, wait_for_reachability};
13
14use anyhow::Context as _;
15use derivative::Derivative;
16use fidl_table_validation::*;
17use futures::{Stream, TryStreamExt as _};
18use std::collections::btree_map::{self, BTreeMap};
19use std::collections::hash_map::{self, HashMap};
20use std::convert::TryFrom as _;
21use std::fmt::Debug;
22use std::marker::PhantomData;
23use std::num::NonZeroU64;
24use thiserror::Error;
25use {
26 fidl_fuchsia_hardware_network as fhardware_network,
27 fidl_fuchsia_net_interfaces as fnet_interfaces,
28};
29
30#[derive(Clone, Copy, Debug, Eq, PartialEq)]
35#[allow(missing_docs)]
36pub enum PortClass {
37 Loopback,
38 Virtual,
39 Ethernet,
40 WlanClient,
41 WlanAp,
42 Ppp,
43 Bridge,
44 Lowpan,
45 Blackhole,
46}
47
48impl PortClass {
49 pub fn is_loopback(&self) -> bool {
51 match self {
52 PortClass::Loopback => true,
53 PortClass::Virtual
54 | PortClass::Blackhole
55 | PortClass::Ethernet
56 | PortClass::WlanClient
57 | PortClass::WlanAp
58 | PortClass::Ppp
59 | PortClass::Bridge
60 | PortClass::Lowpan => false,
61 }
62 }
63}
64
65#[derive(Debug, Error, PartialEq)]
68#[allow(missing_docs)]
69pub enum UnknownPortClassError {
70 #[error(transparent)]
71 NetInterfaces(UnknownNetInterfacesPortClassError),
72 #[error(transparent)]
73 HardwareNetwork(UnknownHardwareNetworkPortClassError),
74}
75
76#[derive(Debug, Error, PartialEq)]
78#[error("unknown fuchsia.net.interfaces/PortClass ordinal: {unknown_ordinal}")]
79pub struct UnknownNetInterfacesPortClassError {
80 unknown_ordinal: u64,
81}
82
83#[derive(Debug, Error, PartialEq)]
85#[error("unknown fuchsia.hardware.network/PortClass ordinal: {unknown_ordinal}")]
86pub struct UnknownHardwareNetworkPortClassError {
87 unknown_ordinal: u16,
88}
89
90impl TryFrom<fnet_interfaces::PortClass> for PortClass {
91 type Error = UnknownPortClassError;
92 fn try_from(port_class: fnet_interfaces::PortClass) -> Result<Self, Self::Error> {
93 match port_class {
94 fnet_interfaces::PortClass::Loopback(fnet_interfaces::Empty) => Ok(PortClass::Loopback),
95 fnet_interfaces::PortClass::Blackhole(fnet_interfaces::Empty) => {
96 Ok(PortClass::Blackhole)
97 }
98 fnet_interfaces::PortClass::Device(port_class) => {
99 PortClass::try_from(port_class).map_err(UnknownPortClassError::HardwareNetwork)
100 }
101 fnet_interfaces::PortClass::__SourceBreaking { unknown_ordinal } => {
102 Err(UnknownPortClassError::NetInterfaces(UnknownNetInterfacesPortClassError {
103 unknown_ordinal,
104 }))
105 }
106 }
107 }
108}
109
110impl From<PortClass> for fnet_interfaces::PortClass {
111 fn from(port_class: PortClass) -> Self {
112 match port_class {
113 PortClass::Loopback => fnet_interfaces::PortClass::Loopback(fnet_interfaces::Empty),
114 PortClass::Blackhole => fnet_interfaces::PortClass::Blackhole(fnet_interfaces::Empty),
115 PortClass::Virtual => {
116 fnet_interfaces::PortClass::Device(fhardware_network::PortClass::Virtual)
117 }
118 PortClass::Ethernet => {
119 fnet_interfaces::PortClass::Device(fhardware_network::PortClass::Ethernet)
120 }
121 PortClass::WlanClient => {
122 fnet_interfaces::PortClass::Device(fhardware_network::PortClass::WlanClient)
123 }
124 PortClass::WlanAp => {
125 fnet_interfaces::PortClass::Device(fhardware_network::PortClass::WlanAp)
126 }
127 PortClass::Ppp => fnet_interfaces::PortClass::Device(fhardware_network::PortClass::Ppp),
128 PortClass::Bridge => {
129 fnet_interfaces::PortClass::Device(fhardware_network::PortClass::Bridge)
130 }
131 PortClass::Lowpan => {
132 fnet_interfaces::PortClass::Device(fhardware_network::PortClass::Lowpan)
133 }
134 }
135 }
136}
137
138impl TryFrom<fhardware_network::PortClass> for PortClass {
139 type Error = UnknownHardwareNetworkPortClassError;
140 fn try_from(port_class: fhardware_network::PortClass) -> Result<Self, Self::Error> {
141 match port_class {
142 fhardware_network::PortClass::Virtual => Ok(PortClass::Virtual),
143 fhardware_network::PortClass::Ethernet => Ok(PortClass::Ethernet),
144 fhardware_network::PortClass::WlanClient => Ok(PortClass::WlanClient),
145 fhardware_network::PortClass::WlanAp => Ok(PortClass::WlanAp),
146 fhardware_network::PortClass::Ppp => Ok(PortClass::Ppp),
147 fhardware_network::PortClass::Bridge => Ok(PortClass::Bridge),
148 fhardware_network::PortClass::Lowpan => Ok(PortClass::Lowpan),
149 fhardware_network::PortClass::__SourceBreaking { unknown_ordinal } => {
150 Err(UnknownHardwareNetworkPortClassError { unknown_ordinal })
151 }
152 }
153 }
154}
155
156#[derive(Derivative, ValidFidlTable)]
158#[derivative(Clone(bound = ""), Debug(bound = ""), Eq(bound = ""), PartialEq(bound = ""))]
159#[fidl_table_src(fnet_interfaces::Properties)]
160pub struct Properties<I: FieldInterests> {
161 pub id: NonZeroU64,
164 pub name: String,
166 pub online: bool,
168 pub addresses: Vec<Address<I>>,
170 pub has_default_ipv4_route: bool,
172 pub has_default_ipv6_route: bool,
174 pub port_class: PortClass,
176}
177
178#[derive(Derivative, ValidFidlTable)]
180#[derivative(
181 Clone(bound = ""),
182 Debug(bound = ""),
183 Eq(bound = ""),
184 PartialEq(bound = ""),
185 Hash(bound = "")
186)]
187#[fidl_table_src(fnet_interfaces::Address)]
188#[fidl_table_strict]
189pub struct Address<I: FieldInterests> {
190 pub addr: fidl_fuchsia_net::Subnet,
192 #[fidl_field_type(optional_converter = InterestConverter::<I, ValidUntilInterest>)]
197 pub valid_until: FromInterest<I, ValidUntilInterest>,
198 #[fidl_field_type(optional_converter = InterestConverter::<I, PreferredLifetimeInfoInterest>)]
200 pub preferred_lifetime_info: FromInterest<I, PreferredLifetimeInfoInterest>,
201 pub assignment_state: fnet_interfaces::AddressAssignmentState,
203}
204
205#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Copy, Clone, Hash)]
210#[allow(missing_docs)]
211pub enum PreferredLifetimeInfo {
212 PreferredUntil(PositiveMonotonicInstant),
213 Deprecated,
214}
215
216impl PreferredLifetimeInfo {
217 pub const fn preferred_forever() -> Self {
219 Self::PreferredUntil(PositiveMonotonicInstant::INFINITE_FUTURE)
220 }
221
222 pub const fn to_fidl(self) -> fnet_interfaces::PreferredLifetimeInfo {
224 match self {
225 PreferredLifetimeInfo::Deprecated => {
226 fnet_interfaces::PreferredLifetimeInfo::Deprecated(fnet_interfaces::Empty)
227 }
228 PreferredLifetimeInfo::PreferredUntil(v) => {
229 fnet_interfaces::PreferredLifetimeInfo::PreferredUntil(v.into_nanos())
230 }
231 }
232 }
233}
234
235impl TryFrom<fnet_interfaces::PreferredLifetimeInfo> for PreferredLifetimeInfo {
236 type Error = NotPositiveMonotonicInstantError;
237
238 fn try_from(value: fnet_interfaces::PreferredLifetimeInfo) -> Result<Self, Self::Error> {
239 match value {
240 fnet_interfaces::PreferredLifetimeInfo::Deprecated(fnet_interfaces::Empty) => {
241 Ok(Self::Deprecated)
242 }
243 fnet_interfaces::PreferredLifetimeInfo::PreferredUntil(v) => {
244 Ok(Self::PreferredUntil(v.try_into()?))
245 }
246 }
247 }
248}
249
250impl From<PreferredLifetimeInfo> for fnet_interfaces::PreferredLifetimeInfo {
251 fn from(value: PreferredLifetimeInfo) -> Self {
252 value.to_fidl()
253 }
254}
255
256#[derive(Error, Debug)]
259#[error("{0} is not a positive monotonic instant")]
260pub struct NotPositiveMonotonicInstantError(i64);
261
262#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Copy, Clone, Hash)]
264pub struct PositiveMonotonicInstant(i64);
265
266impl PositiveMonotonicInstant {
267 pub const INFINITE_FUTURE: Self = Self(zx_types::ZX_TIME_INFINITE);
269
270 pub const fn into_nanos(self) -> i64 {
272 let Self(v) = self;
273 v
274 }
275
276 pub const fn from_nanos(v: i64) -> Option<Self> {
279 if v > 0 { Some(Self(v)) } else { None }
280 }
281
282 pub fn is_infinite(&self) -> bool {
284 self == &Self::INFINITE_FUTURE
285 }
286}
287
288#[cfg(target_os = "fuchsia")]
289impl From<PositiveMonotonicInstant> for zx::MonotonicInstant {
290 fn from(PositiveMonotonicInstant(v): PositiveMonotonicInstant) -> Self {
291 zx::MonotonicInstant::from_nanos(v)
292 }
293}
294
295#[cfg(target_os = "fuchsia")]
296impl TryFrom<zx::MonotonicInstant> for PositiveMonotonicInstant {
297 type Error = NotPositiveMonotonicInstantError;
298
299 fn try_from(value: zx::MonotonicInstant) -> Result<Self, Self::Error> {
300 Self::try_from(value.into_nanos())
301 }
302}
303
304impl From<PositiveMonotonicInstant> for zx_types::zx_time_t {
305 fn from(value: PositiveMonotonicInstant) -> Self {
306 value.into_nanos()
307 }
308}
309
310impl TryFrom<zx_types::zx_time_t> for PositiveMonotonicInstant {
311 type Error = NotPositiveMonotonicInstantError;
312
313 fn try_from(value: zx_types::zx_time_t) -> Result<Self, Self::Error> {
314 Self::from_nanos(value).ok_or(NotPositiveMonotonicInstantError(value))
315 }
316}
317
318#[derive(Error, Debug)]
320pub enum UpdateError {
321 #[error("duplicate added event {0:?}")]
323 DuplicateAdded(fnet_interfaces::Properties),
324 #[error("duplicate existing event {0:?}")]
326 DuplicateExisting(fnet_interfaces::Properties),
327 #[error("failed to validate Properties FIDL table: {0}")]
329 InvalidProperties(#[from] PropertiesValidationError),
330 #[error("failed to validate Address FIDL table: {0}")]
332 InvalidAddress(#[from] AddressValidationError),
333 #[error("changed event with missing ID {0:?}")]
335 MissingId(fnet_interfaces::Properties),
336 #[error("changed event contains no changed fields {0:?}")]
338 EmptyChange(fnet_interfaces::Properties),
339 #[error("interface has been removed")]
341 Removed,
342 #[error("unknown interface changed {0:?}")]
344 UnknownChanged(fnet_interfaces::Properties),
345 #[error("unknown interface with id {0} deleted")]
347 UnknownRemoved(u64),
348 #[error("encountered 0 interface id")]
350 ZeroInterfaceId,
351}
352
353#[derive(Derivative)]
355#[derivative(Debug(bound = "S: Debug"), PartialEq(bound = "S: PartialEq"))]
356pub enum UpdateResult<'a, S, I: FieldInterests> {
357 NoChange,
359 Existing {
361 properties: &'a Properties<I>,
363 state: &'a mut S,
365 },
366 Added {
368 properties: &'a Properties<I>,
370 state: &'a mut S,
372 },
373 Changed {
375 previous: fnet_interfaces::Properties,
381 current: &'a Properties<I>,
383 state: &'a mut S,
385 },
386 Removed(PropertiesAndState<S, I>),
388}
389
390#[derive(Derivative)]
392#[derivative(
393 Clone(bound = "S: Clone"),
394 Debug(bound = "S: Debug"),
395 Eq(bound = "S: Eq"),
396 PartialEq(bound = "S: PartialEq")
397)]
398pub struct PropertiesAndState<S, I: FieldInterests> {
399 pub properties: Properties<I>,
401 pub state: S,
403}
404
405pub trait Update<S> {
407 type Interest: FieldInterests;
409
410 fn update(
412 &mut self,
413 event: EventWithInterest<Self::Interest>,
414 ) -> Result<UpdateResult<'_, S, Self::Interest>, UpdateError>;
415}
416
417impl<S, I: FieldInterests> Update<S> for PropertiesAndState<S, I> {
418 type Interest = I;
419 fn update(
420 &mut self,
421 event: EventWithInterest<I>,
422 ) -> Result<UpdateResult<'_, S, I>, UpdateError> {
423 let Self { properties, state } = self;
424 match event.into_inner() {
425 fnet_interfaces::Event::Existing(existing) => {
426 let existing = Properties::<I>::try_from(existing)?;
427 if existing.id == properties.id {
428 return Err(UpdateError::DuplicateExisting(existing.into()));
429 }
430 }
431 fnet_interfaces::Event::Added(added) => {
432 let added = Properties::<I>::try_from(added)?;
433 if added.id == properties.id {
434 return Err(UpdateError::DuplicateAdded(added.into()));
435 }
436 }
437 fnet_interfaces::Event::Changed(mut change) => {
438 let fnet_interfaces::Properties {
439 id,
440 name: _,
441 port_class: _,
442 online,
443 has_default_ipv4_route,
444 has_default_ipv6_route,
445 addresses,
446 ..
447 } = &mut change;
448 if let Some(id) = *id {
449 if properties.id.get() == id {
450 let mut changed = false;
451 macro_rules! swap_if_some {
452 ($field:ident) => {
453 if let Some($field) = $field {
454 if properties.$field != *$field {
455 std::mem::swap(&mut properties.$field, $field);
456 changed = true;
457 }
458 }
459 };
460 }
461 swap_if_some!(online);
462 swap_if_some!(has_default_ipv4_route);
463 swap_if_some!(has_default_ipv6_route);
464 if let Some(addresses) = addresses {
465 if addresses.len() != properties.addresses.len()
473 || !addresses
474 .iter()
475 .zip(
476 properties
477 .addresses
478 .iter()
479 .cloned()
480 .map(fnet_interfaces::Address::from),
481 )
482 .all(|(a, b)| *a == b)
483 {
484 let previous_len = properties.addresses.len();
485 let () = properties.addresses.reserve(addresses.len());
488 for address in addresses.drain(..).map(Address::try_from) {
489 let () = properties.addresses.push(address?);
490 }
491 let () = addresses.extend(
492 properties.addresses.drain(..previous_len).map(Into::into),
493 );
494 changed = true;
495 }
496 }
497 if changed {
498 change.id = None;
499 return Ok(UpdateResult::Changed {
500 previous: change,
501 current: properties,
502 state,
503 });
504 } else {
505 return Err(UpdateError::EmptyChange(change));
506 }
507 }
508 } else {
509 return Err(UpdateError::MissingId(change));
510 }
511 }
512 fnet_interfaces::Event::Removed(removed_id) => {
513 if properties.id.get() == removed_id {
514 return Err(UpdateError::Removed);
515 }
516 }
517 fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}) => {}
518 }
519 Ok(UpdateResult::NoChange)
520 }
521}
522
523impl<S: Default, I: FieldInterests> Update<S> for InterfaceState<S, I> {
524 type Interest = I;
525 fn update(
526 &mut self,
527 event: EventWithInterest<I>,
528 ) -> Result<UpdateResult<'_, S, I>, UpdateError> {
529 fn get_properties<S, I: FieldInterests>(
530 state: &mut InterfaceState<S, I>,
531 ) -> &mut PropertiesAndState<S, I> {
532 match state {
533 InterfaceState::Known(properties) => properties,
534 InterfaceState::Unknown(id) => unreachable!(
535 "matched `Unknown({})` immediately after assigning with `Known`",
536 id
537 ),
538 }
539 }
540 match self {
541 InterfaceState::Unknown(id) => match event.into_inner() {
542 fnet_interfaces::Event::Existing(existing) => {
543 let properties = Properties::try_from(existing)?;
544 if properties.id.get() == *id {
545 *self = InterfaceState::Known(PropertiesAndState {
546 properties,
547 state: S::default(),
548 });
549 let PropertiesAndState { properties, state } = get_properties(self);
550 return Ok(UpdateResult::Existing { properties, state });
551 }
552 }
553 fnet_interfaces::Event::Added(added) => {
554 let properties = Properties::try_from(added)?;
555 if properties.id.get() == *id {
556 *self = InterfaceState::Known(PropertiesAndState {
557 properties,
558 state: S::default(),
559 });
560 let PropertiesAndState { properties, state } = get_properties(self);
561 return Ok(UpdateResult::Added { properties, state });
562 }
563 }
564 fnet_interfaces::Event::Changed(change) => {
565 if let Some(change_id) = change.id {
566 if change_id == *id {
567 return Err(UpdateError::UnknownChanged(change));
568 }
569 } else {
570 return Err(UpdateError::MissingId(change));
571 }
572 }
573 fnet_interfaces::Event::Removed(removed_id) => {
574 if removed_id == *id {
575 return Err(UpdateError::UnknownRemoved(removed_id));
576 }
577 }
578 fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}) => {}
579 },
580 InterfaceState::Known(properties) => return properties.update(event),
581 }
582 Ok(UpdateResult::NoChange)
583 }
584}
585
586pub struct ZeroError {}
588
589pub trait TryFromMaybeNonZero: Sized {
591 fn try_from(value: u64) -> Result<Self, ZeroError>;
593}
594
595impl TryFromMaybeNonZero for u64 {
596 fn try_from(value: u64) -> Result<Self, ZeroError> {
597 Ok(value)
598 }
599}
600
601impl TryFromMaybeNonZero for NonZeroU64 {
602 fn try_from(value: u64) -> Result<Self, ZeroError> {
603 NonZeroU64::new(value).ok_or(ZeroError {})
604 }
605}
606
607macro_rules! impl_map {
608 ($map_type:ident, $map_mod:tt) => {
609 impl<K, S, I> Update<S> for $map_type<K, PropertiesAndState<S, I>>
610 where
611 K: TryFromMaybeNonZero + Copy + From<NonZeroU64> + Eq + Ord + std::hash::Hash,
612 S: Default,
613 I: FieldInterests,
614 {
615 type Interest = I;
616
617 fn update(
618 &mut self,
619 event: EventWithInterest<I>,
620 ) -> Result<UpdateResult<'_, S, I>, UpdateError> {
621 match event.into_inner() {
622 fnet_interfaces::Event::Existing(existing) => {
623 let existing = Properties::try_from(existing)?;
624 match self.entry(existing.id.into()) {
625 $map_mod::Entry::Occupied(_) => {
626 Err(UpdateError::DuplicateExisting(existing.into()))
627 }
628 $map_mod::Entry::Vacant(entry) => {
629 let PropertiesAndState { properties, state } =
630 entry.insert(PropertiesAndState {
631 properties: existing,
632 state: S::default(),
633 });
634 Ok(UpdateResult::Existing { properties, state })
635 }
636 }
637 }
638 fnet_interfaces::Event::Added(added) => {
639 let added = Properties::try_from(added)?;
640 match self.entry(added.id.into()) {
641 $map_mod::Entry::Occupied(_) => {
642 Err(UpdateError::DuplicateAdded(added.into()))
643 }
644 $map_mod::Entry::Vacant(entry) => {
645 let PropertiesAndState { properties, state } =
646 entry.insert(PropertiesAndState {
647 properties: added,
648 state: S::default(),
649 });
650 Ok(UpdateResult::Added { properties, state })
651 }
652 }
653 }
654 fnet_interfaces::Event::Changed(change) => {
655 let id = if let Some(id) = change.id {
656 id
657 } else {
658 return Err(UpdateError::MissingId(change));
659 };
660 if let Some(properties) = self.get_mut(
661 &K::try_from(id)
662 .map_err(|ZeroError {}| UpdateError::ZeroInterfaceId)?,
663 ) {
664 properties.update(EventWithInterest::new(
665 fnet_interfaces::Event::Changed(change),
666 ))
667 } else {
668 Err(UpdateError::UnknownChanged(change))
669 }
670 }
671 fnet_interfaces::Event::Removed(removed_id) => {
672 if let Some(properties) = self.remove(
673 &K::try_from(removed_id)
674 .map_err(|ZeroError {}| UpdateError::ZeroInterfaceId)?,
675 ) {
676 Ok(UpdateResult::Removed(properties))
677 } else {
678 Err(UpdateError::UnknownRemoved(removed_id))
679 }
680 }
681 fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}) => {
682 Ok(UpdateResult::NoChange)
683 }
684 }
685 }
686 }
687 };
688}
689
690impl_map!(BTreeMap, btree_map);
691impl_map!(HashMap, hash_map);
692
693#[derive(Error, Debug)]
695pub enum WatcherOperationError<S: Debug, B: Update<S> + Debug> {
696 #[error("event stream error: {0}")]
698 EventStream(fidl::Error),
699 #[error("failed to update: {0}")]
701 Update(UpdateError),
702 #[error("watcher event stream ended unexpectedly, final state: {final_state:?}")]
704 UnexpectedEnd {
705 final_state: B,
707 marker: std::marker::PhantomData<S>,
709 },
710 #[error("unexpected event type: {0:?}")]
712 UnexpectedEvent(fnet_interfaces::Event),
713}
714
715#[derive(Error, Debug)]
717pub enum WatcherCreationError {
718 #[error("failed to create interface watcher proxy: {0}")]
720 CreateProxy(fidl::Error),
721 #[error("failed to get interface watcher: {0}")]
723 GetWatcher(fidl::Error),
724}
725
726pub async fn wait_interface<S, B, St, F, T>(
735 stream: St,
736 init: &mut B,
737 mut predicate: F,
738) -> Result<T, WatcherOperationError<S, B>>
739where
740 S: Debug + Default,
741 B: Update<S> + Clone + Debug,
742 St: Stream<Item = Result<EventWithInterest<B::Interest>, fidl::Error>>,
743 F: FnMut(&B) -> Option<T>,
744{
745 async_utils::fold::try_fold_while(
746 stream.map_err(WatcherOperationError::EventStream),
747 init,
748 |acc, event| {
749 futures::future::ready(match acc.update(event) {
750 Ok(changed) => match changed {
751 UpdateResult::Existing { .. }
752 | UpdateResult::Added { .. }
753 | UpdateResult::Changed { .. }
754 | UpdateResult::Removed(_) => {
755 if let Some(rtn) = predicate(acc) {
756 Ok(async_utils::fold::FoldWhile::Done(rtn))
757 } else {
758 Ok(async_utils::fold::FoldWhile::Continue(acc))
759 }
760 }
761 UpdateResult::NoChange => Ok(async_utils::fold::FoldWhile::Continue(acc)),
762 },
763 Err(e) => Err(WatcherOperationError::Update(e)),
764 })
765 },
766 )
767 .await?
768 .short_circuited()
769 .map_err(|final_state| WatcherOperationError::UnexpectedEnd {
770 final_state: final_state.clone(),
771 marker: Default::default(),
772 })
773}
774
775#[derive(Derivative)]
777#[derivative(
778 Clone(bound = "S: Clone"),
779 Debug(bound = "S: Debug"),
780 PartialEq(bound = "S: PartialEq")
781)]
782pub enum InterfaceState<S, I: FieldInterests> {
783 Unknown(u64),
785 Known(PropertiesAndState<S, I>),
787}
788
789pub async fn wait_interface_with_id<S, St, F, T, I>(
801 stream: St,
802 init: &mut InterfaceState<S, I>,
803 mut predicate: F,
804) -> Result<T, WatcherOperationError<S, InterfaceState<S, I>>>
805where
806 S: Default + Clone + Debug,
807 St: Stream<Item = Result<EventWithInterest<I>, fidl::Error>>,
808 F: FnMut(&PropertiesAndState<S, I>) -> Option<T>,
809 I: FieldInterests,
810{
811 wait_interface(stream, init, |state| {
812 match state {
813 InterfaceState::Known(properties) => predicate(properties),
814 InterfaceState::Unknown(_) => None,
817 }
818 })
819 .await
820}
821
822pub async fn existing<S, St, B>(stream: St, init: B) -> Result<B, WatcherOperationError<S, B>>
828where
829 S: Debug,
830 St: futures::Stream<Item = Result<EventWithInterest<B::Interest>, fidl::Error>>,
831 B: Update<S> + Debug,
832{
833 async_utils::fold::try_fold_while(
834 stream.map_err(WatcherOperationError::EventStream),
835 init,
836 |mut acc, event| {
837 futures::future::ready(match event.inner() {
838 fnet_interfaces::Event::Existing(_) => match acc.update(event) {
839 Ok::<UpdateResult<'_, _, _>, _>(_) => {
840 Ok(async_utils::fold::FoldWhile::Continue(acc))
841 }
842 Err(e) => Err(WatcherOperationError::Update(e)),
843 },
844 fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}) => {
845 Ok(async_utils::fold::FoldWhile::Done(acc))
846 }
847 fnet_interfaces::Event::Added(_)
848 | fnet_interfaces::Event::Removed(_)
849 | fnet_interfaces::Event::Changed(_) => {
850 Err(WatcherOperationError::UnexpectedEvent(event.into_inner()))
851 }
852 })
853 },
854 )
855 .await?
856 .short_circuited()
857 .map_err(|acc| WatcherOperationError::UnexpectedEnd {
858 final_state: acc,
859 marker: Default::default(),
860 })
861}
862
863pub enum IncludedAddresses {
865 All,
867 OnlyAssigned,
869}
870
871pub fn event_stream_from_state<I: FieldInterests>(
877 interface_state: &fnet_interfaces::StateProxy,
878 included_addresses: IncludedAddresses,
879) -> Result<impl Stream<Item = Result<EventWithInterest<I>, fidl::Error>>, WatcherCreationError> {
880 let (watcher, server) = ::fidl::endpoints::create_proxy::<fnet_interfaces::WatcherMarker>();
881 let () = interface_state
882 .get_watcher(
883 &fnet_interfaces::WatcherOptions {
884 address_properties_interest: Some(interest_from_params::<I>()),
885 include_non_assigned_addresses: Some(match included_addresses {
886 IncludedAddresses::All => true,
887 IncludedAddresses::OnlyAssigned => false,
888 }),
889 ..Default::default()
890 },
891 server,
892 )
893 .map_err(WatcherCreationError::GetWatcher)?;
894 Ok(futures::stream::try_unfold(watcher, |watcher| async {
895 Ok(Some((EventWithInterest::new(watcher.watch().await?), watcher)))
896 }))
897}
898
899fn interest_from_params<I: FieldInterests>() -> fnet_interfaces::AddressPropertiesInterest {
900 let mut interest = fnet_interfaces::AddressPropertiesInterest::empty();
901 if <I::ValidUntil as MaybeInterest<_>>::ENABLED {
902 interest |= fnet_interfaces::AddressPropertiesInterest::VALID_UNTIL;
903 }
904 if <I::PreferredLifetimeInfo as MaybeInterest<_>>::ENABLED {
905 interest |= fnet_interfaces::AddressPropertiesInterest::PREFERRED_LIFETIME_INFO;
906 }
907 interest
908}
909
910#[derive(Clone, Debug, Eq, Hash, PartialEq, Default)]
912pub struct NoInterest;
913
914mod interest {
915 use super::*;
916
917 use Debug;
918 use std::hash::Hash;
919
920 pub trait FieldInterests {
926 type PreferredLifetimeInfo: MaybeInterest<PreferredLifetimeInfo>;
928 type ValidUntil: MaybeInterest<PositiveMonotonicInstant>;
930 }
931
932 pub trait MaybeInterest<T> {
934 const ENABLED: bool;
936
937 type Ty: Clone + Debug + Eq + Hash + PartialEq + 'static;
939
940 fn try_from_fidl<F: TryInto<T, Error: Into<anyhow::Error>>>(
942 fidl: Option<F>,
943 ) -> Result<Self::Ty, anyhow::Error>;
944
945 fn into_fidl<F: From<T>>(value: Self::Ty) -> Option<F>;
948 }
949
950 pub struct EnableInterest;
954
955 impl<T: Clone + Debug + Eq + Hash + PartialEq + 'static> MaybeInterest<T> for EnableInterest {
956 const ENABLED: bool = true;
957 type Ty = T;
958
959 fn try_from_fidl<F: TryInto<T, Error: Into<anyhow::Error>>>(
960 fidl: Option<F>,
961 ) -> Result<Self::Ty, anyhow::Error> {
962 fidl.map(|f| f.try_into().map_err(Into::into))
963 .unwrap_or_else(|| Err(anyhow::anyhow!("missing field with registered interest")))
964 }
965
966 fn into_fidl<F: From<T>>(value: Self::Ty) -> Option<F> {
967 Some(value.into())
968 }
969 }
970
971 pub struct DisableInterest;
975 impl<T> MaybeInterest<T> for DisableInterest {
976 const ENABLED: bool = false;
977
978 type Ty = NoInterest;
979
980 fn try_from_fidl<F: TryInto<T, Error: Into<anyhow::Error>>>(
981 fidl: Option<F>,
982 ) -> Result<Self::Ty, anyhow::Error> {
983 match fidl {
984 Some(_) => Err(anyhow::anyhow!("unexpected set field with no registered interest")),
985 None => Ok(NoInterest),
986 }
987 }
988
989 fn into_fidl<F: From<T>>(_value: Self::Ty) -> Option<F> {
990 None
991 }
992 }
993
994 pub(super) type FromInterest<I, T> =
997 <<T as FieldSpec>::Interest<I> as MaybeInterest<<T as FieldSpec>::Present>>::Ty;
998
999 pub trait FieldSpec {
1005 type Interest<I: FieldInterests>: MaybeInterest<Self::Present>;
1007
1008 type Fidl: From<Self::Present>;
1010
1011 type Present: TryFrom<Self::Fidl, Error: Into<anyhow::Error>>;
1014
1015 const FIELD_NAME: &'static str;
1018 }
1019
1020 pub struct InterestConverter<I, P>(PhantomData<(I, P)>);
1021
1022 impl<I, P> fidl_table_validation::Converter for InterestConverter<I, P>
1023 where
1024 I: FieldInterests,
1025 P: FieldSpec,
1026 {
1027 type Fidl = Option<P::Fidl>;
1028 type Validated = <P::Interest<I> as MaybeInterest<P::Present>>::Ty;
1029 type Error = anyhow::Error;
1030
1031 fn try_from_fidl(value: Self::Fidl) -> std::result::Result<Self::Validated, Self::Error> {
1032 <P::Interest<I> as MaybeInterest<_>>::try_from_fidl(value).context(P::FIELD_NAME)
1033 }
1034
1035 fn from_validated(validated: Self::Validated) -> Self::Fidl {
1036 <P::Interest<I> as MaybeInterest<_>>::into_fidl(validated)
1037 }
1038 }
1039
1040 pub struct ValidUntilInterest;
1041
1042 impl FieldSpec for ValidUntilInterest {
1043 type Interest<I: FieldInterests> = I::ValidUntil;
1044 type Fidl = zx_types::zx_time_t;
1045 type Present = PositiveMonotonicInstant;
1046 const FIELD_NAME: &'static str = "valid_until";
1047 }
1048
1049 pub struct PreferredLifetimeInfoInterest;
1050
1051 impl FieldSpec for PreferredLifetimeInfoInterest {
1052 type Interest<I: FieldInterests> = I::PreferredLifetimeInfo;
1053 type Fidl = fnet_interfaces::PreferredLifetimeInfo;
1054 type Present = PreferredLifetimeInfo;
1055 const FIELD_NAME: &'static str = "preferred_lifetime_info";
1056 }
1057}
1058pub use interest::{DisableInterest, EnableInterest, FieldInterests};
1059use interest::{
1060 FromInterest, InterestConverter, MaybeInterest, PreferredLifetimeInfoInterest,
1061 ValidUntilInterest,
1062};
1063
1064#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1066pub struct AllInterest;
1067impl FieldInterests for AllInterest {
1068 type PreferredLifetimeInfo = EnableInterest;
1069 type ValidUntil = EnableInterest;
1070}
1071
1072#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1075pub struct DefaultInterest;
1076impl FieldInterests for DefaultInterest {
1077 type PreferredLifetimeInfo = DisableInterest;
1078 type ValidUntil = DisableInterest;
1079}
1080
1081#[derive(Derivative)]
1084#[derivative(Clone(bound = ""), Debug(bound = ""), Eq(bound = ""), PartialEq(bound = ""))]
1085pub struct EventWithInterest<I: FieldInterests> {
1086 event: fnet_interfaces::Event,
1087 #[derivative(Debug = "ignore")]
1088 _marker: PhantomData<I>,
1089}
1090
1091impl<I: FieldInterests> EventWithInterest<I> {
1092 pub fn new(event: fnet_interfaces::Event) -> Self {
1099 Self { event, _marker: PhantomData }
1100 }
1101
1102 pub fn into_inner(self) -> fnet_interfaces::Event {
1104 self.event
1105 }
1106
1107 pub fn inner(&self) -> &fnet_interfaces::Event {
1109 &self.event
1110 }
1111}
1112
1113impl<I: FieldInterests> From<fnet_interfaces::Event> for EventWithInterest<I> {
1114 fn from(value: fnet_interfaces::Event) -> Self {
1115 Self::new(value)
1116 }
1117}
1118
1119impl<I: FieldInterests> From<EventWithInterest<I>> for fnet_interfaces::Event {
1120 fn from(value: EventWithInterest<I>) -> Self {
1121 value.into_inner()
1122 }
1123}
1124
1125#[cfg(test)]
1126mod tests {
1127 use super::*;
1128 use assert_matches::assert_matches;
1129 use fidl_fuchsia_net as fnet;
1130 use futures::FutureExt as _;
1131 use futures::task::Poll;
1132 use net_declare::fidl_subnet;
1133 use std::cell::RefCell;
1134 use std::convert::TryInto as _;
1135 use std::pin::Pin;
1136 use std::rc::Rc;
1137 use test_case::test_case;
1138
1139 fn fidl_properties(id: u64) -> fnet_interfaces::Properties {
1140 fnet_interfaces::Properties {
1141 id: Some(id),
1142 name: Some("test1".to_string()),
1143 port_class: Some(fnet_interfaces::PortClass::Loopback(fnet_interfaces::Empty {})),
1144 online: Some(false),
1145 has_default_ipv4_route: Some(false),
1146 has_default_ipv6_route: Some(false),
1147 addresses: Some(vec![fidl_address(ADDR, zx_types::ZX_TIME_INFINITE)]),
1148 ..Default::default()
1149 }
1150 }
1151
1152 fn validated_properties(id: u64) -> PropertiesAndState<(), AllInterest> {
1153 PropertiesAndState {
1154 properties: fidl_properties(id).try_into().expect("failed to validate FIDL Properties"),
1155 state: (),
1156 }
1157 }
1158
1159 fn properties_delta(id: u64) -> fnet_interfaces::Properties {
1160 fnet_interfaces::Properties {
1161 id: Some(id),
1162 name: None,
1163 port_class: None,
1164 online: Some(true),
1165 has_default_ipv4_route: Some(true),
1166 has_default_ipv6_route: Some(true),
1167 addresses: Some(vec![fidl_address(ADDR2, zx_types::ZX_TIME_INFINITE)]),
1168 ..Default::default()
1169 }
1170 }
1171
1172 fn fidl_properties_after_change(id: u64) -> fnet_interfaces::Properties {
1173 fnet_interfaces::Properties {
1174 id: Some(id),
1175 name: Some("test1".to_string()),
1176 port_class: Some(fnet_interfaces::PortClass::Loopback(fnet_interfaces::Empty {})),
1177 online: Some(true),
1178 has_default_ipv4_route: Some(true),
1179 has_default_ipv6_route: Some(true),
1180 addresses: Some(vec![fidl_address(ADDR2, zx_types::ZX_TIME_INFINITE)]),
1181 ..Default::default()
1182 }
1183 }
1184
1185 fn validated_properties_after_change(id: u64) -> PropertiesAndState<(), AllInterest> {
1186 PropertiesAndState {
1187 properties: fidl_properties_after_change(id)
1188 .try_into()
1189 .expect("failed to validate FIDL Properties"),
1190 state: (),
1191 }
1192 }
1193
1194 fn fidl_address(
1195 addr: fnet::Subnet,
1196 valid_until: zx_types::zx_time_t,
1197 ) -> fnet_interfaces::Address {
1198 fnet_interfaces::Address {
1199 addr: Some(addr),
1200 valid_until: Some(valid_until.try_into().unwrap()),
1201 assignment_state: Some(fnet_interfaces::AddressAssignmentState::Assigned),
1202 preferred_lifetime_info: Some(PreferredLifetimeInfo::preferred_forever().into()),
1203 __source_breaking: Default::default(),
1204 }
1205 }
1206
1207 const ID: u64 = 1;
1208 const ID2: u64 = 2;
1209 const ADDR: fnet::Subnet = fidl_subnet!("1.2.3.4/24");
1210 const ADDR2: fnet::Subnet = fidl_subnet!("5.6.7.8/24");
1211
1212 #[test_case(
1213 &mut std::iter::once((ID, validated_properties(ID))).collect::<HashMap<_, _>>();
1214 "hashmap"
1215 )]
1216 #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1217 #[test_case(&mut validated_properties(ID); "properties")]
1218 fn test_duplicate_error(state: &mut impl Update<(), Interest = AllInterest>) {
1219 assert_matches::assert_matches!(
1220 state.update(fnet_interfaces::Event::Added(fidl_properties(ID)).into()),
1221 Err(UpdateError::DuplicateAdded(added)) if added == fidl_properties(ID)
1222 );
1223 assert_matches::assert_matches!(
1224 state.update(fnet_interfaces::Event::Existing(fidl_properties(ID)).into()),
1225 Err(UpdateError::DuplicateExisting(existing)) if existing == fidl_properties(ID)
1226 );
1227 }
1228
1229 #[test_case(&mut HashMap::<u64, _>::new(); "hashmap")]
1230 #[test_case(&mut InterfaceState::Unknown(ID); "interface_state_unknown")]
1231 fn test_unknown_error(state: &mut impl Update<(), Interest = AllInterest>) {
1232 let unknown =
1233 fnet_interfaces::Properties { id: Some(ID), online: Some(true), ..Default::default() };
1234 assert_matches::assert_matches!(
1235 state.update(fnet_interfaces::Event::Changed(unknown.clone()).into()),
1236 Err(UpdateError::UnknownChanged(changed)) if changed == unknown
1237 );
1238 assert_matches::assert_matches!(
1239 state.update(fnet_interfaces::Event::Removed(ID).into()),
1240 Err(UpdateError::UnknownRemoved(id)) if id == ID
1241 );
1242 }
1243
1244 #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1245 #[test_case(&mut validated_properties(ID); "properties")]
1246 fn test_removed_error(state: &mut impl Update<()>) {
1247 assert_matches::assert_matches!(
1248 state.update(fnet_interfaces::Event::Removed(ID).into()),
1249 Err(UpdateError::Removed)
1250 );
1251 }
1252
1253 #[test_case(&mut HashMap::<u64, _>::new(); "hashmap")]
1254 #[test_case(&mut InterfaceState::Unknown(ID); "interface_state_unknown")]
1255 #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1256 #[test_case(&mut validated_properties(ID); "properties")]
1257 fn test_missing_id_error(state: &mut impl Update<(), Interest = AllInterest>) {
1258 let missing_id = fnet_interfaces::Properties { online: Some(true), ..Default::default() };
1259 assert_matches::assert_matches!(
1260 state.update(fnet_interfaces::Event::Changed(missing_id.clone()).into()),
1261 Err(UpdateError::MissingId(properties)) if properties == missing_id
1262 );
1263 }
1264
1265 #[test_case(
1266 &mut std::iter::once((ID, validated_properties(ID))).collect::<HashMap<_, _>>();
1267 "hashmap"
1268 )]
1269 #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1270 #[test_case(&mut validated_properties(ID); "properties")]
1271 fn test_empty_change_error(state: &mut impl Update<()>) {
1272 let empty_change = fnet_interfaces::Properties { id: Some(ID), ..Default::default() };
1273 let net_zero_change =
1274 fnet_interfaces::Properties { name: None, port_class: None, ..fidl_properties(ID) };
1275 assert_matches::assert_matches!(
1276 state.update(fnet_interfaces::Event::Changed(empty_change.clone()).into()),
1277 Err(UpdateError::EmptyChange(properties)) if properties == empty_change
1278 );
1279 assert_matches::assert_matches!(
1280 state.update(fnet_interfaces::Event::Changed(net_zero_change.clone()).into()),
1281 Err(UpdateError::EmptyChange(properties)) if properties == net_zero_change
1282 );
1283 }
1284
1285 #[test_case(
1286 &mut std::iter::once((ID, validated_properties(ID))).collect::<HashMap<_, _>>();
1287 "hashmap"
1288 )]
1289 #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1290 #[test_case(&mut validated_properties(ID); "properties")]
1291 fn test_update_changed_result(state: &mut impl Update<(), Interest = AllInterest>) {
1292 let want_previous = fnet_interfaces::Properties {
1293 online: Some(false),
1294 has_default_ipv4_route: Some(false),
1295 has_default_ipv6_route: Some(false),
1296 addresses: Some(vec![fidl_address(ADDR, zx_types::ZX_TIME_INFINITE)]),
1297 ..Default::default()
1298 };
1299 assert_matches::assert_matches!(
1300 state.update(fnet_interfaces::Event::Changed(properties_delta(ID).clone()).into()),
1301 Ok(UpdateResult::Changed { previous, current, state: _ }) => {
1302 assert_eq!(previous, want_previous);
1303 let PropertiesAndState { properties, state: () } =
1304 validated_properties_after_change(ID);
1305 assert_eq!(*current, properties);
1306 }
1307 );
1308 }
1309
1310 #[derive(Derivative)]
1311 #[derivative(Clone(bound = ""))]
1312 struct EventStream<I: FieldInterests>(Rc<RefCell<Vec<fnet_interfaces::Event>>>, PhantomData<I>);
1313
1314 impl<I: FieldInterests> Stream for EventStream<I> {
1315 type Item = Result<EventWithInterest<I>, fidl::Error>;
1316
1317 fn poll_next(
1318 self: Pin<&mut Self>,
1319 _cx: &mut futures::task::Context<'_>,
1320 ) -> Poll<Option<Self::Item>> {
1321 let EventStream(events_vec, _marker) = &*self;
1322 if events_vec.borrow().is_empty() {
1323 Poll::Ready(None)
1324 } else {
1325 Poll::Ready(Some(Ok(EventWithInterest::new(events_vec.borrow_mut().remove(0)))))
1326 }
1327 }
1328 }
1329
1330 fn test_event_stream<I: FieldInterests>() -> EventStream<I> {
1331 EventStream(
1332 Rc::new(RefCell::new(vec![
1333 fnet_interfaces::Event::Existing(fidl_properties(ID)),
1334 fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}),
1335 fnet_interfaces::Event::Added(fidl_properties(ID2)),
1336 fnet_interfaces::Event::Changed(properties_delta(ID)),
1337 fnet_interfaces::Event::Changed(properties_delta(ID2)),
1338 fnet_interfaces::Event::Removed(ID),
1339 fnet_interfaces::Event::Removed(ID2),
1340 ])),
1341 PhantomData,
1342 )
1343 }
1344
1345 #[test]
1346 fn test_wait_one_interface() {
1347 let event_stream = test_event_stream::<AllInterest>();
1348 let mut state = InterfaceState::Unknown(ID);
1349 for want in &[validated_properties(ID), validated_properties_after_change(ID)] {
1350 let () = wait_interface_with_id(event_stream.clone(), &mut state, |got| {
1351 assert_eq!(got, want);
1352 Some(())
1353 })
1354 .now_or_never()
1355 .expect("wait_interface_with_id did not complete immediately")
1356 .expect("wait_interface_with_id error");
1357 assert_matches!(state, InterfaceState::Known(ref got) if got == want);
1358 }
1359 }
1360
1361 fn test_wait_interface<'a, B>(state: &mut B, want_states: impl IntoIterator<Item = &'a B>)
1362 where
1363 B: 'a + Update<()> + Clone + Debug + std::cmp::PartialEq,
1364 {
1365 let event_stream = test_event_stream::<B::Interest>();
1366 for want in want_states.into_iter() {
1367 let () = wait_interface(event_stream.clone(), state, |got| {
1368 assert_eq!(got, want);
1369 Some(())
1370 })
1371 .now_or_never()
1372 .expect("wait_interface did not complete immediately")
1373 .expect("wait_interface error");
1374 assert_eq!(state, want);
1375 }
1376 }
1377
1378 #[test]
1379 fn test_wait_interface_hashmap() {
1380 test_wait_interface(
1381 &mut HashMap::new(),
1382 &[
1383 std::iter::once((ID, validated_properties(ID))).collect::<HashMap<_, _>>(),
1384 [(ID, validated_properties(ID)), (ID2, validated_properties(ID2))]
1385 .iter()
1386 .cloned()
1387 .collect::<HashMap<_, _>>(),
1388 [(ID, validated_properties_after_change(ID)), (ID2, validated_properties(ID2))]
1389 .iter()
1390 .cloned()
1391 .collect::<HashMap<_, _>>(),
1392 [
1393 (ID, validated_properties_after_change(ID)),
1394 (ID2, validated_properties_after_change(ID2)),
1395 ]
1396 .iter()
1397 .cloned()
1398 .collect::<HashMap<_, _>>(),
1399 std::iter::once((ID2, validated_properties_after_change(ID2)))
1400 .collect::<HashMap<_, _>>(),
1401 HashMap::new(),
1402 ],
1403 );
1404 }
1405
1406 #[test]
1407 fn test_wait_interface_interface_state() {
1408 test_wait_interface(
1409 &mut InterfaceState::Unknown(ID),
1410 &[
1411 InterfaceState::Known(validated_properties(ID)),
1412 InterfaceState::Known(validated_properties_after_change(ID)),
1413 ],
1414 );
1415 }
1416
1417 const ID_NON_EXISTENT: u64 = 0xffffffff;
1418 #[test_case(
1419 InterfaceState::Unknown(ID_NON_EXISTENT),
1420 InterfaceState::Unknown(ID_NON_EXISTENT);
1421 "interface_state_unknown_different_id"
1422 )]
1423 #[test_case(
1424 InterfaceState::Unknown(ID),
1425 InterfaceState::Known(validated_properties(ID));
1426 "interface_state_unknown")]
1427 #[test_case(
1428 HashMap::new(),
1429 [(ID, validated_properties(ID)), (ID2, validated_properties(ID2))]
1430 .iter()
1431 .cloned()
1432 .collect::<HashMap<_, _>>();
1433 "hashmap"
1434 )]
1435 fn test_existing<B>(state: B, want: B)
1436 where
1437 B: Update<(), Interest = AllInterest> + Debug + std::cmp::PartialEq,
1438 {
1439 let events = [
1440 fnet_interfaces::Event::Existing(fidl_properties(ID)),
1441 fnet_interfaces::Event::Existing(fidl_properties(ID2)),
1442 fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}),
1443 ];
1444 let event_stream =
1445 futures::stream::iter(events.iter().cloned().map(|e| Ok(EventWithInterest::new(e))));
1446 assert_eq!(
1447 existing(event_stream, state)
1448 .now_or_never()
1449 .expect("existing did not complete immediately")
1450 .expect("existing returned error"),
1451 want,
1452 );
1453 }
1454
1455 #[test]
1456 fn positive_instant() {
1457 assert_eq!(PositiveMonotonicInstant::from_nanos(-1), None);
1458 assert_eq!(PositiveMonotonicInstant::from_nanos(0), None);
1459 assert_eq!(PositiveMonotonicInstant::from_nanos(1), Some(PositiveMonotonicInstant(1)));
1460 }
1461}