Skip to main content

Module attribute_text

Module attribute_text 

Expand description

Lexical scanning of .cstack attribute text: where string literals are, where a trailing // comment starts, and where one attribute ends and the next begins.

Shared by cratestack-parser (which strips comments and splits attributes when it builds super::Attribute::raw, and refuses shapes no generator reads) and cratestack-macros (which re-checks every policy attribute before generating code, GHSA-69g4-xvcm-vm2j), so the two cannot disagree about what is inside a string.

Three string forms, matching what the readers accept:

  • """…""" is verbatim (a SQL body, super::sql_body): nothing escapes, a lone " inside is ordinary, only """ closes it.
  • "…" and '…' (policy literals may be single-quoted): only the same quote closes, and a backslash escapes the next character.

Outside strings, (/) and [/] nest.

Invisible characters are refused in attribute text wherever they stand, strings included (invisible_character), and a diagnostic never quotes one raw (escape_for_diagnostic).

Structs§

Unquoted
A character of attribute text that lies outside every string literal.

Constants§

PRIMARY_KEY_ATTRIBUTE
The one spelling that marks a field as its model’s (or view’s) primary key.

Functions§

attribute_starts
Byte offsets of every @ in raw that starts another attribute: one outside any string literal and any (...)/[...] group, after the text’s own leading @s, and not the second @ of an @@.
comment_start
Byte offset of the first // outside a string literal: the start of a trailing comment, which runs to the end of the line.
describe_invisible_character
U+200B (zero width space): the code point, with a name for the ones most likely to be met.
escape_for_diagnostic
text with every invisible or control character except line feed and tab written as \u{…} (module doc).
field_attribute_name
The bare name of a field-level attribute, with the @ sigil and any (...) argument list removed: @length(min: 1) -> Some("length"). None for model-level @@... text or text without the sigil.
group_end
Byte offset just past the )/] that closes the group opened at open (which must be a ( or [ outside any string), or None when the text ends first.
invisible_character
The byte offset and value of the first invisible character in the attribute text raw, inside a string literal or not. Whether raw is a policy attribute (is_policy_attribute) is read from raw itself.
invisible_character_in_policy
invisible_character for text the caller has read as a policy attribute itself: every variation selector is refused.
is_default_ignorable
Whether ch is a Unicode 16.0 Default_Ignorable_Code_Point.
is_policy_attribute
Whether any attribute name in raw, read loosely (super::loose_attribute_names: any case, invisible and punctuation characters dropped), is allow, deny or authorize.
is_primary_key_attribute
Whether attribute marks its field as the primary key. Exact match on PRIMARY_KEY_ATTRIBUTE: @identity, @idx and @id_foo are other (unknown) attributes, not a primary key.
is_relation_attribute
Whether attribute is a @relation / @relation(...) declaration — by name, so a bare @relation counts too. @relationship does not.
loose_attribute_names
The name of every attribute in raw, read loosely: after each run of @s outside strings and groups, whitespace is skipped, the text up to the next (, whitespace, @ or quote is taken, everything in it but letters, digits and _ is dropped, and the rest is lower-cased. A default-ignorable character is dropped even when it counts as a letter (the Hangul filler U+3164 does). @ Deny (x) gives deny, and so do @@de\u{200B}ny(x) (a zero-width space) and @@de-ny(x): an invisible or punctuation character inside the name must not turn a policy into an unknown, inert attribute. For re-checking that no attribute a reader would take for a policy was skipped, whatever its spelling.
split_attributes
raw cut into attributes at every attribute_starts offset that whitespace precedes, each with its byte offset in raw and trimmed. An @ with no whitespace before it (@deny(x)@allow(y)) does not cut, so the run-together text reaches the validators whole and is refused there with a message naming both halves.
strip_comment
raw without its trailing // comment, if any, and without the whitespace before it.
substitute_for_display
text with every invisible character that is not whitespace replaced by one visible character, so it keeps its length in characters (module doc).
unquoted
Every character of raw outside a string literal, in order. Quote characters themselves are not included.