Assisted-by: GLM 5.3 Flash
This commit is contained in:
@@ -46,6 +46,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
(10000 levels, which no hand-written document approaches): a document that
|
||||
nests arrays or inline tables deeper used to run the stack out and is now
|
||||
rejected with a `SyntaxError` naming the limit.
|
||||
- `ParseFile(path)` reads the file and parses it into a `Document`, with the
|
||||
file name at the front of every error it returns, read failure and parse
|
||||
failure alike. `Valid(data)` reports whether a document parses, nil on
|
||||
success and the parse error on failure, the library call the `-validate`
|
||||
mode of interpres-decode is built on.
|
||||
- `Decoder.UseNumber()` decodes the integers and floats of the document into
|
||||
`Number`, which carries the literal the document wrote, so `0x1f`, `1_000`,
|
||||
`+1.0` and `inf` survive a round trip with their spelling instead of the
|
||||
|
||||
+23
@@ -50,6 +50,29 @@ tree, err := interpres.ParseMap([]byte("title = \"x\"\nport = 8080\n"))
|
||||
|
||||
The cancellable variant of `ParseMap`.
|
||||
|
||||
### `func ParseFile(path string) (*Document, error)`
|
||||
|
||||
Reads the file at `path` and parses it into a [Document](#documents), the shape
|
||||
`Parse` gives. Both a read failure and a parse failure come back with the file
|
||||
name as their first words, wrapped so `errors.AsType` still reaches the
|
||||
`SyntaxError` inside a parse failure.
|
||||
|
||||
```go
|
||||
doc, err := interpres.ParseFile("config.toml")
|
||||
```
|
||||
|
||||
### `func Valid(data []byte) error`
|
||||
|
||||
Reports whether `data` is a valid TOML document: `nil` when the parser accepts
|
||||
it, the parse error when it does not. It is the library call the `-validate`
|
||||
mode of interpres-decode is built on.
|
||||
|
||||
```go
|
||||
if err := interpres.Valid(data); err != nil {
|
||||
fmt.Println("invalid:", err)
|
||||
}
|
||||
```
|
||||
|
||||
## Documents
|
||||
|
||||
`Parse` returns a `Document`: the value tree together with what a map cannot
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
)
|
||||
|
||||
@@ -127,6 +128,31 @@ func ParseMapContext(ctx context.Context, data []byte) (map[string]any, error) {
|
||||
return tree, err
|
||||
}
|
||||
|
||||
// ParseFile reads the TOML document at path and parses it into a Document,
|
||||
// the shape Parse gives. Every error names the file it came from: a read
|
||||
// failure and a parse failure alike carry the path as their first words,
|
||||
// wrapped so errors.AsType still reaches the SyntaxError inside.
|
||||
func ParseFile(path string) (*Document, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %w", path, err)
|
||||
}
|
||||
doc, err := Parse(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %w", path, err)
|
||||
}
|
||||
return doc, nil
|
||||
}
|
||||
|
||||
// Valid reports whether data is a valid TOML document: nil when the parser
|
||||
// accepts it, and the parse error when it does not. It is the library call
|
||||
// the -validate mode of interpres-decode is built on, and it reads nothing
|
||||
// but the bytes it is given.
|
||||
func Valid(data []byte) error {
|
||||
_, err := ParseMapContext(context.Background(), data)
|
||||
return err
|
||||
}
|
||||
|
||||
// parseOptions bound the work one parse may do and the shape it produces. A
|
||||
// zero field takes the default.
|
||||
type parseOptions struct {
|
||||
|
||||
@@ -6,6 +6,8 @@ package interpres
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -726,3 +728,48 @@ func TestParseNestingLimit(t *testing.T) {
|
||||
t.Errorf("Msg = %q, want it to name the nesting limit", se.Msg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseFile(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "config.toml")
|
||||
if err := os.WriteFile(path, []byte("port = 8080\n"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
doc, err := ParseFile(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := doc.Map()["port"]; got != int64(8080) {
|
||||
t.Errorf("port = %v, want 8080", got)
|
||||
}
|
||||
|
||||
_, err = ParseFile(filepath.Join(t.TempDir(), "missing.toml"))
|
||||
if err == nil || !strings.Contains(err.Error(), "missing.toml") {
|
||||
t.Errorf("read error = %v, want it to name the file", err)
|
||||
}
|
||||
|
||||
bad := filepath.Join(t.TempDir(), "broken.toml")
|
||||
if err := os.WriteFile(bad, []byte("port =\n"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = ParseFile(bad)
|
||||
if err == nil || !strings.Contains(err.Error(), "broken.toml") {
|
||||
t.Errorf("parse error = %v, want it to name the file", err)
|
||||
}
|
||||
s, ok := errors.AsType[*SyntaxError](err)
|
||||
if !ok || s.Line != 1 {
|
||||
t.Errorf("parse error = %v, want a SyntaxError with line 1 inside", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValid(t *testing.T) {
|
||||
if err := Valid([]byte("a = 1\n[t]\nb = 2\n")); err != nil {
|
||||
t.Errorf("Valid(valid) = %v, want nil", err)
|
||||
}
|
||||
err := Valid([]byte("a = \n"))
|
||||
if err == nil {
|
||||
t.Fatal("Valid(invalid) = nil, want an error")
|
||||
}
|
||||
if _, ok := errors.AsType[*SyntaxError](err); !ok {
|
||||
t.Errorf("Valid(invalid) = %v, want a SyntaxError", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user