fidl_fuchsia_net_interfaces_ext/
lib.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Extensions for the fuchsia.net.interfaces FIDL library.
6
7#![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/// Like [`fnet_interfaces::PortClass`], with the inner `device` flattened.
31///
32/// This type also derives additional impls that are not available on
33/// `fnet_interfaces::PortClass`.
34#[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    /// Returns `true` if this `PortClass` is `Loopback`.
50    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/// An Error returned when converting from `fnet_interfaces::PortClass` to
66/// `PortClass`.
67#[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/// An error returned when `fnet_interfaces::PortClass` is an unknown variant.
77#[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/// An error returned when `fhardware_network::PortClass` is an unknown variant.
84#[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/// The type of [`Properties::port_identity_koid`].
157///
158/// This is a new-type to handle the OS abstraction around the availability of
159/// [`zx::Koid`] type, offering easy conversions for use both in host and target
160/// code.
161#[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    /// Creates a [`PortIdentityKoid`] from a raw `koid` value.
168    pub fn from_raw(koid: zx_types::zx_koid_t) -> Self {
169        Self(koid)
170    }
171
172    /// Returns the raw koid value.
173    pub fn raw_koid(&self) -> zx_types::zx_koid_t {
174        let Self(raw) = self;
175        *raw
176    }
177}
178
179/// Type alias as [`zx::Koid`] when available in Fuchsia.
180#[cfg(target_os = "fuchsia")]
181pub type PortIdentityKoid = zx::Koid;
182
183/// Properties of a network interface.
184#[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    /// An opaque identifier for the interface. Its value will not be reused
189    /// even if the device is removed and subsequently re-added. Immutable.
190    pub id: NonZeroU64,
191    /// The name of the interface. Immutable.
192    pub name: String,
193    /// The device is enabled and its physical state is online.
194    pub online: bool,
195    /// The addresses currently assigned to the interface.
196    pub addresses: Vec<Address<I>>,
197    /// Whether there is a default IPv4 route through this interface.
198    pub has_default_ipv4_route: bool,
199    /// Whether there is a default IPv6 route through this interface.
200    pub has_default_ipv6_route: bool,
201    /// The device type of the interface. Immutable.
202    pub port_class: PortClass,
203    /// The KOID of the event vended out by the interface's backing
204    /// `fuchsia.hardware.network/Port`.
205    ///
206    /// Callers may use this identifier to correlate port instances to installed
207    /// interfaces.
208    ///
209    /// Immutable. Absent for interfaces not backed by a
210    /// `fuchsia.hardware.network/Port`.
211    #[fidl_field_type(optional_converter = PortIdentityKoidConverter)]
212    pub port_identity_koid: Option<PortIdentityKoid>,
213}
214
215/// An address and its properties.
216#[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    /// The address and prefix length.
228    pub addr: fidl_fuchsia_net::Subnet,
229    /// The time after which the address will no longer be valid.
230    ///
231    /// Its value must be greater than 0. A value of zx.time.INFINITE indicates
232    /// that the address will always be valid.
233    #[fidl_field_type(optional_converter = InterestConverter::<I, ValidUntilInterest>)]
234    pub valid_until: FromInterest<I, ValidUntilInterest>,
235    /// Preferred lifetime information.
236    #[fidl_field_type(optional_converter = InterestConverter::<I, PreferredLifetimeInfoInterest>)]
237    pub preferred_lifetime_info: FromInterest<I, PreferredLifetimeInfoInterest>,
238    /// The address's assignment state.
239    pub assignment_state: fnet_interfaces::AddressAssignmentState,
240}
241
242/// Information about the preferred lifetime of an IP address or delegated
243/// prefix.
244///
245/// Type-safe version of [`fnet_interfaces::PreferredLifetimeInfo`].
246#[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    /// Returns a lifetime information for an address that is always preferred.
255    pub const fn preferred_forever() -> Self {
256        Self::PreferredUntil(PositiveMonotonicInstant::INFINITE_FUTURE)
257    }
258
259    /// Converts to the equivalent FIDL type.
260    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/// The error returned by attempting to convert a non positive instant to
294/// `PositiveMonotonicInstant`.
295#[derive(Error, Debug)]
296#[error("{0} is not a positive monotonic instant")]
297pub struct NotPositiveMonotonicInstantError(i64);
298
299/// A positive monotonic instant.
300#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Copy, Clone, Hash)]
301pub struct PositiveMonotonicInstant(i64);
302
303impl PositiveMonotonicInstant {
304    /// An instant in the infinite future.
305    pub const INFINITE_FUTURE: Self = Self(zx_types::ZX_TIME_INFINITE);
306
307    /// Returns the nanoseconds value for the instant.
308    pub const fn into_nanos(self) -> i64 {
309        let Self(v) = self;
310        v
311    }
312
313    /// Returns the the positive nanoseconds value from the monotonic timestamp
314    /// in nanoseconds, if it's positive.
315    pub const fn from_nanos(v: i64) -> Option<Self> {
316        if v > 0 { Some(Self(v)) } else { None }
317    }
318
319    /// Returns true if `self` is equal to `INFINITE_FUTURE`.
320    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/// Interface watcher event update errors.
356#[derive(Error, Debug)]
357pub enum UpdateError {
358    /// The update attempted to add an already-known added interface into local state.
359    #[error("duplicate added event {0:?}")]
360    DuplicateAdded(fnet_interfaces::Properties),
361    /// The update attempted to add an already-known existing interface into local state.
362    #[error("duplicate existing event {0:?}")]
363    DuplicateExisting(fnet_interfaces::Properties),
364    /// The event contained one or more invalid properties.
365    #[error("failed to validate Properties FIDL table: {0}")]
366    InvalidProperties(#[from] PropertiesValidationError),
367    /// The event contained one or more invalid addresses.
368    #[error("failed to validate Address FIDL table: {0}")]
369    InvalidAddress(#[from] AddressValidationError),
370    /// The event was required to have contained an ID, but did not.
371    #[error("changed event with missing ID {0:?}")]
372    MissingId(fnet_interfaces::Properties),
373    /// The event did not contain any changes.
374    #[error("changed event contains no changed fields {0:?}")]
375    EmptyChange(fnet_interfaces::Properties),
376    /// The update removed the only interface in the local state.
377    #[error("interface has been removed")]
378    Removed,
379    /// The event contained changes for an interface that did not exist in local state.
380    #[error("unknown interface changed {0:?}")]
381    UnknownChanged(fnet_interfaces::Properties),
382    /// The event removed an interface that did not exist in local state.
383    #[error("unknown interface with id {0} deleted")]
384    UnknownRemoved(u64),
385    /// The event included an interface id = 0, which should never happen.
386    #[error("encountered 0 interface id")]
387    ZeroInterfaceId,
388}
389
390/// The result of updating network interface state with an event.
391#[derive(Derivative)]
392#[derivative(Debug(bound = "S: Debug"), PartialEq(bound = "S: PartialEq"))]
393pub enum UpdateResult<'a, S, I: FieldInterests> {
394    /// The update did not change the local state.
395    NoChange,
396    /// The update inserted an existing interface into the local state.
397    Existing {
398        /// The properties,
399        properties: &'a Properties<I>,
400        /// The state.
401        state: &'a mut S,
402    },
403    /// The update inserted an added interface into the local state.
404    Added {
405        /// The properties,
406        properties: &'a Properties<I>,
407        /// The state.
408        state: &'a mut S,
409    },
410    /// The update changed an existing interface in the local state.
411    Changed {
412        /// The previous values of any properties which changed.
413        ///
414        /// This is sparsely populated: none of the immutable properties are present (they can
415        /// all be found on `current`), and a mutable property is present with its value pre-update
416        /// iff it has changed as a result of the update.
417        previous: fnet_interfaces::Properties,
418        /// The properties of the interface post-update.
419        current: &'a Properties<I>,
420        /// The state of the interface.
421        state: &'a mut S,
422    },
423    /// The update removed an interface from the local state.
424    Removed(PropertiesAndState<S, I>),
425}
426
427/// The properties and state for an interface.
428#[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    /// Properties.
437    pub properties: Properties<I>,
438    /// State.
439    pub state: S,
440}
441
442/// A trait for types holding interface state that can be updated by change events.
443pub trait Update<S> {
444    /// The expected watcher interest type for this update target.
445    type Interest: FieldInterests;
446
447    /// Update state with the interface change event.
448    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                            // NB The following iterator comparison assumes that the server is
504                            // well-behaved and will not send a permutation of the existing
505                            // addresses with no actual changes (additions or removals). Making the
506                            // comparison via set equality is possible, but more expensive than
507                            // it's worth.
508                            // TODO(https://github.com/rust-lang/rust/issues/64295) Use `eq_by` to
509                            // compare the iterators once stabilized.
510                            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                                // NB This is equivalent to Vec::try_extend, if such a method
524                                // existed.
525                                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
624/// An error indicated an unexpected zero value.
625pub struct ZeroError {}
626
627/// A type that may fallibly convert from a u64 because the value is 0.
628pub trait TryFromMaybeNonZero: Sized {
629    /// Try to convert the u64 into `Self`.
630    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/// Interface watcher operational errors.
732#[derive(Error, Debug)]
733pub enum WatcherOperationError<S: Debug, B: Update<S> + Debug> {
734    /// Watcher event stream yielded an error.
735    #[error("event stream error: {0}")]
736    EventStream(fidl::Error),
737    /// Watcher event stream yielded an event that could not be applied to the local state.
738    #[error("failed to update: {0}")]
739    Update(UpdateError),
740    /// Watcher event stream ended unexpectedly.
741    #[error("watcher event stream ended unexpectedly, final state: {final_state:?}")]
742    UnexpectedEnd {
743        /// The local state at the time of the watcher event stream's end.
744        final_state: B,
745        /// Marker for the state held alongside interface properties.
746        marker: std::marker::PhantomData<S>,
747    },
748    /// Watcher event stream yielded an event with unexpected type.
749    #[error("unexpected event type: {0:?}")]
750    UnexpectedEvent(fnet_interfaces::Event),
751}
752
753/// Interface watcher creation errors.
754#[derive(Error, Debug)]
755pub enum WatcherCreationError {
756    /// Proxy creation failed.
757    #[error("failed to create interface watcher proxy: {0}")]
758    CreateProxy(fidl::Error),
759    /// Watcher acquisition failed.
760    #[error("failed to get interface watcher: {0}")]
761    GetWatcher(fidl::Error),
762}
763
764/// Wait for a condition on interface state to be satisfied.
765///
766/// With the initial state in `init`, take events from `stream` and update the state, calling
767/// `predicate` whenever the state changes. When `predicate` returns `Some(T)`, yield `Ok(T)`.
768///
769/// Since the state passed via `init` is mutably updated for every event, when this function
770/// returns successfully, the state can be used as the initial state in a subsequent call with a
771/// stream of events from the same watcher.
772pub 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/// The local state of an interface's properties.
814#[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    /// Not yet known.
822    Unknown(u64),
823    /// Locally known.
824    Known(PropertiesAndState<S, I>),
825}
826
827/// Wait for a condition on a specific interface to be satisfied.
828///
829/// Note that `stream` must be created from a watcher with interest in all
830/// fields, such as one created from [`event_stream_from_state`].
831///
832/// With the initial state in `init`, take events from `stream` and update the state, calling
833/// `predicate` whenever the state changes. When `predicate` returns `Some(T)`, yield `Ok(T)`.
834///
835/// Since the state passed via `init` is mutably updated for every event, when this function
836/// returns successfully, the state can be used as the initial state in a subsequent call with a
837/// stream of events from the same watcher.
838pub 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            // NB This is technically unreachable because a successful update will always change
853            // `Unknown` to `Known` (and `Known` will stay `Known`).
854            InterfaceState::Unknown(_) => None,
855        }
856    })
857    .await
858}
859
860/// Read Existing interface events from `stream`, updating `init` until the Idle
861/// event is detected, returning the resulting state.
862///
863/// Note that `stream` must be created from a watcher with interest in the
864/// correct fields, such as one created from [`event_stream_from_state`].
865pub 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
901/// The kind of addresses included from the watcher.
902pub enum IncludedAddresses {
903    /// All addresses are returned from the watcher.
904    All,
905    /// Only assigned addresses are returned rom the watcher.
906    OnlyAssigned,
907}
908
909/// Initialize a watcher with interest in all fields and return its events as a
910/// stream.
911///
912/// If `included_addresses` is `All`, then all addresses will be returned, not
913/// just assigned addresses.
914pub 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/// A marker for a field that didn't register interest with the watcher.
952#[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    /// A trait that parameterizes interest in fields from interfaces watcher.
962    ///
963    /// Use [`EnableInterest`] or [`DisableInterest`] in each type to
964    /// enable/disable interest in receiving those fields from the server,
965    /// respectively.
966    pub trait FieldInterests {
967        /// Interest in the `preferred_lifetime_info` field.
968        type PreferredLifetimeInfo: MaybeInterest<PreferredLifetimeInfo>;
969        /// Interest in the `valid_until` field.
970        type ValidUntil: MaybeInterest<PositiveMonotonicInstant>;
971    }
972
973    /// Helper trait to implement conversion with optional field interest.
974    pub trait MaybeInterest<T> {
975        /// Whether this is an enabled interest.
976        const ENABLED: bool;
977
978        /// The actual type carried by the validated struct.
979        type Ty: Clone + Debug + Eq + Hash + PartialEq + 'static;
980
981        /// Converts from an optional FIDL input to the target type `Self::Ty`.
982        fn try_from_fidl<F: TryInto<T, Error: Into<anyhow::Error>>>(
983            fidl: Option<F>,
984        ) -> Result<Self::Ty, anyhow::Error>;
985
986        /// Converts from the target type `Self::Ty` into an optional FIDL
987        /// value.
988        fn into_fidl<F: From<T>>(value: Self::Ty) -> Option<F>;
989    }
990
991    /// Enabled interest in a FIDL field.
992    ///
993    /// Use as a type parameter in [`FieldInterests`].
994    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    /// Disabled interest in a FIDL field.
1013    ///
1014    /// Use as a type parameter in [`FieldInterests`].
1015    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    /// A handy alias to shorten the signature of a type derived from
1036    /// [`MaybeInterest`] based on [`FieldSpec`].
1037    pub(super) type FromInterest<I, T> =
1038        <<T as FieldSpec>::Interest<I> as MaybeInterest<<T as FieldSpec>::Present>>::Ty;
1039
1040    /// Parameterizes interest fields.
1041    ///
1042    /// This trait allows a common converter implementation for the FIDL table
1043    /// validation structure and unifies the schema of how interest fields
1044    /// behave.
1045    pub trait FieldSpec {
1046        /// Extracts the interest type from [`FieldInterests`].
1047        type Interest<I: FieldInterests>: MaybeInterest<Self::Present>;
1048
1049        /// The FIDL representation of the field.
1050        type Fidl: From<Self::Present>;
1051
1052        /// The validated representation of the field when interest is
1053        /// expressed.
1054        type Present: TryFrom<Self::Fidl, Error: Into<anyhow::Error>>;
1055
1056        /// The field name in the originating struct. This helps generate better
1057        /// error messages.
1058        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/// A marker for interest in all optional fields.
1106#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1107pub struct AllInterest;
1108impl FieldInterests for AllInterest {
1109    type PreferredLifetimeInfo = EnableInterest;
1110    type ValidUntil = EnableInterest;
1111}
1112
1113/// A marker for the default interest options as defined by the interfaces
1114/// watcher API.
1115#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1116pub struct DefaultInterest;
1117impl FieldInterests for DefaultInterest {
1118    type PreferredLifetimeInfo = DisableInterest;
1119    type ValidUntil = DisableInterest;
1120}
1121
1122/// An [`fnet_interfaces::Event`] tagged with the interest parameters that
1123/// created it.
1124#[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    /// Creates a new `EventWithInterest` with the provided event.
1134    ///
1135    /// Note that this type exists to steer proper usage of this crate. Creating
1136    /// `EventWithInterest` with arbitrary interests is potentially dangerous if
1137    /// the combination of field expectations don't match what was used to
1138    /// create the watcher.
1139    pub fn new(event: fnet_interfaces::Event) -> Self {
1140        Self { event, _marker: PhantomData }
1141    }
1142
1143    /// Retrieves the internal event.
1144    pub fn into_inner(self) -> fnet_interfaces::Event {
1145        self.event
1146    }
1147
1148    /// Borrows the internal event.
1149    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}