Skip to content
Permalink
Browse files

refactor(broker): pull string_type! macro out to the types directory

  • Loading branch information...
philbooth committed Apr 13, 2019
1 parent 7610dae commit b8389fa7c7ebbb989bc9b8e9d08e3843964a4294

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
@@ -19,3 +19,4 @@ rusoto_credential = ">=0.16.0"
rusoto_sqs = ">=0.37.0"
sentry = ">=0.15.2"
serde = { version = ">=1.0.90", features = ["derive"] }
serde_json = ">=1.0.39"
@@ -7,19 +7,15 @@
#[cfg(test)]
mod test;

use std::{
convert::TryFrom,
env,
fmt::{self, Display},
};
use std::{convert::TryFrom, env};

use config::{Config, ConfigError, Environment, File};
use serde::{
de::{Error, Unexpected},
Deserialize, Deserializer, Serialize,
};
use serde::{Deserialize, Serialize};

use crate::types::{env::Env, validate};
use crate::types::{
aws::{AccessKey, Region, SecretKey, SqsUrl},
env::Env,
};

/// The root settings object.
#[derive(Debug, Default, Deserialize, Serialize)]
@@ -74,7 +70,7 @@ pub struct Aws {
pub keys: Option<AwsKeys>,

/// The AWS region for SES and SQS.
pub region: AwsRegion,
pub region: Region,

/// URLs for SQS queues.
pub sqsurls: Option<SqsUrls>,
@@ -84,10 +80,10 @@ pub struct Aws {
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct AwsKeys {
/// The AWS access key.
pub access: AwsAccess,
pub access: AccessKey,

/// The AWS secret key.
pub secret: AwsSecret,
pub secret: SecretKey,
}

/// URLs for SQS queues.
@@ -96,49 +92,3 @@ pub struct SqsUrls {
/// The incoming queue URL.
pub incoming: SqsUrl,
}

macro_rules! settings_strings {
($(#[$docs:meta] ($type:ident, $validator:ident, $expected:expr)),+) => ($(
#[$docs]
#[derive(Clone, Debug, Default, Serialize, PartialEq)]
pub struct $type(pub String);

impl AsRef<str> for $type {
fn as_ref(&self) -> &str {
self.0.as_str()
}
}

impl Display for $type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&self.0)
}
}

impl<'d> Deserialize<'d> for $type {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'d>,
{
let value: String = Deserialize::deserialize(deserializer)?;
if validate::$validator(&value) {
Ok($type(value))
} else {
let expected = $expected;
Err(D::Error::invalid_value(Unexpected::Str(&value), &expected))
}
}
}
)*);
}

settings_strings! {
/// AWS access key type.
(AwsAccess, aws_access, "AWS access key"),
/// AWS region type.
(AwsRegion, aws_region, "AWS region"),
/// AWS secret key type.
(AwsSecret, aws_secret, "AWS secret key"),
/// AWS SQS queue URL type.
(SqsUrl, sqs_url, "SQS queue URL")
}
@@ -24,13 +24,13 @@ fn env_vars_take_precedence() {
Ok(settings) => {
let aws_keys = if let Some(ref keys) = settings.aws.keys {
AwsKeys {
access: AwsAccess(format!("{}A", keys.access)),
secret: AwsSecret(format!("{}s", keys.secret)),
access: AccessKey(format!("{}A", keys.access)),
secret: SecretKey(format!("{}s", keys.secret)),
}
} else {
AwsKeys {
access: AwsAccess(String::from("A")),
secret: AwsSecret(String::from("s")),
access: AccessKey(String::from("A")),
secret: SecretKey(String::from("s")),
}
};
let aws_region = if settings.aws.region.0 == "us-east-1" {
@@ -66,7 +66,7 @@ fn env_vars_take_precedence() {

match Settings::new() {
Ok(env_settings) => {
assert_eq!(env_settings.aws.region, AwsRegion(aws_region.to_string()));
assert_eq!(env_settings.aws.region, Region(aws_region.to_string()));

if let Some(env_keys) = env_settings.aws.keys {
assert_eq!(env_keys.access, aws_keys.access);
@@ -6,71 +6,10 @@
//! for modules that implement
//! miscellaneous generally-used types.

macro_rules! enum_boilerplate {
($name:ident ($description:expr, $default:ident, $error:ident) {
$($variant:ident => $serialization:expr,)+
}) => {
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum $name {
$($variant,
)+
}

impl AsRef<str> for $name {
fn as_ref(&self) -> &str {
match *self {
$($name::$variant => $serialization,
)+
}
}
}

impl std::default::Default for $name {
fn default() -> Self {
$name::$default
}
}

impl std::fmt::Display for $name {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "{}", self.as_ref())
}
}

impl<'d> serde::de::Deserialize<'d> for $name {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'d>,
{
let value: String = serde::de::Deserialize::deserialize(deserializer)?;
std::convert::TryFrom::try_from(value.as_str())
.map_err(|_| D::Error::invalid_value(serde::de::Unexpected::Str(&value), &$description))
}
}

impl serde::ser::Serialize for $name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
serializer.serialize_str(self.as_ref())
}
}

impl<'v> std::convert::TryFrom<&'v str> for $name {
type Error = AppError;

fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
$($serialization => Ok($name::$variant),
)+
_ => Err(AppErrorKind::$error(value.to_owned()))?,
}
}
}
}
}
#[macro_use]
pub mod macros;

pub mod aws;
pub mod env;
pub mod error;
pub mod validate;
@@ -0,0 +1,24 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at https://mozilla.org/MPL/2.0/.

//! AWS-related types.

#[cfg(test)]
mod test;

use serde::{de::Error, Serialize};

string_types! {
/// AWS region.
Region (aws_region, "AWS region", InvalidAwsRegion),

/// AWS access key.
AccessKey (aws_access_key, "AWS access key", InvalidAwsAccessKey),

/// AWS secret key.
SecretKey (aws_secret_key, "AWS secret key", InvalidAwsSecretKey),

/// SQS URL.
SqsUrl (sqs_url, "SQS queue URL", InvalidSqsUrl),
}
@@ -0,0 +1,129 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at https://mozilla.org/MPL/2.0/.

use std::convert::TryFrom;

use serde_json::{self, Error as JsonError};

use super::*;
use crate::types::error::AppResult;

#[test]
fn region_try_from() -> AppResult<()> {
let region: Region = TryFrom::try_from("us-east-1")?;
assert_eq!(region.as_ref(), "us-east-1");

let result: AppResult<Region> = TryFrom::try_from("us-east-7");
assert!(result.is_err());
assert_eq!(
result.unwrap_err().to_string(),
"Invalid AWS region: us-east-7"
);

Ok(())
}

#[test]
fn region_serialize_deserialize() -> Result<(), JsonError> {
let region = Region("eu-west-1".to_owned());
let serialized = serde_json::to_string(&region)?;
assert_eq!(serialized, "\"eu-west-1\"");

let region: Region = serde_json::from_str(&serialized)?;
assert_eq!(region.as_ref(), "eu-west-1");

Ok(())
}

#[test]
fn access_key_try_from() -> AppResult<()> {
let access_key: AccessKey = TryFrom::try_from("0123456789ABCDEF")?;
assert_eq!(access_key.as_ref(), "0123456789ABCDEF");

let result: AppResult<AccessKey> = TryFrom::try_from("0123456789 ABCDEF");
assert!(result.is_err());
assert_eq!(
result.unwrap_err().to_string(),
"Invalid AWS access key: 0123456789 ABCDEF"
);

Ok(())
}

#[test]
fn access_key_serialize_deserialize() -> Result<(), JsonError> {
let access_key = AccessKey("ABC".to_owned());
let serialized = serde_json::to_string(&access_key)?;
assert_eq!(serialized, "\"ABC\"");

let access_key: AccessKey = serde_json::from_str(&serialized)?;
assert_eq!(access_key.as_ref(), "ABC");

Ok(())
}

#[test]
fn secret_key_try_from() -> AppResult<()> {
let secret_key: SecretKey = TryFrom::try_from("0123456789ABCabc+/=")?;
assert_eq!(secret_key.as_ref(), "0123456789ABCabc+/=");

let result: AppResult<SecretKey> = TryFrom::try_from("0123456789_ABC");
assert!(result.is_err());
assert_eq!(
result.unwrap_err().to_string(),
"Invalid AWS secret key: 0123456789_ABC"
);

Ok(())
}

#[test]
fn secret_key_serialize_deserialize() -> Result<(), JsonError> {
let secret_key = SecretKey("+/=".to_owned());
let serialized = serde_json::to_string(&secret_key)?;
assert_eq!(serialized, "\"+/=\"");

let secret_key: SecretKey = serde_json::from_str(&serialized)?;
assert_eq!(secret_key.as_ref(), "+/=");

Ok(())
}

#[test]
fn sqs_url_try_from() -> AppResult<()> {
let sqs_url: SqsUrl =
TryFrom::try_from("https://sqs.us-east-1.amazonaws.com/123456789012/MyQueue")?;
assert_eq!(
sqs_url.as_ref(),
"https://sqs.us-east-1.amazonaws.com/123456789012/MyQueue"
);

let result: AppResult<SqsUrl> =
TryFrom::try_from("http://sqs.us-east-1.amazonaws.com/123456789012/MyQueue");
assert!(result.is_err());
assert_eq!(
result.unwrap_err().to_string(),
"Invalid SQS URL: http://sqs.us-east-1.amazonaws.com/123456789012/MyQueue"
);

Ok(())
}

#[test]
fn sqs_url_serialize_deserialize() -> Result<(), JsonError> {
let sqs_url = SqsUrl("https://sqs.us-west-2.amazonaws.com/42/wibble".to_owned());
let serialized = serde_json::to_string(&sqs_url)?;
assert_eq!(
serialized,
"\"https://sqs.us-west-2.amazonaws.com/42/wibble\""
);

let sqs_url: SqsUrl = serde_json::from_str(&serialized)?;
assert_eq!(
sqs_url.as_ref(),
"https://sqs.us-west-2.amazonaws.com/42/wibble"
);

Ok(())
}
@@ -9,11 +9,11 @@ mod test;

use serde::de::Error;

use crate::types::error::{AppError, AppErrorKind};

enum_boilerplate!(Env ("env", Dev, InvalidEnv) {
// These values are consistent with the conventions followed by other FxA repos.
Dev => "dev",
Prod => "production",
Test => "test",
});
enum_type! {
/// Environment enumerated type.
Env ("environment", Dev, InvalidEnv) {
Dev => "dev",
Prod => "production",
Test => "test",
}
}
Oops, something went wrong.

0 comments on commit b8389fa

Please sign in to comment.
You can’t perform that action at this time.