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