Skip to content

Commit

Permalink
add Zed url_parse function (#3080)
Browse files Browse the repository at this point in the history
  • Loading branch information
nwt authored Sep 16, 2021
1 parent bd9d01d commit 309f8d6
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 0 deletions.
70 changes: 70 additions & 0 deletions expr/function/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package function
import (
"errors"
"fmt"
"net/url"
"strconv"
"strings"

"github.com/brimdata/zed/anymath"
Expand Down Expand Up @@ -113,6 +115,8 @@ func New(zctx *zson.Context, name string, narg int) (Interface, bool, error) {
case "network_of":
argmax = 2
f = &networkOf{}
case "url_parse":
f = &urlParse{marshaler: zson.NewZNGMarshalerWithContext(zctx)}
case "zson_parse":
f = &zsonParse{zctx: zctx}
}
Expand Down Expand Up @@ -233,6 +237,72 @@ func (i *is) Call(args []zng.Value) (zng.Value, error) {
return zng.False, nil
}

type urlParse struct {
marshaler *zson.MarshalZNGContext
}

func (u *urlParse) Call(args []zng.Value) (zng.Value, error) {
in := args[0]
if !in.IsStringy() {
return badarg("url_parse: input must be string")
}
if in.Bytes == nil {
return badarg("url_parse: input must not be null")
}
s, err := zng.DecodeString(in.Bytes)
if err != nil {
return zng.Value{}, err
}
ur, err := url.Parse(s)
if err != nil {
return zng.Value{}, fmt.Errorf("url_parse: %q: %w", s, errors.Unwrap(err))
}
var v struct {
Scheme *string `zng:"scheme"`
Opaque *string `zng:"opaque"`
User *string `zng:"user"`
Password *string `zng:"password"`
Host *string `zng:"host"`
Port *uint16 `zng:"port"`
Path *string `zng:"path"`
Query url.Values `zng:"query"`
Fragment *string `zng:"fragment"`
}
if ur.Scheme != "" {
v.Scheme = &ur.Scheme
}
if ur.Opaque != "" {
v.Opaque = &ur.Opaque
}
if s := ur.User.Username(); s != "" {
v.User = &s
}
if s, ok := ur.User.Password(); ok {
v.Password = &s
}
if s := ur.Hostname(); s != "" {
v.Host = &s
}
if ss := ur.Port(); ss != "" {
u64, err := strconv.ParseUint(ss, 10, 16)
if err != nil {
return zng.Value{}, fmt.Errorf("url_parse: %q: invalid port: %s", s, errors.Unwrap(err))
}
u16 := uint16(u64)
v.Port = &u16
}
if ur.Path != "" {
v.Path = &ur.Path
}
if q := ur.Query(); len(q) > 0 {
v.Query = q
}
if ur.Fragment != "" {
v.Fragment = &ur.Fragment
}
return u.marshaler.Marshal(v)
}

type zsonParse struct {
zctx *zson.Context
}
Expand Down
86 changes: 86 additions & 0 deletions expr/ztests/url-parse.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
zed: |
url := url_parse(s)
input: |
{s:""}
{s:"scheme://user:password@host:12345/path?a=1&a=2&b=3&c=#fragment"}
{s:"scheme:opaque"}
{s:1}
{s:null(string)}
{s:":no-scheme"}
{s:"scheme://bad-port:1234567"}
output-flags: -pretty=2

output: |
{
s: "",
url: {
scheme: null (string),
opaque: null (string),
user: null (string),
password: null (string),
host: null (string),
port: null (uint16),
path: null (string),
query: null (|{string,[string]}|),
fragment: null (string)
}
}
{
s: "scheme://user:password@host:12345/path?a=1&a=2&b=3&c=#fragment",
url: {
scheme: "scheme",
opaque: null (string),
user: "user",
password: "password",
host: "host",
port: 12345 (uint16),
path: "/path",
query: |{
{"a",[
"1",
"2"
]},
{"b",[
"3"
]},
{"c",[
""
]}
}|,
fragment: "fragment"
}
}
{
s: "scheme:opaque",
url: {
scheme: "scheme",
opaque: "opaque",
user: null (string),
password: null (string),
host: null (string),
port: null (uint16),
path: null (string),
query: null (|{string,[string]}|),
fragment: null (string)
}
}
{
s: 1
}
{
s: null (string)
}
{
s: ":no-scheme"
}
{
s: "scheme://bad-port:1234567"
}
warnings: |
url_parse: input must be string: bad argument
url_parse: input must not be null: bad argument
url_parse: ":no-scheme": missing protocol scheme
url_parse: "scheme://bad-port:1234567": invalid port: value out of range

0 comments on commit 309f8d6

Please sign in to comment.