Releases: TriasDev/tabular
Releases · TriasDev/tabular
Release list
v0.7.0
v0.6.0
0.6.0 (2026-10-05)
⚠ BREAKING CHANGES
- the concrete cursors (CsvCursor, XlsxCursor, OdsCursor, ArchiveCursor, GzipCursor) and CsvDialectDetector are internal, so TabularFile.Open is the only way to a cursor; every public type is in the TriasDev.Tabular namespace, the format options included; CsvCursorOptions.Dialect is replaced by the Delimiter, Encoding and Quote hints and CsvDialect is output-only; the writer reports its limits as TabularWriteException (write.too-many-rows, write.too-many-merges, write.too-many-styles) instead of TabularLimitException; row numbers are int on both sides (TabularExport returns ValueTask, AnalysisProgress.RowsRead is int); HorizontalAlignment is CellHorizontalAlignment, TabularWriter.Style is RegisterStyle, CellStyle.Number and Date are NumberFormat and DateFormat; NumberFormat.Parse and DateFormat.Parse throw FormatException; typed import fields come only from their factories; CursorDiagnostics has no public constructor.
Features
- count a column's rows that fall in a set of values (5b773ae)
- declare field groups as alternatives in priority order (d819eff)
- hand a mapper the group that locates its row (5b6ba1d)
- judge field alternatives per row during extraction (8adec9f)
- precheck what a mapping decides about field alternatives (a27d6c6)
- read gzip-compressed files as the file inside them (#80) (78e1223)
- read tar and tar.gz archives as one workbook (#82) (faf0be7)
- report how each set of alternatives covers the rows (d691f7c)
- review a whole file through a mapping before importing it (ac2758c)
- write: cell styles for xlsx and ods (#90) (45dbca8)
- write: ColumnBatch — rows given by column, typed, with a style per column (#93) (13f2807)
- write: sheet layout — header style, frozen panes, auto-filter, merged cells (#91) (c158848)
- write: TabularExport<T> — an export declared once with typed columns (#84) (78f2379)
- write: TabularFormat.Zip — a zip of csv sheets (#94) (1e8e23c)
- write: write csv through TabularWriter, round-tripping through the import (#77) (5de3450)
- write: write OpenDocument spreadsheets through TabularWriter (#83) (8567fa1)
- write: write xlsx through TabularWriter, with our own streaming zip writer (#81) (6451e59)
Bug Fixes
- an invalid value locates nothing, an unneeded one costs nothing, and the precheck does not block AllOrNothing for rows that import (547cc20)
- archive: refuse a tar metadata entry that claims more than it can hold, instead of letting TarReader's InvalidOperationException escape (#99) (7363a42), closes #92
- csv: find the delimiter when the dialect probe ends inside a multi-line quoted field (#101) (916adf8)
- the deferred archive and OpenDocument findings (#102) (d88ea71)
- write: keep a failed write's exception on close; sheet options for a declared export (#98) (0fea097)
Code Refactoring
v0.5.0
0.5.0 (2026-09-29)
Features
- count the rows of every distinct value, and give precheck rule findings their exact affected rows (#64) (52ef740)
Bug Fixes
- analysis: a date written with a culture's own separator ranks that culture first, and the facts say when date readings disagree (#59) (1fa72b3)
- analysis: numbers that read under both separators rank the one the sheet's evidence favours, and the facts say when number readings disagree (#62) (8b14042)
v0.4.0
0.4.0 (2026-09-28)
Features
- count, and on request import, a decimal written with the other separator where it can be read no other way (42c26ad)
- say whether a sheet is hidden, on SheetInfo and SheetProfile (#55) (88820a6)
Bug Fixes
- analysis,import: read a decimal written in exponential notation, in the profile and the import alike (#52) (b174704)
- analysis: empty cells past a row's last value are padding, not columns (#53) (f9783ba)
- analysis: when the distinct budget runs out, settled and rightmost columns give theirs up, so the key keeps an answer (#54) (eea4c64)
- csv: a quoted value whose line would overfill the record is not read as a stray quote (69a7b47)
- mapping: a pattern on the linear engine runs without a clock, so a valid value cannot fail on a slow first match (994cb1f)
- xlsx,ods: read a line end written as it is the way XML does, as a line feed (fc203bb)
- xlsx: a number format counts only inside numFmts, not in a conditional format's dxf (43cbda2)
- xlsx: decode the xHHHH escapes OOXML writers use for characters in strings (6b1b0b4)
- xlsx: read a boolean cell holding no xsd:boolean as its text, not as false (58f7eb3)
- xlsx: read a boolean written as true or false, as the Open XML SDK writes it (b13a703)
- xlsx: read a value on past text that follows a CDATA section (9b0d785)
Performance Improvements
- the other-separator check runs only on values that could be numbers, with the separator looked up once (b9ba1c5)
v0.3.0
0.3.0 (2026-09-27)
⚠ BREAKING CHANGES
- TabularExtractor.Start → TabularExtractor.Extract, and ExtractionSession → ExtractionRun; its members are unchanged.
- precheck argument keys renamed — readableCount → judgedCount (PrecheckArguments.ReadableCount → JudgedCount), profileHeaderRow → profileHeaderRowIndex, planHeaderRow → planHeaderRowIndex, boundColumns → boundColumnCount. A schema with a typed field whose Type is not its class's is refused with ArgumentException. The constraints, now classes, no longer have init setters on Length and Value.
- the public API is reshaped once for 1.0. Old → new:
new TabularAnalyzer(options).Analyze(cursor, progress, ct)→TabularAnalyzer.Analyze(cursor, options, progress, ct)TargetField→ImportField(also the factory:ImportField.Text(...)and the others)TextField/IntegerField/DecimalField/DateField/BooleanField→TextImportField/IntegerImportField/DecimalImportField/DateImportField/BooleanImportFieldTranslatedField→TranslatedImportField;TargetSchema→ImportSchemaSourceColumnIndex→ColumnIndexandTargetFieldName→FieldNameonColumnBinding,RowError,MappingFault,PrecheckFinding;TabularStructureException.SourceColumnIndex→ColumnIndex;ColumnBinding.SourceHeader→Header- new
ITabularProblem, implemented byRowError,MappingFault,PrecheckFinding PrecheckFinding.Detailremoved →AffectedRowsBound,Examples,Arguments(PrecheckArguments,PrecheckReasons);PrecheckFinding.FieldName/ColumnIndexare nullableforeach (var o in run)→run.ReadRows(ct);Rows→ReadRows,InChunks→ReadChunks,All→ReadAll;ImportRun<T>is not anIEnumerableExtractionSummaryis an immutable record;Summaryproperties return snapshots- fields,
ImportSchemaand constraints are classes (nowith, reference equality); plans, bindings, options, profiles and results compare by value;CursorDiagnosticsis a record ITabularCursor.MoveToSheet(int)→MoveToSheet(int, CancellationToken = default)
- ITabularCursor.MoveToSheet(int) is MoveToSheet(int, CancellationToken = default); a cursor implemented outside the library must add the parameter.
- ImportRun no longer implements IEnumerable;
foreach (var o in run)isforeach (var o in run.ReadRows(ct)). Rows → ReadRows, InChunks → ReadChunks, All → ReadAll. ExtractionSummary is an immutable record: ImportRun.Summary and ExtractionSession.Summary return a snapshot rather than a live object. - ImportField (and TextImportField and the other typed fields), ImportSchema, FieldConstraint and its nested rules are classes, not records:
withexpressions, value equality and deconstruction on them are gone. CursorDiagnostics is a record. Collections in MappingPlan, ColumnBinding, AnalysisOptions, FileProfile, SheetProfile, ColumnProfile, ColumnFacts, CultureParseCounts, TypeHypothesis, PrecheckFinding, PrecheckResult, ImportPreviewRow and ImportResult compare by value. - PrecheckFinding.Detail is removed; its data is in AffectedRowsBound, Examples and Arguments. PrecheckFinding.FieldName is string? and ColumnIndex is int?, null where a finding concerns no field or column.
- renamed types — TargetField → ImportField (now also the factory; the static class ImportField is merged into it), TextField → TextImportField, IntegerField → IntegerImportField, DecimalField → DecimalImportField, DateField → DateImportField, BooleanField → BooleanImportField, TranslatedField → TranslatedImportField, TargetSchema → ImportSchema. Renamed members — SourceColumnIndex → ColumnIndex and TargetFieldName → FieldName on ColumnBinding, RowError, MappingFault and PrecheckFinding; TabularStructureException.SourceColumnIndex → ColumnIndex; ColumnBinding.SourceHeader → Header. A plan stored with the old property names must be migrated.
new TabularAnalyzer(options).Analyze(cursor, progress, ct)is nowTabularAnalyzer.Analyze(cursor, options, progress, ct); the constructor and the instance overloads are gone.
Features
- an import run reads once, through ReadRows, ReadChunks or ReadAll (53a5248)
- MoveToSheet takes a cancellation token (362737a)
- precheck findings carry their data instead of an English sentence (d45f2ee)
- TabularAnalyzer is static, its options passed to Analyze (47a84e7)
Bug Fixes
- findings of the final reviews of the 1.0 API (b7e5bd3)
Documentation
- the documentation follows the 1.0 API (f80cf99)
Code Refactoring
v0.2.0
0.2.0 (2026-09-26)
Features
- archive: open a zip archive by its contents (49cde9b)
- archive: options, the zip format and skipped entries (a0aea63)
- archive: read the csv files in a zip archive as sheets (59b086c)
- archive: read xlsx and ods workbooks inside an archive (2bcdf09)
- ods: read OpenDocument spreadsheets (5f5f927), closes #13
Bug Fixes
- archive: findings of the final review (88bcd8f)
- csv: read quoted notes of any length, catching stray quotes by what they swallow (b550474)
- csv: refuse an XML document instead of reading its markup as lines (efd98fc)
- drop the raw HTML image from the readme, which nuget.org shows as text (2ccdbbf)
- ods: findings of two independent reviews (a9324a9)
- xlsx: read a written-out bare time on the day a serial time reads on (010aca1)
Performance Improvements
- analysis: ask the culture-free shape questions once per value, not once per culture (eba9e27)
- analysis: count cell kinds in an array, not a dictionary (2b06499)
- analysis: keep distinct-value hashes in a flat open-addressing set (c8eb8f6)
- analysis: read a number once for cultures that write numbers alike (94fd7a8)
- csv: make a field that is one run of the buffer straight from the buffer (d526580)
- csv: take runs of ordinary text inside quotes as one span too (64d527b)
- csv: take runs of ordinary text outside quotes as one span, found by a vectorised search (b1273a5)
v0.1.0
0.1.0 (2026-09-25)
⚠ BREAKING CHANGES
- one exception hierarchy with a code on every instance
- one namespace for the workflow
Features
- benchmarks: a synthetic fixture generator, so the published numbers can be reproduced (c913bd2)
- let callers add their own value rules (4c0ec6c)
- public ErrorCodes constants for every code the library reports (4bd9567)
- report progress while analysing a file (0428a2a)
Bug Fixes
- a successful MoveToSheet clears a fault from the sheet before (#14) (ef3589f)
- a zoned timestamp is read as the clock time it states, on every server (e8b7883)
- an empty shared string no longer hangs the xlsx reader (52c5fc2)
- csv: read records joined by a pair of stray quotes as records, and count the repair (6832d78)
- files in formats the library does not read are refused by name (7f5571f)
- find workbook parts through the package relationships (8eb6890)
- four value errors found against other readers' corpora (7ed3376)
- HeaderRowIndex names a spreadsheet row, the numbering every report uses (4577245)
- malformed parts and truncated worksheets are refused instead of leaking or shortening (d19aaf2)
- options are validated up front, and invariant globalization degrades instead of failing (d7fff36)
- read worksheets whose markup has elements with many attributes (3b3add5)
- the import applies the profiler's number-grouping rule, and honours AllOrNothing (406f6f3)
- the precheck judges a workbook's own numbers, dates and booleans against the field (dbf5855)
- workbook row numbers only ever increase (b18cd0b)
Performance Improvements
- read the shared string table with one chunk buffer, not one per entry (87104be)