Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
95d7c90492
|
|||
23a3235c7e
|
|||
506d276962
|
@@ -1,5 +1,5 @@
|
|||||||
package goext
|
package goext
|
||||||
|
|
||||||
const GoextVersion = "0.0.580"
|
const GoextVersion = "0.0.583"
|
||||||
|
|
||||||
const GoextVersionTimestamp = "2025-06-16T08:40:07+0200"
|
const GoextVersionTimestamp = "2025-06-25T10:59:23+0200"
|
||||||
|
23
langext/base64.go
Normal file
23
langext/base64.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package langext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DecodeBase64Any decodes a base64 encoded string
|
||||||
|
// Works with all variants (std, url, imap), padded and unpadded and even ignores linrebreaks and indents
|
||||||
|
func DecodeBase64Any(data string) ([]byte, error) {
|
||||||
|
data = strings.ReplaceAll(data, "\n", "") // remove linebreaks and indents
|
||||||
|
data = strings.ReplaceAll(data, "\t", "") // remove linebreaks and indents
|
||||||
|
data = strings.ReplaceAll(data, " ", "") // remove linebreaks and indents
|
||||||
|
|
||||||
|
data = strings.ReplaceAll(data, ",", "/") // base64_imap --> base64_std
|
||||||
|
|
||||||
|
data = strings.ReplaceAll(data, "_", "/") // base64_url --> base64_std
|
||||||
|
data = strings.ReplaceAll(data, "-", "+") // base64_url --> base64_std
|
||||||
|
|
||||||
|
data = strings.ReplaceAll(data, "=", "") // no padding
|
||||||
|
|
||||||
|
return base64.RawStdEncoding.DecodeString(data)
|
||||||
|
}
|
246
langext/base64_test.go
Normal file
246
langext/base64_test.go
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
package langext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDecodeBase64Any_StandardPadded(t *testing.T) {
|
||||||
|
input := "SGVsbG8gV29ybGQ=" // "Hello World" in standard Base64 (padded)
|
||||||
|
expected := "Hello World"
|
||||||
|
|
||||||
|
result, err := DecodeBase64Any(input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(result) != expected {
|
||||||
|
t.Errorf("expected %q, got %q", expected, string(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeBase64Any_StandardUnpadded(t *testing.T) {
|
||||||
|
input := "SGVsbG8gV29ybGQ" // "Hello World" in standard Base64 (unpadded)
|
||||||
|
expected := "Hello World"
|
||||||
|
|
||||||
|
result, err := DecodeBase64Any(input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(result) != expected {
|
||||||
|
t.Errorf("expected %q, got %q", expected, string(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeBase64Any_URLPadded(t *testing.T) {
|
||||||
|
input := "SGVsbG8tV29ybGQ=" // "Hello-World" in Base64 URL (padded)
|
||||||
|
expected := "Hello-World"
|
||||||
|
|
||||||
|
result, err := DecodeBase64Any(input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(result) != expected {
|
||||||
|
t.Errorf("expected %q, got %q", expected, string(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeBase64Any_URLUnpadded(t *testing.T) {
|
||||||
|
input := "SGVsbG8tV29ybGQ" // "Hello-World" in Base64 URL (unpadded)
|
||||||
|
expected := "Hello-World"
|
||||||
|
|
||||||
|
result, err := DecodeBase64Any(input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(result) != expected {
|
||||||
|
t.Errorf("expected %q, got %q", expected, string(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeBase64Any_IMAPPadded(t *testing.T) {
|
||||||
|
input := "SGVsbG8,V29ybGQ=" // "Hello/World" in Base64 IMAP (padded)
|
||||||
|
expected := "Hello?World"
|
||||||
|
|
||||||
|
result, err := DecodeBase64Any(input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(result) != expected {
|
||||||
|
t.Errorf("expected %q, got %q", expected, string(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeBase64Any_IMAPUnpadded(t *testing.T) {
|
||||||
|
input := "SGVsbG8,V29ybGQ" // "Hello/World" in Base64 IMAP (unpadded)
|
||||||
|
expected := "Hello?World"
|
||||||
|
|
||||||
|
result, err := DecodeBase64Any(input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(result) != expected {
|
||||||
|
t.Errorf("expected %q, got %q", expected, string(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeBase64Any_InvalidInput(t *testing.T) {
|
||||||
|
input := "Invalid@@Base64" // Invalid Base64 input
|
||||||
|
|
||||||
|
_, err := DecodeBase64Any(input)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected an error, but got none")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeBase64Any_EmptyInput(t *testing.T) {
|
||||||
|
input := "" // Empty input
|
||||||
|
|
||||||
|
result, err := DecodeBase64Any(input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(result) != 0 {
|
||||||
|
t.Errorf("expected empty result, got %q", string(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeBase64Any_WhitespaceInput(t *testing.T) {
|
||||||
|
input := " SGVsbG8gV29ybGQ= " // Input with leading and trailing spaces
|
||||||
|
expected := "Hello World"
|
||||||
|
|
||||||
|
result, err := DecodeBase64Any(input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(result) != expected {
|
||||||
|
t.Errorf("expected %q, got %q", expected, string(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeBase64Any_LineBreaksInput(t *testing.T) {
|
||||||
|
input := "SGVs\nbG8g\nV29y\nbGQ=" // Input with line breaks
|
||||||
|
expected := "Hello World"
|
||||||
|
|
||||||
|
result, err := DecodeBase64Any(input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(result) != expected {
|
||||||
|
t.Errorf("expected %q, got %q", expected, string(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeBase64Any_TabCharactersInput(t *testing.T) {
|
||||||
|
input := "SGVsbG8g\tV29ybGQ=" // Input with tab characters
|
||||||
|
expected := "Hello World"
|
||||||
|
|
||||||
|
result, err := DecodeBase64Any(input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(result) != expected {
|
||||||
|
t.Errorf("expected %q, got %q", expected, string(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeBase64Any_SpecialCharactersIgnored(t *testing.T) {
|
||||||
|
input := "SGVsbG8gV29ybGQ=!!" // Input with ignored special characters
|
||||||
|
|
||||||
|
_, err := DecodeBase64Any(input)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected an error, but got none")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeBase64Any_SingleCharacterInput(t *testing.T) {
|
||||||
|
input := "QQ==" // "A" in Base64
|
||||||
|
expected := "A"
|
||||||
|
|
||||||
|
result, err := DecodeBase64Any(input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(result) != expected {
|
||||||
|
t.Errorf("expected %q, got %q", expected, string(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeBase64Any_LongInput(t *testing.T) {
|
||||||
|
input := "U29tZSB2ZXJ5IGxvbmcgc3RyaW5nIHdpdGggbXVsdGlwbGUgbGluZXMgYW5kIHNwYWNlcy4=" // Long Base64 string
|
||||||
|
expected := "Some very long string with multiple lines and spaces."
|
||||||
|
|
||||||
|
result, err := DecodeBase64Any(input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(result) != expected {
|
||||||
|
t.Errorf("expected %q, got %q", expected, string(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeBase64Any_Standard63And64(t *testing.T) {
|
||||||
|
input := "Pz8/Pw==" // "???" in standard Base64 (63 = '+', 64 = '/')
|
||||||
|
expected := "????"
|
||||||
|
|
||||||
|
result, err := DecodeBase64Any(input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(result) != expected {
|
||||||
|
t.Errorf("expected %q, got %q", expected, string(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeBase64Any_Standard63And64_NoPad(t *testing.T) {
|
||||||
|
input := "Pz8/Pw" // "???" in standard Base64 (63 = '+', 64 = '/')
|
||||||
|
expected := "????"
|
||||||
|
|
||||||
|
result, err := DecodeBase64Any(input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(result) != expected {
|
||||||
|
t.Errorf("expected %q, got %q", expected, string(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeBase64Any_URL63And64(t *testing.T) {
|
||||||
|
input := "Pz8_Pw==" // "???" in Base64 URL-safe (63 = '_', 64 = '-')
|
||||||
|
expected := "????"
|
||||||
|
|
||||||
|
result, err := DecodeBase64Any(input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(result) != expected {
|
||||||
|
t.Errorf("expected %q, got %q", expected, string(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeBase64Any_URL63And64_NoPad(t *testing.T) {
|
||||||
|
input := "Pz8_Pw==" // "???" in Base64 URL-safe (63 = '_', 64 = '-')
|
||||||
|
expected := "????"
|
||||||
|
|
||||||
|
result, err := DecodeBase64Any(input)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(result) != expected {
|
||||||
|
t.Errorf("expected %q, got %q", expected, string(result))
|
||||||
|
}
|
||||||
|
}
|
@@ -1,57 +1,51 @@
|
|||||||
package mongoext
|
package mongoext
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
|
||||||
"go.mongodb.org/mongo-driver/bson/bsoncodec"
|
|
||||||
"go.mongodb.org/mongo-driver/bson/bsontype"
|
|
||||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/exerr"
|
"git.blackforestbytes.com/BlackForestBytes/goext/exerr"
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
"git.blackforestbytes.com/BlackForestBytes/goext/langext"
|
||||||
"git.blackforestbytes.com/BlackForestBytes/goext/rfctime"
|
"git.blackforestbytes.com/BlackForestBytes/goext/rfctime"
|
||||||
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/bsoncodec"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
"reflect"
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CreateGoExtBsonRegistry() *bsoncodec.Registry {
|
func CreateGoExtBsonRegistry() *bsoncodec.Registry {
|
||||||
rb := bsoncodec.NewRegistryBuilder()
|
reg := bson.NewRegistry()
|
||||||
|
|
||||||
rb.RegisterTypeDecoder(reflect.TypeOf(rfctime.RFC3339Time{}), rfctime.RFC3339Time{})
|
reg.RegisterTypeDecoder(reflect.TypeOf(rfctime.RFC3339Time{}), rfctime.RFC3339Time{})
|
||||||
rb.RegisterTypeDecoder(reflect.TypeOf(&rfctime.RFC3339Time{}), rfctime.RFC3339Time{})
|
reg.RegisterTypeDecoder(reflect.TypeOf(&rfctime.RFC3339Time{}), rfctime.RFC3339Time{})
|
||||||
|
|
||||||
rb.RegisterTypeDecoder(reflect.TypeOf(rfctime.RFC3339NanoTime{}), rfctime.RFC3339NanoTime{})
|
reg.RegisterTypeDecoder(reflect.TypeOf(rfctime.RFC3339NanoTime{}), rfctime.RFC3339NanoTime{})
|
||||||
rb.RegisterTypeDecoder(reflect.TypeOf(&rfctime.RFC3339NanoTime{}), rfctime.RFC3339NanoTime{})
|
reg.RegisterTypeDecoder(reflect.TypeOf(&rfctime.RFC3339NanoTime{}), rfctime.RFC3339NanoTime{})
|
||||||
|
|
||||||
rb.RegisterTypeDecoder(reflect.TypeOf(rfctime.UnixTime{}), rfctime.UnixTime{})
|
reg.RegisterTypeDecoder(reflect.TypeOf(rfctime.UnixTime{}), rfctime.UnixTime{})
|
||||||
rb.RegisterTypeDecoder(reflect.TypeOf(&rfctime.UnixTime{}), rfctime.UnixTime{})
|
reg.RegisterTypeDecoder(reflect.TypeOf(&rfctime.UnixTime{}), rfctime.UnixTime{})
|
||||||
|
|
||||||
rb.RegisterTypeDecoder(reflect.TypeOf(rfctime.UnixMilliTime{}), rfctime.UnixMilliTime{})
|
reg.RegisterTypeDecoder(reflect.TypeOf(rfctime.UnixMilliTime{}), rfctime.UnixMilliTime{})
|
||||||
rb.RegisterTypeDecoder(reflect.TypeOf(&rfctime.UnixMilliTime{}), rfctime.UnixMilliTime{})
|
reg.RegisterTypeDecoder(reflect.TypeOf(&rfctime.UnixMilliTime{}), rfctime.UnixMilliTime{})
|
||||||
|
|
||||||
rb.RegisterTypeDecoder(reflect.TypeOf(rfctime.UnixNanoTime{}), rfctime.UnixNanoTime{})
|
reg.RegisterTypeDecoder(reflect.TypeOf(rfctime.UnixNanoTime{}), rfctime.UnixNanoTime{})
|
||||||
rb.RegisterTypeDecoder(reflect.TypeOf(&rfctime.UnixNanoTime{}), rfctime.UnixNanoTime{})
|
reg.RegisterTypeDecoder(reflect.TypeOf(&rfctime.UnixNanoTime{}), rfctime.UnixNanoTime{})
|
||||||
|
|
||||||
rb.RegisterTypeDecoder(reflect.TypeOf(rfctime.Date{}), rfctime.Date{})
|
reg.RegisterTypeDecoder(reflect.TypeOf(rfctime.Date{}), rfctime.Date{})
|
||||||
rb.RegisterTypeDecoder(reflect.TypeOf(&rfctime.Date{}), rfctime.Date{})
|
reg.RegisterTypeDecoder(reflect.TypeOf(&rfctime.Date{}), rfctime.Date{})
|
||||||
|
|
||||||
rb.RegisterTypeDecoder(reflect.TypeOf(rfctime.SecondsF64(0)), rfctime.SecondsF64(0))
|
reg.RegisterTypeDecoder(reflect.TypeOf(rfctime.SecondsF64(0)), rfctime.SecondsF64(0))
|
||||||
rb.RegisterTypeDecoder(reflect.TypeOf(langext.Ptr(rfctime.SecondsF64(0))), rfctime.SecondsF64(0))
|
reg.RegisterTypeDecoder(reflect.TypeOf(langext.Ptr(rfctime.SecondsF64(0))), rfctime.SecondsF64(0))
|
||||||
|
|
||||||
rb.RegisterTypeDecoder(reflect.TypeOf(exerr.ErrorCategory{}), exerr.ErrorCategory{})
|
reg.RegisterTypeDecoder(reflect.TypeOf(exerr.ErrorCategory{}), exerr.ErrorCategory{})
|
||||||
rb.RegisterTypeDecoder(reflect.TypeOf(langext.Ptr(exerr.ErrorCategory{})), exerr.ErrorCategory{})
|
reg.RegisterTypeDecoder(reflect.TypeOf(langext.Ptr(exerr.ErrorCategory{})), exerr.ErrorCategory{})
|
||||||
|
|
||||||
rb.RegisterTypeDecoder(reflect.TypeOf(exerr.ErrorSeverity{}), exerr.ErrorSeverity{})
|
reg.RegisterTypeDecoder(reflect.TypeOf(exerr.ErrorSeverity{}), exerr.ErrorSeverity{})
|
||||||
rb.RegisterTypeDecoder(reflect.TypeOf(langext.Ptr(exerr.ErrorSeverity{})), exerr.ErrorSeverity{})
|
reg.RegisterTypeDecoder(reflect.TypeOf(langext.Ptr(exerr.ErrorSeverity{})), exerr.ErrorSeverity{})
|
||||||
|
|
||||||
rb.RegisterTypeDecoder(reflect.TypeOf(exerr.ErrorType{}), exerr.ErrorType{})
|
reg.RegisterTypeDecoder(reflect.TypeOf(exerr.ErrorType{}), exerr.ErrorType{})
|
||||||
rb.RegisterTypeDecoder(reflect.TypeOf(langext.Ptr(exerr.ErrorType{})), exerr.ErrorType{})
|
reg.RegisterTypeDecoder(reflect.TypeOf(langext.Ptr(exerr.ErrorType{})), exerr.ErrorType{})
|
||||||
|
|
||||||
bsoncodec.DefaultValueEncoders{}.RegisterDefaultEncoders(rb)
|
|
||||||
bsoncodec.DefaultValueDecoders{}.RegisterDefaultDecoders(rb)
|
|
||||||
|
|
||||||
bson.PrimitiveCodecs{}.RegisterPrimitiveCodecs(rb)
|
|
||||||
|
|
||||||
// otherwise we get []primitve.E when unmarshalling into any
|
// otherwise we get []primitve.E when unmarshalling into any
|
||||||
// which will result in {'key': .., 'value': ...}[] json when json-marshalling
|
// which will result in {'key': .., 'value': ...}[] json when json-marshalling
|
||||||
rb.RegisterTypeMapEntry(bsontype.EmbeddedDocument, reflect.TypeOf(primitive.M{}))
|
reg.RegisterTypeMapEntry(bson.TypeEmbeddedDocument, reflect.TypeOf(primitive.M{}))
|
||||||
|
|
||||||
return rb.Build()
|
return reg
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user