mod csp;
mod datetime;
mod language;
mod media_query;
mod misc;
mod network;
mod simple;
pub(crate) mod structural;
mod svg_mathml;
use relax_ng::{DatatypeContext, DatatypeLibrary};
const KNOWN_TYPES: &[&str] = &[
"ID",
"IDREF",
"IDREFS",
"non-empty-string",
"string",
"string-without-line-breaks",
"zero",
"integer",
"integer-non-negative",
"integer-positive",
"float",
"float-non-negative",
"float-positive",
"hash-name",
"custom-element-name",
"autocomplete-any",
"browsing-context",
"browsing-context-or-keyword",
"keylabellist",
"language",
"rel-value",
"sandbox-allow-list",
"script-type",
"microdata-property",
"simple-color",
"date",
"datetime-local",
"datetime-tz",
"month",
"time",
"time-datetime",
"week",
"iri",
"iri-ref",
"iri-ref-http-or-https",
"email-address",
"email-address-list",
"mime-type",
"mime-type-list",
"charset",
"meta-charset",
"integrity-metadata",
"refresh",
"color",
"circle",
"polyline",
"rectangle",
"source-size-list",
"image-candidate-strings",
"content-security-policy",
"media-query",
"xml-name",
"svg-pathdata",
];
pub(crate) struct WhatwgDatatypeLibrary;
impl DatatypeLibrary for WhatwgDatatypeLibrary {
fn validate_params(&self, type_name: &str, params: &[(&str, &str)]) -> Result<(), String> {
if !KNOWN_TYPES.contains(&type_name) {
return Err(format!(
"unknown datatype `{type_name}` in library `http://whattf.org/datatype-draft`"
));
}
if !params.is_empty() {
return Err(format!("datatype `{type_name}` does not take parameters"));
}
Ok(())
}
fn matches(
&self,
type_name: &str,
_params: &[(&str, &str)],
value: &str,
_context: &DatatypeContext,
) -> bool {
check(type_name, value).is_ok()
}
fn values_equal(
&self,
type_name: &str,
expected: &str,
_expected_context: &DatatypeContext,
actual: &str,
_actual_context: &DatatypeContext,
) -> bool {
if type_name == "string" {
simple::values_equal_ascii_case_insensitive(expected, actual)
} else {
expected == actual
}
}
}
fn check(type_name: &str, value: &str) -> Result<(), String> {
match type_name {
"ID" => simple::check_id(value),
"IDREF" => simple::check_idref(value),
"IDREFS" => simple::check_idrefs(value),
"non-empty-string" => simple::check_non_empty_string(value),
"string" => simple::check_string(value),
"string-without-line-breaks" => simple::check_string_without_line_breaks(value),
"zero" => simple::check_zero(value),
"integer" => simple::check_integer(value),
"integer-non-negative" => simple::check_integer_non_negative(value),
"integer-positive" => simple::check_integer_positive(value),
"float" => simple::check_float(value),
"float-non-negative" => simple::check_float_non_negative(value),
"float-positive" => simple::check_float_positive(value),
"hash-name" => simple::check_hash_name(value),
"custom-element-name" => structural::check_custom_element_name(value),
"autocomplete-any" => structural::check_autocomplete_any(value),
"browsing-context" => structural::check_browsing_context(value),
"browsing-context-or-keyword" => structural::check_browsing_context_or_keyword(value),
"keylabellist" => structural::check_keylabellist(value),
"language" => language::check_language(value),
"rel-value" => structural::check_rel_value(value),
"sandbox-allow-list" => structural::check_sandbox_allow_list(value),
"script-type" => structural::check_script_type(value),
"microdata-property" => structural::check_microdata_property(value),
"simple-color" => structural::check_simple_color(value),
"date" => datetime::check_date(value),
"datetime-local" => datetime::check_datetime_local(value),
"datetime-tz" => datetime::check_datetime_tz(value),
"month" => datetime::check_month(value),
"time" => datetime::check_time(value),
"time-datetime" => datetime::check_time_datetime(value),
"week" => datetime::check_week(value),
"iri" => network::check_iri(value),
"iri-ref" => network::check_iri_ref(value),
"iri-ref-http-or-https" => network::check_iri_ref_http_or_https(value),
"email-address" => misc::check_email_address(value),
"email-address-list" => misc::check_email_address_list(value),
"mime-type" => network::check_mime_type(value),
"mime-type-list" => network::check_mime_type_list(value),
"charset" => network::check_charset(value),
"meta-charset" => network::check_meta_charset(value),
"integrity-metadata" => network::check_integrity_metadata(value),
"refresh" => network::check_refresh(value),
"color" => misc::check_color(value),
"circle" => misc::check_circle(value),
"polyline" => misc::check_polyline(value),
"rectangle" => misc::check_rectangle(value),
"source-size-list" => misc::check_source_size_list(value),
"image-candidate-strings" => misc::check_image_candidate_strings(value),
"content-security-policy" => csp::check_content_security_policy(value),
"media-query" => media_query::check_media_query(value),
"xml-name" => svg_mathml::check_xml_name(value),
"svg-pathdata" => svg_mathml::check_svg_pathdata(value),
_ => Err(format!("unknown datatype `{type_name}`")),
}
}
#[cfg(test)]
mod tests {
use super::{KNOWN_TYPES, WhatwgDatatypeLibrary};
use relax_ng::{DatatypeContext, DatatypeLibrary};
#[test]
fn all_known_types_validate_params_with_no_params() {
let lib = WhatwgDatatypeLibrary;
for &type_name in KNOWN_TYPES {
assert!(
lib.validate_params(type_name, &[]).is_ok(),
"{type_name} should accept zero params"
);
}
}
#[test]
fn validate_params_rejects_unknown_type() {
let lib = WhatwgDatatypeLibrary;
assert!(lib.validate_params("not-a-real-type", &[]).is_err());
}
#[test]
fn validate_params_rejects_any_params() {
let lib = WhatwgDatatypeLibrary;
assert!(lib.validate_params("zero", &[("foo", "bar")]).is_err());
}
#[test]
fn matches_dispatches_to_the_right_checker() {
let lib = WhatwgDatatypeLibrary;
let ctx = DatatypeContext::default();
assert!(lib.matches("zero", &[], "0", &ctx));
assert!(!lib.matches("zero", &[], "00", &ctx));
assert!(lib.matches("simple-color", &[], "#ff0000", &ctx));
assert!(!lib.matches("simple-color", &[], "red", &ctx));
}
#[test]
fn matches_returns_false_for_unknown_type_instead_of_panicking() {
let lib = WhatwgDatatypeLibrary;
let ctx = DatatypeContext::default();
assert!(!lib.matches("not-a-real-type", &[], "anything", &ctx));
}
#[test]
fn values_equal_is_ascii_case_insensitive_for_string_only() {
let lib = WhatwgDatatypeLibrary;
let ctx = DatatypeContext::default();
assert!(lib.values_equal("string", "Foo", &ctx, "foo", &ctx));
assert!(!lib.values_equal("hash-name", "#Foo", &ctx, "#foo", &ctx));
}
}