bomboni_request 0.5.0

Utilities for working with API requests. Part of Bomboni library.
Documentation
WHITESPACE = _{ " " | "\t" | "\r" | "\n" }

// Filter, possibly empty.
Filter = {
    SOI ~ Expression? ~ EOI
}

// Expressions may either be a conjunction (AND) of sequences or a simple
// sequence.
// 
// Note, the AND is case-sensitive.
// 
// Example: `a b AND c AND d`
// 
// The expression `(a b) AND c AND d` is equivalent to the example.
Expression = {
    Sequence ~ ("AND" ~ Sequence)*
}

// Sequence is composed of one or more whitespace (WHITESPACE) separated factors.
// 
// A sequence expresses a logical relationship between 'factors' where
// the ranking of a filter result may be scored according to the number
// factors that match and other such criteria as the proximity of factors
// to each other within a document.
// 
// When filters are used with exact match semantics rather than fuzzy
// match semantics, a sequence is equivalent to AND.
// 
// Example: `New York Giants OR Yankees`
// 
// The expression `New York (Giants OR Yankees)` is equivalent to the
// example.
Sequence = _{
    Factor ~ Factor*
}

// Factors may either be a disjunction (OR) of terms or a simple term.
// 
// Note, the OR is case-sensitive.
// 
// Example: `a < 10 OR a >= 100`
Factor = {
    Term ~ ("OR" ~ Term)*
}

// Terms may either be unary or simple expressions.
// 
// Unary expressions negate the simple expression, either mathematically `-`
// or logically `NOT`. The negation styles may be used interchangeably.
// 
// Note, the `NOT` is case-sensitive and must be followed by at least one
// whitespace (WHITESPACE).
// 
// Examples:
// * logical not     : `NOT (a OR b)`
// * alternative not : `-file:".java"`
// * negation        : `-30`
Term = @{
    ("NOT" ~ WHITESPACE+)? ~ Simple
}

// Simple expressions may either be a restriction or a nested (composite)
// expression.
Simple = _{
    Restriction
  | Composite
}

// Restrictions express a relationship between a comparable value and a
// single argument. When the restriction only specifies a comparable
// without an operator, this is a global restriction.
// 
// Note, restrictions are not whitespace sensitive.
// 
// Examples:
// * equality         : `package=com.google`
// * inequality       : `msg != 'hello'`
// * greater than     : `1 > 0`
// * greater or equal : `2.5 >= 2.4`
// * less than        : `yesterday < request.time`
// * less or equal    : `experiment.rollout <= cohort(request.user)`
// * has              : `map:key`
// * global           : `prod`
// 
// In addition to the global, equality, and ordering operators, filters
// also support the has (`:`) operator. The has operator is unique in
// that it can test for presence or value based on the proto3 type of
// the `comparable` value. The has operator is useful for validating the
// structure and contents of complex values.
Restriction = !{
    Comparable ~ (Comparator ~ Argument)?
}

// Comparable may either be a member, function or a value.
Comparable = {
    Function
  | Value
  | Name
}

// Function calls may use simple or qualified names with zero or more
// arguments.
// 
// All functions declared within the list filter, apart from the special
// `arguments` function must be provided by the host service.
// 
// Examples:
// * `regex(m.key, '^.*prod.*$')`
// * `math.mem('30mb')`
// 
// Antipattern: simple and qualified function names may include keywords:
// NOT, AND, OR. It is not recommended that any of these names be used
// within functions exposed by a service that supports list filters.
Function = {
    Name ~ "(" ~ ArgumentList? ~ ")"
}

// Comparators supported by list filters.
Comparator = {
    "<="
  | "<"
  | ">="
  | ">"
  | "!="
  | "="
  | ":"
}

// Composite is a parenthesized expression, commonly used to group
// terms or clarify operator precedence.
// 
// Example: `(msg.endsWith('world') AND retries < 10)`
Composite = !{
    "(" ~ Expression ~ ")"
}

Name = @{
    Identifier ~ ("." ~ Identifier)*
}

Identifier = ${ !Keyword ~ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "_")* }

Keyword = { "AND" | "OR" | "NOT" }

ArgumentList = _{
    Argument ~ ("," ~ Argument)*
}

Argument = _{
    Comparable
  | Composite
}

Value = _{ String | Boolean | Number | Any }

String = ${ "\"" ~ Inner ~ "\"" }
Inner  = @{ Char* }
Char   =  {
    !("\"" | "\\") ~ ANY
  | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t")
  | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4})
}

Boolean = { "true" | "false" }

Number = @{
    "-"? ~ ("0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*) ~ ("." ~ ASCII_DIGIT*)? ~ (^"e" ~ ("+" | "-")? ~ ASCII_DIGIT+)?
}

Any = { "*" }