spin_locked_app/
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
use std::marker::PhantomData;

use serde::Deserialize;
use serde_json::Value;

use crate::{values::ValuesMap, Error, Result};

/// MetadataKey is a handle to a typed metadata value.
pub struct MetadataKey<T = String> {
    key: &'static str,
    _phantom: PhantomData<T>,
}

impl<T> MetadataKey<T> {
    /// Creates a new MetadataKey.
    pub const fn new(key: &'static str) -> Self {
        Self {
            key,
            _phantom: PhantomData,
        }
    }
}

impl<T> Clone for MetadataKey<T> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<T> Copy for MetadataKey<T> {}

impl<T> AsRef<str> for MetadataKey<T> {
    fn as_ref(&self) -> &str {
        self.key
    }
}

impl<T> From<MetadataKey<T>> for String {
    fn from(value: MetadataKey<T>) -> Self {
        value.key.to_string()
    }
}

impl<T> std::fmt::Debug for MetadataKey<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:?}", self.key)
    }
}

/// Helper functions for reading LockedApp metadata
pub trait MetadataExt {
    /// Get a value from a metadata map
    fn get_value(&self, key: &str) -> Option<&Value>;

    /// Get a typed value from a metadata map
    fn get_typed<'a, T: Deserialize<'a>>(&'a self, key: MetadataKey<T>) -> Result<Option<T>> {
        self.get_value(key.as_ref())
            .map(T::deserialize)
            .transpose()
            .map_err(|err| {
                Error::MetadataError(format!("invalid metadata value for {key:?}: {err:?}"))
            })
    }

    /// Get a required value from a metadata map, returning an error
    /// if it is not present
    fn require_typed<'a, T: Deserialize<'a>>(&'a self, key: MetadataKey<T>) -> Result<T> {
        self.get_typed(key)?
            .ok_or_else(|| Error::MetadataError(format!("missing required metadata {key:?}")))
    }
}

impl MetadataExt for ValuesMap {
    fn get_value(&self, key: &str) -> Option<&Value> {
        self.get(key)
    }
}