Skip to content
Open
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
2 changes: 1 addition & 1 deletion cargo-typify/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ impl std::str::FromStr for Name {
if value.len() > 63usize {
return Err("longer than 63 characters");
}
if regress::Regex::new("^(?![0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$)^[a-z][a-z0-9-]*[a-zA-Z0-9]$")
if regress::Regex::with_flags("^(?![0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$)^[a-z][a-z0-9-]*[a-zA-Z0-9]$", "u")
.unwrap()
.find(value)
.is_none()
Expand Down
22 changes: 19 additions & 3 deletions typify-impl/src/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -875,11 +875,12 @@ impl TypeSpace {

Some(validation) => {
if let Some(pattern) = &validation.pattern {
let _ =
regress::Regex::new(pattern).map_err(|e| Error::InvalidSchema {
let _ = regress::Regex::with_flags(pattern, "u").map_err(|e| {
Error::InvalidSchema {
type_name: type_name.clone().into_option(),
reason: format!("invalid pattern '{}' {}", pattern, e),
})?;
}
})?;
self.uses_regress = true;
}

Expand Down Expand Up @@ -2192,6 +2193,21 @@ mod tests {
assert_eq!(type_space.iter_types().count(), 4);
}

#[test]
fn test_unicode_pattern_rejects_identity_escape() {
let schema = serde_json::from_value(json!({
"title": "InvalidUnicodePattern",
"type": "string",
"pattern": r"\a",
}))
.unwrap();

assert!(matches!(
TypeSpace::default().add_root_schema(schema),
Err(Error::InvalidSchema { .. })
));
}

#[test]
fn test_basic_option_flat() {
#[derive(JsonSchema, Schema)]
Expand Down
2 changes: 1 addition & 1 deletion typify-impl/src/type_entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1596,7 +1596,7 @@ impl TypeEntry {
let err = format!("doesn't match pattern \"{}\"", p);
quote! {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> = ::std::sync::LazyLock::new(|| {
::regress::Regex::new(#p).unwrap()
::regress::Regex::with_flags(#p, "u").unwrap()
});
if PATTERN.find(value).is_none() {
return Err(#err.into());
Expand Down
21 changes: 20 additions & 1 deletion typify-impl/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -909,7 +909,7 @@ impl StringValidator {
.pattern
.as_ref()
.map(|pattern| {
regress::Regex::new(pattern).map_err(|e| Error::InvalidSchema {
regress::Regex::with_flags(pattern, "u").map_err(|e| Error::InvalidSchema {
type_name: type_name.clone().into_option(),
reason: format!("invalid pattern '{}' {}", pattern, e),
})
Expand Down Expand Up @@ -1195,6 +1195,25 @@ mod tests {
assert!(!ach.is_valid("Abednego"));
}

#[test]
fn test_string_validation_unicode_pattern() {
for (pattern, valid, invalid) in [
(r"^\p{Letter}+$", "héllo", "123"),
(r"^\u{1F354}$", "🍔", "a"),
] {
let validator = StringValidator::new(
&Name::Unknown,
Some(&StringValidation {
pattern: Some(pattern.to_string()),
..Default::default()
}),
)
.unwrap();
assert!(validator.is_valid(valid), "{pattern} should match {valid}");
assert!(!validator.is_valid(invalid));
}
}

#[test]
fn test_string_validation_multi_byte() {
// minLength/maxLength count Unicode code points, not UTF-8 bytes.
Expand Down
36 changes: 26 additions & 10 deletions typify-impl/tests/vega.out
Original file line number Diff line number Diff line change
Expand Up @@ -7165,7 +7165,9 @@ impl ::std::convert::TryFrom<&str>
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^(date|utc):.*$").unwrap());
::std::sync::LazyLock::new(|| {
::regress::Regex::with_flags("^(date|utc):.*$", "u").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^(date|utc):.*$\"".into());
}
Expand Down Expand Up @@ -7451,7 +7453,9 @@ impl ::std::convert::TryFrom<&str>
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^(date|utc):.*$").unwrap());
::std::sync::LazyLock::new(|| {
::regress::Regex::with_flags("^(date|utc):.*$", "u").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^(date|utc):.*$\"".into());
}
Expand Down Expand Up @@ -7779,7 +7783,9 @@ impl ::std::convert::TryFrom<&str>
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^(date|utc):.*$").unwrap());
::std::sync::LazyLock::new(|| {
::regress::Regex::with_flags("^(date|utc):.*$", "u").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^(date|utc):.*$\"".into());
}
Expand Down Expand Up @@ -8112,7 +8118,9 @@ impl ::std::convert::TryFrom<&str>
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^(date|utc):.*$").unwrap());
::std::sync::LazyLock::new(|| {
::regress::Regex::with_flags("^(date|utc):.*$", "u").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^(date|utc):.*$\"".into());
}
Expand Down Expand Up @@ -8635,7 +8643,9 @@ impl ::std::convert::TryFrom<&str>
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^(date|utc):.*$").unwrap());
::std::sync::LazyLock::new(|| {
::regress::Regex::with_flags("^(date|utc):.*$", "u").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^(date|utc):.*$\"".into());
}
Expand Down Expand Up @@ -8921,7 +8931,9 @@ impl ::std::convert::TryFrom<&str>
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^(date|utc):.*$").unwrap());
::std::sync::LazyLock::new(|| {
::regress::Regex::with_flags("^(date|utc):.*$", "u").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^(date|utc):.*$\"".into());
}
Expand Down Expand Up @@ -9249,7 +9261,9 @@ impl ::std::convert::TryFrom<&str>
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^(date|utc):.*$").unwrap());
::std::sync::LazyLock::new(|| {
::regress::Regex::with_flags("^(date|utc):.*$", "u").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^(date|utc):.*$\"".into());
}
Expand Down Expand Up @@ -9582,7 +9596,9 @@ impl ::std::convert::TryFrom<&str>
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^(date|utc):.*$").unwrap());
::std::sync::LazyLock::new(|| {
::regress::Regex::with_flags("^(date|utc):.*$", "u").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^(date|utc):.*$\"".into());
}
Expand Down Expand Up @@ -11176,7 +11192,7 @@ impl ::std::convert::TryFrom<&str> for EncodeKey {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^.+$").unwrap());
::std::sync::LazyLock::new(|| ::regress::Regex::with_flags("^.+$", "u").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^.+$\"".into());
}
Expand Down Expand Up @@ -42548,7 +42564,7 @@ impl ::std::convert::TryFrom<&str> for TitleObjectEncodeSubtype0Key {
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^(?!interactive|name|style).+$").unwrap()
::regress::Regex::with_flags("^(?!interactive|name|style).+$", "u").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^(?!interactive|name|style).+$\"".into());
Expand Down
4 changes: 2 additions & 2 deletions typify/tests/schemas/id-or-name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ impl ::std::convert::TryFrom<&str> for IdOrYoloYolo {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new(".*").unwrap());
::std::sync::LazyLock::new(|| ::regress::Regex::with_flags(".*", "u").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \".*\"".into());
}
Expand Down Expand Up @@ -232,7 +232,7 @@ impl ::std::convert::TryFrom<&str> for Name {
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> = ::std::sync::LazyLock::new(
|| {
:: regress :: Regex :: new ("^(?![0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$)^[a-z][a-z0-9-]*[a-zA-Z0-9]$") . unwrap ()
:: regress :: Regex :: with_flags ("^(?![0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$)^[a-z][a-z0-9-]*[a-zA-Z0-9]$" , "u") . unwrap ()
},
);
if PATTERN.find(value).is_none() {
Expand Down
2 changes: 1 addition & 1 deletion typify/tests/schemas/merged-schemas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ impl ::std::convert::TryFrom<&str> for TriplePattern {
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("(?=^[a-z].+$)(?=^.{4,8}$)(?=.+[a-z]$)").unwrap()
::regress::Regex::with_flags("(?=^[a-z].+$)(?=^.{4,8}$)(?=.+[a-z]$)", "u").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"(?=^[a-z].+$)(?=^.{4,8}$)(?=.+[a-z]$)\"".into());
Expand Down
4 changes: 3 additions & 1 deletion typify/tests/schemas/property-pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ impl ::std::convert::TryFrom<&str> for TestGrammarForPatternPropertiesRulesKey {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("^[a-zA-Z_]\\w*$").unwrap());
::std::sync::LazyLock::new(|| {
::regress::Regex::with_flags("^[a-zA-Z_]\\w*$", "u").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[a-zA-Z_]\\w*$\"".into());
}
Expand Down
2 changes: 1 addition & 1 deletion typify/tests/schemas/types-with-more-impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ impl ::std::convert::TryFrom<&str> for PatternString {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new("xx").unwrap());
::std::sync::LazyLock::new(|| ::regress::Regex::with_flags("xx", "u").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"xx\"".into());
}
Expand Down
4 changes: 2 additions & 2 deletions typify/tests/schemas/various-enums-json-schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ impl ::std::convert::TryFrom<&str> for Ipv4Net {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new(".*").unwrap());
::std::sync::LazyLock::new(|| ::regress::Regex::with_flags(".*", "u").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \".*\"".into());
}
Expand Down Expand Up @@ -496,7 +496,7 @@ impl ::std::convert::TryFrom<&str> for Ipv6Net {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new(".*").unwrap());
::std::sync::LazyLock::new(|| ::regress::Regex::with_flags(".*", "u").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \".*\"".into());
}
Expand Down
4 changes: 2 additions & 2 deletions typify/tests/schemas/various-enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ impl ::std::convert::TryFrom<&str> for Ipv4Net {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new(".*").unwrap());
::std::sync::LazyLock::new(|| ::regress::Regex::with_flags(".*", "u").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \".*\"".into());
}
Expand Down Expand Up @@ -437,7 +437,7 @@ impl ::std::convert::TryFrom<&str> for Ipv6Net {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| ::regress::Regex::new(".*").unwrap());
::std::sync::LazyLock::new(|| ::regress::Regex::with_flags(".*", "u").unwrap());
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \".*\"".into());
}
Expand Down