From a9c1d50f42d63d178052ef2104719dfd9186b926 Mon Sep 17 00:00:00 2001 From: Jan-Lukas Else Date: Tue, 25 May 2021 21:06:15 +0200 Subject: [PATCH] First version with 100% test coverage --- LICENSE | 2 +- README.md | 6 ++- geouri.go | 62 +++++++++++++++++++++++++ geouri_test.go | 122 +++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 3 ++ go.sum | 0 6 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 geouri.go create mode 100644 geouri_test.go create mode 100644 go.mod create mode 100644 go.sum diff --git a/LICENSE b/LICENSE index 2071b23..73a5796 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) +Copyright (c) 2021 Jan-Lukas Else Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/README.md b/README.md index 7098738..02b74c9 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ # go-geouri -Parse geo URI (RFC 5870) with Go \ No newline at end of file +Parse geo URI (RFC 5870) with Go + +https://datatracker.ietf.org/doc/html/rfc5870 + +Just the basics so far, doesn't check all the rules yet. \ No newline at end of file diff --git a/geouri.go b/geouri.go new file mode 100644 index 0000000..d4ed7cd --- /dev/null +++ b/geouri.go @@ -0,0 +1,62 @@ +package gogeouri + +import ( + "errors" + "strconv" + "strings" +) + +type Geo struct { + Latitude, Longitude, Altitude float64 + Parameters map[string][]string +} + +const scheme = "geo" + +func Parse(uri string) (*Geo, error) { + g := &Geo{ + Parameters: map[string][]string{}, + } + if !strings.HasPrefix(uri, scheme+":") { + return nil, errors.New("no or wrong scheme") + } + uri = strings.TrimPrefix(uri, scheme+":") + uriParts := strings.Split(uri, ";") + if len(strings.TrimSpace(uriParts[0])) < 1 { + return nil, errors.New("empty path") + } + coords := strings.Split(uriParts[0], ",") + if l := len(coords); l < 2 || l > 3 { + return nil, errors.New("wrong number of coordinates") + } + if f, e := strconv.ParseFloat(coords[0], 64); e == nil { + g.Latitude = f + } else { + return nil, errors.New("can't parse latitude") + } + if f, e := strconv.ParseFloat(coords[1], 64); e == nil { + g.Longitude = f + } else { + return nil, errors.New("can't parse longitude") + } + if len(coords) == 3 { + if f, e := strconv.ParseFloat(coords[2], 64); e == nil { + g.Altitude = f + } else { + return nil, errors.New("can't parse altitude") + } + } + for _, p := range uriParts[1:] { + pParts := strings.Split(p, "=") + if l := len(pParts); l == 1 { + if _, ok := g.Parameters[pParts[0]]; !ok { + g.Parameters[pParts[0]] = []string{} + } + } else if l == 2 { + g.Parameters[pParts[0]] = append(g.Parameters[pParts[0]], pParts[1]) + } else { + return nil, errors.New("wrong parameter") + } + } + return g, nil +} diff --git a/geouri_test.go b/geouri_test.go new file mode 100644 index 0000000..6c5f37b --- /dev/null +++ b/geouri_test.go @@ -0,0 +1,122 @@ +package gogeouri + +import ( + "reflect" + "testing" +) + +func TestParse(t *testing.T) { + type args struct { + uri string + } + type testCase struct { + name string + args args + want *Geo + wantErr bool + } + tests := []testCase{ + // Positive + { + name: "Simple", + args: args{"geo:37.786971,-122.399677"}, + want: &Geo{ + Latitude: 37.786971, + Longitude: -122.399677, + Parameters: map[string][]string{}, + }, + wantErr: false, + }, + { + name: "Altitude", + args: args{"geo:37.786971,-122.399677,-123.456"}, + want: &Geo{ + Latitude: 37.786971, + Longitude: -122.399677, + Altitude: -123.456, + Parameters: map[string][]string{}, + }, + wantErr: false, + }, + { + name: "Indigenous", + args: args{"geo:51.5258325,-0.1359825,0.0;name=london;url=https://hwclondon.co.uk"}, + want: &Geo{ + Latitude: 51.5258325, + Longitude: -0.1359825, + Altitude: 0.0, + Parameters: map[string][]string{ + "name": {"london"}, + "url": {"https://hwclondon.co.uk"}, + }, + }, + wantErr: false, + }, + { + name: "Parameter without value", + args: args{"geo:51.5258325,-0.1359825,0.0;name"}, + want: &Geo{ + Latitude: 51.5258325, + Longitude: -0.1359825, + Altitude: 0.0, + Parameters: map[string][]string{ + "name": {}, + }, + }, + wantErr: false, + }, + // Negative + { + name: "Missing scheme", + args: args{"37.786971,-122.399677"}, + wantErr: true, + }, + { + name: "Missing path", + args: args{"geo:"}, + wantErr: true, + }, + { + name: "1 coordinate", + args: args{"geo:37.786971"}, + wantErr: true, + }, + { + name: "4 coordinates", + args: args{"geo:123,123,123,123"}, + wantErr: true, + }, + { + name: "Malformed latitude", + args: args{"geo:12x,123"}, + wantErr: true, + }, + { + name: "Malformed longitude", + args: args{"geo:123,12x"}, + wantErr: true, + }, + { + name: "Malformed altitude", + args: args{"geo:123,123,12x"}, + wantErr: true, + }, + { + name: "Malformed parameter", + args: args{"geo:123,123;a=a=a"}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := Parse(tt.args.uri) + if (err != nil) != tt.wantErr { + t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Parse() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..444baf3 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.jlel.se/jlelse/go-geouri + +go 1.16 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e69de29