Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ version = "0.0.1"
fortifier = { path = "./packages/fortifier", version = "0.0.1" }
fortifier-macros = { path = "./packages/fortifier-macros", version = "0.0.1" }
regex = "1.12.2"
serde = "1.0.228"
serde_json = "1.0.145"
tokio = "1.48.0"

[workspace.lints.rust]
Expand Down
2 changes: 1 addition & 1 deletion example/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ repository.workspace = true
version.workspace = true

[dependencies]
fortifier.workspace = true
fortifier = { workspace = true, features = ["email", "regex", "url"] }
regex.workspace = true
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }

Expand Down
2 changes: 1 addition & 1 deletion packages/fortifier-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ quote = "1.0.42"
syn = "2.0.110"

[dev-dependencies]
fortifier.workspace = true
fortifier = { workspace = true, features = ["email"] }
trybuild = "1.0.114"

[lints]
Expand Down
62 changes: 56 additions & 6 deletions packages/fortifier-macros/src/validations/email.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,53 @@
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{Ident, Result, meta::ParseNestedMeta};
use syn::{Ident, LitBool, LitInt, Result, meta::ParseNestedMeta};

use crate::validation::Validation;

#[derive(Default)]
pub struct Email {}
pub struct Email {
allow_display_text: bool,
allow_domain_literal: bool,
minimum_sub_domains: usize,
}

impl Default for Email {
fn default() -> Self {
Self {
allow_display_text: false,
allow_domain_literal: true,
minimum_sub_domains: 0,
}
}
}

impl Validation for Email {
fn parse(_meta: &ParseNestedMeta<'_>) -> Result<Self> {
Ok(Email::default())
fn parse(meta: &ParseNestedMeta<'_>) -> Result<Self> {
let mut result = Email::default();

if !meta.input.is_empty() {
meta.parse_nested_meta(|meta| {
if meta.path.is_ident("allow_display_text") {
let lit: LitBool = meta.value()?.parse()?;
result.allow_display_text = lit.value;

Ok(())
} else if meta.path.is_ident("allow_domain_literal") {
let lit: LitBool = meta.value()?.parse()?;
result.allow_domain_literal = lit.value;

Ok(())
} else if meta.path.is_ident("minimum_sub_domains") {
let lit: LitInt = meta.value()?.parse()?;
result.minimum_sub_domains = lit.base10_parse()?;

Ok(())
} else {
Err(meta.error("unknown parameter"))
}
})?;
}

Ok(result)
}

fn is_async(&self) -> bool {
Expand All @@ -25,8 +63,20 @@ impl Validation for Email {
}

fn tokens(&self, expr: &TokenStream) -> TokenStream {
let allow_display_text = self.allow_display_text;
let allow_domain_literal = self.allow_domain_literal;
let minimum_sub_domains = self.minimum_sub_domains;

quote! {
#expr.validate_email()
{
const EMAIL_ADDRESS_OPTIONS: EmailOptions = EmailOptions {
allow_display_text: #allow_display_text,
allow_domain_literal: #allow_domain_literal,
minimum_sub_domains: #minimum_sub_domains,
};

#expr.validate_email(EMAIL_ADDRESS_OPTIONS)
}
}
}
}
6 changes: 3 additions & 3 deletions packages/fortifier-macros/src/validations/length.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ use crate::validation::Validation;

#[derive(Default)]
pub struct Length {
pub equal: Option<Expr>,
pub min: Option<Expr>,
pub max: Option<Expr>,
equal: Option<Expr>,
min: Option<Expr>,
max: Option<Expr>,
}

impl Validation for Length {
Expand Down
2 changes: 1 addition & 1 deletion packages/fortifier-macros/src/validations/regex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use syn::{Expr, Ident, Result, meta::ParseNestedMeta};
use crate::validation::Validation;

pub struct Regex {
pub expression: Expr,
expression: Expr,
}

impl Validation for Regex {
Expand Down
11 changes: 10 additions & 1 deletion packages/fortifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,26 @@ repository.workspace = true
version.workspace = true

[features]
default = ["macros", "regex", "url"]
default = ["macros"]
email = ["dep:email_address"]
indexmap = ["dep:indexmap"]
macros = ["dep:fortifier-macros"]
message = []
regex = ["dep:regex"]
serde = ["dep:serde", "email_address/serde_support"]
url = ["dep:url"]

[dependencies]
email_address = { version = "0.2.9", default-features = false, optional = true }
fortifier-macros = { workspace = true, optional = true }
indexmap = { version = "2.12.0", optional = true }
regex = { workspace = true, optional = true }
serde = { workspace = true, features = ["derive"], optional = true }
url = { version = "2.5.7", optional = true }

[dev-dependencies]
pretty_assertions = "1.4.1"
serde_json.workspace = true

[lints]
workspace = true
17 changes: 12 additions & 5 deletions packages/fortifier/src/validate.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
use std::{
error::Error,
fmt::{self, Display},
fmt::{self, Debug, Display},
pin::Pin,
};

/// Validation errors.
#[derive(Debug)]
pub struct ValidationErrors<E: Error>(Vec<E>);
#[derive(Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct ValidationErrors<E>(Vec<E>);

impl<E: Error> Display for ValidationErrors<E> {
impl<E: Debug> Display for ValidationErrors<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.0)
}
}

impl<E: Error> Error for ValidationErrors<E> {}

impl<E: Error> From<Vec<E>> for ValidationErrors<E> {
impl<E> FromIterator<E> for ValidationErrors<E> {
fn from_iter<T: IntoIterator<Item = E>>(iter: T) -> Self {
Self(Vec::from_iter(iter))
}
}

impl<E> From<Vec<E>> for ValidationErrors<E> {
fn from(value: Vec<E>) -> Self {
Self(value)
}
Expand Down
2 changes: 2 additions & 0 deletions packages/fortifier/src/validations.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
#[cfg(feature = "email")]
mod email;
mod length;
#[cfg(feature = "regex")]
mod regex;
#[cfg(feature = "url")]
mod url;

#[cfg(feature = "email")]
pub use email::*;
pub use length::*;
#[cfg(feature = "regex")]
Expand Down
Loading
Loading