Skip to content

Commit

Permalink
Start implementing CSV parser
Browse files Browse the repository at this point in the history
  • Loading branch information
TomWright committed Oct 27, 2024
1 parent 3ac9c8b commit 4ef342c
Show file tree
Hide file tree
Showing 8 changed files with 310 additions and 44 deletions.
1 change: 1 addition & 0 deletions cmd/dasel/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
_ "github.com/tomwright/dasel/v3/parsing/d"
_ "github.com/tomwright/dasel/v3/parsing/json"
_ "github.com/tomwright/dasel/v3/parsing/toml"
_ "github.com/tomwright/dasel/v3/parsing/xml"
_ "github.com/tomwright/dasel/v3/parsing/yaml"
)

Expand Down
12 changes: 11 additions & 1 deletion execution/execute_binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,17 @@ func binaryExprExecutor(opts *Options, e ast.BinaryExpr) (expressionExecutor, er
case lexer.Equals:
doOperation = func(a *model.Value, b *model.Value) (*model.Value, error) {
err := a.Set(b)
return b, err
if err != nil {
return nil, fmt.Errorf("error setting value: %w", err)
}
switch a.Type() {
case model.TypeMap:
return a, nil
case model.TypeSlice:
return a, nil
default:
return b, nil
}
}
case lexer.And:
doOperation = func(a *model.Value, b *model.Value) (*model.Value, error) {
Expand Down
38 changes: 24 additions & 14 deletions execution/execute_branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,37 @@ import (
func branchExprExecutor(opts *Options, e ast.BranchExpr) (expressionExecutor, error) {
return func(data *model.Value) (*model.Value, error) {
res := model.NewSliceValue()
res.MarkAsBranch()

for _, expr := range e.Exprs {
r, err := ExecuteAST(expr, data, opts)
if err != nil {
return nil, fmt.Errorf("failed to execute branch expr: %w", err)
if len(e.Exprs) == 0 {
if err := data.RangeSlice(func(_ int, value *model.Value) error {
if err := res.Append(value); err != nil {
return fmt.Errorf("failed to append branch result: %w", err)
}
return nil
}); err != nil {
return nil, fmt.Errorf("failed to range slice: %w", err)
}
} else {
for _, expr := range e.Exprs {
r, err := ExecuteAST(expr, data, opts)
if err != nil {
return nil, fmt.Errorf("failed to execute branch expr: %w", err)
}

// This deals with the spread operator in the branch expression.
valsToAppend, err := prepareSpreadValues(r)
if err != nil {
return nil, fmt.Errorf("error handling spread values: %w", err)
}
for _, v := range valsToAppend {
if err := res.Append(v); err != nil {
return nil, fmt.Errorf("failed to append branch result: %w", err)
// This deals with the spread operator in the branch expression.
valsToAppend, err := prepareSpreadValues(r)
if err != nil {
return nil, fmt.Errorf("error handling spread values: %w", err)
}
for _, v := range valsToAppend {
if err := res.Append(v); err != nil {
return nil, fmt.Errorf("failed to append branch result: %w", err)
}
}
}
}

res.MarkAsBranch()

return res, nil
}, nil
}
33 changes: 9 additions & 24 deletions internal/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package cli

import (
"fmt"
"github.com/tomwright/dasel/v3"
"io"

"github.com/tomwright/dasel/v3/execution"
Expand Down Expand Up @@ -71,38 +70,24 @@ func (c *QueryCmd) Run(ctx *Globals) error {

opts = append(opts, execution.WithVariable("root", inputData))

out, num, err := dasel.Query(inputData, c.Query, opts...)
options := execution.NewOptions(opts...)
out, err := execution.ExecuteSelector(c.Query, inputData, options)
if err != nil {
return err
}

if c.ReturnRoot {
outputBytes, err := writer.Write(inputData)
if err != nil {
return fmt.Errorf("error writing output: %w", err)
}

_, err = ctx.Stdout.Write(outputBytes)
if err != nil {
return fmt.Errorf("error writing output: %w", err)
}
return nil
out = inputData
}

if num == 0 {
return nil
outputBytes, err := writer.Write(out)
if err != nil {
return fmt.Errorf("error writing output: %w", err)
}

for _, o := range out {
outputBytes, err := writer.Write(o)
if err != nil {
return fmt.Errorf("error writing output: %w", err)
}

_, err = ctx.Stdout.Write(outputBytes)
if err != nil {
return fmt.Errorf("error writing output: %w", err)
}
_, err = ctx.Stdout.Write(outputBytes)
if err != nil {
return fmt.Errorf("error writing output: %w", err)
}

return nil
Expand Down
10 changes: 10 additions & 0 deletions model/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ type KeyValue struct {
// Values represents a list of values.
type Values []*Value

func (v Values) ToSliceValue() (*Value, error) {
slice := NewSliceValue()
for _, val := range v {
if err := slice.Append(val); err != nil {
return nil, err
}
}
return slice, nil
}

// Value represents a value.
type Value struct {
Value reflect.Value
Expand Down
26 changes: 21 additions & 5 deletions parsing/json/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,12 +220,28 @@ func (j *jsonWriter) Write(value *model.Value) ([]byte, error) {
return err
}

if err := j.write(buf, encoderFn, es, value); err != nil {
return nil, err
}
if value.IsBranch() {
if err := value.RangeSlice(func(i int, v *model.Value) error {
if err := j.write(buf, encoderFn, es, v); err != nil {
return err
}

if _, err := buf.Write([]byte("\n")); err != nil {
return nil, err
if _, err := buf.Write([]byte("\n")); err != nil {
return err
}

return nil
}); err != nil {
return nil, err
}
} else {
if err := j.write(buf, encoderFn, es, value); err != nil {
return nil, err
}

if _, err := buf.Write([]byte("\n")); err != nil {
return nil, err
}
}

return buf.Bytes(), nil
Expand Down
156 changes: 156 additions & 0 deletions parsing/xml/xml.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package xml

import (
"bytes"
"encoding/xml"
"errors"
"fmt"
"io"
"unicode"

"github.com/tomwright/dasel/v3/model"
"github.com/tomwright/dasel/v3/parsing"
)

const (
// XML represents the XML file format.
XML parsing.Format = "xml"
)

var _ parsing.Reader = (*xmlReader)(nil)
var _ parsing.Writer = (*xmlWriter)(nil)

//var _ parsing.Writer = (*xmlWriter)(nil)

func init() {
parsing.RegisterReader(XML, newXMLReader)
parsing.RegisterWriter(XML, newXMLWriter)
}

func newXMLReader() (parsing.Reader, error) {
return &xmlReader{}, nil
}

// NewXMLWriter creates a new XML writer.
func newXMLWriter(options parsing.WriterOptions) (parsing.Writer, error) {
return &xmlWriter{
options: options,
}, nil
}

type xmlReader struct{}

// Read reads a value from a byte slice.
func (j *xmlReader) Read(data []byte) (*model.Value, error) {
decoder := xml.NewDecoder(bytes.NewReader(data))
decoder.Strict = true

el, err := j.parseElement(decoder, xml.StartElement{
Name: xml.Name{
Local: "root",
},
})
if err != nil {
return nil, err
}

return el.toModel()
}

type xmlAttr struct {
Name string
Value string
}

type xmlElement struct {
Name string
Attrs []xmlAttr
Children []*xmlElement
Content string
}

func (e *xmlElement) toModel() (*model.Value, error) {
attrs := model.NewMapValue()
for _, attr := range e.Attrs {
if err := attrs.SetMapKey(attr.Name, model.NewStringValue(attr.Value)); err != nil {
return nil, err
}
}
res := model.NewMapValue()
if err := res.SetMapKey("name", model.NewStringValue(e.Name)); err != nil {
return nil, err
}
if err := res.SetMapKey("attrs", attrs); err != nil {
return nil, err
}

if err := res.SetMapKey("content", model.NewStringValue(e.Content)); err != nil {
return nil, err
}
children := model.NewSliceValue()
for _, child := range e.Children {
childModel, err := child.toModel()
if err != nil {
return nil, err
}
if err := children.Append(childModel); err != nil {
return nil, err
}
}
if err := res.SetMapKey("children", children); err != nil {
return nil, err
}
return res, nil
}

func (j *xmlReader) parseElement(decoder *xml.Decoder, element xml.StartElement) (*xmlElement, error) {
el := &xmlElement{
Name: element.Name.Local,
Attrs: make([]xmlAttr, 0),
Children: make([]*xmlElement, 0),
}

for _, attr := range element.Attr {
el.Attrs = append(el.Attrs, xmlAttr{
Name: attr.Name.Local,
Value: attr.Value,
})
}

for {
t, err := decoder.Token()
if errors.Is(err, io.EOF) {
if el.Name == "root" {
return el, nil
}
return nil, fmt.Errorf("unexpected EOF")
}

switch t := t.(type) {
case xml.StartElement:
child, err := j.parseElement(decoder, t)
if err != nil {
return nil, err
}
el.Children = append(el.Children, child)
case xml.CharData:
if unicode.IsSpace([]rune(string(t))[0]) {
continue
}
el.Content += string(t)
case xml.EndElement:
return el, nil
default:
return nil, fmt.Errorf("unexpected token: %v", t)
}
}
}

type xmlWriter struct {
options parsing.WriterOptions
}

// Write writes a value to a byte slice.
func (j *xmlWriter) Write(value *model.Value) ([]byte, error) {
return nil, nil
}
Loading

0 comments on commit 4ef342c

Please sign in to comment.