Skip to main content

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_fuchsia_hardware_network as fhardware_network;
17use fidl_fuchsia_net_interfaces as fnet_interfaces;
18use fidl_table_validation::*;
19use futures::{Stream, TryStreamExt as _};
20use std::collections::btree_map::{self, BTreeMap};
21use std::collections::hash_map::{self, HashMap};
22use std::convert::{Infallible as Never, TryFrom as _};
23use std::fmt::Debug;
24use std::marker::PhantomData;
25use std::num::NonZeroU64;
26use thiserror::Error;
27
28/// Like [`fnet_interfaces::PortClass`], with the inner `device` flattened.
29///
30/// This type also derives additional impls that are not available on
31/// `fnet_interfaces::PortClass`.
32#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
33#[allow(missing_docs)]
34pub enum PortClass {
35    Loopback,
36    Virtual,
37    Ethernet,
38    WlanClient,
39    WlanAp,
40    Ppp,
41    Bridge,
42    Lowpan,
43    Blackhole,
44}
45
46impl PortClass {
47    /// Returns `true` if this `PortClass` is `Loopback`.
48    pub fn is_loopback(&self) -> bool {
49        match self {
50            PortClass::Loopback => true,
51            PortClass::Virtual
52            | PortClass::Blackhole
53            | PortClass::Ethernet
54            | PortClass::WlanClient
55            | PortClass::WlanAp
56            | PortClass::Ppp
57            | PortClass::Bridge
58            | PortClass::Lowpan => false,
59        }
60    }
61}
62
63/// An Error returned when converting from `fnet_interfaces::PortClass` to
64/// `PortClass`.
65#[derive(Debug, Error, PartialEq, Eq)]
66#[allow(missing_docs)]
67pub enum UnknownPortClassError {
68    #[error(transparent)]
69    NetInterfaces(UnknownNetInterfacesPortClassError),
70    #[error(transparent)]
71    HardwareNetwork(UnknownHardwareNetworkPortClassError),
72}
73
74/// An error returned when `fnet_interfaces::PortClass` is an unknown variant.
75#[derive(Debug, Error, PartialEq, Eq)]
76#[error("unknown fuchsia.net.interfaces/PortClass ordinal: {unknown_ordinal}")]
77pub struct UnknownNetInterfacesPortClassError {
78    unknown_ordinal: u64,
79}
80
81/// An error returned when `fhardware_network::PortClass` is an unknown variant.
82#[derive(Debug, Error, PartialEq, Eq)]
83#[error("unknown fuchsia.hardware.network/PortClass ordinal: {unknown_ordinal}")]
84pub struct UnknownHardwareNetworkPortClassError {
85    unknown_ordinal: u16,
86}
87
88impl TryFrom<fnet_interfaces::PortClass> for PortClass {
89    type Error = UnknownPortClassError;
90    fn try_from(port_class: fnet_interfaces::PortClass) -> Result<Self, Self::Error> {
91        match port_class {
92            fnet_interfaces::PortClass::Loopback(fnet_interfaces::Empty) => Ok(PortClass::Loopback),
93            fnet_interfaces::PortClass::Blackhole(fnet_interfaces::Empty) => {
94                Ok(PortClass::Blackhole)
95            }
96            fnet_interfaces::PortClass::Device(port_class) => {
97                PortClass::try_from(port_class).map_err(UnknownPortClassError::HardwareNetwork)
98            }
99            fnet_interfaces::PortClass::__SourceBreaking { unknown_ordinal } => {
100                Err(UnknownPortClassError::NetInterfaces(UnknownNetInterfacesPortClassError {
101                    unknown_ordinal,
102                }))
103            }
104        }
105    }
106}
107
108impl From<PortClass> for fnet_interfaces::PortClass {
109    fn from(port_class: PortClass) -> Self {
110        match port_class {
111            PortClass::Loopback => fnet_interfaces::PortClass::Loopback(fnet_interfaces::Empty),
112            PortClass::Blackhole => fnet_interfaces::PortClass::Blackhole(fnet_interfaces::Empty),
113            PortClass::Virtual => {
114                fnet_interfaces::PortClass::Device(fhardware_network::PortClass::Virtual)
115            }
116            PortClass::Ethernet => {
117                fnet_interfaces::PortClass::Device(fhardware_network::PortClass::Ethernet)
118            }
119            PortClass::WlanClient => {
120                fnet_interfaces::PortClass::Device(fhardware_network::PortClass::WlanClient)
121            }
122            PortClass::WlanAp => {
123                fnet_interfaces::PortClass::Device(fhardware_network::PortClass::WlanAp)
124            }
125            PortClass::Ppp => fnet_interfaces::PortClass::Device(fhardware_network::PortClass::Ppp),
126            PortClass::Bridge => {
127                fnet_interfaces::PortClass::Device(fhardware_network::PortClass::Bridge)
128            }
129            PortClass::Lowpan => {
130                fnet_interfaces::PortClass::Device(fhardware_network::PortClass::Lowpan)
131            }
132        }
133    }
134}
135
136impl TryFrom<fhardware_network::PortClass> for PortClass {
137    type Error = UnknownHardwareNetworkPortClassError;
138    fn try_from(port_class: fhardware_network::PortClass) -> Result<Self, Self::Error> {
139        match port_class {
140            fhardware_network::PortClass::Virtual => Ok(PortClass::Virtual),
141            fhardware_network::PortClass::Ethernet => Ok(PortClass::Ethernet),
142            fhardware_network::PortClass::WlanClient => Ok(PortClass::WlanClient),
143            fhardware_network::PortClass::WlanAp => Ok(PortClass::WlanAp),
144            fhardware_network::PortClass::Ppp => Ok(PortClass::Ppp),
145            fhardware_network::PortClass::Bridge => Ok(PortClass::Bridge),
146            fhardware_network::PortClass::Lowpan => Ok(PortClass::Lowpan),
147            fhardware_network::PortClass::__SourceBreaking { unknown_ordinal } => {
148                Err(UnknownHardwareNetworkPortClassError { unknown_ordinal })
149            }
150        }
151    }
152}
153
154/// The type of [`Properties::port_identity_koid`].
155///
156/// This is a new-type to handle the OS abstraction around the availability of
157/// [`zx::Koid`] type, offering easy conversions for use both in host and target
158/// code.
159#[cfg(not(target_os = "fuchsia"))]
160#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
161pub struct PortIdentityKoid(zx_types::zx_koid_t);
162
163#[cfg(not(target_os = "fuchsia"))]
164impl PortIdentityKoid {
165    /// Creates a [`PortIdentityKoid`] from a raw `koid` value.
166    pub fn from_raw(koid: zx_types::zx_koid_t) -> Self {
167        Self(koid)
168    }
169
170    /// Returns the raw koid value.
171    pub fn raw_koid(&self) -> zx_types::zx_koid_t {
172        let Self(raw) = self;
173        *raw
174    }
175}
176
177/// Type alias as [`zx::Koid`] when available in Fuchsia.
178#[cfg(target_os = "fuchsia")]
179pub type PortIdentityKoid = zx::Koid;
180
181/// Properties of a network interface.
182#[derive(Derivative, ValidFidlTable)]
183#[derivative(Clone(bound = ""), Debug(bound = ""), Eq(bound = ""), PartialEq(bound = ""))]
184#[fidl_table_src(fnet_interfaces::Properties)]
185pub struct Properties<I: FieldInterests> {
186    /// An opaque identifier for the interface. Its value will not be reused
187    /// even if the device is removed and subsequently re-added. Immutable.
188    pub id: NonZeroU64,
189    /// The name of the interface. Immutable.
190    pub name: String,
191    /// The device is enabled and its physical state is online.
192    pub online: bool,
193    /// The addresses currently assigned to the interface.
194    pub addresses: Vec<Address<I>>,
195    /// Whether there is a default IPv4 route through this interface.
196    pub has_default_ipv4_route: bool,
197    /// Whether there is a default IPv6 route through this interface.
198    pub has_default_ipv6_route: bool,
199    /// The device type of the interface. Immutable.
200    pub port_class: PortClass,
201    /// The KOID of the event vended out by the interface's backing
202    /// `fuchsia.hardware.network/Port`.
203    ///
204    /// Callers may use this identifier to correlate port instances to installed
205    /// interfaces.
206    ///
207    /// Immutable. Absent for interfaces not backed by a
208    /// `fuchsia.hardware.network/Port`.
209    #[fidl_field_type(optional_converter = PortIdentityKoidConverter)]
210    pub port_identity_koid: Option<PortIdentityKoid>,
211}
212
213/// An address and its properties.
214#[derive(Derivative, ValidFidlTable)]
215#[derivative(
216    Clone(bound = ""),
217    Debug(bound = ""),
218    Eq(bound = ""),
219    PartialEq(bound = ""),
220    Hash(bound = "")
221)]
222#[fidl_table_src(fnet_interfaces::Address)]
223#[fidl_table_strict]
224pub struct Address<I: FieldInterests> {
225    /// The address and prefix length.
226    pub addr: fidl_fuchsia_net::Subnet,
227    /// The time after which the address will no longer be valid.
228    ///
229    /// Its value must be greater than 0. A value of zx.time.INFINITE indicates
230    /// that the address will always be valid.
231    #[fidl_field_type(optional_converter = InterestConverter::<I, ValidUntilInterest>)]
232    pub valid_until: FromInterest<I, ValidUntilInterest>,
233    /// Preferred lifetime information.
234    #[fidl_field_type(optional_converter = InterestConverter::<I, PreferredLifetimeInfoInterest>)]
235    pub preferred_lifetime_info: FromInterest<I, PreferredLifetimeInfoInterest>,
236    /// The address's assignment state.
237    pub assignment_state: fnet_interfaces::AddressAssignmentState,
238}
239
240/// Information about the preferred lifetime of an IP address or delegated
241/// prefix.
242///
243/// Type-safe version of [`fnet_interfaces::PreferredLifetimeInfo`].
244#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Copy, Clone, Hash)]
245#[allow(missing_docs)]
246pub enum PreferredLifetimeInfo {
247    PreferredUntil(PositiveMonotonicInstant),
248    Deprecated,
249}
250
251impl PreferredLifetimeInfo {
252    /// Returns a lifetime information for an address that is always preferred.
253    pub const fn preferred_forever() -> Self {
254        Self::PreferredUntil(PositiveMonotonicInstant::INFINITE_FUTURE)
255    }
256
257    /// Converts to the equivalent FIDL type.
258    pub const fn to_fidl(self) -> fnet_interfaces::PreferredLifetimeInfo {
259        match self {
260            PreferredLifetimeInfo::Deprecated => {
261                fnet_interfaces::PreferredLifetimeInfo::Deprecated(fnet_interfaces::Empty)
262            }
263            PreferredLifetimeInfo::PreferredUntil(v) => {
264                fnet_interfaces::PreferredLifetimeInfo::PreferredUntil(v.into_nanos())
265            }
266        }
267    }
268}
269
270impl TryFrom<fnet_interfaces::PreferredLifetimeInfo> for PreferredLifetimeInfo {
271    type Error = NotPositiveMonotonicInstantError;
272
273    fn try_from(value: fnet_interfaces::PreferredLifetimeInfo) -> Result<Self, Self::Error> {
274        match value {
275            fnet_interfaces::PreferredLifetimeInfo::Deprecated(fnet_interfaces::Empty) => {
276                Ok(Self::Deprecated)
277            }
278            fnet_interfaces::PreferredLifetimeInfo::PreferredUntil(v) => {
279                Ok(Self::PreferredUntil(v.try_into()?))
280            }
281        }
282    }
283}
284
285impl From<PreferredLifetimeInfo> for fnet_interfaces::PreferredLifetimeInfo {
286    fn from(value: PreferredLifetimeInfo) -> Self {
287        value.to_fidl()
288    }
289}
290
291/// The error returned by attempting to convert a non positive instant to
292/// `PositiveMonotonicInstant`.
293#[derive(Error, Debug)]
294#[error("{0} is not a positive monotonic instant")]
295pub struct NotPositiveMonotonicInstantError(i64);
296
297/// A positive monotonic instant.
298#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Copy, Clone, Hash)]
299pub struct PositiveMonotonicInstant(i64);
300
301impl PositiveMonotonicInstant {
302    /// An instant in the infinite future.
303    pub const INFINITE_FUTURE: Self = Self(zx_types::ZX_TIME_INFINITE);
304
305    /// Returns the nanoseconds value for the instant.
306    pub const fn into_nanos(self) -> i64 {
307        let Self(v) = self;
308        v
309    }
310
311    /// Returns the the positive nanoseconds value from the monotonic timestamp
312    /// in nanoseconds, if it's positive.
313    pub const fn from_nanos(v: i64) -> Option<Self> {
314        if v > 0 { Some(Self(v)) } else { None }
315    }
316
317    /// Returns true if `self` is equal to `INFINITE_FUTURE`.
318    pub fn is_infinite(&self) -> bool {
319        self == &Self::INFINITE_FUTURE
320    }
321}
322
323#[cfg(target_os = "fuchsia")]
324impl From<PositiveMonotonicInstant> for zx::MonotonicInstant {
325    fn from(PositiveMonotonicInstant(v): PositiveMonotonicInstant) -> Self {
326        zx::MonotonicInstant::from_nanos(v)
327    }
328}
329
330#[cfg(target_os = "fuchsia")]
331impl TryFrom<zx::MonotonicInstant> for PositiveMonotonicInstant {
332    type Error = NotPositiveMonotonicInstantError;
333
334    fn try_from(value: zx::MonotonicInstant) -> Result<Self, Self::Error> {
335        Self::try_from(value.into_nanos())
336    }
337}
338
339impl From<PositiveMonotonicInstant> for zx_types::zx_time_t {
340    fn from(value: PositiveMonotonicInstant) -> Self {
341        value.into_nanos()
342    }
343}
344
345impl TryFrom<zx_types::zx_time_t> for PositiveMonotonicInstant {
346    type Error = NotPositiveMonotonicInstantError;
347
348    fn try_from(value: zx_types::zx_time_t) -> Result<Self, Self::Error> {
349        Self::from_nanos(value).ok_or(NotPositiveMonotonicInstantError(value))
350    }
351}
352
353/// Interface watcher event update errors.
354#[derive(Error, Debug)]
355pub enum UpdateError {
356    /// The update attempted to add an already-known added interface into local state.
357    #[error("duplicate added event {0:?}")]
358    DuplicateAdded(fnet_interfaces::Properties),
359    /// The update attempted to add an already-known existing interface into local state.
360    #[error("duplicate existing event {0:?}")]
361    DuplicateExisting(fnet_interfaces::Properties),
362    /// The event contained one or more invalid properties.
363    #[error("failed to validate Properties FIDL table: {0}")]
364    InvalidProperties(#[from] PropertiesValidationError),
365    /// The event contained one or more invalid addresses.
366    #[error("failed to validate Address FIDL table: {0}")]
367    InvalidAddress(#[from] AddressValidationError),
368    /// The event was required to have contained an ID, but did not.
369    #[error("changed event with missing ID {0:?}")]
370    MissingId(fnet_interfaces::Properties),
371    /// The event did not contain any changes.
372    #[error("changed event contains no changed fields {0:?}")]
373    EmptyChange(fnet_interfaces::Properties),
374    /// The update removed the only interface in the local state.
375    #[error("interface has been removed")]
376    Removed,
377    /// The event contained changes for an interface that did not exist in local state.
378    #[error("unknown interface changed {0:?}")]
379    UnknownChanged(fnet_interfaces::Properties),
380    /// The event removed an interface that did not exist in local state.
381    #[error("unknown interface with id {0} deleted")]
382    UnknownRemoved(u64),
383    /// The event included an interface id = 0, which should never happen.
384    #[error("encountered 0 interface id")]
385    ZeroInterfaceId,
386}
387
388/// The result of updating network interface state with an event.
389#[derive(Derivative)]
390#[derivative(Debug(bound = "S: Debug"), PartialEq(bound = "S: PartialEq"))]
391pub enum UpdateResult<'a, S, I: FieldInterests> {
392    /// The update did not change the local state.
393    NoChange,
394    /// The update inserted an existing interface into the local state.
395    Existing {
396        /// The properties,
397        properties: &'a Properties<I>,
398        /// The state.
399        state: &'a mut S,
400    },
401    /// The update inserted an added interface into the local state.
402    Added {
403        /// The properties,
404        properties: &'a Properties<I>,
405        /// The state.
406        state: &'a mut S,
407    },
408    /// The update changed an existing interface in the local state.
409    Changed {
410        /// The previous values of any properties which changed.
411        ///
412        /// This is sparsely populated: none of the immutable properties are present (they can
413        /// all be found on `current`), and a mutable property is present with its value pre-update
414        /// iff it has changed as a result of the update.
415        previous: fnet_interfaces::Properties,
416        /// The properties of the interface post-update.
417        current: &'a Properties<I>,
418        /// The state of the interface.
419        state: &'a mut S,
420    },
421    /// The update removed an interface from the local state.
422    Removed(PropertiesAndState<S, I>),
423}
424
425/// The properties and state for an interface.
426#[derive(Derivative)]
427#[derivative(
428    Clone(bound = "S: Clone"),
429    Debug(bound = "S: Debug"),
430    Eq(bound = "S: Eq"),
431    PartialEq(bound = "S: PartialEq")
432)]
433pub struct PropertiesAndState<S, I: FieldInterests> {
434    /// Properties.
435    pub properties: Properties<I>,
436    /// State.
437    pub state: S,
438}
439
440/// A trait for types holding interface state that can be updated by change events.
441pub trait Update<S> {
442    /// The expected watcher interest type for this update target.
443    type Interest: FieldInterests;
444
445    /// Update state with the interface change event.
446    fn update(
447        &mut self,
448        event: EventWithInterest<Self::Interest>,
449    ) -> Result<UpdateResult<'_, S, Self::Interest>, UpdateError>;
450}
451
452impl<S, I: FieldInterests> Update<S> for PropertiesAndState<S, I> {
453    type Interest = I;
454    fn update(
455        &mut self,
456        event: EventWithInterest<I>,
457    ) -> Result<UpdateResult<'_, S, I>, UpdateError> {
458        let Self { properties, state } = self;
459        match event.into_inner() {
460            fnet_interfaces::Event::Existing(existing) => {
461                let existing = Properties::<I>::try_from(existing)?;
462                if existing.id == properties.id {
463                    return Err(UpdateError::DuplicateExisting(existing.into()));
464                }
465            }
466            fnet_interfaces::Event::Added(added) => {
467                let added = Properties::<I>::try_from(added)?;
468                if added.id == properties.id {
469                    return Err(UpdateError::DuplicateAdded(added.into()));
470                }
471            }
472            fnet_interfaces::Event::Changed(mut change) => {
473                let fnet_interfaces::Properties {
474                    id,
475                    name: _,
476                    port_class: _,
477                    online,
478                    has_default_ipv4_route,
479                    has_default_ipv6_route,
480                    addresses,
481                    port_identity_koid: _,
482                    __source_breaking: _,
483                } = &mut change;
484                if let Some(id) = *id {
485                    if properties.id.get() == id {
486                        let mut changed = false;
487                        macro_rules! swap_if_some {
488                            ($field:ident) => {
489                                if let Some($field) = $field {
490                                    if properties.$field != *$field {
491                                        std::mem::swap(&mut properties.$field, $field);
492                                        changed = true;
493                                    }
494                                }
495                            };
496                        }
497                        swap_if_some!(online);
498                        swap_if_some!(has_default_ipv4_route);
499                        swap_if_some!(has_default_ipv6_route);
500                        if let Some(addresses) = addresses {
501                            // NB The following iterator comparison assumes that the server is
502                            // well-behaved and will not send a permutation of the existing
503                            // addresses with no actual changes (additions or removals). Making the
504                            // comparison via set equality is possible, but more expensive than
505                            // it's worth.
506                            // TODO(https://github.com/rust-lang/rust/issues/64295) Use `eq_by` to
507                            // compare the iterators once stabilized.
508                            if addresses.len() != properties.addresses.len()
509                                || !addresses
510                                    .iter()
511                                    .zip(
512                                        properties
513                                            .addresses
514                                            .iter()
515                                            .cloned()
516                                            .map(fnet_interfaces::Address::from),
517                                    )
518                                    .all(|(a, b)| *a == b)
519                            {
520                                let previous_len = properties.addresses.len();
521                                // NB This is equivalent to Vec::try_extend, if such a method
522                                // existed.
523                                properties.addresses.reserve(addresses.len());
524                                for address in addresses.drain(..).map(Address::try_from) {
525                                    properties.addresses.push(address?);
526                                }
527                                addresses.extend(
528                                    properties.addresses.drain(..previous_len).map(Into::into),
529                                );
530                                changed = true;
531                            }
532                        }
533                        if changed {
534                            change.id = None;
535                            return Ok(UpdateResult::Changed {
536                                previous: change,
537                                current: properties,
538                                state,
539                            });
540                        } else {
541                            return Err(UpdateError::EmptyChange(change));
542                        }
543                    }
544                } else {
545                    return Err(UpdateError::MissingId(change));
546                }
547            }
548            fnet_interfaces::Event::Removed(removed_id) => {
549                if properties.id.get() == removed_id {
550                    return Err(UpdateError::Removed);
551                }
552            }
553            fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}) => {}
554        }
555        Ok(UpdateResult::NoChange)
556    }
557}
558
559impl<S: Default, I: FieldInterests> Update<S> for InterfaceState<S, I> {
560    type Interest = I;
561    fn update(
562        &mut self,
563        event: EventWithInterest<I>,
564    ) -> Result<UpdateResult<'_, S, I>, UpdateError> {
565        fn get_properties<S, I: FieldInterests>(
566            state: &mut InterfaceState<S, I>,
567        ) -> &mut PropertiesAndState<S, I> {
568            match state {
569                InterfaceState::Known(properties) => properties,
570                InterfaceState::Unknown(id) => unreachable!(
571                    "matched `Unknown({})` immediately after assigning with `Known`",
572                    id
573                ),
574            }
575        }
576        match self {
577            InterfaceState::Unknown(id) => match event.into_inner() {
578                fnet_interfaces::Event::Existing(existing) => {
579                    let properties = Properties::try_from(existing)?;
580                    if properties.id.get() == *id {
581                        *self = InterfaceState::Known(PropertiesAndState {
582                            properties,
583                            state: S::default(),
584                        });
585                        let PropertiesAndState { properties, state } = get_properties(self);
586                        return Ok(UpdateResult::Existing { properties, state });
587                    }
588                }
589                fnet_interfaces::Event::Added(added) => {
590                    let properties = Properties::try_from(added)?;
591                    if properties.id.get() == *id {
592                        *self = InterfaceState::Known(PropertiesAndState {
593                            properties,
594                            state: S::default(),
595                        });
596                        let PropertiesAndState { properties, state } = get_properties(self);
597                        return Ok(UpdateResult::Added { properties, state });
598                    }
599                }
600                fnet_interfaces::Event::Changed(change) => {
601                    if let Some(change_id) = change.id {
602                        if change_id == *id {
603                            return Err(UpdateError::UnknownChanged(change));
604                        }
605                    } else {
606                        return Err(UpdateError::MissingId(change));
607                    }
608                }
609                fnet_interfaces::Event::Removed(removed_id) => {
610                    if removed_id == *id {
611                        return Err(UpdateError::UnknownRemoved(removed_id));
612                    }
613                }
614                fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}) => {}
615            },
616            InterfaceState::Known(properties) => return properties.update(event),
617        }
618        Ok(UpdateResult::NoChange)
619    }
620}
621
622/// An error indicated an unexpected zero value.
623pub struct ZeroError {}
624
625/// A type that may fallibly convert from a u64 because the value is 0.
626pub trait TryFromMaybeNonZero: Sized {
627    /// Try to convert the u64 into `Self`.
628    fn try_from(value: u64) -> Result<Self, ZeroError>;
629}
630
631impl TryFromMaybeNonZero for u64 {
632    fn try_from(value: u64) -> Result<Self, ZeroError> {
633        Ok(value)
634    }
635}
636
637impl TryFromMaybeNonZero for NonZeroU64 {
638    fn try_from(value: u64) -> Result<Self, ZeroError> {
639        NonZeroU64::new(value).ok_or(ZeroError {})
640    }
641}
642
643macro_rules! impl_map {
644    ($map_type:ident, $map_mod:tt) => {
645        impl<K, S, I> Update<S> for $map_type<K, PropertiesAndState<S, I>>
646        where
647            K: TryFromMaybeNonZero + Copy + From<NonZeroU64> + Eq + Ord + std::hash::Hash,
648            S: Default,
649            I: FieldInterests,
650        {
651            type Interest = I;
652
653            fn update(
654                &mut self,
655                event: EventWithInterest<I>,
656            ) -> Result<UpdateResult<'_, S, I>, UpdateError> {
657                match event.into_inner() {
658                    fnet_interfaces::Event::Existing(existing) => {
659                        let existing = Properties::try_from(existing)?;
660                        match self.entry(existing.id.into()) {
661                            $map_mod::Entry::Occupied(_) => {
662                                Err(UpdateError::DuplicateExisting(existing.into()))
663                            }
664                            $map_mod::Entry::Vacant(entry) => {
665                                let PropertiesAndState { properties, state } =
666                                    entry.insert(PropertiesAndState {
667                                        properties: existing,
668                                        state: S::default(),
669                                    });
670                                Ok(UpdateResult::Existing { properties, state })
671                            }
672                        }
673                    }
674                    fnet_interfaces::Event::Added(added) => {
675                        let added = Properties::try_from(added)?;
676                        match self.entry(added.id.into()) {
677                            $map_mod::Entry::Occupied(_) => {
678                                Err(UpdateError::DuplicateAdded(added.into()))
679                            }
680                            $map_mod::Entry::Vacant(entry) => {
681                                let PropertiesAndState { properties, state } =
682                                    entry.insert(PropertiesAndState {
683                                        properties: added,
684                                        state: S::default(),
685                                    });
686                                Ok(UpdateResult::Added { properties, state })
687                            }
688                        }
689                    }
690                    fnet_interfaces::Event::Changed(change) => {
691                        let id = if let Some(id) = change.id {
692                            id
693                        } else {
694                            return Err(UpdateError::MissingId(change));
695                        };
696                        if let Some(properties) = self.get_mut(
697                            &K::try_from(id)
698                                .map_err(|ZeroError {}| UpdateError::ZeroInterfaceId)?,
699                        ) {
700                            properties.update(EventWithInterest::new(
701                                fnet_interfaces::Event::Changed(change),
702                            ))
703                        } else {
704                            Err(UpdateError::UnknownChanged(change))
705                        }
706                    }
707                    fnet_interfaces::Event::Removed(removed_id) => {
708                        if let Some(properties) = self.remove(
709                            &K::try_from(removed_id)
710                                .map_err(|ZeroError {}| UpdateError::ZeroInterfaceId)?,
711                        ) {
712                            Ok(UpdateResult::Removed(properties))
713                        } else {
714                            Err(UpdateError::UnknownRemoved(removed_id))
715                        }
716                    }
717                    fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}) => {
718                        Ok(UpdateResult::NoChange)
719                    }
720                }
721            }
722        }
723    };
724}
725
726impl_map!(BTreeMap, btree_map);
727impl_map!(HashMap, hash_map);
728
729/// Interface watcher operational errors.
730#[derive(Error, Debug)]
731pub enum WatcherOperationError<S: Debug, B: Update<S> + Debug> {
732    /// Watcher event stream yielded an error.
733    #[error("event stream error: {0}")]
734    EventStream(fidl::Error),
735    /// Watcher event stream yielded an event that could not be applied to the local state.
736    #[error("failed to update: {0}")]
737    Update(UpdateError),
738    /// Watcher event stream ended unexpectedly.
739    #[error("watcher event stream ended unexpectedly, final state: {final_state:?}")]
740    UnexpectedEnd {
741        /// The local state at the time of the watcher event stream's end.
742        final_state: B,
743        /// Marker for the state held alongside interface properties.
744        marker: std::marker::PhantomData<S>,
745    },
746    /// Watcher event stream yielded an event with unexpected type.
747    #[error("unexpected event type: {0:?}")]
748    UnexpectedEvent(fnet_interfaces::Event),
749}
750
751/// Interface watcher creation errors.
752#[derive(Error, Debug)]
753pub enum WatcherCreationError {
754    /// Proxy creation failed.
755    #[error("failed to create interface watcher proxy: {0}")]
756    CreateProxy(fidl::Error),
757    /// Watcher acquisition failed.
758    #[error("failed to get interface watcher: {0}")]
759    GetWatcher(fidl::Error),
760}
761
762/// Wait for a condition on interface state to be satisfied.
763///
764/// With the initial state in `init`, take events from `stream` and update the state, calling
765/// `predicate` whenever the state changes. When `predicate` returns `Some(T)`, yield `Ok(T)`.
766///
767/// Since the state passed via `init` is mutably updated for every event, when this function
768/// returns successfully, the state can be used as the initial state in a subsequent call with a
769/// stream of events from the same watcher.
770pub async fn wait_interface<S, B, St, F, T>(
771    stream: St,
772    init: &mut B,
773    mut predicate: F,
774) -> Result<T, WatcherOperationError<S, B>>
775where
776    S: Debug + Default,
777    B: Update<S> + Clone + Debug,
778    St: Stream<Item = Result<EventWithInterest<B::Interest>, fidl::Error>>,
779    F: FnMut(&B) -> Option<T>,
780{
781    async_utils::fold::try_fold_while(
782        stream.map_err(WatcherOperationError::EventStream),
783        init,
784        |acc, event| {
785            futures::future::ready(match acc.update(event) {
786                Ok(changed) => match changed {
787                    UpdateResult::Existing { .. }
788                    | UpdateResult::Added { .. }
789                    | UpdateResult::Changed { .. }
790                    | UpdateResult::Removed(_) => {
791                        if let Some(rtn) = predicate(acc) {
792                            Ok(async_utils::fold::FoldWhile::Done(rtn))
793                        } else {
794                            Ok(async_utils::fold::FoldWhile::Continue(acc))
795                        }
796                    }
797                    UpdateResult::NoChange => Ok(async_utils::fold::FoldWhile::Continue(acc)),
798                },
799                Err(e) => Err(WatcherOperationError::Update(e)),
800            })
801        },
802    )
803    .await?
804    .short_circuited()
805    .map_err(|final_state| WatcherOperationError::UnexpectedEnd {
806        final_state: final_state.clone(),
807        marker: Default::default(),
808    })
809}
810
811/// The local state of an interface's properties.
812#[derive(Derivative)]
813#[derivative(
814    Clone(bound = "S: Clone"),
815    Debug(bound = "S: Debug"),
816    PartialEq(bound = "S: PartialEq")
817)]
818pub enum InterfaceState<S, I: FieldInterests> {
819    /// Not yet known.
820    Unknown(u64),
821    /// Locally known.
822    Known(PropertiesAndState<S, I>),
823}
824
825/// Wait for a condition on a specific interface to be satisfied.
826///
827/// Note that `stream` must be created from a watcher with interest in all
828/// fields, such as one created from [`event_stream_from_state`].
829///
830/// With the initial state in `init`, take events from `stream` and update the state, calling
831/// `predicate` whenever the state changes. When `predicate` returns `Some(T)`, yield `Ok(T)`.
832///
833/// Since the state passed via `init` is mutably updated for every event, when this function
834/// returns successfully, the state can be used as the initial state in a subsequent call with a
835/// stream of events from the same watcher.
836pub async fn wait_interface_with_id<S, St, F, T, I>(
837    stream: St,
838    init: &mut InterfaceState<S, I>,
839    mut predicate: F,
840) -> Result<T, WatcherOperationError<S, InterfaceState<S, I>>>
841where
842    S: Default + Clone + Debug,
843    St: Stream<Item = Result<EventWithInterest<I>, fidl::Error>>,
844    F: FnMut(&PropertiesAndState<S, I>) -> Option<T>,
845    I: FieldInterests,
846{
847    wait_interface(stream, init, |state| {
848        match state {
849            InterfaceState::Known(properties) => predicate(properties),
850            // NB This is technically unreachable because a successful update will always change
851            // `Unknown` to `Known` (and `Known` will stay `Known`).
852            InterfaceState::Unknown(_) => None,
853        }
854    })
855    .await
856}
857
858/// Read Existing interface events from `stream`, updating `init` until the Idle
859/// event is detected, returning the resulting state.
860///
861/// Note that `stream` must be created from a watcher with interest in the
862/// correct fields, such as one created from [`event_stream_from_state`].
863pub async fn existing<S, St, B>(stream: St, init: B) -> Result<B, WatcherOperationError<S, B>>
864where
865    S: Debug,
866    St: futures::Stream<Item = Result<EventWithInterest<B::Interest>, fidl::Error>>,
867    B: Update<S> + Debug,
868{
869    async_utils::fold::try_fold_while(
870        stream.map_err(WatcherOperationError::EventStream),
871        init,
872        |mut acc, event| {
873            futures::future::ready(match event.inner() {
874                fnet_interfaces::Event::Existing(_) => match acc.update(event) {
875                    Ok::<UpdateResult<'_, _, _>, _>(_) => {
876                        Ok(async_utils::fold::FoldWhile::Continue(acc))
877                    }
878                    Err(e) => Err(WatcherOperationError::Update(e)),
879                },
880                fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}) => {
881                    Ok(async_utils::fold::FoldWhile::Done(acc))
882                }
883                fnet_interfaces::Event::Added(_)
884                | fnet_interfaces::Event::Removed(_)
885                | fnet_interfaces::Event::Changed(_) => {
886                    Err(WatcherOperationError::UnexpectedEvent(event.into_inner()))
887                }
888            })
889        },
890    )
891    .await?
892    .short_circuited()
893    .map_err(|acc| WatcherOperationError::UnexpectedEnd {
894        final_state: acc,
895        marker: Default::default(),
896    })
897}
898
899/// The kind of addresses included from the watcher.
900#[derive(Default)]
901pub enum IncludedAddresses {
902    /// All addresses are returned from the watcher.
903    All,
904    /// Only assigned addresses are returned rom the watcher.
905    #[default]
906    OnlyAssigned,
907}
908
909/// The options given to [`event_stream_from_state`] for interface state
910/// observation.
911#[derive(Default)]
912pub struct WatchOptions {
913    /// The addresses returned from the watcher.
914    pub included_addresses: IncludedAddresses,
915    /// The port identity to filter on, if any.
916    pub port_identity_koid_filter: Option<PortIdentityKoid>,
917}
918
919/// Initialize a watcher with interest in all fields and return its events as a
920/// stream.
921///
922/// If `included_addresses` is `All`, then all addresses will be returned, not
923/// just assigned addresses.
924pub fn event_stream_from_state<I: FieldInterests>(
925    interface_state: &fnet_interfaces::StateProxy,
926    options: WatchOptions,
927) -> Result<
928    impl Stream<Item = Result<EventWithInterest<I>, fidl::Error>> + use<I>,
929    WatcherCreationError,
930> {
931    let (watcher, server) = ::fidl::endpoints::create_proxy::<fnet_interfaces::WatcherMarker>();
932    let WatchOptions { included_addresses, port_identity_koid_filter } = options;
933    interface_state
934        .get_watcher(
935            &fnet_interfaces::WatcherOptions {
936                address_properties_interest: Some(interest_from_params::<I>()),
937                include_non_assigned_addresses: Some(match included_addresses {
938                    IncludedAddresses::All => true,
939                    IncludedAddresses::OnlyAssigned => false,
940                }),
941                port_identity_koid_filter: port_identity_koid_filter.map(|p| p.raw_koid()),
942                ..Default::default()
943            },
944            server,
945        )
946        .map_err(WatcherCreationError::GetWatcher)?;
947    Ok(futures::stream::try_unfold(watcher, |watcher| async {
948        Ok(Some((EventWithInterest::new(watcher.watch().await?), watcher)))
949    }))
950}
951
952fn interest_from_params<I: FieldInterests>() -> fnet_interfaces::AddressPropertiesInterest {
953    let mut interest = fnet_interfaces::AddressPropertiesInterest::empty();
954    if <I::ValidUntil as MaybeInterest<_>>::ENABLED {
955        interest |= fnet_interfaces::AddressPropertiesInterest::VALID_UNTIL;
956    }
957    if <I::PreferredLifetimeInfo as MaybeInterest<_>>::ENABLED {
958        interest |= fnet_interfaces::AddressPropertiesInterest::PREFERRED_LIFETIME_INFO;
959    }
960    interest
961}
962
963/// A marker for a field that didn't register interest with the watcher.
964#[derive(Clone, Debug, Eq, Hash, PartialEq, Default)]
965pub struct NoInterest;
966
967mod interest {
968    use super::*;
969
970    use Debug;
971    use std::hash::Hash;
972
973    /// A trait that parameterizes interest in fields from interfaces watcher.
974    ///
975    /// Use [`EnableInterest`] or [`DisableInterest`] in each type to
976    /// enable/disable interest in receiving those fields from the server,
977    /// respectively.
978    pub trait FieldInterests {
979        /// Interest in the `preferred_lifetime_info` field.
980        type PreferredLifetimeInfo: MaybeInterest<PreferredLifetimeInfo>;
981        /// Interest in the `valid_until` field.
982        type ValidUntil: MaybeInterest<PositiveMonotonicInstant>;
983    }
984
985    /// Helper trait to implement conversion with optional field interest.
986    pub trait MaybeInterest<T> {
987        /// Whether this is an enabled interest.
988        const ENABLED: bool;
989
990        /// The actual type carried by the validated struct.
991        type Ty: Clone + Debug + Eq + Hash + PartialEq + 'static;
992
993        /// Converts from an optional FIDL input to the target type `Self::Ty`.
994        fn try_from_fidl<F: TryInto<T, Error: Into<anyhow::Error>>>(
995            fidl: Option<F>,
996        ) -> Result<Self::Ty, anyhow::Error>;
997
998        /// Converts from the target type `Self::Ty` into an optional FIDL
999        /// value.
1000        fn into_fidl<F: From<T>>(value: Self::Ty) -> Option<F>;
1001    }
1002
1003    /// Enabled interest in a FIDL field.
1004    ///
1005    /// Use as a type parameter in [`FieldInterests`].
1006    pub struct EnableInterest;
1007
1008    impl<T: Clone + Debug + Eq + Hash + PartialEq + 'static> MaybeInterest<T> for EnableInterest {
1009        const ENABLED: bool = true;
1010        type Ty = T;
1011
1012        fn try_from_fidl<F: TryInto<T, Error: Into<anyhow::Error>>>(
1013            fidl: Option<F>,
1014        ) -> Result<Self::Ty, anyhow::Error> {
1015            fidl.map(|f| f.try_into().map_err(Into::into))
1016                .unwrap_or_else(|| Err(anyhow::anyhow!("missing field with registered interest")))
1017        }
1018
1019        fn into_fidl<F: From<T>>(value: Self::Ty) -> Option<F> {
1020            Some(value.into())
1021        }
1022    }
1023
1024    /// Disabled interest in a FIDL field.
1025    ///
1026    /// Use as a type parameter in [`FieldInterests`].
1027    pub struct DisableInterest;
1028    impl<T> MaybeInterest<T> for DisableInterest {
1029        const ENABLED: bool = false;
1030
1031        type Ty = NoInterest;
1032
1033        fn try_from_fidl<F: TryInto<T, Error: Into<anyhow::Error>>>(
1034            fidl: Option<F>,
1035        ) -> Result<Self::Ty, anyhow::Error> {
1036            match fidl {
1037                Some(_) => Err(anyhow::anyhow!("unexpected set field with no registered interest")),
1038                None => Ok(NoInterest),
1039            }
1040        }
1041
1042        fn into_fidl<F: From<T>>(_value: Self::Ty) -> Option<F> {
1043            None
1044        }
1045    }
1046
1047    /// A handy alias to shorten the signature of a type derived from
1048    /// [`MaybeInterest`] based on [`FieldSpec`].
1049    pub(super) type FromInterest<I, T> =
1050        <<T as FieldSpec>::Interest<I> as MaybeInterest<<T as FieldSpec>::Present>>::Ty;
1051
1052    /// Parameterizes interest fields.
1053    ///
1054    /// This trait allows a common converter implementation for the FIDL table
1055    /// validation structure and unifies the schema of how interest fields
1056    /// behave.
1057    pub trait FieldSpec {
1058        /// Extracts the interest type from [`FieldInterests`].
1059        type Interest<I: FieldInterests>: MaybeInterest<Self::Present>;
1060
1061        /// The FIDL representation of the field.
1062        type Fidl: From<Self::Present>;
1063
1064        /// The validated representation of the field when interest is
1065        /// expressed.
1066        type Present: TryFrom<Self::Fidl, Error: Into<anyhow::Error>>;
1067
1068        /// The field name in the originating struct. This helps generate better
1069        /// error messages.
1070        const FIELD_NAME: &'static str;
1071    }
1072
1073    pub struct InterestConverter<I, P>(PhantomData<(I, P)>);
1074
1075    impl<I, P> fidl_table_validation::Converter for InterestConverter<I, P>
1076    where
1077        I: FieldInterests,
1078        P: FieldSpec,
1079    {
1080        type Fidl = Option<P::Fidl>;
1081        type Validated = <P::Interest<I> as MaybeInterest<P::Present>>::Ty;
1082        type Error = anyhow::Error;
1083
1084        fn try_from_fidl(value: Self::Fidl) -> std::result::Result<Self::Validated, Self::Error> {
1085            <P::Interest<I> as MaybeInterest<_>>::try_from_fidl(value).context(P::FIELD_NAME)
1086        }
1087
1088        fn from_validated(validated: Self::Validated) -> Self::Fidl {
1089            <P::Interest<I> as MaybeInterest<_>>::into_fidl(validated)
1090        }
1091    }
1092
1093    pub struct ValidUntilInterest;
1094
1095    impl FieldSpec for ValidUntilInterest {
1096        type Interest<I: FieldInterests> = I::ValidUntil;
1097        type Fidl = zx_types::zx_time_t;
1098        type Present = PositiveMonotonicInstant;
1099        const FIELD_NAME: &'static str = "valid_until";
1100    }
1101
1102    pub struct PreferredLifetimeInfoInterest;
1103
1104    impl FieldSpec for PreferredLifetimeInfoInterest {
1105        type Interest<I: FieldInterests> = I::PreferredLifetimeInfo;
1106        type Fidl = fnet_interfaces::PreferredLifetimeInfo;
1107        type Present = PreferredLifetimeInfo;
1108        const FIELD_NAME: &'static str = "preferred_lifetime_info";
1109    }
1110}
1111pub use interest::{DisableInterest, EnableInterest, FieldInterests};
1112use interest::{
1113    FromInterest, InterestConverter, MaybeInterest, PreferredLifetimeInfoInterest,
1114    ValidUntilInterest,
1115};
1116
1117/// A marker for interest in all optional fields.
1118#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1119pub struct AllInterest;
1120impl FieldInterests for AllInterest {
1121    type PreferredLifetimeInfo = EnableInterest;
1122    type ValidUntil = EnableInterest;
1123}
1124
1125/// A marker for the default interest options as defined by the interfaces
1126/// watcher API.
1127#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1128pub struct DefaultInterest;
1129impl FieldInterests for DefaultInterest {
1130    type PreferredLifetimeInfo = DisableInterest;
1131    type ValidUntil = DisableInterest;
1132}
1133
1134/// An [`fnet_interfaces::Event`] tagged with the interest parameters that
1135/// created it.
1136#[derive(Derivative)]
1137#[derivative(Clone(bound = ""), Debug(bound = ""), Eq(bound = ""), PartialEq(bound = ""))]
1138pub struct EventWithInterest<I: FieldInterests> {
1139    event: fnet_interfaces::Event,
1140    #[derivative(Debug = "ignore")]
1141    _marker: PhantomData<I>,
1142}
1143
1144impl<I: FieldInterests> EventWithInterest<I> {
1145    /// Creates a new `EventWithInterest` with the provided event.
1146    ///
1147    /// Note that this type exists to steer proper usage of this crate. Creating
1148    /// `EventWithInterest` with arbitrary interests is potentially dangerous if
1149    /// the combination of field expectations don't match what was used to
1150    /// create the watcher.
1151    pub fn new(event: fnet_interfaces::Event) -> Self {
1152        Self { event, _marker: PhantomData }
1153    }
1154
1155    /// Retrieves the internal event.
1156    pub fn into_inner(self) -> fnet_interfaces::Event {
1157        self.event
1158    }
1159
1160    /// Borrows the internal event.
1161    pub fn inner(&self) -> &fnet_interfaces::Event {
1162        &self.event
1163    }
1164}
1165
1166impl<I: FieldInterests> From<fnet_interfaces::Event> for EventWithInterest<I> {
1167    fn from(value: fnet_interfaces::Event) -> Self {
1168        Self::new(value)
1169    }
1170}
1171
1172impl<I: FieldInterests> From<EventWithInterest<I>> for fnet_interfaces::Event {
1173    fn from(value: EventWithInterest<I>) -> Self {
1174        value.into_inner()
1175    }
1176}
1177
1178struct PortIdentityKoidConverter;
1179
1180impl From<Never> for PropertiesValidationError {
1181    fn from(value: Never) -> Self {
1182        match value {}
1183    }
1184}
1185
1186impl fidl_table_validation::Converter for PortIdentityKoidConverter {
1187    type Fidl = Option<u64>;
1188    type Validated = Option<PortIdentityKoid>;
1189    type Error = Never;
1190
1191    fn try_from_fidl(value: Self::Fidl) -> Result<Self::Validated, Self::Error> {
1192        Ok(value.map(|value| PortIdentityKoid::from_raw(value)))
1193    }
1194
1195    fn from_validated(validated: Self::Validated) -> Self::Fidl {
1196        validated.map(|validated| validated.raw_koid())
1197    }
1198}
1199
1200#[cfg(test)]
1201mod tests {
1202    use super::*;
1203    use assert_matches::assert_matches;
1204    use fidl_fuchsia_net as fnet;
1205    use futures::FutureExt as _;
1206    use futures::task::Poll;
1207    use net_declare::fidl_subnet;
1208    use std::cell::RefCell;
1209    use std::convert::TryInto as _;
1210    use std::pin::Pin;
1211    use std::rc::Rc;
1212    use test_case::test_case;
1213
1214    fn fidl_properties(id: u64) -> fnet_interfaces::Properties {
1215        fnet_interfaces::Properties {
1216            id: Some(id),
1217            name: Some("test1".to_string()),
1218            port_class: Some(fnet_interfaces::PortClass::Loopback(fnet_interfaces::Empty {})),
1219            online: Some(false),
1220            has_default_ipv4_route: Some(false),
1221            has_default_ipv6_route: Some(false),
1222            addresses: Some(vec![fidl_address(ADDR, zx_types::ZX_TIME_INFINITE)]),
1223            ..Default::default()
1224        }
1225    }
1226
1227    fn validated_properties(id: u64) -> PropertiesAndState<(), AllInterest> {
1228        PropertiesAndState {
1229            properties: fidl_properties(id).try_into().expect("failed to validate FIDL Properties"),
1230            state: (),
1231        }
1232    }
1233
1234    fn properties_delta(id: u64) -> fnet_interfaces::Properties {
1235        fnet_interfaces::Properties {
1236            id: Some(id),
1237            name: None,
1238            port_class: None,
1239            online: Some(true),
1240            has_default_ipv4_route: Some(true),
1241            has_default_ipv6_route: Some(true),
1242            addresses: Some(vec![fidl_address(ADDR2, zx_types::ZX_TIME_INFINITE)]),
1243            ..Default::default()
1244        }
1245    }
1246
1247    fn fidl_properties_after_change(id: u64) -> fnet_interfaces::Properties {
1248        fnet_interfaces::Properties {
1249            id: Some(id),
1250            name: Some("test1".to_string()),
1251            port_class: Some(fnet_interfaces::PortClass::Loopback(fnet_interfaces::Empty {})),
1252            online: Some(true),
1253            has_default_ipv4_route: Some(true),
1254            has_default_ipv6_route: Some(true),
1255            addresses: Some(vec![fidl_address(ADDR2, zx_types::ZX_TIME_INFINITE)]),
1256            ..Default::default()
1257        }
1258    }
1259
1260    fn validated_properties_after_change(id: u64) -> PropertiesAndState<(), AllInterest> {
1261        PropertiesAndState {
1262            properties: fidl_properties_after_change(id)
1263                .try_into()
1264                .expect("failed to validate FIDL Properties"),
1265            state: (),
1266        }
1267    }
1268
1269    fn fidl_address(
1270        addr: fnet::Subnet,
1271        valid_until: zx_types::zx_time_t,
1272    ) -> fnet_interfaces::Address {
1273        fnet_interfaces::Address {
1274            addr: Some(addr),
1275            valid_until: Some(valid_until.try_into().unwrap()),
1276            assignment_state: Some(fnet_interfaces::AddressAssignmentState::Assigned),
1277            preferred_lifetime_info: Some(PreferredLifetimeInfo::preferred_forever().into()),
1278            __source_breaking: Default::default(),
1279        }
1280    }
1281
1282    const ID: u64 = 1;
1283    const ID2: u64 = 2;
1284    const ADDR: fnet::Subnet = fidl_subnet!("1.2.3.4/24");
1285    const ADDR2: fnet::Subnet = fidl_subnet!("5.6.7.8/24");
1286
1287    #[test_case(
1288        &mut std::iter::once((ID, validated_properties(ID))).collect::<HashMap<_, _>>();
1289        "hashmap"
1290    )]
1291    #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1292    #[test_case(&mut validated_properties(ID); "properties")]
1293    fn test_duplicate_error(state: &mut impl Update<(), Interest = AllInterest>) {
1294        assert_matches::assert_matches!(
1295            state.update(fnet_interfaces::Event::Added(fidl_properties(ID)).into()),
1296            Err(UpdateError::DuplicateAdded(added)) if added == fidl_properties(ID)
1297        );
1298        assert_matches::assert_matches!(
1299            state.update(fnet_interfaces::Event::Existing(fidl_properties(ID)).into()),
1300            Err(UpdateError::DuplicateExisting(existing)) if existing == fidl_properties(ID)
1301        );
1302    }
1303
1304    #[test_case(&mut HashMap::<u64, _>::new(); "hashmap")]
1305    #[test_case(&mut InterfaceState::Unknown(ID); "interface_state_unknown")]
1306    fn test_unknown_error(state: &mut impl Update<(), Interest = AllInterest>) {
1307        let unknown =
1308            fnet_interfaces::Properties { id: Some(ID), online: Some(true), ..Default::default() };
1309        assert_matches::assert_matches!(
1310            state.update(fnet_interfaces::Event::Changed(unknown.clone()).into()),
1311            Err(UpdateError::UnknownChanged(changed)) if changed == unknown
1312        );
1313        assert_matches::assert_matches!(
1314            state.update(fnet_interfaces::Event::Removed(ID).into()),
1315            Err(UpdateError::UnknownRemoved(id)) if id == ID
1316        );
1317    }
1318
1319    #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1320    #[test_case(&mut validated_properties(ID); "properties")]
1321    fn test_removed_error(state: &mut impl Update<()>) {
1322        assert_matches::assert_matches!(
1323            state.update(fnet_interfaces::Event::Removed(ID).into()),
1324            Err(UpdateError::Removed)
1325        );
1326    }
1327
1328    #[test_case(&mut HashMap::<u64, _>::new(); "hashmap")]
1329    #[test_case(&mut InterfaceState::Unknown(ID); "interface_state_unknown")]
1330    #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1331    #[test_case(&mut validated_properties(ID); "properties")]
1332    fn test_missing_id_error(state: &mut impl Update<(), Interest = AllInterest>) {
1333        let missing_id = fnet_interfaces::Properties { online: Some(true), ..Default::default() };
1334        assert_matches::assert_matches!(
1335            state.update(fnet_interfaces::Event::Changed(missing_id.clone()).into()),
1336            Err(UpdateError::MissingId(properties)) if properties == missing_id
1337        );
1338    }
1339
1340    #[test_case(
1341        &mut std::iter::once((ID, validated_properties(ID))).collect::<HashMap<_, _>>();
1342        "hashmap"
1343    )]
1344    #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1345    #[test_case(&mut validated_properties(ID); "properties")]
1346    fn test_empty_change_error(state: &mut impl Update<()>) {
1347        let empty_change = fnet_interfaces::Properties { id: Some(ID), ..Default::default() };
1348        let net_zero_change =
1349            fnet_interfaces::Properties { name: None, port_class: None, ..fidl_properties(ID) };
1350        assert_matches::assert_matches!(
1351            state.update(fnet_interfaces::Event::Changed(empty_change.clone()).into()),
1352            Err(UpdateError::EmptyChange(properties)) if properties == empty_change
1353        );
1354        assert_matches::assert_matches!(
1355            state.update(fnet_interfaces::Event::Changed(net_zero_change.clone()).into()),
1356            Err(UpdateError::EmptyChange(properties)) if properties == net_zero_change
1357        );
1358    }
1359
1360    #[test_case(
1361        &mut std::iter::once((ID, validated_properties(ID))).collect::<HashMap<_, _>>();
1362        "hashmap"
1363    )]
1364    #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1365    #[test_case(&mut validated_properties(ID); "properties")]
1366    fn test_update_changed_result(state: &mut impl Update<(), Interest = AllInterest>) {
1367        let want_previous = fnet_interfaces::Properties {
1368            online: Some(false),
1369            has_default_ipv4_route: Some(false),
1370            has_default_ipv6_route: Some(false),
1371            addresses: Some(vec![fidl_address(ADDR, zx_types::ZX_TIME_INFINITE)]),
1372            ..Default::default()
1373        };
1374        assert_matches::assert_matches!(
1375            state.update(fnet_interfaces::Event::Changed(properties_delta(ID).clone()).into()),
1376            Ok(UpdateResult::Changed { previous, current, state: _ }) => {
1377                assert_eq!(previous, want_previous);
1378                let PropertiesAndState { properties, state: () } =
1379                    validated_properties_after_change(ID);
1380                assert_eq!(*current, properties);
1381            }
1382        );
1383    }
1384
1385    #[derive(Derivative)]
1386    #[derivative(Clone(bound = ""))]
1387    struct EventStream<I: FieldInterests>(Rc<RefCell<Vec<fnet_interfaces::Event>>>, PhantomData<I>);
1388
1389    impl<I: FieldInterests> Stream for EventStream<I> {
1390        type Item = Result<EventWithInterest<I>, fidl::Error>;
1391
1392        fn poll_next(
1393            self: Pin<&mut Self>,
1394            _cx: &mut futures::task::Context<'_>,
1395        ) -> Poll<Option<Self::Item>> {
1396            let EventStream(events_vec, _marker) = &*self;
1397            if events_vec.borrow().is_empty() {
1398                Poll::Ready(None)
1399            } else {
1400                Poll::Ready(Some(Ok(EventWithInterest::new(events_vec.borrow_mut().remove(0)))))
1401            }
1402        }
1403    }
1404
1405    fn test_event_stream<I: FieldInterests>() -> EventStream<I> {
1406        EventStream(
1407            Rc::new(RefCell::new(vec![
1408                fnet_interfaces::Event::Existing(fidl_properties(ID)),
1409                fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}),
1410                fnet_interfaces::Event::Added(fidl_properties(ID2)),
1411                fnet_interfaces::Event::Changed(properties_delta(ID)),
1412                fnet_interfaces::Event::Changed(properties_delta(ID2)),
1413                fnet_interfaces::Event::Removed(ID),
1414                fnet_interfaces::Event::Removed(ID2),
1415            ])),
1416            PhantomData,
1417        )
1418    }
1419
1420    #[test]
1421    fn test_wait_one_interface() {
1422        let event_stream = test_event_stream::<AllInterest>();
1423        let mut state = InterfaceState::Unknown(ID);
1424        for want in &[validated_properties(ID), validated_properties_after_change(ID)] {
1425            wait_interface_with_id(event_stream.clone(), &mut state, |got| {
1426                assert_eq!(got, want);
1427                Some(())
1428            })
1429            .now_or_never()
1430            .expect("wait_interface_with_id did not complete immediately")
1431            .expect("wait_interface_with_id error");
1432            assert_matches!(state, InterfaceState::Known(ref got) if got == want);
1433        }
1434    }
1435
1436    fn test_wait_interface<'a, B>(state: &mut B, want_states: impl IntoIterator<Item = &'a B>)
1437    where
1438        B: 'a + Update<()> + Clone + Debug + std::cmp::PartialEq,
1439    {
1440        let event_stream = test_event_stream::<B::Interest>();
1441        for want in want_states.into_iter() {
1442            wait_interface(event_stream.clone(), state, |got| {
1443                assert_eq!(got, want);
1444                Some(())
1445            })
1446            .now_or_never()
1447            .expect("wait_interface did not complete immediately")
1448            .expect("wait_interface error");
1449            assert_eq!(state, want);
1450        }
1451    }
1452
1453    #[test]
1454    fn test_wait_interface_hashmap() {
1455        test_wait_interface(
1456            &mut HashMap::new(),
1457            &[
1458                std::iter::once((ID, validated_properties(ID))).collect::<HashMap<_, _>>(),
1459                [(ID, validated_properties(ID)), (ID2, validated_properties(ID2))]
1460                    .iter()
1461                    .cloned()
1462                    .collect::<HashMap<_, _>>(),
1463                [(ID, validated_properties_after_change(ID)), (ID2, validated_properties(ID2))]
1464                    .iter()
1465                    .cloned()
1466                    .collect::<HashMap<_, _>>(),
1467                [
1468                    (ID, validated_properties_after_change(ID)),
1469                    (ID2, validated_properties_after_change(ID2)),
1470                ]
1471                .iter()
1472                .cloned()
1473                .collect::<HashMap<_, _>>(),
1474                std::iter::once((ID2, validated_properties_after_change(ID2)))
1475                    .collect::<HashMap<_, _>>(),
1476                HashMap::new(),
1477            ],
1478        );
1479    }
1480
1481    #[test]
1482    fn test_wait_interface_interface_state() {
1483        test_wait_interface(
1484            &mut InterfaceState::Unknown(ID),
1485            &[
1486                InterfaceState::Known(validated_properties(ID)),
1487                InterfaceState::Known(validated_properties_after_change(ID)),
1488            ],
1489        );
1490    }
1491
1492    const ID_NON_EXISTENT: u64 = 0xffffffff;
1493    #[test_case(
1494        InterfaceState::Unknown(ID_NON_EXISTENT),
1495        InterfaceState::Unknown(ID_NON_EXISTENT);
1496        "interface_state_unknown_different_id"
1497    )]
1498    #[test_case(
1499        InterfaceState::Unknown(ID),
1500        InterfaceState::Known(validated_properties(ID));
1501        "interface_state_unknown")]
1502    #[test_case(
1503        HashMap::new(),
1504        [(ID, validated_properties(ID)), (ID2, validated_properties(ID2))]
1505            .iter()
1506            .cloned()
1507            .collect::<HashMap<_, _>>();
1508        "hashmap"
1509    )]
1510    fn test_existing<B>(state: B, want: B)
1511    where
1512        B: Update<(), Interest = AllInterest> + Debug + std::cmp::PartialEq,
1513    {
1514        let events = [
1515            fnet_interfaces::Event::Existing(fidl_properties(ID)),
1516            fnet_interfaces::Event::Existing(fidl_properties(ID2)),
1517            fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}),
1518        ];
1519        let event_stream =
1520            futures::stream::iter(events.iter().cloned().map(|e| Ok(EventWithInterest::new(e))));
1521        assert_eq!(
1522            existing(event_stream, state)
1523                .now_or_never()
1524                .expect("existing did not complete immediately")
1525                .expect("existing returned error"),
1526            want,
1527        );
1528    }
1529
1530    #[test]
1531    fn positive_instant() {
1532        assert_eq!(PositiveMonotonicInstant::from_nanos(-1), None);
1533        assert_eq!(PositiveMonotonicInstant::from_nanos(0), None);
1534        assert_eq!(PositiveMonotonicInstant::from_nanos(1), Some(PositiveMonotonicInstant(1)));
1535    }
1536}