diff --git a/Cargo.lock b/Cargo.lock index e0da38b..e2532bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -271,6 +271,8 @@ dependencies = [ "germ", "native_db", "native_model", + "serde", + "serde_derive", "thiserror", "tokio", "url", diff --git a/Cargo.toml b/Cargo.toml index b26e771..9cc6605 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,8 @@ fluffer = "4" germ = "0.4" native_db = "0.5" native_model = "0.4" +serde = "1.0.197" +serde_derive = "1.0.197" thiserror = "1.0.58" tokio = {version = "1.37", features = [ "full" ] } url = "2.5.0" diff --git a/src/comments.rs b/src/comments.rs index 17e1a38..6987477 100644 --- a/src/comments.rs +++ b/src/comments.rs @@ -1,6 +1,6 @@ use crate::error::GementionError; -use crate::models::mentions::{C2SMentionRequest, Mention}; -use crate::models::web::MentionResponse; +use crate::models::mentions::Mention; +use crate::models::web::{C2SMentionRequest, MentionResponse}; use crate::validation::{AsyncValidation, Validation}; use fluffer::Client; diff --git a/src/db.rs b/src/db.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/main.rs b/src/main.rs index 7f31888..fbb9806 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,12 @@ use fluffer::AppErr; -mod comments; -mod error; -mod models; -mod validation; -mod routes; -mod verification; +pub(crate) mod comments; +pub(crate) mod error; +pub(crate) mod models; +pub(crate) mod validation; +pub(crate) mod routes; +pub(crate) mod verification; +pub(crate) mod db; #[tokio::main] async fn main() -> Result<(), AppErr> { diff --git a/src/models/mentions.rs b/src/models/mentions.rs index 00161f7..d37f92d 100644 --- a/src/models/mentions.rs +++ b/src/models/mentions.rs @@ -1,103 +1,11 @@ -use async_trait::async_trait; -use fluffer::{Client, GemBytes}; use std::fmt::Display; use url::Url; use crate::error::GementionError; +use crate::models::web::C2SMentionRequest; use super::verification::VerificationStatus; -macro_rules! titan { - ($self:expr) => { - $self.client.titan.as_ref().unwrap() - }; -} - -fn parse_content_body(raw_body: &[u8]) -> Result<&str, GementionError> { - let content = std::str::from_utf8(raw_body)?; - - // Drop 1st line if it is a mention type. Otherwise return whole thing. - let mention_type = MentionType::try_from(raw_body); - let content: &str = if let Ok(_) = mention_type { - let amt_to_chop = content.lines().next().map(|line| line.len()).unwrap(); - &content[amt_to_chop..] - } else { - content - }; - - Ok(content.trim()) -} - -/// Wraps an incoming Titan request and provides handy methods for -/// extracting the expected information for a client-to-server -/// gemention. -pub(crate) struct C2SMentionRequest<'a> { - client: &'a Client, - accessed_url: &'a Url, -} - -impl<'a> C2SMentionRequest<'a> { - pub fn from_client(client: &'a Client) -> Result { - if let Some(_) = client.titan { - Ok(Self { - client: &client, - accessed_url: &client.url, - }) - } else { - Err(GementionError::NotTitanResource) - } - } - - pub fn raw_body(&self) -> &[u8] { - titan!(self).content.as_slice() - } - - pub fn mime(&self) -> &str { - titan!(self).mime.as_ref() - } - - pub fn token(&self) -> Option<&str> { - titan!(self).token.as_deref() - } - - pub fn size(&self) -> usize { - titan!(self).size - } - - pub fn certificate(&self) -> Option { - self.client.certificate() - } - - pub fn fingerprint(&self) -> Option { - self.client.fingerprint() - } - - pub fn username(&self) -> Option { - self.client.name() - } - - pub fn target(&self) -> Option<&str> { - self.client.parameter("target") - } - - pub fn content(&self) -> Result<&str, GementionError> { - parse_content_body(self.raw_body()) - } - - /// The full URL that was accessed for the incoming request. - pub fn accessed_url(&self) -> &Url { - self.accessed_url - } -} - -impl<'a> TryFrom<&'a Client> for C2SMentionRequest<'a> { - type Error = GementionError; - - fn try_from(client: &'a Client) -> Result { - C2SMentionRequest::from_client(client) - } -} - #[derive(Debug)] pub(crate) enum MentionType { Reply, @@ -267,28 +175,3 @@ impl Display for MentionType { } } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn parse_comment_body_with_mention_type() -> Result<(), GementionError> { - let body = "reply\nthis is my comment body, which is a reply.".as_bytes(); - let expected = "this is my comment body, which is a reply."; - let parsed = parse_content_body(body)?; - - assert_eq!(expected, parsed); - Ok(()) - } - - #[test] - fn parse_comment_body_without_mention_type() -> Result<(), GementionError> { - let expected = "this is my comment body, which has no mention type."; - let body = expected.as_bytes(); - let parsed = parse_content_body(body)?; - - assert_eq!(expected, parsed); - Ok(()) - } -} diff --git a/src/models/web.rs b/src/models/web.rs index 952432d..7c969de 100644 --- a/src/models/web.rs +++ b/src/models/web.rs @@ -1,5 +1,99 @@ use super::{mentions::Mention, verification::VerificationStatus}; -use fluffer::{async_trait, GemBytes}; +use crate::error::GementionError; +use url::Url; +use crate::models::mentions::MentionType; +use fluffer::{async_trait, Client, GemBytes}; + +macro_rules! titan { + ($self:expr) => { + $self.client.titan.as_ref().unwrap() + }; +} + +fn parse_content_body(raw_body: &[u8]) -> Result<&str, GementionError> { + let content = std::str::from_utf8(raw_body)?; + + // Drop 1st line if it is a mention type. Otherwise return whole thing. + let mention_type = MentionType::try_from(raw_body); + let content: &str = if let Ok(_) = mention_type { + let amt_to_chop = content.lines().next().map(|line| line.len()).unwrap(); + &content[amt_to_chop..] + } else { + content + }; + + Ok(content.trim()) +} + +/// Wraps an incoming Titan request and provides handy methods for +/// extracting the expected information for a client-to-server +/// gemention. +pub(crate) struct C2SMentionRequest<'a> { + client: &'a Client, + accessed_url: &'a Url, +} + +impl<'a> C2SMentionRequest<'a> { + pub fn from_client(client: &'a Client) -> Result { + if let Some(_) = client.titan { + Ok(Self { + client: &client, + accessed_url: &client.url, + }) + } else { + Err(GementionError::NotTitanResource) + } + } + + pub fn raw_body(&self) -> &[u8] { + titan!(self).content.as_slice() + } + + pub fn mime(&self) -> &str { + titan!(self).mime.as_ref() + } + + pub fn token(&self) -> Option<&str> { + titan!(self).token.as_deref() + } + + pub fn size(&self) -> usize { + titan!(self).size + } + + pub fn certificate(&self) -> Option { + self.client.certificate() + } + + pub fn fingerprint(&self) -> Option { + self.client.fingerprint() + } + + pub fn username(&self) -> Option { + self.client.name() + } + + pub fn target(&self) -> Option<&str> { + self.client.parameter("target") + } + + pub fn content(&self) -> Result<&str, GementionError> { + parse_content_body(self.raw_body()) + } + + /// The full URL that was accessed for the incoming request. + pub fn accessed_url(&self) -> &Url { + self.accessed_url + } +} + +impl<'a> TryFrom<&'a Client> for C2SMentionRequest<'a> { + type Error = GementionError; + + fn try_from(client: &'a Client) -> Result { + C2SMentionRequest::from_client(client) + } +} pub(crate) enum MentionResponse { VerifiedMention(Mention), @@ -82,3 +176,28 @@ impl GemBytes for MentionResponse { format!("{}\r\n{}", status_line, body).into_bytes() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_comment_body_with_mention_type() -> Result<(), GementionError> { + let body = "reply\nthis is my comment body, which is a reply.".as_bytes(); + let expected = "this is my comment body, which is a reply."; + let parsed = parse_content_body(body)?; + + assert_eq!(expected, parsed); + Ok(()) + } + + #[test] + fn parse_comment_body_without_mention_type() -> Result<(), GementionError> { + let expected = "this is my comment body, which has no mention type."; + let body = expected.as_bytes(); + let parsed = parse_content_body(body)?; + + assert_eq!(expected, parsed); + Ok(()) + } +} diff --git a/src/validation/c2s.rs b/src/validation/c2s.rs index 8abe38e..9b8f574 100644 --- a/src/validation/c2s.rs +++ b/src/validation/c2s.rs @@ -1,5 +1,5 @@ -use crate::{models::mentions::C2SMentionRequest, error::GementionError}; use super::Validation; +use crate::models::web::C2SMentionRequest; use thiserror::Error; #[derive(Debug, Error)]