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.
902#[derive(Default)]
903pub enum IncludedAddresses {
904    /// All addresses are returned from the watcher.
905    All,
906    /// Only assigned addresses are returned rom the watcher.
907    #[default]
908    OnlyAssigned,
909}
910
911/// The options given to [`event_stream_from_state`] for interface state
912/// observation.
913#[derive(Default)]
914pub struct WatchOptions {
915    /// The addresses returned from the watcher.
916    pub included_addresses: IncludedAddresses,
917    /// The port identity to filter on, if any.
918    pub port_identity_koid_filter: Option<PortIdentityKoid>,
919}
920
921/// Initialize a watcher with interest in all fields and return its events as a
922/// stream.
923///
924/// If `included_addresses` is `All`, then all addresses will be returned, not
925/// just assigned addresses.
926pub fn event_stream_from_state<I: FieldInterests>(
927    interface_state: &fnet_interfaces::StateProxy,
928    options: WatchOptions,
929) -> Result<
930    impl Stream<Item = Result<EventWithInterest<I>, fidl::Error>> + use<I>,
931    WatcherCreationError,
932> {
933    let (watcher, server) = ::fidl::endpoints::create_proxy::<fnet_interfaces::WatcherMarker>();
934    let WatchOptions { included_addresses, port_identity_koid_filter } = options;
935    let () = interface_state
936        .get_watcher(
937            &fnet_interfaces::WatcherOptions {
938                address_properties_interest: Some(interest_from_params::<I>()),
939                include_non_assigned_addresses: Some(match included_addresses {
940                    IncludedAddresses::All => true,
941                    IncludedAddresses::OnlyAssigned => false,
942                }),
943                port_identity_koid_filter: port_identity_koid_filter.map(|p| p.raw_koid()),
944                ..Default::default()
945            },
946            server,
947        )
948        .map_err(WatcherCreationError::GetWatcher)?;
949    Ok(futures::stream::try_unfold(watcher, |watcher| async {
950        Ok(Some((EventWithInterest::new(watcher.watch().await?), watcher)))
951    }))
952}
953
954fn interest_from_params<I: FieldInterests>() -> fnet_interfaces::AddressPropertiesInterest {
955    let mut interest = fnet_interfaces::AddressPropertiesInterest::empty();
956    if <I::ValidUntil as MaybeInterest<_>>::ENABLED {
957        interest |= fnet_interfaces::AddressPropertiesInterest::VALID_UNTIL;
958    }
959    if <I::PreferredLifetimeInfo as MaybeInterest<_>>::ENABLED {
960        interest |= fnet_interfaces::AddressPropertiesInterest::PREFERRED_LIFETIME_INFO;
961    }
962    interest
963}
964
965/// A marker for a field that didn't register interest with the watcher.
966#[derive(Clone, Debug, Eq, Hash, PartialEq, Default)]
967pub struct NoInterest;
968
969mod interest {
970    use super::*;
971
972    use Debug;
973    use std::hash::Hash;
974
975    /// A trait that parameterizes interest in fields from interfaces watcher.
976    ///
977    /// Use [`EnableInterest`] or [`DisableInterest`] in each type to
978    /// enable/disable interest in receiving those fields from the server,
979    /// respectively.
980    pub trait FieldInterests {
981        /// Interest in the `preferred_lifetime_info` field.
982        type PreferredLifetimeInfo: MaybeInterest<PreferredLifetimeInfo>;
983        /// Interest in the `valid_until` field.
984        type ValidUntil: MaybeInterest<PositiveMonotonicInstant>;
985    }
986
987    /// Helper trait to implement conversion with optional field interest.
988    pub trait MaybeInterest<T> {
989        /// Whether this is an enabled interest.
990        const ENABLED: bool;
991
992        /// The actual type carried by the validated struct.
993        type Ty: Clone + Debug + Eq + Hash + PartialEq + 'static;
994
995        /// Converts from an optional FIDL input to the target type `Self::Ty`.
996        fn try_from_fidl<F: TryInto<T, Error: Into<anyhow::Error>>>(
997            fidl: Option<F>,
998        ) -> Result<Self::Ty, anyhow::Error>;
999
1000        /// Converts from the target type `Self::Ty` into an optional FIDL
1001        /// value.
1002        fn into_fidl<F: From<T>>(value: Self::Ty) -> Option<F>;
1003    }
1004
1005    /// Enabled interest in a FIDL field.
1006    ///
1007    /// Use as a type parameter in [`FieldInterests`].
1008    pub struct EnableInterest;
1009
1010    impl<T: Clone + Debug + Eq + Hash + PartialEq + 'static> MaybeInterest<T> for EnableInterest {
1011        const ENABLED: bool = true;
1012        type Ty = T;
1013
1014        fn try_from_fidl<F: TryInto<T, Error: Into<anyhow::Error>>>(
1015            fidl: Option<F>,
1016        ) -> Result<Self::Ty, anyhow::Error> {
1017            fidl.map(|f| f.try_into().map_err(Into::into))
1018                .unwrap_or_else(|| Err(anyhow::anyhow!("missing field with registered interest")))
1019        }
1020
1021        fn into_fidl<F: From<T>>(value: Self::Ty) -> Option<F> {
1022            Some(value.into())
1023        }
1024    }
1025
1026    /// Disabled interest in a FIDL field.
1027    ///
1028    /// Use as a type parameter in [`FieldInterests`].
1029    pub struct DisableInterest;
1030    impl<T> MaybeInterest<T> for DisableInterest {
1031        const ENABLED: bool = false;
1032
1033        type Ty = NoInterest;
1034
1035        fn try_from_fidl<F: TryInto<T, Error: Into<anyhow::Error>>>(
1036            fidl: Option<F>,
1037        ) -> Result<Self::Ty, anyhow::Error> {
1038            match fidl {
1039                Some(_) => Err(anyhow::anyhow!("unexpected set field with no registered interest")),
1040                None => Ok(NoInterest),
1041            }
1042        }
1043
1044        fn into_fidl<F: From<T>>(_value: Self::Ty) -> Option<F> {
1045            None
1046        }
1047    }
1048
1049    /// A handy alias to shorten the signature of a type derived from
1050    /// [`MaybeInterest`] based on [`FieldSpec`].
1051    pub(super) type FromInterest<I, T> =
1052        <<T as FieldSpec>::Interest<I> as MaybeInterest<<T as FieldSpec>::Present>>::Ty;
1053
1054    /// Parameterizes interest fields.
1055    ///
1056    /// This trait allows a common converter implementation for the FIDL table
1057    /// validation structure and unifies the schema of how interest fields
1058    /// behave.
1059    pub trait FieldSpec {
1060        /// Extracts the interest type from [`FieldInterests`].
1061        type Interest<I: FieldInterests>: MaybeInterest<Self::Present>;
1062
1063        /// The FIDL representation of the field.
1064        type Fidl: From<Self::Present>;
1065
1066        /// The validated representation of the field when interest is
1067        /// expressed.
1068        type Present: TryFrom<Self::Fidl, Error: Into<anyhow::Error>>;
1069
1070        /// The field name in the originating struct. This helps generate better
1071        /// error messages.
1072        const FIELD_NAME: &'static str;
1073    }
1074
1075    pub struct InterestConverter<I, P>(PhantomData<(I, P)>);
1076
1077    impl<I, P> fidl_table_validation::Converter for InterestConverter<I, P>
1078    where
1079        I: FieldInterests,
1080        P: FieldSpec,
1081    {
1082        type Fidl = Option<P::Fidl>;
1083        type Validated = <P::Interest<I> as MaybeInterest<P::Present>>::Ty;
1084        type Error = anyhow::Error;
1085
1086        fn try_from_fidl(value: Self::Fidl) -> std::result::Result<Self::Validated, Self::Error> {
1087            <P::Interest<I> as MaybeInterest<_>>::try_from_fidl(value).context(P::FIELD_NAME)
1088        }
1089
1090        fn from_validated(validated: Self::Validated) -> Self::Fidl {
1091            <P::Interest<I> as MaybeInterest<_>>::into_fidl(validated)
1092        }
1093    }
1094
1095    pub struct ValidUntilInterest;
1096
1097    impl FieldSpec for ValidUntilInterest {
1098        type Interest<I: FieldInterests> = I::ValidUntil;
1099        type Fidl = zx_types::zx_time_t;
1100        type Present = PositiveMonotonicInstant;
1101        const FIELD_NAME: &'static str = "valid_until";
1102    }
1103
1104    pub struct PreferredLifetimeInfoInterest;
1105
1106    impl FieldSpec for PreferredLifetimeInfoInterest {
1107        type Interest<I: FieldInterests> = I::PreferredLifetimeInfo;
1108        type Fidl = fnet_interfaces::PreferredLifetimeInfo;
1109        type Present = PreferredLifetimeInfo;
1110        const FIELD_NAME: &'static str = "preferred_lifetime_info";
1111    }
1112}
1113pub use interest::{DisableInterest, EnableInterest, FieldInterests};
1114use interest::{
1115    FromInterest, InterestConverter, MaybeInterest, PreferredLifetimeInfoInterest,
1116    ValidUntilInterest,
1117};
1118
1119/// A marker for interest in all optional fields.
1120#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1121pub struct AllInterest;
1122impl FieldInterests for AllInterest {
1123    type PreferredLifetimeInfo = EnableInterest;
1124    type ValidUntil = EnableInterest;
1125}
1126
1127/// A marker for the default interest options as defined by the interfaces
1128/// watcher API.
1129#[derive(Clone, Debug, Eq, Hash, PartialEq)]
1130pub struct DefaultInterest;
1131impl FieldInterests for DefaultInterest {
1132    type PreferredLifetimeInfo = DisableInterest;
1133    type ValidUntil = DisableInterest;
1134}
1135
1136/// An [`fnet_interfaces::Event`] tagged with the interest parameters that
1137/// created it.
1138#[derive(Derivative)]
1139#[derivative(Clone(bound = ""), Debug(bound = ""), Eq(bound = ""), PartialEq(bound = ""))]
1140pub struct EventWithInterest<I: FieldInterests> {
1141    event: fnet_interfaces::Event,
1142    #[derivative(Debug = "ignore")]
1143    _marker: PhantomData<I>,
1144}
1145
1146impl<I: FieldInterests> EventWithInterest<I> {
1147    /// Creates a new `EventWithInterest` with the provided event.
1148    ///
1149    /// Note that this type exists to steer proper usage of this crate. Creating
1150    /// `EventWithInterest` with arbitrary interests is potentially dangerous if
1151    /// the combination of field expectations don't match what was used to
1152    /// create the watcher.
1153    pub fn new(event: fnet_interfaces::Event) -> Self {
1154        Self { event, _marker: PhantomData }
1155    }
1156
1157    /// Retrieves the internal event.
1158    pub fn into_inner(self) -> fnet_interfaces::Event {
1159        self.event
1160    }
1161
1162    /// Borrows the internal event.
1163    pub fn inner(&self) -> &fnet_interfaces::Event {
1164        &self.event
1165    }
1166}
1167
1168impl<I: FieldInterests> From<fnet_interfaces::Event> for EventWithInterest<I> {
1169    fn from(value: fnet_interfaces::Event) -> Self {
1170        Self::new(value)
1171    }
1172}
1173
1174impl<I: FieldInterests> From<EventWithInterest<I>> for fnet_interfaces::Event {
1175    fn from(value: EventWithInterest<I>) -> Self {
1176        value.into_inner()
1177    }
1178}
1179
1180struct PortIdentityKoidConverter;
1181
1182impl From<Never> for PropertiesValidationError {
1183    fn from(value: Never) -> Self {
1184        match value {}
1185    }
1186}
1187
1188impl fidl_table_validation::Converter for PortIdentityKoidConverter {
1189    type Fidl = Option<u64>;
1190    type Validated = Option<PortIdentityKoid>;
1191    type Error = Never;
1192
1193    fn try_from_fidl(value: Self::Fidl) -> Result<Self::Validated, Self::Error> {
1194        Ok(value.map(|value| PortIdentityKoid::from_raw(value)))
1195    }
1196
1197    fn from_validated(validated: Self::Validated) -> Self::Fidl {
1198        validated.map(|validated| validated.raw_koid())
1199    }
1200}
1201
1202#[cfg(test)]
1203mod tests {
1204    use super::*;
1205    use assert_matches::assert_matches;
1206    use fidl_fuchsia_net as fnet;
1207    use futures::FutureExt as _;
1208    use futures::task::Poll;
1209    use net_declare::fidl_subnet;
1210    use std::cell::RefCell;
1211    use std::convert::TryInto as _;
1212    use std::pin::Pin;
1213    use std::rc::Rc;
1214    use test_case::test_case;
1215
1216    fn fidl_properties(id: u64) -> fnet_interfaces::Properties {
1217        fnet_interfaces::Properties {
1218            id: Some(id),
1219            name: Some("test1".to_string()),
1220            port_class: Some(fnet_interfaces::PortClass::Loopback(fnet_interfaces::Empty {})),
1221            online: Some(false),
1222            has_default_ipv4_route: Some(false),
1223            has_default_ipv6_route: Some(false),
1224            addresses: Some(vec![fidl_address(ADDR, zx_types::ZX_TIME_INFINITE)]),
1225            ..Default::default()
1226        }
1227    }
1228
1229    fn validated_properties(id: u64) -> PropertiesAndState<(), AllInterest> {
1230        PropertiesAndState {
1231            properties: fidl_properties(id).try_into().expect("failed to validate FIDL Properties"),
1232            state: (),
1233        }
1234    }
1235
1236    fn properties_delta(id: u64) -> fnet_interfaces::Properties {
1237        fnet_interfaces::Properties {
1238            id: Some(id),
1239            name: None,
1240            port_class: None,
1241            online: Some(true),
1242            has_default_ipv4_route: Some(true),
1243            has_default_ipv6_route: Some(true),
1244            addresses: Some(vec![fidl_address(ADDR2, zx_types::ZX_TIME_INFINITE)]),
1245            ..Default::default()
1246        }
1247    }
1248
1249    fn fidl_properties_after_change(id: u64) -> fnet_interfaces::Properties {
1250        fnet_interfaces::Properties {
1251            id: Some(id),
1252            name: Some("test1".to_string()),
1253            port_class: Some(fnet_interfaces::PortClass::Loopback(fnet_interfaces::Empty {})),
1254            online: Some(true),
1255            has_default_ipv4_route: Some(true),
1256            has_default_ipv6_route: Some(true),
1257            addresses: Some(vec![fidl_address(ADDR2, zx_types::ZX_TIME_INFINITE)]),
1258            ..Default::default()
1259        }
1260    }
1261
1262    fn validated_properties_after_change(id: u64) -> PropertiesAndState<(), AllInterest> {
1263        PropertiesAndState {
1264            properties: fidl_properties_after_change(id)
1265                .try_into()
1266                .expect("failed to validate FIDL Properties"),
1267            state: (),
1268        }
1269    }
1270
1271    fn fidl_address(
1272        addr: fnet::Subnet,
1273        valid_until: zx_types::zx_time_t,
1274    ) -> fnet_interfaces::Address {
1275        fnet_interfaces::Address {
1276            addr: Some(addr),
1277            valid_until: Some(valid_until.try_into().unwrap()),
1278            assignment_state: Some(fnet_interfaces::AddressAssignmentState::Assigned),
1279            preferred_lifetime_info: Some(PreferredLifetimeInfo::preferred_forever().into()),
1280            __source_breaking: Default::default(),
1281        }
1282    }
1283
1284    const ID: u64 = 1;
1285    const ID2: u64 = 2;
1286    const ADDR: fnet::Subnet = fidl_subnet!("1.2.3.4/24");
1287    const ADDR2: fnet::Subnet = fidl_subnet!("5.6.7.8/24");
1288
1289    #[test_case(
1290        &mut std::iter::once((ID, validated_properties(ID))).collect::<HashMap<_, _>>();
1291        "hashmap"
1292    )]
1293    #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1294    #[test_case(&mut validated_properties(ID); "properties")]
1295    fn test_duplicate_error(state: &mut impl Update<(), Interest = AllInterest>) {
1296        assert_matches::assert_matches!(
1297            state.update(fnet_interfaces::Event::Added(fidl_properties(ID)).into()),
1298            Err(UpdateError::DuplicateAdded(added)) if added == fidl_properties(ID)
1299        );
1300        assert_matches::assert_matches!(
1301            state.update(fnet_interfaces::Event::Existing(fidl_properties(ID)).into()),
1302            Err(UpdateError::DuplicateExisting(existing)) if existing == fidl_properties(ID)
1303        );
1304    }
1305
1306    #[test_case(&mut HashMap::<u64, _>::new(); "hashmap")]
1307    #[test_case(&mut InterfaceState::Unknown(ID); "interface_state_unknown")]
1308    fn test_unknown_error(state: &mut impl Update<(), Interest = AllInterest>) {
1309        let unknown =
1310            fnet_interfaces::Properties { id: Some(ID), online: Some(true), ..Default::default() };
1311        assert_matches::assert_matches!(
1312            state.update(fnet_interfaces::Event::Changed(unknown.clone()).into()),
1313            Err(UpdateError::UnknownChanged(changed)) if changed == unknown
1314        );
1315        assert_matches::assert_matches!(
1316            state.update(fnet_interfaces::Event::Removed(ID).into()),
1317            Err(UpdateError::UnknownRemoved(id)) if id == ID
1318        );
1319    }
1320
1321    #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1322    #[test_case(&mut validated_properties(ID); "properties")]
1323    fn test_removed_error(state: &mut impl Update<()>) {
1324        assert_matches::assert_matches!(
1325            state.update(fnet_interfaces::Event::Removed(ID).into()),
1326            Err(UpdateError::Removed)
1327        );
1328    }
1329
1330    #[test_case(&mut HashMap::<u64, _>::new(); "hashmap")]
1331    #[test_case(&mut InterfaceState::Unknown(ID); "interface_state_unknown")]
1332    #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1333    #[test_case(&mut validated_properties(ID); "properties")]
1334    fn test_missing_id_error(state: &mut impl Update<(), Interest = AllInterest>) {
1335        let missing_id = fnet_interfaces::Properties { online: Some(true), ..Default::default() };
1336        assert_matches::assert_matches!(
1337            state.update(fnet_interfaces::Event::Changed(missing_id.clone()).into()),
1338            Err(UpdateError::MissingId(properties)) if properties == missing_id
1339        );
1340    }
1341
1342    #[test_case(
1343        &mut std::iter::once((ID, validated_properties(ID))).collect::<HashMap<_, _>>();
1344        "hashmap"
1345    )]
1346    #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1347    #[test_case(&mut validated_properties(ID); "properties")]
1348    fn test_empty_change_error(state: &mut impl Update<()>) {
1349        let empty_change = fnet_interfaces::Properties { id: Some(ID), ..Default::default() };
1350        let net_zero_change =
1351            fnet_interfaces::Properties { name: None, port_class: None, ..fidl_properties(ID) };
1352        assert_matches::assert_matches!(
1353            state.update(fnet_interfaces::Event::Changed(empty_change.clone()).into()),
1354            Err(UpdateError::EmptyChange(properties)) if properties == empty_change
1355        );
1356        assert_matches::assert_matches!(
1357            state.update(fnet_interfaces::Event::Changed(net_zero_change.clone()).into()),
1358            Err(UpdateError::EmptyChange(properties)) if properties == net_zero_change
1359        );
1360    }
1361
1362    #[test_case(
1363        &mut std::iter::once((ID, validated_properties(ID))).collect::<HashMap<_, _>>();
1364        "hashmap"
1365    )]
1366    #[test_case(&mut InterfaceState::Known(validated_properties(ID)); "interface_state_known")]
1367    #[test_case(&mut validated_properties(ID); "properties")]
1368    fn test_update_changed_result(state: &mut impl Update<(), Interest = AllInterest>) {
1369        let want_previous = fnet_interfaces::Properties {
1370            online: Some(false),
1371            has_default_ipv4_route: Some(false),
1372            has_default_ipv6_route: Some(false),
1373            addresses: Some(vec![fidl_address(ADDR, zx_types::ZX_TIME_INFINITE)]),
1374            ..Default::default()
1375        };
1376        assert_matches::assert_matches!(
1377            state.update(fnet_interfaces::Event::Changed(properties_delta(ID).clone()).into()),
1378            Ok(UpdateResult::Changed { previous, current, state: _ }) => {
1379                assert_eq!(previous, want_previous);
1380                let PropertiesAndState { properties, state: () } =
1381                    validated_properties_after_change(ID);
1382                assert_eq!(*current, properties);
1383            }
1384        );
1385    }
1386
1387    #[derive(Derivative)]
1388    #[derivative(Clone(bound = ""))]
1389    struct EventStream<I: FieldInterests>(Rc<RefCell<Vec<fnet_interfaces::Event>>>, PhantomData<I>);
1390
1391    impl<I: FieldInterests> Stream for EventStream<I> {
1392        type Item = Result<EventWithInterest<I>, fidl::Error>;
1393
1394        fn poll_next(
1395            self: Pin<&mut Self>,
1396            _cx: &mut futures::task::Context<'_>,
1397        ) -> Poll<Option<Self::Item>> {
1398            let EventStream(events_vec, _marker) = &*self;
1399            if events_vec.borrow().is_empty() {
1400                Poll::Ready(None)
1401            } else {
1402                Poll::Ready(Some(Ok(EventWithInterest::new(events_vec.borrow_mut().remove(0)))))
1403            }
1404        }
1405    }
1406
1407    fn test_event_stream<I: FieldInterests>() -> EventStream<I> {
1408        EventStream(
1409            Rc::new(RefCell::new(vec![
1410                fnet_interfaces::Event::Existing(fidl_properties(ID)),
1411                fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}),
1412                fnet_interfaces::Event::Added(fidl_properties(ID2)),
1413                fnet_interfaces::Event::Changed(properties_delta(ID)),
1414                fnet_interfaces::Event::Changed(properties_delta(ID2)),
1415                fnet_interfaces::Event::Removed(ID),
1416                fnet_interfaces::Event::Removed(ID2),
1417            ])),
1418            PhantomData,
1419        )
1420    }
1421
1422    #[test]
1423    fn test_wait_one_interface() {
1424        let event_stream = test_event_stream::<AllInterest>();
1425        let mut state = InterfaceState::Unknown(ID);
1426        for want in &[validated_properties(ID), validated_properties_after_change(ID)] {
1427            let () = wait_interface_with_id(event_stream.clone(), &mut state, |got| {
1428                assert_eq!(got, want);
1429                Some(())
1430            })
1431            .now_or_never()
1432            .expect("wait_interface_with_id did not complete immediately")
1433            .expect("wait_interface_with_id error");
1434            assert_matches!(state, InterfaceState::Known(ref got) if got == want);
1435        }
1436    }
1437
1438    fn test_wait_interface<'a, B>(state: &mut B, want_states: impl IntoIterator<Item = &'a B>)
1439    where
1440        B: 'a + Update<()> + Clone + Debug + std::cmp::PartialEq,
1441    {
1442        let event_stream = test_event_stream::<B::Interest>();
1443        for want in want_states.into_iter() {
1444            let () = wait_interface(event_stream.clone(), state, |got| {
1445                assert_eq!(got, want);
1446                Some(())
1447            })
1448            .now_or_never()
1449            .expect("wait_interface did not complete immediately")
1450            .expect("wait_interface error");
1451            assert_eq!(state, want);
1452        }
1453    }
1454
1455    #[test]
1456    fn test_wait_interface_hashmap() {
1457        test_wait_interface(
1458            &mut HashMap::new(),
1459            &[
1460                std::iter::once((ID, validated_properties(ID))).collect::<HashMap<_, _>>(),
1461                [(ID, validated_properties(ID)), (ID2, validated_properties(ID2))]
1462                    .iter()
1463                    .cloned()
1464                    .collect::<HashMap<_, _>>(),
1465                [(ID, validated_properties_after_change(ID)), (ID2, validated_properties(ID2))]
1466                    .iter()
1467                    .cloned()
1468                    .collect::<HashMap<_, _>>(),
1469                [
1470                    (ID, validated_properties_after_change(ID)),
1471                    (ID2, validated_properties_after_change(ID2)),
1472                ]
1473                .iter()
1474                .cloned()
1475                .collect::<HashMap<_, _>>(),
1476                std::iter::once((ID2, validated_properties_after_change(ID2)))
1477                    .collect::<HashMap<_, _>>(),
1478                HashMap::new(),
1479            ],
1480        );
1481    }
1482
1483    #[test]
1484    fn test_wait_interface_interface_state() {
1485        test_wait_interface(
1486            &mut InterfaceState::Unknown(ID),
1487            &[
1488                InterfaceState::Known(validated_properties(ID)),
1489                InterfaceState::Known(validated_properties_after_change(ID)),
1490            ],
1491        );
1492    }
1493
1494    const ID_NON_EXISTENT: u64 = 0xffffffff;
1495    #[test_case(
1496        InterfaceState::Unknown(ID_NON_EXISTENT),
1497        InterfaceState::Unknown(ID_NON_EXISTENT);
1498        "interface_state_unknown_different_id"
1499    )]
1500    #[test_case(
1501        InterfaceState::Unknown(ID),
1502        InterfaceState::Known(validated_properties(ID));
1503        "interface_state_unknown")]
1504    #[test_case(
1505        HashMap::new(),
1506        [(ID, validated_properties(ID)), (ID2, validated_properties(ID2))]
1507            .iter()
1508            .cloned()
1509            .collect::<HashMap<_, _>>();
1510        "hashmap"
1511    )]
1512    fn test_existing<B>(state: B, want: B)
1513    where
1514        B: Update<(), Interest = AllInterest> + Debug + std::cmp::PartialEq,
1515    {
1516        let events = [
1517            fnet_interfaces::Event::Existing(fidl_properties(ID)),
1518            fnet_interfaces::Event::Existing(fidl_properties(ID2)),
1519            fnet_interfaces::Event::Idle(fnet_interfaces::Empty {}),
1520        ];
1521        let event_stream =
1522            futures::stream::iter(events.iter().cloned().map(|e| Ok(EventWithInterest::new(e))));
1523        assert_eq!(
1524            existing(event_stream, state)
1525                .now_or_never()
1526                .expect("existing did not complete immediately")
1527                .expect("existing returned error"),
1528            want,
1529        );
1530    }
1531
1532    #[test]
1533    fn positive_instant() {
1534        assert_eq!(PositiveMonotonicInstant::from_nanos(-1), None);
1535        assert_eq!(PositiveMonotonicInstant::from_nanos(0), None);
1536        assert_eq!(PositiveMonotonicInstant::from_nanos(1), Some(PositiveMonotonicInstant(1)));
1537    }
1538}