struct Shape {
before: &'static str,
after: &'static str,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum SkipClass {
AuthoringGap,
GeneratorGap,
LanguageLimitation,
}
macro_rules! field_skip_variants {
($($(#[$meta:meta])* $variant:ident : $class:ident => ($before:expr, $after:expr $(,)?)),+ $(,)?) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum FieldSkip {
$($(#[$meta])* $variant,)+
}
impl FieldSkip {
const ALL: &'static [Self] = &[$(Self::$variant,)+];
const fn shape(self) -> Shape {
match self {
$(Self::$variant => Shape { before: $before, after: $after },)+
}
}
pub(crate) const fn class(self) -> SkipClass {
match self {
$(Self::$variant => SkipClass::$class,)+
}
}
}
};
}
field_skip_variants! {
NotAvailableOnResultType: AuthoringGap => ("field ", " not available on result type"),
NotAvailableOnJsonStructResult: AuthoringGap => ("field ", " not available on the JSON-struct result"),
NotAvailableWhenResultIsSimple: LanguageLimitation => ("field ", " not available when result_is_simple"),
NotAvailableInCFfi: LanguageLimitation => ("field ", " not available in C FFI"),
NotAvailableOnGoProcessingResult: AuthoringGap => ("field ", " not available on Go ProcessingResult"),
NotAvailableOnPythonProcessingResult: AuthoringGap => ("field ", " not available on Python ProcessingResult"),
NotAvailableOnRubyProcessingResult: AuthoringGap => ("field ", " not available on Ruby ProcessingResult"),
NotAvailableOnRProcessingResult: AuthoringGap => ("field ", " not available on R ProcessingResult"),
NotAvailableOnNodeProcessingResult: AuthoringGap => ("field ", " not available on Node JsProcessingResult"),
NotAvailableOnGeneratedCsharpResultType: AuthoringGap => (
"field ",
" not available on the generated C# result type",
),
NotAvailableOnDartResultType: AuthoringGap => ("field ", " not available on dart result type"),
NotAvailableOnElixirResultType: AuthoringGap => ("field ", " not available on Elixir result type"),
NotAvailableOnStreamingResultType: GeneratorGap => ("field ", " not available on streaming result type"),
NotApplicableForSimpleResultType: LanguageLimitation => ("field ", " not applicable for simple result type"),
NotAccessibleOnSimpleResultType: LanguageLimitation => ("field ", " not accessible on simple result type"),
ResultIsSimpleForFieldNotAvailable: AuthoringGap => (
"result_is_simple for field ",
" not available on result type",
),
CrossesTaggedUnionBoundaryInDart: LanguageLimitation => (
"field ",
" crosses a tagged-union variant boundary (not expressible in Dart)",
),
CrossesTaggedUnionBoundaryInSwift: LanguageLimitation => (
"field ",
" crosses a tagged-union variant boundary (not expressible in Swift)",
),
ExcludedFromSwiftBinding: LanguageLimitation => (
"field ",
" references a field or type excluded from the Swift binding",
),
CountOnJsonBridgedLeafInSwift: LanguageLimitation => (
"field ",
" has no countable Swift leaf (swift-bridge JSON-bridges it to RustString)",
),
NestedArrayWildcardNotSupported: LanguageLimitation => (
"nested array-wildcard field ",
" not supported",
),
ArrayElementNotSupportedInGleam: LanguageLimitation => (
"array element field ",
" not yet supported in Gleam e2e",
),
EnumVariantAccessorNotAvailableInRuby: LanguageLimitation => (
"enum variant accessor ",
" not available on Ruby (serialized to Hash)",
),
EnumSerializationDiffersInRuby: LanguageLimitation => ("field ", " enum serialization differs in Ruby"),
NoPythonStreamingAccessor: GeneratorGap => ("streaming field ", ": no python accessor"),
StreamingAssertionOnUnsupportedField: GeneratorGap => ("streaming assertion on unsupported field ", ""),
ResultIsSimpleNotOnSimpleResultType: LanguageLimitation => (
"result_is_simple, field ",
" not on simple result type",
),
NotAvailableOnJavaResultType: AuthoringGap => ("field ", " not available on Java result type"),
NotAvailableOnPhpResultType: AuthoringGap => ("field ", " not available on PHP result type"),
NotAvailableOnRResultType: AuthoringGap => ("field ", " not available on R result type"),
}
impl FieldSkip {
pub(crate) fn message(self, field: &str) -> String {
let Shape { before, after } = self.shape();
format!("{before}'{field}'{after}")
}
#[cfg(test)]
pub(crate) fn extract(line: &str) -> Option<&str> {
Self::extract_classified(line).map(|(field, _)| field)
}
pub(crate) fn extract_classified(line: &str) -> Option<(&str, Self)> {
Self::ALL
.iter()
.find_map(|variant| variant.field_in(line).map(|field| (field, *variant)))
}
fn field_in(self, line: &str) -> Option<&str> {
let Shape { before, after } = self.shape();
for (start, _) in line.match_indices(before) {
let rest = &line[start + before.len()..];
let Some(quoted) = rest.strip_prefix('\'') else {
continue;
};
let Some(end) = quoted.find('\'') else {
continue;
};
if quoted[end + 1..].starts_with(after) {
return Some("ed[..end]);
}
}
None
}
}
pub(crate) fn nested_wildcard_skip_line(
indent: &str,
comment_open: &str,
field: &str,
element_sub_path: &str,
) -> Option<String> {
if !element_sub_path.contains("[]") {
return None;
}
Some(format!(
"{indent}{comment_open} skipped: {}",
FieldSkip::NestedArrayWildcardNotSupported.message(field)
))
}
#[cfg(test)]
mod tests {
use super::{FieldSkip, SkipClass, nested_wildcard_skip_line};
#[test]
fn the_load_bearing_classifications_are_pinned() {
assert_eq!(FieldSkip::NotAvailableOnResultType.class(), SkipClass::AuthoringGap);
assert_eq!(
FieldSkip::StreamingAssertionOnUnsupportedField.class(),
SkipClass::GeneratorGap
);
assert_eq!(FieldSkip::NoPythonStreamingAccessor.class(), SkipClass::GeneratorGap);
assert_eq!(
FieldSkip::NotAvailableOnStreamingResultType.class(),
SkipClass::GeneratorGap
);
assert_eq!(
FieldSkip::CrossesTaggedUnionBoundaryInDart.class(),
SkipClass::LanguageLimitation
);
assert_eq!(FieldSkip::NotAvailableInCFfi.class(), SkipClass::LanguageLimitation);
assert_eq!(
FieldSkip::CountOnJsonBridgedLeafInSwift.class(),
SkipClass::LanguageLimitation,
"this guard fires only on fields the resolver ACCEPTED; classifying it as an authoring \
gap makes the backend and the strict gate contradict each other about one field"
);
}
#[test]
fn a_trailing_reason_does_not_break_recognition() {
let line = format!(
" # skipped: {}; this stream's chunks carry no terminal finish_reason, so \
completion is not observable here",
FieldSkip::StreamingAssertionOnUnsupportedField.message("stream_complete")
);
assert_eq!(
FieldSkip::extract_classified(&line),
Some(("stream_complete", FieldSkip::StreamingAssertionOnUnsupportedField)),
"got: {line}"
);
}
#[test]
fn every_class_is_represented() {
for class in [
SkipClass::AuthoringGap,
SkipClass::GeneratorGap,
SkipClass::LanguageLimitation,
] {
assert!(
FieldSkip::ALL.iter().any(|variant| variant.class() == class),
"no variant is classified {class:?}"
);
}
}
#[test]
fn every_variant_round_trips_through_extract() {
for variant in FieldSkip::ALL {
let rendered = format!(" // skipped: {}", variant.message("metadata.format.excel"));
assert_eq!(
FieldSkip::extract(&rendered),
Some("metadata.format.excel"),
"variant {variant:?} rendered `{rendered}` but the gate did not recognise it"
);
}
}
#[test]
fn tagged_union_boundary_wordings_are_recognised() {
let dart = " // skipped: field 'tags' crosses a tagged-union variant boundary (not expressible in Dart)";
assert_eq!(FieldSkip::extract(dart), Some("tags"));
let swift = " // skipped: field 'tags' crosses a tagged-union variant boundary (not expressible in Swift)";
assert_eq!(FieldSkip::extract(swift), Some("tags"));
}
#[test]
fn ruby_serialized_enum_accessor_wording_is_recognised() {
let line = " # skipped: enum variant accessor 'format.excel' not available on Ruby (serialized to Hash)";
assert_eq!(FieldSkip::extract(line), Some("format.excel"));
}
#[test]
fn result_is_simple_template_wording_is_recognised() {
let line = " // skipped: result_is_simple, field 'metadata.title' not on simple result type";
assert_eq!(FieldSkip::extract(line), Some("metadata.title"));
}
#[test]
fn unsupported_assertion_type_wordings_stay_uncounted() {
let synthetic = "\t// skipped: unsupported assertion type on synthetic field 'embeddings'";
assert_eq!(FieldSkip::extract(synthetic), None);
let traversal = " // skipped: unsupported traversal assertion 'equals' on 'pages[].url'";
assert_eq!(FieldSkip::extract(traversal), None);
let streaming = " // skipped: assertion type 'count_min' on field 'chunks' not yet supported for streaming";
assert_eq!(FieldSkip::extract(streaming), None);
let scalar = " // skipped: field 'content' is a scalar String without meaningful .count";
assert_eq!(FieldSkip::extract(scalar), None);
}
#[test]
fn a_line_with_no_marker_is_not_recognised() {
assert_eq!(FieldSkip::extract(" assert result.count == 1"), None);
assert_eq!(FieldSkip::extract(" // skipped: field is a scalar String"), None);
}
#[test]
fn extracts_the_quoted_name_not_a_later_phrase() {
let line = " # skipped: result_is_simple for field 'metadata' not available on result type";
assert_eq!(FieldSkip::extract(line), Some("metadata"));
}
#[test]
fn should_refuse_when_the_element_sub_path_still_carries_a_wildcard() {
assert_eq!(
nested_wildcard_skip_line(" ", "//", "pages[].links[].url", "links[].url").as_deref(),
Some(" // skipped: nested array-wildcard field 'pages[].links[].url' not supported")
);
}
#[test]
fn should_not_refuse_a_single_wildcard() {
assert_eq!(nested_wildcard_skip_line(" ", "#", "links[].url", "url"), None);
}
#[test]
fn should_not_refuse_an_explicit_inner_index() {
assert_eq!(
nested_wildcard_skip_line(" ", "#", "pages[].links[0].url", "links[0].url"),
None
);
}
#[test]
fn the_nested_wildcard_refusal_is_counted_by_the_strict_gate() {
let line = nested_wildcard_skip_line(" ", "#", "pages[].links[].url", "links[].url").unwrap();
assert_eq!(FieldSkip::extract(&line), Some("pages[].links[].url"));
}
}