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