Skip to content

Commit

Permalink
Adds @uri/@urid #1529
Browse files Browse the repository at this point in the history
  • Loading branch information
mikefarah committed Jan 23, 2023
1 parent dd6cf3d commit 6d7d76a
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 0 deletions.
1 change: 1 addition & 0 deletions pkg/yqlib/decoder.go
Expand Up @@ -15,6 +15,7 @@ const (
JsonInputFormat
CSVObjectInputFormat
TSVObjectInputFormat
UriInputFormat
)

type Decoder interface {
Expand Down
60 changes: 60 additions & 0 deletions pkg/yqlib/decoder_uri.go
@@ -0,0 +1,60 @@
package yqlib

import (
"bytes"
"io"
"net/url"

yaml "gopkg.in/yaml.v3"
)

type uriDecoder struct {
reader io.Reader
finished bool
readAnything bool
}

func NewUriDecoder() Decoder {
return &uriDecoder{finished: false}
}

func (dec *uriDecoder) Init(reader io.Reader) error {
dec.reader = reader
dec.readAnything = false
dec.finished = false
return nil
}

func (dec *uriDecoder) Decode() (*CandidateNode, error) {
if dec.finished {
return nil, io.EOF
}

buf := new(bytes.Buffer)

if _, err := buf.ReadFrom(dec.reader); err != nil {
return nil, err
}
if buf.Len() == 0 {
dec.finished = true

// if we've read _only_ an empty string, lets return that
// otherwise if we've already read some bytes, and now we get
// an empty string, then we are done.
if dec.readAnything {
return nil, io.EOF
}
}
newValue, err := url.QueryUnescape(buf.String())
if err != nil {
return nil, err
}
dec.readAnything = true
return &CandidateNode{
Node: &yaml.Node{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: newValue,
},
}, nil
}
29 changes: 29 additions & 0 deletions pkg/yqlib/doc/operators/encode-decode.md
Expand Up @@ -16,6 +16,7 @@ These operators are useful to process yaml documents that have stringified embed
| TSV | from_tsv/@tsvd | to_tsv/@tsv |
| XML | from_xml/@xmld | to_xml(i)/@xml |
| Base64 | @base64d | @base64 |
| URI | @urid | @uri |


See CSV and TSV [documentation](https://mikefarah.gitbook.io/yq/usage/csv-tsv) for accepted formats.
Expand Down Expand Up @@ -435,6 +436,34 @@ will output
YTogYXBwbGUK
```

## Encode a string to uri
Given a sample.yml file of:
```yaml
coolData: this has & special () characters *
```
then
```bash
yq '.coolData | @uri' sample.yml
```
will output
```yaml
this+has+%26+special+%28%29+characters+%2A
```

## Decode a URI to a string
Given a sample.yml file of:
```yaml
this+has+%26+special+%28%29+characters+%2A
```
then
```bash
yq '@urid' sample.yml
```
will output
```yaml
this has & special () characters *
```

## Decode a base64 encoded string
Decoded data is assumed to be a string.

Expand Down
1 change: 1 addition & 0 deletions pkg/yqlib/doc/operators/headers/encode-decode.md
Expand Up @@ -16,6 +16,7 @@ These operators are useful to process yaml documents that have stringified embed
| TSV | from_tsv/@tsvd | to_tsv/@tsv |
| XML | from_xml/@xmld | to_xml(i)/@xml |
| Base64 | @base64d | @base64 |
| URI | @urid | @uri |


See CSV and TSV [documentation](https://mikefarah.gitbook.io/yq/usage/csv-tsv) for accepted formats.
Expand Down
37 changes: 37 additions & 0 deletions pkg/yqlib/encoder_uri.go
@@ -0,0 +1,37 @@
package yqlib

import (
"fmt"
"io"
"net/url"

yaml "gopkg.in/yaml.v3"
)

type uriEncoder struct {
}

func NewUriEncoder() Encoder {
return &uriEncoder{}
}

func (e *uriEncoder) CanHandleAliases() bool {
return false
}

func (e *uriEncoder) PrintDocumentSeparator(writer io.Writer) error {
return nil
}

func (e *uriEncoder) PrintLeadingContent(writer io.Writer, content string) error {
return nil
}

func (e *uriEncoder) Encode(writer io.Writer, originalNode *yaml.Node) error {
node := unwrapDoc(originalNode)
if guessTagFromCustomType(node) != "!!str" {
return fmt.Errorf("cannot encode %v as url, can only operate on strings. Please first pipe through another encoding operator to convert the value to a string", node.Tag)

This comment has been minimized.

Copy link
@hhromic

hhromic Jan 24, 2023

Noticed another inconsistency here. Shouldn't it be Cannot encode %v as URI, ... ?

}
_, err := writer.Write([]byte(url.QueryEscape(originalNode.Value)))
return err
}
3 changes: 3 additions & 0 deletions pkg/yqlib/lexer_participle.go
Expand Up @@ -78,6 +78,9 @@ var participleYqRules = []*participleYqRule{
{"Base64d", `@base64d`, decodeOp(Base64InputFormat), 0},
{"Base64", `@base64`, encodeWithIndent(Base64OutputFormat, 0), 0},

{"Urld", `@urid`, decodeOp(UriInputFormat), 0},
{"Url", `@uri`, encodeWithIndent(UriOutputFormat, 0), 0},

This comment has been minimized.

Copy link
@hhromic

hhromic Jan 24, 2023

Was checking how you implemented this out of curiosity and noticed you used Urld/Url here.
Looks like is just a symbol name but I wonder if this is a typo and more correct is Urid/Uri for consistency?


{"LoadXML", `load_?xml|xml_?load`, loadOp(NewXMLDecoder(ConfiguredXMLPreferences), false), 0},

{"LoadBase64", `load_?base64`, loadOp(NewBase64Decoder(), false), 0},
Expand Down
4 changes: 4 additions & 0 deletions pkg/yqlib/operator_encoder_decoder.go
Expand Up @@ -26,6 +26,8 @@ func configureEncoder(format PrinterOutputFormat, indent int) Encoder {
return NewXMLEncoder(indent, ConfiguredXMLPreferences)
case Base64OutputFormat:
return NewBase64Encoder()
case UriOutputFormat:
return NewUriEncoder()
}
panic("invalid encoder")
}
Expand Down Expand Up @@ -113,6 +115,8 @@ func decodeOperator(d *dataTreeNavigator, context Context, expressionNode *Expre
decoder = NewCSVObjectDecoder(',')
case TSVObjectInputFormat:
decoder = NewCSVObjectDecoder('\t')
case UriInputFormat:
decoder = NewUriDecoder()
}

var results = list.New()
Expand Down
16 changes: 16 additions & 0 deletions pkg/yqlib/operator_encoder_decoder_test.go
Expand Up @@ -241,6 +241,22 @@ var encoderDecoderOperatorScenarios = []expressionScenario{
"D0, P[], (!!str)::YTogYXBwbGUK\n",
},
},
{
description: "Encode a string to uri",
document: "coolData: this has & special () characters *",
expression: ".coolData | @uri",
expected: []string{
"D0, P[coolData], (!!str)::this+has+%26+special+%28%29+characters+%2A\n",
},
},
{
description: "Decode a URI to a string",
document: "this+has+%26+special+%28%29+characters+%2A",
expression: "@urid",
expected: []string{
"D0, P[], (!!str)::this has & special () characters *\n",
},
},
{
description: "Decode a base64 encoded string",
subdescription: "Decoded data is assumed to be a string.",
Expand Down
1 change: 1 addition & 0 deletions pkg/yqlib/printer.go
Expand Up @@ -27,6 +27,7 @@ const (
TSVOutputFormat
XMLOutputFormat
Base64OutputFormat
UriOutputFormat
)

func OutputFormatFromString(format string) (PrinterOutputFormat, error) {
Expand Down

1 comment on commit 6d7d76a

@mikefarah
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for reviewing! I'll make those changes

Please sign in to comment.