use http::StatusCode;
use serde::Deserialize;
#[derive(Deserialize, Debug, Clone)]
pub struct Query {
#[serde(rename = "hub.mode")]
pub mode: String,
#[serde(rename = "hub.challenge")]
pub challenge: i64,
#[serde(rename = "hub.verify_token")]
pub verify_token: String,
}
pub fn verify(query_str: &str, verify_token: &str) -> Result<Query, VerifyError> {
let query: Query = serde_qs::from_str(query_str)?;
verify_with_query(query, verify_token)
}
pub fn verify_with_query(query: Query, verify_token: &str) -> Result<Query, VerifyError> {
if query.mode != "subscribe" {
return Err(VerifyError::ModeMismatch);
}
if query.verify_token != verify_token {
return Err(VerifyError::VerifyTokenMismatch);
}
Ok(query)
}
#[derive(thiserror::Error, Debug)]
pub enum VerifyError {
#[error("QueryInvalid {0}")]
QueryInvalid(#[from] serde_qs::Error),
#[error("ModeMismatch")]
ModeMismatch,
#[error("VerifyTokenMismatch")]
VerifyTokenMismatch,
}
pub fn pass_back(query_str: &str, verify_token: &str) -> PassBackResponse {
match serde_qs::from_str(query_str) {
Ok(query) => pass_back_with_query(query, verify_token),
Err(err) => PassBackResponse {
status_code: StatusCode::BAD_REQUEST,
body: err.to_string(),
},
}
}
pub fn pass_back_with_query(query: Query, verify_token: &str) -> PassBackResponse {
match verify_with_query(query, verify_token) {
Ok(query) => PassBackResponse {
status_code: StatusCode::OK,
body: format!("{}", query.challenge),
},
Err(err) => PassBackResponse {
status_code: StatusCode::BAD_REQUEST,
body: err.to_string(),
},
}
}
#[derive(Debug, Clone)]
pub struct PassBackResponse {
pub status_code: StatusCode,
pub body: String,
}
#[cfg(test)]
mod tests {
use super::*;
const SAMPLE_QUERY_STR: &str =
"hub.mode=subscribe&hub.challenge=1158201444&hub.verify_token=meatyhamhock";
#[test]
fn test_verify() {
assert_eq!(
verify(SAMPLE_QUERY_STR, "meatyhamhock").unwrap().challenge,
1158201444
);
match verify(
SAMPLE_QUERY_STR
.replace("hub.mode=", "hub.modexx=")
.as_str(),
"meatyhamhock",
) {
Ok(_) => panic!(""),
Err(VerifyError::QueryInvalid(err)) => {
assert!(err.to_string().contains("missing field `hub.mode`"))
}
Err(err) => panic!("{}", err),
}
match verify(
SAMPLE_QUERY_STR
.replace("=subscribe&", "=subscribexx&")
.as_str(),
"meatyhamhock",
) {
Ok(_) => panic!(""),
Err(VerifyError::ModeMismatch) => (),
Err(err) => panic!("{}", err),
}
match verify(SAMPLE_QUERY_STR, "FOO") {
Ok(_) => panic!(""),
Err(VerifyError::VerifyTokenMismatch) => (),
Err(err) => panic!("{}", err),
}
}
#[test]
fn test_pass_back() {
let res = pass_back(SAMPLE_QUERY_STR, "meatyhamhock");
assert_eq!(res.status_code, StatusCode::OK);
assert_eq!(res.body, "1158201444");
let res = pass_back(SAMPLE_QUERY_STR, "FOO");
assert_eq!(res.status_code, StatusCode::BAD_REQUEST);
assert_eq!(res.body, "VerifyTokenMismatch");
}
}