Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions useragent/from.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package useragent

import (
"strings"

"github.com/hashicorp/aws-sdk-go-base/v2/internal/config"
"github.com/hashicorp/aws-sdk-go-base/v2/internal/slices"
)

// FromSlice applies the conversion defined in [fromAny] to all elements
// of a slice
//
// Slices of types which cannot assert to a string, empty string values, and string
// values which do not match the expected `{product}/{version} ({comment})`
// pattern (where version and comment are optional) return a zero value struct.
func FromSlice[T any](sl []T) config.UserAgentProducts {
return slices.ApplyToAll(sl, func(v T) config.UserAgentProduct { return from(v) })
}

func from[T any](v T) config.UserAgentProduct {
if s, ok := any(v).(string); ok {
parts := strings.Split(s, "/")
switch len(parts) {
case 1:
return config.UserAgentProduct{Name: parts[0]}
case 2: //nolint: mnd
subparts := strings.Split(parts[1], "(")
if len(subparts) == 2 { //nolint: mnd
version := strings.TrimSpace(subparts[0])
comment := strings.TrimSuffix(subparts[1], ")")
return config.UserAgentProduct{Name: parts[0], Version: version, Comment: comment}
}
return config.UserAgentProduct{Name: parts[0], Version: parts[1]}
}
}

return config.UserAgentProduct{}
}
145 changes: 145 additions & 0 deletions useragent/from_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package useragent

import (
"reflect"
"testing"

"github.com/hashicorp/aws-sdk-go-base/v2/internal/config"
)

func TestFromSlice(t *testing.T) {
t.Parallel()

tests := []struct {
name string
s []any
want config.UserAgentProducts
}{
{
"nil",
nil,
config.UserAgentProducts{},
},
{
"non-string element",
[]any{false},
config.UserAgentProducts{config.UserAgentProduct{}},
},
{
"valid string",
[]any{"my-product/v1.2.3"},
config.UserAgentProducts{
config.UserAgentProduct{
Name: "my-product",
Version: "v1.2.3",
},
},
},
{
"valid and invalid string",
[]any{"my-product/v1.2.3", "foo/bar/baz/qux"},
config.UserAgentProducts{
config.UserAgentProduct{
Name: "my-product",
Version: "v1.2.3",
},
config.UserAgentProduct{},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := FromSlice(tt.s)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("FromSlice() = %+v, want %+v", got, tt.want)
}
})
}
}

func Test_from(t *testing.T) {
t.Parallel()

tests := []struct {
name string
v any
want config.UserAgentProduct
}{
{
"nil",
nil,
config.UserAgentProduct{},
},
{
"non-string",
1,
config.UserAgentProduct{},
},
{
"empty",
"",
config.UserAgentProduct{},
},
{
"name only",
"my-product",
config.UserAgentProduct{
Name: "my-product",
},
},
{
"name and version",
"my-product/v1.2.3",
config.UserAgentProduct{
Name: "my-product",
Version: "v1.2.3",
},
},
{
"name, version, and comment",
"my-product/v1.2.3 (a comment)",
config.UserAgentProduct{
Name: "my-product",
Version: "v1.2.3",
Comment: "a comment",
},
},
{
"comment malformed closing",
"my-product/v1.2.3 (a comment",
config.UserAgentProduct{
Name: "my-product",
Version: "v1.2.3",
Comment: "a comment",
},
},
{
"comment missing parenthesis",
"my-product/v1.2.3 a comment",
// This is a known edge case, but the processed output will render identical
// to the original input despite the version and comment merging.
config.UserAgentProduct{
Name: "my-product",
Version: "v1.2.3 a comment",
},
},
{
"all the slash",
"foo/bar/baz/qux",
config.UserAgentProduct{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
got := from(tt.v)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("from() = %+v, want %+v", got, tt.want)
}
})
}
}