Add URL parser
parent
df6f11aa6f
commit
8a8924a2d3
|
@ -9,7 +9,7 @@ import (
|
|||
func TestChooseTemplate(t *testing.T) {
|
||||
tmpl, err := template.ParseGlob("templates/*")
|
||||
if err != nil {
|
||||
t.Errorf("Unable to load templates: %v", err)
|
||||
t.Errorf("unable to load templates: %v", err)
|
||||
}
|
||||
app := &Datetime{tmpl: tmpl}
|
||||
|
||||
|
|
1
go.mod
1
go.mod
|
@ -3,6 +3,7 @@ module github.com/serverwentdown/datetime.link
|
|||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/google/go-cmp v0.5.2
|
||||
github.com/hbollon/go-edlib v1.3.1
|
||||
go.uber.org/zap v1.16.0
|
||||
)
|
||||
|
|
3
go.sum
3
go.sum
|
@ -1,6 +1,8 @@
|
|||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/hbollon/go-edlib v1.3.1 h1:3x2Faq1xbShKhel5wEYyCNZFguh+s8GH75jdp8w6phU=
|
||||
github.com/hbollon/go-edlib v1.3.1/go.mod h1:wnt6o6EIVEzUfgbUZY7BerzQ2uvzp354qmS2xaLkrhM=
|
||||
|
@ -37,6 +39,7 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw
|
|||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ErrMissingComponent is thrown when the URL has empty or missing components
|
||||
var ErrMissingComponent = errors.New("missing URL component")
|
||||
|
||||
// ErrTooManyComponent is thrown when there are more than 2 components
|
||||
var ErrTooManyComponent = errors.New("too many components")
|
||||
|
||||
var timeRFC3339NoSec = "2006-01-02T15:04Z07:00"
|
||||
var timeFormats = []string{time.RFC3339, timeRFC3339NoSec}
|
||||
|
||||
// Request is a parsed datetime URL
|
||||
type Request struct {
|
||||
Time time.Time
|
||||
Zones []string
|
||||
}
|
||||
|
||||
// ParseRequest parses an input URL into a Request
|
||||
func ParseRequest(u *url.URL) (Request, error) {
|
||||
var err error
|
||||
|
||||
parts := strings.Split(u.Path, "/")[1:]
|
||||
if len(parts) > 2 {
|
||||
return Request{}, ErrTooManyComponent
|
||||
}
|
||||
if len(parts) < 1 {
|
||||
return Request{}, ErrMissingComponent
|
||||
}
|
||||
|
||||
// Parse time portion
|
||||
var t time.Time
|
||||
timeString := parts[0]
|
||||
if len(timeString) == 0 {
|
||||
return Request{}, ErrMissingComponent
|
||||
}
|
||||
for _, f := range timeFormats {
|
||||
t, err = time.Parse(f, timeString)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return Request{}, err
|
||||
}
|
||||
|
||||
// Split zones
|
||||
var z []string
|
||||
zoneString := ""
|
||||
if len(parts) >= 2 {
|
||||
zoneString = parts[1]
|
||||
}
|
||||
if len(zoneString) == 0 {
|
||||
return Request{}, ErrMissingComponent
|
||||
}
|
||||
z = strings.Split(zoneString, ",")
|
||||
|
||||
return Request{
|
||||
Time: t,
|
||||
Zones: z,
|
||||
}, nil
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func mustURLParse(s string) *url.URL {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
func TestURLParse(t *testing.T) {
|
||||
u := mustURLParse("http://test/2020-06-02T14:00+08:00/Singapore,Malaysia")
|
||||
got, err := ParseRequest(u)
|
||||
if err != nil {
|
||||
t.Errorf("mismatch: got error %v", err)
|
||||
return
|
||||
}
|
||||
want := Request{
|
||||
time.Date(2020, 6, 2, 14, 0, 0, 0, time.FixedZone("UTC +8", 8*60*60)),
|
||||
[]string{"Singapore", "Malaysia"},
|
||||
}
|
||||
if !cmp.Equal(got, want) {
|
||||
t.Errorf("mismatch: \n%v", cmp.Diff(got, want))
|
||||
}
|
||||
|
||||
u = mustURLParse("http://test/2019-04-30T18:00:00Z/Nowhere")
|
||||
got, err = ParseRequest(u)
|
||||
if err != nil {
|
||||
t.Errorf("mismatch: got error %v", err)
|
||||
return
|
||||
}
|
||||
want = Request{
|
||||
time.Date(2019, 4, 30, 18, 0, 0, 0, time.FixedZone("UTC", 0)),
|
||||
[]string{"Nowhere"},
|
||||
}
|
||||
if !cmp.Equal(got, want) {
|
||||
t.Errorf("mismatch: \n%v", cmp.Diff(got, want))
|
||||
}
|
||||
}
|
||||
|
||||
func TestURLParseFail(t *testing.T) {
|
||||
u := mustURLParse("http://test/2002-08-30T14:00+06:00/")
|
||||
_, err := ParseRequest(u)
|
||||
if !errors.Is(err, ErrMissingComponent) {
|
||||
t.Errorf("mismatch: got error %v, want error %v", err, ErrMissingComponent)
|
||||
return
|
||||
}
|
||||
|
||||
u = mustURLParse("http://test/")
|
||||
_, err = ParseRequest(u)
|
||||
if !errors.Is(err, ErrMissingComponent) {
|
||||
t.Errorf("mismatch: got error %v, want error %v", err, ErrMissingComponent)
|
||||
return
|
||||
}
|
||||
|
||||
u = mustURLParse("http://test/2000-01-13T00:00Z08:00/hi")
|
||||
_, err = ParseRequest(u)
|
||||
_, isParseError := err.(*time.ParseError)
|
||||
if !isParseError {
|
||||
t.Errorf("mismatch: got error %v, want time.ParseError", err)
|
||||
return
|
||||
}
|
||||
|
||||
u = mustURLParse("http://test/2000-01-13 00:00+08:00/hi")
|
||||
_, err = ParseRequest(u)
|
||||
_, isParseError = err.(*time.ParseError)
|
||||
if !isParseError {
|
||||
t.Errorf("mismatch: got error %v, want time.ParseError", err)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue