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)]
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)]
78#[error("unknown fuchsia.net.interfaces/PortClass ordinal: {unknown_ordinal}")]
79pub struct UnknownNetInterfacesPortClassError {
80 unknown_ordinal: u64,
81}
82
83#[derive(Debug, Error)]
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)]
160#[fidl_table_strict(device_class)]
161pub struct Properties<I: FieldInterests> {
162 pub id: NonZeroU64,
165 pub name: String,
167 pub online: bool,
169 pub addresses: Vec<Address<I>>,
171 pub has_default_ipv4_route: bool,
173 pub has_default_ipv6_route: bool,
175 pub port_class: PortClass,
177}
178
179#[derive(Derivative, ValidFidlTable)]
181#[derivative(
182 Clone(bound = ""),
183 Debug(bound = ""),
184 Eq(bound = ""),
185 PartialEq(bound = ""),
186 Hash(bound = "")
187)]
188#[fidl_table_src(fnet_interfaces::Address)]
189#[fidl_table_strict]
190pub struct Address<I: FieldInterests> {
191 pub addr: fidl_fuchsia_net::Subnet,
193 #[fidl_field_type(optional_converter = InterestConverter::<I, ValidUntilInterest>)]
198 pub valid_until: FromInterest<I, ValidUntilInterest>,
199 #[fidl_field_type(optional_converter = InterestConverter::<I, PreferredLifetimeInfoInterest>)]
201 pub preferred_lifetime_info: FromInterest<I, PreferredLifetimeInfoInterest>,
202 pub assignment_state: fnet_interfaces::AddressAssignmentState,
204}
205
206#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Copy, Clone, Hash)]
211#[allow(missing_docs)]
212pub enum PreferredLifetimeInfo {
213 PreferredUntil(PositiveMonotonicInstant),
214 Deprecated,
215}
216
217impl PreferredLifetimeInfo {
218 pub const fn preferred_forever() -> Self {
220 Self::PreferredUntil(PositiveMonotonicInstant::INFINITE_FUTURE)
221 }
222
223 pub const fn to_fidl(self) -> fnet_interfaces::PreferredLifetimeInfo {
225 match self {
226 PreferredLifetimeInfo::Deprecated => {
227 fnet_interfaces::PreferredLifetimeInfo::Deprecated(fnet_interfaces::Empty)
228 }
229 PreferredLifetimeInfo::PreferredUntil(v) => {
230 fnet_interfaces::PreferredLifetimeInfo::PreferredUntil(v.into_nanos())
231 }
232 }
233 }
234}
235
236impl TryFrom<fnet_interfaces::PreferredLifetimeInfo> for PreferredLifetimeInfo {
237 type Error = NotPositiveMonotonicInstantError;
238
239 fn try_from(value: fnet_interfaces::PreferredLifetimeInfo) -> Result<Self, Self::Error> {
240 match value {
241 fnet_interfaces::PreferredLifetimeInfo::Deprecated(fnet_interfaces::Empty) => {
242 Ok(Self::Deprecated)
243 }
244 fnet_interfaces::PreferredLifetimeInfo::PreferredUntil(v) => {
245 Ok(Self::PreferredUntil(v.try_into()?))
246 }
247 }
248 }
249}
250
251impl From<PreferredLifetimeInfo> for fnet_interfaces::PreferredLifetimeInfo {
252 fn from(value: PreferredLifetimeInfo) -> Self {
253 value.to_fidl()
254 }
255}
256
257#[derive(Error, Debug)]
260#[error("{0} is not a positive monotonic instant")]
261pub struct NotPositiveMonotonicInstantError(i64);
262
263#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Copy, Clone, Hash)]
265pub struct PositiveMonotonicInstant(i64);
266
267impl PositiveMonotonicInstant {
268 pub const INFINITE_FUTURE: Self = Self(zx_types::ZX_TIME_INFINITE);
270
271 pub const fn into_nanos(self) -> i64 {
273 let Self(v) = self;
274 v
275 }
276
277 pub const fn from_nanos(v: i64) -> Option<Self> {
280 if v > 0 {
281 Some(Self(v))
282 } else {
283 None
284 }
285 }
286
287 pub fn is_infinite(&self) -> bool {
289 self == &Self::INFINITE_FUTURE
290 }
291}
292
293#[cfg(target_os = "fuchsia")]
294impl From<PositiveMonotonicInstant> for zx::MonotonicInstant {
295 fn from(PositiveMonotonicInstant(v): PositiveMonotonicInstant) -> Self {
296 zx::MonotonicInstant::from_nanos(v)
297 }
298}
299
300#[cfg(target_os = "fuchsia")]
301impl TryFrom<zx::MonotonicInstant> for PositiveMonotonicInstant {
302 type Error = NotPositiveMonotonicInstantError;
303
304 fn try_from(value: zx::MonotonicInstant) -> Result<Self, Self::Error> {
305 Self::try_from(value.into_nanos())
306 }
307}
308
309impl From<PositiveMonotonicInstant> for zx_types::zx_time_t {
310 fn from(value: PositiveMonotonicInstant) -> Self {
311 value.into_nanos()
312 }
313}
314
315impl TryFrom<zx_types::zx_time_t> for PositiveMonotonicInstant {
316 type Error = NotPositiveMonotonicInstantError;
317
318 fn try_from(value: zx_types::zx_time_t) -> Result<Self, Self::Error> {
319 Self::from_nanos(value).ok_or(NotPositiveMonotonicInstantError(value))
320 }
321}
322
323#[derive(Error, Debug)]
325pub enum UpdateError {
326 #[error("duplicate added event {0:?}")]
328 DuplicateAdded(fnet_interfaces::Properties),
329 #[error("duplicate existing event {0:?}")]
331 DuplicateExisting(fnet_interfaces::Properties),
332 #[error("failed to validate Properties FIDL table: {0}")]
334 InvalidProperties(#[from] PropertiesValidationError),
335 #[error("failed to validate Address FIDL table: {0}")]
337 InvalidAddress(#[from] AddressValidationError),
338 #[error("changed event with missing ID {0:?}")]
340 MissingId(fnet_interfaces::Properties),
341 #[error("changed event contains no changed fields {0:?}")]
343 EmptyChange(fnet_interfaces::Properties),
344 #[error("interface has been removed")]
346 Removed,
347 #[error("unknown interface changed {0:?}")]
349 UnknownChanged(fnet_interfaces::Properties),
350 #[error("unknown interface with id {0} deleted")]
352 UnknownRemoved(u64),
353 #[error("encountered 0 interface id")]
355 ZeroInterfaceId,
356}
357
358#[derive(Derivative)]
360#[derivative(Debug(bound = "S: Debug"), PartialEq(bound = "S: PartialEq"))]
361pub enum UpdateResult<'a, S, I: FieldInterests> {
362 NoChange,
364 Existing {
366 properties: &'a Properties<I>,
368 state: &'a mut S,
370 },
371 Added {
373 properties: &'a Properties<I>,
375 state: &'a mut S,
377 },
378 Changed {
380 previous: fnet_interfaces::Properties,
386 current: &'a Properties<I>,
388 state: &'a mut S,
390 },
391 Removed(PropertiesAndState<S, I>),
393}
394
395#[derive(Derivative)]
397#[derivative(
398 Clone(bound = "S: Clone"),
399 Debug(bound = "S: Debug"),
400 Eq(bound = "S: Eq"),
401 PartialEq(bound = "S: PartialEq")
402)]
403pub struct PropertiesAndState<S, I: FieldInterests> {
404 pub properties: Properties<I>,
406 pub state: S,
408}
409
410pub trait Update<S> {
412 type Interest: FieldInterests;
414
415 fn update(
417 &mut self,
418 event: EventWithInterest<Self::Interest>,
419 ) -> Result<UpdateResult<'_, S, Self::Interest>, UpdateError>;
420}
421
422impl<S, I: FieldInterests> Update<S> for PropertiesAndState<S, I> {
423 type Interest = I;
424 fn update(
425 &mut self,
426 event: EventWithInterest<I>,
427 ) -> Result<UpdateResult<'_, S, I>, UpdateError> {
428 let Self { properties, state } = self;
429 match event.into_inner() {
430 fnet_interfaces::Event::Existing(existing) => {
431 let existing = Properties::<I>::try_from(existing)?;
432 if existing.id == properties.id {
433 return Err(UpdateError::DuplicateExisting(existing.into()));
434 }
435 }
436 fnet_interfaces::Event::Added(added) => {
437 let added = Properties::<I>::try_from(added)?;
438 if added.id == properties.id {
439 return Err(UpdateError::DuplicateAdded(added.into()));
440 }
441 }
442 fnet_interfaces::Event::Changed(mut change) => {
443 let fnet_interfaces::Properties {
444 id,
445 name: _,
446 port_class: _,
447 online,
448 has_default_ipv4_route,
449 has_default_ipv6_route,
450 addresses,
451 ..
452 } = &mut change;
453 if let Some(id) = *id {
454 if properties.id.get() == id {
455 let mut changed = false;
456 macro_rules! swap_if_some {
457 ($field:ident) => {
458 if let Some($field) = $field {
459 if properties.$field != *$field {
460 std::mem::swap(&mut properties.$field, $field);
461 changed = true;
462 }
463 }
464 };
465 }
466 swap_if_some!(online);
467 swap_if_some!(has_default_ipv4_route);
468 swap_if_some!(has_default_ipv6_route);
469 if let Some(addresses) = addresses {
470 if addresses.len() != properties.addresses.len()
478 || !addresses
479 .iter()
480 .zip(
481 properties
482 .addresses
483 .iter()
484 .cloned()
485 .map(fnet_interfaces::Address::from),
486 )
487 .all(|(a, b)| *a == b)
488 {
489 let previous_len = properties.addresses.len();
490 let () = properties.addresses.reserve(addresses.len());
493 for address in addresses.drain(..).map(Address::try_from) {
494 let () = properties.addresses.push(address?);
495 }
496 let () = addresses.extend(
497 properties.addresses.drain(..previous_len).map(Into::into),
498 );
499 changed = true;
500 }
501 }
502 if changed {
503 change.id = None;
504 return Ok(UpdateResult::Changed {
505 previous: change,
506 current: properties,
507 state,
508 });
509 } else {
510 return Err(UpdateError::EmptyChange(change));
511 }
512 }
513 } else {
514 return Err(UpdateError::MissingId(change));
515 }
516 }
517 fnet_interfaces::Event::Removed(removed_id) => {
518 if properties.id.get() == removed_id {
519 return Err(UpdateError::Removed);
520 }
521 }
522 fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}) => {}
523 }
524 Ok(UpdateResult::NoChange)
525 }
526}
527
528impl<S: Default, I: FieldInterests> Update<S> for InterfaceState<S, I> {
529 type Interest = I;
530 fn update(
531 &mut self,
532 event: EventWithInterest<I>,
533 ) -> Result<UpdateResult<'_, S, I>, UpdateError> {
534 fn get_properties<S, I: FieldInterests>(
535 state: &mut InterfaceState<S, I>,
536 ) -> &mut PropertiesAndState<S, I> {
537 match state {
538 InterfaceState::Known(properties) => properties,
539 InterfaceState::Unknown(id) => unreachable!(
540 "matched `Unknown({})` immediately after assigning with `Known`",
541 id
542 ),
543 }
544 }
545 match self {
546 InterfaceState::Unknown(id) => match event.into_inner() {
547 fnet_interfaces::Event::Existing(existing) => {
548 let properties = Properties::try_from(existing)?;
549 if properties.id.get() == *id {
550 *self = InterfaceState::Known(PropertiesAndState {
551 properties,
552 state: S::default(),
553 });
554 let PropertiesAndState { properties, state } = get_properties(self);
555 return Ok(UpdateResult::Existing { properties, state });
556 }
557 }
558 fnet_interfaces::Event::Added(added) => {
559 let properties = Properties::try_from(added)?;
560 if properties.id.get() == *id {
561 *self = InterfaceState::Known(PropertiesAndState {
562 properties,
563 state: S::default(),
564 });
565 let PropertiesAndState { properties, state } = get_properties(self);
566 return Ok(UpdateResult::Added { properties, state });
567 }
568 }
569 fnet_interfaces::Event::Changed(change) => {
570 if let Some(change_id) = change.id {
571 if change_id == *id {
572 return Err(UpdateError::UnknownChanged(change));
573 }
574 } else {
575 return Err(UpdateError::MissingId(change));
576 }
577 }
578 fnet_interfaces::Event::Removed(removed_id) => {
579 if removed_id == *id {
580 return Err(UpdateError::UnknownRemoved(removed_id));
581 }
582 }
583 fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}) => {}
584 },
585 InterfaceState::Known(properties) => return properties.update(event),
586 }
587 Ok(UpdateResult::NoChange)
588 }
589}
590
591pub struct ZeroError {}
593
594pub trait TryFromMaybeNonZero: Sized {
596 fn try_from(value: u64) -> Result<Self, ZeroError>;
598}
599
600impl TryFromMaybeNonZero for u64 {
601 fn try_from(value: u64) -> Result<Self, ZeroError> {
602 Ok(value)
603 }
604}
605
606impl TryFromMaybeNonZero for NonZeroU64 {
607 fn try_from(value: u64) -> Result<Self, ZeroError> {
608 NonZeroU64::new(value).ok_or(ZeroError {})
609 }
610}
611
612macro_rules! impl_map {
613 ($map_type:ident, $map_mod:tt) => {
614 impl<K, S, I> Update<S> for $map_type<K, PropertiesAndState<S, I>>
615 where
616 K: TryFromMaybeNonZero + Copy + From<NonZeroU64> + Eq + Ord + std::hash::Hash,
617 S: Default,
618 I: FieldInterests,
619 {
620 type Interest = I;
621
622 fn update(
623 &mut self,
624 event: EventWithInterest<I>,
625 ) -> Result<UpdateResult<'_, S, I>, UpdateError> {
626 match event.into_inner() {
627 fnet_interfaces::Event::Existing(existing) => {
628 let existing = Properties::try_from(existing)?;
629 match self.entry(existing.id.into()) {
630 $map_mod::Entry::Occupied(_) => {
631 Err(UpdateError::DuplicateExisting(existing.into()))
632 }
633 $map_mod::Entry::Vacant(entry) => {
634 let PropertiesAndState { properties, state } =
635 entry.insert(PropertiesAndState {
636 properties: existing,
637 state: S::default(),
638 });
639 Ok(UpdateResult::Existing { properties, state })
640 }
641 }
642 }
643 fnet_interfaces::Event::Added(added) => {
644 let added = Properties::try_from(added)?;
645 match self.entry(added.id.into()) {
646 $map_mod::Entry::Occupied(_) => {
647 Err(UpdateError::DuplicateAdded(added.into()))
648 }
649 $map_mod::Entry::Vacant(entry) => {
650 let PropertiesAndState { properties, state } =
651 entry.insert(PropertiesAndState {
652 properties: added,
653 state: S::default(),
654 });
655 Ok(UpdateResult::Added { properties, state })
656 }
657 }
658 }
659 fnet_interfaces::Event::Changed(change) => {
660 let id = if let Some(id) = change.id {
661 id
662 } else {
663 return Err(UpdateError::MissingId(change));
664 };
665 if let Some(properties) = self.get_mut(
666 &K::try_from(id)
667 .map_err(|ZeroError {}| UpdateError::ZeroInterfaceId)?,
668 ) {
669 properties.update(EventWithInterest::new(
670 fnet_interfaces::Event::Changed(change),
671 ))
672 } else {
673 Err(UpdateError::UnknownChanged(change))
674 }
675 }
676 fnet_interfaces::Event::Removed(removed_id) => {
677 if let Some(properties) = self.remove(
678 &K::try_from(removed_id)
679 .map_err(|ZeroError {}| UpdateError::ZeroInterfaceId)?,
680 ) {
681 Ok(UpdateResult::Removed(properties))
682 } else {
683 Err(UpdateError::UnknownRemoved(removed_id))
684 }
685 }
686 fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}) => {
687 Ok(UpdateResult::NoChange)
688 }
689 }
690 }
691 }
692 };
693}
694
695impl_map!(BTreeMap, btree_map);
696impl_map!(HashMap, hash_map);
697
698#[derive(Error, Debug)]
700pub enum WatcherOperationError<S: Debug, B: Update<S> + Debug> {
701 #[error("event stream error: {0}")]
703 EventStream(fidl::Error),
704 #[error("failed to update: {0}")]
706 Update(UpdateError),
707 #[error("watcher event stream ended unexpectedly, final state: {final_state:?}")]
709 UnexpectedEnd {
710 final_state: B,
712 marker: std::marker::PhantomData<S>,
714 },
715 #[error("unexpected event type: {0:?}")]
717 UnexpectedEvent(fnet_interfaces::Event),
718}
719
720#[derive(Error, Debug)]
722pub enum WatcherCreationError {
723 #[error("failed to create interface watcher proxy: {0}")]
725 CreateProxy(fidl::Error),
726 #[error("failed to get interface watcher: {0}")]
728 GetWatcher(fidl::Error),
729}
730
731pub async fn wait_interface<S, B, St, F, T>(
740 stream: St,
741 init: &mut B,
742 mut predicate: F,
743) -> Result<T, WatcherOperationError<S, B>>
744where
745 S: Debug + Default,
746 B: Update<S> + Clone + Debug,
747 St: Stream<Item = Result<EventWithInterest<B::Interest>, fidl::Error>>,
748 F: FnMut(&B) -> Option<T>,
749{
750 async_utils::fold::try_fold_while(
751 stream.map_err(WatcherOperationError::EventStream),
752 init,
753 |acc, event| {
754 futures::future::ready(match acc.update(event) {
755 Ok(changed) => match changed {
756 UpdateResult::Existing { .. }
757 | UpdateResult::Added { .. }
758 | UpdateResult::Changed { .. }
759 | UpdateResult::Removed(_) => {
760 if let Some(rtn) = predicate(acc) {
761 Ok(async_utils::fold::FoldWhile::Done(rtn))
762 } else {
763 Ok(async_utils::fold::FoldWhile::Continue(acc))
764 }
765 }
766 UpdateResult::NoChange => Ok(async_utils::fold::FoldWhile::Continue(acc)),
767 },
768 Err(e) => Err(WatcherOperationError::Update(e)),
769 })
770 },
771 )
772 .await?
773 .short_circuited()
774 .map_err(|final_state| WatcherOperationError::UnexpectedEnd {
775 final_state: final_state.clone(),
776 marker: Default::default(),
777 })
778}
779
780#[derive(Derivative)]
782#[derivative(
783 Clone(bound = "S: Clone"),
784 Debug(bound = "S: Debug"),
785 PartialEq(bound = "S: PartialEq")
786)]
787pub enum InterfaceState<S, I: FieldInterests> {
788 Unknown(u64),
790 Known(PropertiesAndState<S, I>),
792}
793
794pub async fn wait_interface_with_id<S, St, F, T, I>(
806 stream: St,
807 init: &mut InterfaceState<S, I>,
808 mut predicate: F,
809) -> Result<T, WatcherOperationError<S, InterfaceState<S, I>>>
810where
811 S: Default + Clone + Debug,
812 St: Stream<Item = Result<EventWithInterest<I>, fidl::Error>>,
813 F: FnMut(&PropertiesAndState<S, I>) -> Option<T>,
814 I: FieldInterests,
815{
816 wait_interface(stream, init, |state| {
817 match state {
818 InterfaceState::Known(properties) => predicate(properties),
819 InterfaceState::Unknown(_) => None,
822 }
823 })
824 .await
825}
826
827pub async fn existing<S, St, B>(stream: St, init: B) -> Result<B, WatcherOperationError<S, B>>
833where
834 S: Debug,
835 St: futures::Stream<Item = Result<EventWithInterest<B::Interest>, fidl::Error>>,
836 B: Update<S> + Debug,
837{
838 async_utils::fold::try_fold_while(
839 stream.map_err(WatcherOperationError::EventStream),
840 init,
841 |mut acc, event| {
842 futures::future::ready(match event.inner() {
843 fnet_interfaces::Event::Existing(_) => match acc.update(event) {
844 Ok::<UpdateResult<'_, _, _>, _>(_) => {
845 Ok(async_utils::fold::FoldWhile::Continue(acc))
846 }
847 Err(e) => Err(WatcherOperationError::Update(e)),
848 },
849 fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}) => {
850 Ok(async_utils::fold::FoldWhile::Done(acc))
851 }
852 fnet_interfaces::Event::Added(_)
853 | fnet_interfaces::Event::Removed(_)
854 | fnet_interfaces::Event::Changed(_) => {
855 Err(WatcherOperationError::UnexpectedEvent(event.into_inner()))
856 }
857 })
858 },
859 )
860 .await?
861 .short_circuited()
862 .map_err(|acc| WatcherOperationError::UnexpectedEnd {
863 final_state: acc,
864 marker: Default::default(),
865 })
866}
867
868pub enum IncludedAddresses {
870 All,
872 OnlyAssigned,
874}
875
876pub fn event_stream_from_state<I: FieldInterests>(
882 interface_state: &fnet_interfaces::StateProxy,
883 included_addresses: IncludedAddresses,
884) -> Result<impl Stream<Item = Result<EventWithInterest<I>, fidl::Error>>, WatcherCreationError> {
885 let (watcher, server) = ::fidl::endpoints::create_proxy::<fnet_interfaces::WatcherMarker>();
886 let () = interface_state
887 .get_watcher(
888 &fnet_interfaces::WatcherOptions {
889 address_properties_interest: Some(interest_from_params::<I>()),
890 include_non_assigned_addresses: Some(match included_addresses {
891 IncludedAddresses::All => true,
892 IncludedAddresses::OnlyAssigned => false,
893 }),
894 ..Default::default()
895 },
896 server,
897 )
898 .map_err(WatcherCreationError::GetWatcher)?;
899 Ok(futures::stream::try_unfold(watcher, |watcher| async {
900 Ok(Some((EventWithInterest::new(watcher.watch().await?), watcher)))
901 }))
902}
903
904fn interest_from_params<I: FieldInterests>() -> fnet_interfaces::AddressPropertiesInterest {
905 let mut interest = fnet_interfaces::AddressPropertiesInterest::empty();
906 if <I::ValidUntil as MaybeInterest<_>>::ENABLED {
907 interest |= fnet_interfaces::AddressPropertiesInterest::VALID_UNTIL;
908 }
909 if <I::PreferredLifetimeInfo as MaybeInterest<_>>::ENABLED {
910 interest |= fnet_interfaces::AddressPropertiesInterest::PREFERRED_LIFETIME_INFO;
911 }
912 interest
913}
914
915#[derive(Clone, Debug, Eq, Hash, PartialEq, Default)]
917pub struct NoInterest;
918
919mod interest {
920 use super::*;
921
922 use std::hash::Hash;
923 use Debug;
924
925 pub trait FieldInterests {
931 type PreferredLifetimeInfo: MaybeInterest<PreferredLifetimeInfo>;
933 type ValidUntil: MaybeInterest<PositiveMonotonicInstant>;
935 }
936
937 pub trait MaybeInterest<T> {
939 const ENABLED: bool;
941
942 type Ty: Clone + Debug + Eq + Hash + PartialEq + 'static;
944
945 fn try_from_fidl<F: TryInto<T, Error: Into<anyhow::Error>>>(
947 fidl: Option<F>,
948 ) -> Result<Self::Ty, anyhow::Error>;
949
950 fn into_fidl<F: From<T>>(value: Self::Ty) -> Option<F>;
953 }
954
955 pub struct EnableInterest;
959
960 impl<T: Clone + Debug + Eq + Hash + PartialEq + 'static> MaybeInterest<T> for EnableInterest {
961 const ENABLED: bool = true;
962 type Ty = T;
963
964 fn try_from_fidl<F: TryInto<T, Error: Into<anyhow::Error>>>(
965 fidl: Option<F>,
966 ) -> Result<Self::Ty, anyhow::Error> {
967 fidl.map(|f| f.try_into().map_err(Into::into))
968 .unwrap_or_else(|| Err(anyhow::anyhow!("missing field with registered interest")))
969 }
970
971 fn into_fidl<F: From<T>>(value: Self::Ty) -> Option<F> {
972 Some(value.into())
973 }
974 }
975
976 pub struct DisableInterest;
980 impl<T> MaybeInterest<T> for DisableInterest {
981 const ENABLED: bool = false;
982
983 type Ty = NoInterest;
984
985 fn try_from_fidl<F: TryInto<T, Error: Into<anyhow::Error>>>(
986 fidl: Option<F>,
987 ) -> Result<Self::Ty, anyhow::Error> {
988 match fidl {
989 Some(_) => Err(anyhow::anyhow!("unexpected set field with no registered interest")),
990 None => Ok(NoInterest),
991 }
992 }
993
994 fn into_fidl<F: From<T>>(_value: Self::Ty) -> Option<F> {
995 None
996 }
997 }
998
999 pub(super) type FromInterest<I, T> =
1002 <<T as FieldSpec>::Interest<I> as MaybeInterest<<T as FieldSpec>::Present>>::Ty;
1003
1004 pub trait FieldSpec {
1010 type Interest<I: FieldInterests>: MaybeInterest<Self::Present>;
1012
1013 type Fidl: From<Self::Present>;
1015
1016 type Present: TryFrom<Self::Fidl, Error: Into<anyhow::Error>>;
1019
1020 const FIELD_NAME: &'static str;
1023 }
1024
1025 pub struct InterestConverter<I, P>(PhantomData<(I, P)>);
1026
1027 impl<I, P> fidl_table_validation::Converter for InterestConverter<I, P>
1028 where
1029 I: FieldInterests,
1030 P: FieldSpec,
1031 {
1032 type Fidl = Option<P::Fidl>;
1033 type Validated = <P::Interest<I> as MaybeInterest<P::Present>>::Ty;
1034 type Error = anyhow::Error;
1035
1036 fn try_from_fidl(value: Self::Fidl) -> std::result::Result<Self::Validated, Self::Error> {
1037 <P::Interest<I> as MaybeInterest<_>>::try_from_fidl(value).context(P::FIELD_NAME)
1038 }
1039
1040 fn from_validated(validated: Self::Validated) -> Self::Fidl {
1041 <P::Interest<I> as MaybeInterest<_>>::into_fidl(validated)
1042 }
1043 }
1044
1045 pub struct ValidUntilInterest;
1046
1047 impl FieldSpec for ValidUntilInterest {
1048 type Interest<I: FieldInterests> = I::ValidUntil;
1049 type Fidl = zx_types::zx_time_t;
1050 type Present = PositiveMonotonicInstant;
1051 const FIELD_NAME: &'static str = "valid_until";
1052 }
1053
1054 pub struct PreferredLifetimeInfoInterest;
1055
1056 impl FieldSpec for PreferredLifetimeInfoInterest {
1057 type Interest<I: FieldInterests> = I::PreferredLifetimeInfo;
1058 type Fidl = fnet_interfaces::PreferredLifetimeInfo;
1059 type Present = PreferredLifetimeInfo;
1060 const FIELD_NAME: &'static str = "preferred_lifetime_info";
1061 }
1062}
1063pub use interest::{DisableInterest, EnableInterest, FieldInterests};
1064use interest::{
1065 FromInterest, InterestConverter, MaybeInterest, PreferredLifetimeInfoInterest,
1066 ValidUntilInterest,
1067};
1068
1069#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1071pub struct AllInterest;
1072impl FieldInterests for AllInterest {
1073 type PreferredLifetimeInfo = EnableInterest;
1074 type ValidUntil = EnableInterest;
1075}
1076
1077#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1080pub struct DefaultInterest;
1081impl FieldInterests for DefaultInterest {
1082 type PreferredLifetimeInfo = DisableInterest;
1083 type ValidUntil = DisableInterest;
1084}
1085
1086#[derive(Derivative)]
1089#[derivative(Clone(bound = ""), Debug(bound = ""), Eq(bound = ""), PartialEq(bound = ""))]
1090pub struct EventWithInterest<I: FieldInterests> {
1091 event: fnet_interfaces::Event,
1092 #[derivative(Debug = "ignore")]
1093 _marker: PhantomData<I>,
1094}
1095
1096impl<I: FieldInterests> EventWithInterest<I> {
1097 pub fn new(event: fnet_interfaces::Event) -> Self {
1104 Self { event, _marker: PhantomData }
1105 }
1106
1107 pub fn into_inner(self) -> fnet_interfaces::Event {
1109 self.event
1110 }
1111
1112 pub fn inner(&self) -> &fnet_interfaces::Event {
1114 &self.event
1115 }
1116}
1117
1118impl<I: FieldInterests> From<fnet_interfaces::Event> for EventWithInterest<I> {
1119 fn from(value: fnet_interfaces::Event) -> Self {
1120 Self::new(value)
1121 }
1122}
1123
1124impl<I: FieldInterests> From<EventWithInterest<I>> for fnet_interfaces::Event {
1125 fn from(value: EventWithInterest<I>) -> Self {
1126 value.into_inner()
1127 }
1128}
1129
1130#[cfg(test)]
1131mod tests {
1132 use super::*;
1133 use assert_matches::assert_matches;
1134 use fidl_fuchsia_net as fnet;
1135 use futures::task::Poll;
1136 use futures::FutureExt as _;
1137 use net_declare::fidl_subnet;
1138 use std::cell::RefCell;
1139 use std::convert::TryInto as _;
1140 use std::pin::Pin;
1141 use std::rc::Rc;
1142 use test_case::test_case;
1143
1144 fn fidl_properties(id: u64) -> fnet_interfaces::Properties {
1145 fnet_interfaces::Properties {
1146 id: Some(id),
1147 name: Some("test1".to_string()),
1148 port_class: Some(fnet_interfaces::PortClass::Loopback(fnet_interfaces::Empty {})),
1149 online: Some(false),
1150 has_default_ipv4_route: Some(false),
1151 has_default_ipv6_route: Some(false),
1152 addresses: Some(vec![fidl_address(ADDR, zx_types::ZX_TIME_INFINITE)]),
1153 ..Default::default()
1154 }
1155 }
1156
1157 fn validated_properties(id: u64) -> PropertiesAndState<(), AllInterest> {
1158 PropertiesAndState {
1159 properties: fidl_properties(id).try_into().expect("failed to validate FIDL Properties"),
1160 state: (),
1161 }
1162 }
1163
1164 fn properties_delta(id: u64) -> fnet_interfaces::Properties {
1165 fnet_interfaces::Properties {
1166 id: Some(id),
1167 name: None,
1168 port_class: None,
1169 online: Some(true),
1170 has_default_ipv4_route: Some(true),
1171 has_default_ipv6_route: Some(true),
1172 addresses: Some(vec![fidl_address(ADDR2, zx_types::ZX_TIME_INFINITE)]),
1173 ..Default::default()
1174 }
1175 }
1176
1177 fn fidl_properties_after_change(id: u64) -> fnet_interfaces::Properties {
1178 fnet_interfaces::Properties {
1179 id: Some(id),
1180 name: Some("test1".to_string()),
1181 port_class: Some(fnet_interfaces::PortClass::Loopback(fnet_interfaces::Empty {})),
1182 online: Some(true),
1183 has_default_ipv4_route: Some(true),
1184 has_default_ipv6_route: Some(true),
1185 addresses: Some(vec![fidl_address(ADDR2, zx_types::ZX_TIME_INFINITE)]),
1186 ..Default::default()
1187 }
1188 }
1189
1190 fn validated_properties_after_change(id: u64) -> PropertiesAndState<(), AllInterest> {
1191 PropertiesAndState {
1192 properties: fidl_properties_after_change(id)
1193 .try_into()
1194 .expect("failed to validate FIDL Properties"),
1195 state: (),
1196 }
1197 }
1198
1199 fn fidl_address(
1200 addr: fnet::Subnet,
1201 valid_until: zx_types::zx_time_t,
1202 ) -> fnet_interfaces::Address {
1203 fnet_interfaces::Address {
1204 addr: Some(addr),
1205 valid_until: Some(valid_until.try_into().unwrap()),
1206 assignment_state: Some(fnet_interfaces::AddressAssignmentState::Assigned),
1207 preferred_lifetime_info: Some(PreferredLifetimeInfo::preferred_forever().into()),
1208 __source_breaking: Default::default(),
1209 }
1210 }
1211
1212 const ID: u64 = 1;
1213 const ID2: u64 = 2;
1214 const ADDR: fnet::Subnet = fidl_subnet!("1.2.3.4/24");
1215 const ADDR2: fnet::Subnet = fidl_subnet!("5.6.7.8/24");
1216
1217 #[test_case(
1218 &mut std::iter::once((ID, validated_properties(ID))).collect::<HashMap<_, _>>();
1219 "hashmap"
1220 )]
1221 #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1222 #[test_case(&mut validated_properties(ID); "properties")]
1223 fn test_duplicate_error(state: &mut impl Update<(), Interest = AllInterest>) {
1224 assert_matches::assert_matches!(
1225 state.update(fnet_interfaces::Event::Added(fidl_properties(ID)).into()),
1226 Err(UpdateError::DuplicateAdded(added)) if added == fidl_properties(ID)
1227 );
1228 assert_matches::assert_matches!(
1229 state.update(fnet_interfaces::Event::Existing(fidl_properties(ID)).into()),
1230 Err(UpdateError::DuplicateExisting(existing)) if existing == fidl_properties(ID)
1231 );
1232 }
1233
1234 #[test_case(&mut HashMap::<u64, _>::new(); "hashmap")]
1235 #[test_case(&mut InterfaceState::Unknown(ID); "interface_state_unknown")]
1236 fn test_unknown_error(state: &mut impl Update<(), Interest = AllInterest>) {
1237 let unknown =
1238 fnet_interfaces::Properties { id: Some(ID), online: Some(true), ..Default::default() };
1239 assert_matches::assert_matches!(
1240 state.update(fnet_interfaces::Event::Changed(unknown.clone()).into()),
1241 Err(UpdateError::UnknownChanged(changed)) if changed == unknown
1242 );
1243 assert_matches::assert_matches!(
1244 state.update(fnet_interfaces::Event::Removed(ID).into()),
1245 Err(UpdateError::UnknownRemoved(id)) if id == ID
1246 );
1247 }
1248
1249 #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1250 #[test_case(&mut validated_properties(ID); "properties")]
1251 fn test_removed_error(state: &mut impl Update<()>) {
1252 assert_matches::assert_matches!(
1253 state.update(fnet_interfaces::Event::Removed(ID).into()),
1254 Err(UpdateError::Removed)
1255 );
1256 }
1257
1258 #[test_case(&mut HashMap::<u64, _>::new(); "hashmap")]
1259 #[test_case(&mut InterfaceState::Unknown(ID); "interface_state_unknown")]
1260 #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1261 #[test_case(&mut validated_properties(ID); "properties")]
1262 fn test_missing_id_error(state: &mut impl Update<(), Interest = AllInterest>) {
1263 let missing_id = fnet_interfaces::Properties { online: Some(true), ..Default::default() };
1264 assert_matches::assert_matches!(
1265 state.update(fnet_interfaces::Event::Changed(missing_id.clone()).into()),
1266 Err(UpdateError::MissingId(properties)) if properties == missing_id
1267 );
1268 }
1269
1270 #[test_case(
1271 &mut std::iter::once((ID, validated_properties(ID))).collect::<HashMap<_, _>>();
1272 "hashmap"
1273 )]
1274 #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1275 #[test_case(&mut validated_properties(ID); "properties")]
1276 fn test_empty_change_error(state: &mut impl Update<()>) {
1277 let empty_change = fnet_interfaces::Properties { id: Some(ID), ..Default::default() };
1278 let net_zero_change =
1279 fnet_interfaces::Properties { name: None, port_class: None, ..fidl_properties(ID) };
1280 assert_matches::assert_matches!(
1281 state.update(fnet_interfaces::Event::Changed(empty_change.clone()).into()),
1282 Err(UpdateError::EmptyChange(properties)) if properties == empty_change
1283 );
1284 assert_matches::assert_matches!(
1285 state.update(fnet_interfaces::Event::Changed(net_zero_change.clone()).into()),
1286 Err(UpdateError::EmptyChange(properties)) if properties == net_zero_change
1287 );
1288 }
1289
1290 #[test_case(
1291 &mut std::iter::once((ID, validated_properties(ID))).collect::<HashMap<_, _>>();
1292 "hashmap"
1293 )]
1294 #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1295 #[test_case(&mut validated_properties(ID); "properties")]
1296 fn test_update_changed_result(state: &mut impl Update<(), Interest = AllInterest>) {
1297 let want_previous = fnet_interfaces::Properties {
1298 online: Some(false),
1299 has_default_ipv4_route: Some(false),
1300 has_default_ipv6_route: Some(false),
1301 addresses: Some(vec![fidl_address(ADDR, zx_types::ZX_TIME_INFINITE)]),
1302 ..Default::default()
1303 };
1304 assert_matches::assert_matches!(
1305 state.update(fnet_interfaces::Event::Changed(properties_delta(ID).clone()).into()),
1306 Ok(UpdateResult::Changed { previous, current, state: _ }) => {
1307 assert_eq!(previous, want_previous);
1308 let PropertiesAndState { properties, state: () } =
1309 validated_properties_after_change(ID);
1310 assert_eq!(*current, properties);
1311 }
1312 );
1313 }
1314
1315 #[derive(Derivative)]
1316 #[derivative(Clone(bound = ""))]
1317 struct EventStream<I: FieldInterests>(Rc<RefCell<Vec<fnet_interfaces::Event>>>, PhantomData<I>);
1318
1319 impl<I: FieldInterests> Stream for EventStream<I> {
1320 type Item = Result<EventWithInterest<I>, fidl::Error>;
1321
1322 fn poll_next(
1323 self: Pin<&mut Self>,
1324 _cx: &mut futures::task::Context<'_>,
1325 ) -> Poll<Option<Self::Item>> {
1326 let EventStream(events_vec, _marker) = &*self;
1327 if events_vec.borrow().is_empty() {
1328 Poll::Ready(None)
1329 } else {
1330 Poll::Ready(Some(Ok(EventWithInterest::new(events_vec.borrow_mut().remove(0)))))
1331 }
1332 }
1333 }
1334
1335 fn test_event_stream<I: FieldInterests>() -> EventStream<I> {
1336 EventStream(
1337 Rc::new(RefCell::new(vec![
1338 fnet_interfaces::Event::Existing(fidl_properties(ID)),
1339 fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}),
1340 fnet_interfaces::Event::Added(fidl_properties(ID2)),
1341 fnet_interfaces::Event::Changed(properties_delta(ID)),
1342 fnet_interfaces::Event::Changed(properties_delta(ID2)),
1343 fnet_interfaces::Event::Removed(ID),
1344 fnet_interfaces::Event::Removed(ID2),
1345 ])),
1346 PhantomData,
1347 )
1348 }
1349
1350 #[test]
1351 fn test_wait_one_interface() {
1352 let event_stream = test_event_stream::<AllInterest>();
1353 let mut state = InterfaceState::Unknown(ID);
1354 for want in &[validated_properties(ID), validated_properties_after_change(ID)] {
1355 let () = wait_interface_with_id(event_stream.clone(), &mut state, |got| {
1356 assert_eq!(got, want);
1357 Some(())
1358 })
1359 .now_or_never()
1360 .expect("wait_interface_with_id did not complete immediately")
1361 .expect("wait_interface_with_id error");
1362 assert_matches!(state, InterfaceState::Known(ref got) if got == want);
1363 }
1364 }
1365
1366 fn test_wait_interface<'a, B>(state: &mut B, want_states: impl IntoIterator<Item = &'a B>)
1367 where
1368 B: 'a + Update<()> + Clone + Debug + std::cmp::PartialEq,
1369 {
1370 let event_stream = test_event_stream::<B::Interest>();
1371 for want in want_states.into_iter() {
1372 let () = wait_interface(event_stream.clone(), state, |got| {
1373 assert_eq!(got, want);
1374 Some(())
1375 })
1376 .now_or_never()
1377 .expect("wait_interface did not complete immediately")
1378 .expect("wait_interface error");
1379 assert_eq!(state, want);
1380 }
1381 }
1382
1383 #[test]
1384 fn test_wait_interface_hashmap() {
1385 test_wait_interface(
1386 &mut HashMap::new(),
1387 &[
1388 std::iter::once((ID, validated_properties(ID))).collect::<HashMap<_, _>>(),
1389 [(ID, validated_properties(ID)), (ID2, validated_properties(ID2))]
1390 .iter()
1391 .cloned()
1392 .collect::<HashMap<_, _>>(),
1393 [(ID, validated_properties_after_change(ID)), (ID2, validated_properties(ID2))]
1394 .iter()
1395 .cloned()
1396 .collect::<HashMap<_, _>>(),
1397 [
1398 (ID, validated_properties_after_change(ID)),
1399 (ID2, validated_properties_after_change(ID2)),
1400 ]
1401 .iter()
1402 .cloned()
1403 .collect::<HashMap<_, _>>(),
1404 std::iter::once((ID2, validated_properties_after_change(ID2)))
1405 .collect::<HashMap<_, _>>(),
1406 HashMap::new(),
1407 ],
1408 );
1409 }
1410
1411 #[test]
1412 fn test_wait_interface_interface_state() {
1413 test_wait_interface(
1414 &mut InterfaceState::Unknown(ID),
1415 &[
1416 InterfaceState::Known(validated_properties(ID)),
1417 InterfaceState::Known(validated_properties_after_change(ID)),
1418 ],
1419 );
1420 }
1421
1422 const ID_NON_EXISTENT: u64 = 0xffffffff;
1423 #[test_case(
1424 InterfaceState::Unknown(ID_NON_EXISTENT),
1425 InterfaceState::Unknown(ID_NON_EXISTENT);
1426 "interface_state_unknown_different_id"
1427 )]
1428 #[test_case(
1429 InterfaceState::Unknown(ID),
1430 InterfaceState::Known(validated_properties(ID));
1431 "interface_state_unknown")]
1432 #[test_case(
1433 HashMap::new(),
1434 [(ID, validated_properties(ID)), (ID2, validated_properties(ID2))]
1435 .iter()
1436 .cloned()
1437 .collect::<HashMap<_, _>>();
1438 "hashmap"
1439 )]
1440 fn test_existing<B>(state: B, want: B)
1441 where
1442 B: Update<(), Interest = AllInterest> + Debug + std::cmp::PartialEq,
1443 {
1444 let events = [
1445 fnet_interfaces::Event::Existing(fidl_properties(ID)),
1446 fnet_interfaces::Event::Existing(fidl_properties(ID2)),
1447 fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}),
1448 ];
1449 let event_stream =
1450 futures::stream::iter(events.iter().cloned().map(|e| Ok(EventWithInterest::new(e))));
1451 assert_eq!(
1452 existing(event_stream, state)
1453 .now_or_never()
1454 .expect("existing did not complete immediately")
1455 .expect("existing returned error"),
1456 want,
1457 );
1458 }
1459
1460 #[test]
1461 fn positive_instant() {
1462 assert_eq!(PositiveMonotonicInstant::from_nanos(-1), None);
1463 assert_eq!(PositiveMonotonicInstant::from_nanos(0), None);
1464 assert_eq!(PositiveMonotonicInstant::from_nanos(1), Some(PositiveMonotonicInstant(1)));
1465 }
1466}