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, ParseBoolError};
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!(i32);
158as_from_str!(ResourceIdentifier);
159as_from_str!(SafeLong);
160as_from_str!(String);
161as_from_str!(Uuid);
162
163impl FromPlain for bool {
164    type Err = ParseBoolError;
165
166    #[inline]
167    fn from_plain(s: &str) -> Result<Self, Self::Err> {
168        if s.eq_ignore_ascii_case("true") {
169            Ok(true)
170        } else if s.eq_ignore_ascii_case("false") {
171            Ok(false)
172        } else {
173            // this will always fail, but we can't construct a ParseBoolError directly otherwise
174            s.parse()
175        }
176    }
177}
178
179impl FromPlain for Bytes {
180    type Err = ParseBinaryError;
181
182    #[inline]
183    fn from_plain(s: &str) -> Result<Self, ParseBinaryError> {
184        let buf = STANDARD.decode(s).map_err(ParseBinaryError)?;
185        Ok(Bytes::from(buf))
186    }
187}
188
189/// An error parsing a binary value from its Conjure PLAIN format.
190#[derive(Debug)]
191pub struct ParseBinaryError(DecodeError);
192
193impl fmt::Display for ParseBinaryError {
194    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
195        fmt::Display::fmt(&self.0, fmt)
196    }
197}
198
199impl Error for ParseBinaryError {
200    fn source(&self) -> Option<&(dyn Error + 'static)> {
201        self.0.source()
202    }
203}
204
205impl FromPlain for DateTime<Utc> {
206    type Err = ParseError;
207
208    #[inline]
209    fn from_plain(s: &str) -> Result<DateTime<Utc>, ParseError> {
210        DateTime::parse_from_rfc3339(s).map(|t| t.with_timezone(&Utc))
211    }
212}
213
214impl FromPlain for f64 {
215    type Err = ParseFloatError;
216
217    #[inline]
218    fn from_plain(s: &str) -> Result<f64, ParseFloatError> {
219        // f64's normal parser works except for its handling of infinities
220        match s {
221            "Infinity" => Ok(f64::INFINITY),
222            "-Infinity" => Ok(f64::NEG_INFINITY),
223            s => s.parse(),
224        }
225    }
226}
227
228/// An error parsing an enum from its Conjure PLAIN format.
229#[derive(Debug, Default)]
230pub struct ParseEnumError(());
231
232impl ParseEnumError {
233    /// Creates a new `ParseEnumError`.
234    #[inline]
235    pub fn new() -> ParseEnumError {
236        ParseEnumError(())
237    }
238}
239
240impl fmt::Display for ParseEnumError {
241    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
242        fmt.write_str("invalid enum variant")
243    }
244}
245
246impl Error for ParseEnumError {}