diff --git a/.golangci.yml b/.golangci.yml index 7cea1af..1ad5adf 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -16,7 +16,7 @@ linters: - godox - gosmopolitan - inamedparam - #- intrange # disabled while < go1.22 + - intrange - ireturn - lll - musttag diff --git a/README.md b/README.md index f6b39c6..de5afe1 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ It also provides convenient extensions to go-openapi users. - mac (e.g "01:02:03:04:05:06") - rgbcolor (e.g. "rgb(100,100,100)") - ssn - - uuid, uuid3, uuid4, uuid5 + - uuid, uuid3, uuid4, uuid5, uuid7 - cidr (e.g. "192.0.2.1/24", "2001:db8:a0b:12f0::1/32") - ulid (e.g. "00000PP9HGSBSSDZ1JTEXBJ0PW", [spec](https://github.com/ulid/spec)) @@ -81,7 +81,12 @@ List of defined types: - SSN - URI - UUID -- UUID3 -- UUID4 -- UUID5 +- [UUID3](https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-3) +- [UUID4](https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-4) +- [UUID5](https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-5) +- [UUID7](https://www.rfc-editor.org/rfc/rfc9562.html#name-uuid-version-7) - [ULID](https://github.com/ulid/spec) + +## Licensing + +This library ships under the [SPDX-License-Identifier: Apache-2.0](./LICENSE). diff --git a/bson.go b/bson.go index 78d6e07..0eec8f6 100644 --- a/bson.go +++ b/bson.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package strfmt @@ -18,9 +7,6 @@ import ( "database/sql/driver" "fmt" - "go.mongodb.org/mongo-driver/bson" - - "go.mongodb.org/mongo-driver/bson/bsontype" bsonprim "go.mongodb.org/mongo-driver/bson/primitive" ) @@ -75,7 +61,7 @@ func (id *ObjectId) UnmarshalText(data []byte) error { // validation is performe } // Scan read a value from a database driver -func (id *ObjectId) Scan(raw interface{}) error { +func (id *ObjectId) Scan(raw any) error { var data []byte switch v := raw.(type) { case []byte: @@ -113,42 +99,6 @@ func (id *ObjectId) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON renders the object id as a BSON document -func (id ObjectId) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": bsonprim.ObjectID(id)}) -} - -// UnmarshalBSON reads the objectId from a BSON document -func (id *ObjectId) UnmarshalBSON(data []byte) error { - var obj struct { - Data bsonprim.ObjectID - } - if err := bson.Unmarshal(data, &obj); err != nil { - return err - } - *id = ObjectId(obj.Data) - return nil -} - -// MarshalBSONValue is an interface implemented by types that can marshal themselves -// into a BSON document represented as bytes. The bytes returned must be a valid -// BSON document if the error is nil. -func (id ObjectId) MarshalBSONValue() (bsontype.Type, []byte, error) { - oid := bsonprim.ObjectID(id) - return bson.TypeObjectID, oid[:], nil -} - -// UnmarshalBSONValue is an interface implemented by types that can unmarshal a -// BSON value representation of themselves. The BSON bytes and type can be -// assumed to be valid. UnmarshalBSONValue must copy the BSON value bytes if it -// wishes to retain the data after returning. -func (id *ObjectId) UnmarshalBSONValue(_ bsontype.Type, data []byte) error { - var oid bsonprim.ObjectID - copy(oid[:], data) - *id = ObjectId(oid) - return nil -} - // DeepCopyInto copies the receiver and writes its value into out. func (id *ObjectId) DeepCopyInto(out *ObjectId) { *out = *id diff --git a/bson_test.go b/bson_test.go index 7a367ad..85b4b05 100644 --- a/bson_test.go +++ b/bson_test.go @@ -1,32 +1,25 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package strfmt import ( "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" "go.mongodb.org/mongo-driver/bson" ) func TestBSONObjectId_fullCycle(t *testing.T) { + const str = "507f1f77bcf86cd799439011" + id := NewObjectId("507f1f77bcf86cd799439011") bytes, err := id.MarshalText() require.NoError(t, err) + require.True(t, IsBSONObjectID(str)) + var idCopy ObjectId err = idCopy.Scan(bytes) @@ -37,6 +30,8 @@ func TestBSONObjectId_fullCycle(t *testing.T) { require.NoError(t, err) assert.Equal(t, id, idCopy) + require.Equal(t, str, idCopy.String()) + jsonBytes, err := id.MarshalJSON() require.NoError(t, err) @@ -50,6 +45,7 @@ func TestBSONObjectId_fullCycle(t *testing.T) { err = bson.Unmarshal(bsonBytes, &idCopy) require.NoError(t, err) assert.Equal(t, id, idCopy) + } func TestDeepCopyObjectId(t *testing.T) { diff --git a/conv/date.go b/conv/date.go index b5c3820..5de2274 100644 --- a/conv/date.go +++ b/conv/date.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package conv import "github.com/go-openapi/strfmt" diff --git a/conv/date_test.go b/conv/date_test.go index 85586f4..d719d65 100644 --- a/conv/date_test.go +++ b/conv/date_test.go @@ -1,10 +1,13 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package conv import ( "testing" "time" - "github.com/stretchr/testify/assert" + "github.com/go-openapi/testify/v2/assert" "github.com/go-openapi/strfmt" ) diff --git a/conv/default.go b/conv/default.go index 078a1e5..b87dfb4 100644 --- a/conv/default.go +++ b/conv/default.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package conv import ( @@ -184,6 +187,21 @@ func UUID5Value(v *strfmt.UUID5) strfmt.UUID5 { return *v } +// UUID7 returns a pointer to of the UUID7 value passed in. +func UUID7(v strfmt.UUID7) *strfmt.UUID7 { + return &v +} + +// UUID7Value returns the value of the UUID7 pointer passed in or +// the default value if the pointer is nil. +func UUID7Value(v *strfmt.UUID7) strfmt.UUID7 { + if v == nil { + return strfmt.UUID7("") + } + + return *v +} + // ISBN returns a pointer to of the ISBN value passed in. func ISBN(v strfmt.ISBN) *strfmt.ISBN { return &v diff --git a/conv/default_test.go b/conv/default_test.go index 8b779c5..aaece3c 100644 --- a/conv/default_test.go +++ b/conv/default_test.go @@ -1,9 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package conv import ( "testing" - "github.com/stretchr/testify/assert" + "github.com/go-openapi/testify/v2/assert" "github.com/go-openapi/strfmt" ) @@ -80,6 +83,12 @@ func TestUUID5Value(t *testing.T) { assert.Equal(t, value, UUID5Value(&value)) } +func TestUUID7Value(t *testing.T) { + assert.Equal(t, strfmt.UUID7(""), UUID7Value(nil)) + value := strfmt.UUID7("foo") + assert.Equal(t, value, UUID7Value(&value)) +} + func TestISBNValue(t *testing.T) { assert.Equal(t, strfmt.ISBN(""), ISBNValue(nil)) value := strfmt.ISBN("foo") diff --git a/conv/duration.go b/conv/duration.go index ea30132..897a422 100644 --- a/conv/duration.go +++ b/conv/duration.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package conv import "github.com/go-openapi/strfmt" diff --git a/conv/duration_test.go b/conv/duration_test.go index a015936..d8a5fdb 100644 --- a/conv/duration_test.go +++ b/conv/duration_test.go @@ -1,9 +1,12 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package conv import ( "testing" - "github.com/stretchr/testify/assert" + "github.com/go-openapi/testify/v2/assert" "github.com/go-openapi/strfmt" ) diff --git a/conv/time.go b/conv/time.go index 627cd12..e10f5d0 100644 --- a/conv/time.go +++ b/conv/time.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package conv import "github.com/go-openapi/strfmt" diff --git a/conv/time_test.go b/conv/time_test.go index 9196bdb..c62417e 100644 --- a/conv/time_test.go +++ b/conv/time_test.go @@ -1,10 +1,13 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package conv import ( "testing" "time" - "github.com/stretchr/testify/assert" + "github.com/go-openapi/testify/v2/assert" "github.com/go-openapi/strfmt" ) diff --git a/conv/ulid.go b/conv/ulid.go index 112bec3..6ffff13 100644 --- a/conv/ulid.go +++ b/conv/ulid.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package conv import "github.com/go-openapi/strfmt" diff --git a/conv/ulid_test.go b/conv/ulid_test.go index 0bfb797..daf5cc6 100644 --- a/conv/ulid_test.go +++ b/conv/ulid_test.go @@ -1,11 +1,14 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package conv import ( "testing" "github.com/go-openapi/strfmt" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) const testUlid = string("01EYXZVGBHG26MFTG4JWR4K558") diff --git a/date.go b/date.go index a8f52ff..8aa17b8 100644 --- a/date.go +++ b/date.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package strfmt @@ -19,8 +8,6 @@ import ( "encoding/json" "fmt" "time" - - "go.mongodb.org/mongo-driver/bson" ) func init() { @@ -70,7 +57,7 @@ func (d Date) MarshalText() ([]byte, error) { } // Scan scans a Date value from database driver type. -func (d *Date) Scan(raw interface{}) error { +func (d *Date) Scan(raw any) error { switch v := raw.(type) { case []byte: return d.UnmarshalText(v) @@ -114,28 +101,6 @@ func (d *Date) UnmarshalJSON(data []byte) error { return nil } -func (d Date) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": d.String()}) -} - -func (d *Date) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if data, ok := m["data"].(string); ok { - rd, err := time.ParseInLocation(RFC3339FullDate, data, DefaultTimeLocation) - if err != nil { - return err - } - *d = Date(rd) - return nil - } - - return fmt.Errorf("couldn't unmarshal bson bytes value as Date: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (d *Date) DeepCopyInto(out *Date) { *out = *d diff --git a/date_test.go b/date_test.go index bd7c3ca..27f3a5d 100644 --- a/date_test.go +++ b/date_test.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package strfmt @@ -22,9 +11,8 @@ import ( "testing" "time" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.mongodb.org/mongo-driver/bson" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) var _ sql.Scanner = &Date{} @@ -57,16 +45,6 @@ func TestDate(t *testing.T) { require.NoError(t, err) assert.Equal(t, bj, b) - dateOriginal := Date(time.Date(2014, 10, 10, 0, 0, 0, 0, time.UTC)) - - bsonData, err := bson.Marshal(&dateOriginal) - require.NoError(t, err) - - var dateCopy Date - err = bson.Unmarshal(bsonData, &dateCopy) - require.NoError(t, err) - assert.Equal(t, dateOriginal, dateCopy) - var dateZero Date err = dateZero.UnmarshalJSON([]byte(jsonNull)) require.NoError(t, err) @@ -77,7 +55,7 @@ func TestDate_Scan(t *testing.T) { ref := time.Now().Truncate(24 * time.Hour).UTC() date, str := Date(ref), ref.Format(RFC3339FullDate) - values := []interface{}{str, []byte(str), ref} + values := []any{str, []byte(str), ref} for _, value := range values { result := Date{} _ = (&result).Scan(value) diff --git a/default.go b/default.go index 02e1112..8a80cfb 100644 --- a/default.go +++ b/default.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package strfmt @@ -19,14 +8,15 @@ import ( "encoding/base64" "encoding/json" "fmt" + "net" "net/mail" "net/netip" + "net/url" + "regexp" "strconv" "strings" - "github.com/asaskevich/govalidator" "github.com/google/uuid" - "go.mongodb.org/mongo-driver/bson" "golang.org/x/net/idna" ) @@ -60,10 +50,33 @@ const ( // // Deprecated: strfmt no longer uses regular expressions to validate UUIDs. UUID5Pattern = `(?i)(^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$)|(^[0-9a-f]{12}5[0-9a-f]{3}[89ab][0-9a-f]{15}$)` + + isbn10Pattern string = "^(?:[0-9]{9}X|[0-9]{10})$" + isbn13Pattern string = "^(?:[0-9]{13})$" + usCardPattern string = "^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11}|6[27][0-9]{14})$" + ssnPattern string = `^\d{3}[- ]?\d{2}[- ]?\d{4}$` + hexColorPattern string = "^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" + rgbColorPattern string = "^rgb\\(\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*\\)$" +) + +const ( + isbnVersion10 = 10 + isbnVersion13 = 13 + decimalBase = 10 ) -var idnaHostChecker = idna.New( - idna.ValidateForRegistration(), // shorthand for [idna.StrictDomainName], [idna.ValidateLabels], [idna.VerifyDNSLength], [idna.BidiRule] +var ( + idnaHostChecker = idna.New( + idna.ValidateForRegistration(), // shorthand for [idna.StrictDomainName], [idna.ValidateLabels], [idna.VerifyDNSLength], [idna.BidiRule] + ) + + whiteSpacesAndMinus = regexp.MustCompile(`[\s-]+`) + rxISBN10 = regexp.MustCompile(isbn10Pattern) + rxISBN13 = regexp.MustCompile(isbn13Pattern) + rxCreditCard = regexp.MustCompile(usCardPattern) + rxSSN = regexp.MustCompile(ssnPattern) + rxHexcolor = regexp.MustCompile(hexColorPattern) + rxRGBcolor = regexp.MustCompile(rgbColorPattern) ) // IsHostname returns true when the string is a valid hostname. @@ -322,6 +335,7 @@ const ( uuidV3 = 3 uuidV4 = 4 uuidV5 = 5 + uuidV7 = 7 ) // IsUUID3 returns true is the string matches a UUID v3, upper case is allowed @@ -342,6 +356,12 @@ func IsUUID5(str string) bool { return err == nil && id.Version() == uuid.Version(uuidV5) } +// IsUUID7 returns true is the string matches a UUID v7, upper case is allowed +func IsUUID7(str string) bool { + id, err := uuid.Parse(str) + return err == nil && id.Version() == uuid.Version(uuidV7) +} + // IsEmail validates an email address. func IsEmail(str string) bool { addr, e := mail.ParseAddress(str) @@ -370,8 +390,9 @@ func init() { // - uuid3 // - uuid4 // - uuid5 + // - uuid7 u := URI("") - Default.Add("uri", &u, govalidator.IsRequestURI) + Default.Add("uri", &u, isRequestURI) eml := Email("") Default.Add("email", &eml, IsEmail) @@ -380,16 +401,16 @@ func init() { Default.Add("hostname", &hn, IsHostname) ip4 := IPv4("") - Default.Add("ipv4", &ip4, govalidator.IsIPv4) + Default.Add("ipv4", &ip4, isIPv4) ip6 := IPv6("") - Default.Add("ipv6", &ip6, govalidator.IsIPv6) + Default.Add("ipv6", &ip6, isIPv6) cidr := CIDR("") - Default.Add("cidr", &cidr, govalidator.IsCIDR) + Default.Add("cidr", &cidr, isCIDR) mac := MAC("") - Default.Add("mac", &mac, govalidator.IsMAC) + Default.Add("mac", &mac, isMAC) uid := UUID("") Default.Add("uuid", &uid, IsUUID) @@ -403,29 +424,32 @@ func init() { uid5 := UUID5("") Default.Add("uuid5", &uid5, IsUUID5) + uid7 := UUID7("") + Default.Add("uuid7", &uid7, IsUUID7) + isbn := ISBN("") - Default.Add("isbn", &isbn, func(str string) bool { return govalidator.IsISBN10(str) || govalidator.IsISBN13(str) }) + Default.Add("isbn", &isbn, func(str string) bool { return isISBN10(str) || isISBN13(str) }) isbn10 := ISBN10("") - Default.Add("isbn10", &isbn10, govalidator.IsISBN10) + Default.Add("isbn10", &isbn10, isISBN10) isbn13 := ISBN13("") - Default.Add("isbn13", &isbn13, govalidator.IsISBN13) + Default.Add("isbn13", &isbn13, isISBN13) cc := CreditCard("") - Default.Add("creditcard", &cc, govalidator.IsCreditCard) + Default.Add("creditcard", &cc, isCreditCard) ssn := SSN("") - Default.Add("ssn", &ssn, govalidator.IsSSN) + Default.Add("ssn", &ssn, isSSN) hc := HexColor("") - Default.Add("hexcolor", &hc, govalidator.IsHexcolor) + Default.Add("hexcolor", &hc, isHexcolor) rc := RGBColor("") - Default.Add("rgbcolor", &rc, govalidator.IsRGBcolor) + Default.Add("rgbcolor", &rc, isRGBcolor) b64 := Base64([]byte(nil)) - Default.Add("byte", &b64, govalidator.IsBase64) + Default.Add("byte", &b64, isBase64) pw := Password("") Default.Add("password", &pw, func(_ string) bool { return true }) @@ -460,7 +484,7 @@ func (b *Base64) UnmarshalText(data []byte) error { // validation is performed l } // Scan read a value from a database driver -func (b *Base64) Scan(raw interface{}) error { +func (b *Base64) Scan(raw any) error { switch v := raw.(type) { case []byte: dbuf := make([]byte, base64.StdEncoding.DecodedLen(len(v))) @@ -510,29 +534,6 @@ func (b *Base64) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (b Base64) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": b.String()}) -} - -// UnmarshalBSON document into this value -func (b *Base64) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if bd, ok := m["data"].(string); ok { - vb, err := base64.StdEncoding.DecodeString(bd) - if err != nil { - return err - } - *b = Base64(vb) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as base64: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (b *Base64) DeepCopyInto(out *Base64) { *out = *b @@ -565,7 +566,7 @@ func (u *URI) UnmarshalText(data []byte) error { // validation is performed late } // Scan read a value from a database driver -func (u *URI) Scan(raw interface{}) error { +func (u *URI) Scan(raw any) error { switch v := raw.(type) { case []byte: *u = URI(string(v)) @@ -602,25 +603,6 @@ func (u *URI) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u URI) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *URI) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = URI(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as uri: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *URI) DeepCopyInto(out *URI) { *out = *u @@ -653,7 +635,7 @@ func (e *Email) UnmarshalText(data []byte) error { // validation is performed la } // Scan read a value from a database driver -func (e *Email) Scan(raw interface{}) error { +func (e *Email) Scan(raw any) error { switch v := raw.(type) { case []byte: *e = Email(string(v)) @@ -690,25 +672,6 @@ func (e *Email) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (e Email) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": e.String()}) -} - -// UnmarshalBSON document into this value -func (e *Email) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *e = Email(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as email: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (e *Email) DeepCopyInto(out *Email) { *out = *e @@ -741,7 +704,7 @@ func (h *Hostname) UnmarshalText(data []byte) error { // validation is performed } // Scan read a value from a database driver -func (h *Hostname) Scan(raw interface{}) error { +func (h *Hostname) Scan(raw any) error { switch v := raw.(type) { case []byte: *h = Hostname(string(v)) @@ -778,25 +741,6 @@ func (h *Hostname) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (h Hostname) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": h.String()}) -} - -// UnmarshalBSON document into this value -func (h *Hostname) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *h = Hostname(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as hostname: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (h *Hostname) DeepCopyInto(out *Hostname) { *out = *h @@ -829,7 +773,7 @@ func (u *IPv4) UnmarshalText(data []byte) error { // validation is performed lat } // Scan read a value from a database driver -func (u *IPv4) Scan(raw interface{}) error { +func (u *IPv4) Scan(raw any) error { switch v := raw.(type) { case []byte: *u = IPv4(string(v)) @@ -866,25 +810,6 @@ func (u *IPv4) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u IPv4) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *IPv4) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = IPv4(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as ipv4: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *IPv4) DeepCopyInto(out *IPv4) { *out = *u @@ -917,7 +842,7 @@ func (u *IPv6) UnmarshalText(data []byte) error { // validation is performed lat } // Scan read a value from a database driver -func (u *IPv6) Scan(raw interface{}) error { +func (u *IPv6) Scan(raw any) error { switch v := raw.(type) { case []byte: *u = IPv6(string(v)) @@ -954,25 +879,6 @@ func (u *IPv6) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u IPv6) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *IPv6) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = IPv6(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as ipv6: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *IPv6) DeepCopyInto(out *IPv6) { *out = *u @@ -1005,7 +911,7 @@ func (u *CIDR) UnmarshalText(data []byte) error { // validation is performed lat } // Scan read a value from a database driver -func (u *CIDR) Scan(raw interface{}) error { +func (u *CIDR) Scan(raw any) error { switch v := raw.(type) { case []byte: *u = CIDR(string(v)) @@ -1042,25 +948,6 @@ func (u *CIDR) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u CIDR) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *CIDR) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = CIDR(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as CIDR: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *CIDR) DeepCopyInto(out *CIDR) { *out = *u @@ -1093,7 +980,7 @@ func (u *MAC) UnmarshalText(data []byte) error { // validation is performed late } // Scan read a value from a database driver -func (u *MAC) Scan(raw interface{}) error { +func (u *MAC) Scan(raw any) error { switch v := raw.(type) { case []byte: *u = MAC(string(v)) @@ -1130,25 +1017,6 @@ func (u *MAC) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u MAC) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *MAC) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = MAC(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as MAC: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *MAC) DeepCopyInto(out *MAC) { *out = *u @@ -1181,7 +1049,7 @@ func (u *UUID) UnmarshalText(data []byte) error { // validation is performed lat } // Scan read a value from a database driver -func (u *UUID) Scan(raw interface{}) error { +func (u *UUID) Scan(raw any) error { switch v := raw.(type) { case []byte: *u = UUID(string(v)) @@ -1221,25 +1089,6 @@ func (u *UUID) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u UUID) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *UUID) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = UUID(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as UUID: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *UUID) DeepCopyInto(out *UUID) { *out = *u @@ -1272,7 +1121,7 @@ func (u *UUID3) UnmarshalText(data []byte) error { // validation is performed la } // Scan read a value from a database driver -func (u *UUID3) Scan(raw interface{}) error { +func (u *UUID3) Scan(raw any) error { switch v := raw.(type) { case []byte: *u = UUID3(string(v)) @@ -1312,25 +1161,6 @@ func (u *UUID3) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u UUID3) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *UUID3) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = UUID3(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as UUID3: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *UUID3) DeepCopyInto(out *UUID3) { *out = *u @@ -1363,7 +1193,7 @@ func (u *UUID4) UnmarshalText(data []byte) error { // validation is performed la } // Scan read a value from a database driver -func (u *UUID4) Scan(raw interface{}) error { +func (u *UUID4) Scan(raw any) error { switch v := raw.(type) { case []byte: *u = UUID4(string(v)) @@ -1403,25 +1233,6 @@ func (u *UUID4) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u UUID4) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *UUID4) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = UUID4(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as UUID4: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *UUID4) DeepCopyInto(out *UUID4) { *out = *u @@ -1454,7 +1265,7 @@ func (u *UUID5) UnmarshalText(data []byte) error { // validation is performed la } // Scan read a value from a database driver -func (u *UUID5) Scan(raw interface{}) error { +func (u *UUID5) Scan(raw any) error { switch v := raw.(type) { case []byte: *u = UUID5(string(v)) @@ -1494,36 +1305,89 @@ func (u *UUID5) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u UUID5) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) +// DeepCopyInto copies the receiver and writes its value into out. +func (u *UUID5) DeepCopyInto(out *UUID5) { + *out = *u } -// UnmarshalBSON document into this value -func (u *UUID5) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err +// DeepCopy copies the receiver into a new UUID5. +func (u *UUID5) DeepCopy() *UUID5 { + if u == nil { + return nil } + out := new(UUID5) + u.DeepCopyInto(out) + return out +} - if ud, ok := m["data"].(string); ok { - *u = UUID5(ud) +// UUID7 represents a uuid7 string format +// +// swagger:strfmt uuid7 +type UUID7 string + +// MarshalText turns this instance into text +func (u UUID7) MarshalText() ([]byte, error) { + return []byte(string(u)), nil +} + +// UnmarshalText hydrates this instance from text +func (u *UUID7) UnmarshalText(data []byte) error { // validation is performed later on + *u = UUID7(string(data)) + return nil +} + +// Scan read a value from a database driver +func (u *UUID7) Scan(raw any) error { + switch v := raw.(type) { + case []byte: + *u = UUID7(string(v)) + case string: + *u = UUID7(v) + default: + return fmt.Errorf("cannot sql.Scan() strfmt.UUID7 from: %#v: %w", v, ErrFormat) + } + + return nil +} + +// Value converts a value to a database driver value +func (u UUID7) Value() (driver.Value, error) { + return driver.Value(string(u)), nil +} + +func (u UUID7) String() string { + return string(u) +} + +// MarshalJSON returns the UUID as JSON +func (u UUID7) MarshalJSON() ([]byte, error) { + return json.Marshal(string(u)) +} + +// UnmarshalJSON sets the UUID from JSON +func (u *UUID7) UnmarshalJSON(data []byte) error { + if string(data) == jsonNull { return nil } - return fmt.Errorf("couldn't unmarshal bson bytes as UUID5: %w", ErrFormat) + var ustr string + if err := json.Unmarshal(data, &ustr); err != nil { + return err + } + *u = UUID7(ustr) + return nil } // DeepCopyInto copies the receiver and writes its value into out. -func (u *UUID5) DeepCopyInto(out *UUID5) { +func (u *UUID7) DeepCopyInto(out *UUID7) { *out = *u } -// DeepCopy copies the receiver into a new UUID5. -func (u *UUID5) DeepCopy() *UUID5 { +// DeepCopy copies the receiver into a new UUID7. +func (u *UUID7) DeepCopy() *UUID7 { if u == nil { return nil } - out := new(UUID5) + out := new(UUID7) u.DeepCopyInto(out) return out } @@ -1545,7 +1409,7 @@ func (u *ISBN) UnmarshalText(data []byte) error { // validation is performed lat } // Scan read a value from a database driver -func (u *ISBN) Scan(raw interface{}) error { +func (u *ISBN) Scan(raw any) error { switch v := raw.(type) { case []byte: *u = ISBN(string(v)) @@ -1585,25 +1449,6 @@ func (u *ISBN) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u ISBN) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *ISBN) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = ISBN(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as ISBN: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *ISBN) DeepCopyInto(out *ISBN) { *out = *u @@ -1636,7 +1481,7 @@ func (u *ISBN10) UnmarshalText(data []byte) error { // validation is performed l } // Scan read a value from a database driver -func (u *ISBN10) Scan(raw interface{}) error { +func (u *ISBN10) Scan(raw any) error { switch v := raw.(type) { case []byte: *u = ISBN10(string(v)) @@ -1676,25 +1521,6 @@ func (u *ISBN10) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u ISBN10) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *ISBN10) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = ISBN10(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as ISBN10: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *ISBN10) DeepCopyInto(out *ISBN10) { *out = *u @@ -1727,7 +1553,7 @@ func (u *ISBN13) UnmarshalText(data []byte) error { // validation is performed l } // Scan read a value from a database driver -func (u *ISBN13) Scan(raw interface{}) error { +func (u *ISBN13) Scan(raw any) error { switch v := raw.(type) { case []byte: *u = ISBN13(string(v)) @@ -1767,25 +1593,6 @@ func (u *ISBN13) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u ISBN13) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *ISBN13) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = ISBN13(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as ISBN13: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *ISBN13) DeepCopyInto(out *ISBN13) { *out = *u @@ -1818,7 +1625,7 @@ func (u *CreditCard) UnmarshalText(data []byte) error { // validation is perform } // Scan read a value from a database driver -func (u *CreditCard) Scan(raw interface{}) error { +func (u *CreditCard) Scan(raw any) error { switch v := raw.(type) { case []byte: *u = CreditCard(string(v)) @@ -1858,25 +1665,6 @@ func (u *CreditCard) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u CreditCard) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *CreditCard) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = CreditCard(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as CreditCard: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *CreditCard) DeepCopyInto(out *CreditCard) { *out = *u @@ -1909,7 +1697,7 @@ func (u *SSN) UnmarshalText(data []byte) error { // validation is performed late } // Scan read a value from a database driver -func (u *SSN) Scan(raw interface{}) error { +func (u *SSN) Scan(raw any) error { switch v := raw.(type) { case []byte: *u = SSN(string(v)) @@ -1949,25 +1737,6 @@ func (u *SSN) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u SSN) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *SSN) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *u = SSN(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as SSN: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *SSN) DeepCopyInto(out *SSN) { *out = *u @@ -2000,7 +1769,7 @@ func (h *HexColor) UnmarshalText(data []byte) error { // validation is performed } // Scan read a value from a database driver -func (h *HexColor) Scan(raw interface{}) error { +func (h *HexColor) Scan(raw any) error { switch v := raw.(type) { case []byte: *h = HexColor(string(v)) @@ -2040,25 +1809,6 @@ func (h *HexColor) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (h HexColor) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": h.String()}) -} - -// UnmarshalBSON document into this value -func (h *HexColor) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *h = HexColor(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as HexColor: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (h *HexColor) DeepCopyInto(out *HexColor) { *out = *h @@ -2091,7 +1841,7 @@ func (r *RGBColor) UnmarshalText(data []byte) error { // validation is performed } // Scan read a value from a database driver -func (r *RGBColor) Scan(raw interface{}) error { +func (r *RGBColor) Scan(raw any) error { switch v := raw.(type) { case []byte: *r = RGBColor(string(v)) @@ -2131,25 +1881,6 @@ func (r *RGBColor) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (r RGBColor) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": r.String()}) -} - -// UnmarshalBSON document into this value -func (r *RGBColor) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *r = RGBColor(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as RGBColor: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (r *RGBColor) DeepCopyInto(out *RGBColor) { *out = *r @@ -2183,7 +1914,7 @@ func (r *Password) UnmarshalText(data []byte) error { // validation is performed } // Scan read a value from a database driver -func (r *Password) Scan(raw interface{}) error { +func (r *Password) Scan(raw any) error { switch v := raw.(type) { case []byte: *r = Password(string(v)) @@ -2223,25 +1954,6 @@ func (r *Password) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (r Password) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": r.String()}) -} - -// UnmarshalBSON document into this value -func (r *Password) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - *r = Password(ud) - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as Password: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (r *Password) DeepCopyInto(out *Password) { *out = *r @@ -2256,3 +1968,143 @@ func (r *Password) DeepCopy() *Password { r.DeepCopyInto(out) return out } + +func isRequestURI(rawurl string) bool { + _, err := url.ParseRequestURI(rawurl) + return err == nil +} + +// isIPv4 checks if the string is an IP version 4. +func isIPv4(str string) bool { + ip := net.ParseIP(str) + return ip != nil && strings.Contains(str, ".") +} + +// isIPv6 checks if the string is an IP version 6. +func isIPv6(str string) bool { + ip := net.ParseIP(str) + return ip != nil && strings.Contains(str, ":") +} + +// isCIDR checks if the string is an valid CIDR notiation (IPV4 & IPV6) +func isCIDR(str string) bool { + _, _, err := net.ParseCIDR(str) + return err == nil +} + +// isMAC checks if a string is valid MAC address. +// Possible MAC formats: +// 01:23:45:67:89:ab +// 01:23:45:67:89:ab:cd:ef +// 01-23-45-67-89-ab +// 01-23-45-67-89-ab-cd-ef +// 0123.4567.89ab +// 0123.4567.89ab.cdef +func isMAC(str string) bool { + _, err := net.ParseMAC(str) + return err == nil +} + +// isISBN checks if the string is an ISBN (version 10 or 13). +// If version value is not equal to 10 or 13, it will be checks both variants. +func isISBN(str string, version int) bool { + sanitized := whiteSpacesAndMinus.ReplaceAllString(str, "") + var checksum int32 + var i int32 + + switch version { + case isbnVersion10: + if !rxISBN10.MatchString(sanitized) { + return false + } + for i = range isbnVersion10 - 1 { + checksum += (i + 1) * int32(sanitized[i]-'0') + } + if sanitized[isbnVersion10-1] == 'X' { + checksum += isbnVersion10 * isbnVersion10 + } else { + checksum += isbnVersion10 * int32(sanitized[isbnVersion10-1]-'0') + } + if checksum%(isbnVersion10+1) == 0 { + return true + } + return false + case isbnVersion13: + if !rxISBN13.MatchString(sanitized) { + return false + } + factor := []int32{1, 3} + for i = range isbnVersion13 - 1 { + checksum += factor[i%2] * int32(sanitized[i]-'0') + } + return (int32(sanitized[isbnVersion13-1]-'0'))-((decimalBase-(checksum%decimalBase))%decimalBase) == 0 + default: + return isISBN(str, isbnVersion10) || isISBN(str, isbnVersion13) + } +} + +// isISBN10 checks if the string is an ISBN version 10. +func isISBN10(str string) bool { + return isISBN(str, isbnVersion10) +} + +// isISBN13 checks if the string is an ISBN version 13. +func isISBN13(str string) bool { + return isISBN(str, isbnVersion13) +} + +// isCreditCard checks if the string is a credit card. +func isCreditCard(str string) bool { + sanitized := whiteSpacesAndMinus.ReplaceAllString(str, "") + if !rxCreditCard.MatchString(sanitized) { + return false + } + + number, err := strconv.ParseInt(sanitized, 0, 64) + if err != nil { + return false + } + number, lastDigit := number/decimalBase, number%decimalBase + + var sum int64 + for i := 0; number > 0; i++ { + digit := number % decimalBase + + if i%2 == 0 { + digit *= 2 + if digit > decimalBase-1 { + digit -= decimalBase - 1 + } + } + + sum += digit + number /= decimalBase + } + + return (sum+lastDigit)%decimalBase == 0 +} + +// isSSN will validate the given string as a U.S. Social Security Number +func isSSN(str string) bool { + if str == "" || len(str) != 11 { + return false + } + return rxSSN.MatchString(str) +} + +// isHexcolor checks if the string is a hexadecimal color. +func isHexcolor(str string) bool { + return rxHexcolor.MatchString(str) +} + +// isRGBcolor checks if the string is a valid RGB color in form rgb(RRR, GGG, BBB). +func isRGBcolor(str string) bool { + return rxRGBcolor.MatchString(str) +} + +// isBase64 checks if a string is base64 encoded. +func isBase64(str string) bool { + _, err := base64.StdEncoding.DecodeString(str) + + return err == nil +} diff --git a/default_test.go b/default_test.go index ab1d422..6c12015 100644 --- a/default_test.go +++ b/default_test.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package strfmt @@ -27,10 +16,9 @@ import ( "strings" "testing" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" "github.com/google/uuid" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.mongodb.org/mongo-driver/bson" ) func TestFormatURI(t *testing.T) { @@ -39,10 +27,8 @@ func TestFormatURI(t *testing.T) { testStringFormat(t, &uri, "uri", str, []string{}, []string{"somewhere.com"}) } -func TestFormatEmail(t *testing.T) { - email := Email("somebody@somewhere.com") - str := string("somebodyelse@somewhere.com") - validEmails := []string{ +func validEmails() []string { + return []string{ "blah@gmail.com", "test@d.verylongtoplevel", "email+tag@gmail.com", @@ -62,17 +48,21 @@ func TestFormatEmail(t *testing.T) { "john@com", "api@piston.ninja", } +} + +func TestFormatEmail(t *testing.T) { + email := Email("somebody@somewhere.com") + str := string("somebodyelse@somewhere.com") - testStringFormat(t, &email, "email", str, validEmails, []string{"somebody@somewhere@com"}) + testStringFormat(t, &email, "email", str, validEmails(), []string{"somebody@somewhere@com"}) } -func TestFormatHostname(t *testing.T) { - hostname := Hostname("somewhere.com") - str := string("somewhere.com") +func invalidHostnames() []string { veryLongStr := strings.Repeat("a", 256) longStr := strings.Repeat("a", 64) longAddrSegment := strings.Join([]string{"x", "y", longStr}, ".") - invalidHostnames := []string{ + + return []string{ "somewhere.com!", "user@email.domain", veryLongStr, @@ -144,8 +134,10 @@ func TestFormatHostname(t *testing.T) { "0o07.2.3.4", // unsupported alternated octal notation "localhost:81", } +} - validHostnames := []string{ +func validHostnames() []string { + return []string{ "somewhere.com", "Somewhere.Com", "888.com", @@ -207,9 +199,14 @@ func TestFormatHostname(t *testing.T) { "0300.0250.0340.001", // octal IP v4 "1.2.3.00", // leading 0, valid octal value } +} - testStringFormat(t, &hostname, "hostname", str, []string{}, invalidHostnames) - testStringFormat(t, &hostname, "hostname", str, validHostnames, []string{}) +func TestFormatHostname(t *testing.T) { + hostname := Hostname("somewhere.com") + str := string("somewhere.com") + + testStringFormat(t, &hostname, "hostname", str, []string{}, invalidHostnames()) + testStringFormat(t, &hostname, "hostname", str, validHostnames(), []string{}) } func TestFormatIPv4(t *testing.T) { @@ -237,28 +234,39 @@ func TestFormatMAC(t *testing.T) { testStringFormat(t, &mac, "mac", str, []string{}, []string{"01:02:03:04:05"}) } -func TestFormatUUID3(t *testing.T) { - first3 := uuid.NewMD5(uuid.NameSpaceURL, []byte("somewhere.com")) +func validUUID3s() []string { + other3 := uuid.NewMD5(uuid.NameSpaceURL, []byte("somewhereelse.com")) + + return []string{ + other3.String(), + strings.ReplaceAll(other3.String(), "-", ""), + } +} + +func invalidUUID3s() []string { other3 := uuid.NewMD5(uuid.NameSpaceURL, []byte("somewhereelse.com")) other4 := uuid.Must(uuid.NewRandom()) other5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) + + return []string{ + "not-a-uuid", + other4.String(), + other5.String(), + strings.ReplaceAll(other4.String(), "-", ""), + strings.ReplaceAll(other5.String(), "-", ""), + strings.Replace(other3.String(), "-", "", 2), + strings.Replace(other4.String(), "-", "", 2), + strings.Replace(other5.String(), "-", "", 2), + } +} + +func TestFormatUUID3(t *testing.T) { + first3 := uuid.NewMD5(uuid.NameSpaceURL, []byte("somewhere.com")) uuid3 := UUID3(first3.String()) - str := other3.String() + str := first3.String() testStringFormat(t, &uuid3, "uuid3", str, - []string{ - other3.String(), - strings.ReplaceAll(other3.String(), "-", ""), - }, - []string{ - "not-a-uuid", - other4.String(), - other5.String(), - strings.ReplaceAll(other4.String(), "-", ""), - strings.ReplaceAll(other5.String(), "-", ""), - strings.Replace(other3.String(), "-", "", 2), - strings.Replace(other4.String(), "-", "", 2), - strings.Replace(other5.String(), "-", "", 2), - }, + validUUID3s(), + invalidUUID3s(), ) // special case for zero UUID @@ -268,28 +276,39 @@ func TestFormatUUID3(t *testing.T) { assert.Equal(t, UUID3(""), uuidZero) } -func TestFormatUUID4(t *testing.T) { - first4 := uuid.Must(uuid.NewRandom()) +func validUUID4s() []string { + other4 := uuid.Must(uuid.NewRandom()) + + return []string{ + other4.String(), + strings.ReplaceAll(other4.String(), "-", ""), + } +} + +func invalidUUID4s() []string { other3 := uuid.NewMD5(uuid.NameSpaceURL, []byte("somewhere.com")) other4 := uuid.Must(uuid.NewRandom()) other5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) + + return []string{ + "not-a-uuid", + other3.String(), + other5.String(), + strings.ReplaceAll(other3.String(), "-", ""), + strings.ReplaceAll(other5.String(), "-", ""), + strings.Replace(other3.String(), "-", "", 2), + strings.Replace(other4.String(), "-", "", 2), + strings.Replace(other5.String(), "-", "", 2), + } +} +func TestFormatUUID4(t *testing.T) { + first4 := uuid.Must(uuid.NewRandom()) + other4 := uuid.Must(uuid.NewRandom()) uuid4 := UUID4(first4.String()) str := other4.String() testStringFormat(t, &uuid4, "uuid4", str, - []string{ - other4.String(), - strings.ReplaceAll(other4.String(), "-", ""), - }, - []string{ - "not-a-uuid", - other3.String(), - other5.String(), - strings.ReplaceAll(other3.String(), "-", ""), - strings.ReplaceAll(other5.String(), "-", ""), - strings.Replace(other3.String(), "-", "", 2), - strings.Replace(other4.String(), "-", "", 2), - strings.Replace(other5.String(), "-", "", 2), - }, + validUUID4s(), + invalidUUID4s(), ) // special case for zero UUID @@ -299,28 +318,40 @@ func TestFormatUUID4(t *testing.T) { assert.Equal(t, UUID4(""), uuidZero) } -func TestFormatUUID5(t *testing.T) { - first5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhere.com")) +func validUUID5s() []string { + other5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) + + return []string{ + other5.String(), + strings.ReplaceAll(other5.String(), "-", ""), + } +} + +func invalidUUID5s() []string { other3 := uuid.NewMD5(uuid.NameSpaceURL, []byte("somewhere.com")) other4 := uuid.Must(uuid.NewRandom()) other5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) + + return []string{ + "not-a-uuid", + other3.String(), + other4.String(), + strings.ReplaceAll(other3.String(), "-", ""), + strings.ReplaceAll(other4.String(), "-", ""), + strings.Replace(other3.String(), "-", "", 2), + strings.Replace(other4.String(), "-", "", 2), + strings.Replace(other5.String(), "-", "", 2), + } +} + +func TestFormatUUID5(t *testing.T) { + first5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhere.com")) + other5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) uuid5 := UUID5(first5.String()) str := other5.String() testStringFormat(t, &uuid5, "uuid5", str, - []string{ - other5.String(), - strings.ReplaceAll(other5.String(), "-", ""), - }, - []string{ - "not-a-uuid", - other3.String(), - other4.String(), - strings.ReplaceAll(other3.String(), "-", ""), - strings.ReplaceAll(other4.String(), "-", ""), - strings.Replace(other3.String(), "-", "", 2), - strings.Replace(other4.String(), "-", "", 2), - strings.Replace(other5.String(), "-", "", 2), - }, + validUUID5s(), + invalidUUID5s(), ) // special case for zero UUID @@ -330,8 +361,49 @@ func TestFormatUUID5(t *testing.T) { assert.Equal(t, UUID5(""), uuidZero) } -func TestFormatUUID(t *testing.T) { - first5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhere.com")) +func validUUID7s() []string { + other7 := uuid.Must(uuid.NewV7()) + + return []string{ + other7.String(), + strings.ReplaceAll(other7.String(), "-", ""), + } +} + +func invalidUUID7s() []string { + other3 := uuid.NewMD5(uuid.NameSpaceURL, []byte("somewhere.com")) + other4 := uuid.Must(uuid.NewRandom()) + other5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) + + return []string{ + "not-a-uuid", + other3.String(), + other4.String(), + strings.ReplaceAll(other3.String(), "-", ""), + strings.ReplaceAll(other4.String(), "-", ""), + strings.Replace(other3.String(), "-", "", 2), + strings.Replace(other4.String(), "-", "", 2), + strings.Replace(other5.String(), "-", "", 2), + } +} + +func TestFormatUUID7(t *testing.T) { + first7 := uuid.Must(uuid.NewV7()) + str := first7.String() + uuid7 := UUID7(str) + testStringFormat(t, &uuid7, "uuid7", str, + validUUID7s(), + invalidUUID7s(), + ) + + // special case for zero UUID + var uuidZero UUID7 + err := uuidZero.UnmarshalJSON([]byte(jsonNull)) + require.NoError(t, err) + assert.Equal(t, UUID7(""), uuidZero) +} + +func validUUIDs() []string { other3 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) other4 := uuid.Must(uuid.NewRandom()) other5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) @@ -339,26 +411,39 @@ func TestFormatUUID(t *testing.T) { other7 := uuid.Must(uuid.NewV7()) microsoft := "0" + other4.String() + "f" + return []string{ + other3.String(), + other4.String(), + other5.String(), + strings.ReplaceAll(other3.String(), "-", ""), + strings.ReplaceAll(other4.String(), "-", ""), + strings.ReplaceAll(other5.String(), "-", ""), + other6.String(), + other7.String(), + microsoft, + } +} + +func invalidUUIDs() []string { + other3 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) + other4 := uuid.Must(uuid.NewRandom()) + other5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) + + return []string{ + "not-a-uuid", + strings.Replace(other3.String(), "-", "", 2), + strings.Replace(other4.String(), "-", "", 2), + strings.Replace(other5.String(), "-", "", 2), + } +} +func TestFormatUUID(t *testing.T) { + first5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhere.com")) + other5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) uuid := UUID(first5.String()) str := other5.String() testStringFormat(t, &uuid, "uuid", str, - []string{ - other3.String(), - other4.String(), - other5.String(), - strings.ReplaceAll(other3.String(), "-", ""), - strings.ReplaceAll(other4.String(), "-", ""), - strings.ReplaceAll(other5.String(), "-", ""), - other6.String(), - other7.String(), - microsoft, - }, - []string{ - "not-a-uuid", - strings.Replace(other3.String(), "-", "", 2), - strings.Replace(other4.String(), "-", "", 2), - strings.Replace(other5.String(), "-", "", 2), - }, + validUUIDs(), + invalidUUIDs(), ) // special case for zero UUID @@ -440,14 +525,6 @@ func TestFormatBase64(t *testing.T) { require.NoError(t, err) assert.Equal(t, bj, b) - bsonData, err := bson.Marshal(subj2) - require.NoError(t, err) - - var b64Copy Base64 - err = bson.Unmarshal(bsonData, &b64Copy) - require.NoError(t, err) - assert.Equal(t, subj2, b64Copy) - testValid(t, "byte", str) testInvalid(t, "byte", "ZWxpemFiZXRocG9zZXk") // missing pad char @@ -478,8 +555,6 @@ type testableFormat interface { encoding.TextUnmarshaler json.Marshaler json.Unmarshaler - bson.Marshaler - bson.Unmarshaler fmt.Stringer sql.Scanner driver.Valuer @@ -517,18 +592,6 @@ func testStringFormat(t *testing.T, what testableFormat, format, with string, va require.NoError(t, err) assert.Equalf(t, bj, b, "[%s]MarshalJSON: expected %v and %v to be value equal as []byte", format, string(b), with) - // bson encoding interface - bsonData, err := bson.Marshal(what) - require.NoError(t, err) - - resetValue(t, format, what) - - err = bson.Unmarshal(bsonData, what) - require.NoError(t, err) - val = reflect.Indirect(reflect.ValueOf(what)) - strVal = val.String() - assert.Equal(t, with, strVal, "[%s]bson.Unmarshal: expected %v and %v to be equal (reset value) ", format, what, with) - // Scanner interface resetValue(t, format, what) err = what.Scan(with) @@ -788,6 +851,23 @@ func TestDeepCopyUUID5(t *testing.T) { assert.Nil(t, out3) } +func TestDeepCopyUUID7(t *testing.T) { + first7 := uuid.Must(uuid.NewV7()) + uuid7 := UUID7(first7.String()) + in := &uuid7 + + out := new(UUID7) + in.DeepCopyInto(out) + assert.Equal(t, in, out) + + out2 := in.DeepCopy() + assert.Equal(t, in, out2) + + var inNil *UUID7 + out3 := inNil.DeepCopy() + assert.Nil(t, out3) +} + func TestDeepCopyISBN(t *testing.T) { isbn := ISBN("0321751043") in := &isbn diff --git a/doc.go b/doc.go index 41aebe6..5825b72 100644 --- a/doc.go +++ b/doc.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 // Package strfmt contains custom string formats // diff --git a/duration.go b/duration.go index 749c4b5..908c1b0 100644 --- a/duration.go +++ b/duration.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package strfmt @@ -23,8 +12,6 @@ import ( "strconv" "strings" "time" - - "go.mongodb.org/mongo-driver/bson" ) func init() { @@ -154,7 +141,7 @@ func ParseDuration(cand string) (time.Duration, error) { } // Scan reads a Duration value from database driver type. -func (d *Duration) Scan(raw interface{}) error { +func (d *Duration) Scan(raw any) error { switch v := raw.(type) { // TODO: case []byte: // ? case int64: @@ -203,28 +190,6 @@ func (d *Duration) UnmarshalJSON(data []byte) error { return nil } -func (d Duration) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": d.String()}) -} - -func (d *Duration) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if data, ok := m["data"].(string); ok { - rd, err := ParseDuration(data) - if err != nil { - return err - } - *d = Duration(rd) - return nil - } - - return fmt.Errorf("couldn't unmarshal bson bytes value as Date: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (d *Duration) DeepCopyInto(out *Duration) { *out = *d diff --git a/duration_test.go b/duration_test.go index d76fa36..1eac2a2 100644 --- a/duration_test.go +++ b/duration_test.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package strfmt @@ -19,9 +8,8 @@ import ( "testing" "time" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.mongodb.org/mongo-driver/bson" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) func TestDuration(t *testing.T) { @@ -65,15 +53,6 @@ func TestDuration(t *testing.T) { b, err = pp.MarshalJSON() require.NoError(t, err) assert.Equal(t, bj, b) - - dur := Duration(42) - bsonData, err := bson.Marshal(&dur) - require.NoError(t, err) - - var durCopy Duration - err = bson.Unmarshal(bsonData, &durCopy) - require.NoError(t, err) - assert.Equal(t, dur, durCopy) } func testDurationParser(t *testing.T, toParse string, expected time.Duration) { @@ -97,7 +76,7 @@ func TestIsDuration_Failed(t *testing.T) { func testDurationSQLScanner(t *testing.T, dur time.Duration) { t.Helper() - values := []interface{}{int64(dur), float64(dur)} + values := []any{int64(dur), float64(dur)} for _, value := range values { var result Duration err := result.Scan(value) diff --git a/errors.go b/errors.go index 9a92403..9faa37c 100644 --- a/errors.go +++ b/errors.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package strfmt type strfmtError string diff --git a/format.go b/format.go index 73f83af..d9d9e04 100644 --- a/format.go +++ b/format.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package strfmt @@ -18,6 +7,7 @@ import ( "encoding" "fmt" "reflect" + "slices" "strings" "sync" "time" @@ -32,27 +22,6 @@ var Default = NewSeededFormats(nil, nil) // Validator represents a validator for a string format. type Validator func(string) bool -// Format represents a string format. -// -// All implementations of Format provide a string representation and text -// marshaling/unmarshaling interface to be used by encoders (e.g. encoding/json). -type Format interface { - String() string - encoding.TextMarshaler - encoding.TextUnmarshaler -} - -// Registry is a registry of string formats, with a validation method. -type Registry interface { - Add(string, Format, Validator) bool - DelByName(string) bool - GetType(string) (reflect.Type, bool) - ContainsName(string) bool - Validates(string, string) bool - Parse(string, string) (any, error) - MapStructureHookFunc() mapstructure.DecodeHookFunc -} - // NewFormats creates a new formats registry seeded with the values from the default func NewFormats() Registry { //nolint:forcetypeassert @@ -64,10 +33,9 @@ func NewSeededFormats(seeds []knownFormat, normalizer NameNormalizer) Registry { if normalizer == nil { normalizer = DefaultNameNormalizer } - // copy here, don't modify original - d := append([]knownFormat(nil), seeds...) + // copy here, don't modify the original return &defaultFormats{ - data: d, + data: slices.Clone(seeds), normalizeName: normalizer, } } @@ -139,6 +107,8 @@ func (f *defaultFormats) MapStructureHookFunc() mapstructure.DecodeHookFunc { return UUID4(data), nil case "uuid5": return UUID5(data), nil + case "uuid7": + return UUID7(data), nil case "hostname": return Hostname(data), nil case "ipv4": diff --git a/format_test.go b/format_test.go index 6693cc2..7e45c5f 100644 --- a/format_test.go +++ b/format_test.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package strfmt @@ -19,9 +8,9 @@ import ( "testing" "time" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" "github.com/go-viper/mapstructure/v2" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) type testFormat string @@ -138,6 +127,7 @@ type testStruct struct { UUID3 UUID3 `json:"uuid3,omitempty"` UUID4 UUID4 `json:"uuid4,omitempty"` UUID5 UUID5 `json:"uuid5,omitempty"` + UUID7 UUID7 `json:"uuid7,omitempty"` Hn Hostname `json:"hn,omitempty"` Ipv4 IPv4 `json:"ipv4,omitempty"` Ipv6 IPv6 `json:"ipv6,omitempty"` @@ -167,6 +157,7 @@ func TestDecodeHook(t *testing.T) { "uuid3": "bcd02e22-68f0-3046-a512-327cca9def8f", "uuid4": "025b0d74-00a2-4048-bf57-227c5111bb34", "uuid5": "886313e1-3b8a-5372-9b90-0c9aee199e5d", + "uuid7": "019a15e6-cd5e-7204-b11b-12075f4c8a25", "hn": "somewhere.com", "ipv4": "192.168.254.1", "ipv6": "::1", @@ -199,6 +190,7 @@ func TestDecodeHook(t *testing.T) { UUID3: UUID3("bcd02e22-68f0-3046-a512-327cca9def8f"), UUID4: UUID4("025b0d74-00a2-4048-bf57-227c5111bb34"), UUID5: UUID5("886313e1-3b8a-5372-9b90-0c9aee199e5d"), + UUID7: UUID7("019a15e6-cd5e-7204-b11b-12075f4c8a25"), Hn: Hostname("somewhere.com"), Ipv4: IPv4("192.168.254.1"), Ipv6: IPv6("::1"), diff --git a/go.mod b/go.mod index 754c822..7337a10 100644 --- a/go.mod +++ b/go.mod @@ -1,21 +1,15 @@ module github.com/go-openapi/strfmt require ( - github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 - github.com/go-openapi/errors v0.22.3 + github.com/go-openapi/errors v0.22.4 + github.com/go-openapi/testify/v2 v2.0.2 github.com/go-viper/mapstructure/v2 v2.4.0 github.com/google/uuid v1.6.0 github.com/oklog/ulid v1.3.1 - github.com/stretchr/testify v1.11.1 - go.mongodb.org/mongo-driver v1.17.4 - golang.org/x/net v0.44.0 + go.mongodb.org/mongo-driver v1.17.6 + golang.org/x/net v0.46.0 ) -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/text v0.29.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) +require golang.org/x/text v0.30.0 // indirect go 1.24.0 diff --git a/go.sum b/go.sum index 61539b4..95798c7 100644 --- a/go.sum +++ b/go.sum @@ -1,33 +1,20 @@ -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-openapi/errors v0.22.3 h1:k6Hxa5Jg1TUyZnOwV2Lh81j8ayNw5VVYLvKrp4zFKFs= -github.com/go-openapi/errors v0.22.3/go.mod h1:+WvbaBBULWCOna//9B9TbLNGSFOfF8lY9dw4hGiEiKQ= +github.com/go-openapi/errors v0.22.4 h1:oi2K9mHTOb5DPW2Zjdzs/NIvwi2N3fARKaTJLdNabaM= +github.com/go-openapi/errors v0.22.4/go.mod h1:z9S8ASTUqx7+CP1Q8dD8ewGH/1JWFFLX/2PmAYNQLgk= +github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= +github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw= -go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= -golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= -golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +go.mongodb.org/mongo-driver v1.17.6 h1:87JUG1wZfWsr6rIz3ZmpH90rL5tea7O3IHuSwHUpsss= +go.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= +golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= +golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= diff --git a/hack/coverage b/hack/coverage deleted file mode 100755 index 76d8bfd..0000000 --- a/hack/coverage +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -set -e -o pipefail - -# Run test coverage on each subdirectories and merge the coverage profile. -echo "mode: ${GOCOVMODE-atomic}" > coverage.txt - -# Standard go tooling behavior is to ignore dirs with leading underscores -# skip generator for race detection and coverage -for dir in $(go list ./...) -do - pth="$GOPATH/src/$dir" - go test -race -timeout 20m -covermode=${GOCOVMODE-atomic} -coverprofile=${pth}/profile.out $dir - if [ -f $pth/profile.out ] - then - cat $pth/profile.out | tail -n +2 >> coverage.txt - rm $pth/profile.out - fi -done - -go tool cover -func coverage.txt \ No newline at end of file diff --git a/ifaces.go b/ifaces.go new file mode 100644 index 0000000..1b9e72c --- /dev/null +++ b/ifaces.go @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package strfmt + +import ( + "encoding" + "reflect" + + "github.com/go-viper/mapstructure/v2" +) + +// Format represents a string format. +// +// All implementations of Format provide a string representation and text +// marshaling/unmarshaling interface to be used by encoders (e.g. encoding/json). +type Format interface { + String() string + encoding.TextMarshaler + encoding.TextUnmarshaler +} + +// Registry is a registry of string formats, with a validation method. +type Registry interface { + Add(string, Format, Validator) bool + DelByName(string) bool + GetType(string) (reflect.Type, bool) + ContainsName(string) bool + Validates(string, string) bool + Parse(string, string) (any, error) + MapStructureHookFunc() mapstructure.DecodeHookFunc +} diff --git a/mongo.go b/mongo.go new file mode 100644 index 0000000..641fed9 --- /dev/null +++ b/mongo.go @@ -0,0 +1,646 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package strfmt + +import ( + "encoding/base64" + "encoding/binary" + "fmt" + "time" + + "github.com/oklog/ulid" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/bsontype" + bsonprim "go.mongodb.org/mongo-driver/bson/primitive" +) + +var ( + _ bson.Marshaler = Date{} + _ bson.Unmarshaler = &Date{} + _ bson.Marshaler = Base64{} + _ bson.Unmarshaler = &Base64{} + _ bson.Marshaler = Duration(0) + _ bson.Unmarshaler = (*Duration)(nil) + _ bson.Marshaler = DateTime{} + _ bson.Unmarshaler = &DateTime{} + _ bson.Marshaler = ULID{} + _ bson.Unmarshaler = &ULID{} + _ bson.Marshaler = URI("") + _ bson.Unmarshaler = (*URI)(nil) + _ bson.Marshaler = Email("") + _ bson.Unmarshaler = (*Email)(nil) + _ bson.Marshaler = Hostname("") + _ bson.Unmarshaler = (*Hostname)(nil) + _ bson.Marshaler = IPv4("") + _ bson.Unmarshaler = (*IPv4)(nil) + _ bson.Marshaler = IPv6("") + _ bson.Unmarshaler = (*IPv6)(nil) + _ bson.Marshaler = CIDR("") + _ bson.Unmarshaler = (*CIDR)(nil) + _ bson.Marshaler = MAC("") + _ bson.Unmarshaler = (*MAC)(nil) + _ bson.Marshaler = Password("") + _ bson.Unmarshaler = (*Password)(nil) + _ bson.Marshaler = UUID("") + _ bson.Unmarshaler = (*UUID)(nil) + _ bson.Marshaler = UUID3("") + _ bson.Unmarshaler = (*UUID3)(nil) + _ bson.Marshaler = UUID4("") + _ bson.Unmarshaler = (*UUID4)(nil) + _ bson.Marshaler = UUID5("") + _ bson.Unmarshaler = (*UUID5)(nil) + _ bson.Marshaler = UUID7("") + _ bson.Unmarshaler = (*UUID7)(nil) + _ bson.Marshaler = ISBN("") + _ bson.Unmarshaler = (*ISBN)(nil) + _ bson.Marshaler = ISBN10("") + _ bson.Unmarshaler = (*ISBN10)(nil) + _ bson.Marshaler = ISBN13("") + _ bson.Unmarshaler = (*ISBN13)(nil) + _ bson.Marshaler = CreditCard("") + _ bson.Unmarshaler = (*CreditCard)(nil) + _ bson.Marshaler = SSN("") + _ bson.Unmarshaler = (*SSN)(nil) + _ bson.Marshaler = HexColor("") + _ bson.Unmarshaler = (*HexColor)(nil) + _ bson.Marshaler = RGBColor("") + _ bson.Unmarshaler = (*RGBColor)(nil) + _ bson.Marshaler = ObjectId{} + _ bson.Unmarshaler = &ObjectId{} + + _ bson.ValueMarshaler = DateTime{} + _ bson.ValueUnmarshaler = &DateTime{} + _ bson.ValueMarshaler = ObjectId{} + _ bson.ValueUnmarshaler = &ObjectId{} +) + +const ( + millisec = 1000 + microsec = 1_000_000 + bsonDateTimeSize = 8 +) + +func (d Date) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": d.String()}) +} + +func (d *Date) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if data, ok := m["data"].(string); ok { + rd, err := time.ParseInLocation(RFC3339FullDate, data, DefaultTimeLocation) + if err != nil { + return err + } + *d = Date(rd) + return nil + } + + return fmt.Errorf("couldn't unmarshal bson bytes value as Date: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (b Base64) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": b.String()}) +} + +// UnmarshalBSON document into this value +func (b *Base64) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if bd, ok := m["data"].(string); ok { + vb, err := base64.StdEncoding.DecodeString(bd) + if err != nil { + return err + } + *b = Base64(vb) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as base64: %w", ErrFormat) +} + +func (d Duration) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": d.String()}) +} + +func (d *Duration) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if data, ok := m["data"].(string); ok { + rd, err := ParseDuration(data) + if err != nil { + return err + } + *d = Duration(rd) + return nil + } + + return fmt.Errorf("couldn't unmarshal bson bytes value as Date: %w", ErrFormat) +} + +// MarshalBSON renders the DateTime as a BSON document +func (t DateTime) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": t}) +} + +// UnmarshalBSON reads the DateTime from a BSON document +func (t *DateTime) UnmarshalBSON(data []byte) error { + var obj struct { + Data DateTime + } + + if err := bson.Unmarshal(data, &obj); err != nil { + return err + } + + *t = obj.Data + + return nil +} + +// MarshalBSONValue is an interface implemented by types that can marshal themselves +// into a BSON document represented as bytes. The bytes returned must be a valid +// BSON document if the error is nil. +// +// Marshals a DateTime as a bson.TypeDateTime, an int64 representing +// milliseconds since epoch. +func (t DateTime) MarshalBSONValue() (bsontype.Type, []byte, error) { + // UnixNano cannot be used directly, the result of calling UnixNano on the zero + // Time is undefined. Thats why we use time.Nanosecond() instead. + + tNorm := NormalizeTimeForMarshal(time.Time(t)) + i64 := tNorm.Unix()*millisec + int64(tNorm.Nanosecond())/microsec + buf := make([]byte, bsonDateTimeSize) + binary.LittleEndian.PutUint64(buf, uint64(i64)) //nolint:gosec // it's okay to handle negative int64 this way + + return bson.TypeDateTime, buf, nil +} + +// UnmarshalBSONValue is an interface implemented by types that can unmarshal a +// BSON value representation of themselves. The BSON bytes and type can be +// assumed to be valid. UnmarshalBSONValue must copy the BSON value bytes if it +// wishes to retain the data after returning. +func (t *DateTime) UnmarshalBSONValue(tpe bsontype.Type, data []byte) error { + if tpe == bson.TypeNull { + *t = DateTime{} + return nil + } + + if len(data) != bsonDateTimeSize { + return fmt.Errorf("bson date field length not exactly %d bytes: %w", bsonDateTimeSize, ErrFormat) + } + + i64 := int64(binary.LittleEndian.Uint64(data)) //nolint:gosec // it's okay if we overflow and get a negative datetime + *t = DateTime(time.Unix(i64/millisec, i64%millisec*microsec)) + + return nil +} + +// MarshalBSON document from this value +func (u ULID) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *ULID) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + id, err := ulid.ParseStrict(ud) + if err != nil { + return fmt.Errorf("couldn't parse bson bytes as ULID: %w: %w", err, ErrFormat) + } + u.ULID = id + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as ULID: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u URI) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *URI) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = URI(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as uri: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (e Email) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": e.String()}) +} + +// UnmarshalBSON document into this value +func (e *Email) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *e = Email(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as email: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (h Hostname) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": h.String()}) +} + +// UnmarshalBSON document into this value +func (h *Hostname) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *h = Hostname(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as hostname: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u IPv4) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *IPv4) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = IPv4(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as ipv4: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u IPv6) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *IPv6) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = IPv6(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as ipv6: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u CIDR) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *CIDR) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = CIDR(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as CIDR: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u MAC) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *MAC) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = MAC(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as MAC: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (r Password) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": r.String()}) +} + +// UnmarshalBSON document into this value +func (r *Password) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *r = Password(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as Password: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u UUID) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *UUID) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = UUID(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as UUID: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u UUID3) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *UUID3) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = UUID3(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as UUID3: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u UUID4) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *UUID4) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = UUID4(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as UUID4: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u UUID5) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *UUID5) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = UUID5(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as UUID5: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u UUID7) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *UUID7) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = UUID7(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as UUID7: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u ISBN) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *ISBN) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = ISBN(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as ISBN: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u ISBN10) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *ISBN10) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = ISBN10(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as ISBN10: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u ISBN13) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *ISBN13) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = ISBN13(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as ISBN13: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u CreditCard) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *CreditCard) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = CreditCard(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as CreditCard: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (u SSN) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": u.String()}) +} + +// UnmarshalBSON document into this value +func (u *SSN) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *u = SSN(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as SSN: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (h HexColor) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": h.String()}) +} + +// UnmarshalBSON document into this value +func (h *HexColor) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *h = HexColor(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as HexColor: %w", ErrFormat) +} + +// MarshalBSON document from this value +func (r RGBColor) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": r.String()}) +} + +// UnmarshalBSON document into this value +func (r *RGBColor) UnmarshalBSON(data []byte) error { + var m bson.M + if err := bson.Unmarshal(data, &m); err != nil { + return err + } + + if ud, ok := m["data"].(string); ok { + *r = RGBColor(ud) + return nil + } + return fmt.Errorf("couldn't unmarshal bson bytes as RGBColor: %w", ErrFormat) +} + +// MarshalBSON renders the object id as a BSON document +func (id ObjectId) MarshalBSON() ([]byte, error) { + return bson.Marshal(bson.M{"data": bsonprim.ObjectID(id)}) +} + +// UnmarshalBSON reads the objectId from a BSON document +func (id *ObjectId) UnmarshalBSON(data []byte) error { + var obj struct { + Data bsonprim.ObjectID + } + if err := bson.Unmarshal(data, &obj); err != nil { + return err + } + *id = ObjectId(obj.Data) + return nil +} + +// MarshalBSONValue is an interface implemented by types that can marshal themselves +// into a BSON document represented as bytes. The bytes returned must be a valid +// BSON document if the error is nil. +func (id ObjectId) MarshalBSONValue() (bsontype.Type, []byte, error) { + oid := bsonprim.ObjectID(id) + return bson.TypeObjectID, oid[:], nil +} + +// UnmarshalBSONValue is an interface implemented by types that can unmarshal a +// BSON value representation of themselves. The BSON bytes and type can be +// assumed to be valid. UnmarshalBSONValue must copy the BSON value bytes if it +// wishes to retain the data after returning. +func (id *ObjectId) UnmarshalBSONValue(_ bsontype.Type, data []byte) error { + var oid bsonprim.ObjectID + copy(oid[:], data) + *id = ObjectId(oid) + return nil +} diff --git a/mongo_test.go b/mongo_test.go new file mode 100644 index 0000000..c49190d --- /dev/null +++ b/mongo_test.go @@ -0,0 +1,295 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package strfmt + +import ( + "reflect" + "testing" + "time" + + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" + "github.com/google/uuid" + "go.mongodb.org/mongo-driver/bson" +) + +type testableBSONFormat interface { + testableFormat + + bson.Marshaler + bson.Unmarshaler +} + +func TestBSONDate(t *testing.T) { + dateOriginal := Date(time.Date(2014, 10, 10, 0, 0, 0, 0, time.UTC)) + + bsonData, err := bson.Marshal(&dateOriginal) + require.NoError(t, err) + + var dateCopy Date + err = bson.Unmarshal(bsonData, &dateCopy) + require.NoError(t, err) + assert.Equal(t, dateOriginal, dateCopy) +} + +func TestBSONBase64(t *testing.T) { + const b64 string = "This is a byte array with unprintable chars, but it also isn" + b := []byte(b64) + subj := Base64(b) + + bsonData, err := bson.Marshal(subj) + require.NoError(t, err) + + var b64Copy Base64 + err = bson.Unmarshal(bsonData, &b64Copy) + require.NoError(t, err) + assert.Equal(t, subj, b64Copy) +} + +func TestBSONDuration(t *testing.T) { + dur := Duration(42) + bsonData, err := bson.Marshal(&dur) + require.NoError(t, err) + + var durCopy Duration + err = bson.Unmarshal(bsonData, &durCopy) + require.NoError(t, err) + assert.Equal(t, dur, durCopy) +} + +func TestBSONDateTime(t *testing.T) { + for caseNum, example := range testCases { + t.Logf("Case #%d", caseNum) + dt := DateTime(example.time) + + bsonData, err := bson.Marshal(&dt) + require.NoError(t, err) + + var dtCopy DateTime + err = bson.Unmarshal(bsonData, &dtCopy) + require.NoError(t, err) + // BSON DateTime type loses timezone information, so compare UTC() + assert.Equal(t, time.Time(dt).UTC(), time.Time(dtCopy).UTC()) + + // Check value marshaling explicitly + m := bson.M{"data": dt} + bsonData, err = bson.Marshal(&m) + require.NoError(t, err) + + var mCopy bson.M + err = bson.Unmarshal(bsonData, &mCopy) + require.NoError(t, err) + + data, ok := m["data"].(DateTime) + assert.True(t, ok) + assert.Equal(t, time.Time(dt).UTC(), time.Time(data).UTC()) + } +} + +func TestBSONULID(t *testing.T) { + t.Parallel() + t.Run("positive", func(t *testing.T) { + t.Parallel() + ulid, _ := ParseULID(testUlid) + + bsonData, err := bson.Marshal(&ulid) + require.NoError(t, err) + + var ulidUnmarshaled ULID + err = bson.Unmarshal(bsonData, &ulidUnmarshaled) + require.NoError(t, err) + assert.Equal(t, ulid, ulidUnmarshaled) + + // Check value marshaling explicitly + m := bson.M{"data": ulid} + bsonData, err = bson.Marshal(&m) + require.NoError(t, err) + + var mUnmarshaled bson.M + err = bson.Unmarshal(bsonData, &mUnmarshaled) + require.NoError(t, err) + + data, ok := m["data"].(ULID) + assert.True(t, ok) + assert.Equal(t, ulid, data) + }) + t.Run("negative", func(t *testing.T) { + t.Parallel() + uuid := UUID("00000000-0000-0000-0000-000000000000") + bsonData, err := bson.Marshal(&uuid) + require.NoError(t, err) + + var ulidUnmarshaled ULID + err = bson.Unmarshal(bsonData, &ulidUnmarshaled) + require.Error(t, err) + }) +} + +func TestFormatBSON(t *testing.T) { + t.Run("with URI", func(t *testing.T) { + t.Run("should bson.Marshal and bson.Unmarshal", func(t *testing.T) { + uri := URI("http://somewhere.com") + str := "http://somewhereelse.com" + testBSONStringFormat(t, &uri, "uri", str, []string{}, []string{"somewhere.com"}) + }) + }) + + t.Run("with Email", func(t *testing.T) { + email := Email("somebody@somewhere.com") + str := string("somebodyelse@somewhere.com") + + testBSONStringFormat(t, &email, "email", str, validEmails(), []string{"somebody@somewhere@com"}) + }) + + t.Run("with Hostname", func(t *testing.T) { + hostname := Hostname("somewhere.com") + str := string("somewhere.com") + + testBSONStringFormat(t, &hostname, "hostname", str, []string{}, invalidHostnames()) + testBSONStringFormat(t, &hostname, "hostname", str, validHostnames(), []string{}) + }) + + t.Run("with IPv4", func(t *testing.T) { + ipv4 := IPv4("192.168.254.1") + str := string("192.168.254.2") + testBSONStringFormat(t, &ipv4, "ipv4", str, []string{}, []string{"198.168.254.2.2"}) + }) + + t.Run("with IPv6", func(t *testing.T) { + ipv6 := IPv6("::1") + str := string("::2") + testBSONStringFormat(t, &ipv6, "ipv6", str, []string{}, []string{"127.0.0.1"}) + }) + + t.Run("with CIDR", func(t *testing.T) { + cidr := CIDR("192.168.254.1/24") + str := string("192.168.254.2/24") + testBSONStringFormat(t, &cidr, "cidr", str, []string{"192.0.2.1/24", "2001:db8:a0b:12f0::1/32"}, []string{"198.168.254.2", "2001:db8:a0b:12f0::1"}) + }) + + t.Run("with MAC", func(t *testing.T) { + mac := MAC("01:02:03:04:05:06") + str := string("06:05:04:03:02:01") + testBSONStringFormat(t, &mac, "mac", str, []string{}, []string{"01:02:03:04:05"}) + }) + + t.Run("with UUID3", func(t *testing.T) { + first3 := uuid.NewMD5(uuid.NameSpaceURL, []byte("somewhere.com")) + uuid3 := UUID3(first3.String()) + str := first3.String() + testBSONStringFormat(t, &uuid3, "uuid3", str, + validUUID3s(), + invalidUUID3s(), + ) + }) + + t.Run("with UUID4", func(t *testing.T) { + first4 := uuid.Must(uuid.NewRandom()) + other4 := uuid.Must(uuid.NewRandom()) + uuid4 := UUID4(first4.String()) + str := other4.String() + testBSONStringFormat(t, &uuid4, "uuid4", str, + validUUID4s(), + invalidUUID4s(), + ) + }) + + t.Run("with UUID5", func(t *testing.T) { + first5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhere.com")) + other5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) + uuid5 := UUID5(first5.String()) + str := other5.String() + testBSONStringFormat(t, &uuid5, "uuid5", str, + validUUID5s(), + invalidUUID5s(), + ) + }) + + t.Run("with UUID7", func(t *testing.T) { + first7 := uuid.Must(uuid.NewV7()) + str := first7.String() + uuid7 := UUID7(str) + testBSONStringFormat(t, &uuid7, "uuid7", str, + validUUID7s(), + invalidUUID7s(), + ) + }) + + t.Run("with UUID", func(t *testing.T) { + first5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhere.com")) + other5 := uuid.NewSHA1(uuid.NameSpaceURL, []byte("somewhereelse.com")) + uuid := UUID(first5.String()) + str := other5.String() + testBSONStringFormat(t, &uuid, "uuid", str, + validUUIDs(), + invalidUUIDs(), + ) + }) + + t.Run("with ISBN", func(t *testing.T) { + isbn := ISBN("0321751043") + str := string("0321751043") + testBSONStringFormat(t, &isbn, "isbn", str, []string{}, []string{"836217463"}) // bad checksum + }) + + t.Run("with ISBN10", func(t *testing.T) { + isbn10 := ISBN10("0321751043") + str := string("0321751043") + testBSONStringFormat(t, &isbn10, "isbn10", str, []string{}, []string{"836217463"}) // bad checksum + }) + + t.Run("with ISBN13", func(t *testing.T) { + isbn13 := ISBN13("978-0321751041") + str := string("978-0321751041") + testBSONStringFormat(t, &isbn13, "isbn13", str, []string{}, []string{"978-0321751042"}) // bad checksum + }) + + t.Run("with HexColor", func(t *testing.T) { + hexColor := HexColor("#FFFFFF") + str := string("#000000") + testBSONStringFormat(t, &hexColor, "hexcolor", str, []string{}, []string{"#fffffffz"}) + }) + + t.Run("with RGBColor", func(t *testing.T) { + rgbColor := RGBColor("rgb(255,255,255)") + str := string("rgb(0,0,0)") + testBSONStringFormat(t, &rgbColor, "rgbcolor", str, []string{}, []string{"rgb(300,0,0)"}) + }) + + t.Run("with SSN", func(t *testing.T) { + ssn := SSN("111-11-1111") + str := string("999 99 9999") + testBSONStringFormat(t, &ssn, "ssn", str, []string{}, []string{"999 99 999"}) + }) + + t.Run("with CreditCard", func(t *testing.T) { + creditCard := CreditCard("4111-1111-1111-1111") + str := string("4012-8888-8888-1881") + testBSONStringFormat(t, &creditCard, "creditcard", str, []string{}, []string{"9999-9999-9999-999"}) + }) + + t.Run("with Password", func(t *testing.T) { + password := Password("super secret stuff here") + testBSONStringFormat(t, &password, "password", "super secret!!!", []string{"even more secret"}, []string{}) + }) +} + +func testBSONStringFormat(t *testing.T, what testableBSONFormat, format, with string, _, _ []string) { + t.Helper() + b := []byte(with) + err := what.UnmarshalText(b) + require.NoError(t, err) + + // bson encoding interface + bsonData, err := bson.Marshal(what) + require.NoError(t, err) + + resetValue(t, format, what) + + err = bson.Unmarshal(bsonData, what) + require.NoError(t, err) + val := reflect.Indirect(reflect.ValueOf(what)) + strVal := val.String() + assert.Equal(t, with, strVal, "[%s]bson.Unmarshal: expected %v and %v to be equal (reset value) ", format, what, with) +} diff --git a/time.go b/time.go index 84e2412..8085aaf 100644 --- a/time.go +++ b/time.go @@ -1,31 +1,15 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package strfmt import ( "database/sql/driver" - "encoding/binary" "encoding/json" "fmt" "regexp" "strings" "time" - - "go.mongodb.org/mongo-driver/bson" - - "go.mongodb.org/mongo-driver/bson/bsontype" ) var ( @@ -178,7 +162,7 @@ func (t *DateTime) UnmarshalText(text []byte) error { } // Scan scans a DateTime value from database driver type. -func (t *DateTime) Scan(raw interface{}) error { +func (t *DateTime) Scan(raw any) error { // TODO: case int64: and case float64: ? switch v := raw.(type) { case []byte: @@ -224,69 +208,6 @@ func (t *DateTime) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON renders the DateTime as a BSON document -func (t DateTime) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": t}) -} - -// UnmarshalBSON reads the DateTime from a BSON document -func (t *DateTime) UnmarshalBSON(data []byte) error { - var obj struct { - Data DateTime - } - - if err := bson.Unmarshal(data, &obj); err != nil { - return err - } - - *t = obj.Data - - return nil -} - -const bsonDateLength = 8 - -// MarshalBSONValue is an interface implemented by types that can marshal themselves -// into a BSON document represented as bytes. The bytes returned must be a valid -// BSON document if the error is nil. -// -// Marshals a DateTime as a bsontype.DateTime, an int64 representing -// milliseconds since epoch. -func (t DateTime) MarshalBSONValue() (bsontype.Type, []byte, error) { - // UnixNano cannot be used directly, the result of calling UnixNano on the zero - // Time is undefined. That's why we use time.Nanosecond() instead. - tNorm := NormalizeTimeForMarshal(time.Time(t)) - i64 := tNorm.UnixMilli() - - buf := make([]byte, bsonDateLength) - // int64 -> uint64 conversion is safe here - binary.LittleEndian.PutUint64(buf, uint64(i64)) //nolint:gosec - - return bson.TypeDateTime, buf, nil -} - -// UnmarshalBSONValue is an interface implemented by types that can unmarshal a -// BSON value representation of themselves. The BSON bytes and type can be -// assumed to be valid. UnmarshalBSONValue must copy the BSON value bytes if it -// wishes to retain the data after returning. -func (t *DateTime) UnmarshalBSONValue(tpe bsontype.Type, data []byte) error { - if tpe == bson.TypeNull { - *t = DateTime{} - return nil - } - - if len(data) != bsonDateLength { - return fmt.Errorf("bson date field length not exactly 8 bytes: %w", ErrFormat) - } - - // it's ok to get negative values after conversion - i64 := int64(binary.LittleEndian.Uint64(data)) //nolint:gosec - // TODO: Use bsonprim.DateTime.Time() method - *t = DateTime(time.UnixMilli(i64)) - - return nil -} - // DeepCopyInto copies the receiver and writes its value into out. func (t *DateTime) DeepCopyInto(out *DateTime) { *out = *t diff --git a/time_test.go b/time_test.go index e95f13f..0848212 100644 --- a/time_test.go +++ b/time_test.go @@ -1,16 +1,5 @@ -// Copyright 2015 go-swagger maintainers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 package strfmt @@ -20,9 +9,8 @@ import ( "testing" "time" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.mongodb.org/mongo-driver/bson" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) var ( @@ -280,35 +268,6 @@ func TestDateTime_Scan_Failed(t *testing.T) { require.Error(t, err) } -func TestDateTime_BSON(t *testing.T) { - for caseNum, example := range testCases { - t.Logf("Case #%d", caseNum) - dt := DateTime(example.time) - - bsonData, err := bson.Marshal(&dt) - require.NoError(t, err) - - var dtCopy DateTime - err = bson.Unmarshal(bsonData, &dtCopy) - require.NoError(t, err) - // BSON DateTime type loses timezone information, so compare UTC() - assert.Equal(t, time.Time(dt).UTC(), time.Time(dtCopy).UTC()) - - // Check value marshaling explicitly - m := bson.M{"data": dt} - bsonData, err = bson.Marshal(&m) - require.NoError(t, err) - - var mCopy bson.M - err = bson.Unmarshal(bsonData, &mCopy) - require.NoError(t, err) - - data, ok := m["data"].(DateTime) - assert.True(t, ok) - assert.Equal(t, time.Time(dt).UTC(), time.Time(data).UTC()) - } -} - func TestDeepCopyDateTime(t *testing.T) { p, err := ParseDateTime("2011-08-18T19:03:37.000000000+01:00") require.NoError(t, err) diff --git a/ulid.go b/ulid.go index 434eb01..85c5b53 100644 --- a/ulid.go +++ b/ulid.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package strfmt import ( @@ -9,7 +12,6 @@ import ( "sync" "github.com/oklog/ulid" - "go.mongodb.org/mongo-driver/bson" ) // ULID represents a ulid string format @@ -28,12 +30,12 @@ type ULID struct { var ( ulidEntropyPool = sync.Pool{ - New: func() interface{} { + New: func() any { return cryptorand.Reader }, } - ULIDScanDefaultFunc = func(raw interface{}) (ULID, error) { + ULIDScanDefaultFunc = func(raw any) (ULID, error) { u := NewULIDZero() switch x := raw.(type) { case nil: @@ -111,7 +113,7 @@ func NewULID() (ULID, error) { } // GetULID returns underlying instance of ULID -func (u *ULID) GetULID() interface{} { +func (u *ULID) GetULID() any { return u.ULID } @@ -126,7 +128,7 @@ func (u *ULID) UnmarshalText(data []byte) error { // validation is performed lat } // Scan reads a value from a database driver -func (u *ULID) Scan(raw interface{}) error { +func (u *ULID) Scan(raw any) error { ul, err := ULIDScanOverrideFunc(raw) if err == nil { *u = ul @@ -165,29 +167,6 @@ func (u *ULID) UnmarshalJSON(data []byte) error { return nil } -// MarshalBSON document from this value -func (u ULID) MarshalBSON() ([]byte, error) { - return bson.Marshal(bson.M{"data": u.String()}) -} - -// UnmarshalBSON document into this value -func (u *ULID) UnmarshalBSON(data []byte) error { - var m bson.M - if err := bson.Unmarshal(data, &m); err != nil { - return err - } - - if ud, ok := m["data"].(string); ok { - id, err := ulid.ParseStrict(ud) - if err != nil { - return fmt.Errorf("couldn't parse bson bytes as ULID: %w: %w", err, ErrFormat) - } - u.ULID = id - return nil - } - return fmt.Errorf("couldn't unmarshal bson bytes as ULID: %w", ErrFormat) -} - // DeepCopyInto copies the receiver and writes its value into out. func (u *ULID) DeepCopyInto(out *ULID) { *out = *u diff --git a/ulid_test.go b/ulid_test.go index 8be6714..ac6e1c7 100644 --- a/ulid_test.go +++ b/ulid_test.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2015-2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package strfmt import ( @@ -8,9 +11,8 @@ import ( "sync" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.mongodb.org/mongo-driver/bson" + "github.com/go-openapi/testify/v2/assert" + "github.com/go-openapi/testify/v2/require" ) const testUlid = string("01EYXZVGBHG26MFTG4JWR4K558") @@ -51,45 +53,6 @@ func TestFormatULID_Text(t *testing.T) { }) } -func TestFormatULID_BSON(t *testing.T) { - t.Parallel() - t.Run("positive", func(t *testing.T) { - t.Parallel() - ulid, _ := ParseULID(testUlid) - - bsonData, err := bson.Marshal(&ulid) - require.NoError(t, err) - - var ulidUnmarshaled ULID - err = bson.Unmarshal(bsonData, &ulidUnmarshaled) - require.NoError(t, err) - assert.Equal(t, ulid, ulidUnmarshaled) - - // Check value marshaling explicitly - m := bson.M{"data": ulid} - bsonData, err = bson.Marshal(&m) - require.NoError(t, err) - - var mUnmarshaled bson.M - err = bson.Unmarshal(bsonData, &mUnmarshaled) - require.NoError(t, err) - - data, ok := m["data"].(ULID) - assert.True(t, ok) - assert.Equal(t, ulid, data) - }) - t.Run("negative", func(t *testing.T) { - t.Parallel() - uuid := UUID("00000000-0000-0000-0000-000000000000") - bsonData, err := bson.Marshal(&uuid) - require.NoError(t, err) - - var ulidUnmarshaled ULID - err = bson.Unmarshal(bsonData, &ulidUnmarshaled) - require.Error(t, err) - }) -} - func TestFormatULID_JSON(t *testing.T) { t.Parallel() t.Run("positive", func(t *testing.T) { @@ -195,7 +158,7 @@ func TestFormatULID_Scan(t *testing.T) { ulid2, err := ParseULID(testUlidAlt) require.NoError(t, err) - ULIDScanOverrideFunc = func(raw interface{}) (ULID, error) { + ULIDScanOverrideFunc = func(raw any) (ULID, error) { u := NewULIDZero() switch x := raw.(type) { case [16]byte: