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