Skip to content

Instantly share code, notes, and snippets.

@terry90
Created April 7, 2020 15:20
Show Gist options
  • Save terry90/c9daf4a3086df4c498b2ce84b27d4b44 to your computer and use it in GitHub Desktop.
Save terry90/c9daf4a3086df4c498b2ce84b27d4b44 to your computer and use it in GitHub Desktop.
Utils
use rocket::http::Status;
use rocket::{
http::ContentType,
response::{self, Responder},
Request,
};
use rocket_contrib::json::Json;
use std::collections::HashMap;
#[derive(Debug)]
pub struct JsonError {
pub errors: Vec<String>,
pub details: HashMap<String, Vec<String>>,
pub status: u16,
}
#[derive(Serialize)]
pub struct JsonErrorResponse {
pub errors: Vec<String>,
pub details: HashMap<String, Vec<String>>,
}
impl From<JsonError> for JsonErrorResponse {
fn from(e: JsonError) -> Self {
JsonErrorResponse {
errors: e.errors,
details: e.details,
}
}
}
impl Default for JsonError {
fn default() -> Self {
Self {
errors: vec![],
details: HashMap::new(),
status: 500,
}
}
}
impl JsonError {
pub fn add(&mut self, error: &str) -> &mut Self {
self.errors.push(error.to_owned());
self
}
pub fn combine(mut self, other: Result<(), Self>) -> Self {
match other {
Ok(_) => (),
Err(mut other) => {
self.errors.append(&mut other.errors);
for (key, val) in self.details.iter_mut() {
if let Some(other_val) = other.details.get_mut(key) {
val.append(other_val)
}
}
}
}
self
}
pub fn ok(self) -> Result<(), Self> {
match self.errors.is_empty() {
true => Ok(()),
false => Err(self),
}
}
}
impl<'r> Responder<'r> for JsonError {
fn respond_to(self, req: &Request) -> response::Result<'r> {
let code = self.status;
let mut res = Json(JsonErrorResponse::from(self)).respond_to(req).unwrap();
res.set_status(Status::from_code(code).unwrap_or(Status::InternalServerError));
res.set_header(ContentType::JSON);
Ok(res)
}
}
#[macro_export]
macro_rules! json_err {
($val:expr, $status:expr) => {{
JsonError {
errors: vec![$val],
details: std::collections::HashMap::new(),
status: $status,
}
}};
($key:expr, $val:expr, $status:expr) => {{
JsonError {
errors: vec![$val],
details: std::collections::HashMap::new(),
status: $status,
}
}};
}
#[derive(Copy, Debug, Serialize, Deserialize, PartialEq, Eq, AsExpression, DieselEnum, Clone)]
#[serde(rename_all = "snake_case")]
pub enum Lang {
En,
Fr,
De,
Nl,
}
impl FromStr for Lang {
type Err = ();
fn from_str(s: &str) -> Result<Self, ()> {
match s {
"en" => Ok(Lang::En),
"fr" => Ok(Lang::Fr),
"de" => Ok(Lang::De),
"nl" => Ok(Lang::Nl),
_ => Err(()),
}
}
}
impl Lang {
pub fn from_header(header: Vec<&str>) -> Self {
let parse_header = |h: &str| -> Lang {
let code = h.to_owned();
let code = match code.contains('-') {
true => h
.split('-')
.collect::<Vec<&str>>()
.first()
.map(ToOwned::to_owned),
false => h.get(0..2),
};
if let Some(code) = code {
Lang::from_str(code).unwrap_or(Lang::default())
} else {
Lang::default()
}
};
match header.len() {
0 => Lang::default(),
1 => parse_header(header[0]),
_ => Lang::default(),
}
}
}
#[test]
fn lang_from_header() {
assert_eq!(Lang::from_header(vec!["fr"]), Lang::Fr);
assert_eq!(Lang::from_header(vec!["en-US,en;q=0.5"]), Lang::En);
assert_eq!(Lang::from_header(vec!["de"]), Lang::De);
}
impl Default for Lang {
fn default() -> Self {
Self::Fr
}
}
impl std::ops::Deref for Lang {
type Target = str;
fn deref(&self) -> &str {
match self {
Lang::En => "en",
Lang::Fr => "fr",
Lang::De => "de",
Lang::Nl => "nl",
}
}
}
impl<'a, 'r> FromRequest<'a, 'r> for Lang {
type Error = JsonError;
fn from_request(request: &'a rocket::Request<'r>) -> request::Outcome<Self, Self::Error> {
let header = request.headers().get("Accept-Language").collect::<Vec<_>>();
Outcome::Success(Lang::from_header(header))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment