pub fn is_chart_keyword_shape(s: &str) -> Result<(), String>Expand description
Predicate: assert that s is a valid chart-keyword shape. The
:etiquetas axis is a per-entry registry-search-tag identifier
that lands in the rendered lareira-<nome> Helm chart’s
Chart.yaml keywords: array via [caixa-helm]’s
build_chart_yaml (folded through a std::collections::BTreeSet
alongside the four substrate-fixed tags lareira / wasm /
tatara-lisp / caixa-servico) and indexes the chart through
Artifact Hub’s keyword-search axis + the future caixa-registry’s
keyword index. The contract — modeled on Cargo’s crates.io
[package] keywords grammar (the parser the crates.io publish API
routes every keywords: entry through at publish time), narrowed
to the strict ASCII subset every realistic search tag uses:
- 1..=
CHART_KEYWORD_MAX_LEN(20) bytes; - first byte: ASCII letter (
A-Zora-z). Leading digit,-,_, whitespace, control, and non-ASCII are each surfaced with a self-locating reason naming the canonical authoring footgun (paste-from-numbered-list"1foo", kebab-leak"-foo", snake-leak"_foo", paste-from-aligned-doc whitespace, paste-from-Unicode-doc non-ASCII); - remaining bytes: ASCII alphanumeric,
_, or-(Cargo’s crates.io-accepted continuation set; tighter thanis_cargo_feature_name’s_/-/+/.continuation set —+and.are not part of the keyword grammar). Whitespace,,///;/.list-separator confusions, control bytes, and non-ASCII bytes are each surfaced with a self-locating reason naming the canonical authoring footgun (multi-tag blob in one entry, CSV-list-belongs-to-list-grammar miscomprehension, CR/LF paste-from-doc, NFC/NFD normalization drift).
Returns the parser-shaped reason on rejection (without wrapping in
any error variant) so each per-axis caller —
crate::Caixa::validate_etiquetas for the universal :etiquetas
axis at validate time, every future per-keyword axis (a future
caixa-registry keyword-index lookup, a future Artifact Hub-keyword
scraper validator, a future per-Aplicacao aggregated keyword set)
— wraps the same reason in its own typed *Invalid { <axis>, reason }
variant.
Empty input is rejected here (defensively) and at each call site
via the narrower crate::ManifestError::EtiquetaEmpty variant —
the same empty-first cascade is_dns_1123_label,
is_gateway_api_http_path, is_wit_world_ref,
is_nats_subject, is_wasi_keyvalue_slot, is_git_ref_name,
is_git_oid, is_git_repo_url, is_cargo_feature_name,
is_spdx_expression_shape, is_chart_description_shape, and
is_chart_maintainer_name_shape all carry at their call sites.
§Errors
Returns the parser-shaped reason naming the specific violation
(length / first-byte-class / continuation-byte-class / whitespace /
control-char / non-ASCII / ,-list-separator-confusion /
/-path-separator-confusion / ;-list-separator-confusion /
.-namespace-confusion), without wrapping in any error variant —
every caller maps the same String into its own typed
*Invalid { <axis>, reason } enum variant.