Skip to content

Commit 67e5766

Browse files
authored
Merge pull request #1389 from hashicorp/f-useragent-strings
useragent: add `FromSlice` function
2 parents 2bc688e + e509f3f commit 67e5766

File tree

2 files changed

+186
-0
lines changed

2 files changed

+186
-0
lines changed

useragent/from.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package useragent
5+
6+
import (
7+
"strings"
8+
9+
"github.com/hashicorp/aws-sdk-go-base/v2/internal/config"
10+
"github.com/hashicorp/aws-sdk-go-base/v2/internal/slices"
11+
)
12+
13+
// FromSlice applies the conversion defined in [fromAny] to all elements
14+
// of a slice
15+
//
16+
// Slices of types which cannot assert to a string, empty string values, and string
17+
// values which do not match the expected `{product}/{version} ({comment})`
18+
// pattern (where version and comment are optional) return a zero value struct.
19+
func FromSlice[T any](sl []T) config.UserAgentProducts {
20+
return slices.ApplyToAll(sl, func(v T) config.UserAgentProduct { return from(v) })
21+
}
22+
23+
func from[T any](v T) config.UserAgentProduct {
24+
if s, ok := any(v).(string); ok {
25+
parts := strings.Split(s, "/")
26+
switch len(parts) {
27+
case 1:
28+
return config.UserAgentProduct{Name: parts[0]}
29+
case 2: //nolint: mnd
30+
subparts := strings.Split(parts[1], "(")
31+
if len(subparts) == 2 { //nolint: mnd
32+
version := strings.TrimSpace(subparts[0])
33+
comment := strings.TrimSuffix(subparts[1], ")")
34+
return config.UserAgentProduct{Name: parts[0], Version: version, Comment: comment}
35+
}
36+
return config.UserAgentProduct{Name: parts[0], Version: parts[1]}
37+
}
38+
}
39+
40+
return config.UserAgentProduct{}
41+
}

useragent/from_test.go

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package useragent
5+
6+
import (
7+
"reflect"
8+
"testing"
9+
10+
"github.com/hashicorp/aws-sdk-go-base/v2/internal/config"
11+
)
12+
13+
func TestFromSlice(t *testing.T) {
14+
t.Parallel()
15+
16+
tests := []struct {
17+
name string
18+
s []any
19+
want config.UserAgentProducts
20+
}{
21+
{
22+
"nil",
23+
nil,
24+
config.UserAgentProducts{},
25+
},
26+
{
27+
"non-string element",
28+
[]any{false},
29+
config.UserAgentProducts{config.UserAgentProduct{}},
30+
},
31+
{
32+
"valid string",
33+
[]any{"my-product/v1.2.3"},
34+
config.UserAgentProducts{
35+
config.UserAgentProduct{
36+
Name: "my-product",
37+
Version: "v1.2.3",
38+
},
39+
},
40+
},
41+
{
42+
"valid and invalid string",
43+
[]any{"my-product/v1.2.3", "foo/bar/baz/qux"},
44+
config.UserAgentProducts{
45+
config.UserAgentProduct{
46+
Name: "my-product",
47+
Version: "v1.2.3",
48+
},
49+
config.UserAgentProduct{},
50+
},
51+
},
52+
}
53+
for _, tt := range tests {
54+
t.Run(tt.name, func(t *testing.T) {
55+
t.Parallel()
56+
got := FromSlice(tt.s)
57+
if !reflect.DeepEqual(got, tt.want) {
58+
t.Errorf("FromSlice() = %+v, want %+v", got, tt.want)
59+
}
60+
})
61+
}
62+
}
63+
64+
func Test_from(t *testing.T) {
65+
t.Parallel()
66+
67+
tests := []struct {
68+
name string
69+
v any
70+
want config.UserAgentProduct
71+
}{
72+
{
73+
"nil",
74+
nil,
75+
config.UserAgentProduct{},
76+
},
77+
{
78+
"non-string",
79+
1,
80+
config.UserAgentProduct{},
81+
},
82+
{
83+
"empty",
84+
"",
85+
config.UserAgentProduct{},
86+
},
87+
{
88+
"name only",
89+
"my-product",
90+
config.UserAgentProduct{
91+
Name: "my-product",
92+
},
93+
},
94+
{
95+
"name and version",
96+
"my-product/v1.2.3",
97+
config.UserAgentProduct{
98+
Name: "my-product",
99+
Version: "v1.2.3",
100+
},
101+
},
102+
{
103+
"name, version, and comment",
104+
"my-product/v1.2.3 (a comment)",
105+
config.UserAgentProduct{
106+
Name: "my-product",
107+
Version: "v1.2.3",
108+
Comment: "a comment",
109+
},
110+
},
111+
{
112+
"comment malformed closing",
113+
"my-product/v1.2.3 (a comment",
114+
config.UserAgentProduct{
115+
Name: "my-product",
116+
Version: "v1.2.3",
117+
Comment: "a comment",
118+
},
119+
},
120+
{
121+
"comment missing parenthesis",
122+
"my-product/v1.2.3 a comment",
123+
// This is a known edge case, but the processed output will render identical
124+
// to the original input despite the version and comment merging.
125+
config.UserAgentProduct{
126+
Name: "my-product",
127+
Version: "v1.2.3 a comment",
128+
},
129+
},
130+
{
131+
"all the slash",
132+
"foo/bar/baz/qux",
133+
config.UserAgentProduct{},
134+
},
135+
}
136+
for _, tt := range tests {
137+
t.Run(tt.name, func(t *testing.T) {
138+
t.Parallel()
139+
got := from(tt.v)
140+
if !reflect.DeepEqual(got, tt.want) {
141+
t.Errorf("from() = %+v, want %+v", got, tt.want)
142+
}
143+
})
144+
}
145+
}

0 commit comments

Comments
 (0)