azure_functions/
util.rs

1use crate::rpc::{typed_data::Data, TypedData};
2use chrono::{DateTime, FixedOffset, Utc};
3use serde::{de::Error, de::IntoDeserializer, Deserialize, Deserializer};
4use serde_json::from_str;
5use std::str::{from_utf8, FromStr};
6
7pub fn convert_from<'a, T>(data: &'a TypedData) -> Option<T>
8where
9    T: FromStr + Deserialize<'a>,
10{
11    match &data.data {
12        Some(Data::String(s)) => s.parse::<T>().ok(),
13        Some(Data::Json(s)) => from_str(s).ok(),
14        Some(Data::Bytes(b)) => {
15            if let Ok(s) = from_utf8(b) {
16                return s.parse::<T>().ok();
17            }
18            None
19        }
20        Some(Data::Stream(s)) => {
21            if let Ok(s) = from_utf8(s) {
22                return s.parse::<T>().ok();
23            }
24            None
25        }
26        Some(Data::Int(i)) => {
27            let deserializer: ::serde::de::value::I64Deserializer<::serde_json::error::Error> =
28                i.into_deserializer();
29            T::deserialize(deserializer).ok()
30        }
31        Some(Data::Double(d)) => {
32            let deserializer: ::serde::de::value::F64Deserializer<::serde_json::error::Error> =
33                d.into_deserializer();
34            T::deserialize(deserializer).ok()
35        }
36        _ => None,
37    }
38}
39
40pub fn deserialize_datetime<'a, D>(deserializer: D) -> Result<DateTime<Utc>, D::Error>
41where
42    D: Deserializer<'a>,
43{
44    let mut s = String::deserialize(deserializer)?;
45
46    // This exists because the Azure Functions Host serializes DateTime.MinValue without a timezone
47    // However, chrono::DateTime requires one for DateTime<Utc>
48    if s == "0001-01-01T00:00:00" {
49        s += "Z";
50    }
51
52    s.parse::<DateTime<FixedOffset>>()
53        .map_err(|e| Error::custom(format!("{}", e)))
54        .map(|dt| dt.with_timezone(&Utc))
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60
61    #[test]
62    fn it_converts_from_string_data() {
63        const DATA: &'static str = "test";
64
65        let data = TypedData {
66            data: Some(Data::String(DATA.to_string())),
67        };
68
69        let s: String = convert_from(&data).unwrap();
70        assert_eq!(s, DATA);
71    }
72
73    #[test]
74    fn it_converts_from_json_data() {
75        let data = TypedData {
76            data: Some(Data::Json(r#""hello world""#.to_string())),
77        };
78
79        let s: String = convert_from(&data).unwrap();
80        assert_eq!(s, "hello world");
81    }
82
83    #[test]
84    fn it_converts_from_bytes_data() {
85        let data = TypedData {
86            data: Some(Data::Bytes(vec![
87                0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64,
88            ])),
89        };
90
91        let s: String = convert_from(&data).unwrap();
92        assert_eq!(s, "hello world");
93    }
94
95    #[test]
96    fn it_converts_from_stream_data() {
97        let data = TypedData {
98            data: Some(Data::Stream(vec![
99                0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64,
100            ])),
101        };
102
103        let s: String = convert_from(&data).unwrap();
104        assert_eq!(s, "hello world");
105    }
106
107    #[test]
108    fn it_converts_from_int_data() {
109        const DATA: i64 = 42;
110
111        let data = TypedData {
112            data: Some(Data::Int(DATA)),
113        };
114
115        let d: i64 = convert_from(&data).unwrap();
116        assert_eq!(d, DATA);
117    }
118
119    #[test]
120    fn it_converts_from_double_data() {
121        const DATA: f64 = 42.24;
122
123        let data = TypedData {
124            data: Some(Data::Double(DATA)),
125        };
126
127        let d: f64 = convert_from(&data).unwrap();
128        assert_eq!(d, DATA);
129    }
130}