Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
1579d81
design: add deserialization design exploration
trippwill Apr 12, 2026
006a109
encoding: streaming-first deserialization redesign
trippwill Apr 12, 2026
b2df11c
encoding: add composite navigation helpers
trippwill Apr 12, 2026
661f0c2
encoding: rename StructFields → ReflectStructFields for tag introspec…
trippwill Apr 12, 2026
22e4e61
encoding: add financial benchmark dataset (1K/10K trades)
trippwill Apr 12, 2026
4437186
encoding: add JSON NDJSON streaming comparison for financial benchmarks
trippwill Apr 12, 2026
0f4b80b
encoding: align benchmark naming scheme
trippwill Apr 12, 2026
0a5ee59
encoding: Event.Value []byte with borrow semantics
trippwill Apr 12, 2026
0b2a3c0
encoding: zero-copy scalar readers write directly to valBuf
trippwill Apr 12, 2026
c86aadb
encoding: zero-copy readBinTo avoids intermediate string allocation
trippwill Apr 12, 2026
d0aff72
encoding: rename StatementReader → UnitReader, Statement → Property
trippwill Apr 12, 2026
e9df934
site, docs: update Go API examples to new UnitReader design
trippwill Apr 12, 2026
e49d707
encoding: unsafe.String for zero-copy scalar parsing in ReadValue
trippwill Apr 12, 2026
abd8617
encoding: remove dead string-returning scalar readers
trippwill Apr 12, 2026
44dcb9c
spec: compact error codes — remove reserved code 2
trippwill Apr 12, 2026
75f11ca
encoding: improve test coverage 71.3% → 77.6%
trippwill Apr 13, 2026
2f83cac
encoding: tighten lint rules + add 5 fuzz tests
trippwill Apr 13, 2026
11e8453
ci: add weekly fuzz testing workflow
trippwill Apr 13, 2026
3a5cefb
site: fix hero example — unquoted timestamps, atom set, better comment
trippwill Apr 13, 2026
1a2ebbc
fix: suppress gosec G115 false positives, fix staticcheck QF1012
trippwill Apr 13, 2026
072b53d
ci: use mise for Go toolchain, pin golangci-lint v2.11.4
trippwill Apr 13, 2026
c7cf902
ci: fix workflow issues found in review
trippwill Apr 13, 2026
1440e77
encoding: improve patch coverage for new code paths
trippwill Apr 13, 2026
a5fe3a3
encoding: boost patch coverage, lower patch target to 60%
trippwill Apr 13, 2026
a8d743d
fix: address all PR review comments
trippwill Apr 13, 2026
ffb1561
fix: StructFields/TupleElements infinite loop on unconsumed values
trippwill Apr 13, 2026
dfca701
fix: address all round-2 PR review comments
trippwill Apr 13, 2026
4286549
encoding: remove RegisterNamedConverter
trippwill Apr 13, 2026
aa66f54
docs: update design doc to match final API
trippwill Apr 13, 2026
9ec9157
fix: address round-3 PR review comments
trippwill Apr 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,17 @@ permissions:
jobs:
go:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
- uses: jdx/mise-action@v2
with:
go-version-file: 'go.mod'
install_args: go golangci-lint

- name: Build
run: go build ./...
Expand All @@ -29,7 +30,7 @@ jobs:
run: go test ./... -count=1 -race -coverprofile=coverage.out

- name: Lint
uses: golangci/golangci-lint-action@v7
run: golangci-lint run

- name: Upload coverage to Codecov
if: matrix.os == 'ubuntu-latest'
Expand All @@ -42,6 +43,7 @@ jobs:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

- name: Coverage summary
if: matrix.os == 'ubuntu-latest'
uses: actions/github-script@v7
with:
script: |
Expand All @@ -56,6 +58,7 @@ jobs:

dotnet:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
Expand Down
59 changes: 59 additions & 0 deletions .github/workflows/fuzz.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Fuzz

on:
schedule:
- cron: '0 4 * * 1' # Monday 4am UTC
workflow_dispatch:
inputs:
fuzztime:
description: 'Fuzz duration per target (Go duration string)'
default: '5m'
type: string

permissions:
contents: read

jobs:
fuzz:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target:
- FuzzDecode
- FuzzUnmarshalNew
- FuzzReadString
- FuzzParseIntLiteral
- FuzzParseType
steps:
- uses: actions/checkout@v4

- uses: jdx/mise-action@v2
with:
install_args: go

- name: Restore fuzz corpus
uses: actions/cache@v4
with:
path: |
~/.cache/go-test-fuzz
encoding/testdata/fuzz
key: fuzz-corpus-${{ matrix.target }}-week-${{ github.run_number }}
restore-keys: |
fuzz-corpus-${{ matrix.target }}-week-
fuzz-corpus-${{ matrix.target }}-

- name: Fuzz ${{ matrix.target }}
run: |
go test ./encoding/ \
-fuzz=${{ matrix.target }} \
-fuzztime=${{ inputs.fuzztime || '5m' }} \
-race

- name: Upload crash artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: fuzz-crash-${{ matrix.target }}
path: encoding/testdata/fuzz/${{ matrix.target }}/
retention-days: 30
10 changes: 10 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,18 @@ linters:
- unused
- ineffassign
- misspell
- gosec
- nilerr
- exhaustive
settings:
errcheck:
exclude-functions:
- (io.Closer).Close
- (*bufio.Reader).UnreadByte
gosec:
excludes:
- G104 # unhandled errors — covered by errcheck linter with nolint directives
- G204 # subprocess with variable — used in test builds
- G304 # file open with variable — CLI args and test fixtures
exhaustive:
default-signifies-exhaustive: true
1 change: 1 addition & 0 deletions .mise.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[tools]
go = "1.25"
golangci-lint = "2.11.4"
hugo = "latest"
node = "22"
42 changes: 19 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,40 +59,36 @@ type Config struct {
Port int `pakt:"port"`
}

data := []byte("host:str = 'localhost'\nport:int = 8080")
var cfg Config
if err := encoding.Unmarshal(data, &cfg); err != nil {
log.Fatal(err)
}
cfg, err := encoding.UnmarshalNew[Config](data)
```

### Streaming Decode (Events)
### Streaming (UnitReader)

```go
dec := encoding.NewDecoder(reader)
defer dec.Close()
for {
ev, err := dec.Decode()
if err == io.EOF { break }
fmt.Println(ev.Kind, ev.Name, ev.Value)
ur := encoding.NewUnitReader(reader)
defer ur.Close()
for prop := range ur.Properties() {
switch prop.Name {
case "config":
cfg, err := encoding.ReadValue[Config](ur)
case "events":
for event := range encoding.PackItems[LogEvent](ur) {
process(event)
}
}
}
if err := ur.Err(); err != nil { ... }
```

### Streaming Unmarshal (large datasets)

Process stream entries one at a time with constant memory:
### Event-Level Decode

```go
dec := encoding.NewDecoder(reader)
defer dec.Close()

// Read top-level fields into a struct
for dec.More() {
var entry FSEntry
if err := dec.UnmarshalNext(&entry); err != nil {
break
}
process(entry)
for {
ev, err := dec.Decode()
if err == io.EOF { break }
fmt.Println(ev.Kind, ev.Name, string(ev.Value))
}
```

Expand Down
26 changes: 2 additions & 24 deletions cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,12 @@ type CLI struct {
// ParseCmd reads a PAKT file and emits streaming events to stdout.
type ParseCmd struct {
File string `arg:"" help:"Path to .pakt file (use - for stdin)." type:"existingfile"`
Spec string `short:"s" optional:"" help:"Path to .spec.pakt for projection." type:"existingfile" env:"PAKT_SPEC"`
Format string `short:"f" enum:"text,json" default:"text" help:"Output format (text or json)." env:"PAKT_FORMAT"`
}

// ValidateCmd checks a PAKT file for errors without emitting events.
type ValidateCmd struct {
File string `arg:"" help:"Path to .pakt file (use - for stdin)." type:"existingfile"`
Spec string `short:"s" optional:"" help:"Path to .spec.pakt for projection." type:"existingfile" env:"PAKT_SPEC"`
}

// VersionCmd prints version information.
Expand All @@ -41,17 +39,7 @@ func (c *ParseCmd) Run(cli *CLI) error {
defer func() { _ = r.Close() }()

dec := encoding.NewDecoder(r)

if c.Spec != "" {
specFile, err := os.Open(c.Spec)
if err != nil {
return fmt.Errorf("opening spec: %w", err)
}
defer func() { _ = specFile.Close() }()
if err := dec.SetSpec(specFile); err != nil {
return fmt.Errorf("loading spec: %w", err)
}
}
defer dec.Close()

jsonEnc := json.NewEncoder(os.Stdout)

Expand Down Expand Up @@ -85,17 +73,7 @@ func (c *ValidateCmd) Run(cli *CLI) error {
defer func() { _ = r.Close() }()

dec := encoding.NewDecoder(r)

if c.Spec != "" {
specFile, err := os.Open(c.Spec)
if err != nil {
return fmt.Errorf("opening spec: %w", err)
}
defer func() { _ = specFile.Close() }()
if err := dec.SetSpec(specFile); err != nil {
return fmt.Errorf("loading spec: %w", err)
}
}
defer dec.Close()

hasErrors := false
for {
Expand Down
13 changes: 0 additions & 13 deletions cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,19 +130,6 @@ func TestParseStdin(t *testing.T) {
}
}

func TestParseWithSpec(t *testing.T) {
cmd := exec.Command(binaryPath, "parse", "testdata/valid/full.pakt",
"--spec", "testdata/valid/spec-example.spec.pakt")
out, err := cmd.Output()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
lines := strings.Split(strings.TrimSpace(string(out)), "\n")
if len(lines) == 0 {
t.Fatal("expected output with spec projection, got none")
}
}

func TestFormatEnvVar(t *testing.T) {
cmd := exec.Command(binaryPath, "parse", "testdata/valid/scalars.pakt")
cmd.Env = append(os.Environ(), "PAKT_FORMAT=json")
Expand Down
2 changes: 1 addition & 1 deletion codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ coverage:
threshold: 2%
patch:
default:
target: 70%
target: 65%

Comment thread
trippwill marked this conversation as resolved.
ignore:
- "main.go"
Expand Down
Loading
Loading