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::{Infallible as Never, 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, Hash)]
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, Eq)]
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, Eq)]
78#[error("unknown fuchsia.net.interfaces/PortClass ordinal: {unknown_ordinal}")]
79pub struct UnknownNetInterfacesPortClassError {
80 unknown_ordinal: u64,
81}
82
83#[derive(Debug, Error, PartialEq, Eq)]
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#[cfg(not(target_os = "fuchsia"))]
162#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
163pub struct PortIdentityKoid(zx_types::zx_koid_t);
164
165#[cfg(not(target_os = "fuchsia"))]
166impl PortIdentityKoid {
167 pub fn from_raw(koid: zx_types::zx_koid_t) -> Self {
169 Self(koid)
170 }
171
172 pub fn raw_koid(&self) -> zx_types::zx_koid_t {
174 let Self(raw) = self;
175 *raw
176 }
177}
178
179#[cfg(target_os = "fuchsia")]
181pub type PortIdentityKoid = zx::Koid;
182
183#[derive(Derivative, ValidFidlTable)]
185#[derivative(Clone(bound = ""), Debug(bound = ""), Eq(bound = ""), PartialEq(bound = ""))]
186#[fidl_table_src(fnet_interfaces::Properties)]
187pub struct Properties<I: FieldInterests> {
188 pub id: NonZeroU64,
191 pub name: String,
193 pub online: bool,
195 pub addresses: Vec<Address<I>>,
197 pub has_default_ipv4_route: bool,
199 pub has_default_ipv6_route: bool,
201 pub port_class: PortClass,
203 #[fidl_field_type(optional_converter = PortIdentityKoidConverter)]
212 pub port_identity_koid: Option<PortIdentityKoid>,
213}
214
215#[derive(Derivative, ValidFidlTable)]
217#[derivative(
218 Clone(bound = ""),
219 Debug(bound = ""),
220 Eq(bound = ""),
221 PartialEq(bound = ""),
222 Hash(bound = "")
223)]
224#[fidl_table_src(fnet_interfaces::Address)]
225#[fidl_table_strict]
226pub struct Address<I: FieldInterests> {
227 pub addr: fidl_fuchsia_net::Subnet,
229 #[fidl_field_type(optional_converter = InterestConverter::<I, ValidUntilInterest>)]
234 pub valid_until: FromInterest<I, ValidUntilInterest>,
235 #[fidl_field_type(optional_converter = InterestConverter::<I, PreferredLifetimeInfoInterest>)]
237 pub preferred_lifetime_info: FromInterest<I, PreferredLifetimeInfoInterest>,
238 pub assignment_state: fnet_interfaces::AddressAssignmentState,
240}
241
242#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Copy, Clone, Hash)]
247#[allow(missing_docs)]
248pub enum PreferredLifetimeInfo {
249 PreferredUntil(PositiveMonotonicInstant),
250 Deprecated,
251}
252
253impl PreferredLifetimeInfo {
254 pub const fn preferred_forever() -> Self {
256 Self::PreferredUntil(PositiveMonotonicInstant::INFINITE_FUTURE)
257 }
258
259 pub const fn to_fidl(self) -> fnet_interfaces::PreferredLifetimeInfo {
261 match self {
262 PreferredLifetimeInfo::Deprecated => {
263 fnet_interfaces::PreferredLifetimeInfo::Deprecated(fnet_interfaces::Empty)
264 }
265 PreferredLifetimeInfo::PreferredUntil(v) => {
266 fnet_interfaces::PreferredLifetimeInfo::PreferredUntil(v.into_nanos())
267 }
268 }
269 }
270}
271
272impl TryFrom<fnet_interfaces::PreferredLifetimeInfo> for PreferredLifetimeInfo {
273 type Error = NotPositiveMonotonicInstantError;
274
275 fn try_from(value: fnet_interfaces::PreferredLifetimeInfo) -> Result<Self, Self::Error> {
276 match value {
277 fnet_interfaces::PreferredLifetimeInfo::Deprecated(fnet_interfaces::Empty) => {
278 Ok(Self::Deprecated)
279 }
280 fnet_interfaces::PreferredLifetimeInfo::PreferredUntil(v) => {
281 Ok(Self::PreferredUntil(v.try_into()?))
282 }
283 }
284 }
285}
286
287impl From<PreferredLifetimeInfo> for fnet_interfaces::PreferredLifetimeInfo {
288 fn from(value: PreferredLifetimeInfo) -> Self {
289 value.to_fidl()
290 }
291}
292
293#[derive(Error, Debug)]
296#[error("{0} is not a positive monotonic instant")]
297pub struct NotPositiveMonotonicInstantError(i64);
298
299#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Copy, Clone, Hash)]
301pub struct PositiveMonotonicInstant(i64);
302
303impl PositiveMonotonicInstant {
304 pub const INFINITE_FUTURE: Self = Self(zx_types::ZX_TIME_INFINITE);
306
307 pub const fn into_nanos(self) -> i64 {
309 let Self(v) = self;
310 v
311 }
312
313 pub const fn from_nanos(v: i64) -> Option<Self> {
316 if v > 0 { Some(Self(v)) } else { None }
317 }
318
319 pub fn is_infinite(&self) -> bool {
321 self == &Self::INFINITE_FUTURE
322 }
323}
324
325#[cfg(target_os = "fuchsia")]
326impl From<PositiveMonotonicInstant> for zx::MonotonicInstant {
327 fn from(PositiveMonotonicInstant(v): PositiveMonotonicInstant) -> Self {
328 zx::MonotonicInstant::from_nanos(v)
329 }
330}
331
332#[cfg(target_os = "fuchsia")]
333impl TryFrom<zx::MonotonicInstant> for PositiveMonotonicInstant {
334 type Error = NotPositiveMonotonicInstantError;
335
336 fn try_from(value: zx::MonotonicInstant) -> Result<Self, Self::Error> {
337 Self::try_from(value.into_nanos())
338 }
339}
340
341impl From<PositiveMonotonicInstant> for zx_types::zx_time_t {
342 fn from(value: PositiveMonotonicInstant) -> Self {
343 value.into_nanos()
344 }
345}
346
347impl TryFrom<zx_types::zx_time_t> for PositiveMonotonicInstant {
348 type Error = NotPositiveMonotonicInstantError;
349
350 fn try_from(value: zx_types::zx_time_t) -> Result<Self, Self::Error> {
351 Self::from_nanos(value).ok_or(NotPositiveMonotonicInstantError(value))
352 }
353}
354
355#[derive(Error, Debug)]
357pub enum UpdateError {
358 #[error("duplicate added event {0:?}")]
360 DuplicateAdded(fnet_interfaces::Properties),
361 #[error("duplicate existing event {0:?}")]
363 DuplicateExisting(fnet_interfaces::Properties),
364 #[error("failed to validate Properties FIDL table: {0}")]
366 InvalidProperties(#[from] PropertiesValidationError),
367 #[error("failed to validate Address FIDL table: {0}")]
369 InvalidAddress(#[from] AddressValidationError),
370 #[error("changed event with missing ID {0:?}")]
372 MissingId(fnet_interfaces::Properties),
373 #[error("changed event contains no changed fields {0:?}")]
375 EmptyChange(fnet_interfaces::Properties),
376 #[error("interface has been removed")]
378 Removed,
379 #[error("unknown interface changed {0:?}")]
381 UnknownChanged(fnet_interfaces::Properties),
382 #[error("unknown interface with id {0} deleted")]
384 UnknownRemoved(u64),
385 #[error("encountered 0 interface id")]
387 ZeroInterfaceId,
388}
389
390#[derive(Derivative)]
392#[derivative(Debug(bound = "S: Debug"), PartialEq(bound = "S: PartialEq"))]
393pub enum UpdateResult<'a, S, I: FieldInterests> {
394 NoChange,
396 Existing {
398 properties: &'a Properties<I>,
400 state: &'a mut S,
402 },
403 Added {
405 properties: &'a Properties<I>,
407 state: &'a mut S,
409 },
410 Changed {
412 previous: fnet_interfaces::Properties,
418 current: &'a Properties<I>,
420 state: &'a mut S,
422 },
423 Removed(PropertiesAndState<S, I>),
425}
426
427#[derive(Derivative)]
429#[derivative(
430 Clone(bound = "S: Clone"),
431 Debug(bound = "S: Debug"),
432 Eq(bound = "S: Eq"),
433 PartialEq(bound = "S: PartialEq")
434)]
435pub struct PropertiesAndState<S, I: FieldInterests> {
436 pub properties: Properties<I>,
438 pub state: S,
440}
441
442pub trait Update<S> {
444 type Interest: FieldInterests;
446
447 fn update(
449 &mut self,
450 event: EventWithInterest<Self::Interest>,
451 ) -> Result<UpdateResult<'_, S, Self::Interest>, UpdateError>;
452}
453
454impl<S, I: FieldInterests> Update<S> for PropertiesAndState<S, I> {
455 type Interest = I;
456 fn update(
457 &mut self,
458 event: EventWithInterest<I>,
459 ) -> Result<UpdateResult<'_, S, I>, UpdateError> {
460 let Self { properties, state } = self;
461 match event.into_inner() {
462 fnet_interfaces::Event::Existing(existing) => {
463 let existing = Properties::<I>::try_from(existing)?;
464 if existing.id == properties.id {
465 return Err(UpdateError::DuplicateExisting(existing.into()));
466 }
467 }
468 fnet_interfaces::Event::Added(added) => {
469 let added = Properties::<I>::try_from(added)?;
470 if added.id == properties.id {
471 return Err(UpdateError::DuplicateAdded(added.into()));
472 }
473 }
474 fnet_interfaces::Event::Changed(mut change) => {
475 let fnet_interfaces::Properties {
476 id,
477 name: _,
478 port_class: _,
479 online,
480 has_default_ipv4_route,
481 has_default_ipv6_route,
482 addresses,
483 port_identity_koid: _,
484 __source_breaking: _,
485 } = &mut change;
486 if let Some(id) = *id {
487 if properties.id.get() == id {
488 let mut changed = false;
489 macro_rules! swap_if_some {
490 ($field:ident) => {
491 if let Some($field) = $field {
492 if properties.$field != *$field {
493 std::mem::swap(&mut properties.$field, $field);
494 changed = true;
495 }
496 }
497 };
498 }
499 swap_if_some!(online);
500 swap_if_some!(has_default_ipv4_route);
501 swap_if_some!(has_default_ipv6_route);
502 if let Some(addresses) = addresses {
503 if addresses.len() != properties.addresses.len()
511 || !addresses
512 .iter()
513 .zip(
514 properties
515 .addresses
516 .iter()
517 .cloned()
518 .map(fnet_interfaces::Address::from),
519 )
520 .all(|(a, b)| *a == b)
521 {
522 let previous_len = properties.addresses.len();
523 let () = properties.addresses.reserve(addresses.len());
526 for address in addresses.drain(..).map(Address::try_from) {
527 let () = properties.addresses.push(address?);
528 }
529 let () = addresses.extend(
530 properties.addresses.drain(..previous_len).map(Into::into),
531 );
532 changed = true;
533 }
534 }
535 if changed {
536 change.id = None;
537 return Ok(UpdateResult::Changed {
538 previous: change,
539 current: properties,
540 state,
541 });
542 } else {
543 return Err(UpdateError::EmptyChange(change));
544 }
545 }
546 } else {
547 return Err(UpdateError::MissingId(change));
548 }
549 }
550 fnet_interfaces::Event::Removed(removed_id) => {
551 if properties.id.get() == removed_id {
552 return Err(UpdateError::Removed);
553 }
554 }
555 fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}) => {}
556 }
557 Ok(UpdateResult::NoChange)
558 }
559}
560
561impl<S: Default, I: FieldInterests> Update<S> for InterfaceState<S, I> {
562 type Interest = I;
563 fn update(
564 &mut self,
565 event: EventWithInterest<I>,
566 ) -> Result<UpdateResult<'_, S, I>, UpdateError> {
567 fn get_properties<S, I: FieldInterests>(
568 state: &mut InterfaceState<S, I>,
569 ) -> &mut PropertiesAndState<S, I> {
570 match state {
571 InterfaceState::Known(properties) => properties,
572 InterfaceState::Unknown(id) => unreachable!(
573 "matched `Unknown({})` immediately after assigning with `Known`",
574 id
575 ),
576 }
577 }
578 match self {
579 InterfaceState::Unknown(id) => match event.into_inner() {
580 fnet_interfaces::Event::Existing(existing) => {
581 let properties = Properties::try_from(existing)?;
582 if properties.id.get() == *id {
583 *self = InterfaceState::Known(PropertiesAndState {
584 properties,
585 state: S::default(),
586 });
587 let PropertiesAndState { properties, state } = get_properties(self);
588 return Ok(UpdateResult::Existing { properties, state });
589 }
590 }
591 fnet_interfaces::Event::Added(added) => {
592 let properties = Properties::try_from(added)?;
593 if properties.id.get() == *id {
594 *self = InterfaceState::Known(PropertiesAndState {
595 properties,
596 state: S::default(),
597 });
598 let PropertiesAndState { properties, state } = get_properties(self);
599 return Ok(UpdateResult::Added { properties, state });
600 }
601 }
602 fnet_interfaces::Event::Changed(change) => {
603 if let Some(change_id) = change.id {
604 if change_id == *id {
605 return Err(UpdateError::UnknownChanged(change));
606 }
607 } else {
608 return Err(UpdateError::MissingId(change));
609 }
610 }
611 fnet_interfaces::Event::Removed(removed_id) => {
612 if removed_id == *id {
613 return Err(UpdateError::UnknownRemoved(removed_id));
614 }
615 }
616 fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}) => {}
617 },
618 InterfaceState::Known(properties) => return properties.update(event),
619 }
620 Ok(UpdateResult::NoChange)
621 }
622}
623
624pub struct ZeroError {}
626
627pub trait TryFromMaybeNonZero: Sized {
629 fn try_from(value: u64) -> Result<Self, ZeroError>;
631}
632
633impl TryFromMaybeNonZero for u64 {
634 fn try_from(value: u64) -> Result<Self, ZeroError> {
635 Ok(value)
636 }
637}
638
639impl TryFromMaybeNonZero for NonZeroU64 {
640 fn try_from(value: u64) -> Result<Self, ZeroError> {
641 NonZeroU64::new(value).ok_or(ZeroError {})
642 }
643}
644
645macro_rules! impl_map {
646 ($map_type:ident, $map_mod:tt) => {
647 impl<K, S, I> Update<S> for $map_type<K, PropertiesAndState<S, I>>
648 where
649 K: TryFromMaybeNonZero + Copy + From<NonZeroU64> + Eq + Ord + std::hash::Hash,
650 S: Default,
651 I: FieldInterests,
652 {
653 type Interest = I;
654
655 fn update(
656 &mut self,
657 event: EventWithInterest<I>,
658 ) -> Result<UpdateResult<'_, S, I>, UpdateError> {
659 match event.into_inner() {
660 fnet_interfaces::Event::Existing(existing) => {
661 let existing = Properties::try_from(existing)?;
662 match self.entry(existing.id.into()) {
663 $map_mod::Entry::Occupied(_) => {
664 Err(UpdateError::DuplicateExisting(existing.into()))
665 }
666 $map_mod::Entry::Vacant(entry) => {
667 let PropertiesAndState { properties, state } =
668 entry.insert(PropertiesAndState {
669 properties: existing,
670 state: S::default(),
671 });
672 Ok(UpdateResult::Existing { properties, state })
673 }
674 }
675 }
676 fnet_interfaces::Event::Added(added) => {
677 let added = Properties::try_from(added)?;
678 match self.entry(added.id.into()) {
679 $map_mod::Entry::Occupied(_) => {
680 Err(UpdateError::DuplicateAdded(added.into()))
681 }
682 $map_mod::Entry::Vacant(entry) => {
683 let PropertiesAndState { properties, state } =
684 entry.insert(PropertiesAndState {
685 properties: added,
686 state: S::default(),
687 });
688 Ok(UpdateResult::Added { properties, state })
689 }
690 }
691 }
692 fnet_interfaces::Event::Changed(change) => {
693 let id = if let Some(id) = change.id {
694 id
695 } else {
696 return Err(UpdateError::MissingId(change));
697 };
698 if let Some(properties) = self.get_mut(
699 &K::try_from(id)
700 .map_err(|ZeroError {}| UpdateError::ZeroInterfaceId)?,
701 ) {
702 properties.update(EventWithInterest::new(
703 fnet_interfaces::Event::Changed(change),
704 ))
705 } else {
706 Err(UpdateError::UnknownChanged(change))
707 }
708 }
709 fnet_interfaces::Event::Removed(removed_id) => {
710 if let Some(properties) = self.remove(
711 &K::try_from(removed_id)
712 .map_err(|ZeroError {}| UpdateError::ZeroInterfaceId)?,
713 ) {
714 Ok(UpdateResult::Removed(properties))
715 } else {
716 Err(UpdateError::UnknownRemoved(removed_id))
717 }
718 }
719 fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}) => {
720 Ok(UpdateResult::NoChange)
721 }
722 }
723 }
724 }
725 };
726}
727
728impl_map!(BTreeMap, btree_map);
729impl_map!(HashMap, hash_map);
730
731#[derive(Error, Debug)]
733pub enum WatcherOperationError<S: Debug, B: Update<S> + Debug> {
734 #[error("event stream error: {0}")]
736 EventStream(fidl::Error),
737 #[error("failed to update: {0}")]
739 Update(UpdateError),
740 #[error("watcher event stream ended unexpectedly, final state: {final_state:?}")]
742 UnexpectedEnd {
743 final_state: B,
745 marker: std::marker::PhantomData<S>,
747 },
748 #[error("unexpected event type: {0:?}")]
750 UnexpectedEvent(fnet_interfaces::Event),
751}
752
753#[derive(Error, Debug)]
755pub enum WatcherCreationError {
756 #[error("failed to create interface watcher proxy: {0}")]
758 CreateProxy(fidl::Error),
759 #[error("failed to get interface watcher: {0}")]
761 GetWatcher(fidl::Error),
762}
763
764pub async fn wait_interface<S, B, St, F, T>(
773 stream: St,
774 init: &mut B,
775 mut predicate: F,
776) -> Result<T, WatcherOperationError<S, B>>
777where
778 S: Debug + Default,
779 B: Update<S> + Clone + Debug,
780 St: Stream<Item = Result<EventWithInterest<B::Interest>, fidl::Error>>,
781 F: FnMut(&B) -> Option<T>,
782{
783 async_utils::fold::try_fold_while(
784 stream.map_err(WatcherOperationError::EventStream),
785 init,
786 |acc, event| {
787 futures::future::ready(match acc.update(event) {
788 Ok(changed) => match changed {
789 UpdateResult::Existing { .. }
790 | UpdateResult::Added { .. }
791 | UpdateResult::Changed { .. }
792 | UpdateResult::Removed(_) => {
793 if let Some(rtn) = predicate(acc) {
794 Ok(async_utils::fold::FoldWhile::Done(rtn))
795 } else {
796 Ok(async_utils::fold::FoldWhile::Continue(acc))
797 }
798 }
799 UpdateResult::NoChange => Ok(async_utils::fold::FoldWhile::Continue(acc)),
800 },
801 Err(e) => Err(WatcherOperationError::Update(e)),
802 })
803 },
804 )
805 .await?
806 .short_circuited()
807 .map_err(|final_state| WatcherOperationError::UnexpectedEnd {
808 final_state: final_state.clone(),
809 marker: Default::default(),
810 })
811}
812
813#[derive(Derivative)]
815#[derivative(
816 Clone(bound = "S: Clone"),
817 Debug(bound = "S: Debug"),
818 PartialEq(bound = "S: PartialEq")
819)]
820pub enum InterfaceState<S, I: FieldInterests> {
821 Unknown(u64),
823 Known(PropertiesAndState<S, I>),
825}
826
827pub async fn wait_interface_with_id<S, St, F, T, I>(
839 stream: St,
840 init: &mut InterfaceState<S, I>,
841 mut predicate: F,
842) -> Result<T, WatcherOperationError<S, InterfaceState<S, I>>>
843where
844 S: Default + Clone + Debug,
845 St: Stream<Item = Result<EventWithInterest<I>, fidl::Error>>,
846 F: FnMut(&PropertiesAndState<S, I>) -> Option<T>,
847 I: FieldInterests,
848{
849 wait_interface(stream, init, |state| {
850 match state {
851 InterfaceState::Known(properties) => predicate(properties),
852 InterfaceState::Unknown(_) => None,
855 }
856 })
857 .await
858}
859
860pub async fn existing<S, St, B>(stream: St, init: B) -> Result<B, WatcherOperationError<S, B>>
866where
867 S: Debug,
868 St: futures::Stream<Item = Result<EventWithInterest<B::Interest>, fidl::Error>>,
869 B: Update<S> + Debug,
870{
871 async_utils::fold::try_fold_while(
872 stream.map_err(WatcherOperationError::EventStream),
873 init,
874 |mut acc, event| {
875 futures::future::ready(match event.inner() {
876 fnet_interfaces::Event::Existing(_) => match acc.update(event) {
877 Ok::<UpdateResult<'_, _, _>, _>(_) => {
878 Ok(async_utils::fold::FoldWhile::Continue(acc))
879 }
880 Err(e) => Err(WatcherOperationError::Update(e)),
881 },
882 fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}) => {
883 Ok(async_utils::fold::FoldWhile::Done(acc))
884 }
885 fnet_interfaces::Event::Added(_)
886 | fnet_interfaces::Event::Removed(_)
887 | fnet_interfaces::Event::Changed(_) => {
888 Err(WatcherOperationError::UnexpectedEvent(event.into_inner()))
889 }
890 })
891 },
892 )
893 .await?
894 .short_circuited()
895 .map_err(|acc| WatcherOperationError::UnexpectedEnd {
896 final_state: acc,
897 marker: Default::default(),
898 })
899}
900
901pub enum IncludedAddresses {
903 All,
905 OnlyAssigned,
907}
908
909pub fn event_stream_from_state<I: FieldInterests>(
915 interface_state: &fnet_interfaces::StateProxy,
916 included_addresses: IncludedAddresses,
917) -> Result<
918 impl Stream<Item = Result<EventWithInterest<I>, fidl::Error>> + use<I>,
919 WatcherCreationError,
920> {
921 let (watcher, server) = ::fidl::endpoints::create_proxy::<fnet_interfaces::WatcherMarker>();
922 let () = interface_state
923 .get_watcher(
924 &fnet_interfaces::WatcherOptions {
925 address_properties_interest: Some(interest_from_params::<I>()),
926 include_non_assigned_addresses: Some(match included_addresses {
927 IncludedAddresses::All => true,
928 IncludedAddresses::OnlyAssigned => false,
929 }),
930 ..Default::default()
931 },
932 server,
933 )
934 .map_err(WatcherCreationError::GetWatcher)?;
935 Ok(futures::stream::try_unfold(watcher, |watcher| async {
936 Ok(Some((EventWithInterest::new(watcher.watch().await?), watcher)))
937 }))
938}
939
940fn interest_from_params<I: FieldInterests>() -> fnet_interfaces::AddressPropertiesInterest {
941 let mut interest = fnet_interfaces::AddressPropertiesInterest::empty();
942 if <I::ValidUntil as MaybeInterest<_>>::ENABLED {
943 interest |= fnet_interfaces::AddressPropertiesInterest::VALID_UNTIL;
944 }
945 if <I::PreferredLifetimeInfo as MaybeInterest<_>>::ENABLED {
946 interest |= fnet_interfaces::AddressPropertiesInterest::PREFERRED_LIFETIME_INFO;
947 }
948 interest
949}
950
951#[derive(Clone, Debug, Eq, Hash, PartialEq, Default)]
953pub struct NoInterest;
954
955mod interest {
956 use super::*;
957
958 use Debug;
959 use std::hash::Hash;
960
961 pub trait FieldInterests {
967 type PreferredLifetimeInfo: MaybeInterest<PreferredLifetimeInfo>;
969 type ValidUntil: MaybeInterest<PositiveMonotonicInstant>;
971 }
972
973 pub trait MaybeInterest<T> {
975 const ENABLED: bool;
977
978 type Ty: Clone + Debug + Eq + Hash + PartialEq + 'static;
980
981 fn try_from_fidl<F: TryInto<T, Error: Into<anyhow::Error>>>(
983 fidl: Option<F>,
984 ) -> Result<Self::Ty, anyhow::Error>;
985
986 fn into_fidl<F: From<T>>(value: Self::Ty) -> Option<F>;
989 }
990
991 pub struct EnableInterest;
995
996 impl<T: Clone + Debug + Eq + Hash + PartialEq + 'static> MaybeInterest<T> for EnableInterest {
997 const ENABLED: bool = true;
998 type Ty = T;
999
1000 fn try_from_fidl<F: TryInto<T, Error: Into<anyhow::Error>>>(
1001 fidl: Option<F>,
1002 ) -> Result<Self::Ty, anyhow::Error> {
1003 fidl.map(|f| f.try_into().map_err(Into::into))
1004 .unwrap_or_else(|| Err(anyhow::anyhow!("missing field with registered interest")))
1005 }
1006
1007 fn into_fidl<F: From<T>>(value: Self::Ty) -> Option<F> {
1008 Some(value.into())
1009 }
1010 }
1011
1012 pub struct DisableInterest;
1016 impl<T> MaybeInterest<T> for DisableInterest {
1017 const ENABLED: bool = false;
1018
1019 type Ty = NoInterest;
1020
1021 fn try_from_fidl<F: TryInto<T, Error: Into<anyhow::Error>>>(
1022 fidl: Option<F>,
1023 ) -> Result<Self::Ty, anyhow::Error> {
1024 match fidl {
1025 Some(_) => Err(anyhow::anyhow!("unexpected set field with no registered interest")),
1026 None => Ok(NoInterest),
1027 }
1028 }
1029
1030 fn into_fidl<F: From<T>>(_value: Self::Ty) -> Option<F> {
1031 None
1032 }
1033 }
1034
1035 pub(super) type FromInterest<I, T> =
1038 <<T as FieldSpec>::Interest<I> as MaybeInterest<<T as FieldSpec>::Present>>::Ty;
1039
1040 pub trait FieldSpec {
1046 type Interest<I: FieldInterests>: MaybeInterest<Self::Present>;
1048
1049 type Fidl: From<Self::Present>;
1051
1052 type Present: TryFrom<Self::Fidl, Error: Into<anyhow::Error>>;
1055
1056 const FIELD_NAME: &'static str;
1059 }
1060
1061 pub struct InterestConverter<I, P>(PhantomData<(I, P)>);
1062
1063 impl<I, P> fidl_table_validation::Converter for InterestConverter<I, P>
1064 where
1065 I: FieldInterests,
1066 P: FieldSpec,
1067 {
1068 type Fidl = Option<P::Fidl>;
1069 type Validated = <P::Interest<I> as MaybeInterest<P::Present>>::Ty;
1070 type Error = anyhow::Error;
1071
1072 fn try_from_fidl(value: Self::Fidl) -> std::result::Result<Self::Validated, Self::Error> {
1073 <P::Interest<I> as MaybeInterest<_>>::try_from_fidl(value).context(P::FIELD_NAME)
1074 }
1075
1076 fn from_validated(validated: Self::Validated) -> Self::Fidl {
1077 <P::Interest<I> as MaybeInterest<_>>::into_fidl(validated)
1078 }
1079 }
1080
1081 pub struct ValidUntilInterest;
1082
1083 impl FieldSpec for ValidUntilInterest {
1084 type Interest<I: FieldInterests> = I::ValidUntil;
1085 type Fidl = zx_types::zx_time_t;
1086 type Present = PositiveMonotonicInstant;
1087 const FIELD_NAME: &'static str = "valid_until";
1088 }
1089
1090 pub struct PreferredLifetimeInfoInterest;
1091
1092 impl FieldSpec for PreferredLifetimeInfoInterest {
1093 type Interest<I: FieldInterests> = I::PreferredLifetimeInfo;
1094 type Fidl = fnet_interfaces::PreferredLifetimeInfo;
1095 type Present = PreferredLifetimeInfo;
1096 const FIELD_NAME: &'static str = "preferred_lifetime_info";
1097 }
1098}
1099pub use interest::{DisableInterest, EnableInterest, FieldInterests};
1100use interest::{
1101 FromInterest, InterestConverter, MaybeInterest, PreferredLifetimeInfoInterest,
1102 ValidUntilInterest,
1103};
1104
1105#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1107pub struct AllInterest;
1108impl FieldInterests for AllInterest {
1109 type PreferredLifetimeInfo = EnableInterest;
1110 type ValidUntil = EnableInterest;
1111}
1112
1113#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1116pub struct DefaultInterest;
1117impl FieldInterests for DefaultInterest {
1118 type PreferredLifetimeInfo = DisableInterest;
1119 type ValidUntil = DisableInterest;
1120}
1121
1122#[derive(Derivative)]
1125#[derivative(Clone(bound = ""), Debug(bound = ""), Eq(bound = ""), PartialEq(bound = ""))]
1126pub struct EventWithInterest<I: FieldInterests> {
1127 event: fnet_interfaces::Event,
1128 #[derivative(Debug = "ignore")]
1129 _marker: PhantomData<I>,
1130}
1131
1132impl<I: FieldInterests> EventWithInterest<I> {
1133 pub fn new(event: fnet_interfaces::Event) -> Self {
1140 Self { event, _marker: PhantomData }
1141 }
1142
1143 pub fn into_inner(self) -> fnet_interfaces::Event {
1145 self.event
1146 }
1147
1148 pub fn inner(&self) -> &fnet_interfaces::Event {
1150 &self.event
1151 }
1152}
1153
1154impl<I: FieldInterests> From<fnet_interfaces::Event> for EventWithInterest<I> {
1155 fn from(value: fnet_interfaces::Event) -> Self {
1156 Self::new(value)
1157 }
1158}
1159
1160impl<I: FieldInterests> From<EventWithInterest<I>> for fnet_interfaces::Event {
1161 fn from(value: EventWithInterest<I>) -> Self {
1162 value.into_inner()
1163 }
1164}
1165
1166struct PortIdentityKoidConverter;
1167
1168impl From<Never> for PropertiesValidationError {
1169 fn from(value: Never) -> Self {
1170 match value {}
1171 }
1172}
1173
1174impl fidl_table_validation::Converter for PortIdentityKoidConverter {
1175 type Fidl = Option<u64>;
1176 type Validated = Option<PortIdentityKoid>;
1177 type Error = Never;
1178
1179 fn try_from_fidl(value: Self::Fidl) -> Result<Self::Validated, Self::Error> {
1180 Ok(value.map(|value| PortIdentityKoid::from_raw(value)))
1181 }
1182
1183 fn from_validated(validated: Self::Validated) -> Self::Fidl {
1184 validated.map(|validated| validated.raw_koid())
1185 }
1186}
1187
1188#[cfg(test)]
1189mod tests {
1190 use super::*;
1191 use assert_matches::assert_matches;
1192 use fidl_fuchsia_net as fnet;
1193 use futures::FutureExt as _;
1194 use futures::task::Poll;
1195 use net_declare::fidl_subnet;
1196 use std::cell::RefCell;
1197 use std::convert::TryInto as _;
1198 use std::pin::Pin;
1199 use std::rc::Rc;
1200 use test_case::test_case;
1201
1202 fn fidl_properties(id: u64) -> fnet_interfaces::Properties {
1203 fnet_interfaces::Properties {
1204 id: Some(id),
1205 name: Some("test1".to_string()),
1206 port_class: Some(fnet_interfaces::PortClass::Loopback(fnet_interfaces::Empty {})),
1207 online: Some(false),
1208 has_default_ipv4_route: Some(false),
1209 has_default_ipv6_route: Some(false),
1210 addresses: Some(vec![fidl_address(ADDR, zx_types::ZX_TIME_INFINITE)]),
1211 ..Default::default()
1212 }
1213 }
1214
1215 fn validated_properties(id: u64) -> PropertiesAndState<(), AllInterest> {
1216 PropertiesAndState {
1217 properties: fidl_properties(id).try_into().expect("failed to validate FIDL Properties"),
1218 state: (),
1219 }
1220 }
1221
1222 fn properties_delta(id: u64) -> fnet_interfaces::Properties {
1223 fnet_interfaces::Properties {
1224 id: Some(id),
1225 name: None,
1226 port_class: None,
1227 online: Some(true),
1228 has_default_ipv4_route: Some(true),
1229 has_default_ipv6_route: Some(true),
1230 addresses: Some(vec![fidl_address(ADDR2, zx_types::ZX_TIME_INFINITE)]),
1231 ..Default::default()
1232 }
1233 }
1234
1235 fn fidl_properties_after_change(id: u64) -> fnet_interfaces::Properties {
1236 fnet_interfaces::Properties {
1237 id: Some(id),
1238 name: Some("test1".to_string()),
1239 port_class: Some(fnet_interfaces::PortClass::Loopback(fnet_interfaces::Empty {})),
1240 online: Some(true),
1241 has_default_ipv4_route: Some(true),
1242 has_default_ipv6_route: Some(true),
1243 addresses: Some(vec![fidl_address(ADDR2, zx_types::ZX_TIME_INFINITE)]),
1244 ..Default::default()
1245 }
1246 }
1247
1248 fn validated_properties_after_change(id: u64) -> PropertiesAndState<(), AllInterest> {
1249 PropertiesAndState {
1250 properties: fidl_properties_after_change(id)
1251 .try_into()
1252 .expect("failed to validate FIDL Properties"),
1253 state: (),
1254 }
1255 }
1256
1257 fn fidl_address(
1258 addr: fnet::Subnet,
1259 valid_until: zx_types::zx_time_t,
1260 ) -> fnet_interfaces::Address {
1261 fnet_interfaces::Address {
1262 addr: Some(addr),
1263 valid_until: Some(valid_until.try_into().unwrap()),
1264 assignment_state: Some(fnet_interfaces::AddressAssignmentState::Assigned),
1265 preferred_lifetime_info: Some(PreferredLifetimeInfo::preferred_forever().into()),
1266 __source_breaking: Default::default(),
1267 }
1268 }
1269
1270 const ID: u64 = 1;
1271 const ID2: u64 = 2;
1272 const ADDR: fnet::Subnet = fidl_subnet!("1.2.3.4/24");
1273 const ADDR2: fnet::Subnet = fidl_subnet!("5.6.7.8/24");
1274
1275 #[test_case(
1276 &mut std::iter::once((ID, validated_properties(ID))).collect::<HashMap<_, _>>();
1277 "hashmap"
1278 )]
1279 #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1280 #[test_case(&mut validated_properties(ID); "properties")]
1281 fn test_duplicate_error(state: &mut impl Update<(), Interest = AllInterest>) {
1282 assert_matches::assert_matches!(
1283 state.update(fnet_interfaces::Event::Added(fidl_properties(ID)).into()),
1284 Err(UpdateError::DuplicateAdded(added)) if added == fidl_properties(ID)
1285 );
1286 assert_matches::assert_matches!(
1287 state.update(fnet_interfaces::Event::Existing(fidl_properties(ID)).into()),
1288 Err(UpdateError::DuplicateExisting(existing)) if existing == fidl_properties(ID)
1289 );
1290 }
1291
1292 #[test_case(&mut HashMap::<u64, _>::new(); "hashmap")]
1293 #[test_case(&mut InterfaceState::Unknown(ID); "interface_state_unknown")]
1294 fn test_unknown_error(state: &mut impl Update<(), Interest = AllInterest>) {
1295 let unknown =
1296 fnet_interfaces::Properties { id: Some(ID), online: Some(true), ..Default::default() };
1297 assert_matches::assert_matches!(
1298 state.update(fnet_interfaces::Event::Changed(unknown.clone()).into()),
1299 Err(UpdateError::UnknownChanged(changed)) if changed == unknown
1300 );
1301 assert_matches::assert_matches!(
1302 state.update(fnet_interfaces::Event::Removed(ID).into()),
1303 Err(UpdateError::UnknownRemoved(id)) if id == ID
1304 );
1305 }
1306
1307 #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1308 #[test_case(&mut validated_properties(ID); "properties")]
1309 fn test_removed_error(state: &mut impl Update<()>) {
1310 assert_matches::assert_matches!(
1311 state.update(fnet_interfaces::Event::Removed(ID).into()),
1312 Err(UpdateError::Removed)
1313 );
1314 }
1315
1316 #[test_case(&mut HashMap::<u64, _>::new(); "hashmap")]
1317 #[test_case(&mut InterfaceState::Unknown(ID); "interface_state_unknown")]
1318 #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1319 #[test_case(&mut validated_properties(ID); "properties")]
1320 fn test_missing_id_error(state: &mut impl Update<(), Interest = AllInterest>) {
1321 let missing_id = fnet_interfaces::Properties { online: Some(true), ..Default::default() };
1322 assert_matches::assert_matches!(
1323 state.update(fnet_interfaces::Event::Changed(missing_id.clone()).into()),
1324 Err(UpdateError::MissingId(properties)) if properties == missing_id
1325 );
1326 }
1327
1328 #[test_case(
1329 &mut std::iter::once((ID, validated_properties(ID))).collect::<HashMap<_, _>>();
1330 "hashmap"
1331 )]
1332 #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1333 #[test_case(&mut validated_properties(ID); "properties")]
1334 fn test_empty_change_error(state: &mut impl Update<()>) {
1335 let empty_change = fnet_interfaces::Properties { id: Some(ID), ..Default::default() };
1336 let net_zero_change =
1337 fnet_interfaces::Properties { name: None, port_class: None, ..fidl_properties(ID) };
1338 assert_matches::assert_matches!(
1339 state.update(fnet_interfaces::Event::Changed(empty_change.clone()).into()),
1340 Err(UpdateError::EmptyChange(properties)) if properties == empty_change
1341 );
1342 assert_matches::assert_matches!(
1343 state.update(fnet_interfaces::Event::Changed(net_zero_change.clone()).into()),
1344 Err(UpdateError::EmptyChange(properties)) if properties == net_zero_change
1345 );
1346 }
1347
1348 #[test_case(
1349 &mut std::iter::once((ID, validated_properties(ID))).collect::<HashMap<_, _>>();
1350 "hashmap"
1351 )]
1352 #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1353 #[test_case(&mut validated_properties(ID); "properties")]
1354 fn test_update_changed_result(state: &mut impl Update<(), Interest = AllInterest>) {
1355 let want_previous = fnet_interfaces::Properties {
1356 online: Some(false),
1357 has_default_ipv4_route: Some(false),
1358 has_default_ipv6_route: Some(false),
1359 addresses: Some(vec![fidl_address(ADDR, zx_types::ZX_TIME_INFINITE)]),
1360 ..Default::default()
1361 };
1362 assert_matches::assert_matches!(
1363 state.update(fnet_interfaces::Event::Changed(properties_delta(ID).clone()).into()),
1364 Ok(UpdateResult::Changed { previous, current, state: _ }) => {
1365 assert_eq!(previous, want_previous);
1366 let PropertiesAndState { properties, state: () } =
1367 validated_properties_after_change(ID);
1368 assert_eq!(*current, properties);
1369 }
1370 );
1371 }
1372
1373 #[derive(Derivative)]
1374 #[derivative(Clone(bound = ""))]
1375 struct EventStream<I: FieldInterests>(Rc<RefCell<Vec<fnet_interfaces::Event>>>, PhantomData<I>);
1376
1377 impl<I: FieldInterests> Stream for EventStream<I> {
1378 type Item = Result<EventWithInterest<I>, fidl::Error>;
1379
1380 fn poll_next(
1381 self: Pin<&mut Self>,
1382 _cx: &mut futures::task::Context<'_>,
1383 ) -> Poll<Option<Self::Item>> {
1384 let EventStream(events_vec, _marker) = &*self;
1385 if events_vec.borrow().is_empty() {
1386 Poll::Ready(None)
1387 } else {
1388 Poll::Ready(Some(Ok(EventWithInterest::new(events_vec.borrow_mut().remove(0)))))
1389 }
1390 }
1391 }
1392
1393 fn test_event_stream<I: FieldInterests>() -> EventStream<I> {
1394 EventStream(
1395 Rc::new(RefCell::new(vec![
1396 fnet_interfaces::Event::Existing(fidl_properties(ID)),
1397 fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}),
1398 fnet_interfaces::Event::Added(fidl_properties(ID2)),
1399 fnet_interfaces::Event::Changed(properties_delta(ID)),
1400 fnet_interfaces::Event::Changed(properties_delta(ID2)),
1401 fnet_interfaces::Event::Removed(ID),
1402 fnet_interfaces::Event::Removed(ID2),
1403 ])),
1404 PhantomData,
1405 )
1406 }
1407
1408 #[test]
1409 fn test_wait_one_interface() {
1410 let event_stream = test_event_stream::<AllInterest>();
1411 let mut state = InterfaceState::Unknown(ID);
1412 for want in &[validated_properties(ID), validated_properties_after_change(ID)] {
1413 let () = wait_interface_with_id(event_stream.clone(), &mut state, |got| {
1414 assert_eq!(got, want);
1415 Some(())
1416 })
1417 .now_or_never()
1418 .expect("wait_interface_with_id did not complete immediately")
1419 .expect("wait_interface_with_id error");
1420 assert_matches!(state, InterfaceState::Known(ref got) if got == want);
1421 }
1422 }
1423
1424 fn test_wait_interface<'a, B>(state: &mut B, want_states: impl IntoIterator<Item = &'a B>)
1425 where
1426 B: 'a + Update<()> + Clone + Debug + std::cmp::PartialEq,
1427 {
1428 let event_stream = test_event_stream::<B::Interest>();
1429 for want in want_states.into_iter() {
1430 let () = wait_interface(event_stream.clone(), state, |got| {
1431 assert_eq!(got, want);
1432 Some(())
1433 })
1434 .now_or_never()
1435 .expect("wait_interface did not complete immediately")
1436 .expect("wait_interface error");
1437 assert_eq!(state, want);
1438 }
1439 }
1440
1441 #[test]
1442 fn test_wait_interface_hashmap() {
1443 test_wait_interface(
1444 &mut HashMap::new(),
1445 &[
1446 std::iter::once((ID, validated_properties(ID))).collect::<HashMap<_, _>>(),
1447 [(ID, validated_properties(ID)), (ID2, validated_properties(ID2))]
1448 .iter()
1449 .cloned()
1450 .collect::<HashMap<_, _>>(),
1451 [(ID, validated_properties_after_change(ID)), (ID2, validated_properties(ID2))]
1452 .iter()
1453 .cloned()
1454 .collect::<HashMap<_, _>>(),
1455 [
1456 (ID, validated_properties_after_change(ID)),
1457 (ID2, validated_properties_after_change(ID2)),
1458 ]
1459 .iter()
1460 .cloned()
1461 .collect::<HashMap<_, _>>(),
1462 std::iter::once((ID2, validated_properties_after_change(ID2)))
1463 .collect::<HashMap<_, _>>(),
1464 HashMap::new(),
1465 ],
1466 );
1467 }
1468
1469 #[test]
1470 fn test_wait_interface_interface_state() {
1471 test_wait_interface(
1472 &mut InterfaceState::Unknown(ID),
1473 &[
1474 InterfaceState::Known(validated_properties(ID)),
1475 InterfaceState::Known(validated_properties_after_change(ID)),
1476 ],
1477 );
1478 }
1479
1480 const ID_NON_EXISTENT: u64 = 0xffffffff;
1481 #[test_case(
1482 InterfaceState::Unknown(ID_NON_EXISTENT),
1483 InterfaceState::Unknown(ID_NON_EXISTENT);
1484 "interface_state_unknown_different_id"
1485 )]
1486 #[test_case(
1487 InterfaceState::Unknown(ID),
1488 InterfaceState::Known(validated_properties(ID));
1489 "interface_state_unknown")]
1490 #[test_case(
1491 HashMap::new(),
1492 [(ID, validated_properties(ID)), (ID2, validated_properties(ID2))]
1493 .iter()
1494 .cloned()
1495 .collect::<HashMap<_, _>>();
1496 "hashmap"
1497 )]
1498 fn test_existing<B>(state: B, want: B)
1499 where
1500 B: Update<(), Interest = AllInterest> + Debug + std::cmp::PartialEq,
1501 {
1502 let events = [
1503 fnet_interfaces::Event::Existing(fidl_properties(ID)),
1504 fnet_interfaces::Event::Existing(fidl_properties(ID2)),
1505 fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}),
1506 ];
1507 let event_stream =
1508 futures::stream::iter(events.iter().cloned().map(|e| Ok(EventWithInterest::new(e))));
1509 assert_eq!(
1510 existing(event_stream, state)
1511 .now_or_never()
1512 .expect("existing did not complete immediately")
1513 .expect("existing returned error"),
1514 want,
1515 );
1516 }
1517
1518 #[test]
1519 fn positive_instant() {
1520 assert_eq!(PositiveMonotonicInstant::from_nanos(-1), None);
1521 assert_eq!(PositiveMonotonicInstant::from_nanos(0), None);
1522 assert_eq!(PositiveMonotonicInstant::from_nanos(1), Some(PositiveMonotonicInstant(1)));
1523 }
1524}