use serde::Deserialize;
mod values;
use values::{ExpectedValues, Values};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Invalid token")]
Token(#[from] uuid::Error),
#[error("Invalid JSON data")]
Json(#[from] serde_json::error::Error),
}
#[derive(Debug, Deserialize)]
pub struct Transaction {
pub n: String,
expected: ExpectedValues,
#[serde(flatten)]
v: Values,
pub token: String,
}
impl Transaction {
pub fn from_bytes(data: &[u8]) -> Result<Transaction, Error> {
let res: Transaction = serde_json::de::from_slice(data)?;
Self::validate_token(&res.token)?;
Ok(res)
}
fn validate_token(token: &str) -> Result<(), Error> {
uuid::Uuid::parse_str(token)?;
Ok(())
}
pub fn value(&self) -> String {
self.v.to_string()
}
pub fn expected(&self) -> String {
self.expected.to_string()
}
}
#[cfg(test)]
mod test {
use super::Transaction;
type TestResult = Result<(), Box<dyn std::error::Error>>;
#[test]
fn good_parsing_json_values() -> TestResult {
let various = vec![
r#"{"n":"test.test","expected":"0","token":"6d27d3dd-a409-43a4-9905-9951211d5058", "v":321}"#,
r#"{"n":"test.test","expected":"","token":"6d27d3dd-a409-43a4-9905-9951211d5058", "vs":"abc1234"}"#,
r#"{"n":"test.test","expected":true,"token":"6d27d3dd-a409-43a4-9905-9951211d5058", "vb":false}"#,
r#"{"n":"test.test","expected":false,"token":"6d27d3dd-a409-43a4-9905-9951211d5058", "vb":true}"#,
r#"{"n":"test.test","expected":1234.01,"token":"6d27d3dd-a409-43a4-9905-9951211d5058", "vs":"true"}"#,
r#"{"n":"test.test","expected":"1234","token":"6d27d3dd-a409-43a4-9905-9951211d5058", "v":1230.000}"#,
];
for input in various {
Transaction::from_bytes(input.as_bytes())?;
}
Ok(())
}
#[test]
fn good_transaction_uuid() -> TestResult {
let input = r#"{"n":"test.test","expected":"abc123","v":321,"token":"6d27d3dd-a409-43a4-9905-9951211d5058"}"#;
Transaction::from_bytes(input.as_bytes())?;
Ok(())
}
#[test]
fn good_transaction_uuid_short() -> TestResult {
let input = r#"{"n":"test.test","expected":"abc123","v":321,"token":"6d27d3dda40943a499059951211d5058"}"#;
Transaction::from_bytes(input.as_bytes())?;
Ok(())
}
#[test]
fn bad_transaction_date() {
let input = r#"{"n":"test.test","expected":"abc123","v":321,"token":"1637163026"}"#;
Transaction::from_bytes(input.as_bytes()).unwrap_err();
}
#[test]
fn bad_transaction_alphabetic() {
let input = r#"{"n":"test.test","expected":"abc123","v":321,"token":"test-test"}"#;
Transaction::from_bytes(input.as_bytes()).unwrap_err();
}
#[test]
fn bad_transaction_spaces() {
let input = r#"{"n":"test.test","expected":"abc123","v":321,"token":" 6d27d3dda40943a499059951211d5058"}"#;
Transaction::from_bytes(input.as_bytes()).unwrap_err();
}
}