use super::*;
use crate::element::*;
use crate::error::{Error, Result};
use regex::Regex;
use serde::{Deserialize, Serialize};
#[inline]
fn is_false(v: &bool) -> bool {
!v
}
#[inline]
fn u32_is_zero(v: &u32) -> bool {
*v == 0
}
#[inline]
fn u32_is_max(v: &u32) -> bool {
*v == u32::MAX
}
#[inline]
fn normalize_is_none(v: &Normalize) -> bool {
matches!(v, Normalize::None)
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields, default)]
pub struct StrValidator {
#[serde(skip_serializing_if = "String::is_empty")]
pub comment: String,
#[serde(rename = "in", skip_serializing_if = "Vec::is_empty")]
pub in_list: Vec<String>,
#[serde(rename = "nin", skip_serializing_if = "Vec::is_empty")]
pub nin_list: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none", with = "serde_regex")]
pub matches: Option<Box<Regex>>,
#[serde(skip_serializing_if = "u32_is_max")]
pub max_len: u32,
#[serde(skip_serializing_if = "u32_is_zero")]
pub min_len: u32,
#[serde(skip_serializing_if = "u32_is_max")]
pub max_char: u32,
#[serde(skip_serializing_if = "u32_is_zero")]
pub min_char: u32,
#[serde(skip_serializing_if = "normalize_is_none")]
pub normalize: Normalize,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub ban_prefix: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub ban_suffix: Vec<String>,
#[serde(skip_serializing_if = "String::is_empty")]
pub ban_char: String,
#[serde(skip_serializing_if = "is_false")]
pub query: bool,
#[serde(skip_serializing_if = "is_false")]
pub regex: bool,
#[serde(skip_serializing_if = "is_false")]
pub ban: bool,
#[serde(skip_serializing_if = "is_false")]
pub size: bool,
}
impl PartialEq for StrValidator {
fn eq(&self, rhs: &Self) -> bool {
(self.comment == rhs.comment)
&& (self.in_list == rhs.in_list)
&& (self.nin_list == rhs.nin_list)
&& (self.max_len == rhs.max_len)
&& (self.min_len == rhs.min_len)
&& (self.max_char == rhs.max_char)
&& (self.min_char == rhs.min_char)
&& (self.normalize == rhs.normalize)
&& (self.ban_prefix == rhs.ban_prefix)
&& (self.ban_suffix == rhs.ban_suffix)
&& (self.ban_char == rhs.ban_char)
&& (self.query == rhs.query)
&& (self.regex == rhs.regex)
&& (self.size == rhs.size)
&& (self.ban == rhs.ban)
&& match (&self.matches, &rhs.matches) {
(None, None) => true,
(Some(_), None) => false,
(None, Some(_)) => false,
(Some(lhs), Some(rhs)) => lhs.as_str() == rhs.as_str(),
}
}
}
impl std::default::Default for StrValidator {
fn default() -> Self {
Self {
comment: String::new(),
in_list: Vec::new(),
nin_list: Vec::new(),
matches: None,
max_len: u32::MAX,
min_len: u32::MIN,
max_char: u32::MAX,
min_char: u32::MIN,
normalize: Normalize::None,
ban_prefix: Vec::new(),
ban_suffix: Vec::new(),
ban_char: String::new(),
query: false,
regex: false,
ban: false,
size: false,
}
}
}
impl StrValidator {
pub fn new() -> Self {
Self::default()
}
pub fn comment(mut self, comment: impl Into<String>) -> Self {
self.comment = comment.into();
self
}
pub fn max_len(mut self, max_len: u32) -> Self {
self.max_len = max_len;
self
}
pub fn min_len(mut self, min_len: u32) -> Self {
self.min_len = min_len;
self
}
pub fn max_char(mut self, max_char: u32) -> Self {
self.max_char = max_char;
self
}
pub fn min_char(mut self, min_char: u32) -> Self {
self.min_char = min_char;
self
}
pub fn normalize(mut self, normalize: Normalize) -> Self {
self.normalize = normalize;
self
}
pub fn matches(mut self, matches: Regex) -> Self {
self.matches = Some(Box::new(matches));
self
}
pub fn in_add(mut self, add: impl Into<String>) -> Self {
self.in_list.push(add.into());
self
}
pub fn nin_add(mut self, add: impl Into<String>) -> Self {
self.nin_list.push(add.into());
self
}
pub fn ban_prefix_add(mut self, add: impl Into<String>) -> Self {
self.ban_prefix.push(add.into());
self
}
pub fn ban_suffix_add(mut self, add: impl Into<String>) -> Self {
self.ban_suffix.push(add.into());
self
}
pub fn ban_char(mut self, ban_char: impl Into<String>) -> Self {
self.ban_char = ban_char.into();
self
}
pub fn query(mut self, query: bool) -> Self {
self.query = query;
self
}
pub fn regex(mut self, regex: bool) -> Self {
self.regex = regex;
self
}
pub fn ban(mut self, ban: bool) -> Self {
self.ban = ban;
self
}
pub fn size(mut self, ord: bool) -> Self {
self.size = ord;
self
}
pub fn build(self) -> Validator {
Validator::Str(Box::new(self))
}
pub(crate) fn validate(&self, parser: &mut Parser) -> Result<()> {
let elem = parser
.next()
.ok_or_else(|| Error::FailValidate("expected a string".to_string()))??;
let val = if let Element::Str(v) = elem {
v
} else {
return Err(Error::FailValidate(format!(
"expected Str, got {}",
elem.name()
)));
};
self.validate_str(val)
}
pub(crate) fn validate_str(&self, val: &str) -> Result<()> {
if (val.len() as u32) > self.max_len {
return Err(Error::FailValidate(
"String is longer than max_len".to_string(),
));
}
if (val.len() as u32) < self.min_len {
return Err(Error::FailValidate(
"String is shorter than min_len".to_string(),
));
}
if self.max_char < u32::MAX || self.min_char > 0 {
let len_char = bytecount::num_chars(val.as_bytes()) as u32;
if len_char > self.max_char {
return Err(Error::FailValidate(
"String is longer than max_len".to_string(),
));
}
if len_char < self.min_char {
return Err(Error::FailValidate(
"String is shorter than min_len".to_string(),
));
}
}
use unicode_normalization::{
is_nfc_quick, is_nfkc_quick, IsNormalized, UnicodeNormalization,
};
match self.normalize {
Normalize::None => {
if !self.in_list.is_empty() && !self.in_list.iter().any(|v| *v == val) {
return Err(Error::FailValidate(
"String is not on `in` list".to_string(),
));
}
if self.nin_list.iter().any(|v| *v == val) {
return Err(Error::FailValidate("String is on `nin` list".to_string()));
}
if let Some(pre) = self.ban_prefix.iter().find(|v| val.starts_with(*v)) {
return Err(Error::FailValidate(format!(
"String begins with banned prefix {:?}",
pre
)));
}
if let Some(suf) = self.ban_suffix.iter().find(|v| val.ends_with(*v)) {
return Err(Error::FailValidate(format!(
"String ends with banned suffix {:?}",
suf
)));
}
if !self.ban_char.is_empty() {
if let Some(c) = val.chars().find(|c| self.ban_char.contains(*c)) {
return Err(Error::FailValidate(format!(
"String contains banned character {:?}",
c
)));
}
}
if let Some(ref regex) = self.matches {
if !regex.is_match(val) {
return Err(Error::FailValidate(
"String doesn't match regular expression".to_string(),
));
}
}
}
Normalize::NFC => {
let temp_string: String;
let val = match is_nfc_quick(val.chars()) {
IsNormalized::Yes => val,
_ => {
temp_string = val.nfc().collect::<String>();
temp_string.as_str()
}
};
if !self.in_list.is_empty() && !self.in_list.iter().any(|v| v.nfc().eq(val.chars()))
{
return Err(Error::FailValidate(
"NFC String is not on `in` list".to_string(),
));
}
if self.nin_list.iter().any(|v| v.nfc().eq(val.chars())) {
return Err(Error::FailValidate(
"NFC String is on `nin` list".to_string(),
));
}
if let Some(pre) = self
.ban_prefix
.iter()
.find(|v| v.nfc().zip(val.chars()).all(|(vc, valc)| vc == valc))
{
return Err(Error::FailValidate(format!(
"NFC String begins with banned prefix {:?}",
pre
)));
}
if !self.ban_suffix.is_empty() {
let mut temp = String::new();
if self.ban_suffix.iter().any(|v| {
temp.clear();
temp.extend(v.nfc());
val.ends_with(&temp)
}) {
return Err(Error::FailValidate(format!(
"NFC String ends with banned suffix {:?}",
temp
)));
}
}
if !self.ban_char.is_empty() {
if let Some(c) = val.chars().find(|c| self.ban_char.contains(*c)) {
return Err(Error::FailValidate(format!(
"NFC String contains banned character {:?}",
c
)));
}
}
if let Some(ref regex) = self.matches {
if !regex.is_match(val) {
return Err(Error::FailValidate(
"String doesn't match regular expression".to_string(),
));
}
}
}
Normalize::NFKC => {
let temp_string: String;
let val = match is_nfkc_quick(val.chars()) {
IsNormalized::Yes => val,
_ => {
temp_string = val.nfkc().collect::<String>();
temp_string.as_str()
}
};
if !self.in_list.is_empty()
&& !self.in_list.iter().any(|v| v.nfkc().eq(val.chars()))
{
return Err(Error::FailValidate(
"NFKC String is not on `in` list".to_string(),
));
}
if self.nin_list.iter().any(|v| v.nfkc().eq(val.chars())) {
return Err(Error::FailValidate(
"NFKC String is on `nin` list".to_string(),
));
}
if let Some(pre) = self
.ban_prefix
.iter()
.find(|v| v.nfkc().zip(val.chars()).all(|(vc, valc)| vc == valc))
{
return Err(Error::FailValidate(format!(
"NFKC String begins with banned prefix {:?}",
pre
)));
}
if !self.ban_suffix.is_empty() {
let mut temp = String::new();
if self.ban_suffix.iter().any(|v| {
temp.clear();
temp.extend(v.nfkc());
val.ends_with(&temp)
}) {
return Err(Error::FailValidate(format!(
"NFKC String ends with banned suffix {:?}",
temp
)));
}
}
if !self.ban_char.is_empty() {
if let Some(c) = val.chars().find(|c| self.ban_char.contains(*c)) {
return Err(Error::FailValidate(format!(
"NFKC String contains banned character {:?}",
c
)));
}
}
if let Some(ref regex) = self.matches {
if !regex.is_match(val) {
return Err(Error::FailValidate(
"NFKC String doesn't match regular expression".to_string(),
));
}
}
}
}
Ok(())
}
pub(crate) fn query_check_str(&self, other: &Self) -> bool {
(self.query || (other.in_list.is_empty() && other.nin_list.is_empty()))
&& (self.regex || other.matches.is_none())
&& (self.ban
|| (other.ban_prefix.is_empty()
&& other.ban_suffix.is_empty()
&& other.ban_char.is_empty()))
&& (self.size
|| (u32_is_max(&other.max_len)
&& u32_is_zero(&other.min_len)
&& u32_is_max(&other.max_char)
&& u32_is_zero(&other.min_char)))
}
pub(crate) fn query_check(&self, other: &Validator) -> bool {
match other {
Validator::Str(other) => self.query_check_str(other),
Validator::Multi(list) => list.iter().all(|other| match other {
Validator::Str(other) => self.query_check_str(other),
_ => false,
}),
Validator::Any => true,
_ => false,
}
}
}