Summary
<filters> in ECMA-376 (§18.3.2.7, CT_Filters) holds any number of <filter val="…"/> children — that element is Excel's checkbox list, and a user who ticks four regions gets four of them.
Workbook::set_auto_filter can only produce one or two. Its criteria arrive as an expression string whose grammar is exactly 3 tokens, or 7 with a conjunction in the middle, so x == EMEA or x == APAC is the widest checkbox list expressible and a third value is rejected outright.
The writer and the reader both handle N values; only the front door caps it.
Repro
Same module setup as #477.
///|
test "three checkbox values are refused" {
let wb = @xlsx.Workbook::new()
wb.new_sheet("S") |> ignore
wb.set_cell_value("S", "A1", @xlsx.CellValue::String("Region"))
try
wb.set_auto_filter("S", "A1:D50", [
{ column: "A", expression: "x == EMEA or x == APAC or x == AMER" },
]) catch {
e => println("three values -> \{@debug.repr(e)}")
} noraise {
_ => println("three values -> accepted")
}
}
Actual:
three values -> InvalidAutoFilter(msg="auto filter expression invalid")
Expected: a filterColumn whose filters holds all three.
Where
xlsx/auto_filter.mbt, parse_filter_expression:
if tokens.length() == 7 {
...
} else if tokens.length() == 3 {
...
} else {
raise InvalidAutoFilter(msg="auto filter expression invalid")
}
Downstream of it, nothing else is limited:
auto_filter_column_from_expression already collapses a chain of == into filters: Some(values) — it just never sees more than two;
- the writer (
write_worksheet_layout_xml.mbt) loops over column.filters and emits one <filter> per value;
- the reader (
read.mbt, parse_auto_filter_columns) parses every <filter> child it finds.
So a file with a five-value checkbox list reads back correctly and cannot be written.
Suggestion
Two options, and the second is the one I would ask for:
-
Widen the grammar to accept an or-chain of any length, folding it into filters when every operator is ==. Smallest change, keeps the API as it is.
-
Expose the structured form. AutoFilterColumn / AutoFilterCustomFilters are already public types, but their fields cannot be constructed outside the package (pub struct, not pub(all)), so the expression string is the only way in. An overload taking a built AutoFilterColumn — or pub(all) on those three structs — would let a caller that already has a typed criterion skip the string round trip entirely.
Option 2 matters for callers that are compilers rather than humans: they hold ["EMEA", "APAC", "AMER"], format it into a string, and hand it to a parser that has to take it apart again — and a value containing a space or an or cannot survive that round trip at all (#477 is one instance of the same seam).
Context
Found while adding per-column auto-filter criteria to yxl, a YAML→xlsx compiler built on bobzhang/mbtexcel. A spec that says which values a column is filtered to is the shape users ask for, and the two-value ceiling is what stops it: filtering to three of five regions is unremarkable in a real workbook.
Summary
<filters>in ECMA-376 (§18.3.2.7,CT_Filters) holds any number of<filter val="…"/>children — that element is Excel's checkbox list, and a user who ticks four regions gets four of them.Workbook::set_auto_filtercan only produce one or two. Its criteria arrive as an expression string whose grammar is exactly 3 tokens, or 7 with a conjunction in the middle, sox == EMEA or x == APACis the widest checkbox list expressible and a third value is rejected outright.The writer and the reader both handle N values; only the front door caps it.
Repro
Same module setup as #477.
Actual:
Expected: a
filterColumnwhosefiltersholds all three.Where
xlsx/auto_filter.mbt,parse_filter_expression:Downstream of it, nothing else is limited:
auto_filter_column_from_expressionalready collapses a chain of==intofilters: Some(values)— it just never sees more than two;write_worksheet_layout_xml.mbt) loops overcolumn.filtersand emits one<filter>per value;read.mbt,parse_auto_filter_columns) parses every<filter>child it finds.So a file with a five-value checkbox list reads back correctly and cannot be written.
Suggestion
Two options, and the second is the one I would ask for:
Widen the grammar to accept an
or-chain of any length, folding it intofilterswhen every operator is==. Smallest change, keeps the API as it is.Expose the structured form.
AutoFilterColumn/AutoFilterCustomFiltersare already public types, but their fields cannot be constructed outside the package (pub struct, notpub(all)), so the expression string is the only way in. An overload taking a builtAutoFilterColumn— orpub(all)on those three structs — would let a caller that already has a typed criterion skip the string round trip entirely.Option 2 matters for callers that are compilers rather than humans: they hold
["EMEA", "APAC", "AMER"], format it into a string, and hand it to a parser that has to take it apart again — and a value containing a space or anorcannot survive that round trip at all (#477 is one instance of the same seam).Context
Found while adding per-column auto-filter criteria to yxl, a YAML→xlsx compiler built on
bobzhang/mbtexcel. A spec that says which values a column is filtered to is the shape users ask for, and the two-value ceiling is what stops it: filtering to three of five regions is unremarkable in a real workbook.