conjure_object/
plain.rs

1// Copyright 2019 Palantir Technologies, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! The Conjure PLAIN format.
16
17use base64::display::Base64Display;
18use base64::engine::general_purpose::STANDARD;
19use base64::{DecodeError, Engine};
20use bytes::Bytes;
21use chrono::format::{Fixed, Item, ParseError};
22use chrono::{DateTime, Utc};
23use std::error::Error;
24use std::f64;
25use std::fmt;
26use std::iter;
27use std::num::ParseFloatError;
28use std::str::FromStr;
29use uuid::Uuid;
30
31use crate::{BearerToken, ResourceIdentifier, SafeLong};
32
33/// Format trait for the Conjure PLAIN format.
34pub trait Plain {
35    /// Formats this value in its Conjure PLAIN format.
36    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result;
37}
38
39impl<T> Plain for &T
40where
41    T: ?Sized + Plain,
42{
43    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
44        Plain::fmt(&**self, fmt)
45    }
46}
47
48macro_rules! as_display {
49    ($t:ty) => {
50        impl Plain for $t {
51            fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
52                fmt::Display::fmt(self, fmt)
53            }
54        }
55    };
56}
57
58as_display!(bool);
59as_display!(i32);
60as_display!(ResourceIdentifier);
61as_display!(SafeLong);
62as_display!(str);
63as_display!(String);
64as_display!(Uuid);
65
66impl Plain for BearerToken {
67    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
68        fmt::Display::fmt(self.as_str(), fmt)
69    }
70}
71
72impl Plain for DateTime<Utc> {
73    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
74        fmt::Display::fmt(
75            &self.format_with_items(iter::once(Item::Fixed(Fixed::RFC3339))),
76            fmt,
77        )
78    }
79}
80
81impl Plain for f64 {
82    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
83        // f64's display uses `inf` and `-inf` for infinities, but works otherwise
84        if *self == f64::INFINITY {
85            fmt::Display::fmt("Infinity", fmt)
86        } else if *self == f64::NEG_INFINITY {
87            fmt::Display::fmt("-Infinity", fmt)
88        } else {
89            fmt::Display::fmt(self, fmt)
90        }
91    }
92}
93
94impl Plain for [u8] {
95    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
96        fmt::Display::fmt(&Base64Display::new(self, &STANDARD), fmt)
97    }
98}
99
100impl Plain for Bytes {
101    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
102        Plain::fmt(&**self, fmt)
103    }
104}
105
106/// A trait for converting a value to its Conjure PLAIN string representation.
107///
108/// This is implemented for all types that implement the `Plain` trait.
109pub trait ToPlain {
110    /// Returns the conjure PLAIN string representation of this value.
111    fn to_plain(&self) -> String;
112}
113
114impl<T> ToPlain for T
115where
116    T: ?Sized + Plain,
117{
118    fn to_plain(&self) -> String {
119        struct Adaptor<T>(T);
120
121        impl<T> fmt::Display for Adaptor<T>
122        where
123            T: Plain,
124        {
125            fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
126                Plain::fmt(&self.0, fmt)
127            }
128        }
129
130        Adaptor(self).to_string()
131    }
132}
133
134/// Parse a value from its Conjure PLAIN string representation.
135pub trait FromPlain: Sized {
136    /// The error type returned when parsing fails.
137    type Err;
138
139    /// Parse a value from its Conjure PLAIN string representation.
140    fn from_plain(s: &str) -> Result<Self, Self::Err>;
141}
142
143macro_rules! as_from_str {
144    ($t:ty) => {
145        impl FromPlain for $t {
146            type Err = <$t as FromStr>::Err;
147
148            #[inline]
149            fn from_plain(s: &str) -> Result<Self, Self::Err> {
150                s.parse()
151            }
152        }
153    };
154}
155
156as_from_str!(BearerToken);
157as_from_str!(bool);
158as_from_str!(i32);
159as_from_str!(ResourceIdentifier);
160as_from_str!(SafeLong);
161as_from_str!(String);
162as_from_str!(Uuid);
163
164impl FromPlain for Bytes {
165    type Err = ParseBinaryError;
166
167    #[inline]
168    fn from_plain(s: &str) -> Result<Self, ParseBinaryError> {
169        let buf = STANDARD.decode(s).map_err(ParseBinaryError)?;
170        Ok(Bytes::from(buf))
171    }
172}
173
174/// An error parsing a binary value from its Conjure PLAIN format.
175#[derive(Debug)]
176pub struct ParseBinaryError(DecodeError);
177
178impl fmt::Display for ParseBinaryError {
179    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
180        fmt::Display::fmt(&self.0, fmt)
181    }
182}
183
184impl Error for ParseBinaryError {
185    fn source(&self) -> Option<&(dyn Error + 'static)> {
186        self.0.source()
187    }
188}
189
190impl FromPlain for DateTime<Utc> {
191    type Err = ParseError;
192
193    #[inline]
194    fn from_plain(s: &str) -> Result<DateTime<Utc>, ParseError> {
195        DateTime::parse_from_rfc3339(s).map(|t| t.with_timezone(&Utc))
196    }
197}
198
199impl FromPlain for f64 {
200    type Err = ParseFloatError;
201
202    #[inline]
203    fn from_plain(s: &str) -> Result<f64, ParseFloatError> {
204        // f64's normal parser works except for its handling of infinities
205        match s {
206            "Infinity" => Ok(f64::INFINITY),
207            "-Infinity" => Ok(f64::NEG_INFINITY),
208            s => s.parse(),
209        }
210    }
211}
212
213/// An error parsing an enum from its Conjure PLAIN format.
214#[derive(Debug, Default)]
215pub struct ParseEnumError(());
216
217impl ParseEnumError {
218    /// Creates a new `ParseEnumError`.
219    #[inline]
220    pub fn new() -> ParseEnumError {
221        ParseEnumError(())
222    }
223}
224
225impl fmt::Display for ParseEnumError {
226    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
227        fmt.write_str("invalid enum variant")
228    }
229}
230
231impl Error for ParseEnumError {}