pdk_core/host/property/
mod.rs

1// Copyright (c) 2025, Salesforce, Inc.,
2// All rights reserved.
3// For full license text, see the LICENSE.txt file
4
5//! Abstract the access properties from the context.
6
7use classy::extract::FromContext;
8use classy::proxy_wasm::types::Bytes;
9use std::convert::Infallible;
10
11pub use self::properties::*;
12use crate::host::{self};
13use anyhow::format_err;
14
15mod properties;
16
17/// Trait to access properties from the context.
18pub trait PropertyAccessor {
19    /// Returns a property, if not missing
20    fn read_property(&self, path: &[&str]) -> Option<Bytes>;
21
22    /// Overrides a given property with a given value
23    fn set_property(&self, path: &[&str], value: &[u8]);
24}
25
26/// Wrapper for a [`PropertyAccessor`] to cast properties to strings.
27pub struct PropertyMapper<'a> {
28    property_accessor: &'a dyn PropertyAccessor,
29}
30
31impl<'a> PropertyMapper<'a> {
32    fn string_property(&self, path: &[&str]) -> host::Result<Option<String>> {
33        if let Some(bytes) = self.property_accessor.read_property(path) {
34            String::from_utf8(bytes).map(Option::from).map_err(|e| {
35                format_err!("Retrieved value for property {path:?} was not valid: {e:?}")
36            })
37        } else {
38            Ok(None)
39        }
40    }
41
42    pub fn from(property_accessor: &'a dyn PropertyAccessor) -> Self {
43        Self { property_accessor }
44    }
45}
46
47impl dyn PropertyAccessor {
48    #[allow(clippy::should_implement_trait)]
49    pub fn default() -> &'static dyn PropertyAccessor {
50        &impls::Host
51    }
52}
53
54impl<'a> dyn PropertyAccessor + 'a {
55    pub fn request(&'a self) -> RequestInfo<'a> {
56        RequestInfo {
57            mapper: PropertyMapper::from(self),
58        }
59    }
60
61    pub fn source(&'a self) -> SourceInfo<'a> {
62        SourceInfo {
63            mapper: PropertyMapper::from(self),
64        }
65    }
66
67    pub fn destination(&'a self) -> DestinationInfo<'a> {
68        DestinationInfo {
69            mapper: PropertyMapper::from(self),
70        }
71    }
72
73    pub fn tracing(&'a self) -> TracingInfo<'a> {
74        TracingInfo {
75            mapper: PropertyMapper::from(self),
76        }
77    }
78}
79
80pub struct RequestInfo<'a> {
81    mapper: PropertyMapper<'a>,
82}
83
84impl RequestInfo<'_> {
85    pub fn id(&self) -> host::Result<Option<String>> {
86        self.mapper.string_property(REQUEST_ID)
87    }
88
89    pub fn protocol(&self) -> host::Result<Option<String>> {
90        self.mapper.string_property(REQUEST_PROTOCOL)
91    }
92
93    pub fn scheme(&self) -> host::Result<Option<String>> {
94        self.mapper.string_property(REQUEST_SCHEME)
95    }
96}
97
98pub struct SourceInfo<'a> {
99    mapper: PropertyMapper<'a>,
100}
101
102impl SourceInfo<'_> {
103    pub fn address(&self) -> host::Result<Option<String>> {
104        self.mapper.string_property(SOURCE_ADDRESS)
105    }
106}
107
108pub struct DestinationInfo<'a> {
109    mapper: PropertyMapper<'a>,
110}
111
112impl DestinationInfo<'_> {
113    pub fn address(&self) -> host::Result<Option<String>> {
114        self.mapper.string_property(DESTINATION_ADDRESS)
115    }
116}
117
118pub struct TracingInfo<'a> {
119    mapper: PropertyMapper<'a>,
120}
121
122impl TracingInfo<'_> {
123    pub fn id(&self) -> host::Result<Option<String>> {
124        self.mapper.string_property(TRACING_ID_PATH)
125    }
126}
127
128impl<C> FromContext<C> for &'static dyn PropertyAccessor {
129    type Error = Infallible;
130
131    fn from_context(_: &C) -> Result<Self, Self::Error> {
132        Ok(<dyn PropertyAccessor>::default())
133    }
134}
135
136mod impls {
137    use crate::host::property::PropertyAccessor;
138    use classy::proxy_wasm::types::Bytes;
139    use classy::Host as ClassyHost;
140
141    pub(super) struct Host;
142
143    impl PropertyAccessor for Host {
144        fn read_property(&self, path: &[&str]) -> Option<Bytes> {
145            crate::Host.get_property(path.to_vec())
146        }
147
148        fn set_property(&self, path: &[&str], value: &[u8]) {
149            crate::Host.set_property(path.to_vec(), Some(value))
150        }
151    }
152}