Zettelstore Client

Check-in [4623af27f1]
Login

Check-in [4623af27f1]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Implement api.URLBuilder w/ webs/urlbuilder
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 4623af27f1128f185c373331abb2162f2d76db1b85e47c7256676316b471d9aa
User & Date: stern 2024-04-18 13:27:24
Context
2024-04-22
14:51
Package sxhtml was renamed to sxwebs/sxhtml ... (check-in: 3bbfcfd939 user: stern tags: trunk)
2024-04-18
13:27
Implement api.URLBuilder w/ webs/urlbuilder ... (check-in: 4623af27f1 user: stern tags: trunk)
07:42
Adapt to SxHTML separation from Sx ... (check-in: 0218a29221 user: stern tags: trunk)
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to api/urlbuilder.go.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

25
26
27
28
29
30
31
32
33
34



35




36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------

package api

import (
	"net/url"
	"strings"
)

type urlQuery struct{ key, val string }

// URLBuilder should be used to create zettelstore URLs.
type URLBuilder struct {

	prefix   string
	key      byte
	rawLocal string
	path     []string
	query    []urlQuery
	fragment string
}

// NewURLBuilder creates a new URL builder with the given prefix and key.
func NewURLBuilder(prefix string, key byte) *URLBuilder {



	return &URLBuilder{prefix: prefix, key: key}




}

// Clone an URLBuilder
func (ub *URLBuilder) Clone() *URLBuilder {
	cpy := new(URLBuilder)
	cpy.key = ub.key
	if len(ub.path) > 0 {
		cpy.path = make([]string, 0, len(ub.path))
		cpy.path = append(cpy.path, ub.path...)
	}
	if len(ub.query) > 0 {
		cpy.query = make([]urlQuery, 0, len(ub.query))
		cpy.query = append(cpy.query, ub.query...)
	}
	cpy.fragment = ub.fragment
	return cpy
}

// SetRawLocal sets everything that follows the prefix / key.
func (ub *URLBuilder) SetRawLocal(rawLocal string) *URLBuilder {
	for len(rawLocal) > 0 && rawLocal[0] == '/' {
		rawLocal = rawLocal[1:]
	}
	ub.rawLocal = rawLocal
	ub.path = nil
	ub.query = nil
	ub.fragment = ""
	return ub
}

// SetZid sets the zettel identifier.
func (ub *URLBuilder) SetZid(zid ZettelID) *URLBuilder {
	if len(ub.path) > 0 {
		panic("Cannot add Zid")
	}
	ub.rawLocal = ""
	ub.path = append(ub.path, string(zid))
	return ub
}

// AppendPath adds a new path element
func (ub *URLBuilder) AppendPath(p string) *URLBuilder {
	ub.rawLocal = ""
	for len(p) > 0 && p[0] == '/' {
		p = p[1:]
	}
	if p != "" {
		ub.path = append(ub.path, p)
	}
	return ub
}

// AppendKVQuery adds a new key/value query parameter
func (ub *URLBuilder) AppendKVQuery(key, value string) *URLBuilder {
	ub.rawLocal = ""
	ub.query = append(ub.query, urlQuery{key, value})
	return ub
}

// AppendQuery adds a new query
func (ub *URLBuilder) AppendQuery(value string) *URLBuilder {
	if value != "" {
		ub.rawLocal = ""
		ub.query = append(ub.query, urlQuery{QueryKeyQuery, value})
	}
	return ub
}

// ClearQuery removes all query parameters.
func (ub *URLBuilder) ClearQuery() *URLBuilder {
	ub.rawLocal = ""
	ub.query = nil
	ub.fragment = ""
	return ub
}

// SetFragment stores the fragment
func (ub *URLBuilder) SetFragment(s string) *URLBuilder {
	ub.rawLocal = ""
	ub.fragment = s
	return ub
}

// String produces a string value.
func (ub *URLBuilder) String() string {
	var sb strings.Builder

	sb.WriteString(ub.prefix)
	if ub.key != '/' {
		sb.WriteByte(ub.key)
	}
	if ub.rawLocal != "" {
		sb.WriteString(ub.rawLocal)
		return sb.String()
	}
	for i, p := range ub.path {
		if i > 0 || ub.key != '/' {
			sb.WriteByte('/')
		}
		sb.WriteString(url.PathEscape(p))
	}
	if len(ub.fragment) > 0 {
		sb.WriteByte('#')
		sb.WriteString(ub.fragment)
	}
	for i, q := range ub.query {
		if i == 0 {
			sb.WriteByte('?')
		} else {
			sb.WriteByte('&')
		}
		sb.WriteString(q.key)
		if val := q.val; val != "" {
			sb.WriteByte('=')
			sb.WriteString(url.QueryEscape(val))
		}
	}
	return sb.String()
}







|
<
<
<
<
<



>
|
<
<
<
<
<




>
>
>
|
>
>
>
>





|
<
<
<
<
<
<
<
<
|



<
<
<
<
<
<
<
<
<
<
<
<


<
<
<
<
|





|
<
<
<
<
<
<





<
|






<
|






<
|
<





<
|





<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

9
10
11
12
13
14
15
16





17
18
19
20
21





22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39








40
41
42
43












44
45




46
47
48
49
50
51
52






53
54
55
56
57

58
59
60
61
62
63
64

65
66
67
68
69
70
71

72

73
74
75
76
77

78
79
80
81
82
83

84































85
//
// SPDX-License-Identifier: EUPL-1.2
// SPDX-FileCopyrightText: 2020-present Detlef Stern
//-----------------------------------------------------------------------------

package api

import "t73f.de/r/webs/urlbuilder"






// URLBuilder should be used to create zettelstore URLs.
type URLBuilder struct {
	base   urlbuilder.URLBuilder
	prefix string





}

// NewURLBuilder creates a new URL builder with the given prefix and key.
func NewURLBuilder(prefix string, key byte) *URLBuilder {
	for len(prefix) > 0 && prefix[len(prefix)-1] == '/' {
		prefix = prefix[0 : len(prefix)-1]
	}
	result := URLBuilder{prefix: prefix}
	if key != '/' {
		result.base.AddPath(string([]byte{key}))
	}
	return &result
}

// Clone an URLBuilder
func (ub *URLBuilder) Clone() *URLBuilder {
	cpy := new(URLBuilder)
	ub.base.Copy(&cpy.base)








	cpy.prefix = ub.prefix
	return cpy
}













// SetZid sets the zettel identifier.
func (ub *URLBuilder) SetZid(zid ZettelID) *URLBuilder {




	ub.base.AddPath(string(zid))
	return ub
}

// AppendPath adds a new path element
func (ub *URLBuilder) AppendPath(p string) *URLBuilder {
	ub.base.AddPath(p)






	return ub
}

// AppendKVQuery adds a new key/value query parameter
func (ub *URLBuilder) AppendKVQuery(key, value string) *URLBuilder {

	ub.base.AddQuery(key, value)
	return ub
}

// AppendQuery adds a new query
func (ub *URLBuilder) AppendQuery(value string) *URLBuilder {
	if value != "" {

		ub.base.AddQuery(QueryKeyQuery, value)
	}
	return ub
}

// ClearQuery removes all query parameters.
func (ub *URLBuilder) ClearQuery() *URLBuilder {

	ub.base.RemoveQueries()

	return ub
}

// SetFragment stores the fragment
func (ub *URLBuilder) SetFragment(s string) *URLBuilder {

	ub.base.SetFragment(s)
	return ub
}

// String produces a string value.
func (ub *URLBuilder) String() string {

	return ub.prefix + ub.base.String()































}

Changes to client/client.go.

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
	myURL := *u
	myURL.User = nil
	myURL.ForceQuery = false
	myURL.RawQuery = ""
	myURL.Fragment = ""
	myURL.RawFragment = ""
	base := myURL.String()
	if !strings.HasSuffix(base, "/") {
		base += "/"
	}
	c := Client{
		base: base,
		client: http.Client{
			Timeout: 10 * time.Second,
			Transport: &http.Transport{
				DialContext: (&net.Dialer{
					Timeout: 5 * time.Second, // TCP connect timeout







<
<
<







53
54
55
56
57
58
59



60
61
62
63
64
65
66
	myURL := *u
	myURL.User = nil
	myURL.ForceQuery = false
	myURL.RawQuery = ""
	myURL.Fragment = ""
	myURL.RawFragment = ""
	base := myURL.String()



	c := Client{
		base: base,
		client: http.Client{
			Timeout: 10 * time.Second,
			Transport: &http.Transport{
				DialContext: (&net.Dialer{
					Timeout: 5 * time.Second, // TCP connect timeout

Changes to go.mod.

1
2
3
4
5
6
7

8
module t73f.de/r/zsc

go 1.22

require (
	t73f.de/r/sx v0.0.0-20240418072254-b6eff7d787f9
	t73f.de/r/sxhtml v0.0.0-20240418073213-2d735b1e4353

)







>

1
2
3
4
5
6
7
8
9
module t73f.de/r/zsc

go 1.22

require (
	t73f.de/r/sx v0.0.0-20240418072254-b6eff7d787f9
	t73f.de/r/sxhtml v0.0.0-20240418073213-2d735b1e4353
	t73f.de/r/webs v0.0.0-20240418131849-b90a59f7f704
)

Changes to go.sum.

1
2
3
4


t73f.de/r/sx v0.0.0-20240418072254-b6eff7d787f9 h1:lVPkYN8+J9f6JA9SmoF6icvpLxz4u3h1MCTuDYJYwdU=
t73f.de/r/sx v0.0.0-20240418072254-b6eff7d787f9/go.mod h1:G9pD1j2R6y9ZkPBb81mSnmwaAvTOg7r6jKp/OF7WeFA=
t73f.de/r/sxhtml v0.0.0-20240418073213-2d735b1e4353 h1:FYE8m1ewouQXG3uc4FoUcr+++qzX8OtIJ9ZhI8CSs1s=
t73f.de/r/sxhtml v0.0.0-20240418073213-2d735b1e4353/go.mod h1:AGX5DjZ1x6agvQA8VVK8/bwae9Hcr9qBkt/kXgGQDjE=






>
>
1
2
3
4
5
6
t73f.de/r/sx v0.0.0-20240418072254-b6eff7d787f9 h1:lVPkYN8+J9f6JA9SmoF6icvpLxz4u3h1MCTuDYJYwdU=
t73f.de/r/sx v0.0.0-20240418072254-b6eff7d787f9/go.mod h1:G9pD1j2R6y9ZkPBb81mSnmwaAvTOg7r6jKp/OF7WeFA=
t73f.de/r/sxhtml v0.0.0-20240418073213-2d735b1e4353 h1:FYE8m1ewouQXG3uc4FoUcr+++qzX8OtIJ9ZhI8CSs1s=
t73f.de/r/sxhtml v0.0.0-20240418073213-2d735b1e4353/go.mod h1:AGX5DjZ1x6agvQA8VVK8/bwae9Hcr9qBkt/kXgGQDjE=
t73f.de/r/webs v0.0.0-20240418131849-b90a59f7f704 h1:UkPXoJC0DUczgvuQbptvjKJ6N9vnBTQ6gIflBp7tC/k=
t73f.de/r/webs v0.0.0-20240418131849-b90a59f7f704/go.mod h1:UGAAtul0TK5ACeZ6zTS3SX6GqwMFXxlUpHiV8oqNq5w=