routing/bedrock/
request_metadata.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// Copyright 2024 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use crate::rights::Rights;
use cm_rust::Availability;
use sandbox::{Capability, Data, Dict, DictKey};
use {fidl_fuchsia_component_sandbox as fsandbox, fidl_fuchsia_io as fio};

/// A route request metadata key for the capability type.
pub const METADATA_KEY_TYPE: &'static str = "type";

/// The capability type value for a protocol.
pub const TYPE_PROTOCOL: &'static str = "protocol";
pub const TYPE_DICTIONARY: &'static str = "dictionary";
pub const TYPE_CONFIG: &'static str = "configuration";

/// A type which has accessors for route request metadata of type T.
pub trait Metadata<T> {
    /// A key string used for setting and getting the metadata.
    const KEY: &'static str;

    /// Infallibly assigns `value` to `self`.
    fn set_metadata(&self, value: T);

    /// Retrieves the subdir metadata from `self`, if present.
    fn get_metadata(&self) -> Option<T>;
}

impl Metadata<Availability> for Dict {
    const KEY: &'static str = "availability";

    fn set_metadata(&self, value: Availability) {
        let key = DictKey::new(<Self as Metadata<Availability>>::KEY)
            .expect("dict key creation failed unexpectedly");
        match self.insert(key, Capability::Data(Data::String(value.to_string()))) {
            // When an entry already exists for a key in a Dict, insert() will
            // still replace that entry with the new value, even though it
            // returns an ItemAlreadyExists error. As a result, we can treat
            // ItemAlreadyExists as a success case.
            Ok(()) | Err(fsandbox::CapabilityStoreError::ItemAlreadyExists) => (),
            // Dict::insert() only returns `CapabilityStoreError::ItemAlreadyExists` variant
            Err(e) => panic!("unexpected error variant returned from Dict::insert(): {e:?}"),
        }
    }

    fn get_metadata(&self) -> Option<Availability> {
        let key = DictKey::new(<Self as Metadata<Availability>>::KEY)
            .expect("dict key creation failed unexpectedly");
        let capability = self.get(&key).ok()??;
        match capability {
            Capability::Data(Data::String(availability)) => match availability.as_str() {
                "Optional" => Some(Availability::Optional),
                "Required" => Some(Availability::Required),
                "SameAsTarget" => Some(Availability::SameAsTarget),
                "Transitional" => Some(Availability::Transitional),
                _ => None,
            },
            _ => None,
        }
    }
}

impl Metadata<Rights> for Dict {
    const KEY: &'static str = "rights";

    fn set_metadata(&self, value: Rights) {
        let key = DictKey::new(<Self as Metadata<Rights>>::KEY)
            .expect("dict key creation failed unexpectedly");
        match self.insert(key, Capability::Data(Data::Uint64(value.into()))) {
            // When an entry already exists for a key in a Dict, insert() will
            // still replace that entry with the new value, even though it
            // returns an ItemAlreadyExists error. As a result, we can treat
            // ItemAlreadyExists as a success case.
            Ok(()) | Err(fsandbox::CapabilityStoreError::ItemAlreadyExists) => (),
            // Dict::insert() only returns `CapabilityStoreError::ItemAlreadyExists` variant
            Err(e) => panic!("unexpected error variant returned from Dict::insert(): {e:?}"),
        }
    }

    fn get_metadata(&self) -> Option<Rights> {
        let key = DictKey::new(<Self as Metadata<Rights>>::KEY)
            .expect("dict key creation failed unexpectedly");
        let capability = self.get(&key).ok()??;
        let rights = match capability {
            Capability::Data(Data::Uint64(rights)) => fio::Operations::from_bits(rights)?,
            _ => None?,
        };
        Some(Rights::from(rights))
    }
}

/// Returns a `Dict` containing Router Request metadata specifying a Protocol porcelain type.
pub fn protocol_metadata(availability: cm_types::Availability) -> sandbox::Dict {
    let metadata = sandbox::Dict::new();
    metadata
        .insert(
            cm_types::Name::new(METADATA_KEY_TYPE).unwrap(),
            sandbox::Capability::Data(sandbox::Data::String(String::from(TYPE_PROTOCOL))),
        )
        .unwrap();
    metadata.set_metadata(availability);
    metadata
}

/// Returns a `Dict` containing Router Request metadata specifying a Dictionary porcelain type.
pub fn dictionary_metadata(availability: cm_types::Availability) -> sandbox::Dict {
    let metadata = sandbox::Dict::new();
    metadata
        .insert(
            cm_types::Name::new(METADATA_KEY_TYPE).unwrap(),
            sandbox::Capability::Data(sandbox::Data::String(String::from(TYPE_DICTIONARY))),
        )
        .unwrap();
    metadata.set_metadata(availability);
    metadata
}

/// Returns a `Dict` containing Router Request metadata specifying a Config porcelain type.
pub fn config_metadata(availability: cm_types::Availability) -> sandbox::Dict {
    let metadata = sandbox::Dict::new();
    metadata
        .insert(
            cm_types::Name::new(METADATA_KEY_TYPE).unwrap(),
            sandbox::Capability::Data(sandbox::Data::String(String::from(TYPE_CONFIG))),
        )
        .unwrap();
    metadata.set_metadata(availability);
    metadata
}

/// Returns a `Dict` containing Router Request metadata specifying a Runner porcelain type.
pub fn runner_metadata(availability: cm_types::Availability) -> sandbox::Dict {
    let metadata = sandbox::Dict::new();
    metadata
        .insert(
            cm_types::Name::new(METADATA_KEY_TYPE).unwrap(),
            sandbox::Capability::Data(sandbox::Data::String(
                cm_rust::CapabilityTypeName::Runner.to_string(),
            )),
        )
        .unwrap();
    metadata.set_metadata(availability);
    metadata
}

/// Returns a `Dict` Containing Router Request metadata specifying a Resolver porcelain type.
pub fn resolver_metadata(availability: cm_types::Availability) -> sandbox::Dict {
    let metadata = sandbox::Dict::new();
    metadata
        .insert(
            cm_types::Name::new(METADATA_KEY_TYPE).unwrap(),
            sandbox::Capability::Data(sandbox::Data::String(
                cm_rust::CapabilityTypeName::Resolver.to_string(),
            )),
        )
        .unwrap();
    metadata.set_metadata(availability);
    metadata
}