cargo_lambda_metadata/
lambda.rs

1use clap::{
2    Arg, Command,
3    builder::TypedValueParser,
4    error::{ContextKind, ContextValue},
5};
6use serde::{
7    Deserialize, Serialize, Serializer,
8    de::{Deserializer, Error, Visitor},
9};
10use std::{ffi::OsStr, fmt, str::FromStr, time::Duration};
11use strum_macros::{Display, EnumString};
12
13use crate::error::MetadataError;
14
15#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
16pub struct Timeout(u32);
17
18impl Timeout {
19    pub fn new(i: u32) -> Timeout {
20        Timeout(i)
21    }
22
23    pub fn is_zero(&self) -> bool {
24        self.0 == 0
25    }
26
27    pub fn duration(&self) -> Duration {
28        Duration::from_secs(self.0 as u64)
29    }
30}
31
32impl Default for Timeout {
33    fn default() -> Self {
34        Timeout(30)
35    }
36}
37
38impl FromStr for Timeout {
39    type Err = MetadataError;
40
41    fn from_str(t: &str) -> Result<Timeout, Self::Err> {
42        let t = u32::from_str(t).map_err(MetadataError::InvalidTimeout)?;
43
44        Ok(Timeout(t))
45    }
46}
47
48impl From<Timeout> for i32 {
49    fn from(t: Timeout) -> i32 {
50        t.0 as i32
51    }
52}
53
54impl From<&Timeout> for i32 {
55    fn from(t: &Timeout) -> i32 {
56        t.0 as i32
57    }
58}
59
60impl From<i32> for Timeout {
61    fn from(t: i32) -> Timeout {
62        Timeout(t as u32)
63    }
64}
65
66#[derive(Clone, Debug, Eq, PartialEq)]
67pub struct Memory(u32);
68
69impl From<Memory> for i32 {
70    fn from(m: Memory) -> i32 {
71        (&m).into()
72    }
73}
74
75impl From<&Memory> for i32 {
76    fn from(m: &Memory) -> i32 {
77        m.0 as i32
78    }
79}
80
81impl From<i32> for Memory {
82    fn from(t: i32) -> Memory {
83        Memory(t as u32)
84    }
85}
86
87impl TryFrom<i64> for Memory {
88    type Error = MetadataError;
89
90    fn try_from(m: i64) -> Result<Memory, Self::Error> {
91        if !(128..=10240).contains(&m) {
92            return Err(MetadataError::InvalidMemory(format!("{m}")));
93        }
94        Ok(Memory(m as u32))
95    }
96}
97
98impl FromStr for Memory {
99    type Err = MetadataError;
100
101    fn from_str(t: &str) -> Result<Memory, Self::Err> {
102        let t = i64::from_str(t).map_err(|_| MetadataError::InvalidMemory(t.into()))?;
103
104        t.try_into()
105    }
106}
107
108impl<'de> Deserialize<'de> for Memory {
109    fn deserialize<D>(deserializer: D) -> Result<Memory, D::Error>
110    where
111        D: Deserializer<'de>,
112    {
113        struct MemoryVisitor;
114        impl Visitor<'_> for MemoryVisitor {
115            type Value = Memory;
116
117            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
118                formatter.write_str("an integer that matches Lambda's memory values")
119            }
120
121            fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
122            where
123                E: Error,
124            {
125                Memory::try_from(value).map_err(|e| Error::custom(e.to_string()))
126            }
127
128            fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
129            where
130                E: serde::de::Error,
131            {
132                self.visit_i64(value as i64)
133            }
134        }
135
136        deserializer.deserialize_i64(MemoryVisitor)
137    }
138}
139
140impl Serialize for Memory {
141    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
142    where
143        S: Serializer,
144    {
145        serializer.serialize_i32(self.into())
146    }
147}
148
149#[derive(Clone)]
150pub struct MemoryValueParser;
151impl TypedValueParser for MemoryValueParser {
152    type Value = Memory;
153
154    fn parse_ref(
155        &self,
156        cmd: &Command,
157        arg: Option<&Arg>,
158        value: &OsStr,
159    ) -> Result<Self::Value, clap::Error> {
160        let val = value
161            .to_str()
162            .ok_or_else(|| clap::Error::new(clap::error::ErrorKind::InvalidUtf8).with_cmd(cmd))?;
163
164        Memory::from_str(val).map_err(|_| {
165            let mut err = clap::Error::new(clap::error::ErrorKind::ValueValidation).with_cmd(cmd);
166
167            if let Some(arg) = arg {
168                err.insert(
169                    ContextKind::InvalidArg,
170                    ContextValue::String(arg.to_string()),
171                );
172            }
173
174            let context = ContextValue::String(val.to_string());
175            err.insert(ContextKind::InvalidValue, context);
176
177            err
178        })
179    }
180}
181
182#[derive(Clone, Debug, Default, Display, EnumString, Eq, PartialEq, Serialize)]
183#[strum(ascii_case_insensitive)]
184pub enum Tracing {
185    Active,
186    #[default]
187    PassThrough,
188}
189
190impl Tracing {
191    pub fn as_str(&self) -> &str {
192        match self {
193            Tracing::Active => "Active",
194            Tracing::PassThrough => "PassThrough",
195        }
196    }
197}
198
199impl TryFrom<String> for Tracing {
200    type Error = MetadataError;
201
202    fn try_from(s: String) -> Result<Tracing, Self::Error> {
203        match s.to_lowercase().as_str() {
204            "active" => Ok(Self::Active),
205            "passthrough" => Ok(Self::PassThrough),
206            _ => Err(MetadataError::InvalidTracing(s)),
207        }
208    }
209}
210
211impl<'de> Deserialize<'de> for Tracing {
212    fn deserialize<D>(deserializer: D) -> Result<Tracing, D::Error>
213    where
214        D: Deserializer<'de>,
215    {
216        struct TracingVisitor;
217        impl Visitor<'_> for TracingVisitor {
218            type Value = Tracing;
219
220            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
221                formatter.write_str(
222                    "a string that matches Lambda's tracing options: `active` or `passthrough`",
223                )
224            }
225
226            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
227            where
228                E: Error,
229            {
230                self.visit_string(v.to_string())
231            }
232
233            fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
234            where
235                E: Error,
236            {
237                Tracing::try_from(v).map_err(|e| Error::custom(e.to_string()))
238            }
239        }
240
241        deserializer.deserialize_string(TracingVisitor)
242    }
243}