spin_locked_app/
metadata.rs

1use std::marker::PhantomData;
2
3use serde::Deserialize;
4use serde_json::Value;
5
6use crate::{values::ValuesMap, Error, Result};
7
8/// MetadataKey is a handle to a typed metadata value.
9pub struct MetadataKey<T = String> {
10    key: &'static str,
11    _phantom: PhantomData<T>,
12}
13
14impl<T> MetadataKey<T> {
15    /// Creates a new MetadataKey.
16    pub const fn new(key: &'static str) -> Self {
17        Self {
18            key,
19            _phantom: PhantomData,
20        }
21    }
22}
23
24impl<T> Clone for MetadataKey<T> {
25    fn clone(&self) -> Self {
26        *self
27    }
28}
29
30impl<T> Copy for MetadataKey<T> {}
31
32impl<T> AsRef<str> for MetadataKey<T> {
33    fn as_ref(&self) -> &str {
34        self.key
35    }
36}
37
38impl<T> From<MetadataKey<T>> for String {
39    fn from(value: MetadataKey<T>) -> Self {
40        value.key.to_string()
41    }
42}
43
44impl<T> std::fmt::Debug for MetadataKey<T> {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        write!(f, "{:?}", self.key)
47    }
48}
49
50/// Helper functions for reading LockedApp metadata
51pub trait MetadataExt {
52    /// Get a value from a metadata map
53    fn get_value(&self, key: &str) -> Option<&Value>;
54
55    /// Get a typed value from a metadata map
56    fn get_typed<'a, T: Deserialize<'a>>(&'a self, key: MetadataKey<T>) -> Result<Option<T>> {
57        self.get_value(key.as_ref())
58            .map(T::deserialize)
59            .transpose()
60            .map_err(|err| {
61                Error::MetadataError(format!("invalid metadata value for {key:?}: {err:?}"))
62            })
63    }
64
65    /// Get a required value from a metadata map, returning an error
66    /// if it is not present
67    fn require_typed<'a, T: Deserialize<'a>>(&'a self, key: MetadataKey<T>) -> Result<T> {
68        self.get_typed(key)?
69            .ok_or_else(|| Error::MetadataError(format!("missing required metadata {key:?}")))
70    }
71}
72
73impl MetadataExt for ValuesMap {
74    fn get_value(&self, key: &str) -> Option<&Value> {
75        self.get(key)
76    }
77}