#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Namespace {
GraphqlSchema,
GrpcSchema,
Bot,
ApiSchema,
}
impl Namespace {
pub const fn prefix(self) -> &'static str {
match self {
Namespace::GraphqlSchema => "schema-graphql",
Namespace::GrpcSchema => "schema-grpc",
Namespace::Bot => "bot",
Namespace::ApiSchema => "schema-api",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Rule {
ns: Namespace,
leaf: &'static str,
}
impl Rule {
const fn new(ns: Namespace, leaf: &'static str) -> Self {
Self { ns, leaf }
}
pub const fn namespace(self) -> Namespace {
self.ns
}
pub fn id(self) -> String {
format!("{}-{}", self.ns.prefix(), self.leaf)
}
}
pub const GRAPHQL_PARSE_ERROR: Rule = Rule::new(Namespace::GraphqlSchema, "parse-error");
pub const GRAPHQL_UNKNOWN_FIELD: Rule = Rule::new(Namespace::GraphqlSchema, "unknown-field");
pub const GRPC_UNKNOWN_METHOD: Rule = Rule::new(Namespace::GrpcSchema, "unknown-method");
pub const GRPC_DECODE_ERROR: Rule = Rule::new(Namespace::GrpcSchema, "decode-error");
pub const GRPC_UNKNOWN_FIELD: Rule = Rule::new(Namespace::GrpcSchema, "unknown-field");
pub const BOT_BROWSER_IMPERSONATION: Rule = Rule::new(Namespace::Bot, "browser-impersonation");
pub const API_UNKNOWN_ROUTE: Rule = Rule::new(Namespace::ApiSchema, "unknown-route");
pub const API_METHOD_NOT_ALLOWED: Rule = Rule::new(Namespace::ApiSchema, "method-not-allowed");
pub const API_UNKNOWN_PARAM: Rule = Rule::new(Namespace::ApiSchema, "unknown-param");
pub const API_MISSING_REQUIRED: Rule = Rule::new(Namespace::ApiSchema, "missing-required");
pub const API_TYPE_VIOLATION: Rule = Rule::new(Namespace::ApiSchema, "type-violation");
pub const API_UNKNOWN_PROPERTY: Rule = Rule::new(Namespace::ApiSchema, "unknown-property");
pub const API_CONTENT_TYPE: Rule = Rule::new(Namespace::ApiSchema, "content-type");
pub const API_BODY_PARSE_ERROR: Rule = Rule::new(Namespace::ApiSchema, "body-parse-error");
pub const ALL: &[Rule] = &[
GRAPHQL_PARSE_ERROR,
GRAPHQL_UNKNOWN_FIELD,
GRPC_UNKNOWN_METHOD,
GRPC_DECODE_ERROR,
GRPC_UNKNOWN_FIELD,
BOT_BROWSER_IMPERSONATION,
API_UNKNOWN_ROUTE,
API_METHOD_NOT_ALLOWED,
API_UNKNOWN_PARAM,
API_MISSING_REQUIRED,
API_TYPE_VIOLATION,
API_UNKNOWN_PROPERTY,
API_CONTENT_TYPE,
API_BODY_PARSE_ERROR,
];
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
#[test]
fn every_rule_id_is_namespaced_and_unique() {
let mut seen = HashSet::new();
for rule in ALL {
let id = rule.id();
let ns = rule.namespace().prefix();
assert!(
id.starts_with(ns) && id.len() > ns.len() + 1 && id.as_bytes()[ns.len()] == b'-',
"{id} is not under its namespace prefix `{ns}-`"
);
assert!(seen.insert(id.clone()), "duplicate rule_id in the registry: {id}");
}
}
#[test]
fn ids_are_the_expected_wire_strings() {
assert_eq!(GRAPHQL_PARSE_ERROR.id(), "schema-graphql-parse-error");
assert_eq!(GRAPHQL_UNKNOWN_FIELD.id(), "schema-graphql-unknown-field");
assert_eq!(GRPC_UNKNOWN_METHOD.id(), "schema-grpc-unknown-method");
assert_eq!(GRPC_DECODE_ERROR.id(), "schema-grpc-decode-error");
assert_eq!(GRPC_UNKNOWN_FIELD.id(), "schema-grpc-unknown-field");
assert_eq!(BOT_BROWSER_IMPERSONATION.id(), "bot-browser-impersonation");
assert_eq!(API_UNKNOWN_ROUTE.id(), "schema-api-unknown-route");
assert_eq!(API_METHOD_NOT_ALLOWED.id(), "schema-api-method-not-allowed");
assert_eq!(API_UNKNOWN_PARAM.id(), "schema-api-unknown-param");
assert_eq!(API_MISSING_REQUIRED.id(), "schema-api-missing-required");
assert_eq!(API_TYPE_VIOLATION.id(), "schema-api-type-violation");
assert_eq!(API_UNKNOWN_PROPERTY.id(), "schema-api-unknown-property");
assert_eq!(API_CONTENT_TYPE.id(), "schema-api-content-type");
assert_eq!(API_BODY_PARSE_ERROR.id(), "schema-api-body-parse-error");
}
}