Skip to content

Commit 378fd4f

Browse files
committed
first stab at v2 layout
Signed-off-by: Frédéric BIDON <[email protected]>
1 parent 72277ad commit 378fd4f

File tree

9 files changed

+334
-103
lines changed

9 files changed

+334
-103
lines changed

docs/V2.md

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,24 @@
44

55
* [x] Make `uri.URI` a concrete type
66
* In theory, this is a breaking change, but has most likely little impact on actual usage
7+
* [x] Checked that this wouldn't break fyne-io/fyne
78

89
* [x] The interface abstraction comes at a (small) performance cost, and there is no palatable benefit from it
9-
* More methods from `URL` should be supported by `URI`, e.g. `UnmarshalText()`, `MarshalBinary()`, `Redacted()`, `IsAbs()`...
10+
* More methods from `URL` should be supported by `URI`, e.g. :
11+
* [x] `UnmarshalText()`,
12+
* [x] `MarshalBinary()`,
13+
* `Redacted()`, `IsAbs()`...
1014
* Similarly, support more methods from `net/url.UserInfo` in the `Authority` type.
1115
* However:
12-
* Let's keep the `Authority` part, as it better sticks to how the object is structured according to the RFC.
13-
* Let's keep the fluent `Builder` component. I don't think that exposing fields like in `URL` is a good choice.
16+
* [x] Let's keep the `Authority` part, as it better sticks to how the object is structured according to the RFC.
17+
* [x] Let's keep the fluent `Builder` component. I don't think that exposing fields like in `URL` is a good choice.
18+
* [x] Remove `Authority` and `Builder` interfaces.
1419

1520
## Canonicalization
16-
* Extra feature: no breaking change
21+
* Extra features only: no breaking change
1722

18-
* `URI.String()` currently just prints out the URL. We may leave it like this.
19-
* A `Normalize()` method should canonicalize the URI (case, simplified path, etc), like the `purell` package does.
23+
* [x] `URI.String()` currently just prints out the URL. We may leave it like this.
24+
* `Normalize()` and `Normaized()` methods should canonicalize the URI (case, simplified path, etc), like the `purell` package does.
2025
Notice that `purell` seems to be no longer maintained: pulling a dependency is probably not appropriate.
2126

2227
## Strictness/compliance options
@@ -29,11 +34,15 @@
2934
* callers should be able to opt in for IRI vs strict (historical) URI - e.g. only ASCII - rather than the current mixed
3035
implementation (yet again, that was a pragmatic... still pondering if putting more nitpicking is appropriate).
3136
* [x] callers should be able to configure DNS schemes from options rather than overriding a package-level variable
32-
* default options could be set at the package level to save on systematic option resolution at parsing time
37+
* [x] default options could be set at the package level to save on systematic option resolution at parsing time
3338
(alternatively, expose a `Parser` type to wrap options once for a series of subsequent calls to `Parse()`/`Validate()`)
3439
* support canonicalization options with flags such as those defined in `https://pkg.go.dev/golang.org/x/net/idna`
40+
* add windows-friendly tolerance option for windows file paths (`Normalize()` would produce a RFC-compliant form)
3541

3642
* Standard compliance improvements
3743
* Improve IRI support: current support for IRI is at best loose, albeit pragmatic
3844
(currently, a valid ALPHA token is a unicode letter codepoint, only ASCII digits are supported)
3945

46+
## Performance
47+
* Introduce `Make` methods to return a struct rather than a pointer: this saves an allocation
48+
for most use cases in which it is not necessary to get a pointer receiver.

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ go 1.20
55
require (
66
github.com/pkg/profile v1.7.0
77
github.com/stretchr/testify v1.8.4
8+
golang.org/x/net v0.15.0
89
)
910

1011
require (
1112
github.com/davecgh/go-spew v1.1.1 // indirect
1213
github.com/felixge/fgprof v0.9.3 // indirect
1314
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd // indirect
1415
github.com/pmezard/go-difflib v1.0.0 // indirect
16+
golang.org/x/text v0.13.0 // indirect
1517
gopkg.in/yaml.v3 v3.0.1 // indirect
1618
)

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
1919
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
2020
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
2121
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
22+
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
23+
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
2224
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
25+
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
26+
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
2327
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
2428
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
2529
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

normalize.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
11
package uri
22

3-
func (u *URI) Normalize() string {
3+
import "strings"
4+
5+
// Normalize yields a canonicalized representation of the URI.
6+
func (u URI) Normalize() string {
7+
return u.Normalized().String()
8+
}
9+
10+
// Normalized yields a new URI with normalized content.
11+
//
12+
// Calling String() on that one would produce the same string as calling
13+
// Normalize() on the original URI.
14+
func (u URI) Normalized() URI {
15+
return URI{
16+
scheme: strings.ToLower(u.scheme),
17+
authority: Authority{
18+
path: normalizedPath(u.authority.path),
19+
},
20+
} // TODO
21+
}
22+
23+
func normalizedPath(path string) string {
424
return "" // TODO
525
}

options.go

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,108 @@
1+
// nolint
12
package uri
23

4+
import (
5+
"sync"
6+
7+
"golang.org/x/net/idna"
8+
)
9+
310
type (
11+
// Option allows for fine-grained tuning of tolerances to standards
12+
// when validating an URI
413
Option func(*options)
514

615
options struct {
7-
schemeIsDNSFunc func(string) bool
8-
withStrictASCII bool
9-
withStrictIPv6 bool
16+
schemeIsDNSFunc func(string) bool
17+
withDNSHostValidation bool
18+
withStrictASCII bool
19+
withStrictIPv6 bool
20+
withURIReference bool
21+
withStrictIRI bool
22+
withWindowsFriendly bool
23+
withRedactedPassword bool
24+
iDNAFlags []idna.Option
1025
}
1126
)
1227

13-
var packageLevelDefaults = options{
14-
schemeIsDNSFunc: UsesDNSHostValidation,
15-
}
28+
var (
29+
packageLevelDefaults = options{
30+
schemeIsDNSFunc: UsesDNSHostValidation,
31+
}
32+
33+
muxDefaults sync.Mutex
34+
)
1635

1736
func defaultOptions() *options {
18-
o := packageLevelDefaults
37+
o := packageLevelDefaults // shallow-clone defaults
1938

2039
return &o
2140
}
2241

42+
func applyOptions(opts []Option) *options {
43+
if len(opts) == 0 {
44+
// no overrides, no need to allocate a copy of the options
45+
return &packageLevelDefaults
46+
}
47+
48+
o := defaultOptions()
49+
50+
for _, apply := range opts {
51+
apply(o)
52+
}
53+
54+
return o
55+
}
56+
57+
// SetDefaultOptions allows to tweak package level defaults.
58+
//
59+
// You should only use this in initialization steps, as this manipulates
60+
// a package global variable.
2361
func SetDefaultOptions(opts ...Option) {
62+
muxDefaults.Lock()
63+
defer muxDefaults.Unlock()
2464

65+
o := &packageLevelDefaults
66+
for _, apply := range opts {
67+
apply(o)
68+
}
2569
}
2670

71+
// WithSchemeIsDNSFunc overrides the default DNS scheme identification function.
72+
//
73+
// The passed function is assumed to return true whenever a (lower cased) scheme
74+
// should be considered to use DNS-compliant host names.
2775
func WithSchemeIsDNSFunc(fn func(string) bool) Option {
2876
return func(o *options) {
2977
o.schemeIsDNSFunc = fn
3078
}
3179
}
3280

81+
// WithDNSSchemes adds extra schemes to the DNS host name validation.
3382
func WithDNSSchemes(schemes ...string) Option {
3483
return func(o *options) {
3584
// TODO
3685
}
3786
}
87+
88+
// WithReference tells the validator whether to accept URI references.
89+
func WithReference(enabled bool) Option {
90+
return func(o *options) {
91+
o.withURIReference = enabled
92+
}
93+
}
94+
95+
// WithStrictIRI tells the validator to be strict regarding RFCxyz for IRIs
96+
func WithStrictIRI(enabled bool) Option {
97+
return func(o *options) {
98+
o.withStrictIRI = enabled
99+
}
100+
}
101+
102+
// WithWindowsFriendly tells the validator to accept Windows file paths that
103+
// are common, but formally invalid URI path (e.g. 'C:\folder\File.txt').
104+
func WithWindowsFriendly(enabled bool) Option {
105+
return func(o *options) {
106+
o.withWindowsFriendly = enabled
107+
}
108+
}

todo.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package uri
2+
3+
func (u *URI) JoinPath(elem ...string) *URI {
4+
// TODO: builder method
5+
return nil
6+
}
7+
8+
func (u URI) RequestURI() string {
9+
return "" // TODO
10+
}
11+
12+
func (u *URI) ResolveReference(ref URI) *URI {
13+
return nil // TODO
14+
}
15+
16+
func (u URI) EscapedFragment() string {
17+
// TODO
18+
return u.fragment
19+
}
20+
21+
func (u URI) IsReference() bool {
22+
return false // TODO
23+
}
24+
25+
func (a Authority) Redacted() string { // NOTE: net/url.URL mutates
26+
return "" // TODO
27+
}
28+
29+
func (a Authority) Username() string {
30+
return ""
31+
}
32+
33+
func (a Authority) User() string {
34+
return ""
35+
}
36+
37+
func (a Authority) Password() (string, bool) {
38+
return "", false
39+
}
40+
41+
func (a Authority) SetUserPassword(username, password string) *URI {
42+
return nil
43+
}

0 commit comments

Comments
 (0)