diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 66474e60..08e31336 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -96,17 +96,17 @@ jobs: continue-on-error: false run: | IGNORE_BUILD=$(sed '/^[[:space:]]*$/d' "build.$(go env | grep GOARCH | cut -d'=' -f2 | tr -d '"' | tr -d "'")" | tr '\n' '|') - CGO_ENABLED=1 GOOS=linux GOARCH=amd64 GOAMD64=v4 go build -a -v -race -installsuffix cgo -ldflags "-w -s -extldflags '-static' " $(go list ./... | grep -vPi ${IGNORE_BUILD::-1}) + CGO_ENABLED=1 GOOS=linux GOARCH=amd64 GOAMD64=v4 go build -a -race -installsuffix cgo -ldflags "-w -s -extldflags '-static' " $(go list ./... | grep -vPi ${IGNORE_BUILD::-1}) - name: Test Build Windows/amd64 with CGO continue-on-error: false run: | IGNORE_BUILD=$(sed '/^[[:space:]]*$/d' "build.$(go env | grep GOARCH | cut -d'=' -f2 | tr -d '"' | tr -d "'")" | tr '\n' '|') - CC=/usr/bin/x86_64-w64-mingw32-gcc CGO_ENABLED=1 GOOS=windows GOARCH=amd64 GOAMD64=v4 go build -a -v -ldflags "-w -s -extldflags '-static' " $(go list ./... | grep -vPi ${IGNORE_BUILD::-1}) + CC=/usr/bin/x86_64-w64-mingw32-gcc CGO_ENABLED=1 GOOS=windows GOARCH=amd64 GOAMD64=v4 go build -a -ldflags "-w -s -extldflags '-static' " $(go list ./... | grep -vPi ${IGNORE_BUILD::-1}) - name: Test Build Darwin/arm64 with suffix/CGO continue-on-error: false run: | IGNORE_BUILD=$(sed '/^[[:space:]]*$/d' "build.$(go env | grep GOARCH | cut -d'=' -f2 | tr -d '"' | tr -d "'")" | tr '\n' '|') - CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 GOAMD64=v1 go build -a -v -ldflags "-w -s -extldflags '-static' " $(go list ./... | grep -vPi ${IGNORE_BUILD::-1}) + CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 GOAMD64=v1 go build -a -ldflags "-w -s -extldflags '-static' " $(go list ./... | grep -vPi ${IGNORE_BUILD::-1}) diff --git a/archive/bz2/reader.go b/archive/bz2/reader.go index f45db154..b09cbf1b 100644 --- a/archive/bz2/reader.go +++ b/archive/bz2/reader.go @@ -41,9 +41,10 @@ func GetFile(src io.ReadSeeker, dst io.WriteSeeker) errors.Error { r := bzip2.NewReader(src) - //nolint #nosec - /* #nosec */ - if _, e := io.Copy(dst, r); e != nil { + // #nosec + _, e := io.Copy(dst, r) + + if e != nil { return ErrorIOCopy.Error(e) } else if _, e = dst.Seek(0, io.SeekStart); e != nil { return ErrorFileSeek.Error(e) diff --git a/archive/gzip/reader.go b/archive/gzip/reader.go index ad7f09d8..0b34713f 100644 --- a/archive/gzip/reader.go +++ b/archive/gzip/reader.go @@ -29,9 +29,8 @@ import ( gz "compress/gzip" "io" - libfpg "github.com/nabbar/golib/file/progress" - "github.com/nabbar/golib/errors" + libfpg "github.com/nabbar/golib/file/progress" ) func GetFile(src io.ReadSeeker, dst io.WriteSeeker) errors.Error { @@ -50,9 +49,10 @@ func GetFile(src io.ReadSeeker, dst io.WriteSeeker) errors.Error { _ = r.Close() }() - //nolint #nosec - /* #nosec */ - if _, e = io.Copy(dst, r); e != nil { + // #nosec + _, e = io.Copy(dst, r) + + if e != nil { return ErrorIOCopy.Error(e) } else if _, e = dst.Seek(0, io.SeekStart); e != nil { return ErrorFileSeek.Error(e) @@ -88,6 +88,7 @@ func GetGunZipSize(src io.ReadSeeker) int64 { } var n int64 + // #nosec n, e = io.Copy(io.Discard, r) if e != nil { diff --git a/archive/tar/reader.go b/archive/tar/reader.go index 1dc92e69..09e99304 100644 --- a/archive/tar/reader.go +++ b/archive/tar/reader.go @@ -33,10 +33,9 @@ import ( "runtime" "strings" - libfpg "github.com/nabbar/golib/file/progress" - libarc "github.com/nabbar/golib/archive/archive" liberr "github.com/nabbar/golib/errors" + libfpg "github.com/nabbar/golib/file/progress" ) func GetFile(src, dst libfpg.Progress, filenameContain, filenameRegex string) liberr.Error { @@ -93,9 +92,11 @@ func GetAll(src io.ReadSeeker, outputFolder string, defaultDirPerm os.FileMode) return ErrorTarNext.Error(e) } + dst := filepath.Join(outputFolder, libarc.CleanPath(strings.Replace(h.Name, ".."+string(filepath.Separator), "", -1))) + //nolint #nosec /* #nosec */ - if err := writeContent(r, h, filepath.Join(outputFolder, libarc.CleanPath(h.Name)), defaultDirPerm); err != nil { + if err := writeContent(r, h, dst, defaultDirPerm); err != nil { return err } } diff --git a/archive/zip/reader.go b/archive/zip/reader.go index 746a5633..9eab7449 100644 --- a/archive/zip/reader.go +++ b/archive/zip/reader.go @@ -30,11 +30,11 @@ import ( "io" "os" "path/filepath" - - libfpg "github.com/nabbar/golib/file/progress" + "strings" arcmod "github.com/nabbar/golib/archive/archive" liberr "github.com/nabbar/golib/errors" + libfpg "github.com/nabbar/golib/file/progress" ) func GetFile(src, dst libfpg.Progress, filenameContain, filenameRegex string) liberr.Error { @@ -119,9 +119,10 @@ func GetAll(src libfpg.Progress, outputFolder string, defaultDirPerm os.FileMode continue } - //nolint #nosec - /* #nosec */ - if err := writeContent(f, filepath.Join(outputFolder, arcmod.CleanPath(f.Name)), defaultDirPerm); err != nil { + dst := filepath.Join(outputFolder, arcmod.CleanPath(strings.Replace(f.Name, ".."+string(filepath.Separator), "..", -1))) + + // #nosec + if err := writeContent(f, dst, defaultDirPerm); err != nil { return err } } @@ -175,9 +176,10 @@ func writeContent(f *zip.File, out string, defaultDirPerm os.FileMode) (err libe return ErrorZipFileOpen.Error(e) } - //nolint #nosec - /* #nosec */ - if _, e = io.Copy(dst, r); e != nil { + // #nosec + _, e = io.Copy(dst, r) + + if e != nil { return ErrorIOCopy.Error(e) } else if e = dst.Close(); e != nil { return ErrorFileClose.Error(e) diff --git a/aws/bucket/bucket.go b/aws/bucket/bucket.go index 30303ba5..a507942a 100644 --- a/aws/bucket/bucket.go +++ b/aws/bucket/bucket.go @@ -28,6 +28,7 @@ package bucket import ( "fmt" + sdkaws "github.com/aws/aws-sdk-go-v2/aws" sdksss "github.com/aws/aws-sdk-go-v2/service/s3" sdkstp "github.com/aws/aws-sdk-go-v2/service/s3/types" libhlp "github.com/nabbar/golib/aws/helper" @@ -64,10 +65,12 @@ func (cli *client) _create(RegionConstraint string, lockEnable bool) error { if RegionConstraint != "" { in.CreateBucketConfiguration.LocationConstraint = sdkstp.BucketLocationConstraint(RegionConstraint) + } else { + in.CreateBucketConfiguration.LocationConstraint = sdkstp.BucketLocationConstraint(cli.GetRegion()) } if lockEnable { - in.ObjectLockEnabledForBucket = true + in.ObjectLockEnabledForBucket = sdkaws.Bool(true) } out, err := cli.s3.CreateBucket(cli.GetContext(), in) diff --git a/aws/model.go b/aws/model.go index bf7270ab..ae8806c9 100644 --- a/aws/model.go +++ b/aws/model.go @@ -118,11 +118,13 @@ func (c *client) _NewClientIAM(ctx context.Context, httpClient *http.Client) (*s EndpointOptions: sdkiam.EndpointResolverOptions{ DisableHTTPS: !c.c.IsHTTPs(), }, - EndpointResolver: c._NewIAMResolver(cfg), - HTTPSignerV4: sig, - Region: cfg.Region, - Retryer: ret, - HTTPClient: httpClient, + BaseEndpoint: sdkaws.String(c.c.GetEndpoint().String()), + EndpointResolver: c._NewIAMResolver(cfg), + EndpointResolverV2: c._NewIAMResolverV2(c.c), + HTTPSignerV4: sig, + Region: cfg.Region, + Retryer: ret, + HTTPClient: httpClient, }) return iam, nil @@ -161,12 +163,14 @@ func (c *client) _NewClientS3(ctx context.Context, httpClient *http.Client) (*sd EndpointOptions: sdksss.EndpointResolverOptions{ DisableHTTPS: !c.c.IsHTTPs(), }, - EndpointResolver: c._NewS3Resolver(cfg), - HTTPSignerV4: sig, - Region: cfg.Region, - Retryer: ret, - HTTPClient: httpClient, - UsePathStyle: c.p, + BaseEndpoint: sdkaws.String(c.c.GetEndpoint().String()), + EndpointResolver: c._NewS3Resolver(cfg), + EndpointResolverV2: c._NewS3ResolverV2(c.c), + HTTPSignerV4: sig, + Region: cfg.Region, + Retryer: ret, + HTTPClient: httpClient, + UsePathStyle: c.p, }) return sss, nil diff --git a/aws/multipart/copy.go b/aws/multipart/copy.go index 9e4c34b1..4a2e44d1 100644 --- a/aws/multipart/copy.go +++ b/aws/multipart/copy.go @@ -65,7 +65,7 @@ func (m *mpu) Copy(fromBucket, fromObject, fromVersionId string) error { Bucket: sdkaws.String(bck), CopySource: sdkaws.String(src), Key: sdkaws.String(obj), - PartNumber: m.Counter() + 1, + PartNumber: sdkaws.Int32(m.Counter() + 1), UploadId: sdkaws.String(mid), CopySourceRange: sdkaws.String("bytes=" + p), RequestPayer: sdktyp.RequestPayerRequester, @@ -121,7 +121,9 @@ func (m *mpu) getCopyPart(fromBucket, fromObject, fromVersionId string) []string return res } else if hdo == nil || hdo.ETag == nil || len(*hdo.ETag) < 1 { return res - } else if size := hdo.ContentLength; size < 1 { + } else if s := hdo.ContentLength; s == nil { + return res + } else if size := *s; size < 1 { return res } else { var i int64 = 0 diff --git a/aws/multipart/part.go b/aws/multipart/part.go index 320cacd4..04a0547f 100644 --- a/aws/multipart/part.go +++ b/aws/multipart/part.go @@ -98,7 +98,7 @@ func (m *mpu) RegisterPart(etag string) { m.n++ m.l = append(m.l, sdktyp.CompletedPart{ ETag: sdkaws.String(strings.Replace(etag, "\"", "", -1)), - PartNumber: m.n, + PartNumber: sdkaws.Int32(m.n), }) } @@ -154,8 +154,8 @@ func (m *mpu) AddPart(r io.Reader) (n int64, e error) { Bucket: sdkaws.String(bck), Key: sdkaws.String(obj), UploadId: sdkaws.String(mid), - PartNumber: m.Counter() + 1, - ContentLength: n, + PartNumber: sdkaws.Int32(m.Counter() + 1), + ContentLength: sdkaws.Int64(n), Body: tmp, RequestPayer: sdktyp.RequestPayerRequester, ContentMD5: sdkaws.String(hss), diff --git a/aws/object/lifecycle.go b/aws/object/lifecycle.go index 48c9b28c..637249b1 100644 --- a/aws/object/lifecycle.go +++ b/aws/object/lifecycle.go @@ -75,7 +75,7 @@ func (cli *client) SetRetention(object, version string, bypass bool, until time. } if bypass { - in.BypassGovernanceRetention = true + in.BypassGovernanceRetention = sdkaws.Bool(true) } switch { diff --git a/aws/object/multipart.go b/aws/object/multipart.go index d4a95741..e1f947f4 100644 --- a/aws/object/multipart.go +++ b/aws/object/multipart.go @@ -42,7 +42,7 @@ import ( func (cli *client) MultipartList(keyMarker, markerId string) (uploads []sdktyp.MultipartUpload, nextKeyMarker string, nextIdMarker string, count int64, e error) { in := &sdksss.ListMultipartUploadsInput{ Bucket: sdkaws.String(cli.GetBucketName()), - MaxUploads: 1000, + MaxUploads: sdkaws.Int32(1000), } if keyMarker != "" && markerId != "" { @@ -54,10 +54,17 @@ func (cli *client) MultipartList(keyMarker, markerId string) (uploads []sdktyp.M if err != nil { return nil, "", "", 0, cli.GetError(err) - } else if out.IsTruncated { - return out.Uploads, *out.NextKeyMarker, *out.NextUploadIdMarker, int64(out.MaxUploads), nil + } + + var maxKeys int32 + if out != nil && out.MaxUploads != nil { + maxKeys = *out.MaxUploads + } + + if out != nil && out.IsTruncated != nil && *out.IsTruncated { + return out.Uploads, *out.NextKeyMarker, *out.NextUploadIdMarker, int64(maxKeys), nil } else { - return out.Uploads, "", "", int64(out.MaxUploads), nil + return out.Uploads, "", "", int64(maxKeys), nil } } diff --git a/aws/object/object.go b/aws/object/object.go index dde11fbd..1b8e5fca 100644 --- a/aws/object/object.go +++ b/aws/object/object.go @@ -30,9 +30,8 @@ import ( "mime" "path/filepath" - sdksss "github.com/aws/aws-sdk-go-v2/service/s3" - sdkaws "github.com/aws/aws-sdk-go-v2/aws" + sdksss "github.com/aws/aws-sdk-go-v2/service/s3" sdktps "github.com/aws/aws-sdk-go-v2/service/s3/types" libhlp "github.com/nabbar/golib/aws/helper" ) @@ -58,10 +57,17 @@ func (cli *client) ListPrefix(continuationToken string, prefix string) ([]sdktps if err != nil { return nil, "", 0, cli.GetError(err) - } else if out.IsTruncated { - return out.Contents, *out.NextContinuationToken, int64(out.KeyCount), nil + } + + var maxKeys int32 + if out != nil && out.MaxKeys != nil { + maxKeys = *out.MaxKeys + } + + if out != nil && out.IsTruncated != nil && *out.IsTruncated { + return out.Contents, *out.NextContinuationToken, int64(maxKeys), nil } else { - return out.Contents, "", int64(out.KeyCount), nil + return out.Contents, "", int64(maxKeys), nil } } @@ -104,7 +110,7 @@ func (cli *client) WalkPrefix(prefix string, f WalkFunc) error { } } - if out.IsTruncated { + if out != nil && out.IsTruncated != nil && *out.IsTruncated { t = out.NextContinuationToken } else { return e diff --git a/aws/object/version.go b/aws/object/version.go index 20dfed65..4cb28953 100644 --- a/aws/object/version.go +++ b/aws/object/version.go @@ -38,7 +38,7 @@ import ( func (cli *client) VersionList(prefix, keyMarker, markerId string) (version []sdktps.ObjectVersion, delMarker []sdktps.DeleteMarkerEntry, nextKeyMarker, nextMarkerId string, count int64, err error) { in := sdksss.ListObjectVersionsInput{ Bucket: cli.GetBucketAws(), - MaxKeys: 1000, + MaxKeys: sdkaws.Int32(1000), } if prefix != "" { @@ -54,10 +54,17 @@ func (cli *client) VersionList(prefix, keyMarker, markerId string) (version []sd if e != nil { return nil, nil, "", "", 0, cli.GetError(e) - } else if out.IsTruncated { - return out.Versions, out.DeleteMarkers, *out.NextKeyMarker, *out.NextVersionIdMarker, int64(out.MaxKeys), nil + } + + var maxKeys int32 + if out != nil && out.MaxKeys != nil { + maxKeys = *out.MaxKeys + } + + if out != nil && out.IsTruncated != nil && *out.IsTruncated { + return out.Versions, out.DeleteMarkers, *out.NextKeyMarker, *out.NextVersionIdMarker, int64(maxKeys), nil } else { - return out.Versions, out.DeleteMarkers, "", "", int64(out.MaxKeys), nil + return out.Versions, out.DeleteMarkers, "", "", int64(maxKeys), nil } } @@ -68,7 +75,7 @@ func (cli *client) VersionWalk(fv VersionWalkFunc, fd DelMakWalkFunc) error { func (cli *client) VersionWalkPrefix(prefix string, fv VersionWalkFunc, fd DelMakWalkFunc) error { in := sdksss.ListObjectVersionsInput{ Bucket: cli.GetBucketAws(), - MaxKeys: 1000, + MaxKeys: sdkaws.Int32(1000), } if prefix != "" { @@ -117,7 +124,7 @@ func (cli *client) VersionWalkPrefix(prefix string, fv VersionWalkFunc, fd DelMa } } - if out.IsTruncated { + if out != nil && out.IsTruncated != nil && *out.IsTruncated { km = out.NextKeyMarker mi = out.NextVersionIdMarker } else { @@ -181,8 +188,10 @@ func (cli *client) VersionSize(object, version string) (size int64, err error) { if h, err = cli.VersionHead(object, version); err != nil { return + } else if h != nil && h.ContentLength != nil { + return *h.ContentLength, nil } else { - return h.ContentLength, nil + return 0, nil } } @@ -207,7 +216,7 @@ func (cli *client) VersionDeleteLock(check bool, object, version string, byPassG } if byPassGovernance { - in.BypassGovernanceRetention = true + in.BypassGovernanceRetention = sdkaws.Bool(true) } _, err := cli.s3.DeleteObject(cli.GetContext(), &in) diff --git a/aws/resolver.go b/aws/resolver.go index b5c39e2c..7dc55b5b 100644 --- a/aws/resolver.go +++ b/aws/resolver.go @@ -26,9 +26,13 @@ package aws import ( + "context" + "net/url" + sdkaws "github.com/aws/aws-sdk-go-v2/aws" sdkiam "github.com/aws/aws-sdk-go-v2/service/iam" sdksss "github.com/aws/aws-sdk-go-v2/service/s3" + awsedp "github.com/aws/smithy-go/endpoints" ) type resolverIam struct { @@ -39,6 +43,14 @@ func (r *resolverIam) ResolveEndpoint(region string, options sdkiam.EndpointReso return r.r("iam", region) } +type resolverIamV2 struct { + r func(service, region string) (awsedp.Endpoint, error) +} + +func (r *resolverIamV2) ResolveEndpoint(ctx context.Context, params sdkiam.EndpointParameters) (awsedp.Endpoint, error) { + return r.r("iam", *params.Region) +} + type resolverS3 struct { r func(service, region string) (sdkaws.Endpoint, error) } @@ -47,14 +59,62 @@ func (r *resolverS3) ResolveEndpoint(region string, options sdksss.EndpointResol return r.r("s3", region) } +type resolverS3V2 struct { + r func(service, region string) (awsedp.Endpoint, error) +} + +func (r *resolverS3V2) ResolveEndpoint(ctx context.Context, params sdksss.EndpointParameters) (awsedp.Endpoint, error) { + return r.r("s3", *params.Region) +} + func (c *client) _NewIAMResolver(cfg *sdkaws.Config) sdkiam.EndpointResolver { return &resolverIam{ r: cfg.EndpointResolver.ResolveEndpoint, } } +func (c *client) _NewIAMResolverV2(cfg Config) sdkiam.EndpointResolverV2 { + return &resolverIamV2{ + r: func(service, region string) (awsedp.Endpoint, error) { + edp, err := cfg.ResolveEndpoint(service, region) + if err != nil { + return awsedp.Endpoint{}, err + } + + uri, err := url.Parse(edp.URL) + if err != nil { + return awsedp.Endpoint{}, err + } + + return awsedp.Endpoint{ + URI: *uri, + }, nil + }, + } +} + func (c *client) _NewS3Resolver(cfg *sdkaws.Config) sdksss.EndpointResolver { return &resolverS3{ r: cfg.EndpointResolver.ResolveEndpoint, } } + +func (c *client) _NewS3ResolverV2(cfg Config) sdksss.EndpointResolverV2 { + return &resolverS3V2{ + r: func(service, region string) (awsedp.Endpoint, error) { + edp, err := cfg.ResolveEndpoint(service, region) + if err != nil { + return awsedp.Endpoint{}, err + } + + uri, err := url.Parse(edp.URL) + if err != nil { + return awsedp.Endpoint{}, err + } + + return awsedp.Endpoint{ + URI: *uri, + }, nil + }, + } +} diff --git a/cobra/completion.go b/cobra/completion.go index ff698f7a..66473f04 100644 --- a/cobra/completion.go +++ b/cobra/completion.go @@ -61,7 +61,9 @@ func (c *cobra) AddCommandCompletion() { os.Exit(1) } else if len(args) >= 2 { file = filepath.Clean(args[1]) - c.getLog().CheckError(loglvl.ErrorLevel, loglvl.NilLevel, "create file path", os.MkdirAll(filepath.Dir(file), 0755)) + // #nosec + e := os.MkdirAll(filepath.Dir(file), 0755) + c.getLog().CheckError(loglvl.ErrorLevel, loglvl.NilLevel, "create file path", e) } switch strings.ToLower(args[0]) { diff --git a/cobra/configure.go b/cobra/configure.go index fe972b79..1aab6145 100644 --- a/cobra/configure.go +++ b/cobra/configure.go @@ -110,6 +110,7 @@ func (c *cobra) ConfigureWriteConfig(basename string, defaultConfig func() io.Re cfgFile = strings.TrimRight(cfgFile, ext) + ".json" } + // #nosec fs, err = os.OpenFile(cfgFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) if err != nil { return err diff --git a/config/components/smtp/client.go b/config/components/smtp/client.go index aed87df5..3da0952d 100644 --- a/config/components/smtp/client.go +++ b/config/components/smtp/client.go @@ -147,6 +147,7 @@ func (o *componentSmtp) _GetTLS() libtls.TLSConfig { func (o *componentSmtp) _GetTLSConfig(cfg libtls.Config) *tls.Config { if i, e := cfg.NewFrom(o._GetTLS()); e != nil { + // #nosec return &tls.Config{} } else { return i.TlsConfig("") diff --git a/context/config.go b/context/config.go index 1482c744..1bcf32dc 100644 --- a/context/config.go +++ b/context/config.go @@ -52,6 +52,7 @@ type Config[T comparable] interface { MapManage[T] Context + SetContext(ctx FuncContext) Clone(ctx context.Context) Config[T] Merge(cfg Config[T]) bool Walk(fct FuncWalk[T]) bool @@ -81,6 +82,18 @@ type configContext[T comparable] struct { x FuncContext } +func (c *configContext[T]) SetContext(ctx FuncContext) { + c.n.Lock() + defer c.n.Unlock() + + if ctx == nil { + ctx = context.Background + } + + c.Context = ctx() + c.x = ctx +} + func (c *configContext[T]) Delete(key T) { if c.Err() != nil { c.Clean() diff --git a/database/kvdriver/errors.go b/database/kvdriver/errors.go index 3fc963cb..c674dcf3 100644 --- a/database/kvdriver/errors.go +++ b/database/kvdriver/errors.go @@ -39,6 +39,7 @@ const ( ErrorBadInstance ErrorGetFunction ErrorSetFunction + ErrorDelFunction ErrorListFunction ErrorFunctionParams ) @@ -62,6 +63,8 @@ func getMessage(code liberr.CodeError) (message string) { return "missing get function of " + pkgName case ErrorSetFunction: return "missing set function of " + pkgName + case ErrorDelFunction: + return "missing del function of " + pkgName case ErrorListFunction: return "missing list function of " + pkgName case ErrorFunctionParams: diff --git a/database/kvdriver/interface.go b/database/kvdriver/interface.go index 703a41b6..eefb9a4d 100644 --- a/database/kvdriver/interface.go +++ b/database/kvdriver/interface.go @@ -26,25 +26,33 @@ package kvdriver -type FctWalk[K comparable, M any] func(key K, model M) bool - -type KVDriver[K comparable, M any] interface { - Get(key K, model *M) error - Set(key K, model M) error - List() ([]K, error) - Walk(fct FctWalk[K, M]) error -} +import ( + libkvt "github.com/nabbar/golib/database/kvtypes" +) +type FuncNew[K comparable, M any] func() libkvt.KVDriver[K, M] type FuncGet[K comparable, M any] func(key K) (M, error) type FuncSet[K comparable, M any] func(key K, model M) error +type FuncDel[K comparable] func(key K) error type FuncList[K comparable, M any] func() ([]K, error) -type FuncWalk[K comparable, M any] func(fct FctWalk[K, M]) error - -type Driver[K comparable, M any] struct { - KVDriver[K, M] +type FuncWalk[K comparable, M any] func(fct libkvt.FctWalk[K, M]) error +type drv[K comparable, M any] struct { + FctNew FuncNew[K, M] FctGet FuncGet[K, M] FctSet FuncSet[K, M] + FctDel FuncDel[K] FctList FuncList[K, M] FctWalk FuncWalk[K, M] // optional } + +func New[K comparable, M any](fn FuncNew[K, M], fg FuncGet[K, M], fs FuncSet[K, M], fd FuncDel[K], fl FuncList[K, M], fw FuncWalk[K, M]) libkvt.KVDriver[K, M] { + return &drv[K, M]{ + FctNew: fn, + FctGet: fg, + FctSet: fs, + FctDel: fd, + FctList: fl, + FctWalk: fw, + } +} diff --git a/database/kvdriver/model.go b/database/kvdriver/model.go index af5918f6..cd2536c7 100644 --- a/database/kvdriver/model.go +++ b/database/kvdriver/model.go @@ -26,7 +26,26 @@ package kvdriver -func (o *Driver[K, M]) Get(key K, model *M) error { +import ( + libkvt "github.com/nabbar/golib/database/kvtypes" +) + +func (o *drv[K, M]) New() libkvt.KVDriver[K, M] { + if o.FctNew != nil { + return o.FctNew() + } + + return &drv[K, M]{ + FctNew: o.FctNew, + FctGet: o.FctGet, + FctSet: o.FctSet, + FctDel: o.FctDel, + FctList: o.FctList, + FctWalk: o.FctWalk, + } +} + +func (o *drv[K, M]) Get(key K, model *M) error { if o == nil { return ErrorBadInstance.Error(nil) } else if o.FctGet == nil { @@ -38,7 +57,7 @@ func (o *Driver[K, M]) Get(key K, model *M) error { } } -func (o *Driver[K, M]) Set(key K, model M) error { +func (o *drv[K, M]) Set(key K, model M) error { if o == nil { return ErrorBadInstance.Error(nil) } else if o.FctSet == nil { @@ -48,7 +67,17 @@ func (o *Driver[K, M]) Set(key K, model M) error { } } -func (o *Driver[K, M]) List() ([]K, error) { +func (o *drv[K, M]) Del(key K) error { + if o == nil { + return ErrorBadInstance.Error(nil) + } else if o.FctDel == nil { + return ErrorSetFunction.Error(nil) + } else { + return o.FctDel(key) + } +} + +func (o *drv[K, M]) List() ([]K, error) { if o == nil { return nil, ErrorBadInstance.Error(nil) } else if o.FctList == nil { @@ -58,7 +87,7 @@ func (o *Driver[K, M]) List() ([]K, error) { } } -func (o *Driver[K, M]) Walk(fct FctWalk[K, M]) error { +func (o *drv[K, M]) Walk(fct libkvt.FctWalk[K, M]) error { if o == nil { return ErrorBadInstance.Error(nil) } else if fct == nil { @@ -70,7 +99,7 @@ func (o *Driver[K, M]) Walk(fct FctWalk[K, M]) error { } } -func (o *Driver[K, M]) fakeWalk(fct FctWalk[K, M]) error { +func (o *drv[K, M]) fakeWalk(fct libkvt.FctWalk[K, M]) error { if l, e := o.List(); e != nil { return e } else { diff --git a/database/kvitem/errors.go b/database/kvitem/errors.go index 686ef923..2b717ef5 100644 --- a/database/kvitem/errors.go +++ b/database/kvitem/errors.go @@ -38,6 +38,7 @@ const ( ErrorParamEmpty liberr.CodeError = iota + liberr.MinPkgDatabaseKVItm ErrorLoadFunction ErrorStoreFunction + ErrorRemoveFunction ) func init() { @@ -57,6 +58,8 @@ func getMessage(code liberr.CodeError) (message string) { return "missing load function of " + pkgName case ErrorStoreFunction: return "missing store function of " + pkgName + case ErrorRemoveFunction: + return "missing remove function of " + pkgName } return liberr.NullMessage diff --git a/database/kvitem/interface.go b/database/kvitem/interface.go index 1dec9c34..e9245594 100644 --- a/database/kvitem/interface.go +++ b/database/kvitem/interface.go @@ -26,40 +26,21 @@ package kvitem -import "sync/atomic" +import ( + "sync/atomic" -type FuncLoad[K comparable, M any] func(key K, model *M) error -type FuncStore[K comparable, M any] func(key K, model M) error - -type KVItem[K comparable, M any] interface { - Set(model M) - Get() M - - Load() error - Store(force bool) error - Clean() - - HasChange() bool - - RegisterFctLoad(fct FuncLoad[K, M]) - RegisterFctStore(fct FuncStore[K, M]) -} - -func New[K comparable, M any](key K) KVItem[K, M] { - var ( - ml = new(atomic.Value) - mw = new(atomic.Value) - ) - - ml.Store(nil) - mw.Store(nil) + libkvt "github.com/nabbar/golib/database/kvtypes" +) +func New[K comparable, M any](drv libkvt.KVDriver[K, M], key K) libkvt.KVItem[K, M] { return &itm[K, M]{ k: key, - ml: ml, - ms: mw, - fl: nil, - fs: nil, + d: drv, + ml: new(atomic.Value), + ms: new(atomic.Value), + fl: new(atomic.Value), + fs: new(atomic.Value), + fr: new(atomic.Value), } } diff --git a/database/kvitem/model.go b/database/kvitem/model.go index 4a8b3f9f..b1dad189 100644 --- a/database/kvitem/model.go +++ b/database/kvitem/model.go @@ -29,9 +29,12 @@ package kvitem import ( "reflect" "sync/atomic" + + libkvt "github.com/nabbar/golib/database/kvtypes" ) type itm[K comparable, M any] struct { + d libkvt.KVDriver[K, M] k K // key ml *atomic.Value // model read @@ -39,165 +42,145 @@ type itm[K comparable, M any] struct { fl *atomic.Value fs *atomic.Value + fr *atomic.Value } -func (o *itm[K, M]) RegisterFctLoad(fct FuncLoad[K, M]) { - o.fl.Store(fct) -} - -func (o *itm[K, M]) getFctLoad() FuncLoad[K, M] { +func (o *itm[K, M]) getDriver() libkvt.KVDriver[K, M] { if o == nil { return nil } - i := o.fs.Load() - if i == nil { - return nil - } else if f, k := i.(FuncLoad[K, M]); !k { - return nil - } else { - return f - } + return o.d } -func (o *itm[K, M]) RegisterFctStore(fct FuncStore[K, M]) { - o.fs.Store(fct) +func (o *itm[K, M]) Set(model M) { + var val = reflect.ValueOf(model) + o.ms.Store(val.Interface()) } -func (o *itm[K, M]) getFctStore() FuncStore[K, M] { - if o == nil { - return nil +func (o *itm[K, M]) setModelLoad(mod M) { + var val = reflect.ValueOf(mod) + o.ml.Store(val.Interface()) +} + +func (o *itm[K, M]) getModelLoad() M { + var mod M + if i := o.ml.Load(); i == nil { + return mod + } else if v, k := i.(M); !k { + return mod + } else { + return v } +} - i := o.fs.Load() - if i == nil { - return nil - } else if f, k := i.(FuncStore[K, M]); !k { - return nil +func (o *itm[K, M]) getModelStore() M { + var mod M + if i := o.ms.Load(); i == nil { + return mod + } else if v, k := i.(M); !k { + return mod } else { - return f + return v } } -func (o *itm[K, M]) Set(model M) { +func (o *itm[K, M]) Key() K { if o == nil { - return + var k K + return k } - m := o.ml.Load() - - // model not loaded, so store new model - if m == nil { - o.ms.Store(model) - // model loaded and new model given not same, so store new model - } else if !reflect.DeepEqual(m.(M), model) { - o.ms.Store(model) - // model loaded and given model are same, so don't store new model - } else { - o.ms.Store(nil) - } + return o.k } func (o *itm[K, M]) Get() M { + var ( + tmp M + mod M + ) + if o == nil { - return *(new(M)) + return mod } - // update exist so latest fresh value - m := o.ms.Load() - if m != nil { - if v, k := m.(M); k { - return v - } - } + mod = o.getModelStore() - // load model exist so return last model load - m = o.ml.Load() - if m != nil { - if v, k := m.(M); k { - return v - } + if reflect.DeepEqual(mod, tmp) { + mod = o.getModelLoad() } // nothing load, so return new instance - return *(new(M)) + return mod } func (o *itm[K, M]) Load() error { - var fct FuncLoad[K, M] + var ( + mod M + drv = o.getDriver() + ) - if fct = o.getFctLoad(); fct == nil { + if drv == nil { return ErrorLoadFunction.Error(nil) } - m := *(new(M)) - e := fct(o.k, &m) - - if e == nil { - o.ml.Store(m) + if e := drv.Get(o.k, &mod); e == nil { + o.setModelLoad(mod) + } else { + return e } - return e + return nil } func (o *itm[K, M]) Store(force bool) error { - var fct FuncStore[K, M] + var drv = o.getDriver() - if fct = o.getFctStore(); fct == nil { + if drv == nil { return ErrorStoreFunction.Error(nil) } - m := o.ms.Load() - if m != nil { - return fct(o.k, m.(M)) - } else if !force { - return nil + var ( + lod M + str M + ) + + _ = o.Load() + + str = o.getModelStore() + if reflect.DeepEqual(lod, str) { + str = o.getModelLoad() } - // no update, but force store, so use load model - m = o.ml.Load() - if m != nil { - return fct(o.k, m.(M)) + lod = o.getModelLoad() + if !reflect.DeepEqual(lod, str) { + return drv.Set(o.k, str) + } else if force { + return drv.Set(o.k, lod) } - // no update and no load, but force store, so use new instance of model - m = *(new(M)) - return fct(o.k, m.(M)) + return nil +} + +func (o *itm[K, M]) Remove() error { + drv := o.getDriver() + + if drv == nil { + return ErrorStoreFunction.Error(nil) + } + + return drv.Del(o.k) } func (o *itm[K, M]) Clean() { - o.ml.Store(nil) - o.ms.Store(nil) + var tmp M + o.setModelLoad(tmp) + o.Set(tmp) } func (o *itm[K, M]) HasChange() bool { - r := o.ml.Load() - w := o.ms.Load() - - if r == nil && w == nil { - // not loaded and not store, so no change - return false - } else if r == nil { - // not loaded but store is set, so has been updated - return true - } else if w == nil { - // loaded and not store, so no change - return false - } - - mr, kr := r.(M) - mw, kw := w.(M) - - if !kr && !kw { - // no valid model, so no change - return false - } else if !kr { - // not valid model for load, but valid for store, so has been updated - return true - } else if !kw { - // valid model for load, but not valid for store, so like no change - return false - } + r := o.getModelLoad() + w := o.getModelStore() - return !reflect.DeepEqual(mr, mw) + return !reflect.DeepEqual(r, w) } diff --git a/database/kvmap/errors.go b/database/kvmap/errors.go index f8dd0843..b36aeb66 100644 --- a/database/kvmap/errors.go +++ b/database/kvmap/errors.go @@ -39,6 +39,7 @@ const ( ErrorBadInstance ErrorGetFunction ErrorSetFunction + ErrorDelFunction ErrorListFunction ErrorFunctionParams ) @@ -62,6 +63,8 @@ func getMessage(code liberr.CodeError) (message string) { return "missing get function of " + pkgName case ErrorSetFunction: return "missing set function of " + pkgName + case ErrorDelFunction: + return "missing del function of " + pkgName case ErrorListFunction: return "missing list function of " + pkgName case ErrorFunctionParams: diff --git a/database/kvmap/interface.go b/database/kvmap/interface.go index f26b3f0e..27bb65ee 100644 --- a/database/kvmap/interface.go +++ b/database/kvmap/interface.go @@ -27,17 +27,29 @@ package kvmap import ( - libkvd "github.com/nabbar/golib/database/kvdriver" + libkvt "github.com/nabbar/golib/database/kvtypes" ) +type FuncNew[K comparable, M any] func() libkvt.KVDriver[K, M] type FuncGet[K comparable, MK comparable] func(key K) (map[MK]any, error) type FuncSet[K comparable, MK comparable] func(key K, model map[MK]any) error +type FuncDel[K comparable] func(key K) error type FuncList[K comparable, MK comparable] func() ([]K, error) -type Driver[K comparable, MK comparable, M any] struct { - libkvd.KVDriver[K, M] - +type drv[K comparable, MK comparable, M any] struct { + FctNew FuncNew[K, M] FctGet FuncGet[K, MK] FctSet FuncSet[K, MK] + FctDel FuncDel[K] FctList FuncList[K, MK] } + +func New[K comparable, MK comparable, M any](fn FuncNew[K, M], fg FuncGet[K, MK], fs FuncSet[K, MK], fd FuncDel[K], fl FuncList[K, MK]) libkvt.KVDriver[K, M] { + return &drv[K, MK, M]{ + FctNew: fn, + FctGet: fg, + FctSet: fs, + FctDel: fd, + FctList: fl, + } +} diff --git a/database/kvmap/model.go b/database/kvmap/model.go index e9dcf04e..52c53c18 100644 --- a/database/kvmap/model.go +++ b/database/kvmap/model.go @@ -29,10 +29,10 @@ package kvmap import ( "encoding/json" - libkvd "github.com/nabbar/golib/database/kvdriver" + libkvt "github.com/nabbar/golib/database/kvtypes" ) -func (o *Driver[K, MK, M]) serialize(model *M, modelMap *map[MK]any) error { +func (o *drv[K, MK, M]) serialize(model *M, modelMap *map[MK]any) error { if p, e := json.Marshal(model); e != nil { return e } else { @@ -40,7 +40,7 @@ func (o *Driver[K, MK, M]) serialize(model *M, modelMap *map[MK]any) error { } } -func (o *Driver[K, MK, M]) unSerialize(modelMap *map[MK]any, model *M) error { +func (o *drv[K, MK, M]) unSerialize(modelMap *map[MK]any, model *M) error { if p, e := json.Marshal(modelMap); e != nil { return e } else { @@ -48,7 +48,16 @@ func (o *Driver[K, MK, M]) unSerialize(modelMap *map[MK]any, model *M) error { } } -func (o *Driver[K, MK, M]) Get(key K, model *M) error { +func (o *drv[K, MK, M]) New() libkvt.KVDriver[K, M] { + return &drv[K, MK, M]{ + FctGet: o.FctGet, + FctSet: o.FctSet, + FctDel: o.FctDel, + FctList: o.FctList, + } +} + +func (o *drv[K, MK, M]) Get(key K, model *M) error { if o == nil { return ErrorBadInstance.Error(nil) } else if o.FctGet == nil { @@ -60,7 +69,7 @@ func (o *Driver[K, MK, M]) Get(key K, model *M) error { } } -func (o *Driver[K, MK, M]) Set(key K, model M) error { +func (o *drv[K, MK, M]) Set(key K, model M) error { var m = make(map[MK]any) if o == nil { @@ -74,7 +83,17 @@ func (o *Driver[K, MK, M]) Set(key K, model M) error { } } -func (o *Driver[K, MK, M]) List() ([]K, error) { +func (o *drv[K, MK, M]) Del(key K) error { + if o == nil { + return ErrorBadInstance.Error(nil) + } else if o.FctDel == nil { + return ErrorDelFunction.Error(nil) + } else { + return o.FctDel(key) + } +} + +func (o *drv[K, MK, M]) List() ([]K, error) { if o == nil { return nil, ErrorBadInstance.Error(nil) } else if o.FctList == nil { @@ -84,7 +103,7 @@ func (o *Driver[K, MK, M]) List() ([]K, error) { } } -func (o *Driver[K, MK, M]) Walk(fct libkvd.FctWalk[K, M]) error { +func (o *drv[K, MK, M]) Walk(fct libkvt.FctWalk[K, M]) error { if o == nil { return ErrorBadInstance.Error(nil) } else if fct == nil { diff --git a/database/kvtable/interface.go b/database/kvtable/interface.go index 6fc6a466..02c4795e 100644 --- a/database/kvtable/interface.go +++ b/database/kvtable/interface.go @@ -27,25 +27,11 @@ package kvtable import ( - "sync/atomic" - - libkvd "github.com/nabbar/golib/database/kvdriver" - libkvi "github.com/nabbar/golib/database/kvitem" + libkvt "github.com/nabbar/golib/database/kvtypes" ) -type FuncWalk[K comparable, M any] func(kv libkvi.KVItem[K, M]) bool - -type KVTable[K comparable, M any] interface { - Get(key K) (libkvi.KVItem[K, M], error) - List() ([]libkvi.KVItem[K, M], error) - Walk(fct FuncWalk[K, M]) error -} - -func New[K comparable, M any](drv libkvd.KVDriver[K, M]) KVTable[K, M] { - d := new(atomic.Value) - d.Store(drv) - +func New[K comparable, M any](drv libkvt.KVDriver[K, M]) libkvt.KVTable[K, M] { return &tbl[K, M]{ - d: d, + d: drv, } } diff --git a/database/kvtable/model.go b/database/kvtable/model.go index 3a6db8fb..5cf0ca30 100644 --- a/database/kvtable/model.go +++ b/database/kvtable/model.go @@ -27,66 +27,58 @@ package kvtable import ( - "sync/atomic" - - libkvd "github.com/nabbar/golib/database/kvdriver" libkvs "github.com/nabbar/golib/database/kvitem" + libkvt "github.com/nabbar/golib/database/kvtypes" ) type tbl[K comparable, M any] struct { - d *atomic.Value + d libkvt.KVDriver[K, M] } -func (o *tbl[K, M]) getDriver() libkvd.KVDriver[K, M] { +func (o *tbl[K, M]) getDriver() libkvt.KVDriver[K, M] { if o == nil { return nil } - i := o.d.Load() - if i == nil { - return nil - } else if d, k := i.(libkvd.KVDriver[K, M]); !k { + if o.d == nil { return nil } else { - return d + return o.d } } -func (o *tbl[K, M]) Get(key K) (libkvs.KVItem[K, M], error) { - var kvs = libkvs.New[K, M](key) - +func (o *tbl[K, M]) Get(key K) (libkvt.KVItem[K, M], error) { if drv := o.getDriver(); drv == nil { return nil, ErrorBadDriver.Error(nil) } else { - kvs.RegisterFctLoad(drv.Get) - kvs.RegisterFctStore(drv.Set) + var kvi = libkvs.New[K, M](drv.New(), key) + e := kvi.Load() + return kvi, e } +} - return kvs, kvs.Load() +func (o *tbl[K, M]) Del(key K) error { + if drv := o.getDriver(); drv == nil { + return ErrorBadDriver.Error(nil) + } else { + return drv.Del(key) + } } -func (o *tbl[K, M]) Walk(fct FuncWalk[K, M]) error { +func (o *tbl[K, M]) Walk(fct libkvt.FuncWalk[K, M]) error { if drv := o.getDriver(); drv == nil { return ErrorBadDriver.Error(nil) } else { return drv.Walk(func(key K, model M) bool { - var kvs = libkvs.New[K, M](key) - - kvs.RegisterFctStore(drv.Set) - kvs.RegisterFctLoad(func(k K, m *M) error { - *m = model - return nil - }) - _ = kvs.Load() - kvs.RegisterFctLoad(drv.Get) - - return fct(kvs) + kvi := libkvs.New[K, M](drv.New(), key) + kvi.Set(model) + return fct(kvi) }) } } -func (o *tbl[K, M]) List() ([]libkvs.KVItem[K, M], error) { - var res = make([]libkvs.KVItem[K, M], 0) +func (o *tbl[K, M]) List() ([]libkvt.KVItem[K, M], error) { + var res = make([]libkvt.KVItem[K, M], 0) if drv := o.getDriver(); drv == nil { return nil, ErrorBadDriver.Error(nil) @@ -94,12 +86,7 @@ func (o *tbl[K, M]) List() ([]libkvs.KVItem[K, M], error) { return nil, e } else { for _, k := range l { - var kvs = libkvs.New[K, M](k) - - kvs.RegisterFctLoad(drv.Get) - kvs.RegisterFctStore(drv.Set) - - res = append(res, kvs) + res = append(res, libkvs.New[K, M](drv.New(), k)) } return res, nil diff --git a/database/kvtypes/driver.go b/database/kvtypes/driver.go new file mode 100644 index 00000000..9affd32f --- /dev/null +++ b/database/kvtypes/driver.go @@ -0,0 +1,38 @@ +/* + * MIT License + * + * Copyright (c) 2023 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package kvtypes + +type FctWalk[K comparable, M any] func(key K, model M) bool + +type KVDriver[K comparable, M any] interface { + New() KVDriver[K, M] + Get(key K, model *M) error + Set(key K, model M) error + Del(key K) error + List() ([]K, error) + Walk(fct FctWalk[K, M]) error +} diff --git a/database/kvtypes/item.go b/database/kvtypes/item.go new file mode 100644 index 00000000..37727457 --- /dev/null +++ b/database/kvtypes/item.go @@ -0,0 +1,40 @@ +/* + * MIT License + * + * Copyright (c) 2023 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package kvtypes + +type KVItem[K comparable, M any] interface { + Set(model M) + Get() M + Key() K + + Load() error + Store(force bool) error + Remove() error + Clean() + + HasChange() bool +} diff --git a/database/kvtypes/table.go b/database/kvtypes/table.go new file mode 100644 index 00000000..0b2c53db --- /dev/null +++ b/database/kvtypes/table.go @@ -0,0 +1,36 @@ +/* + * MIT License + * + * Copyright (c) 2023 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + */ + +package kvtypes + +type FuncWalk[K comparable, M any] func(kv KVItem[K, M]) bool + +type KVTable[K comparable, M any] interface { + Get(key K) (KVItem[K, M], error) + Del(key K) error + List() ([]KVItem[K, M], error) + Walk(fct FuncWalk[K, M]) error +} diff --git a/duration/encode.go b/duration/encode.go new file mode 100644 index 00000000..8083dafa --- /dev/null +++ b/duration/encode.go @@ -0,0 +1,87 @@ +/*********************************************************************************************************************** + * + * MIT License + * + * Copyright (c) 2022 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + **********************************************************************************************************************/ + +package duration + +import ( + "fmt" + + "gopkg.in/yaml.v3" +) + +func (d Duration) MarshalJSON() ([]byte, error) { + t := d.String() + b := make([]byte, 0, len(t)+2) + b = append(b, '"') + b = append(b, []byte(t)...) + b = append(b, '"') + return b, nil +} + +func (d *Duration) UnmarshalJSON(bytes []byte) error { + return d.unmarshall(bytes) +} + +func (d Duration) MarshalYAML() (interface{}, error) { + return d.MarshalJSON() +} + +func (d *Duration) UnmarshalYAML(value *yaml.Node) error { + return d.unmarshall([]byte(value.Value)) +} + +func (d Duration) MarshalTOML() ([]byte, error) { + return d.MarshalJSON() +} + +func (d *Duration) UnmarshalTOML(i interface{}) error { + if b, k := i.([]byte); k { + return d.unmarshall(b) + } + + if b, k := i.(string); k { + return d.parseString(b) + } + + return fmt.Errorf("size: value not in valid format") +} + +func (d Duration) MarshalText() ([]byte, error) { + return []byte(d.String()), nil +} + +func (d *Duration) UnmarshalText(bytes []byte) error { + return d.unmarshall(bytes) +} + +func (d Duration) MarshalCBOR() ([]byte, error) { + return []byte(d.String()), nil +} + +func (d *Duration) UnmarshalCBOR(bytes []byte) error { + return d.unmarshall(bytes) +} diff --git a/duration/format.go b/duration/format.go new file mode 100644 index 00000000..75a7b33d --- /dev/null +++ b/duration/format.go @@ -0,0 +1,51 @@ +/*********************************************************************************************************************** + * + * MIT License + * + * Copyright (c) 2022 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + **********************************************************************************************************************/ + +package duration + +import ( + "math" + "time" +) + +func (d Duration) Time() time.Duration { + return time.Duration(d) +} + +func (d Duration) String() string { + return time.Duration(d).String() +} + +func (d Duration) Days() int64 { + t := math.Floor(d.Time().Hours()) + + if t > math.MaxInt64 { + return math.MaxInt64 + } + + return int64(t) +} diff --git a/duration/interface.go b/duration/interface.go new file mode 100644 index 00000000..d6c0f72a --- /dev/null +++ b/duration/interface.go @@ -0,0 +1,62 @@ +/*********************************************************************************************************************** + * + * MIT License + * + * Copyright (c) 2022 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + **********************************************************************************************************************/ + +package duration + +import ( + "time" +) + +type Duration time.Duration + +func Parse(s string) (Duration, error) { + return parseString(s) +} + +func ParseByte(p []byte) (Duration, error) { + return parseString(string(p)) +} + +func Seconds(i int64) Duration { + return Duration(time.Duration(i) * time.Second) +} + +func Minutes(i int64) Duration { + return Duration(time.Duration(i) * time.Minute) +} + +func Hours(i int64) Duration { + return Duration(time.Duration(i) * time.Hour) +} + +func Days(i int64) Duration { + return Duration(time.Duration(i) * time.Hour * 24) +} + +func ParseDuration(d time.Duration) Duration { + return Duration(d) +} diff --git a/duration/model.go b/duration/model.go new file mode 100644 index 00000000..83c7df36 --- /dev/null +++ b/duration/model.go @@ -0,0 +1,59 @@ +/*********************************************************************************************************************** + * + * MIT License + * + * Copyright (c) 2022 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + **********************************************************************************************************************/ + +package duration + +import ( + "reflect" + + libmap "github.com/mitchellh/mapstructure" +) + +func ViperDecoderHook() libmap.DecodeHookFuncType { + return func(from reflect.Type, to reflect.Type, data interface{}) (interface{}, error) { + var ( + z = Duration(0) + t string + k bool + ) + + // Check if the data type matches the expected one + if from.Kind() != reflect.String { + return data, nil + } else if t, k = data.(string); !k { + return data, nil + } + + // Check if the target type matches the expected one + if to != reflect.TypeOf(z) { + return data, nil + } + + // Format/decode/parse the data and return the new value + return parseString(t) + } +} diff --git a/duration/parse.go b/duration/parse.go new file mode 100644 index 00000000..f9054f00 --- /dev/null +++ b/duration/parse.go @@ -0,0 +1,61 @@ +/*********************************************************************************************************************** + * + * MIT License + * + * Copyright (c) 2022 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + **********************************************************************************************************************/ + +package duration + +import ( + "strings" + "time" +) + +func parseString(s string) (Duration, error) { + s = strings.Replace(s, "\"", "", -1) + s = strings.Replace(s, "'", "", -1) + if v, e := time.ParseDuration(s); e != nil { + return 0, e + } else { + return Duration(v), nil + } +} + +func (d *Duration) parseString(s string) error { + if v, e := parseString(s); e != nil { + return e + } else { + *d = v + return nil + } +} + +func (d *Duration) unmarshall(val []byte) error { + if tmp, err := ParseByte(val); err != nil { + return err + } else { + *d = tmp + return nil + } +} diff --git a/file/perm/encode.go b/file/perm/encode.go new file mode 100644 index 00000000..aec91295 --- /dev/null +++ b/file/perm/encode.go @@ -0,0 +1,87 @@ +/*********************************************************************************************************************** + * + * MIT License + * + * Copyright (c) 2022 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + **********************************************************************************************************************/ + +package perm + +import ( + "fmt" + + "gopkg.in/yaml.v3" +) + +func (p Perm) MarshalJSON() ([]byte, error) { + t := p.String() + b := make([]byte, 0, len(t)+2) + b = append(b, '"') + b = append(b, []byte(t)...) + b = append(b, '"') + return b, nil +} + +func (p *Perm) UnmarshalJSON(bytes []byte) error { + return p.unmarshall(bytes) +} + +func (p Perm) MarshalYAML() (interface{}, error) { + return p.MarshalJSON() +} + +func (p *Perm) UnmarshalYAML(value *yaml.Node) error { + return p.unmarshall([]byte(value.Value)) +} + +func (p Perm) MarshalTOML() ([]byte, error) { + return p.MarshalJSON() +} + +func (p *Perm) UnmarshalTOML(i interface{}) error { + if b, k := i.([]byte); k { + return p.unmarshall(b) + } + + if b, k := i.(string); k { + return p.parseString(b) + } + + return fmt.Errorf("size: value not in valid format") +} + +func (p Perm) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *Perm) UnmarshalText(bytes []byte) error { + return p.unmarshall(bytes) +} + +func (p Perm) MarshalCBOR() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *Perm) UnmarshalCBOR(bytes []byte) error { + return p.unmarshall(bytes) +} diff --git a/file/perm/format.go b/file/perm/format.go new file mode 100644 index 00000000..9e556bd1 --- /dev/null +++ b/file/perm/format.go @@ -0,0 +1,91 @@ +/*********************************************************************************************************************** + * + * MIT License + * + * Copyright (c) 2022 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + **********************************************************************************************************************/ + +package perm + +import ( + "fmt" + "math" + "os" +) + +func (p Perm) FileMode() os.FileMode { + return os.FileMode(p.Uint64()) +} + +func (p Perm) String() string { + return fmt.Sprintf("%#o", p.Uint64()) +} + +func (p Perm) Int64() int64 { + if uint64(p) > math.MaxInt64 { + // overflow + return math.MaxInt64 + } + + return int64(p) +} + +func (p Perm) Int32() int32 { + if uint64(p) > math.MaxInt32 { + // overflow + return math.MaxInt32 + } + + return int32(p) +} + +func (p Perm) Int() int { + if uint64(p) > math.MaxInt { + // overflow + return math.MaxInt + } + + return int(p) +} + +func (p Perm) Uint64() uint64 { + return uint64(p) +} + +func (p Perm) Uint32() uint32 { + if uint64(p) > math.MaxUint32 { + // overflow + return math.MaxUint32 + } + + return uint32(p) +} + +func (p Perm) Uint() uint { + if uint64(p) > math.MaxUint { + // overflow + return math.MaxUint + } + + return uint(p) +} diff --git a/file/perm/interface.go b/file/perm/interface.go new file mode 100644 index 00000000..581fbcaf --- /dev/null +++ b/file/perm/interface.go @@ -0,0 +1,51 @@ +/*********************************************************************************************************************** + * + * MIT License + * + * Copyright (c) 2022 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + **********************************************************************************************************************/ + +package perm + +import ( + "os" + "strconv" +) + +type Perm os.FileMode + +func Parse(s string) (Perm, error) { + return parseString(s) +} + +func ParseInt(i int) (Perm, error) { + return parseString(strconv.FormatInt(int64(i), 8)) +} + +func ParseInt64(i int64) (Perm, error) { + return parseString(strconv.FormatInt(i, 8)) +} + +func ParseByte(p []byte) (Perm, error) { + return parseString(string(p)) +} diff --git a/file/perm/model.go b/file/perm/model.go new file mode 100644 index 00000000..847d3d95 --- /dev/null +++ b/file/perm/model.go @@ -0,0 +1,59 @@ +/*********************************************************************************************************************** + * + * MIT License + * + * Copyright (c) 2022 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + **********************************************************************************************************************/ + +package perm + +import ( + "reflect" + + libmap "github.com/mitchellh/mapstructure" +) + +func ViperDecoderHook() libmap.DecodeHookFuncType { + return func(from reflect.Type, to reflect.Type, data interface{}) (interface{}, error) { + var ( + z = Perm(0) + t string + k bool + ) + + // Check if the data type matches the expected one + if from.Kind() != reflect.String { + return data, nil + } else if t, k = data.(string); !k { + return data, nil + } + + // Check if the target type matches the expected one + if to != reflect.TypeOf(z) { + return data, nil + } + + // Format/decode/parse the data and return the new value + return parseString(t) + } +} diff --git a/file/perm/parse.go b/file/perm/parse.go new file mode 100644 index 00000000..6efaf215 --- /dev/null +++ b/file/perm/parse.go @@ -0,0 +1,66 @@ +/*********************************************************************************************************************** + * + * MIT License + * + * Copyright (c) 2022 Nicolas JUHEL + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + **********************************************************************************************************************/ + +package perm + +import ( + "fmt" + "math" + "strconv" + "strings" +) + +func parseString(s string) (Perm, error) { + s = strings.Replace(s, "\"", "", -1) + s = strings.Replace(s, "'", "", -1) + + if v, e := strconv.ParseUint(s, 8, 32); e != nil { + return 0, e + } else if v > math.MaxUint32 { + return Perm(0), fmt.Errorf("invalid permission") + } else { + return Perm(v), nil + } +} + +func (p *Perm) parseString(s string) error { + if v, e := parseString(s); e != nil { + return e + } else { + *p = v + return nil + } +} + +func (p *Perm) unmarshall(val []byte) error { + if tmp, err := ParseByte(val); err != nil { + return err + } else { + *p = tmp + return nil + } +} diff --git a/file/progress/interface.go b/file/progress/interface.go index 2e7edca9..33edf27b 100644 --- a/file/progress/interface.go +++ b/file/progress/interface.go @@ -85,7 +85,10 @@ type Progress interface { } func New(name string, flags int, perm os.FileMode) (Progress, error) { - if f, e := os.OpenFile(name, flags, perm); e != nil { + // #nosec + f, e := os.OpenFile(name, flags, perm) + + if e != nil { return nil, e } else { return &progress{ @@ -99,7 +102,10 @@ func New(name string, flags int, perm os.FileMode) (Progress, error) { } func Unique(basePath, pattern string) (Progress, error) { - if f, e := os.CreateTemp(basePath, pattern); e != nil { + // #nosec + f, e := os.CreateTemp(basePath, pattern) + + if e != nil { return nil, e } else { return &progress{ @@ -113,7 +119,10 @@ func Unique(basePath, pattern string) (Progress, error) { } func Temp(pattern string) (Progress, error) { - if f, e := os.CreateTemp("", pattern); e != nil { + // #nosec + f, e := os.CreateTemp("", pattern) + + if e != nil { return nil, e } else { return &progress{ @@ -127,7 +136,10 @@ func Temp(pattern string) (Progress, error) { } func Open(name string) (Progress, error) { - if f, e := os.Open(name); e != nil { + // #nosec + f, e := os.Open(name) + + if e != nil { return nil, e } else { return &progress{ @@ -141,7 +153,10 @@ func Open(name string) (Progress, error) { } func Create(name string) (Progress, error) { - if f, e := os.Create(name); e != nil { + // #nosec + f, e := os.Create(name) + + if e != nil { return nil, e } else { return &progress{ diff --git a/go.mod b/go.mod index 73bcbee7..fc6a0f60 100644 --- a/go.mod +++ b/go.mod @@ -2,25 +2,26 @@ module github.com/nabbar/golib go 1.21 -toolchain go1.21.1 +toolchain go1.21.3 require ( - github.com/aws/aws-sdk-go-v2 v1.21.2 - github.com/aws/aws-sdk-go-v2/config v1.18.45 - github.com/aws/aws-sdk-go-v2/credentials v1.13.43 - github.com/aws/aws-sdk-go-v2/service/iam v1.22.7 - github.com/aws/aws-sdk-go-v2/service/s3 v1.40.2 - github.com/bits-and-blooms/bitset v1.10.0 + github.com/aws/aws-sdk-go-v2 v1.23.1 + github.com/aws/aws-sdk-go-v2/config v1.25.4 + github.com/aws/aws-sdk-go-v2/credentials v1.16.3 + github.com/aws/aws-sdk-go-v2/service/iam v1.27.3 + github.com/aws/aws-sdk-go-v2/service/s3 v1.43.1 + github.com/aws/smithy-go v1.17.0 + github.com/bits-and-blooms/bitset v1.11.0 github.com/c-bata/go-prompt v0.2.6 - github.com/fatih/color v1.15.0 - github.com/fsnotify/fsnotify v1.6.0 + github.com/fatih/color v1.16.0 + github.com/fsnotify/fsnotify v1.7.0 github.com/fxamacker/cbor/v2 v2.5.0 github.com/gin-gonic/gin v1.9.1 github.com/go-ldap/ldap/v3 v3.4.6 - github.com/go-playground/validator/v10 v10.15.5 + github.com/go-playground/validator/v10 v10.16.0 github.com/google/go-github/v33 v33.0.0 github.com/hashicorp/go-hclog v1.5.0 - github.com/hashicorp/go-retryablehttp v0.7.4 + github.com/hashicorp/go-retryablehttp v0.7.5 github.com/hashicorp/go-uuid v1.0.3 github.com/hashicorp/go-version v1.6.0 github.com/jlaffaye/ftp v0.2.0 @@ -29,34 +30,34 @@ require ( github.com/mattn/go-colorable v0.1.13 github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/mapstructure v1.5.0 - github.com/nats-io/jwt/v2 v2.5.2 - github.com/nats-io/nats-server/v2 v2.10.3 - github.com/nats-io/nats.go v1.30.2 - github.com/nutsdb/nutsdb v0.14.1 - github.com/onsi/ginkgo/v2 v2.13.0 - github.com/onsi/gomega v1.28.0 + github.com/nats-io/jwt/v2 v2.5.3 + github.com/nats-io/nats-server/v2 v2.10.5 + github.com/nats-io/nats.go v1.31.0 + github.com/nutsdb/nutsdb v0.14.3 + github.com/onsi/ginkgo/v2 v2.13.1 + github.com/onsi/gomega v1.30.0 github.com/pelletier/go-toml v1.9.5 github.com/prometheus/client_golang v1.17.0 github.com/shirou/gopsutil v3.21.11+incompatible github.com/sirupsen/logrus v1.9.3 - github.com/spf13/cobra v1.7.0 + github.com/spf13/cobra v1.8.0 github.com/spf13/jwalterweatherman v1.1.0 github.com/spf13/viper v1.17.0 github.com/ugorji/go/codec v1.2.11 github.com/vbauerster/mpb/v8 v8.6.2 - github.com/xanzy/go-gitlab v0.93.1 + github.com/xanzy/go-gitlab v0.94.0 github.com/xhit/go-simple-mail v2.2.2+incompatible github.com/xujiajun/utils v0.0.0-20220904132955-5f7c5b914235 - golang.org/x/exp v0.0.0-20231006140011-7918f672742d - golang.org/x/net v0.17.0 - golang.org/x/oauth2 v0.13.0 - golang.org/x/sync v0.4.0 - golang.org/x/sys v0.13.0 - golang.org/x/term v0.13.0 + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa + golang.org/x/net v0.18.0 + golang.org/x/oauth2 v0.14.0 + golang.org/x/sync v0.5.0 + golang.org/x/sys v0.14.0 + golang.org/x/term v0.14.0 gopkg.in/yaml.v3 v3.0.1 gorm.io/driver/clickhouse v0.5.1 gorm.io/driver/mysql v1.5.2 - gorm.io/driver/postgres v1.5.3 + gorm.io/driver/postgres v1.5.4 gorm.io/driver/sqlite v1.5.4 gorm.io/driver/sqlserver v1.5.2 gorm.io/gorm v1.25.5 @@ -65,7 +66,7 @@ require ( require ( github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/ClickHouse/ch-go v0.58.2 // indirect - github.com/ClickHouse/clickhouse-go/v2 v2.14.3 // indirect + github.com/ClickHouse/clickhouse-go/v2 v2.15.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/sprig v2.22.0+incompatible // indirect @@ -78,38 +79,36 @@ require ( github.com/antlabs/stl v0.0.1 // indirect github.com/antlabs/timer v0.0.11 // indirect github.com/armon/go-metrics v0.4.1 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.14 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.13 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.43 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.37 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.45 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.6 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.15 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.38 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.37 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.6 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.15.2 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.3 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.23.2 // indirect - github.com/aws/smithy-go v1.15.0 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.1 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.5 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.4 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.4 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.17.3 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.20.1 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.25.4 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bwmarrin/snowflake v0.3.0 // indirect github.com/bytedance/sonic v1.10.2 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect - github.com/chenzhuoyu/iasm v0.9.0 // indirect + github.com/chenzhuoyu/iasm v0.9.1 // indirect github.com/cockroachdb/errors v1.11.1 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/pebble v0.0.0-20210331181633-27fc006b8bfb // indirect github.com/cockroachdb/redact v1.1.5 // indirect - github.com/emirpasic/gods v1.18.1 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/getsentry/sentry-go v0.25.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect github.com/go-faster/city v1.0.1 // indirect - github.com/go-faster/errors v0.6.1 // indirect - github.com/go-logr/logr v1.2.4 // indirect + github.com/go-faster/errors v0.7.0 // indirect + github.com/go-logr/logr v1.3.0 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect @@ -125,15 +124,15 @@ require ( github.com/google/btree v1.1.2 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 // indirect - github.com/google/uuid v1.3.1 // indirect - github.com/gorilla/css v1.0.0 // indirect + github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect + github.com/google/uuid v1.4.0 // indirect + github.com/gorilla/css v1.0.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-msgpack v0.5.5 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-sockaddr v1.0.5 // indirect + github.com/hashicorp/go-sockaddr v1.0.6 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/memberlist v0.5.0 // indirect @@ -142,32 +141,33 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect - github.com/jackc/pgx/v5 v5.4.3 // indirect + github.com/jackc/pgx/v5 v5.5.0 // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/juju/ratelimit v1.0.2 // indirect - github.com/klauspost/compress v1.17.1 // indirect - github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/klauspost/compress v1.17.3 // indirect + github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/lni/goutils v1.3.0 // indirect github.com/magiconair/properties v1.8.7 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect - github.com/mattn/go-sqlite3 v1.14.17 // indirect + github.com/mattn/go-sqlite3 v1.14.18 // indirect github.com/mattn/go-tty v0.0.5 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/microsoft/go-mssqldb v1.6.0 // indirect - github.com/miekg/dns v1.1.56 // indirect + github.com/miekg/dns v1.1.57 // indirect github.com/minio/highwayhash v1.0.2 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/nats-io/nkeys v0.4.5 // indirect + github.com/nats-io/nkeys v0.4.6 // indirect github.com/nats-io/nuid v1.0.1 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/paulmach/orb v0.10.0 // indirect @@ -176,7 +176,7 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pkg/term v1.2.0-beta.2 // indirect github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.44.0 // indirect + github.com/prometheus/common v0.45.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect @@ -201,15 +201,15 @@ require ( github.com/x448/float16 v0.8.4 // indirect github.com/xujiajun/mmap-go v1.0.1 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect - go.opentelemetry.io/otel v1.19.0 // indirect - go.opentelemetry.io/otel/trace v1.19.0 // indirect + go.opentelemetry.io/otel v1.21.0 // indirect + go.opentelemetry.io/otel/trace v1.21.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/arch v0.5.0 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/mod v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.14.0 // indirect + golang.org/x/arch v0.6.0 // indirect + golang.org/x/crypto v0.15.0 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.4.0 // indirect + golang.org/x/tools v0.15.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/httpserver/server.go b/httpserver/server.go index b52dc734..ca4dd324 100644 --- a/httpserver/server.go +++ b/httpserver/server.go @@ -93,6 +93,7 @@ func (o *srv) setServer(ctx context.Context) error { var stdlog = o.logger() + // #nosec s := &http.Server{ Addr: bind, Handler: o.HandlerLoadFct(), diff --git a/httpserver/serverOpt.go b/httpserver/serverOpt.go index 930fddee..5e37cfff 100644 --- a/httpserver/serverOpt.go +++ b/httpserver/serverOpt.go @@ -56,6 +56,8 @@ func (o *optServer) initServer(s *http.Server) liberr.Error { if o.ReadHeaderTimeout > 0 { s.ReadHeaderTimeout = o.ReadHeaderTimeout + } else { + s.ReadHeaderTimeout = 30 * time.Second } if o.WriteTimeout > 0 { diff --git a/ioutils/tools.go b/ioutils/tools.go index a8f5c0ee..c9381da4 100644 --- a/ioutils/tools.go +++ b/ioutils/tools.go @@ -57,7 +57,11 @@ func PathCheckCreate(isFile bool, path string, permFile os.FileMode, permDir os. return os.MkdirAll(path, permDir) } else if err = PathCheckCreate(false, filepath.Dir(path), permFile, permDir); err != nil { return err - } else if hf, e := os.Create(path); e != nil { + } + + // #nosec + hf, e := os.Create(path) + if e != nil { return e } else { _ = hf.Close() diff --git a/ldap/ldap.go b/ldap/ldap.go index 15831790..a244a745 100644 --- a/ldap/ldap.go +++ b/ldap/ldap.go @@ -72,6 +72,23 @@ func NewLDAP(ctx context.Context, cnf *Config, attributes []string) (*HelperLDAP }, nil } +func (lc *HelperLDAP) Clone() *HelperLDAP { + var att = make([]string, 0) + copy(att, lc.Attributes) + + return &HelperLDAP{ + Attributes: att, + conn: nil, + config: lc.config.Clone(), + tlsConfig: lc.tlsConfig.Clone(), + tlsMode: lc.tlsMode, + bindDN: lc.bindDN, + bindPass: lc.bindPass, + ctx: lc.ctx, + log: lc.log, + } +} + // SetLogger is used to specify the logger to be used for debug messgae func (lc *HelperLDAP) SetLogger(fct liblog.FuncLog) { lc.log = fct @@ -242,7 +259,7 @@ func (lc *HelperLDAP) tryConnect() (TLSMode, liberr.Error) { defer func() { if l != nil { - l.Close() + _ = l.Close() } }() @@ -306,7 +323,7 @@ func (lc *HelperLDAP) connect() liberr.Error { l, err = lc.dialTLS() if err != nil { if l != nil { - l.Close() + _ = l.Close() } return err } @@ -316,7 +333,7 @@ func (lc *HelperLDAP) connect() liberr.Error { l, err = lc.dial() if err != nil { if l != nil { - l.Close() + _ = l.Close() } return err } @@ -326,7 +343,7 @@ func (lc *HelperLDAP) connect() liberr.Error { err = lc.starttls(l) if err != nil { if l != nil { - l.Close() + _ = l.Close() } return err } @@ -348,7 +365,7 @@ func (lc *HelperLDAP) Check() liberr.Error { if lc.conn == nil { defer func() { if lc.conn != nil { - lc.conn.Close() + _ = lc.conn.Close() lc.conn = nil } }() @@ -369,7 +386,7 @@ func (lc *HelperLDAP) Close() { } if lc.conn != nil { - lc.conn.Close() + _ = lc.conn.Close() lc.conn = nil } } diff --git a/logger/hookfile/interface.go b/logger/hookfile/interface.go index fec3dbf0..239b60fe 100644 --- a/logger/hookfile/interface.go +++ b/logger/hookfile/interface.go @@ -99,7 +99,10 @@ func New(opt logcfg.OptionsFile, format logrus.Formatter) (HookFile, error) { } } - if h, e := os.OpenFile(opt.Filepath, flags, opt.FileMode); e != nil { + // #nosec + h, e := os.OpenFile(opt.Filepath, flags, opt.FileMode) + + if e != nil { return nil, e } else if _, e = h.Seek(0, io.SeekEnd); e != nil { _ = h.Close() diff --git a/logger/hookfile/system.go b/logger/hookfile/system.go index 91469120..5297c96b 100644 --- a/logger/hookfile/system.go +++ b/logger/hookfile/system.go @@ -74,7 +74,10 @@ func (o *hkf) writeBuffer(buf *bytes.Buffer) error { } }() - if h, e = os.OpenFile(p, f, m); e != nil { + // #nosec + h, e = os.OpenFile(p, f, m) + + if e != nil { return e } else if _, e = h.Seek(0, io.SeekEnd); e != nil { return e diff --git a/nats/config.go b/nats/config.go index f7f31e14..1fe32453 100644 --- a/nats/config.go +++ b/nats/config.go @@ -104,6 +104,7 @@ func (c Config) LogConfigJson() liberr.Error { return ErrorConfigInvalidFilePath.Error(e) } + // #nosec f, e := os.OpenFile(c.Logs.LogFile, os.O_APPEND|os.O_WRONLY, permFile) if e != nil { return ErrorConfigInvalidFilePath.Error(e) @@ -1054,6 +1055,7 @@ func (c ConfigWebsocket) makeOpt(defTls libtls.TLSConfig) (natsrv.WebsocketOpts, } } else { cfg.NoTLS = true + // #nosec cfg.TLSConfig = &tls.Config{} cfg.HandshakeTimeout = 0 } @@ -1092,6 +1094,7 @@ func (c ConfigMQTT) makeOpt(defTls libtls.TLSConfig) (natsrv.MQTTOpts, liberr.Er cfg.TLSTimeout = float64(c.TLSTimeout) / float64(time.Second) } } else { + // #nosec cfg.TLSConfig = &tls.Config{} cfg.TLSTimeout = 0 } diff --git a/network/protocol/encode.go b/network/protocol/encode.go index 5d072468..bb626fc2 100644 --- a/network/protocol/encode.go +++ b/network/protocol/encode.go @@ -28,12 +28,19 @@ package protocol import ( + "bytes" "fmt" "gopkg.in/yaml.v3" ) +const ( + dblQuote = "\"" + smpQuote = "'" +) + func (s *NetworkProtocol) unmarshall(val []byte) error { + val = bytes.Trim(bytes.Trim(val, smpQuote), dblQuote) *s = ParseBytes(val) return nil } diff --git a/password/password.go b/password/password.go index 08f1c147..354ffd10 100644 --- a/password/password.go +++ b/password/password.go @@ -26,8 +26,8 @@ package password import ( - "math/rand" - "time" + "crypto/rand" + "math/big" ) const letterBytes = "abcdefghijklmnopqrstuvwxyz,;:!?./*%^$&\"'(-_)=+~#{[|`\\^@]}ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" @@ -41,36 +41,32 @@ const ( loopRandMaxLen = 10 ) -func randStringBytesMaskImprSrc(n int) string { - var src = rand.NewSource(time.Now().UnixNano()) +func randIdx() int { + size := int64(len(letterBytes)) - b := make([]byte, n) - // A src.Int63() generates 63 random bits, enough for letterIdxMax characters! - for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; { - if remain == 0 { - cache, remain = src.Int63(), letterIdxMax - } - if idx := int(cache & letterIdxMask); idx < len(letterBytes) { - b[i] = letterBytes[idx] - i-- + for n := 0; n < 100; n++ { + + if i, e := rand.Int(rand.Reader, big.NewInt(size+1)); e != nil { + return 0 + } else { + j := i.Int64() + + if j > 0 && j < size { + return int(j) + } } - cache >>= letterIdxBits - remain-- } - return string(b) + return 0 } -// Generate Generate a random string could be used as password. -// The len is defined by given 'n' parameters. func Generate(n int) string { - if n > loopRandMaxLen { - var s = "" - for i := n; i > 0; i -= loopRandMaxLen { - s += randStringBytesMaskImprSrc(loopRandMaxLen) - } - return s[0 : n-1] + b := make([]byte, n) + // A src.Int63() generates 63 random bits, enough for letterIdxMax characters! + for i := n - 1; i >= 0; { + b[i] = letterBytes[randIdx()] + i-- } - return randStringBytesMaskImprSrc(n) + return string(b) } diff --git a/size/format.go b/size/format.go index 9ef0ab08..980294b1 100644 --- a/size/format.go +++ b/size/format.go @@ -41,7 +41,8 @@ const ( ) var ( - _maxFloat = uint64(math.Ceil(math.MaxFloat64)) + _maxFloat64 = uint64(math.Ceil(math.MaxFloat64)) + _maxFloat32 = uint64(math.Ceil(math.MaxFloat32)) ) func (s Size) String() string { @@ -63,12 +64,48 @@ func (s Size) Int64() int64 { return int64(s) } +func (s Size) Int32() int32 { + if uint64(s) > math.MaxInt32 { + // overflow + return math.MaxInt32 + } + + return int32(s) +} + +func (s Size) Int() int { + if uint64(s) > math.MaxInt { + // overflow + return math.MaxInt + } + + return int(s) +} + func (s Size) Uint64() uint64 { return uint64(s) } +func (s Size) Uint32() uint32 { + if uint64(s) > math.MaxUint32 { + // overflow + return math.MaxUint32 + } + + return uint32(s) +} + +func (s Size) Uint() uint { + if uint64(s) > math.MaxUint { + // overflow + return math.MaxUint + } + + return uint(s) +} + func (s Size) Float64() float64 { - if uint64(s) > _maxFloat { + if uint64(s) > _maxFloat64 { // overflow return math.MaxFloat64 } @@ -76,6 +113,15 @@ func (s Size) Float64() float64 { return float64(s) } +func (s Size) Float32() float32 { + if uint64(s) > _maxFloat32 { + // overflow + return math.MaxFloat32 + } + + return float32(s) +} + func (s Size) Format(format string) string { switch { case SizeExa.isMax(s): diff --git a/size/interface.go b/size/interface.go index 05d19e02..9f87e21c 100644 --- a/size/interface.go +++ b/size/interface.go @@ -27,6 +27,8 @@ package bytes +import "math" + type Size uint64 const ( @@ -65,6 +67,18 @@ func SizeFromInt64(val int64) Size { return Size(v) } +func SizeFromFloat64(val float64) Size { + val = math.Floor(val) + + if val > math.MaxUint64 { + return Size(uint64(math.MaxUint64)) + } else if -val > math.MaxUint64 { + return Size(uint64(math.MaxUint64)) + } else { + return Size(uint64(val)) + } +} + func Parse(s string) (Size, error) { return parseString(s) } diff --git a/size/model.go b/size/model.go index c8e79972..6a7f8428 100644 --- a/size/model.go +++ b/size/model.go @@ -69,10 +69,10 @@ func (s Size) isMax(size Size) bool { } func (s Size) sizeByUnit(unit Size) float64 { - if s > 0 && uint64(s/unit) > _maxFloat { + if s > 0 && uint64(s/unit) > _maxFloat64 { // overflow return math.MaxFloat64 - } else if s < 0 && uint64(-s/unit) > _maxFloat { + } else if s < 0 && uint64(-s/unit) > _maxFloat64 { // overflow return -math.MaxFloat64 } else { diff --git a/socket/config/server.go b/socket/config/server.go index 04b4a2f0..28be1b8c 100644 --- a/socket/config/server.go +++ b/socket/config/server.go @@ -28,9 +28,10 @@ package config import ( "os" - "time" + libdur "github.com/nabbar/golib/duration" libptc "github.com/nabbar/golib/network/protocol" + libsiz "github.com/nabbar/golib/size" libsck "github.com/nabbar/golib/socket" scksrv "github.com/nabbar/golib/socket/server" ) @@ -39,17 +40,17 @@ type ServerConfig struct { Network libptc.NetworkProtocol `` Address string PermFile os.FileMode - BuffSizeRead int32 - TimeoutRead time.Duration - TimeoutWrite time.Duration + BuffSizeRead libsiz.Size + TimeoutRead libdur.Duration + TimeoutWrite libdur.Duration } func (o ServerConfig) New(handler libsck.Handler) (libsck.Server, error) { s, e := scksrv.New(handler, o.Network, o.BuffSizeRead, o.Address, o.PermFile) if e != nil { - s.SetReadTimeout(o.TimeoutRead) - s.SetWriteTimeout(o.TimeoutWrite) + s.SetReadTimeout(o.TimeoutRead.Time()) + s.SetWriteTimeout(o.TimeoutWrite.Time()) } return s, e diff --git a/socket/interface.go b/socket/interface.go index cecf0c08..09ad5c3c 100644 --- a/socket/interface.go +++ b/socket/interface.go @@ -31,6 +31,8 @@ import ( "io" "net" "time" + + libtls "github.com/nabbar/golib/certificates" ) const DefaultBufferSize = 32 * 1024 @@ -48,7 +50,31 @@ const ( ConnectionClose ) +func (c ConnState) String() string { + switch c { + case ConnectionDial: + return "Dial Connection" + case ConnectionNew: + return "New Connection" + case ConnectionRead: + return "Read Incoming Stream" + case ConnectionCloseRead: + return "Close Incoming Stream" + case ConnectionHandler: + return "Run Handler" + case ConnectionWrite: + return "Write Outgoing Steam" + case ConnectionCloseWrite: + return "Close Outgoing Stream" + case ConnectionClose: + return "Close Connection" + } + + return "unknown connection state" +} + type FuncError func(e error) +type FuncInfoSrv func(msg string) type FuncInfo func(local, remote net.Addr, state ConnState) type Handler func(request io.Reader, response io.Writer) type Response func(r io.Reader) @@ -56,10 +82,12 @@ type Response func(r io.Reader) type Server interface { RegisterFuncError(f FuncError) RegisterFuncInfo(f FuncInfo) + RegisterFuncInfoServer(f FuncInfoSrv) SetReadTimeout(d time.Duration) SetWriteTimeout(d time.Duration) + SetTLS(enable bool, config libtls.TLSConfig) error Listen(ctx context.Context) error Shutdown() Done() <-chan struct{} diff --git a/socket/server/interface_linux.go b/socket/server/interface_linux.go index f32a30f6..4ddc87f5 100644 --- a/socket/server/interface_linux.go +++ b/socket/server/interface_linux.go @@ -36,13 +36,14 @@ import ( "strings" libptc "github.com/nabbar/golib/network/protocol" + libsiz "github.com/nabbar/golib/size" libsck "github.com/nabbar/golib/socket" scksrt "github.com/nabbar/golib/socket/server/tcp" scksru "github.com/nabbar/golib/socket/server/udp" scksrx "github.com/nabbar/golib/socket/server/unix" ) -func New(handler libsck.Handler, proto libptc.NetworkProtocol, sizeBufferRead int32, address string, perm os.FileMode) (libsck.Server, error) { +func New(handler libsck.Handler, proto libptc.NetworkProtocol, sizeBufferRead libsiz.Size, address string, perm os.FileMode) (libsck.Server, error) { switch proto { case libptc.NetworkUnix: if strings.EqualFold(runtime.GOOS, "linux") { diff --git a/socket/server/interface_other.go b/socket/server/interface_other.go index d8114bb5..5c04570c 100644 --- a/socket/server/interface_other.go +++ b/socket/server/interface_other.go @@ -34,12 +34,13 @@ import ( "os" libptc "github.com/nabbar/golib/network/protocol" + libsiz "github.com/nabbar/golib/size" libsck "github.com/nabbar/golib/socket" scksrt "github.com/nabbar/golib/socket/server/tcp" scksru "github.com/nabbar/golib/socket/server/udp" ) -func New(handler libsck.Handler, proto libptc.NetworkProtocol, sizeBufferRead int32, address string, perm os.FileMode) (libsck.Server, error) { +func New(handler libsck.Handler, proto libptc.NetworkProtocol, sizeBufferRead libsiz.Size, address string, perm os.FileMode) (libsck.Server, error) { switch proto { case libptc.NetworkTCP, libptc.NetworkTCP4, libptc.NetworkTCP6: s := scksrt.New(handler, sizeBufferRead) diff --git a/socket/server/tcp/inerface.go b/socket/server/tcp/inerface.go index 0de70e38..93a70c08 100644 --- a/socket/server/tcp/inerface.go +++ b/socket/server/tcp/inerface.go @@ -29,6 +29,7 @@ package tcp import ( "sync/atomic" + libsiz "github.com/nabbar/golib/size" libsck "github.com/nabbar/golib/socket" ) @@ -37,7 +38,7 @@ type ServerTcp interface { RegisterServer(address string) error } -func New(h libsck.Handler, sizeBuffRead int32) ServerTcp { +func New(h libsck.Handler, sizeBuffRead libsiz.Size) ServerTcp { c := new(atomic.Value) c.Store(make(chan []byte)) @@ -48,17 +49,20 @@ func New(h libsck.Handler, sizeBuffRead int32) ServerTcp { f.Store(h) sr := new(atomic.Int32) - sr.Store(sizeBuffRead) + sr.Store(sizeBuffRead.Int32()) return &srv{ l: nil, + t: new(atomic.Value), h: f, c: c, s: s, - e: new(atomic.Value), - i: new(atomic.Value), + fe: new(atomic.Value), + fi: new(atomic.Value), + fs: new(atomic.Value), tr: new(atomic.Value), tw: new(atomic.Value), sr: sr, + ad: new(atomic.Value), } } diff --git a/socket/server/tcp/listener.go b/socket/server/tcp/listener.go index de561f3f..2fb43499 100644 --- a/socket/server/tcp/listener.go +++ b/socket/server/tcp/listener.go @@ -29,6 +29,7 @@ package tcp import ( "bytes" "context" + "crypto/tls" "io" "net" "net/url" @@ -83,7 +84,16 @@ func (o *srv) Listen(ctx context.Context) error { if a == nil { return ErrInvalidAddress - } else if l, e = net.Listen(libptc.NetworkTCP.Code(), a.Host); e != nil { + } else if t := o.getTLS(); t == nil { + o.fctInfoSrv("starting listening socket '%s %s'", libptc.NetworkTCP.String(), a.Host) + l, e = net.Listen(libptc.NetworkTCP.Code(), a.Host) + } else { + o.fctInfoSrv("starting listening socket 'TLS %s %s'", libptc.NetworkTCP.String(), a.Host) + l, e = tls.Listen(libptc.NetworkTCP.Code(), a.Host, t) + } + + if e != nil { + o.fctError(e) return e } diff --git a/socket/server/tcp/model.go b/socket/server/tcp/model.go index 6f304ca2..b362de1b 100644 --- a/socket/server/tcp/model.go +++ b/socket/server/tcp/model.go @@ -27,12 +27,15 @@ package tcp import ( + "crypto/tls" + "fmt" "net" "net/url" "strconv" "sync/atomic" "time" + libtls "github.com/nabbar/golib/certificates" libsck "github.com/nabbar/golib/socket" ) @@ -45,14 +48,21 @@ func init() { close(closedChanStruct) } +type data struct { + data any +} + type srv struct { l net.Listener + t *atomic.Value // tls config h *atomic.Value // handler c *atomic.Value // chan []byte s *atomic.Value // chan struct{} - e *atomic.Value // function error - i *atomic.Value // function info + + fe *atomic.Value // function error + fi *atomic.Value // function info + fs *atomic.Value // function info server tr *atomic.Value // connection read timeout tw *atomic.Value // connection write timeout @@ -81,12 +91,31 @@ func (o *srv) Shutdown() { } } +func (o *srv) SetTLS(enable bool, config libtls.TLSConfig) error { + if !enable { + // #nosec + o.t.Store(&tls.Config{}) + return nil + } + + if config == nil { + return fmt.Errorf("invalid tls config") + } else if l := config.GetCertificatePair(); len(l) < 1 { + return fmt.Errorf("invalid tls config, missing certificates pair") + } else if t := config.TlsConfig(""); t == nil { + return fmt.Errorf("invalid tls config") + } else { + o.t.Store(t) + return nil + } +} + func (o *srv) RegisterFuncError(f libsck.FuncError) { if o == nil { return } - o.e.Store(f) + o.fe.Store(f) } func (o *srv) RegisterFuncInfo(f libsck.FuncInfo) { @@ -94,7 +123,15 @@ func (o *srv) RegisterFuncInfo(f libsck.FuncInfo) { return } - o.i.Store(f) + o.fi.Store(f) +} + +func (o *srv) RegisterFuncInfoServer(f libsck.FuncInfoSrv) { + if o == nil { + return + } + + o.fs.Store(f) } func (o *srv) SetReadTimeout(d time.Duration) { @@ -137,7 +174,7 @@ func (o *srv) fctError(e error) { return } - v := o.e.Load() + v := o.fe.Load() if v != nil { v.(libsck.FuncError)(e) } @@ -148,12 +185,23 @@ func (o *srv) fctInfo(local, remote net.Addr, state libsck.ConnState) { return } - v := o.i.Load() + v := o.fi.Load() if v != nil { v.(libsck.FuncInfo)(local, remote, state) } } +func (o *srv) fctInfoSrv(msg string, args ...interface{}) { + if o == nil { + return + } + + v := o.fs.Load() + if v != nil { + v.(libsck.FuncInfoSrv)(fmt.Sprintf(msg, args...)) + } +} + func (o *srv) handler() libsck.Handler { if o == nil { return nil @@ -166,3 +214,17 @@ func (o *srv) handler() libsck.Handler { return nil } + +func (o *srv) getTLS() *tls.Config { + i := o.t.Load() + + if i == nil { + return nil + } else if t, k := i.(*tls.Config); !k { + return nil + } else if len(t.Certificates) < 1 { + return nil + } else { + return t + } +} diff --git a/socket/server/udp/inerface.go b/socket/server/udp/inerface.go index 1c936e7d..760a578f 100644 --- a/socket/server/udp/inerface.go +++ b/socket/server/udp/inerface.go @@ -29,6 +29,7 @@ package udp import ( "sync/atomic" + libsiz "github.com/nabbar/golib/size" libsck "github.com/nabbar/golib/socket" ) @@ -37,7 +38,7 @@ type ServerTcp interface { RegisterServer(address string) error } -func New(h libsck.Handler, sizeBuffRead int32) ServerTcp { +func New(h libsck.Handler, sizeBuffRead libsiz.Size) ServerTcp { c := new(atomic.Value) c.Store(make(chan []byte)) @@ -48,17 +49,19 @@ func New(h libsck.Handler, sizeBuffRead int32) ServerTcp { f.Store(h) sr := new(atomic.Int32) - sr.Store(sizeBuffRead) + sr.Store(sizeBuffRead.Int32()) return &srv{ l: nil, h: f, c: c, s: s, - e: new(atomic.Value), - i: new(atomic.Value), + fe: new(atomic.Value), + fi: new(atomic.Value), + fs: new(atomic.Value), tr: new(atomic.Value), tw: new(atomic.Value), sr: sr, + ad: new(atomic.Value), } } diff --git a/socket/server/udp/listener.go b/socket/server/udp/listener.go index 93153adc..0792e884 100644 --- a/socket/server/udp/listener.go +++ b/socket/server/udp/listener.go @@ -93,6 +93,7 @@ func (o *srv) Listen(ctx context.Context) error { } } + o.fctInfoSrv("starting listening socket 'TLS %s %s'", libptc.NetworkUDP.String(), a.Host) defer fctClose() // Accept new connection or stop if context or shutdown trigger diff --git a/socket/server/udp/model.go b/socket/server/udp/model.go index a41e23a3..dacf9a8a 100644 --- a/socket/server/udp/model.go +++ b/socket/server/udp/model.go @@ -27,12 +27,14 @@ package udp import ( + "fmt" "net" "net/url" "strconv" "sync/atomic" "time" + libtls "github.com/nabbar/golib/certificates" libsck "github.com/nabbar/golib/socket" ) @@ -51,8 +53,10 @@ type srv struct { h *atomic.Value // handler c *atomic.Value // chan []byte s *atomic.Value // chan struct{} - e *atomic.Value // function error - i *atomic.Value // function info + + fe *atomic.Value // function error + fi *atomic.Value // function info + fs *atomic.Value // function info server tr *atomic.Value // connection read timeout tw *atomic.Value // connection write timeout @@ -81,12 +85,16 @@ func (o *srv) Shutdown() { } } +func (o *srv) SetTLS(enable bool, config libtls.TLSConfig) error { + return nil +} + func (o *srv) RegisterFuncError(f libsck.FuncError) { if o == nil { return } - o.e.Store(f) + o.fe.Store(f) } func (o *srv) RegisterFuncInfo(f libsck.FuncInfo) { @@ -94,7 +102,15 @@ func (o *srv) RegisterFuncInfo(f libsck.FuncInfo) { return } - o.i.Store(f) + o.fi.Store(f) +} + +func (o *srv) RegisterFuncInfoServer(f libsck.FuncInfoSrv) { + if o == nil { + return + } + + o.fs.Store(f) } func (o *srv) SetReadTimeout(d time.Duration) { @@ -137,7 +153,7 @@ func (o *srv) fctError(e error) { return } - v := o.e.Load() + v := o.fe.Load() if v != nil { v.(libsck.FuncError)(e) } @@ -148,12 +164,23 @@ func (o *srv) fctInfo(local, remote net.Addr, state libsck.ConnState) { return } - v := o.i.Load() + v := o.fi.Load() if v != nil { v.(libsck.FuncInfo)(local, remote, state) } } +func (o *srv) fctInfoSrv(msg string, args ...interface{}) { + if o == nil { + return + } + + v := o.fs.Load() + if v != nil { + v.(libsck.FuncInfoSrv)(fmt.Sprintf(msg, args...)) + } +} + func (o *srv) handler() libsck.Handler { if o == nil { return nil diff --git a/socket/server/unix/inerface.go b/socket/server/unix/inerface.go index ab96ed9c..73d5c9d3 100644 --- a/socket/server/unix/inerface.go +++ b/socket/server/unix/inerface.go @@ -33,6 +33,7 @@ import ( "os" "sync/atomic" + libsiz "github.com/nabbar/golib/size" libsck "github.com/nabbar/golib/socket" ) @@ -41,7 +42,7 @@ type ServerUnix interface { RegisterSocket(unixFile string, perm os.FileMode) } -func New(h libsck.Handler, sizeBuffRead int32) ServerUnix { +func New(h libsck.Handler, sizeBuffRead libsiz.Size) ServerUnix { c := new(atomic.Value) c.Store(make(chan []byte)) @@ -51,26 +52,30 @@ func New(h libsck.Handler, sizeBuffRead int32) ServerUnix { f := new(atomic.Value) f.Store(h) + // socket read buff size sr := new(atomic.Int32) - sr.Store(sizeBuffRead) + sr.Store(sizeBuffRead.Int32()) - fp := new(atomic.Value) - fp.Store("") + // socket file + sf := new(atomic.Value) + sf.Store("") - pe := new(atomic.Int64) - pe.Store(0) + // socket permission + sp := new(atomic.Int64) + sp.Store(0) return &srv{ l: nil, h: f, c: c, s: s, - e: new(atomic.Value), - i: new(atomic.Value), + fe: new(atomic.Value), + fi: new(atomic.Value), + fs: new(atomic.Value), tr: new(atomic.Value), tw: new(atomic.Value), sr: sr, - fs: fp, - fp: pe, + sf: sf, + sp: sp, } } diff --git a/socket/server/unix/listener.go b/socket/server/unix/listener.go index eeb7713c..52382b97 100644 --- a/socket/server/unix/listener.go +++ b/socket/server/unix/listener.go @@ -82,7 +82,7 @@ func (o *srv) buffRead() *bytes.Buffer { } func (o *srv) getSocketFile() (string, error) { - f := o.fs.Load() + f := o.sf.Load() if f != nil { return o.checkFile(f.(string)) } @@ -91,7 +91,7 @@ func (o *srv) getSocketFile() (string, error) { } func (o *srv) getSocketPerm() os.FileMode { - p := o.fp.Load() + p := o.sp.Load() if p > 0 { return os.FileMode(p) } @@ -149,6 +149,7 @@ func (o *srv) Listen(ctx context.Context) error { } } + o.fctInfoSrv("starting listening socket 'TLS %s %s'", libptc.NetworkUnix.String(), unixFile) defer fctClose() if i.Mode() != perm { diff --git a/socket/server/unix/model.go b/socket/server/unix/model.go index f7b938b7..7f5073c6 100644 --- a/socket/server/unix/model.go +++ b/socket/server/unix/model.go @@ -30,11 +30,13 @@ package unix import ( + "fmt" "net" "os" "sync/atomic" "time" + libtls "github.com/nabbar/golib/certificates" libsck "github.com/nabbar/golib/socket" ) @@ -53,14 +55,17 @@ type srv struct { h *atomic.Value // handler c *atomic.Value // chan []byte s *atomic.Value // chan struct{} - e *atomic.Value // function error - i *atomic.Value // function info + + fe *atomic.Value // function error + fi *atomic.Value // function info + fs *atomic.Value // function info server tr *atomic.Value // connection read timeout tw *atomic.Value // connection write timeout sr *atomic.Int32 // read buffer size - fs *atomic.Value // file unix socket - fp *atomic.Int64 // file unix perm + + sf *atomic.Value // file unix socket + sp *atomic.Int64 // file unix perm } func (o *srv) Done() <-chan struct{} { @@ -83,12 +88,16 @@ func (o *srv) Shutdown() { } } +func (o *srv) SetTLS(enable bool, config libtls.TLSConfig) error { + return nil +} + func (o *srv) RegisterFuncError(f libsck.FuncError) { if o == nil { return } - o.e.Store(f) + o.fe.Store(f) } func (o *srv) RegisterFuncInfo(f libsck.FuncInfo) { @@ -96,7 +105,15 @@ func (o *srv) RegisterFuncInfo(f libsck.FuncInfo) { return } - o.i.Store(f) + o.fi.Store(f) +} + +func (o *srv) RegisterFuncInfoServer(f libsck.FuncInfoSrv) { + if o == nil { + return + } + + o.fs.Store(f) } func (o *srv) SetReadTimeout(d time.Duration) { @@ -116,8 +133,8 @@ func (o *srv) SetWriteTimeout(d time.Duration) { } func (o *srv) RegisterSocket(unixFile string, perm os.FileMode) { - o.fs.Store(unixFile) - o.fp.Store(int64(perm)) + o.sf.Store(unixFile) + o.sp.Store(int64(perm)) } func (o *srv) fctError(e error) { @@ -125,7 +142,7 @@ func (o *srv) fctError(e error) { return } - v := o.e.Load() + v := o.fe.Load() if v != nil { v.(libsck.FuncError)(e) } @@ -136,12 +153,23 @@ func (o *srv) fctInfo(local, remote net.Addr, state libsck.ConnState) { return } - v := o.i.Load() + v := o.fi.Load() if v != nil { v.(libsck.FuncInfo)(local, remote, state) } } +func (o *srv) fctInfoSrv(msg string, args ...interface{}) { + if o == nil { + return + } + + v := o.fs.Load() + if v != nil { + v.(libsck.FuncInfoSrv)(fmt.Sprintf(msg, args...)) + } +} + func (o *srv) handler() libsck.Handler { if o == nil { return nil