1// Copyright 2024 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.
45use cm_types::Availability;
6use moniker::ExtendedMoniker;
7use router_error::Explain;
8use thiserror::Error;
9use zx_status as zx;
1011/// Ensure that availability cannot decrease from target to source.
12pub fn advance(
13 moniker: &ExtendedMoniker,
14 current: Availability,
15 next: Availability,
16) -> Result<Availability, TargetHasStrongerAvailability> {
17match (current, next) {
18// `self` will be `SameAsTarget` when routing starts from an `Offer` or `Expose`. This
19 // is to verify as much as possible the correctness of routes involving `Offer` and
20 // `Expose` without full knowledge of the `use -> offer -> expose` chain.
21 //
22 // For the purpose of availability checking, we will skip any checks until we encounter
23 // a route declaration that has a known availability.
24(Availability::SameAsTarget, _) => Ok(next),
2526// If our availability doesn't change, there's nothing to do.
27(Availability::Required, Availability::Required)
28 | (Availability::Optional, Availability::Optional)
29 | (Availability::Transitional, Availability::Transitional)
3031// If the next availability is explicitly a pass-through, there's nothing to do.
32| (Availability::Required, Availability::SameAsTarget)
33 | (Availability::Optional, Availability::SameAsTarget)
34 | (Availability::Transitional, Availability::SameAsTarget) => Ok(current),
3536// Increasing the strength of availability as we travel toward the source is allowed.
37(Availability::Optional, Availability::Required)
38 | (Availability::Transitional, Availability::Required)
39 | (Availability::Transitional, Availability::Optional) =>
40Ok(next),
4142// Decreasing the strength of availability is not allowed, as that could lead to
43 // unsanctioned broken routes.
44(Availability::Optional, Availability::Transitional)
45 | (Availability::Required, Availability::Transitional)
46 | (Availability::Required, Availability::Optional) =>
47Err(TargetHasStrongerAvailability { moniker: moniker.clone() }),
48 }
49}
5051/// Availability requested by the target has stronger guarantees than what
52/// is being provided at the source.
53#[derive(Debug, Error, Clone, PartialEq)]
54#[error(
55"Availability requested by the target has stronger guarantees than what \
56 is being provided at the source."
57)]
58pub struct TargetHasStrongerAvailability {
59pub moniker: ExtendedMoniker,
60}
6162impl Explain for TargetHasStrongerAvailability {
63fn as_zx_status(&self) -> zx::Status {
64 zx::Status::NOT_FOUND
65 }
66}
6768#[cfg(test)]
69mod tests {
70use super::*;
71use test_case::test_case;
7273#[test_case(Availability::Optional, Availability::Optional, Ok(Availability::Optional))]
74 #[test_case(Availability::Optional, Availability::Required, Ok(Availability::Required))]
75 #[test_case(Availability::Optional, Availability::SameAsTarget, Ok(Availability::Optional))]
76 #[test_case(
77 Availability::Optional,
78 Availability::Transitional,
79Err(TargetHasStrongerAvailability { moniker: ExtendedMoniker::ComponentManager })
80 )]
81 #[test_case(Availability::Required, Availability::Optional, Err(TargetHasStrongerAvailability { moniker: ExtendedMoniker::ComponentManager }))]
82 #[test_case(Availability::Required, Availability::Required, Ok(Availability::Required))]
83 #[test_case(Availability::Required, Availability::SameAsTarget, Ok(Availability::Required))]
84 #[test_case(
85 Availability::Required,
86 Availability::Transitional,
87Err(TargetHasStrongerAvailability { moniker: ExtendedMoniker::ComponentManager })
88 )]
89 #[test_case(Availability::Transitional, Availability::Optional, Ok(Availability::Optional))]
90 #[test_case(Availability::Transitional, Availability::Required, Ok(Availability::Required))]
91 #[test_case(
92 Availability::Transitional,
93 Availability::SameAsTarget,
94Ok(Availability::Transitional)
95 )]
96 #[test_case(
97 Availability::Transitional,
98 Availability::Transitional,
99Ok(Availability::Transitional)
100 )]
101fn advance_tests(
102 current: Availability,
103 next: Availability,
104 expected: Result<Availability, TargetHasStrongerAvailability>,
105 ) {
106let actual = advance(&ExtendedMoniker::ComponentManager, current, next);
107assert_eq!(actual, expected);
108 }
109}