├── fsutils ├── testdata │ ├── test.yaml │ ├── test.dat │ └── test.json ├── doc.go └── fileutils.go ├── ioutils ├── testdata │ └── hello-world.txt ├── doc.go ├── io_utils.go ├── mime_test.go ├── chksum_test.go └── chksum.go ├── rest ├── testdata │ ├── test.json │ ├── test2.json │ ├── server.key │ ├── server.crt │ ├── test-key-2.pem │ └── test-key.pem ├── pkg.go ├── server_common.go ├── consts.go ├── doc.go ├── client_utils.go ├── client_response.go ├── client_utils_test.go ├── client_request_test.go └── server_opts_test.go ├── secrets ├── README.md ├── doc.go ├── store.go ├── storemanager.go ├── credential.go ├── aes_test.go ├── aes.go ├── storemanager_test.go └── localstore.go ├── errutils ├── README.MD ├── doc.go ├── errutils.go └── errutils_test.go ├── l3 ├── doc.go ├── consolewriter.go ├── logmsg.go ├── logger.go ├── filewriter.go ├── logger_test.go └── l3_config.go ├── go.mod ├── turbo ├── doc.go ├── pkg.go ├── auth │ ├── authenticator.go │ └── basicauth.go ├── filters.go ├── helpers.go ├── bench_test.go └── filter_test.go ├── uuid ├── doc.go ├── generator_test.go └── README.md ├── testing ├── doc.go └── assert │ └── README.md ├── .gitignore ├── codec ├── doc.go ├── validator │ ├── default.go │ ├── README.md │ └── constraints.go ├── bench_test.go ├── yaml_codec.go ├── xml_codec.go ├── constraints.go ├── README.md ├── json_codec.go └── codec_test.go ├── vfs ├── doc.go ├── basefile.go ├── README.md ├── localfs.go ├── vfs_manager_test.go ├── vfile.go ├── localfile.go └── vfs.go ├── assertion └── doc.go ├── config ├── doc.go ├── testdata │ └── test.properties ├── README.md ├── attributes.go ├── environment.go ├── properties_test.go └── configuration.go ├── fnutils ├── doc.go └── fn.go ├── textutils └── doc.go ├── semver ├── doc.go ├── utils.go └── README.md ├── lifecycle ├── pkg.go ├── component.go └── README.md ├── .github ├── dependabot.yml ├── workflows │ └── go_ci.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── CODE_OF_CONDUCT.md ├── clients ├── client.go ├── retryhandler.go └── doc.go ├── genai ├── pkg.go ├── template_test.go ├── message.go ├── memory.go ├── exchange_test.go ├── message_test.go └── template.go ├── messaging ├── pkg.go ├── local_message.go ├── doc.go ├── provider.go ├── local_provider_test.go ├── options.go ├── README.md └── local_provider.go ├── Makefile ├── go.sum ├── cli ├── flag.go ├── context.go ├── command.go ├── doc.go ├── usage.go └── command_test.go ├── collections ├── iterator.go ├── set.go ├── queue.go ├── list.go ├── queue_test.go ├── collections.go └── stack_test.go ├── SECURITY.md ├── LICENSE ├── managers ├── item_manager.go └── item_manager_test.go ├── CONTRIBUTING.md └── data └── schema_gen.go /fsutils/testdata/test.yaml: -------------------------------------------------------------------------------- 1 | test: value -------------------------------------------------------------------------------- /fsutils/testdata/test.dat: -------------------------------------------------------------------------------- 1 | Test Data File -------------------------------------------------------------------------------- /ioutils/testdata/hello-world.txt: -------------------------------------------------------------------------------- 1 | Hello, World! -------------------------------------------------------------------------------- /rest/testdata/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "world" 3 | } -------------------------------------------------------------------------------- /secrets/README.md: -------------------------------------------------------------------------------- 1 | # Secrets Management 2 | 3 | TODO -------------------------------------------------------------------------------- /errutils/README.MD: -------------------------------------------------------------------------------- 1 | # write readme for errutils package 2 | -------------------------------------------------------------------------------- /fsutils/testdata/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "key": "value" 3 | } -------------------------------------------------------------------------------- /rest/testdata/test2.json: -------------------------------------------------------------------------------- 1 | { 2 | "test2": "json-file" 3 | } -------------------------------------------------------------------------------- /l3/doc.go: -------------------------------------------------------------------------------- 1 | // Package l3 provides multi level logging for Go applications. 2 | package l3 3 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module oss.nandlabs.io/golly 2 | 3 | go 1.22.1 4 | 5 | require gopkg.in/yaml.v3 v3.0.1 6 | -------------------------------------------------------------------------------- /turbo/doc.go: -------------------------------------------------------------------------------- 1 | // package turbo provides enterprise grade http routing capabilities 2 | package turbo 3 | -------------------------------------------------------------------------------- /uuid/doc.go: -------------------------------------------------------------------------------- 1 | // package uuid provides a set of utilities for working with UUIDs in Go. 2 | package uuid 3 | -------------------------------------------------------------------------------- /testing/doc.go: -------------------------------------------------------------------------------- 1 | // Package testing provides support for automated testing of Go packages. 2 | package testing 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # VS Code specific files 2 | .vscode/ 3 | 4 | .out 5 | 6 | .DS_Store 7 | 8 | main.go 9 | log-config.json -------------------------------------------------------------------------------- /codec/doc.go: -------------------------------------------------------------------------------- 1 | // Package codec provides a set of utilities for encoding and decoding data in Go. 2 | package codec 3 | -------------------------------------------------------------------------------- /rest/pkg.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import "oss.nandlabs.io/golly/l3" 4 | 5 | // Logger 6 | var logger = l3.Get() 7 | -------------------------------------------------------------------------------- /secrets/doc.go: -------------------------------------------------------------------------------- 1 | // Package secrets provides a set of utilities for working with secrets in Go. 2 | package secrets 3 | -------------------------------------------------------------------------------- /turbo/pkg.go: -------------------------------------------------------------------------------- 1 | package turbo 2 | 3 | import "oss.nandlabs.io/golly/l3" 4 | 5 | var ( 6 | logger = l3.Get() 7 | ) 8 | -------------------------------------------------------------------------------- /vfs/doc.go: -------------------------------------------------------------------------------- 1 | // package vfs provides a set of utilities for working with virtual file systems in Go. 2 | package vfs 3 | -------------------------------------------------------------------------------- /assertion/doc.go: -------------------------------------------------------------------------------- 1 | // Package assertion provides a set of utilities for making assertions in Go. 2 | package assertion 3 | -------------------------------------------------------------------------------- /config/doc.go: -------------------------------------------------------------------------------- 1 | // Package config provides a configuration management library for Go applications. 2 | package config 3 | -------------------------------------------------------------------------------- /fnutils/doc.go: -------------------------------------------------------------------------------- 1 | // Package fnutils provides a set of utilities for working with functions in Go. 2 | package fnutils 3 | -------------------------------------------------------------------------------- /ioutils/doc.go: -------------------------------------------------------------------------------- 1 | package ioutils 2 | 3 | // package ioutils provides a set of utilities for working with the IO in Go. 4 | -------------------------------------------------------------------------------- /textutils/doc.go: -------------------------------------------------------------------------------- 1 | // package textutils provides a set of utilities for working with text in Go. 2 | package textutils 3 | -------------------------------------------------------------------------------- /fsutils/doc.go: -------------------------------------------------------------------------------- 1 | // Package fsutils provides a set of utilities for working with the filesystem in Go. 2 | package fsutils 3 | -------------------------------------------------------------------------------- /semver/doc.go: -------------------------------------------------------------------------------- 1 | // Package semver provides a set of utilities for working with semantic versions in Go. 2 | package semver 3 | -------------------------------------------------------------------------------- /lifecycle/pkg.go: -------------------------------------------------------------------------------- 1 | package lifecycle 2 | 3 | import "oss.nandlabs.io/golly/l3" 4 | 5 | // Logger for lifecycle package 6 | var logger = l3.Get() 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gomod 4 | directory: / 5 | schedule: 6 | interval: "daily" 7 | -------------------------------------------------------------------------------- /clients/client.go: -------------------------------------------------------------------------------- 1 | package clients 2 | 3 | type Client[RQ any, RS any] interface { 4 | SetOptions(options *ClientOptions) 5 | Execute(req RQ) (RS, error) 6 | } 7 | -------------------------------------------------------------------------------- /genai/pkg.go: -------------------------------------------------------------------------------- 1 | package genai 2 | 3 | import ( 4 | "oss.nandlabs.io/golly/l3" 5 | ) 6 | 7 | // LOGGER is the logger for the llm package. 8 | var LOGGER l3.Logger = l3.Get() 9 | -------------------------------------------------------------------------------- /config/testdata/test.properties: -------------------------------------------------------------------------------- 1 | #This is key 1 2 | key1=value1 3 | intval=1 4 | floatval=1.2 5 | #This is a test Comment 6 | testkey=test ${key1} 7 | testmultiline=test \ 8 | value -------------------------------------------------------------------------------- /messaging/pkg.go: -------------------------------------------------------------------------------- 1 | package messaging 2 | 3 | import ( 4 | "oss.nandlabs.io/golly/l3" 5 | ) 6 | 7 | // Logger for this package 8 | var logger = l3.Get() 9 | 10 | func init() { 11 | GetManager() 12 | } 13 | -------------------------------------------------------------------------------- /clients/retryhandler.go: -------------------------------------------------------------------------------- 1 | package clients 2 | 3 | // RetryInfo represents the retry configuration for a client. 4 | type RetryInfo struct { 5 | MaxRetries int // Maximum number of retries allowed. 6 | Wait int // Wait time in milliseconds between retries. 7 | } 8 | -------------------------------------------------------------------------------- /secrets/store.go: -------------------------------------------------------------------------------- 1 | package secrets 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | type Store interface { 8 | Get(key string, ctx context.Context) (*Credential, error) 9 | Write(key string, credential *Credential, ctx context.Context) error 10 | Provider() string 11 | } 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | default: all 2 | 3 | all: clean test build 4 | 5 | .PHONY: clean 6 | clean: 7 | @go mod tidy 8 | 9 | .PHONY: build 10 | build: 11 | @go build -v ./... 12 | 13 | .PHONY: test 14 | test: 15 | @go test -v ./... 16 | 17 | .PHONY: test-cover 18 | test-cover: 19 | @go test -cover -covermode=atomic ./... 20 | -------------------------------------------------------------------------------- /turbo/auth/authenticator.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | // TODO : Documentation 8 | 9 | type Authenticator interface { 10 | Apply(handler http.Handler) http.Handler 11 | } 12 | 13 | //OAuth Filter Implementation 14 | 15 | /*type OAuth struct {} 16 | func (oAuth *OAuth) Apply(next http.Handler) http.Handler { 17 | }*/ 18 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 2 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 3 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 4 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 5 | -------------------------------------------------------------------------------- /cli/flag.go: -------------------------------------------------------------------------------- 1 | // Package cli provides functionality for handling command-line flags. 2 | package cli 3 | 4 | // Flag represents a command-line flag. 5 | type Flag struct { 6 | Name string // Name of the flag. 7 | Aliases []string // Aliases for the flag. 8 | Usage string // Usage information for the flag. 9 | Default string // Default value for the flag. 10 | } 11 | -------------------------------------------------------------------------------- /turbo/auth/basicauth.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | type BasicAuthFilter struct { 4 | } 5 | 6 | /*func (ba *BasicAuthFilter) Apply(next http.Handler) http.Handler { 7 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 8 | // TODO : Basic Auth Implementation 9 | }) 10 | }*/ 11 | 12 | func CreateBasicAuthAuthenticator() *BasicAuthFilter { 13 | return &BasicAuthFilter{} 14 | } 15 | -------------------------------------------------------------------------------- /rest/testdata/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PARAMETERS----- 2 | BgUrgQQAIg== 3 | -----END EC PARAMETERS----- 4 | -----BEGIN EC PRIVATE KEY----- 5 | MIGkAgEBBDDk3PFoX1Flxj2Y4L2DjOkTvUQozXzYFCG6CxN+Vm9PVALDL4W7HtVW 6 | 75xf9IDYdsmgBwYFK4EEACKhZANiAATvxkwnOMv9IEl3kuMMTOnxsEgA5kf6IHXZ 7 | alkqE2rwYkZsZ+qLtRtTseLsW4qaUvROy9C1bxCaNimv5cyPPI6Iz0gaYAFQOWN2 8 | klXJT9l22uQnlIjbkcP9KbAC1kqIXdU= 9 | -----END EC PRIVATE KEY----- 10 | -------------------------------------------------------------------------------- /ioutils/io_utils.go: -------------------------------------------------------------------------------- 1 | package ioutils 2 | 3 | import "io" 4 | 5 | var CloserFunc = func(closer io.Closer) { 6 | _ = closer.Close() 7 | } 8 | 9 | func IsChanClosed[T any](ch chan T) (closed bool) { 10 | select { 11 | case <-ch: 12 | closed = true 13 | default: 14 | closed = false 15 | } 16 | return 17 | } 18 | 19 | func CloseChannel[T any](ch chan T) { 20 | if !IsChanClosed(ch) { 21 | close(ch) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /messaging/local_message.go: -------------------------------------------------------------------------------- 1 | package messaging 2 | 3 | type LocalMessage struct { 4 | *BaseMessage 5 | } 6 | 7 | func NewLocalMessage() (msg Message, err error) { 8 | var baseMsg *BaseMessage 9 | baseMsg, err = NewBaseMessage() 10 | if err == nil { 11 | msg = &LocalMessage{ 12 | BaseMessage: baseMsg, 13 | } 14 | } 15 | return 16 | } 17 | 18 | func (lm *LocalMessage) Rsvp(yes bool, options ...Option) (err error) { 19 | // Local message does not support RSVP 20 | return 21 | } 22 | -------------------------------------------------------------------------------- /collections/iterator.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | // Iterator is an interface that allows iterating over a collection 4 | type Iterator[T any] interface { 5 | // HasNext returns true if there are more elements in the collection 6 | HasNext() bool 7 | // Next returns the next element in the collection 8 | Next() T 9 | // Remove removes the last element returned by the iterator from the collection 10 | Remove() 11 | } 12 | 13 | type Iterable[T any] interface { 14 | Iterator() Iterator[T] 15 | } 16 | -------------------------------------------------------------------------------- /vfs/basefile.go: -------------------------------------------------------------------------------- 1 | package vfs 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | type BaseFile struct { 8 | VFile 9 | } 10 | 11 | func (b *BaseFile) AsString() (s string, err error) { 12 | var bytes []byte 13 | bytes, err = io.ReadAll(b) 14 | if err == nil { 15 | s = string(bytes) 16 | } 17 | return 18 | } 19 | 20 | func (b *BaseFile) AsBytes() ([]byte, error) { 21 | return io.ReadAll(b) 22 | } 23 | 24 | func (b *BaseFile) WriteString(s string) (int, error) { 25 | return io.WriteString(b, s) 26 | } 27 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | If you find any vulnerability in our code or the API we are running, please 6 | contact us ASAP by creating an 7 | [Issue](https://github.com/nandlabs/golly/issues/new/choose). We do take this 8 | very seriously. 9 | 10 | We will respond to you as fast as we can and start working to fix the problem. 11 | Please also keep in mind and understand that this is an open-source project and 12 | that the maintainers also have other work to finish. 13 | -------------------------------------------------------------------------------- /config/README.md: -------------------------------------------------------------------------------- 1 | # golly/config 2 | 3 | [![Go Reference](https://pkg.go.dev/badge/oss.nandlabs.io/golly/config.svg)](https://pkg.go.dev/oss.nandlabs.io/golly/config) 4 | 5 | ## Overview 6 | 7 | The `golly/config` package provides a configuration management library for Go 8 | applications. It allows you to easily load and access configuration values from 9 | various sources, such as environment variables, command-line arguments, and 10 | configuration files. 11 | 12 | ## Installation 13 | 14 | To install the package, use the `go get` command: 15 | -------------------------------------------------------------------------------- /rest/server_common.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import "errors" 4 | 5 | var ErrInvalidListenHost = errors.New("empty listen host") 6 | 7 | var ErrInvalidListenPort = errors.New("empty listen port") 8 | 9 | var ErrInvalidPrivateKeyPath = errors.New("empty private key path") 10 | 11 | var ErrInvalidCertPath = errors.New("empty cert path") 12 | 13 | var ErrInvalidConfig = errors.New("empty config path") 14 | 15 | var ErrInvalidID = errors.New("empty id") 16 | 17 | var ErrNilOptions = errors.New("nil options") 18 | 19 | var ErrInvalidParamType = errors.New("invalid param type provided") 20 | -------------------------------------------------------------------------------- /.github/workflows/go_ci.yml: -------------------------------------------------------------------------------- 1 | name: "Build & Test" 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - "*" 7 | push: 8 | branches: 9 | - "main" 10 | 11 | jobs: 12 | build: 13 | name: "Build & Test" 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Setup Go 1.x 17 | uses: actions/setup-go@v4 18 | with: 19 | go-version: ^1.2 20 | - name: Check out code into the Go module directory 21 | uses: actions/checkout@v4 22 | - name: Clean and Build 23 | run: make clean build 24 | - name: Test Coverage 25 | run: make test-cover 26 | -------------------------------------------------------------------------------- /secrets/storemanager.go: -------------------------------------------------------------------------------- 1 | package secrets 2 | 3 | import "sync" 4 | 5 | type Manager struct { 6 | stores map[string]Store 7 | once sync.Once 8 | } 9 | 10 | func GetManager() *Manager { 11 | return &Manager{ 12 | stores: nil, 13 | once: sync.Once{}, 14 | } 15 | } 16 | 17 | func (m *Manager) Register(store Store) { 18 | if m.stores == nil { 19 | m.once.Do(func() { 20 | m.stores = make(map[string]Store) 21 | }) 22 | } 23 | m.stores[store.Provider()] = store 24 | } 25 | 26 | func (m *Manager) Store(name string) (store Store) { 27 | if m.stores != nil { 28 | store = m.stores[name] 29 | } 30 | return 31 | } 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Execute the snippet 16 | 4. See error 17 | 18 | **Expected behavior** 19 | A clear and concise description of what you expected to happen. 20 | 21 | **Screenshots** 22 | If applicable, add screenshots to help explain your problem. 23 | 24 | **Go Version (please complete the following information):** 25 | - Version [e.g. 0.0.13] 26 | 27 | **Additional context** 28 | Add any other context about the problem here. 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Provide the new feature requests for the project 4 | title: "[FEATURE]" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /rest/consts.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | const ( 4 | // ContentTypeHeader 5 | ContentTypeHeader = "Content-Type" 6 | // JSONContentType 7 | JSONContentType = "application/json" 8 | // XMLContentType 9 | XMLContentType = "text/xml" 10 | // XmlApplicationContentType 11 | XmlApplicationContentType = "application/xml" 12 | // YAMLContentType 13 | YAMLContentType = "text/yaml" 14 | 15 | // ProxyAuthorizationHeader 16 | ProxyAuthorization = "Proxy-Authorization" 17 | // AuthorizationHeader 18 | Authorization 19 | // AcceptHeader 20 | AcceptHeader = "Accept" 21 | // AcceptEncodingHeader 22 | AcceptEncodingHeader = "Accept-Encoding" 23 | // AcceptLanguageHeader 24 | AcceptLanguageHeader = "Accept-Language" 25 | 26 | // PathSeparator 27 | PathSeparator = "/" 28 | ) 29 | -------------------------------------------------------------------------------- /errutils/doc.go: -------------------------------------------------------------------------------- 1 | // Package errutils provides a set of utilities for working with errors in Go. 2 | 3 | // Example: 4 | // 5 | // The following example demonstrates how to use the `Wrap` function to add context to an error: 6 | // 7 | // package main 8 | // 9 | // import ( 10 | // "fmt" 11 | // "oss.nandlabs.io/golly/errutils" 12 | // ) 13 | // 14 | // func main() { 15 | // err := someFunction() 16 | // if err != nil { 17 | // wrappedErr := errutils.Wrap(err, "failed to perform operation") 18 | // fmt.Println(wrappedErr) 19 | // } 20 | // } 21 | // 22 | // In the above example, the `Wrap` function is used to add context to the original error returned by `someFunction()`. 23 | // The resulting error is then printed using `fmt.Println()`. 24 | 25 | package errutils 26 | -------------------------------------------------------------------------------- /messaging/doc.go: -------------------------------------------------------------------------------- 1 | // Package messaging provides a set of utilities for working with messaging systems. 2 | // It includes functionality for sending and receiving messages, as well as managing message queues. 3 | // This package supports various messaging protocols, including AMQP and MQTT. 4 | // It provides a simple and consistent API for interacting with different messaging systems. 5 | // The `Sender` type is used for sending messages, while the `Receiver` type is used for receiving messages. 6 | // Both types provide methods for connecting to a messaging server, sending/receiving messages, and closing the connection. 7 | // 8 | // Note: This package requires a messaging server to be running in order to send/receive messages. 9 | // Please refer to the documentation of the specific messaging protocol for more information on how to set up a server. 10 | package messaging 11 | -------------------------------------------------------------------------------- /rest/doc.go: -------------------------------------------------------------------------------- 1 | // Package rest provides a set of utilities for making RESTful API calls. 2 | // 3 | // This package includes functions for sending HTTP requests, handling responses, 4 | // and managing authentication and authorization headers. 5 | // 6 | // Example usage: 7 | // 8 | // // Create a new REST client 9 | // client := rest.NewClient() 10 | // 11 | // // Set the base URL for the API 12 | // client.SetBaseURL("https://api.example.com") 13 | // 14 | // // Set the authentication token 15 | // client.SetAuthToken("YOUR_AUTH_TOKEN") 16 | // 17 | // // Send a GET request 18 | // response, err := client.Get("/users") 19 | // if err != nil { 20 | // log.Fatal(err) 21 | // } 22 | // 23 | // // Print the response body 24 | // fmt.Println(response.Body) 25 | // 26 | // // Close the response body 27 | // response.Close() 28 | // 29 | // For more information and examples, please refer to the package documentation. 30 | package rest 31 | -------------------------------------------------------------------------------- /cli/context.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | // Context represents the context for the command-line interface. 4 | type Context struct { 5 | CommandStack []string // CommandStack stores the stack of executed commands. 6 | Flags map[string]string // Flags stores the command-line flags and their values. 7 | } 8 | 9 | // NewCLIContext creates a new CLI context. 10 | func NewCLIContext() *Context { 11 | return &Context{ 12 | CommandStack: []string{}, 13 | Flags: make(map[string]string), 14 | } 15 | } 16 | 17 | // SetFlag sets the value of a command-line flag. 18 | func (ctx *Context) SetFlag(name, value string) { 19 | ctx.Flags[name] = value 20 | } 21 | 22 | // GetFlag retrieves the value of a command-line flag. 23 | // It returns the value and a boolean indicating whether the flag exists. 24 | func (ctx *Context) GetFlag(name string) (string, bool) { 25 | value, exists := ctx.Flags[name] 26 | return value, exists 27 | } 28 | -------------------------------------------------------------------------------- /cli/command.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | // Command represents a command in the CLI. 4 | type Command struct { 5 | Name string 6 | Usage string 7 | Version string 8 | Aliases []string 9 | Action func(ctx *Context) error 10 | SubCommands map[string]*Command 11 | Flags []*Flag 12 | } 13 | 14 | // NewCommand creates a new command with the given name, description, and handler function. 15 | func NewCommand(name, description, version string, action func(ctx *Context) error) *Command { 16 | return &Command{ 17 | Name: name, 18 | Usage: description, 19 | Version: version, 20 | Action: action, 21 | Aliases: make([]string, 0), 22 | SubCommands: make(map[string]*Command), 23 | Flags: make([]*Flag, 0), 24 | } 25 | } 26 | 27 | // AddSubCommand adds a subcommand to the command. 28 | func (cmd *Command) AddSubCommand(subCmd *Command) { 29 | cmd.SubCommands[subCmd.Name] = subCmd 30 | } 31 | -------------------------------------------------------------------------------- /rest/testdata/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICYTCCAeagAwIBAgIUT2NDXvl1eWM+o6jQqGnR/5eBQ0AwCgYIKoZIzj0EAwIw 3 | ZzELMAkGA1UEBhMCQVUxDDAKBgNVBAgMA05TVzEZMBcGA1UECgwQTmFuZGxhYnMg 4 | UHR5IEx0ZDEOMAwGA1UECwwFR29sbHkxHzAdBgNVBAMMFmdvbGx5LXRlc3QubmFu 5 | ZGxhYnMuaW8wHhcNMjQwOTI2MjMzNTM1WhcNMzQwOTI0MjMzNTM1WjBnMQswCQYD 6 | VQQGEwJBVTEMMAoGA1UECAwDTlNXMRkwFwYDVQQKDBBOYW5kbGFicyBQdHkgTHRk 7 | MQ4wDAYDVQQLDAVHb2xseTEfMB0GA1UEAwwWZ29sbHktdGVzdC5uYW5kbGFicy5p 8 | bzB2MBAGByqGSM49AgEGBSuBBAAiA2IABO/GTCc4y/0gSXeS4wxM6fGwSADmR/og 9 | ddlqWSoTavBiRmxn6ou1G1Ox4uxbippS9E7L0LVvEJo2Ka/lzI88jojPSBpgAVA5 10 | Y3aSVclP2Xba5CeUiNuRw/0psALWSohd1aNTMFEwHQYDVR0OBBYEFPJvnojefXp/ 11 | UtBJJiMabSLzbGZGMB8GA1UdIwQYMBaAFPJvnojefXp/UtBJJiMabSLzbGZGMA8G 12 | A1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDaQAwZgIxAO9BmpuO7d2CJI2unGGL 13 | gxABBsAM9FFroJ1rxe3Lv8q4vUoX+j5nrun3QBGVA1qMVwIxALxVR5oNiwBAcha8 14 | /VmiPPvom6KpvYqCS/Otjq8B3QvtgXRlYDtNYGT5pEKJwY/WuQ== 15 | -----END CERTIFICATE----- 16 | -------------------------------------------------------------------------------- /ioutils/mime_test.go: -------------------------------------------------------------------------------- 1 | package ioutils 2 | 3 | import ( 4 | "testing" 5 | 6 | "oss.nandlabs.io/golly/testing/assert" 7 | ) 8 | 9 | func TestMimeToExt(t *testing.T) { 10 | tests := []struct { 11 | mime string 12 | expected []string 13 | }{ 14 | {MimeTextPlain, []string{".txt", ".text"}}, 15 | {MimeTextHTML, []string{".html", ".htm"}}, 16 | {MimeTextCSS, []string{".css"}}, 17 | // Add more test cases here 18 | } 19 | 20 | for _, test := range tests { 21 | actual := mimeToExt[test.mime] 22 | assert.Equal(t, test.expected, actual) 23 | } 24 | } 25 | 26 | func TestExtToMime(t *testing.T) { 27 | tests := []struct { 28 | ext string 29 | expected string 30 | }{ 31 | {".txt", MimeTextPlain}, 32 | {".text", MimeTextPlain}, 33 | {".html", MimeTextHTML}, 34 | // Add more test cases here 35 | } 36 | 37 | for _, test := range tests { 38 | actual := mapExtToMime[test.ext] 39 | assert.Equal(t, test.expected, actual) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /codec/validator/default.go: -------------------------------------------------------------------------------- 1 | package validator 2 | 3 | import "errors" 4 | 5 | var ( 6 | ErrNotNull = "notnull validation failed for field %s" 7 | 8 | ErrMin = "min value validation failed for field %s" 9 | 10 | ErrMax = "max value validation failed for field %s" 11 | 12 | ErrExclusiveMin = "exclusive min validation failed for field %s" 13 | 14 | ErrExclusiveMax = "exclusive max validation failed for field %s" 15 | 16 | ErrMultipleOf = "multipleOf validation failed for field %s" 17 | 18 | ErrMinLength = "min-length validation failed for field %s" 19 | 20 | ErrMaxLength = "max-length validation failed for field %s" 21 | 22 | ErrPattern = "pattern validation failed for field %s" 23 | 24 | ErrEnums = "enum validation failed for field %s" 25 | 26 | ErrBadConstraint = "invalid constraint %s with value '%s' for field %s" 27 | 28 | ErrInvalidValidationForField = "invalid validation applied to the field %s" 29 | 30 | ErrNotSupported = errors.New("unsupported constraint on type") 31 | 32 | ErrConversionFailed = errors.New("conversion failed") 33 | ) 34 | -------------------------------------------------------------------------------- /codec/bench_test.go: -------------------------------------------------------------------------------- 1 | package codec 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | type BenchTestStruct struct { 9 | Name string `json:"name" constraints:"required=true;nillable=true;min-length=5"` 10 | Age int `json:"age" constraints:"required=true;nillable=true;min=21"` 11 | Description string `json:"description" constraints:"required=true;nillable=true;max-length=50"` 12 | Cost float64 `json:"cost" constraints:"required=true;nillable=true;exclusiveMin=200"` 13 | ItemCount int `json:"itemCount" constraints:"required=true;nillable=true;multipleOf=5"` 14 | } 15 | 16 | func BenchmarkJsonCodec(b *testing.B) { 17 | msg := BenchTestStruct{ 18 | Name: "BenchTest", 19 | Age: 25, 20 | Description: "this is bench testing", 21 | Cost: 299.9, 22 | ItemCount: 2000, 23 | } 24 | c, _ := Get("application/json", nil) 25 | buf := new(bytes.Buffer) 26 | 27 | for i := 0; i < b.N; i++ { 28 | if err := c.Write(msg, buf); err != nil { 29 | b.Errorf("error in write: %d", err) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **IMPORTANT: Please do not create a Pull Request without creating/linking an issue first.** 2 | 3 | *Any change needs to be discussed before proceeding. Failure to do so may result in the rejection of the pull request.* 4 | 5 | Please provide enough information so that others can review your pull request: 6 | 7 | 8 | 9 | Explain the **details** for making this change. What existing problem does the pull request solve? 10 | 11 | 12 | 13 | **Test plan (required)** 14 | 15 | Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI. 16 | 17 | 18 | 19 | **Code formatting** 20 | 21 | 22 | 23 | **Closing issues** 24 | 25 | Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes (if such). -------------------------------------------------------------------------------- /vfs/README.md: -------------------------------------------------------------------------------- 1 | # Virtual File System (VFS) Package 2 | The VFS package provides a unified api for accessing multiple file system. It is extensible and new implementation can be 3 | easily plugged in. 4 | 5 | The default package has methods for local file system. 6 | 7 | --- 8 | - [Installation](#installation) 9 | - [Usage](#usage) 10 | --- 11 | 12 | ### Installation 13 | ```bash 14 | go get oss.nandlabs.io/golly/vfs 15 | ``` 16 | 17 | ### Usage 18 | A simple usage of the library to create a directory in the OS. 19 | 20 | ```go 21 | package main 22 | 23 | import ( 24 | "fmt" 25 | "oss.nandlabs.io/golly/vfs" 26 | ) 27 | 28 | var ( 29 | testManager = GetManager() 30 | ) 31 | 32 | func GetRawPath(input string) (output string) { 33 | currentPath, _ := os.Getwd() 34 | u, _ := url.Parse(input) 35 | path := currentPath + u.Path 36 | output = u.Scheme + "://" + path 37 | return 38 | } 39 | 40 | func main() { 41 | u := GetRawPath("file:///test-data") 42 | _, err := testManager.MkdirRaw(u) 43 | if err != nil { 44 | fmt.Errorf("MkdirRaw() error = %v", err) 45 | } 46 | } 47 | ``` -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) Nandlabs Pty Ltd 2024 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /semver/utils.go: -------------------------------------------------------------------------------- 1 | package semver 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // compare returns an integer with 3 possible values, -1, 0, +1 9 | func compare(c1, c2 *SemVer) (int, error) { 10 | // compare major version 11 | if c1.major != c2.major { 12 | if c1.major > c2.major { 13 | return 1, nil 14 | } else { 15 | return -1, nil 16 | } 17 | } 18 | 19 | // compare minor version 20 | if c1.minor != c2.minor { 21 | if c1.minor > c2.minor { 22 | return 1, nil 23 | } else { 24 | return -1, nil 25 | } 26 | } 27 | 28 | // compare patch version 29 | if c1.patch != c2.patch { 30 | if c1.patch > c2.patch { 31 | return 1, nil 32 | } else { 33 | return -1, nil 34 | } 35 | } 36 | return comparePreRelease(c1.preRelease, c2.preRelease) 37 | } 38 | 39 | func comparePreRelease(v1, v2 string) (int, error) { 40 | 41 | pre1 := len(v1) > 1 42 | pre2 := len(v2) > 1 43 | 44 | if pre1 && pre2 { 45 | return strings.Compare(v1, v2), nil 46 | } 47 | if pre1 { 48 | return -1, nil 49 | } 50 | 51 | if pre2 { 52 | return 1, nil 53 | } 54 | 55 | return 0, fmt.Errorf("no pre-release versions present") 56 | } 57 | -------------------------------------------------------------------------------- /managers/item_manager.go: -------------------------------------------------------------------------------- 1 | package managers 2 | 3 | import "sync" 4 | 5 | type ItemManager[T any] interface { 6 | Register(name string, item T) 7 | Unregister(name string) 8 | Get(name string) T 9 | Items() []T 10 | } 11 | 12 | type itemManager[T any] struct { 13 | items map[string]T 14 | mutex sync.RWMutex 15 | } 16 | 17 | func (it *itemManager[T]) Register(name string, item T) { 18 | it.mutex.Lock() 19 | defer it.mutex.Unlock() 20 | it.items[name] = item 21 | } 22 | 23 | func (it *itemManager[T]) Unregister(name string) { 24 | it.mutex.Lock() 25 | defer it.mutex.Unlock() 26 | delete(it.items, name) 27 | } 28 | 29 | func (it *itemManager[T]) Get(name string) T { 30 | it.mutex.RLock() 31 | defer it.mutex.RUnlock() 32 | item := it.items[name] 33 | 34 | return item 35 | } 36 | 37 | func (it *itemManager[T]) Items() []T { 38 | it.mutex.RLock() 39 | defer it.mutex.RUnlock() 40 | var items []T 41 | for _, item := range it.items { 42 | items = append(items, item) 43 | } 44 | 45 | return items 46 | 47 | } 48 | 49 | func NewItemManager[T any]() ItemManager[T] { 50 | return &itemManager[T]{ 51 | items: make(map[string]T), 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /turbo/filters.go: -------------------------------------------------------------------------------- 1 | package turbo 2 | 3 | import ( 4 | "net/http" 5 | 6 | "oss.nandlabs.io/golly/l3" 7 | "oss.nandlabs.io/golly/turbo/auth" 8 | ) 9 | 10 | // FilterFunc FuncHandler for with which the Filters need to be defined 11 | type FilterFunc func(http.Handler) http.Handler 12 | 13 | // AddFilter Making the Filter Chain in the order of filters being added 14 | // if f1, f2, f3, finalHandler handlers are added to the filter chain then the order of execution remains 15 | // f1 -> f2 -> f3 -> finalHandler 16 | func (route *Route) AddFilter(filter ...FilterFunc) *Route { 17 | newFilters := make([]FilterFunc, 0, len(route.filters)+len(filter)) 18 | newFilters = append(newFilters, route.filters...) 19 | newFilters = append(newFilters, filter...) 20 | route.filters = newFilters 21 | return route 22 | } 23 | 24 | // AddAuthenticator Adding the authenticator filter to the route 25 | func (route *Route) AddAuthenticator(auth auth.Authenticator) *Route { 26 | route.authFilter = auth 27 | return route 28 | } 29 | 30 | // SetLogger Sets the custom logger is required at the route level 31 | func (route *Route) SetLogger(logger *l3.BaseLogger) *Route { 32 | route.logger = logger 33 | return route 34 | } 35 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Nandlabs' Golly Contribution Guidelines 2 | 3 | Thanks for your interest in contributing to Golly 🙌\ 4 | Every form of contribution is appreciated. From issue reports to pull requests 5 | and documentation 👌\ 6 | Please check out the [existing issues](https://github.com/nandlabs/golly/issues) 7 | 8 | #### 🐛 Bug Reports 9 | 10 | For reporting bugs, please create a 11 | [new issue](https://github.com/nandlabs/golly/issues/new/choose) following the 12 | [Bug Template](https://github.com/nandlabs/golly/blob/main/.github/ISSUE_TEMPLATE/bug_report.md). 13 | 14 | #### ✨ Feature Requests 15 | 16 | If you are missing a certain feature, please create a 17 | [new issue](https://github.com/nandlabs/golly/issues/new/choose). Please follow 18 | the 19 | [Feature Template](https://github.com/nandlabs/golly/blob/main/.github/ISSUE_TEMPLATE/feature_request.md). 20 | 21 | #### 📝 Docs 22 | 23 | The docs are present here 24 | [documentation](https://github.com/nandlabs/golly/blob/main/README.md). 25 | 26 | #### 💫 Pull Requests 27 | 28 | If you want to contribute improvements or new features we are happy to review 29 | your PR :)\ 30 | Please use a meaningful commit message and add a little description of your 31 | changes. 32 | -------------------------------------------------------------------------------- /clients/doc.go: -------------------------------------------------------------------------------- 1 | // Package clients provides a collection of client libraries for various services. 2 | // It offers a set of reusable and easy-to-use client implementations that can be used to interact with different services. 3 | // These client libraries are designed to simplify the process of making requests, handling responses, and managing authentication for the respective services. 4 | // The package includes clients for services such as HTTP, database, messaging, storage, and more. 5 | // Each client library is organized into its own subpackage, making it easy to import and use only the necessary clients. 6 | // Additionally, the package provides a consistent and unified interface for all the client libraries, allowing developers to switch between different services seamlessly. 7 | // By using the clients package, developers can save time and effort by leveraging pre-built client implementations and focusing on the core logic of their applications. 8 | // For more information and usage examples, refer to the documentation of each individual client library. 9 | // These clients can be used to interact with the corresponding services and perform 10 | // operations such as making API calls, retrieving data, and more. 11 | package clients 12 | -------------------------------------------------------------------------------- /vfs/localfs.go: -------------------------------------------------------------------------------- 1 | package vfs 2 | 3 | import ( 4 | "net/url" 5 | "os" 6 | ) 7 | 8 | const ( 9 | fileScheme = "file" 10 | emptyScheme = "" 11 | ) 12 | 13 | var localFsSchemes = []string{fileScheme, emptyScheme} 14 | 15 | type OsFs struct { 16 | *BaseVFS 17 | } 18 | 19 | func (o OsFs) Create(u *url.URL) (file VFile, err error) { 20 | var f *os.File 21 | f, err = os.Create(u.Path) 22 | if err == nil { 23 | file = &OsFile{ 24 | file: f, 25 | Location: u, 26 | fs: o, 27 | } 28 | } 29 | return 30 | } 31 | 32 | func (o OsFs) Mkdir(u *url.URL) (file VFile, err error) { 33 | err = os.Mkdir(u.Path, os.ModePerm) 34 | if err == nil { 35 | file, err = o.Open(u) 36 | } 37 | return 38 | } 39 | 40 | func (o OsFs) MkdirAll(u *url.URL) (file VFile, err error) { 41 | err = os.MkdirAll(u.Path, os.ModePerm) 42 | if err == nil { 43 | file, err = o.Open(u) 44 | } 45 | return 46 | } 47 | 48 | func (o OsFs) Open(u *url.URL) (file VFile, err error) { 49 | var f *os.File 50 | f, err = os.Open(u.Path) 51 | if err == nil { 52 | file = &OsFile{ 53 | file: f, 54 | Location: u, 55 | fs: o, 56 | } 57 | } 58 | return 59 | } 60 | 61 | func (o OsFs) Schemes() []string { 62 | return localFsSchemes 63 | } 64 | -------------------------------------------------------------------------------- /secrets/credential.go: -------------------------------------------------------------------------------- 1 | package secrets 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | const ( 8 | CredType = "CredType" 9 | TextSecret = "TextSecret" 10 | ) 11 | 12 | //Credential Struct that can represent any secret value. 13 | //Fields 14 | // 15 | // Value is an array of bytes to hold decrypted value of a secret. 16 | 17 | // LastUpdated specifies the last updated time of the credential. 18 | // 19 | // Version string specifies the current version of the credential. If the credential type/Store implementation 20 | // maintains multiple version the previous version info can be maintained in metadata field. 21 | // 22 | // MetaData Any additional key,value attributes that needs to be associated with the credentials can be done using this 23 | //property 24 | 25 | type Credential struct { 26 | Value []byte 27 | LastUpdated time.Time 28 | Version string 29 | MetaData map[string]interface{} 30 | } 31 | 32 | // Str function gets the Credential.Value field as string 33 | func (c *Credential) Str() (s string) { 34 | if c.Value != nil { 35 | s = string(c.Value) 36 | } 37 | return 38 | } 39 | 40 | // Type Returns the type of the credential 41 | func (c *Credential) Type() (s string) { 42 | s = TextSecret 43 | if c.MetaData != nil { 44 | if v, ok := c.MetaData[CredType]; ok { 45 | s = v.(string) 46 | } 47 | } 48 | return 49 | } 50 | -------------------------------------------------------------------------------- /testing/assert/README.md: -------------------------------------------------------------------------------- 1 | # Assertion 2 | 3 | This is a flexible and extensible assertion library designed to provide a unified interface for asserting conditions in Go tests. It allows developers to seamlessly integrate assertion functionality into their tests without being tied to a specific assertion library. 4 | 5 | --- 6 | 7 | - [Features](#features) 8 | - [Installation](#installation) 9 | - [Usage](#usage) 10 | 11 | --- 12 | 13 | ## Features 14 | 15 | - General assertion interface for asserting conditions in tests. 16 | - Supports various assertion functions for different types of conditions. 17 | - Easy-to-use assertion functions for consistent handling of assertions in tests. 18 | 19 | ## Installation 20 | 21 | To install the assertion library, use the following command: 22 | 23 | ```bash 24 | go get oss.nandlabs.io/golly/testing/assert 25 | ``` 26 | 27 | ## Usage 28 | 29 | 1. Import the library into your Go test file: 30 | ```go 31 | import "oss.nandlabs.io/golly/testing/assert" 32 | ``` 33 | 2. Use the assertion functions in your test cases. For example: 34 | ```go 35 | func TestAdd(t *testing.T) { 36 | result := add(1, 2) 37 | assert.Equal(t, result, 3) 38 | } 39 | ``` 40 | 3. Run your tests using the `go test` command: 41 | ```bash 42 | go test 43 | ``` 44 | 4. View the test results and assertions in the test output. 45 | -------------------------------------------------------------------------------- /l3/consolewriter.go: -------------------------------------------------------------------------------- 1 | package l3 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | "os" 7 | ) 8 | 9 | // ConsoleWriter struct 10 | type ConsoleWriter struct { 11 | errorWriter, warnWriter, infoWriter, debugWriter, traceWriter io.Writer 12 | } 13 | 14 | // InitConfig ConsoleWriter 15 | func (cw *ConsoleWriter) InitConfig(w *WriterConfig) { 16 | if w.Console.WriteErrToStdOut { 17 | cw.errorWriter = bufio.NewWriter(os.Stdout) 18 | } else { 19 | cw.errorWriter = bufio.NewWriter(os.Stderr) 20 | } 21 | if w.Console.WriteWarnToStdOut { 22 | cw.warnWriter = bufio.NewWriter(os.Stdout) 23 | } else { 24 | cw.warnWriter = bufio.NewWriter(os.Stderr) 25 | } 26 | 27 | cw.infoWriter = bufio.NewWriter(os.Stdout) 28 | cw.debugWriter = bufio.NewWriter(os.Stdout) 29 | cw.traceWriter = bufio.NewWriter(os.Stdout) 30 | 31 | } 32 | 33 | // DoLog consoleWriter 34 | func (cw *ConsoleWriter) DoLog(logMsg *LogMessage) { 35 | var writer io.Writer 36 | 37 | switch logMsg.Level { 38 | case Off: 39 | break 40 | case Err: 41 | writer = cw.errorWriter 42 | case Warn: 43 | writer = cw.warnWriter 44 | case Info: 45 | writer = cw.infoWriter 46 | case Debug: 47 | writer = cw.debugWriter 48 | case Trace: 49 | writer = cw.traceWriter 50 | } 51 | 52 | if writer != nil { 53 | 54 | writeLogMsg(writer, logMsg) 55 | } 56 | } 57 | 58 | // Close close ConsoleWriter 59 | func (cw *ConsoleWriter) Close() error { 60 | return nil 61 | } 62 | -------------------------------------------------------------------------------- /rest/client_utils.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "mime/multipart" 7 | "net/http" 8 | "net/textproto" 9 | ) 10 | 11 | // CreateMultipartHeader creates a multipart header with the given parameters 12 | func CreateMultipartHeader(param, fileName, contentType string) textproto.MIMEHeader { 13 | hdr := make(textproto.MIMEHeader) 14 | hdr.Set(ContentTypeHeader, "multipart/form-data") 15 | return hdr 16 | } 17 | 18 | // WriteMultipartFormFile writes a multipart form file to the writer 19 | func WriteMultipartFormFile(w *multipart.Writer, fieldName, fileName string, r io.Reader) error { 20 | // Auto detect actual multipart content type 21 | cbuf := make([]byte, 512) 22 | size, err := r.Read(cbuf) 23 | if err != nil && err != io.EOF { 24 | return err 25 | } 26 | 27 | partWriter, err := w.CreatePart(CreateMultipartHeader(fieldName, fileName, http.DetectContentType(cbuf))) 28 | if err != nil { 29 | return err 30 | } 31 | 32 | if _, err = partWriter.Write(cbuf[:size]); err != nil { 33 | return err 34 | } 35 | 36 | _, err = io.Copy(partWriter, r) 37 | return err 38 | } 39 | 40 | // IsValidMultipartVerb checks if the method is valid for multipart content 41 | func IsValidMultipartVerb(method string) (err error) { 42 | if !(method == http.MethodPost || method == http.MethodPut || method == http.MethodPatch) { 43 | err = fmt.Errorf("multipart content is now allowed on [%v]", method) 44 | } 45 | return 46 | } 47 | -------------------------------------------------------------------------------- /genai/template_test.go: -------------------------------------------------------------------------------- 1 | package genai 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "oss.nandlabs.io/golly/testing/assert" 8 | ) 9 | 10 | func TestGoTemplate_Type(t *testing.T) { 11 | template, err := NewGoTemplate("test-01", "content") 12 | assert.Equal(t, GoTextTemplate, template.Type()) 13 | assert.NoError(t, err) 14 | } 15 | 16 | func TestGoTemplate_FormatAsText(t *testing.T) { 17 | template, err := NewGoTemplate("test-02", "content") 18 | assert.NoError(t, err) 19 | data := make(map[string]interface{}) 20 | _, err = template.FormatAsText(data) 21 | assert.NoError(t, err) 22 | } 23 | 24 | func TestGoTemplate_WriteTo(t *testing.T) { 25 | template, err := NewGoTemplate("test-03", "content") 26 | assert.NoError(t, err) 27 | data := make(map[string]interface{}) 28 | writer := &bytes.Buffer{} 29 | err = template.WriteTo(writer, data) 30 | assert.NoError(t, err) 31 | 32 | } 33 | 34 | func TestGetPromptTemplate(t *testing.T) { 35 | _, err := NewGoTemplate("test-03", "content") 36 | assert.Nil(t, err) 37 | template := GetPromptTemplate("test-03") 38 | assert.NotNil(t, template) 39 | } 40 | 41 | func TestGetOrCreate(t *testing.T) { 42 | template, err := NewGoTemplate("test-05", "content") 43 | assert.NoError(t, err) 44 | assert.NotNil(t, template) 45 | } 46 | 47 | func TestPrepareData(t *testing.T) { 48 | data := make(map[string]interface{}) 49 | preparedData := prepareData(data) 50 | assert.NotNil(t, preparedData) 51 | } 52 | -------------------------------------------------------------------------------- /l3/logmsg.go: -------------------------------------------------------------------------------- 1 | package l3 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "sync" 7 | "time" 8 | 9 | "oss.nandlabs.io/golly/textutils" 10 | ) 11 | 12 | var logMsgPool = &sync.Pool{ 13 | New: func() interface{} { 14 | lm := &LogMessage{ 15 | Content: &bytes.Buffer{}, 16 | Buf: &bytes.Buffer{}, 17 | } 18 | lm.Content.Grow(1024) 19 | lm.Buf.Grow(1280) 20 | return lm 21 | }, 22 | } 23 | 24 | // LogMessage struct. 25 | type LogMessage struct { 26 | Time time.Time `json:"timestamp"` 27 | FnName string `json:"function,omitempty"` 28 | Line int `json:"line,omitempty"` 29 | Content *bytes.Buffer `json:"msg"` 30 | Level Level `json:"level"` 31 | Buf *bytes.Buffer 32 | //SevBytes []byte 33 | } 34 | 35 | func getLogMessageF(level Level, f string, v ...interface{}) *LogMessage { 36 | msg := logMsgPool.Get().(*LogMessage) 37 | msg.Level = level 38 | msg.Time = time.Now() 39 | msg.FnName = textutils.EmptyStr 40 | msg.Line = 0 41 | _, _ = fmt.Fprintf(msg.Content, f, v...) 42 | return msg 43 | } 44 | 45 | func getLogMessage(level Level, v ...interface{}) *LogMessage { 46 | msg := logMsgPool.Get().(*LogMessage) 47 | msg.Level = level 48 | msg.Time = time.Now() 49 | msg.FnName = textutils.EmptyStr 50 | msg.Line = 0 51 | _, _ = fmt.Fprint(msg.Content, v...) 52 | return msg 53 | } 54 | 55 | func putLogMessage(logMsg *LogMessage) { 56 | logMsg.Content.Reset() 57 | logMsgPool.Put(logMsg) 58 | } 59 | -------------------------------------------------------------------------------- /config/attributes.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | // Interface Attributes is the interface that represents the attributes of a message 4 | type Attributes interface { 5 | // Set adds a new attribute to the message 6 | Set(k string, v any) 7 | // Get returns the value of the attribute 8 | Get(k string) any 9 | // GetAsString returns the value of the attribute as a string 10 | GetAsString(k string) string 11 | // GetAsInt returns the value of the attribute as an int 12 | GetAsInt(k string) int 13 | // GetAsFloat returns the value of the attribute as a float 14 | GetAsFloat(k string) float64 15 | // GetAsBool returns the value of the attribute as a bool 16 | GetAsBool(k string) bool 17 | // GetAsBytes returns the value of the attribute as a byte array 18 | GetAsBytes(k string) []byte 19 | // GetAsArray returns the value of the attribute as an array 20 | GetAsArray(k string) []any 21 | // GetAsMap returns the value of the attribute as a map 22 | GetAsMap(k string) map[string]any 23 | // Remove removes the attribute from the message 24 | Remove(k string) 25 | // Keys returns the keys of the attributes 26 | Keys() []string 27 | // AsMap returns the attributes as a map 28 | AsMap() map[string]any 29 | // ThreadSafe makes the attributes thread safe 30 | ThreadSafe(bool) 31 | // IsThreadSafe returns true if the attributes are thread safe 32 | IsThreadSafe() bool 33 | // Merge merges this attribute with another attributes 34 | Merge(m Attributes) 35 | } 36 | -------------------------------------------------------------------------------- /managers/item_manager_test.go: -------------------------------------------------------------------------------- 1 | package managers 2 | 3 | import ( 4 | "testing" 5 | 6 | "oss.nandlabs.io/golly/testing/assert" 7 | ) 8 | 9 | func TestItemManager_Get(t *testing.T) { 10 | manager := NewItemManager[int]() 11 | manager.Register("item1", 1) 12 | manager.Register("item2", 2) 13 | 14 | item := manager.Get("item1") 15 | assert.Equal(t, 1, item) 16 | 17 | item = manager.Get("item2") 18 | assert.Equal(t, 2, item) 19 | 20 | item = manager.Get("item3") 21 | assert.Equal(t, 0, item) // Assuming zero value for int type 22 | } 23 | 24 | func TestItemManager_Items(t *testing.T) { 25 | manager := NewItemManager[int]() 26 | manager.Register("item1", 1) 27 | manager.Register("item2", 2) 28 | manager.Register("item3", 3) 29 | 30 | items := manager.Items() 31 | 32 | assert.ListHas(t, 1, items) 33 | assert.ListHas(t, 2, items) 34 | assert.ListHas(t, 3, items) 35 | } 36 | 37 | func TestItemManager_Items_Empty(t *testing.T) { 38 | manager := NewItemManager[int]() 39 | 40 | items := manager.Items() 41 | expectedItems := []int{} 42 | 43 | assert.ElementsMatch(t, items, expectedItems...) 44 | } 45 | 46 | func TestItemManager_Items_AfterUnregister(t *testing.T) { 47 | manager := NewItemManager[int]() 48 | manager.Register("item1", 1) 49 | manager.Register("item2", 2) 50 | manager.Register("item3", 3) 51 | 52 | manager.Unregister("item2") 53 | 54 | items := manager.Items() 55 | assert.ListHas(t, 1, items) 56 | assert.ListHas(t, 3, items) 57 | 58 | } 59 | -------------------------------------------------------------------------------- /collections/set.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | // Set is a generic interface that defines a collection of unique elements with various methods to manipulate them. 4 | // The Set interface uses a type parameter T to represent the type of elements stored in the set. 5 | // It provides the following methods: 6 | // 7 | 8 | type Set[T comparable] interface { 9 | // Extends Collection[T] 10 | Collection[T] 11 | // Union returns a new set containing all elements from this set and another set 12 | Union(set Set[T]) Set[T] 13 | // Intersection returns a new set containing only the elements that are in both this set and another set 14 | Intersection(set Set[T]) Set[T] 15 | // Difference returns a new set containing only the elements that are in this set but not in another set 16 | Difference(set Set[T]) Set[T] 17 | // SymmetricDifference returns a new set containing only the elements that are in either this set or another set, but not in both 18 | SymmetricDifference(set Set[T]) Set[T] 19 | // IsSubset checks if this set is a subset of another set 20 | IsSubset(set Set[T]) bool 21 | // IsSuperset checks if this set is a superset of another set 22 | IsSuperset(set Set[T]) bool 23 | // IsProperSubset checks if this set is a proper subset of another set 24 | IsProperSubset(set Set[T]) bool 25 | // IsProperSuperset checks if this set is a proper superset of another set 26 | IsProperSuperset(set Set[T]) bool 27 | // IsDisjoint checks if this set has no elements in common with another set 28 | IsDisjoint(set Set[T]) bool 29 | } 30 | -------------------------------------------------------------------------------- /l3/logger.go: -------------------------------------------------------------------------------- 1 | package l3 2 | 3 | const ( 4 | //Off - No logging 5 | Off Level = iota 6 | //Err - logging only for error level. 7 | Err 8 | //Warn - logging turned on for warning & error levels 9 | Warn 10 | //Info - logging turned on for Info, Warning and Error levels. 11 | Info 12 | //Debug - Logging turned on for Debug, Info, Warning and Error levels. 13 | Debug 14 | //Trace - Logging turned on for Trace,Info, Warning and error Levels. 15 | Trace 16 | ) 17 | 18 | // Level specifies the log level 19 | type Level int 20 | 21 | // Levels of the logging by severity 22 | var Levels = [...]string{ 23 | "OFF", 24 | "ERROR", 25 | "WARN", 26 | "INFO", 27 | "DEBUG", 28 | "TRACE", 29 | } 30 | 31 | // LevelsBytes of the logging by Level 32 | var LevelsBytes = [...][]byte{ 33 | []byte("OFF"), 34 | []byte("ERROR"), 35 | []byte("WARN"), 36 | []byte("INFO"), 37 | []byte("DEBUG"), 38 | []byte("TRACE"), 39 | } 40 | 41 | // LevelsMap of the logging by Level string Level type 42 | var LevelsMap = map[string]Level{ 43 | "OFF": Off, 44 | "ERROR": Err, 45 | "WARN": Warn, 46 | "INFO": Info, 47 | "DEBUG": Debug, 48 | "TRACE": Trace, 49 | } 50 | 51 | type Logger interface { 52 | Error(a ...interface{}) 53 | ErrorF(f string, a ...interface{}) 54 | Warn(a ...interface{}) 55 | WarnF(f string, a ...interface{}) 56 | Info(a ...interface{}) 57 | InfoF(f string, a ...interface{}) 58 | Debug(a ...interface{}) 59 | DebugF(f string, a ...interface{}) 60 | Trace(a ...interface{}) 61 | TraceF(f string, a ...interface{}) 62 | } 63 | -------------------------------------------------------------------------------- /codec/yaml_codec.go: -------------------------------------------------------------------------------- 1 | package codec 2 | 3 | import ( 4 | "io" 5 | 6 | "gopkg.in/yaml.v3" 7 | "oss.nandlabs.io/golly/ioutils" 8 | ) 9 | 10 | var yamlmimeTypes = []string{ioutils.MimeTextYAML} 11 | 12 | type yamlRW struct { 13 | options map[string]interface{} 14 | } 15 | 16 | // Write encodes the given value v into YAML format and writes it to the provided io.Writer w. 17 | // It returns an error if the encoding process fails. 18 | // 19 | // Parameters: 20 | // 21 | // v - The value to be encoded into YAML format. 22 | // w - The io.Writer where the encoded YAML data will be written. 23 | // 24 | // Returns: 25 | // 26 | // error - An error if the encoding process fails, otherwise nil. 27 | func (y *yamlRW) Write(v interface{}, w io.Writer) error { 28 | encoder := yaml.NewEncoder(w) 29 | return encoder.Encode(v) 30 | } 31 | 32 | // Read reads YAML-encoded data from the provided io.Reader and decodes it into the provided interface{}. 33 | // It returns an error if the decoding process fails. 34 | // 35 | // Parameters: 36 | // - r: An io.Reader from which the YAML data will be read. 37 | // - v: A pointer to the value where the decoded data will be stored. 38 | // 39 | // Returns: 40 | // - error: An error if the decoding process fails, otherwise nil. 41 | func (y *yamlRW) Read(r io.Reader, v interface{}) error { 42 | decoder := yaml.NewDecoder(r) 43 | return decoder.Decode(v) 44 | } 45 | 46 | // MimeTypes returns a slice of strings representing the MIME types 47 | // that are supported by the yamlRW codec. 48 | func (y *yamlRW) MimeTypes() []string { 49 | return yamlmimeTypes 50 | } 51 | -------------------------------------------------------------------------------- /fsutils/fileutils.go: -------------------------------------------------------------------------------- 1 | package fsutils 2 | 3 | import ( 4 | "net/http" 5 | "os" 6 | "path/filepath" 7 | 8 | "oss.nandlabs.io/golly/ioutils" 9 | ) 10 | 11 | // FileExists function will check if the file exists in the specified path and if it is a file indeed 12 | func FileExists(path string) bool { 13 | fileInfo, err := os.Stat(path) 14 | if os.IsNotExist(err) { 15 | return false 16 | } 17 | 18 | return !fileInfo.IsDir() 19 | } 20 | 21 | // DirExists function will check if the Directory exists in the specified path 22 | func DirExists(path string) bool { 23 | fileInfo, err := os.Stat(path) 24 | if os.IsNotExist(err) { 25 | return false 26 | } 27 | return fileInfo.IsDir() 28 | } 29 | 30 | // PathExists will return a boolean if the file/diretory exists 31 | func PathExists(p string) bool { 32 | _, err := os.Stat(p) 33 | return !os.IsNotExist(err) 34 | } 35 | 36 | // LookupContentType will lookup ContentType based on the file extension. 37 | // This function will only check based on the name of the file and use the file extension. 38 | func LookupContentType(path string) string { 39 | val := ioutils.GetMimeFromExt(filepath.Ext(path)) 40 | if val == "" { 41 | val = "application/octet-stream" 42 | } 43 | return val 44 | } 45 | 46 | // DetectContentType will detect the content type of a file. 47 | func DetectContentType(path string) (string, error) { 48 | file, err := os.Open(path) 49 | if err == nil { 50 | buffer := make([]byte, 512) 51 | n, err := file.Read(buffer) 52 | if err == nil { 53 | return http.DetectContentType(buffer[:n]), nil 54 | } 55 | } 56 | return "", err 57 | 58 | } 59 | -------------------------------------------------------------------------------- /secrets/aes_test.go: -------------------------------------------------------------------------------- 1 | package secrets 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestAes(t *testing.T) { 8 | type args struct { 9 | key string 10 | message string 11 | } 12 | tests := []struct { 13 | name string 14 | args args 15 | wantErr bool 16 | }{ 17 | { 18 | name: "simple-message-16bit", 19 | args: args{ 20 | key: "This12BitKey0001", 21 | message: "This is a simple message", 22 | }, 23 | wantErr: false, 24 | }, { 25 | name: "simple-message-24bit", 26 | args: args{ 27 | key: "This24BitKeyWillBeUsed01", 28 | message: "This is a simple message", 29 | }, 30 | wantErr: false, 31 | }, 32 | { 33 | name: "simple-message-32bit", 34 | args: args{ 35 | key: "thisisa32bitkeythisisa32bitkey02", 36 | message: "This is a simple message", 37 | }, 38 | wantErr: false, 39 | }, 40 | } 41 | 42 | for _, tt := range tests { 43 | var err error 44 | var encryptedMsg string 45 | var decryptedMsg string 46 | 47 | t.Run(tt.name, func(t *testing.T) { 48 | encryptedMsg, err = AesEncryptStr(tt.args.key, tt.args.message) 49 | 50 | if (err != nil) != tt.wantErr { 51 | t.Errorf("AesEncryptStr() error = %v, wantErr %v", err, tt.wantErr) 52 | return 53 | } 54 | decryptedMsg, err = AesDecryptStr(encryptedMsg, tt.args.key) 55 | 56 | if (err != nil) != tt.wantErr { 57 | t.Errorf("AesDecryptStr() error = %v, wantErr %v", err, tt.wantErr) 58 | return 59 | } 60 | 61 | if decryptedMsg != tt.args.message { 62 | t.Errorf("AesEncryptStr() gotEncrypted = %v, want %v", decryptedMsg, tt.args.message) 63 | } 64 | }) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /vfs/vfs_manager_test.go: -------------------------------------------------------------------------------- 1 | package vfs 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestGetManager(t *testing.T) { 9 | tests := []struct { 10 | name string 11 | want VFileSystem 12 | }{ 13 | { 14 | name: "localFs", 15 | want: &fileSystems{ 16 | fileSystems: map[string]VFileSystem{"file": &OsFs{BaseVFS: &BaseVFS{VFileSystem: &OsFs{}}}, "": &OsFs{BaseVFS: &BaseVFS{VFileSystem: &OsFs{}}}}, 17 | }, 18 | }, 19 | } 20 | for _, tt := range tests { 21 | t.Run(tt.name, func(t *testing.T) { 22 | if got := GetManager(); !reflect.DeepEqual(got, tt.want) { 23 | t.Errorf("GetManager() = %v, want %v", got, tt.want) 24 | } 25 | }) 26 | } 27 | } 28 | 29 | func TestFileSystems_IsSupported(t *testing.T) { 30 | var scheme string 31 | var output bool 32 | 33 | testManager := GetManager() 34 | 35 | scheme = "file" 36 | output = testManager.IsSupported(scheme) 37 | if output == false { 38 | t.Error() 39 | } 40 | scheme = "test" 41 | output = testManager.IsSupported(scheme) 42 | if output == true { 43 | t.Error() 44 | } 45 | } 46 | 47 | func Test_InvalidFS(t *testing.T) { 48 | testManager := GetManager() 49 | u := GetRawPath("dummy:///raw-abc.txt") 50 | _, err := testManager.CreateRaw(u) 51 | if err == nil { 52 | t.Errorf("CreateRaw() error = %v", err) 53 | } 54 | if err.Error() != "unsupported scheme dummy for in the url "+u { 55 | t.Errorf("Test_InvalidFS() error = %v", err) 56 | } 57 | } 58 | 59 | func TestFileSystems_Schemes(t *testing.T) { 60 | testManager := GetManager() 61 | 62 | output := testManager.Schemes() 63 | if output[0] != "file" { 64 | t.Errorf("Schemes() default scheme added is file") 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /rest/client_response.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "oss.nandlabs.io/golly/codec" 8 | "oss.nandlabs.io/golly/ioutils" 9 | ) 10 | 11 | type Response struct { 12 | raw *http.Response 13 | client *Client 14 | } 15 | 16 | // IsSuccess determines if the response is a success response 17 | func (r *Response) IsSuccess() bool { 18 | return r.raw.StatusCode >= 200 && r.raw.StatusCode <= 204 19 | } 20 | 21 | // GetError gets the error with status code and value 22 | func (r *Response) GetError() (err error) { 23 | if !r.IsSuccess() { 24 | err = fmt.Errorf("server responded with status code %d and status text %s", 25 | r.raw.StatusCode, r.raw.Status) 26 | } 27 | return 28 | } 29 | 30 | // Decode Function decodes the response body to a suitable object. The format of the body is determined by 31 | // Content-Type header in the response 32 | func (r *Response) Decode(v interface{}) (err error) { 33 | var c codec.Codec 34 | if r.IsSuccess() { 35 | defer ioutils.CloserFunc(r.raw.Body) 36 | contentType := r.raw.Header.Get(ContentTypeHeader) 37 | c, err = codec.Get(contentType, r.client.options.codecOptions) 38 | if err == nil { 39 | err = c.Read(r.raw.Body, v) 40 | } 41 | } else { 42 | err = r.GetError() 43 | } 44 | return 45 | } 46 | 47 | // Status Provides status text of the http response 48 | func (r *Response) Status() string { 49 | return r.Raw().Status 50 | } 51 | 52 | // StatusCode provides the status code of the response 53 | func (r *Response) StatusCode() int { 54 | return r.Raw().StatusCode 55 | } 56 | 57 | // Raw Provides the backend raw response 58 | func (r *Response) Raw() *http.Response { 59 | return r.raw 60 | } 61 | -------------------------------------------------------------------------------- /semver/README.md: -------------------------------------------------------------------------------- 1 | ## Semver 2 | 3 | A SemVer Package is a package that implements the Semantic Versioning (SemVer) specification. SemVer is a widely adopted standard for versioning software libraries and applications, making it easier for developers to understand when and what changes have been made to a package. 4 | 5 | --- 6 | 7 | - [Installation](#installation) 8 | - [Features](#features) 9 | - [Usage](#usage) 10 | - [Documentation](#documentation) 11 | - [Contributing](#contributing) 12 | 13 | --- 14 | 15 | ### Installation 16 | 17 | ```bash 18 | go get oss.nandlabs.io/golly/semver 19 | ``` 20 | 21 | ### Features 22 | 23 | - Adheres to the [SemVer 2.0.0](https://semver.org/spec/v2.0.0.html) specification 24 | - Easy to use API for parsing, comparing and generating SemVer versions 25 | - Supports pre-release and build metadata 26 | - Written in modern Golang and follows best practices 27 | 28 | ### Usage 29 | 30 | Here is an example of how to use the SemVer package in a Golang project: 31 | 32 | ```go 33 | package main 34 | 35 | import ( 36 | "fmt" 37 | "oss.nandlabs.io/golly/semver" 38 | ) 39 | 40 | func main() { 41 | version, err := semver.Parse("v1.2.3") 42 | if err != nil { 43 | fmt.Println(err) 44 | } 45 | fmt.Printf("Major :: %d", version.CurrentMajor()) 46 | fmt.Printf("Minor :: %d", version.CurrentMinor()) 47 | fmt.Printf("Patch :: %d", version.CurrentPatch()) 48 | 49 | metadataVersion, err := semver.Parse("v1.2.3-SNAPSHOT") 50 | if err != nil { 51 | fmt.Println(err) 52 | } 53 | fmt.Printf("Major :: %d", metadataVersion.CurrentMajor()) 54 | fmt.Printf("Minor :: %d", metadataVersion.CurrentMinor()) 55 | fmt.Printf("Patch :: %d", metadataVersion.CurrentPatch()) 56 | fmt.Printf("Pre-Release :: %s", metadataVersion.CurrentPreRelease) 57 | } 58 | ``` 59 | -------------------------------------------------------------------------------- /vfs/vfile.go: -------------------------------------------------------------------------------- 1 | package vfs 2 | 3 | import ( 4 | "io" 5 | "io/fs" 6 | "net/url" 7 | ) 8 | 9 | type FileFilter func(file VFile) (bool, error) 10 | 11 | // VFile interface provides the basic functions required to interact 12 | type VFile interface { 13 | //Closer interface included from io package 14 | io.Closer 15 | //VFileContent provider interface included 16 | VFileContent 17 | //ListAll children of this file instance. can be nil in case of file object instead of directory 18 | ListAll() ([]VFile, error) 19 | //Delete the file object. If the file type is directory all files and subdirectories will be deleted 20 | Delete() error 21 | //DeleteAll deletes all the files and it's subdirectories 22 | DeleteAll() error 23 | //Info Get the file ifo 24 | Info() (VFileInfo, error) 25 | //Parent of the file system 26 | Parent() (VFile, error) 27 | //Url of the file 28 | Url() *url.URL 29 | // AddProperty will add a property to the file 30 | AddProperty(name string, value string) error 31 | // GetProperty will add a property to the file 32 | GetProperty(name string) (string, error) 33 | } 34 | 35 | // VFileContent interface providers access to the content 36 | type VFileContent interface { 37 | io.ReadWriteSeeker 38 | //AsString content of the file. This should be used very carefully as it is not wise to load a large file in to string 39 | AsString() (string, error) 40 | //AsBytes content of the file.This should be used very carefully as it is not wise to load a large file into an array 41 | AsBytes() ([]byte, error) 42 | //WriteString method with write the string 43 | WriteString(s string) (int, error) 44 | //ContentType of the underlying content. If not set defaults to UTF-8 for text files 45 | ContentType() string 46 | } 47 | 48 | type VFileInfo interface { 49 | fs.FileInfo 50 | } 51 | -------------------------------------------------------------------------------- /rest/testdata/test-key-2.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCZxXMQ+LFK9//a 3 | 2uThpQScFyCq5v8TzBsXEGfk+89zT0h/6Lpgzk9p1/PzI55lUMt0T0V47ABElfRs 4 | cFyDori7ZlIJAOundTrHK9vv1tn1gXcnQmlynO/8L0v/jIEijRkDSB+pVBxS6A+c 5 | uN9ikQZL0FhMKbHp7/adZ69KC5j6a+QabrkGaTWhREutMGAL9PtHcQeul8OrywVh 6 | guoAPbgy6wQ9URwxBYxgjcWxNYe8bM6YUMqbIDQ4YYqeMhUq1Xc4a7FIwwXb8+th 7 | MZpXaZxy7bA9sCVagC1l36sCrkg64cxX40Sp7pY/O3pzHaMNHMMXehueu6GoJ4Zz 8 | aTZ/DNetAgMBAAECggEARNRb4BHhkhQWI0ltwA7UVGYJylXhgWQQEC/YwWdxgUTQ 9 | x+hNXL2cUF1IpeGgqesTVrmQ+EJzEEArC0r0LjvtAn0XZU5TLT7NE4/vP5jlMP5F 10 | bn5Mou/agABPvVCQEP4lXxGukmXC091p95XM79rIfbEBhP341CUEN4rMrJEqZZSp 11 | 8JOpapEvZllU+JG6TJL/AtrdOA+mzCIyVNvh2A9tMy5vyOS/rXyGcwlpMWLIWJUa 12 | XZQgpE2sWf6NCJWr/OMvg4pMZHHgHjwl/Toy9VSwJYBMi73IvhQDjn+47U6JQi5z 13 | diZIoCYpFROsvfDFtzy+419fvD20XdJcKBA6oXqihQKBgQDMQC4Fb2k9T+sGpLo1 14 | cj7h7N13WCuc99Z63TuDhjgAN8IJKclBUaoB9inuMZAW/9Mk1/bikyxu+OlKQr0H 15 | qmdCX65sEZP0Hu2ADl9bPdaj15GmvXmGZln/CxYriA+LssJn6Wx0oRdOxAoSwIO/ 16 | X+/1qrW6ui4sHeOyF13ANM/5TwKBgQDAuyUvoVg3J5WuriabBCG9HPLyZG4ZNpYf 17 | POsOXyH9vNYsNIWf6Y6ylrknGyYeeZVaPpakj4LgDP6yJP75dcqBjHXQ7L37mPst 18 | XDEzb7FqKHV5qm49vRXWySJ/pzxso/sk9xNG6Ct0qF1qcPQt+eWVBc5p0J3lJGrJ 19 | Oq3PGKHoQwKBgDgXkAvyEsbBwdurNXAkFdzZveBemGudLIvFYei23X5B0cnObfTA 20 | Rp4PSmidt+J5EzVVEfobTOKU/Qxl05/dTkpOGIqZMTajn7nBOLLIAsAwHVHbSxp7 21 | 1U9Tbh06WnqU9wquchh9fPcmo1WPBR9za2TeItZK9w8qjjOEczyrxEnFAoGBAI8d 22 | KMLSo2fpXAvnQ8GOcuM0EO+PRfYxA47UtJLqb5o0VDFlGuihDRZuN7TjQw4UKKRn 23 | n4M5Eez/mU19jVx+bE6Al6f5dCXU9oc7U0mnoe17d6hNtyjfNM1Wc+iorYTay44q 24 | bkUT99jbgOqeT6OoUiOzuQ/4Gpdh0G32xcPnvapzAoGBAKi3DUkF9wFAmAkReQNv 25 | 6WzA1zhvWejxZdqmJw9JSTwngds1x9qbhayWkYBACT+CKEUIkWS53euI61Hvy1a4 26 | eDyqTcUXpvMJEmaozqvru/yt6m1wShbZvdk/TWe4KWNaMSAoBwpm2c73QNriSB17 27 | Lx1iL2f3O52BJreduDNlf0Bg 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /rest/testdata/test-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCZxXMQ+LFK9//a 3 | 2uThpQScFyCq5v8TzBsXEGfk+89zT0h/6Lpgzk9p1/PzI55lUMt0T0V47ABElfRs 4 | cFyDori7ZlIJAOundTrHK9vv1tn1gXcnQmlynO/8L0v/jIEijRkDSB+pVBxS6A+c 5 | uN9ikQZL0FhMKbHp7/adZ69KC5j6a+QabrkGaTWhREutMGAL9PtHcQeul8OrywVh 6 | guoAPbgy6wQ9URwxBYxgjcWxNYe8bM6YUMqbIDQ4YYqeMhUq1Xc4a7FIwwXb8+th 7 | MZpXaZxy7bA9sCVagC1l36sCrkg64cxX40Sp7pY/O3pzHaMNHMMXehueu6GoJ4Zz 8 | aTZ/DNetAgMBAAECggEARNRb4BHhkhQWI0ltwA7UVGYJylXhgWQQEC/YwWdxgUTQ 9 | x+hNXL2cUF1IpeGgqesTVrmQ+EJzEEArC0r0LjvtAn0XZU5TLT7NE4/vP5jlMP5F 10 | bn5Mou/agABPvVCQEP4lXxGukmXC091p95XM79rIfbEBhP341CUEN4rMrJEqZZSp 11 | 8JOpapEvZllU+JG6TJL/AtrdOA+mzCIyVNvh2A9tMy5vyOS/rXyGcwlpMWLIWJUa 12 | XZQgpE2sWf6NCJWr/OMvg4pMZHHgHjwl/Toy9VSwJYBMi73IvhQDjn+47U6JQi5z 13 | diZIoCYpFROsvfDFtzy+419fvD20XdJcKBA6oXqihQKBgQDMQC4Fb2k9T+sGpLo1 14 | cj7h7N13WCuc99Z63TuDhjgAN8IJKclBUaoB9inuMZAW/9Mk1/bikyxu+OlKQr0H 15 | qmdCX65sEZP0Hu2ADl9bPdaj15GmvXmGZln/CxYriA+LssJn6Wx0oRdOxAoSwIO/ 16 | X+/1qrW6ui4sHeOyF13ANM/5TwKBgQDAuyUvoVg3J5WuriabBCG9HPLyZG4ZNpYf 17 | POsOXyH9vNYsNIWf6Y6ylrknGyYeeZVaPpakj4LgDP6yJP75dcqBjHXQ7L37mPst 18 | XDEzb7FqKHV5qm49vRXWySJ/pzxso/sk9xNG6Ct0qF1qcPQt+eWVBc5p0J3lJGrJ 19 | Oq3PGKHoQwKBgDgXkAvyEsbBwdurNXAkFdzZveBemGudLIvFYei23X5B0cnObfTA 20 | Rp4PSmidt+J5EzVVEfobTOKU/Qxl05/dTkpOGIqZMTajn7nBOLLIAsAwHVHbSxp7 21 | 1U9Tbh06WnqU9wquchh9fPcmo1WPBR9za2TeItZK9w8qjjOEczyrxEnFAoGBAI8d 22 | KMLSo2fpXAvnQ8GOcuM0EO+PRfYxA47UtJLqb5o0VDFlGuihDRZuN7TjQw4UKKRn 23 | n4M5Eez/mU19jVx+bE6Al6f5dCXU9oc7U0mnoe17d6hNtyjfNM1Wc+iorYTay44q 24 | bkUT99jbgOqeT6OoUiOzuQ/4Gpdh0G32xcPnvapzAoGBAKi3DUkF9wFAmAkReQNv 25 | 6WzA1zhvWejxZdqmJw9JSTwngds1x9qbhayWkYBACT+CKEUIkWS53euI61Hvy1a4 26 | eDyqTcUXpvMJEmaozqvru/yt6m1wShbZvdk/TWe4KWNaMSAoBwpm2c73QNriSB17 27 | Lx1iL2f3O52BJreduDNlf0Bg 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /secrets/aes.go: -------------------------------------------------------------------------------- 1 | package secrets 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | "crypto/rand" 7 | "encoding/base64" 8 | "errors" 9 | "io" 10 | ) 11 | 12 | func AesEncryptStr(key, message string) (encrypted string, err error) { 13 | var encryptedBytes []byte 14 | encryptedBytes, err = AesEncrypt([]byte(key), []byte(message)) 15 | if err == nil { 16 | encrypted = base64.RawStdEncoding.EncodeToString(encryptedBytes) 17 | } 18 | return 19 | } 20 | 21 | func AesEncrypt(key, message []byte) (encrypted []byte, err error) { 22 | var block cipher.Block 23 | block, err = aes.NewCipher(key) 24 | if err == nil { 25 | encrypted = make([]byte, aes.BlockSize+len(message)) 26 | iv := encrypted[:aes.BlockSize] 27 | if _, err = io.ReadFull(rand.Reader, iv); err == nil { 28 | stream := cipher.NewCFBEncrypter(block, iv) 29 | stream.XORKeyStream(encrypted[aes.BlockSize:], message) 30 | } 31 | } 32 | return 33 | } 34 | 35 | func AesDecryptStr(encrypted, key string) (message string, err error) { 36 | var decryptedBytes, encryptedBytes []byte 37 | encryptedBytes, err = base64.RawStdEncoding.DecodeString(encrypted) 38 | if err != nil { 39 | return 40 | } 41 | decryptedBytes, err = AesDecrypt(encryptedBytes, []byte(key)) 42 | if err == nil { 43 | message = string(decryptedBytes) 44 | } 45 | return 46 | } 47 | 48 | func AesDecrypt(encrypted, key []byte) (message []byte, err error) { 49 | var block cipher.Block 50 | block, err = aes.NewCipher(key) 51 | message = encrypted 52 | if err == nil { 53 | if len(encrypted) < aes.BlockSize { 54 | err = errors.New("encrypted block size is too short") 55 | } else { 56 | iv := message[:aes.BlockSize] 57 | message = message[aes.BlockSize:] 58 | stream := cipher.NewCFBDecrypter(block, iv) 59 | stream.XORKeyStream(message, message) 60 | } 61 | } 62 | 63 | return 64 | } 65 | -------------------------------------------------------------------------------- /secrets/storemanager_test.go: -------------------------------------------------------------------------------- 1 | package secrets 2 | 3 | import ( 4 | "context" 5 | "reflect" 6 | "sync" 7 | "testing" 8 | ) 9 | 10 | type TestStore struct { 11 | providerName string 12 | } 13 | 14 | func (t *TestStore) Get(key string, ctx context.Context) (*Credential, error) { 15 | 16 | panic("implement me") 17 | } 18 | 19 | func (t *TestStore) Write(key string, credential *Credential, ctx context.Context) error { 20 | 21 | panic("implement me") 22 | } 23 | 24 | func (t *TestStore) Provider() string { 25 | return t.providerName 26 | } 27 | 28 | func TestGetManager(t *testing.T) { 29 | tests := []struct { 30 | name string 31 | want *Manager 32 | }{ 33 | {name: "GetStoreMangerTest", 34 | want: &Manager{ 35 | stores: nil, 36 | once: sync.Once{}, 37 | }}, 38 | } 39 | for _, tt := range tests { 40 | t.Run(tt.name, func(t *testing.T) { 41 | if got := GetManager(); !reflect.DeepEqual(got, tt.want) { 42 | t.Errorf("GetManager() = %v, want %v", got, tt.want) 43 | } 44 | }) 45 | } 46 | } 47 | 48 | func TestManager_Register_Store(t *testing.T) { 49 | type fields struct { 50 | stores map[string]Store 51 | } 52 | 53 | var testStoreName = "TestStore" 54 | var want = &TestStore{providerName: testStoreName} 55 | tests := []struct { 56 | name string 57 | fields fields 58 | want *TestStore 59 | }{ 60 | { 61 | name: "testStore", 62 | fields: fields{ 63 | stores: make(map[string]Store), 64 | }, 65 | want: want, 66 | }, 67 | } 68 | for _, tt := range tests { 69 | t.Run(tt.name, func(t *testing.T) { 70 | m := &Manager{ 71 | stores: tt.fields.stores, 72 | once: sync.Once{}, 73 | } 74 | m.Register(tt.want) 75 | if got := m.Store(testStoreName); !reflect.DeepEqual(got, tt.want) { 76 | t.Errorf("GetManager() = %v, want %v", got, tt.want) 77 | } 78 | }) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /codec/xml_codec.go: -------------------------------------------------------------------------------- 1 | package codec 2 | 3 | import ( 4 | "encoding/xml" 5 | "io" 6 | 7 | "oss.nandlabs.io/golly/ioutils" 8 | ) 9 | 10 | const ( 11 | xmlPrettyPrintPrefix = "" 12 | xmlPrettyPrintIndent = " " 13 | ) 14 | 15 | var xmlmimeTypes = []string{ioutils.MimeApplicationXML, ioutils.MimeTextXML} 16 | 17 | type xmlRW struct { 18 | options map[string]interface{} 19 | } 20 | 21 | // Write encodes the given value v into XML format and writes it to the provided io.Writer w. 22 | // If the PrettyPrint option is set to true in x.options, the output will be indented for readability. 23 | // 24 | // Parameters: 25 | // - v: The value to be encoded into XML. 26 | // - w: The io.Writer to which the encoded XML will be written. 27 | // 28 | // Returns: 29 | // - error: An error if the encoding or writing process fails, otherwise nil. 30 | func (x *xmlRW) Write(v interface{}, w io.Writer) error { 31 | encoder := xml.NewEncoder(w) 32 | var prettyPrint = false 33 | if x.options != nil { 34 | if v, ok := x.options[PrettyPrint]; ok { 35 | prettyPrint = v.(bool) 36 | } 37 | } 38 | if prettyPrint { 39 | encoder.Indent(xmlPrettyPrintPrefix, xmlPrettyPrintIndent) 40 | } 41 | return encoder.Encode(v) 42 | 43 | } 44 | 45 | // Read reads XML data from the provided io.Reader and decodes it into the provided interface{}. 46 | // It uses the xml.NewDecoder to decode the XML data. 47 | // Parameters: 48 | // - r: An io.Reader from which the XML data will be read. 49 | // - v: A pointer to the value where the decoded XML data will be stored. 50 | // 51 | // Returns: 52 | // - An error if the decoding fails, otherwise nil. 53 | func (x *xmlRW) Read(r io.Reader, v interface{}) error { 54 | decoder := xml.NewDecoder(r) 55 | return decoder.Decode(v) 56 | } 57 | 58 | // MimeTypes returns a slice of strings representing the MIME types 59 | // that are supported by the xmlRW codec. 60 | func (x *xmlRW) MimeTypes() []string { 61 | return xmlmimeTypes 62 | } 63 | -------------------------------------------------------------------------------- /messaging/provider.go: -------------------------------------------------------------------------------- 1 | package messaging 2 | 3 | import ( 4 | "io" 5 | "net/url" 6 | ) 7 | 8 | // Producer interface is used to send message(s) to a specific provider 9 | type Producer interface { 10 | // Send function sends an individual message to the url 11 | Send(*url.URL, Message, ...Option) error 12 | // SendBatch sends a batch of messages to the url 13 | SendBatch(*url.URL, []Message, ...Option) error 14 | } 15 | 16 | // Receiver interface provides the functions for receiving a message(s) 17 | type Receiver interface { 18 | // Receive function performs on-demand receive of a single message. 19 | // This function may or may not wait for the messages to arrive. This is purely dependent on the implementation. 20 | Receive(*url.URL, ...Option) (Message, error) 21 | // ReceiveBatch function performs on-demand receive of a batch of messages. 22 | // This function may or may not wait for the messages to arrive. This is purely dependent on the implementation. 23 | ReceiveBatch(*url.URL, ...Option) ([]Message, error) 24 | // AddListener registers a listener for the message 25 | AddListener(*url.URL, func(msg Message), ...Option) error 26 | } 27 | 28 | // Provider interface exposes methods for a messaging provider 29 | // It includes Producer and Receiver interfaces 30 | // It also includes Schemes method to get the supported schemes, 31 | // Setup method to perform initial setup and NewMessage method to create a new message 32 | type Provider interface { 33 | //Extends io.Closer 34 | io.Closer 35 | // Producer Interface included 36 | Producer 37 | // Receiver interface included 38 | Receiver 39 | // Id returns the id of the provider 40 | Id() string 41 | // Schemes is array of URL schemes supported by this provider 42 | Schemes() []string 43 | // Setup method called 44 | Setup() error 45 | 46 | // NewMessage function creates a new message that can be used by the clients. It expects the scheme to be provided 47 | NewMessage(string, ...Option) (Message, error) 48 | } 49 | -------------------------------------------------------------------------------- /collections/queue.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | // Queue is an interface that represents a queue data structure 4 | type Queue[T any] interface { 5 | Collection[T] 6 | // Remove and return the element at the front of the queue 7 | Dequeue() (T, error) 8 | // Add an element to the queue 9 | Enqueue(elem T) error 10 | // Return the element at the front of the queue without removing it 11 | Front() (T, error) 12 | } 13 | 14 | type queueImpl[T any] struct { 15 | List[T] 16 | } 17 | 18 | // NewQueue creates a new Queue 19 | func NewArrayQueue[T any]() Queue[T] { 20 | return &queueImpl[T]{NewArrayList[T]()} 21 | } 22 | 23 | // Remove and return the element at the front of the queue 24 | func (q *queueImpl[T]) Dequeue() (v T, err error) { 25 | if q.IsEmpty() { 26 | err = ErrEmptyCollection 27 | } else { 28 | v, err = q.RemoveFirst() 29 | } 30 | return 31 | } 32 | 33 | // Add an element to the queue 34 | func (q *queueImpl[T]) Enqueue(elem T) error { 35 | q.AddLast(elem) 36 | return nil 37 | } 38 | 39 | // Return the element at the front of the queue without removing it 40 | func (q *queueImpl[T]) Front() (v T, err error) { 41 | if q.IsEmpty() { 42 | err = ErrEmptyCollection 43 | } else { 44 | return q.GetFirst() 45 | } 46 | return 47 | } 48 | 49 | // syncQueueImpl is a thread-safe version of Queue 50 | type syncQueueImpl[T any] struct { 51 | *SyncedArrayList[T] 52 | } 53 | 54 | // NewSyncQueue creates a new thread-safe Queue 55 | func NewSyncQueue[T any]() Queue[T] { 56 | return &syncQueueImpl[T]{NewSyncedArrayList[T]()} 57 | } 58 | 59 | // Remove and return the element at the front of the queue 60 | func (sq *syncQueueImpl[T]) Dequeue() (T, error) { 61 | return sq.RemoveFirst() 62 | } 63 | 64 | // Add an element to the queue 65 | func (sq *syncQueueImpl[T]) Enqueue(elem T) error { 66 | return sq.AddLast(elem) 67 | 68 | } 69 | 70 | // Return the element at the front of the queue without removing it 71 | func (sq *syncQueueImpl[T]) Front() (T, error) { 72 | 73 | return sq.GetFirst() 74 | } 75 | -------------------------------------------------------------------------------- /config/environment.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | //This program contains utility functions related to environment variables 4 | import ( 5 | "os" 6 | "strconv" 7 | ) 8 | 9 | // GetEnvAsString function will fetch the val from environment variable. 10 | // If the value is absent then it will return defaultVal supplied. 11 | func GetEnvAsString(key, defaultVal string) string { 12 | if val, ok := os.LookupEnv(key); ok { 13 | return val 14 | } 15 | return defaultVal 16 | 17 | } 18 | 19 | // GetEnvAsInt function will fetch the val from environment variable and convert that to an integer. 20 | // If the value is absent then it will return defaultVal supplied. 21 | func GetEnvAsInt(key string, defaultVal int) (int, error) { 22 | if val, ok := os.LookupEnv(key); ok { 23 | return strconv.Atoi(val) 24 | } 25 | return defaultVal, nil 26 | } 27 | 28 | // GetEnvAsInt64 function will fetch the val from environment variable and convert that to an integer of 64 bit. 29 | // If the value is absent then it will return defaultVal supplied. 30 | func GetEnvAsInt64(key string, defaultVal int64) (int64, error) { 31 | if val, ok := os.LookupEnv(key); ok { 32 | return strconv.ParseInt(val, 10, 64) 33 | } 34 | return defaultVal, nil 35 | } 36 | 37 | // GetEnvAsDecimal function will fetch the val from environment variable and convert that to an float64. 38 | // If the value is absent then it will return defaultVal supplied. 39 | func GetEnvAsDecimal(key string, defaultVal float64) (float64, error) { 40 | if val, ok := os.LookupEnv(key); ok { 41 | return strconv.ParseFloat(val, 64) 42 | } 43 | return defaultVal, nil 44 | } 45 | 46 | // GetEnvAsBool function will fetch the val from environment variable and convert that to an GetEnvAsBool. 47 | // If the value is absent then it will return defaultVal supplied. 48 | // Valid boolean vals are 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False. 49 | func GetEnvAsBool(key string, defaultVal bool) (bool, error) { 50 | if val, ok := os.LookupEnv(key); ok { 51 | return strconv.ParseBool(val) 52 | } 53 | return defaultVal, nil 54 | } 55 | -------------------------------------------------------------------------------- /cli/doc.go: -------------------------------------------------------------------------------- 1 | // Package cli provides a command-line interface (CLI) framework for Go applications. 2 | // 3 | // This package offers a set of utilities and abstractions to build command-line interfaces 4 | // with ease. It includes features such as command parsing, flag handling, and subcommand support. 5 | // 6 | // Usage: 7 | // To use this package, import it in your Go code: 8 | // 9 | // import "github.com/nandlabs/golly/cli" 10 | // 11 | // Example: 12 | // Here's a simple example that demonstrates how to use the `cli` package: 13 | // 14 | // package main 15 | // 16 | // import ( 17 | // "fmt" 18 | // "github.com/nandlabs/golly/cli" 19 | // ) 20 | // 21 | // func main() { 22 | // app := cli.NewCLI() 23 | 24 | // server := &cli.Command{ 25 | // Name: "server", 26 | // Description: "Server command", 27 | // Handler: func(ctx *cli.Context) error { 28 | // region, _ := ctx.GetFlag("region") 29 | // fmt.Printf("IN REGION, %s\n", region) 30 | // return nil 31 | // }, 32 | // Flags: []cli.Flag{ 33 | // { 34 | // Name: "region", 35 | // Aliases: []string{"r"}, 36 | // Usage: "Provide region", 37 | // Default: "us-east-1", 38 | // }, 39 | // }, 40 | // SubCommands: map[string]*cli.Command{ 41 | // "create": { 42 | // Name: "create", 43 | // Description: "create", 44 | // Handler: func(ctx *cli.Context) error { 45 | // typ, _ := ctx.GetFlag("type") 46 | // fmt.Printf("SERVER TYPE %s\n", typ) 47 | // return nil 48 | // }, 49 | // Flags: []cli.Flag{ 50 | // { 51 | // Name: "type", 52 | // Aliases: []string{"t"}, 53 | // Usage: "server type", 54 | // Default: "t2.micro", 55 | // }, 56 | // }, 57 | // }, 58 | // }, 59 | // } 60 | 61 | // app.AddCommand(server) 62 | 63 | // if err := app.Execute(); err != nil { 64 | // fmt.Println("Error:", err) 65 | // } 66 | // } 67 | // 68 | // For more information and examples, please refer to the package documentation at: 69 | // 70 | // https://github.com/nandlabs/golly/cli 71 | package cli 72 | -------------------------------------------------------------------------------- /config/properties_test.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | func TestProperties_Get(t *testing.T) { 9 | p := NewProperties() 10 | p.Put("key1", "value1") 11 | p.Put("key2", "value2") 12 | 13 | // Test case 1: Key exists 14 | expected1 := "value1" 15 | actual1 := p.Get("key1", "") 16 | if actual1 != expected1 { 17 | t.Errorf("Expected %s, but got %s", expected1, actual1) 18 | } 19 | 20 | // Test case 2: Key does not exist, return default value 21 | expected2 := "default" 22 | actual2 := p.Get("key3", "default") 23 | if actual2 != expected2 { 24 | t.Errorf("Expected %s, but got %s", expected2, actual2) 25 | } 26 | } 27 | 28 | func TestProperties_Put(t *testing.T) { 29 | p := NewProperties() 30 | 31 | // Test case 1: Put new key-value pair 32 | expected1 := "value1" 33 | actual1 := p.Put("key1", "value1") 34 | if actual1 != "" { 35 | t.Errorf("Expected empty string, but got %s", actual1) 36 | } 37 | if p.Get("key1", "") != expected1 { 38 | t.Errorf("Expected %s, but got %s", expected1, p.Get("key1", "")) 39 | } 40 | 41 | // Test case 2: Put existing key-value pair 42 | expected2 := "value1" 43 | actual2 := p.Put("key1", "value2") 44 | 45 | if actual2 != expected2 { 46 | t.Errorf("Expected %s, but got %s", expected2, actual2) 47 | } 48 | if p.Get("key1", "") != "value2" { 49 | t.Errorf("Expected %s, but got %s", expected2, p.Get("key1", "")) 50 | } 51 | } 52 | 53 | func TestProperties_ReadFrom(t *testing.T) { 54 | p := NewProperties() 55 | 56 | // Test case 1: Read properties from reader 57 | input := strings.NewReader("key1=value1\nkey2=value2\n") 58 | err := p.Load(input) 59 | if err != nil { 60 | t.Errorf("Error reading properties: %s", err.Error()) 61 | } 62 | 63 | expected1 := "value1" 64 | actual1 := p.Get("key1", "") 65 | if actual1 != expected1 { 66 | t.Errorf("Expected %s, but got %s", expected1, actual1) 67 | } 68 | 69 | expected2 := "value2" 70 | actual2 := p.Get("key2", "") 71 | if actual2 != expected2 { 72 | t.Errorf("Expected %s, but got %s", expected2, actual2) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /codec/constraints.go: -------------------------------------------------------------------------------- 1 | package codec 2 | 3 | // StructMeta Struct 4 | type StructMeta struct { 5 | Fields map[string]string 6 | } 7 | 8 | // BaseConstraints struct captures the basic information for a field 9 | type BaseConstraints struct { 10 | // Name of the field 11 | Name string 12 | // Dimension holds the field dimension 13 | Dimension int 14 | // Required flag indicating if the field is a required field. 15 | Required bool 16 | // TargetNames stores map for known format types. This allows 17 | TargetNames map[string]string 18 | // TargetConfig stores configuration that is required by the target format . for Eg. Attribute config for XML etc. 19 | TargetConfig map[string]string 20 | // Sequence specifies the order of the fields in the source/target format 21 | Sequence int 22 | //SkipField indicates that if the value of the field is absent/nil then skip the field while writing to data 23 | //This is similar to omitempty 24 | SkipField bool 25 | } 26 | 27 | // StrConstraints Struct 28 | type StrConstraints struct { 29 | BaseConstraints 30 | DefaultVal *string 31 | Pattern *string 32 | Format *string 33 | MinLength *int 34 | MaxLength *int 35 | } 36 | 37 | // IntConstraints Struct 38 | type IntConstraints struct { 39 | BaseConstraints 40 | DefaultVal *int 41 | Min *int //The value is inclusive 42 | Max *int //The value is inclusive 43 | } 44 | 45 | // UIntConstraints Struct 46 | type UIntConstraints struct { 47 | BaseConstraints 48 | DefaultVal *uint 49 | Min *uint //The value is inclusive 50 | Max *uint //The value is inclusive 51 | } 52 | 53 | // F32Constraints Struct 54 | type F32Constraints struct { 55 | BaseConstraints 56 | DefaultVal *float32 57 | Min *float32 //The value is inclusive 58 | Max *float32 //The value is inclusive 59 | } 60 | 61 | // F64Constraints Struct 62 | type F64Constraints struct { 63 | BaseConstraints 64 | DefaultVal *float64 65 | Min *float64 //The value is inclusive 66 | Max *float64 //The value is inclusive 67 | } 68 | 69 | // BoolConstraints Struct 70 | type BoolConstraints struct { 71 | BaseConstraints 72 | DefaultVal *bool 73 | } 74 | -------------------------------------------------------------------------------- /messaging/local_provider_test.go: -------------------------------------------------------------------------------- 1 | package messaging 2 | 3 | import ( 4 | "net/url" 5 | "testing" 6 | 7 | "oss.nandlabs.io/golly/testing/assert" 8 | ) 9 | 10 | func TestLocalProvider_Send(t *testing.T) { 11 | lms := GetManager() 12 | msg, err := lms.NewMessage("chan") 13 | assert.NoError(t, err) 14 | input := "this is a test string" 15 | _, err = msg.SetBodyStr(input) 16 | if err != nil { 17 | t.Errorf("Error SetBodyStr:: %v", err) 18 | } 19 | uri, _ := url.Parse("chan://localhost:8080") 20 | got := lms.Send(uri, msg) 21 | if got != nil { 22 | t.Errorf("Error got :: %v", got) 23 | } 24 | uriErr, _ := url.Parse("http://localhost:8080") 25 | got = lms.Send(uriErr, msg) 26 | if got.Error() != "unsupported scheme http" { 27 | t.Errorf("Error got :: %v", got) 28 | } 29 | } 30 | 31 | func TestLocalProvider_SendBatch(t *testing.T) { 32 | lms := GetManager() 33 | msg1, err := lms.NewMessage("chan") 34 | assert.NoError(t, err) 35 | input1 := "this is a test string 1" 36 | _, err = msg1.SetBodyStr(input1) 37 | if err != nil { 38 | t.Errorf("Error SetBodyStr:: %v", err) 39 | } 40 | msg2, err := lms.NewMessage("chan") 41 | assert.NoError(t, err) 42 | input2 := "this is a test string 2" 43 | _, err = msg2.SetBodyStr(input2) 44 | if err != nil { 45 | t.Errorf("Error SetBodyStr:: %v", err) 46 | } 47 | uri, _ := url.Parse("chan://localhost:8080") 48 | msgs := []Message{msg1, msg2} 49 | got := lms.SendBatch(uri, msgs) 50 | if got != nil { 51 | t.Errorf("Error got :: %v", got) 52 | } 53 | } 54 | 55 | func TestLocalProvider_AddListener(t *testing.T) { 56 | lms := GetManager() 57 | uri, _ := url.Parse("chan://localhost2:8080") 58 | input1 := "this is a listener test" 59 | go func() { 60 | err := lms.AddListener(uri, func(output Message) { 61 | if output.ReadAsStr() != input1 { 62 | t.Errorf("Error AddListner") 63 | } 64 | }) 65 | if err != nil { 66 | t.Errorf("Error AddListner") 67 | } 68 | }() 69 | 70 | msg1, err := lms.NewMessage("chan") 71 | assert.NoError(t, err) 72 | _, err = msg1.SetBodyStr(input1) 73 | if err != nil { 74 | t.Errorf("Error SetBodyStr:: %v", err) 75 | } 76 | _ = lms.Send(uri, msg1) 77 | } 78 | -------------------------------------------------------------------------------- /secrets/localstore.go: -------------------------------------------------------------------------------- 1 | package secrets 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "encoding/gob" 7 | "fmt" 8 | "os" 9 | "sync" 10 | ) 11 | 12 | const ( 13 | LocalStoreProvider = "localStore" 14 | ) 15 | 16 | // localStore will want the credential in a local file. 17 | type localStore struct { 18 | credentials map[string]*Credential 19 | storeFile string 20 | masterKey string 21 | mutex sync.RWMutex 22 | } 23 | 24 | func NewLocalStore(storeFile, masterKey string) (s Store, err error) { 25 | var fileContent []byte 26 | var decryptedContent []byte 27 | var credentials = make(map[string]*Credential) 28 | var fileInfo os.FileInfo 29 | fileInfo, err = os.Stat(storeFile) 30 | s = &localStore{ 31 | credentials: credentials, 32 | storeFile: storeFile, 33 | masterKey: masterKey, 34 | mutex: sync.RWMutex{}, 35 | } 36 | if err == nil && !fileInfo.IsDir() { 37 | fileContent, err = os.ReadFile(storeFile) 38 | if err == nil { 39 | decryptedContent, err = AesDecrypt(fileContent, []byte(masterKey)) 40 | if err == nil { 41 | decoder := gob.NewDecoder(bytes.NewReader(decryptedContent)) 42 | err = decoder.Decode(&credentials) 43 | } 44 | } 45 | } else { 46 | err = nil 47 | } 48 | return 49 | } 50 | 51 | func (ls *localStore) Get(key string, ctx context.Context) (cred *Credential, err error) { 52 | ls.mutex.RLock() 53 | defer ls.mutex.RUnlock() 54 | if v, ok := ls.credentials[key]; ok { 55 | cred = v 56 | } else { 57 | err = fmt.Errorf("Unable to find a credential with key %s", key) 58 | } 59 | 60 | return 61 | } 62 | 63 | func (ls *localStore) Write(key string, credential *Credential, ctx context.Context) (err error) { 64 | ls.mutex.Lock() 65 | defer ls.mutex.Unlock() 66 | ls.credentials[key] = credential 67 | var b = &bytes.Buffer{} 68 | var encodedData []byte 69 | encoder := gob.NewEncoder(b) 70 | err = encoder.Encode(ls.credentials) 71 | if err == nil { 72 | encodedData, err = AesEncrypt([]byte(ls.masterKey), b.Bytes()) 73 | if err == nil { 74 | err = os.WriteFile(ls.storeFile, encodedData, 0600) 75 | } 76 | } 77 | return 78 | } 79 | 80 | func (ls *localStore) Provider() string { 81 | return LocalStoreProvider 82 | } 83 | -------------------------------------------------------------------------------- /turbo/helpers.go: -------------------------------------------------------------------------------- 1 | package turbo 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "html" 7 | "net/http" 8 | "path" 9 | ) 10 | 11 | // Common constants used throughout 12 | const ( 13 | PathSeparator = "/" 14 | GET = "GET" 15 | HEAD = "HEAD" 16 | POST = "POST" 17 | PUT = "PUT" 18 | DELETE = "DELETE" 19 | OPTIONS = "OPTIONS" 20 | TRACE = "TRACE" 21 | PATCH = "PATCH" 22 | ) 23 | 24 | var Methods = map[string]string{ 25 | GET: GET, 26 | HEAD: HEAD, 27 | POST: POST, 28 | PUT: PUT, 29 | DELETE: DELETE, 30 | OPTIONS: OPTIONS, 31 | TRACE: TRACE, 32 | PATCH: PATCH, 33 | } 34 | 35 | var ErrInvalidMethod = errors.New("Invalid method provided") 36 | var ErrInvalidPath = errors.New("Invalid path provided") 37 | var ErrInvalidHandler = errors.New("Invalid handler provided") 38 | 39 | // refinePath Borrowed from the golang's net/turbo package 40 | func refinePath(p string) string { 41 | if p == "" { 42 | return "/" 43 | } 44 | if p[0] != '/' { 45 | p = "/" + p 46 | } 47 | rp := path.Clean(p) 48 | if p[len(p)-1] == '/' && rp != "/" { 49 | rp += "/" 50 | } 51 | return rp 52 | } 53 | 54 | // endpointNotFound to check for the request endpoint 55 | func endpointNotFound(w http.ResponseWriter, r *http.Request) { 56 | w.WriteHeader(http.StatusNotFound) 57 | fmt.Fprintf(w, "Endpoint not found :%q \n", html.EscapeString(r.URL.Path)) 58 | } 59 | 60 | // endpointNotFoundHandler when a requested endpoint is not found in the registered route's this handler is invoked 61 | func endpointNotFoundHandler() http.Handler { 62 | return http.HandlerFunc(endpointNotFound) 63 | } 64 | 65 | // methodNotAllowed to check for the supported method for the incoming request 66 | func methodNotAllowed(w http.ResponseWriter, r *http.Request) { 67 | w.WriteHeader(http.StatusMethodNotAllowed) 68 | fmt.Fprintf(w, "Method %q Not Supported for %q \n", html.EscapeString(r.Method), html.EscapeString(r.URL.Path)) 69 | } 70 | 71 | // methodNotAllowedHandler when a requested method is not allowed in the registered route's method list this handler is invoked 72 | func methodNotAllowedHandler() http.Handler { 73 | return http.HandlerFunc(methodNotAllowed) 74 | } 75 | -------------------------------------------------------------------------------- /rest/client_utils_test.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "mime/multipart" 7 | "net/http" 8 | "strings" 9 | "testing" 10 | ) 11 | 12 | // TestCreateMultipartHeader tests the CreateMultipartHeader function 13 | func TestCreateMultipartHeader(t *testing.T) { 14 | param := "file" 15 | fileName := "test.txt" 16 | contentType := "text/plain" 17 | 18 | hdr := CreateMultipartHeader(param, fileName, contentType) 19 | if hdr.Get(ContentTypeHeader) != "multipart/form-data" { 20 | t.Errorf("CreateMultipartHeader() = %v, want %v", hdr.Get(ContentTypeHeader), "multipart/form-data") 21 | } 22 | } 23 | 24 | // TestWriteMultipartFormFile tests the WriteMultipartFormFile function 25 | func TestWriteMultipartFormFile(t *testing.T) { 26 | var b bytes.Buffer 27 | w := multipart.NewWriter(&b) 28 | fieldName := "file" 29 | fileName := "test.txt" 30 | content := "This is a test file." 31 | 32 | err := WriteMultipartFormFile(w, fieldName, fileName, strings.NewReader(content)) 33 | if err != nil { 34 | t.Errorf("WriteMultipartFormFile() error = %v", err) 35 | } 36 | 37 | w.Close() 38 | 39 | req, err := http.NewRequest("POST", "http://example.com/upload", &b) 40 | if err != nil { 41 | t.Fatalf("http.NewRequest() error = %v", err) 42 | } 43 | req.Header.Set(ContentTypeHeader, w.FormDataContentType()) 44 | 45 | if req.Header.Get(ContentTypeHeader) != w.FormDataContentType() { 46 | t.Errorf("Content-Type header = %v, want %v", req.Header.Get(ContentTypeHeader), w.FormDataContentType()) 47 | } 48 | } 49 | 50 | // TestIsValidMultipartVerb tests the IsValidMultipartVerb function 51 | func TestIsValidMultipartVerb(t *testing.T) { 52 | tests := []struct { 53 | method string 54 | want error 55 | }{ 56 | {http.MethodPost, nil}, 57 | {http.MethodPut, nil}, 58 | {http.MethodPatch, nil}, 59 | {http.MethodGet, fmt.Errorf("multipart content is now allowed on [%v]", http.MethodGet)}, 60 | } 61 | 62 | for _, tt := range tests { 63 | t.Run(tt.method, func(t *testing.T) { 64 | err := IsValidMultipartVerb(tt.method) 65 | if (err != nil && tt.want == nil) || (err == nil && tt.want != nil) || (err != nil && tt.want != nil && err.Error() != tt.want.Error()) { 66 | t.Errorf("IsValidMultipartVerb() = %v, want %v", err, tt.want) 67 | } 68 | }) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /codec/README.md: -------------------------------------------------------------------------------- 1 | ## Codec 2 | 3 | Codec provides a unified interface for interacting with multiple formats along with validation 4 | 5 | --- 6 | 7 | - [Installation](#installation) 8 | - [Supported Formats](#supported--formats) 9 | - [Codec](#codec-usage) 10 | - [Supported Formats](#supported--formats) 11 | - [Examples](#examples) 12 | - [Basic Example](#basic-example) 13 | - [Advanced Example](#advanced-example) 14 | - [Validation Example](#validation-example) 15 | 16 | --- 17 | 18 | ### Installation 19 | 20 | ```bash 21 | go get oss.nandlabs.io/golly/codec 22 | ``` 23 | 24 | ### Usage 25 | 26 | It comes with a simple usage as explained below, just import the package, and you are good to go. 27 | 28 | ### Codec Usage 29 | 30 | #### Supported Formats 31 | 32 | | Format | Status | 33 | |:-------|:---------:| 34 | | JSON | Completed | 35 | | YAML | Completed | 36 | | XML | Completed | 37 | 38 | ### Examples 39 | 40 | #### Advanced Example 41 | 42 | 1. JSON Codec - Encode struct 43 | ```go 44 | package main 45 | 46 | import ( 47 | "bytes" 48 | "fmt" 49 | codec "oss.nandlabs.io/golly/codec" 50 | ) 51 | 52 | type Message struct { 53 | Name string `json:"name"` 54 | Body string `json:"body"` 55 | Time int64 `json:"time"` 56 | } 57 | 58 | func main() { 59 | m := Message{"TestUser", "Hello", 123124124} 60 | 61 | cd, _ := codec.Get("application/json", nil) 62 | buf := new(bytes.Buffer) 63 | if err := cd.Write(m, buf); err != nil { 64 | fmt.Errorf("error in write: %d", err) 65 | } 66 | // use buf in the application 67 | } 68 | ``` 69 | 70 | #### Validation Example 71 | 72 | ```go 73 | package main 74 | 75 | import( 76 | "bytes" 77 | "fmt" 78 | codec "oss.nandlabs.io/golly/codec" 79 | ) 80 | 81 | //Message - add validations for the fields, codec internally validates the struct 82 | type Message struct { 83 | Name string `json:"name" constraints:"min-length=5"` 84 | Body string `json:"body" constraints:"max-length=50"` 85 | Time int64 `json:"time" constraints:"min=10"` 86 | } 87 | 88 | func main() { 89 | m := Message{"TestUser", "Hello", 123124124} 90 | c, _ := codec.Get("application/json", nil) 91 | buf := new(bytes.Buffer) 92 | if err := c.Write(m, buf); err != nil { 93 | fmt.Errorf("error in write: %d", err) 94 | } 95 | } 96 | ``` 97 | -------------------------------------------------------------------------------- /codec/json_codec.go: -------------------------------------------------------------------------------- 1 | package codec 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | 7 | "oss.nandlabs.io/golly/codec/validator" 8 | "oss.nandlabs.io/golly/ioutils" 9 | ) 10 | 11 | const ( 12 | jsonPrettyPrintPrefix = "" 13 | jsonPrettyPrintIndent = " " 14 | ) 15 | 16 | var structValidator = validator.NewStructValidator() 17 | var jsonmimeTypes = []string{ioutils.MimeApplicationJSON} 18 | 19 | type jsonRW struct { 20 | options map[string]interface{} 21 | } 22 | 23 | // Write encodes the given value v into JSON and writes it to the provided io.Writer w. 24 | // It supports options for escaping HTML and pretty-printing the JSON output. 25 | // The options are specified in the jsonRW struct's options map with the keys JsonEscapeHTML and PrettyPrint. 26 | // 27 | // Parameters: 28 | // - v: The value to be encoded into JSON. 29 | // - w: The io.Writer to write the JSON output to. 30 | // 31 | // Returns: 32 | // - error: An error if the encoding or writing fails, otherwise nil. 33 | func (j *jsonRW) Write(v interface{}, w io.Writer) error { 34 | //only utf-8 charset is supported 35 | var escapeHtml = false 36 | var prettyPrint = false 37 | if j.options != nil { 38 | if v, ok := j.options[JsonEscapeHTML]; ok { 39 | escapeHtml = v.(bool) 40 | } 41 | 42 | if v, ok := j.options[PrettyPrint]; ok { 43 | prettyPrint = v.(bool) 44 | } 45 | 46 | } 47 | encoder := json.NewEncoder(w) 48 | if prettyPrint { 49 | encoder.SetIndent(jsonPrettyPrintPrefix, jsonPrettyPrintIndent) 50 | } 51 | encoder.SetEscapeHTML(escapeHtml) 52 | return encoder.Encode(v) 53 | 54 | } 55 | 56 | // Read reads JSON-encoded data from the provided io.Reader and decodes it into the specified interface{}. 57 | // It returns an error if the decoding process fails. 58 | // 59 | // Parameters: 60 | // 61 | // r - the io.Reader to read the JSON data from 62 | // v - the interface{} to decode the JSON data into 63 | // 64 | // Returns: 65 | // 66 | // error - an error if the decoding process fails, or nil if successful 67 | func (j *jsonRW) Read(r io.Reader, v interface{}) error { 68 | decoder := json.NewDecoder(r) 69 | return decoder.Decode(v) 70 | } 71 | 72 | // MimeTypes returns a slice of strings representing the MIME types 73 | // that are supported by the jsonRW codec. 74 | func (j *jsonRW) MimeTypes() []string { 75 | return jsonmimeTypes 76 | } 77 | -------------------------------------------------------------------------------- /collections/list.go: -------------------------------------------------------------------------------- 1 | // Package collections provides a collection of generic data structures. 2 | 3 | package collections 4 | 5 | // List is a generic interface that defines a collection of elements with various methods to manipulate them. 6 | // The List interface uses a type parameter T to represent the type of elements stored in the list. 7 | // It provides the following methods: 8 | // 9 | // Add(elem T): 10 | // Adds an element to the list. 11 | // 12 | // Clear(): 13 | // Removes all elements from the list. 14 | // 15 | // Contains(elem T) bool: 16 | // Checks if an element is in the list. Returns true if the element is found, otherwise false. 17 | // 18 | // Get(index int) T: 19 | // Returns the element at the specified index. The index is zero-based. 20 | // 21 | // Iterator() Iterator[T]: 22 | // Returns an Iterator for the list, which can be used to traverse the elements. 23 | // 24 | // IndexOf(elem T) int: 25 | // Returns the index of the specified element. If the element is not found, it returns -1. 26 | // 27 | // Remove(elem T) bool: 28 | // Removes an element from the list. Returns true if the element was successfully removed, otherwise false. 29 | // 30 | // Size() int: 31 | // Returns the number of elements in the list. 32 | 33 | type List[T any] interface { 34 | Collection[T] 35 | // AddAt adds an element at the specified index 36 | AddAt(index int, elem T) error 37 | // AddFirst adds an element at the beginning of the list 38 | AddFirst(elem T) error 39 | // AddLast adds an element at the end of the list 40 | AddLast(elem T) error 41 | // Clear removes all elements from the list 42 | // Get returns the element at the specified index 43 | Get(index int) (T, error) 44 | // GetFirst returns the first element in the list 45 | GetFirst() (T, error) 46 | // GetLast returns the last element in the list 47 | GetLast() (T, error) 48 | // IndexOf returns the index of the specified element 49 | IndexOf(elem T) int 50 | // IsEmpty checks if the list is empty 51 | IsEmpty() bool 52 | // LastIndexOf returns the last index of the specified element 53 | LastIndexOf(elem T) int 54 | // RemoveAt removes the element at the specified index 55 | RemoveAt(index int) (T, error) 56 | // RemoveFirst removes the first element from the list 57 | RemoveFirst() (T, error) 58 | // RemoveLast removes the last element from the list 59 | RemoveLast() (T, error) 60 | } 61 | -------------------------------------------------------------------------------- /l3/filewriter.go: -------------------------------------------------------------------------------- 1 | package l3 2 | 3 | import ( 4 | "io" 5 | "os" 6 | 7 | "oss.nandlabs.io/golly/textutils" 8 | ) 9 | 10 | // FileWriter struct 11 | type FileWriter struct { 12 | errorWriter, warnWriter, infoWriter, debugWriter, traceWriter *os.File 13 | } 14 | 15 | // InitConfig FileWriter 16 | func (fw *FileWriter) InitConfig(w *WriterConfig) { 17 | 18 | var defaultWriter *os.File 19 | if w.File.DefaultPath != textutils.EmptyStr { 20 | defaultWriter, _ = os.OpenFile(w.File.DefaultPath, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0644) 21 | } 22 | if w.File.ErrorPath != textutils.EmptyStr { 23 | writeLog(os.Stderr, w.File.ErrorPath) 24 | 25 | fw.errorWriter, _ = os.OpenFile(w.File.ErrorPath, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0644) 26 | } 27 | if w.File.WarnPath != textutils.EmptyStr { 28 | fw.warnWriter, _ = os.OpenFile(w.File.WarnPath, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0644) 29 | } 30 | if w.File.InfoPath != textutils.EmptyStr { 31 | fw.infoWriter, _ = os.OpenFile(w.File.InfoPath, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0644) 32 | } 33 | if w.File.DebugPath != textutils.EmptyStr { 34 | fw.debugWriter, _ = os.OpenFile(w.File.DebugPath, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0644) 35 | } 36 | if w.File.TracePath != textutils.EmptyStr { 37 | fw.traceWriter, _ = os.OpenFile(w.File.TracePath, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0644) 38 | } 39 | if defaultWriter != nil { 40 | if fw.errorWriter == nil { 41 | fw.errorWriter = defaultWriter 42 | } 43 | if fw.warnWriter == nil { 44 | fw.warnWriter = defaultWriter 45 | } 46 | if fw.infoWriter == nil { 47 | fw.infoWriter = defaultWriter 48 | } 49 | if fw.debugWriter == nil { 50 | fw.debugWriter = defaultWriter 51 | } 52 | if fw.traceWriter == nil { 53 | fw.traceWriter = defaultWriter 54 | } 55 | } 56 | } 57 | 58 | // DoLog FileWriter 59 | func (fw *FileWriter) DoLog(logMsg *LogMessage) { 60 | var writer io.Writer 61 | switch logMsg.Level { 62 | case Off: 63 | return 64 | case Err: 65 | writer = fw.errorWriter 66 | case Warn: 67 | writer = fw.warnWriter 68 | case Info: 69 | writer = fw.infoWriter 70 | case Debug: 71 | writer = fw.debugWriter 72 | case Trace: 73 | writer = fw.traceWriter 74 | } 75 | 76 | if writer != nil { 77 | writeLogMsg(writer, logMsg) 78 | } 79 | } 80 | 81 | // Close stream 82 | func (fw *FileWriter) Close() error { 83 | return fw.debugWriter.Close() 84 | } 85 | -------------------------------------------------------------------------------- /errutils/errutils.go: -------------------------------------------------------------------------------- 1 | package errutils 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strings" 7 | "sync" 8 | 9 | "oss.nandlabs.io/golly/textutils" 10 | ) 11 | 12 | type MultiError struct { 13 | errs []error 14 | mutex sync.Mutex 15 | } 16 | 17 | // Add adds an error to the MultiError. If the error is nil, it is not added. 18 | func (m *MultiError) Add(err error) { 19 | m.mutex.Lock() 20 | defer m.mutex.Unlock() 21 | if err != nil { 22 | m.errs = append(m.errs, err) 23 | } 24 | } 25 | 26 | // GetAll returns all the errors in the MultiError. 27 | func (m *MultiError) GetAll() (errs []error) { 28 | errs = m.errs 29 | return 30 | } 31 | 32 | // Error function implements the error.Error function of the error interface 33 | func (m *MultiError) Error() string { 34 | m.mutex.Lock() 35 | defer m.mutex.Unlock() 36 | var sb strings.Builder 37 | if m.errs != nil { 38 | for i, e := range m.errs { 39 | if i != 0 { 40 | sb.WriteString(textutils.NewLineString) 41 | } 42 | sb.WriteString(e.Error()) 43 | } 44 | } 45 | 46 | return sb.String() 47 | } 48 | 49 | // HasErrors will return true if the MultiError has any errors 50 | func (m *MultiError) HasErrors() bool { 51 | m.mutex.Lock() 52 | defer m.mutex.Unlock() 53 | return len(m.errs) > 0 54 | } 55 | 56 | // HasError will return true if the MultiError has any errors of the specified type 57 | func (m *MultiError) HasError(err error) bool { 58 | m.mutex.Lock() 59 | defer m.mutex.Unlock() 60 | for _, e := range m.errs { 61 | if errors.Is(e, err) { 62 | return true 63 | } 64 | } 65 | return false 66 | } 67 | 68 | // NewMultiErr creates a new MultiError and adds the given error to it. 69 | func NewMultiErr(err error) (multiErr *MultiError) { 70 | multiErr = &MultiError{} 71 | if err != nil { 72 | multiErr.Add(err) 73 | } 74 | return 75 | } 76 | 77 | // CustomError is a struct that holds a template for creating custom errors. 78 | // The template is a string that can contain format verbs. 79 | type CustomError struct { 80 | template string 81 | } 82 | 83 | // Err creates a new error using the template and the parameters. 84 | func (e *CustomError) Err(params ...any) error { 85 | return fmt.Errorf(e.template, params...) 86 | } 87 | 88 | // NewCustomError creates a new CustomError with the given template. 89 | func NewCustomError(template string) *CustomError { 90 | return &CustomError{template: template} 91 | } 92 | -------------------------------------------------------------------------------- /collections/queue_test.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import ( 4 | "testing" 5 | 6 | "oss.nandlabs.io/golly/testing/assert" 7 | ) 8 | 9 | func TestArrayQueue_Enqueue(t *testing.T) { 10 | queue := NewArrayQueue[int]() 11 | err := queue.Enqueue(1) 12 | assert.Nil(t, err) 13 | assert.Equal(t, 1, queue.Size()) 14 | assert.False(t, queue.IsEmpty()) 15 | } 16 | 17 | func TestArrayQueue_Dequeue(t *testing.T) { 18 | queue := NewArrayQueue[int]() 19 | queue.Enqueue(1) 20 | queue.Enqueue(2) 21 | 22 | val, err := queue.Dequeue() 23 | assert.Nil(t, err) 24 | assert.Equal(t, 1, val) 25 | assert.Equal(t, 1, queue.Size()) 26 | 27 | val, err = queue.Dequeue() 28 | assert.Nil(t, err) 29 | assert.Equal(t, 2, val) 30 | assert.Equal(t, 0, queue.Size()) 31 | assert.True(t, queue.IsEmpty()) 32 | 33 | _, err = queue.Dequeue() 34 | assert.NotNil(t, err) 35 | } 36 | 37 | func TestArrayQueue_Front(t *testing.T) { 38 | queue := NewArrayQueue[int]() 39 | queue.Enqueue(1) 40 | queue.Enqueue(2) 41 | 42 | val, err := queue.Front() 43 | assert.Nil(t, err) 44 | assert.Equal(t, 1, val) 45 | 46 | queue.Dequeue() 47 | val, err = queue.Front() 48 | assert.Nil(t, err) 49 | assert.Equal(t, 2, val) 50 | 51 | queue.Dequeue() 52 | _, err = queue.Front() 53 | assert.NotNil(t, err) 54 | } 55 | 56 | func TestSyncQueue_Enqueue(t *testing.T) { 57 | queue := NewSyncQueue[int]() 58 | err := queue.Enqueue(1) 59 | assert.Nil(t, err) 60 | assert.Equal(t, 1, queue.Size()) 61 | assert.False(t, queue.IsEmpty()) 62 | } 63 | 64 | func TestSyncQueue_Dequeue(t *testing.T) { 65 | queue := NewSyncQueue[int]() 66 | queue.Enqueue(1) 67 | queue.Enqueue(2) 68 | 69 | val, err := queue.Dequeue() 70 | assert.Nil(t, err) 71 | assert.Equal(t, 1, val) 72 | assert.Equal(t, 1, queue.Size()) 73 | 74 | val, err = queue.Dequeue() 75 | assert.Nil(t, err) 76 | assert.Equal(t, 2, val) 77 | assert.Equal(t, 0, queue.Size()) 78 | assert.True(t, queue.IsEmpty()) 79 | 80 | _, err = queue.Dequeue() 81 | assert.NotNil(t, err) 82 | } 83 | 84 | func TestSyncQueue_Front(t *testing.T) { 85 | queue := NewSyncQueue[int]() 86 | queue.Enqueue(1) 87 | queue.Enqueue(2) 88 | 89 | val, err := queue.Front() 90 | assert.Nil(t, err) 91 | assert.Equal(t, 1, val) 92 | 93 | queue.Dequeue() 94 | val, err = queue.Front() 95 | assert.Nil(t, err) 96 | assert.Equal(t, 2, val) 97 | 98 | queue.Dequeue() 99 | _, err = queue.Front() 100 | assert.NotNil(t, err) 101 | } 102 | -------------------------------------------------------------------------------- /collections/collections.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | // ErrEmptyCollection is an error that is returned when a collection is empty 10 | var ErrEmptyCollection error = errors.New("collection is empty") 11 | 12 | // ErrFullCollection is an error that is returned when a collection is full 13 | var ErrFullCollection error = errors.New("collection is full") 14 | 15 | // ErrElementNotFound is an error that is returned when an element is not found in the collection 16 | var ErrElementNotFound error = errors.New("element not found") 17 | 18 | // ErrIndexOutOfBounds is an error that is returned when an index is out of bounds 19 | var ErrIndexOutOfBounds error = errors.New("index out of bounds") 20 | 21 | // ErrInvalidCapacity is an error that is returned when an invalid capacity is specified 22 | var ErrInvalidCapacity error = errors.New("invalid capacity") 23 | 24 | // ErrInvalidIndex is an error that is returned when an invalid index is specified 25 | var ErrInvalidIndex error = errors.New("invalid index") 26 | 27 | //Collection is a generic interface that defines a collection of elements with various methods to manipulate them. 28 | //The Collection interface uses a type parameter T to represent the type of elements stored in the collection. 29 | 30 | type Collection[T any] interface { 31 | //Include Iterable[T] 32 | Iterable[T] 33 | // Include Stringer 34 | fmt.Stringer 35 | 36 | // Add an element to the collection 37 | Add(elem T) error 38 | // AddAll adds all elements from another collection to this collection 39 | AddAll(coll Collection[T]) error 40 | // Clear removes all elements from the collection 41 | Clear() 42 | // Contains checks if an element is in the collection 43 | Contains(elem T) bool 44 | // Return true if the collection is empty 45 | IsEmpty() bool 46 | // Remove an element from the collection 47 | Remove(elem T) bool 48 | // Return the number of elements in the collection 49 | Size() int 50 | } 51 | 52 | // BoundCollection is a generic interface that defines a bounded collection of elements with various methods to manipulate them. 53 | // The BoundCollection interface uses a type parameter T to represent the type of elements stored in the collection. 54 | 55 | type BoundCollection[T any] interface { 56 | Collection[T] 57 | // Return the maximum capacity of the collection 58 | Capacity() int 59 | // Offer adds an element to the collection if it is not full 60 | Offer(elem T, time time.Duration) bool 61 | // OfferAndWait adds an element to the collection if it is not full, blocking until space is available 62 | OfferAndWait(elem T) 63 | } 64 | -------------------------------------------------------------------------------- /rest/client_request_test.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "net/http" 5 | "testing" 6 | 7 | "oss.nandlabs.io/golly/testing/assert" 8 | ) 9 | 10 | func TestRequest_AddFormData(t *testing.T) { 11 | req := &Request{} 12 | req.AddFormData("key1", "value1", "value2") 13 | 14 | if req.formData.Get("key1") != "value1" { 15 | t.Errorf("Expected key1 to be value1, got %s", req.formData.Get("key1")) 16 | } 17 | 18 | values := req.formData["key1"] 19 | if len(values) != 2 || values[1] != "value2" { 20 | t.Errorf("Expected key1 to have two values, got %v", values) 21 | } 22 | } 23 | 24 | func TestRequest_AddQueryParam(t *testing.T) { 25 | req := &Request{} 26 | req.AddQueryParam("key1", "value1", "value2") 27 | 28 | if req.queryParam.Get("key1") != "value1" { 29 | t.Errorf("Expected key1 to be value1, got %s", req.queryParam.Get("key1")) 30 | } 31 | 32 | values := req.queryParam["key1"] 33 | if len(values) != 2 || values[1] != "value2" { 34 | t.Errorf("Expected key1 to have two values, got %v", values) 35 | } 36 | } 37 | 38 | func TestRequest_AddPathParam(t *testing.T) { 39 | req := &Request{} 40 | req.AddPathParam("key1", "value1") 41 | 42 | if req.pathParams["key1"] != "value1" { 43 | t.Errorf("Expected key1 to be value1, got %s", req.pathParams["key1"]) 44 | } 45 | } 46 | 47 | func TestRequest_AddHeader(t *testing.T) { 48 | req := &Request{header: http.Header{}} 49 | req.AddHeader("Content-Type", "application/json") 50 | 51 | if req.header.Get("Content-Type") != "application/json" { 52 | t.Errorf("Expected Content-Type to be application/json, got %s", req.header.Get("Content-Type")) 53 | } 54 | } 55 | 56 | func TestRequest_SetBody(t *testing.T) { 57 | req := &Request{} 58 | body := map[string]string{"key": "value"} 59 | req.SetBody(body) 60 | 61 | assert.Equal(t, body, req.body) 62 | } 63 | 64 | func TestRequest_SetContentType(t *testing.T) { 65 | req := &Request{} 66 | req.SetContentType("application/json") 67 | 68 | if req.contentType != "application/json" { 69 | t.Errorf("Expected contentType to be application/json, got %s", req.contentType) 70 | } 71 | } 72 | 73 | func TestRequest_SetMultipartFiles(t *testing.T) { 74 | req := &Request{} 75 | files := []*MultipartFile{ 76 | {ParamName: "file1", FilePath: "path/to/file1"}, 77 | {ParamName: "file2", FilePath: "path/to/file2"}, 78 | } 79 | req.SetMultipartFiles(files...) 80 | 81 | if len(req.multiPartFiles) != 2 { 82 | t.Errorf("Expected 2 multipart files, got %d", len(req.multiPartFiles)) 83 | } 84 | if req.multiPartFiles[0].ParamName != "file1" || req.multiPartFiles[1].ParamName != "file2" { 85 | t.Errorf("Multipart files not set correctly") 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /vfs/localfile.go: -------------------------------------------------------------------------------- 1 | package vfs 2 | 3 | import ( 4 | "fmt" 5 | "io/fs" 6 | "net/url" 7 | "os" 8 | "path/filepath" 9 | 10 | "oss.nandlabs.io/golly/fsutils" 11 | ) 12 | 13 | type OsFile struct { 14 | *BaseFile 15 | file *os.File 16 | Location *url.URL 17 | fs VFileSystem 18 | } 19 | 20 | func (o *OsFile) Close() error { 21 | return o.file.Close() 22 | } 23 | 24 | func (o *OsFile) Read(b []byte) (int, error) { 25 | return o.file.Read(b) 26 | } 27 | 28 | func (o *OsFile) Write(b []byte) (int, error) { 29 | return o.file.Write(b) 30 | } 31 | 32 | func (o *OsFile) Seek(offset int64, whence int) (int64, error) { 33 | return o.file.Seek(offset, whence) 34 | } 35 | 36 | func (o *OsFile) ContentType() string { 37 | return fsutils.LookupContentType(o.Location.Path) 38 | } 39 | 40 | func (o *OsFile) ListAll() (files []VFile, err error) { 41 | manager := GetManager() 42 | var children []VFile 43 | err = filepath.WalkDir(o.Location.Path, visit(manager, &children)) 44 | if err == nil { 45 | files = children 46 | } 47 | return 48 | } 49 | 50 | func visit(manager Manager, paths *[]VFile) func(string, os.DirEntry, error) (err error) { 51 | return func(path string, info os.DirEntry, err2 error) (err error) { 52 | if err2 != nil { 53 | return 54 | } 55 | if !info.IsDir() { 56 | var child VFile 57 | child, err = manager.OpenRaw(path) 58 | *paths = append(*paths, child) 59 | } 60 | return 61 | } 62 | } 63 | 64 | func (o *OsFile) Delete() error { 65 | return os.Remove(o.Location.Path) 66 | } 67 | 68 | func (o *OsFile) DeleteAll() error { 69 | return os.RemoveAll(o.Location.Path) 70 | } 71 | 72 | func (o *OsFile) Info() (VFileInfo, error) { 73 | return o.file.Stat() 74 | } 75 | 76 | func (o *OsFile) Parent() (file VFile, err error) { 77 | var dirEntries []fs.DirEntry 78 | dirEntries, err = os.ReadDir(o.Location.Path) 79 | if err == nil { 80 | for _, info := range dirEntries { 81 | var f *os.File 82 | var u *url.URL 83 | u, _ = o.Location.Parse("../" + info.Name()) 84 | f, err = os.Open(u.Path) 85 | if err == nil { 86 | file = &OsFile{ 87 | file: f, 88 | Location: u, 89 | } 90 | } 91 | } 92 | } 93 | return 94 | } 95 | 96 | func (o *OsFile) Url() *url.URL { 97 | return o.Location 98 | } 99 | 100 | func (o *OsFile) AddProperty(name string, value string) error { 101 | return fmt.Errorf("unsupported operation AddProperty for scheme") 102 | } 103 | 104 | func (o *OsFile) GetProperty(name string) (v string, err error) { 105 | err = fmt.Errorf("unsupported operation GetProperty for scheme") 106 | return 107 | } 108 | -------------------------------------------------------------------------------- /cli/usage.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // printUsage prints the usage information for the CLI tool. 9 | func (cli *CLI) printUsage() { 10 | fmt.Println("\nCLI Tool Usage:") 11 | fmt.Println("Usage: [command] [subcommand] [flags]") 12 | fmt.Println("\nGlobal Flags:") 13 | fmt.Println(" -h, --help Show help for the CLI tool") 14 | fmt.Println(" -v, --version Show the version of the CLI tool") 15 | 16 | fmt.Println("\nAvailable Commands:") 17 | for _, cmd := range cli.rootCommands { 18 | cli.printCommandHelp(cmd, 1) 19 | } 20 | fmt.Println() 21 | } 22 | 23 | // printCommandHelp prints the help information for a specific command. 24 | func (cli *CLI) printCommandHelp(cmd *Command, indent int) { 25 | indentation := strings.Repeat(" ", indent) 26 | fmt.Printf("%s%s: %s\n", indentation, cmd.Name, cmd.Usage) 27 | 28 | if len(cmd.Aliases) > 0 { 29 | fmt.Printf("%sAliases: %s\n", indentation, strings.Join(cmd.Aliases, ", ")) 30 | } 31 | 32 | if len(cmd.Flags) > 0 { 33 | fmt.Printf("%sFlags:\n", indentation) 34 | for _, flag := range cmd.Flags { 35 | aliases := "" 36 | if len(flag.Aliases) > 0 { 37 | aliases = fmt.Sprintf("-%s, ", strings.Join(flag.Aliases, ", -")) 38 | } 39 | fmt.Printf("%s %s--%s value\t%s (default: %s)\n", 40 | indentation, 41 | aliases, 42 | flag.Name, 43 | flag.Usage, 44 | flag.Default) 45 | } 46 | } 47 | 48 | if len(cmd.SubCommands) > 0 { 49 | fmt.Printf("%sSubcommands:\n", indentation) 50 | for _, subCmd := range cmd.SubCommands { 51 | cli.printCommandHelp(subCmd, indent+1) 52 | } 53 | } 54 | } 55 | 56 | // printDetailedHelp prints the detailed help information for a specific command. 57 | func (cli *CLI) printDetailedHelp(commandStack []string, cmd *Command) { 58 | fmt.Printf("\nHelp for Command: %s\n", strings.Join(commandStack, " ")) 59 | fmt.Printf("\n%s: %s\n", cmd.Name, cmd.Usage) 60 | fmt.Println("\nUsage:") 61 | fmt.Printf(" %s [flags]\n", strings.Join(commandStack, " ")) 62 | 63 | if len(cmd.Aliases) > 0 { 64 | fmt.Printf("\nAliases: %s\n", strings.Join(cmd.Aliases, ", ")) 65 | } 66 | 67 | if len(cmd.Flags) > 0 { 68 | fmt.Println("\nFlags:") 69 | for _, flag := range cmd.Flags { 70 | aliases := "" 71 | if len(flag.Aliases) > 0 { 72 | aliases = fmt.Sprintf("-%s, ", strings.Join(flag.Aliases, ", -")) 73 | } 74 | fmt.Printf("%s--%s value\t%s (default: %s)\n", 75 | aliases, 76 | flag.Name, 77 | flag.Usage, 78 | flag.Default) 79 | } 80 | } 81 | 82 | if len(cmd.SubCommands) > 0 { 83 | fmt.Println("\nSubcommands:") 84 | for _, subCmd := range cmd.SubCommands { 85 | fmt.Printf(" %s: %s\n", subCmd.Name, subCmd.Usage) 86 | } 87 | } 88 | fmt.Println() 89 | } 90 | -------------------------------------------------------------------------------- /uuid/generator_test.go: -------------------------------------------------------------------------------- 1 | package uuid 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | // TestUUID_Bytes tests the Bytes method of the UUID struct. 9 | // It verifies that the Bytes method returns the correct byte slice. 10 | func TestUUID_Bytes(t *testing.T) { 11 | u := &UUID{bytes: []byte{1, 2, 3, 4}} 12 | want := []byte{1, 2, 3, 4} 13 | if got := u.Bytes(); !reflect.DeepEqual(got, want) { 14 | t.Errorf("Bytes() = %v, want %v", got, want) 15 | } 16 | } 17 | 18 | // TestUUID_String tests the String method of the UUID struct. 19 | // It verifies that the String method returns the correct string representation of the UUID. 20 | func TestUUID_String(t *testing.T) { 21 | u := &UUID{bytes: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}} 22 | want := "01020304-0506-0708-090a-0b0c0d0e0f10" 23 | if got := u.String(); got != want { 24 | t.Errorf("String() = %v, want %v", got, want) 25 | } 26 | } 27 | 28 | // TestV1 tests the V1 function. 29 | // It verifies that the V1 function generates a valid UUID. 30 | func TestV1(t *testing.T) { 31 | u, err := V1() 32 | if err != nil { 33 | t.Errorf("V1() error = %v", err) 34 | } 35 | if len(u.Bytes()) != 16 { 36 | t.Errorf("V1() generated invalid UUID") 37 | } 38 | } 39 | 40 | // TestV2 tests the V2 function. 41 | // It verifies that the V2 function generates a valid UUID. 42 | func TestV2(t *testing.T) { 43 | u, err := V2() 44 | if err != nil { 45 | t.Errorf("V2() error = %v", err) 46 | } 47 | if len(u.Bytes()) != 16 { 48 | t.Errorf("V2() generated invalid UUID") 49 | } 50 | } 51 | 52 | // TestV3 tests the V3 function. 53 | // It verifies that the V3 function generates a valid UUID. 54 | func TestV3(t *testing.T) { 55 | u, err := V3("namespace", "name") 56 | 57 | if err != nil { 58 | t.Errorf("V3() error = %v", err) 59 | } 60 | if len(u.Bytes()) != 16 { 61 | t.Errorf("V3() generated invalid UUID") 62 | } 63 | } 64 | 65 | // TestV4 tests the V4 function. 66 | // It verifies that the V4 function generates a valid UUID. 67 | func TestV4(t *testing.T) { 68 | u, err := V4() 69 | if err != nil { 70 | t.Errorf("V4() error = %v", err) 71 | } 72 | if len(u.Bytes()) != 16 { 73 | t.Errorf("V4() generated invalid UUID") 74 | } 75 | } 76 | 77 | // TestParseUUID tests the ParseUUID function. 78 | // It verifies that the function correctly parses a UUID string and returns the expected byte slice. 79 | func TestParseUUID(t *testing.T) { 80 | s := "01020304-0506-0708-090a-0b0c0d0e0f10" 81 | want := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} 82 | u, err := ParseUUID(s) 83 | if err != nil { 84 | t.Errorf("ParseUUID() error = %v", err) 85 | } 86 | if !reflect.DeepEqual(u.Bytes(), want) { 87 | t.Errorf("ParseUUID() = %v, want %v", u.Bytes(), want) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /config/configuration.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "io" 4 | 5 | // Configuration is an interface that wraps the methods for a standard configuration. 6 | 7 | type Configuration interface { 8 | 9 | //Load a reader from Reader 10 | Load(r io.Reader) error 11 | //Save to a writer 12 | Save(w io.Writer) error 13 | //Get returns configuration value as string identified by the key 14 | //If the value is absent then it will return defaultVal supplied. 15 | Get(k, defaultVal string) string 16 | //GetAsInt returns the config value as int64 identified by the key 17 | //If the value is absent then it will return defaultVal supplied. 18 | //This may throw an error if a non Int value is present for the key 19 | GetAsInt(k string, defaultVal int) (int, error) 20 | //GetAsInt64 returns the config value as int64 identified by the key 21 | //If the value is absent then it will return defaultVal supplied. 22 | //This may throw an error if a non int64 value is present for the key 23 | GetAsInt64(k string, defaultVal int64) (int64, error) 24 | //GetAsBool returns the config value as bool identified by the key 25 | //If the value is absent then it will return defaultVal supplied. 26 | //This may throw an error if a non bool value is present for the key 27 | GetAsBool(k string, defaultVal bool) (bool, error) 28 | //GetAsDecimal returns the config value as decimal float64 identified by the key 29 | //If the value is absent then it will return defaultVal supplied. 30 | //This may throw an error if a non float64 value is present for the key 31 | GetAsDecimal(k string, defaultVal float64) (float64, error) 32 | //Put returns configuration value as string identified by the key 33 | //If the value is absent then it will return defaultVal supplied. 34 | Put(k, v string) string 35 | //PutInt returns the config value as int64 identified by the key 36 | //If the value is absent then it will return defaultVal supplied. 37 | //This may throw an error if a non Int value is present for the key 38 | PutInt(k string, v int) (int, error) 39 | //PutInt64 returns the config value as int64 identified by the key 40 | //If the value is absent then it will return defaultVal supplied. 41 | //This may throw an error if a non int64 value is present for the key 42 | PutInt64(k string, v int64) (int64, error) 43 | //PutBool returns the config value as bool identified by the key 44 | //If the value is absent then it will return defaultVal supplied. 45 | //This may throw an error if a non bool value is present for the key 46 | PutBool(k string, v bool) (bool, error) 47 | //PutDecimal returns the config value as decimal float64 identified by the key 48 | //If the value is absent then it will return defaultVal supplied. 49 | //This may throw an error if a non float64 value is present for the key 50 | PutDecimal(k string, v float64) (float64, error) 51 | } 52 | -------------------------------------------------------------------------------- /cli/command_test.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestNewCommand(t *testing.T) { 9 | handler := func(ctx *Context) error { return nil } 10 | cmd := NewCommand("test", "A test command", "v0.0.1", handler) 11 | 12 | if cmd.Name != "test" { 13 | t.Errorf("Expected Name to be 'test', got '%s'", cmd.Name) 14 | } 15 | if cmd.Usage != "A test command" { 16 | t.Errorf("Expected Description to be 'A test command', got '%s'", cmd.Usage) 17 | } 18 | if cmd.Action == nil { 19 | t.Error("Expected Handler to be non-nil") 20 | } 21 | if len(cmd.SubCommands) != 0 { 22 | t.Errorf("Expected SubCommands to be empty, got %d", len(cmd.SubCommands)) 23 | } 24 | if len(cmd.Flags) != 0 { 25 | t.Errorf("Expected Flags to be empty, got %d", len(cmd.Flags)) 26 | } 27 | } 28 | 29 | func TestAddSubCommand(t *testing.T) { 30 | parentCmd := NewCommand("parent", "Parent command", "v0.0.1", nil) 31 | subCmd := NewCommand("child", "Child command", "v0.0.1", nil) 32 | parentCmd.AddSubCommand(subCmd) 33 | 34 | if len(parentCmd.SubCommands) != 1 { 35 | t.Errorf("Expected 1 SubCommand, got %d", len(parentCmd.SubCommands)) 36 | } 37 | if parentCmd.SubCommands["child"] != subCmd { 38 | t.Error("SubCommand 'child' not added correctly") 39 | } 40 | } 41 | 42 | func TestPrintCommandHelp(t *testing.T) { 43 | cli := NewCLI() 44 | cmd := NewCommand("test", "Test command", "v0.0.1", nil) 45 | cmd.Flags = []*Flag{ 46 | { 47 | Name: "attach", 48 | Aliases: []string{"a"}, 49 | Usage: "Attach to STDIN, STDOUT or STDERR", 50 | Default: "[]", 51 | }, 52 | } 53 | 54 | cmd.AddSubCommand(NewCommand("subcmd", "A subcommand", "v0.0.1", nil)) 55 | 56 | cli.printCommandHelp(cmd, 0) 57 | } 58 | 59 | func TestPrintDetailedCommandHelp(t *testing.T) { 60 | cli := NewCLI() 61 | server := &Command{ 62 | Name: "server", 63 | Usage: "Server command", 64 | Action: func(ctx *Context) error { 65 | region, _ := ctx.GetFlag("region") 66 | fmt.Printf("IN REGION, %s\n", region) 67 | return nil 68 | }, 69 | Flags: []*Flag{ 70 | { 71 | Name: "region", 72 | Aliases: []string{"r"}, 73 | Usage: "Provide region", 74 | Default: "us-east-1", 75 | }, 76 | }, 77 | SubCommands: map[string]*Command{ 78 | "create": { 79 | Name: "create", 80 | Usage: "create", 81 | Action: func(ctx *Context) error { 82 | typ, _ := ctx.GetFlag("type") 83 | fmt.Printf("SERVER TYPE %s\n", typ) 84 | return nil 85 | }, 86 | Flags: []*Flag{ 87 | { 88 | Name: "type", 89 | Aliases: []string{"t"}, 90 | Usage: "server type", 91 | Default: "t2.micro", 92 | }, 93 | }, 94 | }, 95 | }, 96 | } 97 | 98 | cli.AddCommand(server) 99 | 100 | cli.printUsage() 101 | 102 | cli.printDetailedHelp([]string{"server", "create"}, server) 103 | } 104 | -------------------------------------------------------------------------------- /messaging/options.go: -------------------------------------------------------------------------------- 1 | package messaging 2 | 3 | import "oss.nandlabs.io/golly/clients" 4 | 5 | const ( 6 | CircuitBreakerOpts = "CircuitBreakerOption" 7 | RetryOpts = "CircuitBreakerOption" 8 | NamedListener = "NamedListener" 9 | ) 10 | 11 | type Option struct { 12 | Key string 13 | Value interface{} 14 | } 15 | 16 | type OptionsBuilder struct { 17 | options []Option 18 | } 19 | 20 | type OptionsResolver struct { 21 | opts map[string]interface{} 22 | } 23 | 24 | func NewOptionsResolver(options ...Option) (optsResolver *OptionsResolver) { 25 | optsResolver = &OptionsResolver{opts: make(map[string]interface{})} 26 | 27 | if len(options) > 0 { 28 | for _, option := range options { 29 | optsResolver.opts[option.Key] = option.Value 30 | } 31 | } 32 | return 33 | } 34 | 35 | // TODO check if we can pool this for performance 36 | func NewOptionsBuilder() *OptionsBuilder { 37 | return &OptionsBuilder{} 38 | } 39 | 40 | // TODO check if you need to pool this for performance 41 | func (ob *OptionsBuilder) Add(key string, value interface{}) *OptionsBuilder { 42 | ob.options = append(ob.options, Option{ 43 | Key: key, 44 | Value: value, 45 | }) 46 | return ob 47 | } 48 | 49 | func (ob *OptionsBuilder) Build() []Option { 50 | return ob.options 51 | } 52 | 53 | func (ob *OptionsBuilder) AddCircuitBreaker(failureThreshold, successThreshold uint64, maxHalfOpen, 54 | timeout uint32) *OptionsBuilder { 55 | breakerInfo := &clients.BreakerInfo{ 56 | FailureThreshold: failureThreshold, 57 | SuccessThreshold: successThreshold, 58 | MaxHalfOpen: maxHalfOpen, 59 | Timeout: timeout, 60 | } 61 | return ob.Add(CircuitBreakerOpts, breakerInfo) 62 | } 63 | 64 | func (ob *OptionsBuilder) AddRetryHandler(maxRetries, wait int) *OptionsBuilder { 65 | retryInfo := &clients.RetryInfo{ 66 | MaxRetries: maxRetries, 67 | Wait: wait, 68 | } 69 | return ob.Add(RetryOpts, retryInfo) 70 | } 71 | 72 | func (ob *OptionsBuilder) AddNamedListener(name string) *OptionsBuilder { 73 | return ob.Add(NamedListener, name) 74 | } 75 | 76 | func GetOptValue[T any](key string, opts ...Option) (value T, has bool) { 77 | defer func() { 78 | if r := recover(); r != nil { 79 | has = false 80 | } 81 | }() 82 | for _, opt := range opts { 83 | if opt.Key == key { 84 | value = opt.Value.(T) 85 | has = true 86 | return 87 | } 88 | } 89 | return 90 | } 91 | 92 | func ResolveOptValue[T any](key string, optionsResolver *OptionsResolver) (value T, has bool) { 93 | defer func() { 94 | if r := recover(); r != nil { 95 | has = false 96 | } 97 | }() 98 | 99 | var val any 100 | val, has = optionsResolver.opts[key] 101 | if has { 102 | value = val.(T) 103 | } 104 | 105 | return 106 | } 107 | 108 | func (or *OptionsResolver) Get(key string) (value interface{}, has bool) { 109 | value, has = or.opts[key] 110 | return 111 | } 112 | -------------------------------------------------------------------------------- /genai/message.go: -------------------------------------------------------------------------------- 1 | package genai 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "net/url" 8 | 9 | "oss.nandlabs.io/golly/ioutils" 10 | ) 11 | 12 | // Message represents a structure that holds information about a message. 13 | type Message struct { 14 | u *url.URL 15 | rwer io.ReadWriter 16 | mimeType string 17 | msgActor Actor 18 | done bool 19 | } 20 | 21 | func NewTextMessage(text string, mimeType string) *Message { 22 | return &Message{ 23 | rwer: bytes.NewBufferString(text), 24 | mimeType: mimeType, 25 | } 26 | } 27 | 28 | func NewBinMessage(data []byte, mimeType string) *Message { 29 | return &Message{ 30 | rwer: bytes.NewBuffer(data), 31 | mimeType: mimeType, 32 | } 33 | } 34 | func NewFileMessage(u string, mimeType string) *Message { 35 | return &Message{ 36 | u: &url.URL{Path: u}, 37 | mimeType: mimeType, 38 | } 39 | } 40 | 41 | // Mime returns the MIME type of the message 42 | 43 | func (m *Message) Mime() string { 44 | return m.mimeType 45 | } 46 | 47 | // Actor returns the actor that sent the message 48 | func (m *Message) Actor() Actor { 49 | return m.msgActor 50 | } 51 | 52 | // Read implements the io.Reader interface 53 | func (m *Message) Read(p []byte) (n int, err error) { 54 | return m.rwer.Read(p) 55 | } 56 | 57 | // ReadFrom implements the io.ReaderFrom interface 58 | func (m *Message) ReadFrom(r io.Reader) (n int64, err error) { 59 | return io.Copy(m.rwer, r) 60 | } 61 | 62 | // SetActor sets the actor that sent the message 63 | func (m *Message) SetActor(actor Actor) { 64 | m.msgActor = actor 65 | } 66 | 67 | // SetMime sets the MIME type of the message 68 | func (m *Message) SetMime(mime string) { 69 | m.mimeType = mime 70 | } 71 | 72 | // Write implements the io.Writer interface 73 | func (m *Message) Write(p []byte) (n int, err error) { 74 | return m.rwer.Write(p) 75 | } 76 | 77 | func (m *Message) WriteTo(w io.Writer) (n int64, err error) { 78 | return io.Copy(w, m.rwer) 79 | } 80 | 81 | // URL returns the URL of the message message 82 | func (b *Message) URL() *url.URL { 83 | return b.u 84 | } 85 | 86 | // IsDone returns true if the message is done 87 | func (m *Message) IsDone() bool { 88 | return m.done 89 | } 90 | 91 | // Done marks the message as done 92 | func (m *Message) Done() { 93 | m.done = true 94 | } 95 | 96 | // String returns the string representation of the message 97 | func (m *Message) String() string { 98 | 99 | switch m.mimeType { 100 | case ioutils.MimeTextPlain, 101 | ioutils.MimeTextHTML, 102 | ioutils.MimeMarkDown, 103 | ioutils.MimeTextYAML, 104 | ioutils.MimeApplicationJSON, 105 | ioutils.MimeApplicationXML, 106 | ioutils.MimeTextXML, 107 | ioutils.MimeTextCSS, 108 | ioutils.MimeTextCSV: 109 | return m.rwer.(*bytes.Buffer).String() 110 | default: 111 | return fmt.Sprintf("{mimeType: %s, actor: %s}", m.mimeType, m.msgActor) 112 | 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /lifecycle/component.go: -------------------------------------------------------------------------------- 1 | package lifecycle 2 | 3 | import "errors" 4 | 5 | type ComponentState int 6 | 7 | const ( 8 | // Unknown is the state of the component when it is not known. 9 | Unknown ComponentState = iota 10 | // Error is the state of the component when it is in error. 11 | Error 12 | // Stopped is the state of the component when it is stopped. 13 | Stopped 14 | //Stopping is the state of the component when it is stopping. 15 | Stopping 16 | // Running is the state of the component when it is running. 17 | Running 18 | // Starting is the state of the component when it is starting. 19 | Starting 20 | ) 21 | 22 | var ErrCompNotFound = errors.New("component not found") 23 | 24 | var ErrCompAlreadyStarted = errors.New("component already started") 25 | 26 | var ErrCompAlreadyStopped = errors.New("component already stopped") 27 | 28 | var ErrInvalidComponentState = errors.New("invalid component state") 29 | 30 | var ErrCyclicDependency = errors.New("cyclic dependency") 31 | 32 | // Component is the interface that wraps the basic Start and Stop methods. 33 | type Component interface { 34 | // Id is the unique identifier for the component. 35 | Id() string 36 | // OnChange is the function that will be called when the component state changes. 37 | // It will be called with the previous state and the new state. 38 | OnChange(f func(prevState, newState ComponentState)) 39 | // Start will starting the LifeCycle. 40 | Start() error 41 | // Stop will stop the LifeCycle. 42 | Stop() error 43 | // State will return the current state of the LifeCycle. 44 | State() ComponentState 45 | } 46 | 47 | // ComponentManager is the interface that manages multiple components. 48 | type ComponentManager interface { 49 | // AddDependency will add a dependency between the two components. 50 | AddDependency(id, dependsOn string) error 51 | // GetState will return the current state of the LifeCycle for the component with the given id. 52 | GetState(id string) ComponentState 53 | //List will return a list of all the Components. 54 | List() []Component 55 | // OnChange is the function that will be called when the component state changes. 56 | OnChange(id string, f func(prevState, newState ComponentState)) 57 | // Register will register a new Components. 58 | Register(component Component) Component 59 | // StartAll will start all the Components. Returns the number of components started 60 | StartAll() error 61 | //StartAndWait will start all the Components and wait for them to finish. 62 | StartAndWait() 63 | // Start will start the LifeCycle for the component with the given id. 64 | // It returns an error if the component was not found or if the component failed to start. 65 | Start(id string) error 66 | // StopAll will stop all the Components. 67 | StopAll() error 68 | // Stop will stop the LifeCycle for the component with the given id. It returns if the component was stopped. 69 | Stop(id string) error 70 | // Unregister will unregister a Component. 71 | Unregister(id string) 72 | // Wait will wait for all the Components to finish. 73 | Wait() 74 | } 75 | -------------------------------------------------------------------------------- /genai/memory.go: -------------------------------------------------------------------------------- 1 | package genai 2 | 3 | import ( 4 | "errors" 5 | 6 | "oss.nandlabs.io/golly/managers" 7 | ) 8 | 9 | const ( 10 | ramMemoryType = "ram" 11 | ramMemoryId = "ram-memory" 12 | ) 13 | 14 | var ErrInvalidSession = errors.New("invalid session") 15 | 16 | // Memory is the interface that represents a generative AI memory 17 | type Memory interface { 18 | // Id returns the id of the memory. This is expected to be unique. 19 | Id() string 20 | // Type returns the type of the memory 21 | Type() string 22 | // Fetch returns the value of the memory 23 | Fetch(sessionId, query string) ([]Exchange, error) 24 | //Last returns the last n message session 25 | Last(sessionId string, n int) ([]Exchange, error) 26 | // Set sets the msg of the memory 27 | Add(sessionId string, exchange Exchange) error 28 | // Erase erases the value of the memory 29 | Erase(query string) error 30 | } 31 | 32 | // MemoryManager is a manager for memories 33 | var MemoryManager managers.ItemManager[Memory] = managers.NewItemManager[Memory]() 34 | 35 | // RamMemory is a memory that stores the data in memory 36 | type RamMemory struct { 37 | data map[string][]Exchange 38 | } 39 | 40 | // NewRamMemory creates a new RamMemory 41 | func NewRamMemory() Memory { 42 | return &RamMemory{ 43 | data: make(map[string][]Exchange), 44 | } 45 | } 46 | 47 | // Id returns the id of the memory 48 | func (r *RamMemory) Id() string { 49 | return ramMemoryId 50 | } 51 | 52 | // Type returns the type of the memory 53 | func (r *RamMemory) Type() string { 54 | return ramMemoryType 55 | } 56 | 57 | // Fetch returns the value of the memory 58 | func (r *RamMemory) Fetch(sessionId, query string) ([]Exchange, error) { 59 | //TODO implement query 60 | if exchanges, ok := r.data[sessionId]; ok { 61 | return exchanges, nil 62 | } else { 63 | return nil, ErrInvalidSession 64 | } 65 | } 66 | 67 | // Last returns the last n message session 68 | func (r *RamMemory) Last(sessionId string, n int) ([]Exchange, error) { 69 | if exchanges, ok := r.data[sessionId]; ok { 70 | if len(exchanges) < n || n <= 0 { 71 | return exchanges, nil 72 | } 73 | return exchanges[len(exchanges)-n:], nil 74 | } else { 75 | return nil, ErrInvalidSession 76 | } 77 | } 78 | 79 | // Set sets the msg of the memory 80 | func (r *RamMemory) Add(sessionId string, exchange Exchange) error { 81 | if _, ok := r.data[sessionId]; !ok { 82 | r.data[sessionId] = []Exchange{} 83 | } 84 | var exchanges = r.data[sessionId] 85 | notFound := true 86 | for i, e := range exchanges { 87 | if e.Id() == exchange.Id() { 88 | // replace the exchange 89 | exchanges[i] = exchange 90 | notFound = false 91 | break 92 | } 93 | } 94 | if notFound { 95 | exchanges = append(exchanges, exchange) 96 | } 97 | r.data[sessionId] = exchanges 98 | return nil 99 | } 100 | 101 | // Erase erases the value of the memory 102 | func (r *RamMemory) Erase(sessionId string) error { 103 | delete(r.data, sessionId) 104 | return nil 105 | } 106 | -------------------------------------------------------------------------------- /codec/validator/README.md: -------------------------------------------------------------------------------- 1 | # go-struct-validator 2 | 3 | The validator is heavily inspired by the OAS Specification approach leading to the creation of structs in a generic manner. 4 | 5 | The validator covers the specifications, and its respective validations according to OAS. 6 | 7 | --- 8 | 9 | - [Installation](#installation) 10 | - [Quick Start Guide](#quick-start-guide) 11 | - [Features](#features) 12 | - [Validations Supported](#validations-supported) 13 | 14 | --- 15 | 16 | ### Installation 17 | 18 | ```bash 19 | go get oss.nandlabs.io/golly/codec/validator 20 | ``` 21 | 22 | ### Quick Start Guide 23 | 24 | It comes with a simple usage as explained below, just import the package, and you are good to go. 25 | 26 | To add check for validations, add the `constraints` tag in the struct fields. 27 | 28 | #### Basic Validations Example 29 | 30 | ```go 31 | type TestStruct struct { 32 | Name string `json:"name" constraints:"min-length=5"` 33 | Age int `json:"age" constraints:"min=21"` 34 | Description string `json:"description" constraints:"max-length=50"` 35 | Cost float64 `json:"cost" constraints:"exclusiveMin=200"` 36 | ItemCount int `json:"itemCount" constraints:"multipleOf=5"` 37 | MobileNumber int `json:"mobile"` // skip validation by not providing any constraints 38 | } 39 | ``` 40 | 41 | #### Basic Example 42 | ```go 43 | package main 44 | 45 | import ( 46 | "fmt" 47 | validator "oss.nandlabs.io/golly/codec/validator" 48 | ) 49 | 50 | type TestStruct struct { 51 | Name string `json:"name" constraints:"min-length=5"` 52 | Age int `json:"age" constraints:"min=21"` 53 | Description string `json:"description" constraints:"max-length=50"` 54 | Cost float64 `json:"cost" constraints:"exclusiveMin=200"` 55 | ItemCount int `json:"itemCount" constraints:"multipleOf=5"` 56 | } 57 | 58 | func main() { 59 | var sv = validator.NewStructValidator() 60 | msg := TestStruct{ 61 | Name: "Test", 62 | Age: 25, 63 | Description: "this is bench testing", 64 | Cost: 299.9, 65 | ItemCount: 2000, 66 | } 67 | 68 | if err := sv.Validate(msg); err != nil { 69 | fmt.Errorf(err) 70 | } 71 | } 72 | ``` 73 | 74 | ### Features 75 | 76 | #### Validations Supported 77 | 78 | | S.No. | Name | Data Type Supported | Status | 79 | |:------|:------------:|---------------------|--------| 80 | | 4 | min | numeric | ✅ | 81 | | 5 | max | numeric | ✅ | 82 | | 6 | exclusiveMin | numeric | ✅ | 83 | | 7 | exclusiveMax | numeric | ✅ | 84 | | 8 | multipleOf | numeric | ✅ | 85 | | 9 | max-length | string | ✅ | 86 | | 10 | min-length | string | ✅ | 87 | | 11 | pattern | string | ✅ | 88 | | 11 | notnull | string | ✅ | 89 | | 12 | enum | all | ✅ | 90 | -------------------------------------------------------------------------------- /errutils/errutils_test.go: -------------------------------------------------------------------------------- 1 | package errutils 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | // TestMultiError_Add tests the Add function of MultiError 10 | func TestMultiError_Add(t *testing.T) { 11 | t.Run("Testing execution of MultiError_Add", func(t *testing.T) { 12 | m := &MultiError{} 13 | err := errors.New("testing error") 14 | m.Add(err) 15 | if len(m.errs) != 1 { 16 | t.Errorf("error not added to MultiError") 17 | } 18 | }) 19 | } 20 | 21 | // TestMultiError_GetAll tests the GetAll function of MultiError 22 | func TestMultiError_GetAll(t *testing.T) { 23 | t.Run("Testing execution of MultiError_GetAll", func(t *testing.T) { 24 | m := &MultiError{} 25 | err := errors.New("testing error") 26 | m.Add(err) 27 | errs := m.GetAll() 28 | if len(errs) != 1 { 29 | t.Errorf("error not added to MultiError") 30 | } 31 | }) 32 | } 33 | 34 | // TestMultiError_Error tests the Error function of MultiError 35 | func TestMultiError_Error(t *testing.T) { 36 | t.Run("Testing execution of MultiError_Error", func(t *testing.T) { 37 | m := &MultiError{} 38 | err := errors.New("testing error") 39 | m.Add(err) 40 | errs := m.Error() 41 | if errs != "testing error" { 42 | t.Errorf("error not added to MultiError") 43 | } 44 | }) 45 | } 46 | 47 | // TestMultiError_HasError tests the HasError function of MultiError 48 | func TestMultiError_HasError(t *testing.T) { 49 | t.Run("Testing execution of MultiError_HasError", func(t *testing.T) { 50 | m := &MultiError{} 51 | err := errors.New("testing error") 52 | m.Add(err) 53 | if !m.HasError(err) { 54 | t.Errorf("error not added to MultiError") 55 | } 56 | }) 57 | } 58 | 59 | // TestNewMultiErr tests the NewMultiErr function of MultiError 60 | func TestNewMultiErr(t *testing.T) { 61 | t.Run("Testing execution of NewMultiErr", func(t *testing.T) { 62 | err := errors.New("testing error") 63 | m := NewMultiErr(err) 64 | if len(m.errs) != 1 { 65 | t.Errorf("error not added to MultiError") 66 | } 67 | }) 68 | } 69 | 70 | // TestMultiError_AddNil tests the Add function of MultiError with nil error 71 | func TestMultiError_AddNil(t *testing.T) { 72 | t.Run("Testing execution of MultiError_AddNil", func(t *testing.T) { 73 | m := &MultiError{} 74 | m.Add(nil) 75 | fmt.Println(m.errs) 76 | if len(m.errs) != 0 { 77 | t.Errorf("nil error added to MultiError") 78 | } 79 | }) 80 | } 81 | 82 | // TestMultiError_HasErrorNil tests the HasError function of MultiError with nil error 83 | func TestMultiError_HasErrorNil(t *testing.T) { 84 | t.Run("Testing execution of MultiError_HasErrorNil", func(t *testing.T) { 85 | m := &MultiError{} 86 | if m.HasError(nil) { 87 | t.Errorf("nil error added to MultiError") 88 | } 89 | }) 90 | } 91 | 92 | // TestMultiError_ErrorNil tests the Error function of MultiError with nil error 93 | func TestMultiError_ErrorNil(t *testing.T) { 94 | t.Run("Testing execution of MultiError_ErrorNil", func(t *testing.T) { 95 | m := &MultiError{} 96 | errs := m.Error() 97 | if errs != "" { 98 | t.Errorf("nil error added to MultiError") 99 | } 100 | }) 101 | 102 | } 103 | -------------------------------------------------------------------------------- /turbo/bench_test.go: -------------------------------------------------------------------------------- 1 | package turbo 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | "net/http/httptest" 7 | "net/url" 8 | "testing" 9 | ) 10 | 11 | // BenchmarkFindRouteStatic: Static Path Test 12 | func BenchmarkFindRouteStatic(b *testing.B) { 13 | var router = NewRouter() 14 | router.Get("/api/v1/health", func(w http.ResponseWriter, r *http.Request) { 15 | json.NewEncoder(w).Encode([]byte("hello from turbo")) 16 | }) 17 | testUrl, _ := url.Parse("/api/v1/health") 18 | req := &http.Request{ 19 | Method: "", 20 | URL: testUrl, 21 | Proto: "", 22 | ProtoMajor: 0, 23 | ProtoMinor: 0, 24 | Header: nil, 25 | Body: nil, 26 | GetBody: nil, 27 | ContentLength: 0, 28 | TransferEncoding: nil, 29 | Close: false, 30 | Host: "", 31 | Form: nil, 32 | PostForm: nil, 33 | MultipartForm: nil, 34 | Trailer: nil, 35 | RemoteAddr: "", 36 | RequestURI: "", 37 | TLS: nil, 38 | Cancel: nil, 39 | Response: nil, 40 | } 41 | for i := 0; i < b.N; i++ { 42 | router.findRoute(req) 43 | } 44 | } 45 | 46 | // BenchmarkFindRoutePathParam: Path Param Test 47 | func BenchmarkFindRoutePathParam(b *testing.B) { 48 | var router = NewRouter() 49 | router.Get("/api/v1/health/:id", func(w http.ResponseWriter, r *http.Request) { 50 | json.NewEncoder(w).Encode([]byte("hello from turbo")) 51 | }) 52 | testUrl, _ := url.Parse("/api/v1/health/123") 53 | req := &http.Request{ 54 | Method: "", 55 | URL: testUrl, 56 | Proto: "", 57 | ProtoMajor: 0, 58 | ProtoMinor: 0, 59 | Header: nil, 60 | Body: nil, 61 | GetBody: nil, 62 | ContentLength: 0, 63 | TransferEncoding: nil, 64 | Close: false, 65 | Host: "", 66 | Form: nil, 67 | PostForm: nil, 68 | MultipartForm: nil, 69 | Trailer: nil, 70 | RemoteAddr: "", 71 | RequestURI: "", 72 | TLS: nil, 73 | Cancel: nil, 74 | Response: nil, 75 | } 76 | for i := 0; i < b.N; i++ { 77 | router.findRoute(req) 78 | } 79 | } 80 | 81 | func BenchmarkRouter_ServeHTTPStatic(b *testing.B) { 82 | var router = NewRouter() 83 | router.Get("/api/fooTest", func(w http.ResponseWriter, r *http.Request) { 84 | w.Write([]byte("Benchmarking!")) 85 | }) 86 | w := httptest.NewRecorder() 87 | r, err := http.NewRequest(GET, "/api/fooTest", nil) 88 | if err != nil { 89 | b.Fatal(err) 90 | } 91 | for i := 0; i < b.N; i++ { 92 | router.ServeHTTP(w, r) 93 | } 94 | } 95 | 96 | func BenchmarkRouter_ServeHTTPParams(b *testing.B) { 97 | var router = NewRouter() 98 | router.Get("/api/fooTest/:id", func(w http.ResponseWriter, r *http.Request) { 99 | w.Write([]byte("Benchmarking!")) 100 | }) 101 | w := httptest.NewRecorder() 102 | r, err := http.NewRequest(GET, "/api/fooTest/123", nil) 103 | if err != nil { 104 | b.Fatal(err) 105 | } 106 | for i := 0; i < b.N; i++ { 107 | router.ServeHTTP(w, r) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /genai/exchange_test.go: -------------------------------------------------------------------------------- 1 | package genai 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "oss.nandlabs.io/golly/ioutils" 8 | "oss.nandlabs.io/golly/testing/assert" 9 | ) 10 | 11 | func TestNewExchange(t *testing.T) { 12 | exchange := NewExchange("test-id") 13 | assert.NotNil(t, exchange) 14 | assert.Equal(t, "test-id", exchange.Id()) 15 | assert.NotNil(t, exchange.Attributes()) 16 | assert.Empty(t, exchange.Messages()) 17 | } 18 | 19 | func TestExchange_MsgsByMime(t *testing.T) { 20 | exchange := NewExchange("test-id") 21 | exchange.AddTxtMsg("test message", UserActor) 22 | messages := exchange.MsgsByMime(ioutils.MimeTextPlain) 23 | assert.Len(t, messages, 1) 24 | assert.Equal(t, ioutils.MimeTextPlain, messages[0].Mime()) 25 | } 26 | 27 | func TestExchange_MsgsByActor(t *testing.T) { 28 | exchange := NewExchange("test-id") 29 | exchange.AddTxtMsg("test message", UserActor) 30 | messages := exchange.MsgsByActors(UserActor) 31 | assert.Len(t, messages, 1) 32 | assert.Equal(t, UserActor, messages[0].Actor()) 33 | } 34 | 35 | func TestExchange_AddMsg(t *testing.T) { 36 | exchange := NewExchange("test-id") 37 | exchange.AddTxtMsg("test message", UserActor) 38 | 39 | assert.Len(t, exchange.Messages(), 1) 40 | } 41 | 42 | func TestExchange_AddTxtMsg(t *testing.T) { 43 | exchange := NewExchange("test-id") 44 | msg, err := exchange.AddTxtMsg("test message", UserActor) 45 | assert.NoError(t, err) 46 | assert.NotNil(t, msg) 47 | assert.Equal(t, "test message", readMessageContent(t, msg)) 48 | assert.Equal(t, UserActor, msg.Actor()) 49 | assert.Equal(t, ioutils.MimeTextPlain, msg.Mime()) 50 | } 51 | 52 | func TestExchange_AddJsonMsg(t *testing.T) { 53 | exchange := NewExchange("test-id") 54 | data := map[string]string{"key": "value"} 55 | msg, err := exchange.AddJsonMsg(data, UserActor) 56 | assert.NoError(t, err) 57 | assert.NotNil(t, msg) 58 | assert.Equal(t, UserActor, msg.Actor()) 59 | assert.Equal(t, ioutils.MimeApplicationJSON, msg.Mime()) 60 | } 61 | 62 | func TestExchange_AddFileMsg(t *testing.T) { 63 | exchange := NewExchange("test-id") 64 | fileURL := "http://example.com/file.txt" 65 | msg, err := exchange.AddFileMsg(fileURL, ioutils.MimeTextPlain, UserActor) 66 | assert.NoError(t, err) 67 | assert.NotNil(t, msg) 68 | assert.Equal(t, UserActor, msg.Actor()) 69 | assert.Equal(t, ioutils.MimeTextPlain, msg.Mime()) 70 | assert.Equal(t, fileURL, msg.URL().String()) 71 | } 72 | 73 | func TestExchange_AddBinMsg(t *testing.T) { 74 | exchange := NewExchange("test-id") 75 | data := []byte("binary data") 76 | msg, err := exchange.AddBinMsg(data, ioutils.MimeApplicationOctetStream, UserActor) 77 | assert.NoError(t, err) 78 | assert.NotNil(t, msg) 79 | assert.Equal(t, UserActor, msg.Actor()) 80 | assert.Equal(t, ioutils.MimeApplicationOctetStream, msg.Mime()) 81 | assert.Equal(t, data, readMessageContentBytes(t, msg)) 82 | } 83 | 84 | func readMessageContent(t *testing.T, msg *Message) string { 85 | buf := new(bytes.Buffer) 86 | _, err := buf.ReadFrom(msg) 87 | assert.NoError(t, err) 88 | return buf.String() 89 | } 90 | 91 | func readMessageContentBytes(t *testing.T, msg *Message) []byte { 92 | buf := new(bytes.Buffer) 93 | _, err := buf.ReadFrom(msg) 94 | assert.NoError(t, err) 95 | return buf.Bytes() 96 | } 97 | -------------------------------------------------------------------------------- /turbo/filter_test.go: -------------------------------------------------------------------------------- 1 | package turbo 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | ) 8 | 9 | func filterFunction(input string) FilterFunc { 10 | return func(next http.Handler) http.Handler { 11 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 12 | w.Write([]byte(input)) 13 | next.ServeHTTP(w, r) 14 | }) 15 | } 16 | } 17 | 18 | var testHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 19 | w.Write([]byte("testHandler")) 20 | }) 21 | 22 | func TestFilter(t *testing.T) { 23 | var router = NewRouter() 24 | route, err := router.Get("/api/foo", testHandler) 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | path := "/api/foo" 29 | 30 | tests := []struct { 31 | name string 32 | input string 33 | }{ 34 | { 35 | name: "Test1", 36 | input: "v1/", 37 | }, 38 | { 39 | name: "Test2", 40 | input: "v2/", 41 | }, 42 | { 43 | name: "Test3", 44 | input: "v3/", 45 | }, 46 | } 47 | for _, tt := range tests { 48 | t.Run(tt.name, func(t *testing.T) { 49 | route.AddFilter(filterFunction(tt.input)) 50 | }) 51 | } 52 | w := httptest.NewRecorder() 53 | r, err := http.NewRequest(GET, path, nil) 54 | if err != nil { 55 | t.Fatal(err) 56 | } 57 | if len(route.filters) != len(tests) { 58 | t.Error("All Test Filters not added") 59 | } 60 | router.ServeHTTP(w, r) 61 | if w.Body.String() != "v1/v2/v3/testHandler" { 62 | t.Error("Filter Chain not working") 63 | } 64 | } 65 | 66 | type BasicAuthFilter struct{} 67 | 68 | func (ba *BasicAuthFilter) Apply(next http.Handler) http.Handler { 69 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 70 | if token := r.Header.Get("token"); token != "" { 71 | next.ServeHTTP(w, r) 72 | } else { 73 | w.WriteHeader(http.StatusForbidden) 74 | w.Write([]byte("Not Authorised")) 75 | } 76 | }) 77 | } 78 | 79 | func CreateBasicAuthAuthenticator() *BasicAuthFilter { 80 | return &BasicAuthFilter{} 81 | } 82 | 83 | func TestAuthenticatorFilter(t *testing.T) { 84 | var router = NewRouter() 85 | route, err := router.Get("/api/foo", testHandler) 86 | if err != nil { 87 | t.Fatal(err) 88 | } 89 | path := "/api/foo" 90 | 91 | var authenticator = CreateBasicAuthAuthenticator() 92 | 93 | route.AddAuthenticator(authenticator) 94 | 95 | var w *httptest.ResponseRecorder 96 | var r *http.Request 97 | 98 | w = httptest.NewRecorder() 99 | r, err = http.NewRequest(GET, path, nil) 100 | if err != nil { 101 | t.Fatal(err) 102 | } 103 | r.Header.Add("token", "value") 104 | 105 | if route.authFilter == nil { 106 | t.Error("Authenticator Filters not added") 107 | } 108 | router.ServeHTTP(w, r) 109 | if w.Result().StatusCode != http.StatusOK { 110 | t.Error("Auth Filter not working") 111 | } 112 | 113 | w = httptest.NewRecorder() 114 | r, err = http.NewRequest(GET, path, nil) 115 | if err != nil { 116 | t.Fatal(err) 117 | } 118 | if route.authFilter == nil { 119 | t.Error("Authenticator Filters not added") 120 | } 121 | router.ServeHTTP(w, r) 122 | if w.Result().StatusCode != http.StatusForbidden { 123 | t.Error("Auth Filter not working") 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /ioutils/chksum_test.go: -------------------------------------------------------------------------------- 1 | package ioutils 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | ) 7 | 8 | func TestSha256Checksum_Calculate(t *testing.T) { 9 | checksum := NewChkSumCalc(SHA256) 10 | content := "Hello, World!" 11 | expectedChecksum := "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f" 12 | 13 | calculatedChecksum, err := checksum.Calculate(content) 14 | if err != nil { 15 | t.Errorf("Error calculating checksum: %v", err) 16 | } 17 | 18 | if calculatedChecksum != expectedChecksum { 19 | t.Errorf("Expected checksum %s, but got %s", expectedChecksum, calculatedChecksum) 20 | } 21 | } 22 | 23 | func TestSha256Checksum_Verify(t *testing.T) { 24 | checksum := NewChkSumCalc(SHA256) 25 | content := "Hello, World!" 26 | expectedChecksum := "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f" 27 | 28 | valid, err := checksum.Verify(content, expectedChecksum) 29 | if err != nil { 30 | t.Errorf("Error verifying checksum: %v", err) 31 | } 32 | 33 | if !valid { 34 | t.Errorf("Expected checksum %s to be valid, but it was not", expectedChecksum) 35 | } 36 | } 37 | 38 | func TestSha256Checksum_CalculateFile(t *testing.T) { 39 | checksum := NewChkSumCalc(SHA256) 40 | file := "./testdata/hello-world.txt" 41 | expectedChecksum := "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f" 42 | 43 | calculatedChecksum, err := checksum.CalculateFile(file) 44 | if err != nil { 45 | t.Errorf("Error calculating file checksum: %v", err) 46 | } 47 | 48 | if calculatedChecksum != expectedChecksum { 49 | t.Errorf("Expected file checksum %s, but got %s", expectedChecksum, calculatedChecksum) 50 | } 51 | } 52 | 53 | func TestSha256Checksum_VerifyFile(t *testing.T) { 54 | checksum := NewChkSumCalc(SHA256) 55 | file := "./testdata/hello-world.txt" 56 | expectedChecksum := "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f" 57 | 58 | valid, err := checksum.VerifyFile(file, expectedChecksum) 59 | if err != nil { 60 | t.Errorf("Error verifying file checksum: %v", err) 61 | } 62 | 63 | if !valid { 64 | t.Errorf("Expected file checksum %s to be valid, but it was not", expectedChecksum) 65 | } 66 | } 67 | 68 | func TestSha256Checksum_CalculateFor(t *testing.T) { 69 | checksum := NewChkSumCalc(SHA256) 70 | content := "Hello, World!" 71 | expectedChecksum := "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f" 72 | 73 | calculatedChecksum, err := checksum.CalculateFor(strings.NewReader(content)) 74 | if err != nil { 75 | t.Errorf("Error calculating reader checksum: %v", err) 76 | } 77 | 78 | if calculatedChecksum != expectedChecksum { 79 | t.Errorf("Expected reader checksum %s, but got %s", expectedChecksum, calculatedChecksum) 80 | } 81 | } 82 | 83 | func TestSha256Checksum_VerifyFor(t *testing.T) { 84 | checksum := NewChkSumCalc(SHA256) 85 | content := "Hello, World!" 86 | expectedChecksum := "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f" 87 | 88 | valid, err := checksum.VerifyFor(strings.NewReader(content), expectedChecksum) 89 | if err != nil { 90 | t.Errorf("Error verifying reader checksum: %v", err) 91 | } 92 | 93 | if !valid { 94 | t.Errorf("Expected reader checksum %s to be valid, but it was not", expectedChecksum) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /codec/codec_test.go: -------------------------------------------------------------------------------- 1 | package codec 2 | 3 | import ( 4 | "bytes" 5 | "strings" 6 | "testing" 7 | 8 | "oss.nandlabs.io/golly/ioutils" 9 | ) 10 | 11 | type Message struct { 12 | Name string `json:"name"` 13 | Body string `json:"body"` 14 | Time int64 `json:"time"` 15 | } 16 | 17 | type XMLMessage struct { 18 | Name string `xml:"name"` 19 | Body string `xml:"body"` 20 | Time int64 `xml:"time"` 21 | } 22 | 23 | type Message2 struct { 24 | Name string `json:"name" constraints:"min-length=5"` 25 | Body string `json:"body" constraints:"max-length=50"` 26 | Time int64 `json:"time" constraints:"min=10"` 27 | } 28 | 29 | func TestNewJson(t *testing.T) { 30 | m := Message2{"TestUser", "Hello", 123124124} 31 | c, _ := Get(ioutils.MimeApplicationJSON, nil) 32 | buf := new(bytes.Buffer) 33 | if err := c.Write(m, buf); err != nil { 34 | t.Errorf("error in write: %d", err) 35 | } 36 | 37 | const want = "{\"name\":\"TestUser\",\"body\":\"Hello\",\"time\":123124124}\n" 38 | if got := buf; got.String() != want { 39 | t.Errorf("got %q, want %q", got.String(), want) 40 | } 41 | } 42 | 43 | func TestNewDefaultJson(t *testing.T) { 44 | m := Message2{"TestUser", "Hello", 123124124} 45 | c, _ := GetDefault(ioutils.MimeApplicationJSON) 46 | buf := new(bytes.Buffer) 47 | if err := c.Write(m, buf); err != nil { 48 | t.Errorf("error in write: %d", err) 49 | } 50 | 51 | const want = "{\"name\":\"TestUser\",\"body\":\"Hello\",\"time\":123124124}\n" 52 | if got := buf; got.String() != want { 53 | t.Errorf("got %q, want %q", got.String(), want) 54 | } 55 | } 56 | 57 | func TestNewJsonCodec2(t *testing.T) { 58 | var m Message 59 | c, _ := Get(ioutils.MimeApplicationJSON, nil) 60 | const input = `{"name":"Test","body":"Hello","time":123124124}` 61 | b := strings.NewReader(input) 62 | if err := c.Read(b, &m); err != nil { 63 | t.Errorf("error in read: %d", err) 64 | } 65 | want := Message{ 66 | Name: "Test", 67 | Body: "Hello", 68 | Time: 123124124, 69 | } 70 | if m != want { 71 | t.Errorf("got %q, want %q", m, want) 72 | } 73 | } 74 | 75 | func TestNewXmlCodec(t *testing.T) { 76 | m := XMLMessage{"Test", "Hello", 123124124} 77 | c := XmlCodec() 78 | buf := new(bytes.Buffer) 79 | if err := c.Write(m, buf); err != nil { 80 | t.Errorf("error in write: %d", err) 81 | } 82 | const want = `TestHello` 83 | if got := buf; got.String() != want { 84 | t.Errorf("got %q, want %q", got.String(), want) 85 | } 86 | } 87 | 88 | func TestNewXmlCodec2(t *testing.T) { 89 | var m XMLMessage 90 | c, _ := Get(ioutils.MimeTextXML, nil) 91 | const input = `TestHello` 92 | b := strings.NewReader(input) 93 | if err := c.Read(b, &m); err != nil { 94 | t.Errorf("error in read: %d", err) 95 | } 96 | want := XMLMessage{ 97 | Name: "Test", 98 | Body: "Hello", 99 | Time: 123124124, 100 | } 101 | if m != want { 102 | t.Errorf("got %q, want %q", m, want) 103 | } 104 | } 105 | 106 | func TestNewInvalid(t *testing.T) { 107 | _, err := Get("text/plain", nil) 108 | 109 | if err == nil { 110 | t.Error("got nil wanted err") 111 | 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /messaging/README.md: -------------------------------------------------------------------------------- 1 | # MESSAGING Client 2 | 3 | This is a flexible and extensible messaging client designed to provide a unified interface for producing and consuming messages across various messaging platforms. It allows developers to seamlessly integrate messaging functionality into their applications without being tied to a specific messaging service. 4 | 5 | --- 6 | 7 | - [Features](#features) 8 | - [Installation](#installation) 9 | - [Usage](#usage) 10 | - [Extending the library](#extending-the-library) 11 | 12 | --- 13 | 14 | ## Features 15 | 16 | - General producer interface for sending messages to different messaging platforms. 17 | - General consumer interface for receiving and processing messages from different messaging platforms. 18 | - Supports QualityOfService features such as 19 | - CircuitBreaker 20 | - Retry 21 | - Easy-to-use message abstraction for consistent handling of messages across platforms. 22 | - Can be extended to multiple messaging platforms with easily pluggable interfaces, including: 23 | - AMQP (Advanced Message Queuing Protocol) 24 | - Apache Kafka 25 | - AWS SNS (Simple Notification Service) 26 | - AWS SQS (Simple Queue Service) 27 | - GCM (Google Cloud Messaging) 28 | - GCP Pub/Sub (Google Cloud Pub/Sub) 29 | 30 | ## Installation 31 | 32 | To install the messaging client, use the following command: 33 | 34 | ```bash 35 | go get oss.nandlabs.io/golly/clients/messaging 36 | ``` 37 | 38 | ## Usage 39 | 40 | 1. Import the library into your Go project: 41 | 42 | ```go 43 | import "oss.nandlabs.io/golly/clients/messaging" 44 | ``` 45 | 46 | 2. Initialize the messaging provider for a specific platform. For example, to use the AMQP extension: 47 | 48 | ```go 49 | type AMQPProvider struct {} // implements the Provider interface defined under the library 50 | amqpProvider := &AMQPProvider{} 51 | 52 | manager := messaging.Get() 53 | manager.Register(amqpProvider) 54 | ``` 55 | 56 | 3. Send a message 57 | 58 | ```go 59 | message := &messaging.Message{ 60 | Body: []byte("Hello, World!"), 61 | /// Add any additional properties or metadata 62 | } 63 | destination := url.Parse("amqp://guest:password@localhost:5672/myvhost") 64 | err := manager.Send(destination, message) 65 | if err != nil { 66 | // Handle error 67 | } 68 | ``` 69 | 70 | 4. Receive a message 71 | 72 | ```go 73 | // Define the onReceive function 74 | onReceive := func(msg Message) error { 75 | // Process the message 76 | // ... 77 | 78 | return nil 79 | } 80 | // Start receiving messages from the channel 81 | manager.Receive(receiverUrl, onReceive) 82 | ``` 83 | 84 | 5. Repeat steps 2-4 for other messaging platforms by initializing the respective clients. 85 | 86 | ## Extending the library 87 | 88 | To add support for additional messaging platforms, you can create new extensions by implementing the producer, consumer, and message interfaces defined in the library. These interfaces provide a consistent way to interact with different messaging systems. 89 | 90 | You can refer to the existing extensions, such as amqp, kafka, etc., as examples for creating your own extensions. Ensure that your extension adheres to the interface definitions and follows the library's conventions for consistency. 91 | -------------------------------------------------------------------------------- /vfs/vfs.go: -------------------------------------------------------------------------------- 1 | package vfs 2 | 3 | import ( 4 | "net/url" 5 | ) 6 | 7 | type WalkFn func(file VFile) error 8 | 9 | type VFileSystem interface { 10 | //Copy File from one location to another. If the source resolves to a directory, then all its nested children 11 | //will be copied. The source and destination can be of different filesystems. 12 | //Since the FS can be different it cannot guarantee to carry forward any symbolic links/shortcuts from source 13 | //instead it may try to create regular files/directories for the same even if the source and destination have 14 | //the same schemes 15 | Copy(src, dst *url.URL) error 16 | //CopyRaw is same as Copy except it accepts url as string 17 | CopyRaw(src, dst string) error 18 | //Create will create a new file this in the specified url. This is a 19 | Create(u *url.URL) (VFile, error) 20 | //CreateRaw is same as Create except it accepts the url as a string 21 | CreateRaw(raw string) (VFile, error) 22 | //Delete file . if the src resolves to a directory then all the files and directories under this will be deleted 23 | Delete(src *url.URL) error 24 | //DeleteRaw is same as Delete except that it will accept url as a string 25 | DeleteRaw(src string) error 26 | //List function will list all the files if the type is a directory 27 | List(url *url.URL) ([]VFile, error) 28 | //ListRaw lists the file in the filesystem for a specific url 29 | ListRaw(url string) ([]VFile, error) 30 | //Mkdir will create the directory and will throw an error if exists or has permission issues or unable to create 31 | Mkdir(u *url.URL) (VFile, error) 32 | //MkdirRaw same as Mkdir, however it accepts url as string 33 | MkdirRaw(u string) (VFile, error) 34 | //MkdirAll will create all directories missing in the path 35 | //If the directory already exists it will not throw error, however if the path resolves to a file instead 36 | //it should error 37 | MkdirAll(u *url.URL) (VFile, error) 38 | //MkdirAllRaw same as MkdirAll, however it accepts url as string 39 | MkdirAllRaw(u string) (VFile, error) 40 | //Move will 41 | Move(src, dst *url.URL) error 42 | //MoveRaw same as Move except it accepts url as string 43 | MoveRaw(src, dst string) error 44 | //Open a file based on the url of the file 45 | Open(u *url.URL) (VFile, error) 46 | // OpenRaw is same as Open function, however it accepts the url as string 47 | OpenRaw(u string) (VFile, error) 48 | //Schemes is the list of schemes supported by this file system 49 | Schemes() []string 50 | //Walk will walk through each of the files in the directory recursively. 51 | //If the URL resolves to a file it's not expected to throw an error instead the fn just be invoked with the VFile 52 | //representing the url once 53 | Walk(url *url.URL, fn WalkFn) error 54 | //Find files based on filter only works if the file.IsDir() is true 55 | Find(location *url.URL, filter FileFilter) ([]VFile, error) 56 | //WalkRaw is same as Walk except that it will accept the url as a string 57 | WalkRaw(raw string, fn WalkFn) error 58 | //DeleteMatching will delete only the files that match the filter. 59 | //If one of the file deletion fails with an error then it stops processing and returns error 60 | DeleteMatching(location *url.URL, filter FileFilter) error 61 | } 62 | 63 | type Manager interface { 64 | VFileSystem 65 | Register(vfs VFileSystem) 66 | IsSupported(scheme string) bool 67 | } 68 | -------------------------------------------------------------------------------- /data/schema_gen.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "errors" 5 | "reflect" 6 | "strings" 7 | ) 8 | 9 | // ErrUnsupportedType is returned when the type is not supported 10 | var ErrUnsupportedType = errors.New("unsupported type for schema generation") 11 | 12 | // GenerateSchema converts a Go type to a JSON Schema. 13 | // 14 | // This function uses reflection to analyze Go types and produce corresponding JSON Schema representations. 15 | // It supports various Go types including structs, slices, maps, primitive types, and pointers. 16 | // 17 | // For structs, it creates an object schema with properties corresponding to the struct fields. 18 | // JSON field names are extracted from json tags if present. 19 | // Unexported fields are skipped. 20 | // 21 | // For slices, it creates an array schema with the element type defined in the Items field. 22 | // 23 | // For maps, it creates an object schema with the value type defined in AdditionalItems. 24 | // 25 | // The function handles pointers by resolving to their base types. 26 | // 27 | // Parameters: 28 | // - t: The reflect.Type to convert to a JSON Schema 29 | // 30 | // Returns: 31 | // - schema: A pointer to the generated Schema 32 | // - err: An error if the type is unsupported or if a nested type cannot be processed 33 | func GenerateSchema(t reflect.Type) (schema *Schema, err error) { 34 | 35 | switch t.Kind() { 36 | case reflect.Ptr: 37 | schema, err = GenerateSchema(t.Elem()) 38 | 39 | case reflect.Struct: 40 | 41 | schema = &Schema{ 42 | Type: "object", 43 | Properties: make(map[string]*Schema), 44 | } 45 | for i := 0; i < t.NumField(); i++ { 46 | field := t.Field(i) 47 | if field.PkgPath != "" { 48 | continue 49 | } 50 | prop, err := GenerateSchema(field.Type) 51 | if err != nil { 52 | return nil, err 53 | } 54 | fieldName := field.Name 55 | if tag, ok := field.Tag.Lookup("json"); ok { 56 | 57 | // check and Split the tag by comma and take the first part as the field name 58 | parts := strings.Split(tag, ",") 59 | if parts[0] != "" { 60 | fieldName = parts[0] 61 | } else { 62 | fieldName = tag 63 | } 64 | 65 | } 66 | schema.Properties[fieldName] = prop 67 | } 68 | case reflect.Slice: 69 | schema = &Schema{ 70 | Type: "array", 71 | } 72 | elemSchema, err := GenerateSchema(t.Elem()) 73 | if err != nil { 74 | return nil, err 75 | } 76 | schema.Items = elemSchema 77 | case reflect.Map: 78 | schema = &Schema{ 79 | Type: "object", 80 | Properties: make(map[string]*Schema), 81 | } 82 | elemSchema, err := GenerateSchema(t.Elem()) 83 | if err != nil { 84 | return nil, err 85 | } 86 | schema.AdditionalItems = elemSchema 87 | 88 | case reflect.String: 89 | schema = &Schema{ 90 | Type: "string", 91 | } 92 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 93 | schema = &Schema{ 94 | Type: "integer", 95 | } 96 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 97 | schema = &Schema{ 98 | Type: "integer", 99 | } 100 | case reflect.Float32, reflect.Float64: 101 | schema = &Schema{ 102 | Type: "number", 103 | } 104 | case reflect.Bool: 105 | schema = &Schema{ 106 | Type: "boolean", 107 | } 108 | default: 109 | err = ErrUnsupportedType 110 | } 111 | 112 | return 113 | } 114 | -------------------------------------------------------------------------------- /uuid/README.md: -------------------------------------------------------------------------------- 1 | # UUID Package 2 | 3 | The `uuid` package provides functionality for generating and handling universally unique identifiers (UUIDs) in Go. This package supports multiple versions of UUIDs, including version 1, 2, 3, and 4. 4 | 5 | ## Installation 6 | 7 | To install the package, use the following command: 8 | 9 | ```sh 10 | go get github.com/nandlabs/golly/uuid 11 | ``` 12 | 13 | --- 14 | 15 | - [UUID Package](#uuid-package) 16 | - [Installation](#installation) 17 | - [Usage](#usage) 18 | - [Generating UUIDs](#generating-uuids) 19 | - [Version 1 UUID](#version-1-uuid) 20 | - [Version 2 UUID](#version-2-uuid) 21 | - [Version 3 UUID](#version-3-uuid) 22 | - [Version 4 UUID](#version-4-uuid) 23 | - [Parsing UUIDs](#parsing-uuids) 24 | - [License](#license) 25 | 26 | --- 27 | 28 | ## Usage 29 | 30 | ### Generating UUIDs 31 | 32 | The `uuid` package provides functions to generate different versions of UUIDs. 33 | 34 | #### Version 1 UUID 35 | 36 | Version 1 UUIDs are based on the current timestamp and the MAC address of the machine. 37 | 38 | ```go 39 | package main 40 | 41 | import ( 42 | "fmt" 43 | "github.com/nandlabs/golly/uuid" 44 | ) 45 | 46 | func main() { 47 | u, err := uuid.V1() 48 | if err != nil { 49 | fmt.Println("Error generating UUID:", err) 50 | return 51 | } 52 | fmt.Println("Version 1 UUID:", u.String()) 53 | } 54 | ``` 55 | 56 | #### Version 2 UUID 57 | 58 | Version 2 UUIDs are based on the MAC address, process ID, and current timestamp. 59 | 60 | ```go 61 | package main 62 | 63 | import ( 64 | "fmt" 65 | "github.com/nandlabs/golly/uuid" 66 | ) 67 | 68 | func main() { 69 | u, err := uuid.V2() 70 | if err != nil { 71 | fmt.Println("Error generating UUID:", err) 72 | return 73 | } 74 | fmt.Println("Version 2 UUID:", u.String()) 75 | } 76 | ``` 77 | 78 | #### Version 3 UUID 79 | 80 | Version 3 UUIDs are based on a namespace and a name, using MD5 hashing. 81 | 82 | ```go 83 | package main 84 | 85 | import ( 86 | "fmt" 87 | "github.com/nandlabs/golly/uuid" 88 | ) 89 | 90 | func main() { 91 | namespace := "example.com" 92 | name := "example" 93 | u, err := uuid.V3(namespace, name) 94 | if err != nil { 95 | fmt.Println("Error generating UUID:", err) 96 | return 97 | } 98 | fmt.Println("Version 3 UUID:", u.String()) 99 | } 100 | ``` 101 | 102 | #### Version 4 UUID 103 | 104 | Version 4 UUIDs are randomly generated. 105 | 106 | ```go 107 | package main 108 | 109 | import ( 110 | "fmt" 111 | "github.com/nandlabs/golly/uuid" 112 | ) 113 | 114 | func main() { 115 | u, err := uuid.V4() 116 | if err != nil { 117 | fmt.Println("Error generating UUID:", err) 118 | return 119 | } 120 | fmt.Println("Version 4 UUID:", u.String()) 121 | } 122 | ``` 123 | 124 | ### Parsing UUIDs 125 | 126 | The `ParseUUID` function allows you to parse a UUID string into a `UUID` object. 127 | 128 | ```go 129 | package main 130 | 131 | import ( 132 | "fmt" 133 | "github.com/nandlabs/golly/uuid" 134 | ) 135 | 136 | func main() { 137 | uuidStr := "123e4567-e89b-12d3-a456-426655440000" 138 | u, err := uuid.ParseUUID(uuidStr) 139 | if err != nil { 140 | fmt.Println("Error parsing UUID:", err) 141 | return 142 | } 143 | fmt.Println("Parsed UUID:", u.String()) 144 | } 145 | ``` 146 | -------------------------------------------------------------------------------- /fnutils/fn.go: -------------------------------------------------------------------------------- 1 | package fnutils 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | ) 7 | 8 | // ExecuteAfterSecs executes the given function after the specified timeout duration, 9 | // expressed in seconds. It returns any error encountered during the execution. 10 | // 11 | // It converts the timeout value from seconds to a duration in seconds and then calls 12 | // the ExecuteAfter function, passing the converted duration and the provided function. 13 | // 14 | // Example: 15 | // 16 | // err := ExecuteAfterSecs(func() { 17 | // fmt.Println("Hello, World!") 18 | // }, 10) 19 | // 20 | // @param fn The function to be executed. 21 | // 22 | // @param timeout The timeout duration in seconds. 23 | // 24 | // @returns An error encountered during execution, if any. 25 | func ExecuteAfterSecs(fn func(), timeout int) (err error) { 26 | err = ExecuteAfter(fn, time.Second*time.Duration(timeout)) 27 | return 28 | } 29 | 30 | // ExecuteAfterMs executes the given function after the specified timeout duration, 31 | // expressed in milliseconds. It returns any error encountered during the execution. 32 | // 33 | // It converts the timeout value from milliseconds to a duration in seconds and then calls 34 | // the ExecuteAfter function, passing the converted duration and the provided function. 35 | // 36 | // Example: 37 | // 38 | // err := ExecuteAfterMs(func() { 39 | // fmt.Println("Hello, World!") 40 | // }, 1000) 41 | // 42 | // @param fn The function to be executed. 43 | // 44 | // @param timeout The timeout duration in milliseconds. 45 | // 46 | // @returns An error encountered during execution, if any. 47 | func ExecuteAfterMs(fn func(), timeout int64) (err error) { 48 | err = ExecuteAfter(fn, time.Millisecond*time.Duration(timeout)) 49 | return 50 | } 51 | 52 | // ExecuteAfterMin executes the given function after the specified timeout duration, 53 | // expressed in minutes. It returns any error encountered during the execution. 54 | // 55 | // It converts the timeout value from minutes to a duration in seconds and then calls 56 | // the ExecuteAfter function, passing the converted duration and the provided function. 57 | // 58 | // Example: 59 | // 60 | // err := ExecuteAfterMin(func() { 61 | // fmt.Println("Hello, World!") 62 | // }, 5) 63 | // 64 | // @param fn The function to be executed. 65 | // 66 | // @param timeout The timeout duration in minutes. 67 | // 68 | // @returns An error encountered during execution, if any. 69 | func ExecuteAfterMin(fn func(), timeout int) (err error) { 70 | err = ExecuteAfter(fn, time.Minute*time.Duration(timeout)) 71 | return 72 | } 73 | 74 | // ExecuteAfter executes the given function after the specified timeout duration. 75 | // It returns any error encountered during the execution. 76 | // It waits for the specified duration and then calls the provided function. 77 | // 78 | // Example: 79 | // 80 | // err := ExecuteAfter(func() { 81 | // fmt.Println("Hello, World!") 82 | // }, time.Second) 83 | // 84 | // @param fn The function to be executed. 85 | // 86 | // @param timeout The duration to wait before executing the function. 87 | // 88 | // @returns An error encountered during execution, if any. 89 | func ExecuteAfter(fn func(), timeout time.Duration) (err error) { 90 | if fn == nil { 91 | err = errors.New("nil function provided") 92 | return 93 | } 94 | if timeout < 0 { 95 | err = errors.New("timeout cannot be negative") 96 | return 97 | } 98 | select { 99 | case <-time.After(timeout): 100 | { 101 | fn() 102 | } 103 | } 104 | return 105 | } 106 | -------------------------------------------------------------------------------- /rest/server_opts_test.go: -------------------------------------------------------------------------------- 1 | package rest 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // TestOptions_Validate tests the Validate function 8 | func TestOptions_Validate(t *testing.T) { 9 | tests := []struct { 10 | name string 11 | options SrvOptions 12 | wantErr bool 13 | }{ 14 | {"Valid options", SrvOptions{Id: "123", ListenHost: "localhost", ListenPort: 8080}, false}, 15 | {"Invalid ID", SrvOptions{ListenHost: "localhost", ListenPort: 8080}, true}, 16 | {"Invalid ListenHost", SrvOptions{Id: "123", ListenPort: 8080}, true}, 17 | {"Invalid ListenPort", SrvOptions{Id: "123", ListenHost: "localhost"}, true}, 18 | {"Invalid TLS options", SrvOptions{Id: "123", ListenHost: "localhost", ListenPort: 8080, EnableTLS: true}, true}, 19 | } 20 | for _, tt := range tests { 21 | t.Run(tt.name, func(t *testing.T) { 22 | if err := tt.options.Validate(); (err != nil) != tt.wantErr { 23 | t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr) 24 | } 25 | }) 26 | } 27 | } 28 | 29 | // TestOptions_Getters tests the getter functions 30 | func TestOptions_Getters(t *testing.T) { 31 | opts := SrvOptions{ 32 | ListenHost: "localhost", 33 | ListenPort: 8080, 34 | EnableTLS: true, 35 | PrivateKeyPath: "/path/to/private.key", 36 | CertPath: "/path/to/cert.crt", 37 | } 38 | if got := opts.GetListenHost(); got != opts.ListenHost { 39 | t.Errorf("GetListenHost() = %v, want %v", got, opts.ListenHost) 40 | } 41 | if got := opts.GetListenPort(); got != opts.ListenPort { 42 | t.Errorf("GetListenPort() = %v, want %v", got, opts.ListenPort) 43 | } 44 | if got := opts.GetEnableTLS(); got != opts.EnableTLS { 45 | t.Errorf("GetEnableTLS() = %v, want %v", got, opts.EnableTLS) 46 | } 47 | if got := opts.GetPrivateKeyPath(); got != opts.PrivateKeyPath { 48 | t.Errorf("GetPrivateKeyPath() = %v, want %v", got, opts.PrivateKeyPath) 49 | } 50 | if got := opts.GetCertPath(); got != opts.CertPath { 51 | t.Errorf("GetCertPath() = %v, want %v", got, opts.CertPath) 52 | } 53 | } 54 | 55 | // TestOptions_Setters tests the setter functions 56 | func TestOptions_Setters(t *testing.T) { 57 | opts := &SrvOptions{} 58 | opts = opts.SetListenHost("localhost") 59 | if opts.ListenHost != "localhost" { 60 | t.Errorf("SetListenHost() = %v, want %v", opts.ListenHost, "localhost") 61 | } 62 | opts = opts.SetListenPort(8080) 63 | if opts.ListenPort != 8080 { 64 | t.Errorf("SetListenPort() = %v, want %v", opts.ListenPort, 8080) 65 | } 66 | opts = opts.SetEnableTLS(true) 67 | if !opts.EnableTLS { 68 | t.Errorf("SetEnableTLS() = %v, want %v", opts.EnableTLS, true) 69 | } 70 | opts = opts.SetPrivateKeyPath("/path/to/private.key") 71 | if opts.PrivateKeyPath != "/path/to/private.key" { 72 | t.Errorf("SetPrivateKeyPath() = %v, want %v", opts.PrivateKeyPath, "/path/to/private.key") 73 | } 74 | opts = opts.SetCertPath("/path/to/cert.crt") 75 | if opts.CertPath != "/path/to/cert.crt" { 76 | t.Errorf("SetCertPath() = %v, want %v", opts.CertPath, "/path/to/cert.crt") 77 | } 78 | } 79 | 80 | // TestNewOptions tests the NewOptions function 81 | func TestNewOptions(t *testing.T) { 82 | opts := EmptySrvOptions() 83 | if opts.ListenHost != "" || opts.ListenPort != 0 { 84 | t.Errorf("NewOptions() = %v, want default values", opts) 85 | } 86 | } 87 | 88 | // TestDefaultOptions tests the DefaultOptions function 89 | func TestDefaultOptions(t *testing.T) { 90 | opts := DefaultSrvOptions() 91 | if opts.ListenHost != "localhost" || opts.ListenPort != 8080 || opts.ReadTimeout != 20000 || opts.WriteTimeout != 20000 { 92 | t.Errorf("DefaultOptions() = %v, want default values", opts) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /codec/validator/constraints.go: -------------------------------------------------------------------------------- 1 | package validator 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "regexp" 7 | "strconv" 8 | ) 9 | 10 | /** 11 | Numerical Type Constraints 12 | */ 13 | 14 | func min(field field, param string) error { 15 | return checkMin(field, param, false) 16 | } 17 | 18 | func max(field field, param string) error { 19 | return checkMax(field, param, false) 20 | } 21 | 22 | func exclusiveMin(field field, param string) error { 23 | return checkMin(field, param, true) 24 | } 25 | 26 | func exclusiveMax(field field, param string) error { 27 | return checkMax(field, param, true) 28 | } 29 | 30 | func multipleOf(field field, param string) error { 31 | // TODO : works only for int as of now 32 | switch field.typ.Kind() { 33 | case reflect.Int: 34 | in, _ := field.value.Interface().(int) 35 | c, err := convertInt(param, 0) 36 | cInt := int(c) 37 | if err != nil { 38 | return err 39 | } 40 | valid := in%cInt == 0 41 | if !valid { 42 | return fmt.Errorf(ErrMultipleOf, field.name) 43 | } 44 | default: 45 | return fmt.Errorf(ErrInvalidValidationForField, field.name) 46 | } 47 | return nil 48 | } 49 | 50 | /** 51 | String Type Constraints 52 | */ 53 | 54 | func notnull(field field, param string) error { 55 | switch field.typ.Kind() { 56 | case reflect.String: 57 | c, err := convertBool(param) 58 | if err != nil { 59 | return fmt.Errorf(ErrBadConstraint, "notnull", param, field.name) 60 | } 61 | if c == true { 62 | in, _ := field.value.Interface().(string) 63 | if in == "" { 64 | return fmt.Errorf(ErrNotNull, field.name) 65 | } 66 | } 67 | default: 68 | return fmt.Errorf(ErrInvalidValidationForField, field.name) 69 | } 70 | return nil 71 | } 72 | 73 | func minLength(field field, param string) error { 74 | switch field.typ.Kind() { 75 | case reflect.String: 76 | lc, _ := strconv.Atoi(param) 77 | lv := len(fmt.Sprint(field.value)) 78 | valid := lv > lc 79 | if !valid { 80 | return fmt.Errorf(ErrMinLength, field.name) 81 | } 82 | default: 83 | return fmt.Errorf(ErrInvalidValidationForField, field.name) 84 | } 85 | return nil 86 | } 87 | 88 | func maxLength(field field, param string) error { 89 | switch field.typ.Kind() { 90 | case reflect.String: 91 | lc, _ := strconv.Atoi(param) 92 | lv := len(fmt.Sprint(field.value)) 93 | valid := lv < lc 94 | if !valid { 95 | return fmt.Errorf(ErrMaxLength, field.name) 96 | } 97 | default: 98 | return fmt.Errorf(ErrInvalidValidationForField, field.name) 99 | } 100 | return nil 101 | } 102 | 103 | func pattern(field field, param string) error { 104 | switch field.typ.Kind() { 105 | case reflect.String: 106 | in, _ := field.value.Interface().(string) 107 | re, err := regexp.Compile(param) 108 | if err != nil { 109 | return fmt.Errorf(ErrBadConstraint, "pattern", param, field.name) 110 | } 111 | if !re.MatchString(in) { 112 | return fmt.Errorf(ErrPattern, field.name) 113 | } 114 | default: 115 | return fmt.Errorf(ErrInvalidValidationForField, field.name) 116 | } 117 | return nil 118 | } 119 | 120 | func enum(field field, param string) error { 121 | flag := false 122 | switch field.value.Kind() { 123 | case reflect.Int: 124 | input := field.value.Interface().(int) 125 | flag = checkIfEnumExists(strconv.Itoa(input), param, ",") 126 | case reflect.String: 127 | input := field.value.String() 128 | flag = checkIfEnumExists(input, param, ",") 129 | } 130 | 131 | if flag == false { 132 | return fmt.Errorf(ErrEnums, field.name) 133 | } 134 | return nil 135 | } 136 | -------------------------------------------------------------------------------- /collections/stack_test.go: -------------------------------------------------------------------------------- 1 | package collections 2 | 3 | import ( 4 | "testing" 5 | 6 | "oss.nandlabs.io/golly/testing/assert" 7 | ) 8 | 9 | func TestStack_Push(t *testing.T) { 10 | stack := NewStack[int]() 11 | stack.Push(1) 12 | stack.Push(2) 13 | stack.Push(3) 14 | 15 | assert.Equal(t, 3, stack.Size()) 16 | v, e := stack.Peek() 17 | assert.Nil(t, e) 18 | assert.Equal(t, 3, v) 19 | } 20 | 21 | func TestStack_Pop(t *testing.T) { 22 | stack := NewStack[int]() 23 | stack.Push(1) 24 | stack.Push(2) 25 | stack.Push(3) 26 | 27 | val, err := stack.Pop() 28 | assert.Nil(t, err) 29 | assert.Equal(t, 3, val) 30 | assert.Equal(t, 2, stack.Size()) 31 | 32 | val, err = stack.Pop() 33 | assert.Nil(t, err) 34 | assert.Equal(t, 2, val) 35 | assert.Equal(t, 1, stack.Size()) 36 | 37 | val, err = stack.Pop() 38 | assert.Nil(t, err) 39 | assert.Equal(t, 1, val) 40 | assert.Equal(t, 0, stack.Size()) 41 | 42 | _, err = stack.Pop() 43 | assert.NotNil(t, err) 44 | } 45 | 46 | func TestStack_Peek(t *testing.T) { 47 | stack := NewStack[int]() 48 | stack.Push(1) 49 | stack.Push(2) 50 | stack.Push(3) 51 | 52 | val, err := stack.Peek() 53 | assert.Nil(t, err) 54 | assert.Equal(t, 3, val) 55 | assert.Equal(t, 3, stack.Size()) 56 | 57 | stack.Pop() 58 | val, err = stack.Peek() 59 | assert.Nil(t, err) 60 | assert.Equal(t, 2, val) 61 | assert.Equal(t, 2, stack.Size()) 62 | } 63 | 64 | func TestStack_Iterator(t *testing.T) { 65 | stack := NewStack[int]() 66 | stack.Push(1) 67 | stack.Push(2) 68 | stack.Push(3) 69 | 70 | it := stack.Iterator() 71 | assert.True(t, it.HasNext()) 72 | assert.Equal(t, 3, it.Next()) 73 | assert.True(t, it.HasNext()) 74 | assert.Equal(t, 2, it.Next()) 75 | assert.True(t, it.HasNext()) 76 | assert.Equal(t, 1, it.Next()) 77 | assert.False(t, it.HasNext()) 78 | } 79 | 80 | func TestSyncStack_Push(t *testing.T) { 81 | stack := NewSyncStack[int]() 82 | stack.Push(1) 83 | stack.Push(2) 84 | stack.Push(3) 85 | 86 | assert.Equal(t, 3, stack.Size()) 87 | v, e := stack.Peek() 88 | assert.Nil(t, e) 89 | assert.Equal(t, 3, v) 90 | } 91 | 92 | func TestSyncStack_Pop(t *testing.T) { 93 | stack := NewSyncStack[int]() 94 | stack.Push(1) 95 | stack.Push(2) 96 | stack.Push(3) 97 | 98 | val, err := stack.Pop() 99 | assert.Nil(t, err) 100 | assert.Equal(t, 3, val) 101 | assert.Equal(t, 2, stack.Size()) 102 | 103 | val, err = stack.Pop() 104 | assert.Nil(t, err) 105 | assert.Equal(t, 2, val) 106 | assert.Equal(t, 1, stack.Size()) 107 | 108 | val, err = stack.Pop() 109 | assert.Nil(t, err) 110 | assert.Equal(t, 1, val) 111 | assert.Equal(t, 0, stack.Size()) 112 | 113 | _, err = stack.Pop() 114 | assert.NotNil(t, err) 115 | } 116 | 117 | func TestSyncStack_Peek(t *testing.T) { 118 | stack := NewSyncStack[int]() 119 | stack.Push(1) 120 | stack.Push(2) 121 | stack.Push(3) 122 | 123 | val, err := stack.Peek() 124 | assert.Nil(t, err) 125 | assert.Equal(t, 3, val) 126 | assert.Equal(t, 3, stack.Size()) 127 | 128 | stack.Pop() 129 | val, err = stack.Peek() 130 | assert.Nil(t, err) 131 | assert.Equal(t, 2, val) 132 | assert.Equal(t, 2, stack.Size()) 133 | } 134 | 135 | func TestSyncStack_Iterator(t *testing.T) { 136 | stack := NewSyncStack[int]() 137 | stack.Push(1) 138 | stack.Push(2) 139 | stack.Push(3) 140 | 141 | it := stack.Iterator() 142 | assert.True(t, it.HasNext()) 143 | assert.Equal(t, 3, it.Next()) 144 | assert.True(t, it.HasNext()) 145 | assert.Equal(t, 2, it.Next()) 146 | assert.True(t, it.HasNext()) 147 | assert.Equal(t, 1, it.Next()) 148 | assert.False(t, it.HasNext()) 149 | } 150 | -------------------------------------------------------------------------------- /ioutils/chksum.go: -------------------------------------------------------------------------------- 1 | package ioutils 2 | 3 | import ( 4 | "crypto/sha256" 5 | "fmt" 6 | "io" 7 | "os" 8 | "strings" 9 | ) 10 | 11 | const ( 12 | SHA256 = "SHA256" 13 | ) 14 | 15 | // ChkSumCalc interface is used to calculate the checksum of a text or file 16 | type ChkSumCalc interface { 17 | // Calculate calculates the checksum of the message 18 | Calculate(content string) (string, error) 19 | // Verify verifies the checksum of the message 20 | Verify(content, sum string) (bool, error) 21 | //CalculateFile calculates the checksum of a file 22 | CalculateFile(file string) (string, error) 23 | //VerifyFile verifies the checksum of a file 24 | VerifyFile(file, sum string) (bool, error) 25 | //CalculateFor calculates the checksum of the reader 26 | CalculateFor(reader io.Reader) (string, error) 27 | // VerifyFor verifies the checksum of the reader 28 | VerifyFor(reader io.Reader, sum string) (bool, error) 29 | //Type returns the type of the checksum 30 | Type() string 31 | } 32 | 33 | // Sha256Checksum is a checksum that uses the SHA256 algorithm 34 | type Sha256Checksum struct { 35 | } 36 | 37 | // Calculate calculates the checksum of the message 38 | func (s *Sha256Checksum) Calculate(content string) (chksum string, err error) { 39 | //Calculate the sha256 checksum 40 | hash := sha256.New() 41 | _, err = io.Copy(hash, strings.NewReader(content)) 42 | if err == nil { 43 | chksum = fmt.Sprintf("%x", hash.Sum(nil)) 44 | } 45 | return 46 | } 47 | 48 | // Verify verifies the checksum of the message 49 | func (s *Sha256Checksum) Verify(content, sum string) (b bool, err error) { 50 | var calcSum string 51 | //Calculate the checksum of the content 52 | calcSum, err = s.Calculate(content) 53 | //Verify the checksum 54 | b = err == nil && sum == calcSum 55 | return 56 | } 57 | 58 | // CalculateFile calculates the checksum of a file 59 | func (s *Sha256Checksum) CalculateFile(file string) (chksum string, err error) { 60 | //Calculate the checksum of the file 61 | hash := sha256.New() 62 | var f *os.File 63 | f, err = os.Open(file) 64 | if err != nil { 65 | return 66 | } 67 | defer f.Close() 68 | io.Copy(hash, f) 69 | return fmt.Sprintf("%x", hash.Sum(nil)), nil 70 | } 71 | 72 | // VerifyFile verifies the checksum of a file 73 | func (s *Sha256Checksum) VerifyFile(file, sum string) (b bool, err error) { 74 | var calcSum string 75 | //Calculate the checksum of the file 76 | calcSum, err = s.CalculateFile(file) 77 | //Verify the checksum 78 | b = err == nil && sum == calcSum 79 | return 80 | } 81 | 82 | // CalculateFor calculates the checksum of the reader 83 | func (s *Sha256Checksum) CalculateFor(reader io.Reader) (chksum string, err error) { 84 | //Calculate the checksum of the reader 85 | hash := sha256.New() 86 | _, err = io.Copy(hash, reader) 87 | if err == nil { 88 | chksum = fmt.Sprintf("%x", hash.Sum(nil)) 89 | } 90 | return 91 | } 92 | 93 | // VerifyFor verifies the checksum of the reader 94 | func (s *Sha256Checksum) VerifyFor(reader io.Reader, sum string) (b bool, err error) { 95 | var calcSum string 96 | //Calculate the checksum of the reader 97 | calcSum, err = s.CalculateFor(reader) 98 | //Verify the checksum 99 | b = err == nil && sum == calcSum 100 | return 101 | } 102 | 103 | // Type returns the type of the checksum 104 | func (s *Sha256Checksum) Type() string { 105 | return SHA256 106 | } 107 | 108 | // NewChkSumCalc creates a new checksum 109 | func NewChkSumCalc(t string) ChkSumCalc { 110 | switch t { 111 | case SHA256: 112 | return &Sha256Checksum{} 113 | default: 114 | return nil 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct - golly 2 | 3 | ### Introduction 4 | 5 | We are committed to creating a welcoming and inclusive environment for everyone 6 | who participates in our golly open-source project, regardless of their 7 | background, identity, or experience. We expect all contributors, maintainers, 8 | and users to adhere to the following code of conduct to ensure a positive and 9 | respectful community. 10 | 11 | ### Our Pledge 12 | 13 | In order to create an open and inclusive space, we pledge to: 14 | 15 | 1. Foster a welcoming and harassment-free environment where all individuals can 16 | freely express their opinions, ideas, and experiences. 17 | 2. Respect and value diverse perspectives, backgrounds, and experiences, and 18 | avoid any form of discrimination or exclusion. 19 | 3. Encourage constructive and respectful discussions that focus on technical 20 | aspects and project-related topics. 21 | 4. Be patient and understanding towards others, especially those who are new to 22 | the project or have different levels of expertise. 23 | 5. Address conflicts and disagreements constructively, seeking resolution 24 | through open communication and collaboration. 25 | 6. Be accountable for our actions and words, and acknowledge and learn from any 26 | mistakes we make. 27 | 7. Protect the privacy and confidentiality of individuals and their personal 28 | information. 29 | 30 | ### Expected Behavior 31 | 32 | All individuals involved in the project, including but not limited to 33 | contributors, maintainers, and users, are expected to: 34 | 35 | 1. Be respectful and inclusive, irrespective of differences such as gender, 36 | sexual orientation, race, ethnicity, religion, or disability. 37 | 2. Use welcoming and inclusive language, avoiding derogatory, offensive, or 38 | discriminatory comments or jokes. 39 | 3. Listen attentively, consider different perspectives, and avoid personal 40 | attacks or inflammatory behavior. 41 | 4. Provide constructive feedback and suggestions while maintaining a focus on 42 | improving the project. 43 | 5. Be open to learning from others and sharing knowledge in a supportive manner. 44 | 6. Respect the decisions made by project maintainers and administrators, 45 | understanding that they have the final authority on the project's direction 46 | and policies. 47 | 7. Report any violations of this code of conduct to the project maintainers via 48 | the designated communication channels. 49 | 50 | ### Unacceptable Behavior 51 | 52 | The following behaviors are considered unacceptable within our project 53 | community: 54 | 55 | 1. Harassment, bullying, or intimidation in any form, including but not limited 56 | to offensive comments, insults, or personal attacks. 57 | 2. Discrimination, exclusion, or any other action that promotes a hostile or 58 | unwelcome environment. 59 | 3. Publishing or sharing others' private information without their consent. 60 | 4. Deliberate disruption of discussions or the project's progress. 61 | 5. Engaging in or promoting any form of offensive, discriminatory, or 62 | inappropriate material or language. 63 | 6. Any behavior that violates applicable laws or regulations. 64 | 65 | ### Enforcement 66 | 67 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 68 | reported by contacting the project maintainers at 69 | [conduct@nandlabs.io](mailto:conduct@nandlabs.io). All complaints will be 70 | reviewed and investigated promptly and fairly. The maintainers are responsible 71 | for determining the appropriate actions to be taken, which may include warnings, 72 | temporary or permanent bans, or other measures deemed necessary. 73 | -------------------------------------------------------------------------------- /genai/message_test.go: -------------------------------------------------------------------------------- 1 | // Package genai contains tests for the Message struct and its methods. 2 | // 3 | // TestMessage_Mime tests the Mime method of the Message struct to ensure it returns the correct MIME type. 4 | // 5 | // TestMessage_Actor tests the Actor method of the Message struct to ensure it returns the correct actor. 6 | // 7 | // TestMessage_Read tests the Read method of the Message struct to ensure it reads data correctly. 8 | // 9 | // TestMessage_SetActor tests the SetActor method of the Message struct to ensure it sets the actor correctly. 10 | // 11 | // TestMessage_SetMime tests the SetMime method of the Message struct to ensure it sets the MIME type correctly. 12 | // 13 | // TestMessage_Write tests the Write method of the Message struct to ensure it writes data correctly. 14 | // 15 | // TestMessage_URL tests the URL method of the Message struct to ensure it returns the correct URL. 16 | package genai 17 | 18 | import ( 19 | "bytes" 20 | "net/url" 21 | "testing" 22 | 23 | "oss.nandlabs.io/golly/testing/assert" 24 | ) 25 | 26 | // TestMessage_Mime tests the Mime method of the Message struct. 27 | // It verifies that the Mime method returns the correct MIME type 28 | // that was set in the Message instance. 29 | func TestMessage_Mime(t *testing.T) { 30 | msg := &Message{mimeType: "text/plain"} 31 | assert.Equal(t, "text/plain", msg.Mime()) 32 | } 33 | 34 | // TestMessage_Actor tests the Actor method of the Message struct. 35 | // It verifies that the Actor method returns the correct actor that was set in the Message. 36 | func TestMessage_Actor(t *testing.T) { 37 | actor := UserActor 38 | msg := &Message{msgActor: actor} 39 | assert.Equal(t, actor, msg.Actor()) 40 | } 41 | 42 | // TestMessage_Read tests the Read method of the Message struct. 43 | // It verifies that the method reads the correct number of bytes 44 | // and that the data read matches the expected data. 45 | func TestMessage_Read(t *testing.T) { 46 | data := []byte("test data") 47 | rwer := bytes.NewBuffer(data) 48 | msg := &Message{rwer: rwer} 49 | 50 | buf := make([]byte, len(data)) 51 | n, err := msg.Read(buf) 52 | assert.NoError(t, err) 53 | assert.Equal(t, len(data), n) 54 | assert.Equal(t, data, buf) 55 | } 56 | 57 | // TestMessage_SetActor tests the SetActor method of the Message struct. 58 | // It verifies that the actor is correctly set and retrieved using the Actor method. 59 | func TestMessage_SetActor(t *testing.T) { 60 | actor := UserActor 61 | msg := &Message{} 62 | msg.SetActor(actor) 63 | assert.Equal(t, actor, msg.Actor()) 64 | } 65 | 66 | // TestMessage_SetMime tests the SetMime method of the Message struct. 67 | // It verifies that the MIME type is correctly set and retrieved. 68 | func TestMessage_SetMime(t *testing.T) { 69 | mime := "application/json" 70 | msg := &Message{} 71 | msg.SetMime(mime) 72 | assert.Equal(t, mime, msg.Mime()) 73 | } 74 | 75 | // TestMessage_Write tests the Write method of the Message struct. 76 | // It verifies that the data is correctly written to the underlying writer 77 | // and that the number of bytes written and the written data match the expected values. 78 | func TestMessage_Write(t *testing.T) { 79 | data := []byte("test data") 80 | rwer := bytes.NewBuffer(nil) 81 | msg := &Message{rwer: rwer} 82 | 83 | n, err := msg.Write(data) 84 | assert.NoError(t, err) 85 | assert.Equal(t, len(data), n) 86 | assert.Equal(t, data, rwer.Bytes()) 87 | } 88 | 89 | // TestMessage_URL tests the URL method of the Message struct. 90 | // It ensures that the URL method returns the correct URL that was set during the creation of the Message instance. 91 | func TestMessage_URL(t *testing.T) { 92 | u, err := url.Parse("http://example.com") 93 | assert.NoError(t, err) 94 | msg := &Message{u: u} 95 | assert.Equal(t, u, msg.URL()) 96 | } 97 | -------------------------------------------------------------------------------- /genai/template.go: -------------------------------------------------------------------------------- 1 | package genai 2 | 3 | import ( 4 | "io" 5 | "strings" 6 | "sync" 7 | "text/template" 8 | ) 9 | 10 | // TemplateType is the type of the template 11 | type TemplateType string 12 | 13 | // templateCache is a cache of templates 14 | var templateCache map[string]PromptTemplate 15 | 16 | // cacheMutex is a mutex for the cache. This is preferred over using a sync.Map because it is faster in most cases 17 | var cacheMutex sync.RWMutex 18 | 19 | const ( 20 | // GoTextTemplate represents a Go text template 21 | GoTextTemplate TemplateType = "go-text" 22 | ) 23 | 24 | // PromptTemplate is the interface that represents a prompt template 25 | type PromptTemplate interface { 26 | // Id returns the id of the template. This is expected to be unique. 27 | Id() string 28 | // Type returns the type of the template 29 | Type() TemplateType 30 | // FormatAsText formats the template as text 31 | FormatAsText(map[string]any) (string, error) 32 | //WriteTo writes the template to a writer 33 | WriteTo(io.Writer, map[string]any) error 34 | } 35 | 36 | // goTemplate is a template that uses the Go template format 37 | type goTemplate struct { 38 | //template is the Go template string 39 | template *template.Template 40 | //id is the id of the template 41 | id string 42 | } 43 | 44 | func (t *goTemplate) Id() string { 45 | return t.id 46 | } 47 | 48 | // Type returns the format type of the template. 49 | func (t *goTemplate) Type() TemplateType { 50 | return GoTextTemplate 51 | } 52 | 53 | // FormatAsText returns the string format of the template. 54 | func (t *goTemplate) FormatAsText(data map[string]any) (s string, err error) { 55 | sb := new(strings.Builder) 56 | err = t.WriteTo(sb, data) 57 | if err == nil { 58 | s = sb.String() 59 | } 60 | return 61 | } 62 | 63 | // WriteTo writes the template to the writer 64 | func (t *goTemplate) WriteTo(w io.Writer, data map[string]any) error { 65 | //prepare the data 66 | d := prepareData(data) 67 | //execute the template 68 | return t.template.Execute(w, d) 69 | } 70 | 71 | // prepareData prepares the data for the template 72 | // the input data is a map and the value can be a function 73 | func prepareData(data map[string]any) map[string]any { 74 | d := make(map[string]any) 75 | //for each key-value pair in the data 76 | for k, v := range data { 77 | //if the value is a function 78 | if f, ok := v.(func() any); ok { 79 | //set the value to the result of the function 80 | d[k] = f() 81 | } else { 82 | //set the value to the original value 83 | d[k] = v 84 | } 85 | } 86 | //return the data 87 | return d 88 | } 89 | 90 | // GetPromptTemplate returns a prompt template from the cache 91 | func GetPromptTemplate(id string) PromptTemplate { 92 | cacheMutex.RLock() 93 | defer cacheMutex.RUnlock() 94 | return templateCache[id] 95 | } 96 | 97 | // NewGoTemplate returns a prompt template from the cache if it matches the id or creates a new one if it does not exist 98 | func NewGoTemplate(id, content string) (PromptTemplate, error) { 99 | //Check if the template is already in the cache 100 | cacheMutex.Lock() 101 | defer cacheMutex.Unlock() 102 | if _, ok := templateCache[id]; ok { 103 | LOGGER.WarnF("Replacing template with id %s", id) 104 | } 105 | 106 | //Create a new template 107 | tmpl, err := template.New(id).Parse(content) 108 | if err != nil { 109 | return nil, err 110 | } 111 | //Add the template to the cache 112 | templateCache[id] = &goTemplate{template: tmpl, id: id} 113 | return templateCache[id], nil 114 | 115 | } 116 | 117 | // init initializes the cache 118 | func init() { 119 | cacheMutex = sync.RWMutex{} 120 | templateCache = make(map[string]PromptTemplate) 121 | } 122 | -------------------------------------------------------------------------------- /l3/logger_test.go: -------------------------------------------------------------------------------- 1 | package l3 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | // TestGetLogger --> Testing BaseLogger object creation 9 | func TestGetLogger(t *testing.T) { 10 | tests := []struct { 11 | name string 12 | want Logger 13 | }{ 14 | { 15 | name: "BaseLogger", 16 | want: Get(), 17 | }, 18 | } 19 | for _, tt := range tests { 20 | t.Run(tt.name, func(t *testing.T) { 21 | if got := Get(); !reflect.DeepEqual(got, tt.want) { 22 | t.Errorf("Get() = %v, want %v", got, tt.want) 23 | } 24 | }) 25 | } 26 | } 27 | 28 | func TestLogger_IsEnabled(t *testing.T) { 29 | type fields struct { 30 | level Level 31 | pkgName string 32 | errorEnabled bool 33 | warnEnabled bool 34 | infoEnabled bool 35 | debugEnabled bool 36 | traceEnabled bool 37 | includeFunction bool 38 | includeLine bool 39 | } 40 | type args struct { 41 | level Level 42 | } 43 | tests := []struct { 44 | name string 45 | fields fields 46 | args args 47 | want bool 48 | }{ 49 | // TODO: Add test cases. 50 | { 51 | name: "WarnTest_true", 52 | fields: fields{ 53 | level: Warn, 54 | warnEnabled: true, 55 | }, 56 | args: args{ 57 | level: Warn, 58 | }, 59 | want: true, 60 | }, 61 | { 62 | name: "WarnTest_Fail", 63 | fields: fields{ 64 | level: Info, 65 | infoEnabled: true, 66 | }, 67 | args: args{ 68 | level: Warn, 69 | }, 70 | want: false, 71 | }, 72 | { 73 | name: "ErrorTest", 74 | fields: fields{ 75 | level: Err, 76 | errorEnabled: true, 77 | }, 78 | args: args{ 79 | level: Err, 80 | }, 81 | want: true, 82 | }, 83 | { 84 | name: "ErrorTest_Fail", 85 | fields: fields{ 86 | level: Trace, 87 | traceEnabled: true, 88 | }, 89 | args: args{ 90 | level: Err, 91 | }, 92 | want: false, 93 | }, 94 | { 95 | name: "InfoTest", 96 | fields: fields{ 97 | level: Info, 98 | infoEnabled: true, 99 | }, 100 | args: args{ 101 | level: Info, 102 | }, 103 | want: true, 104 | }, 105 | { 106 | name: "InfoTest_Fail", 107 | fields: fields{ 108 | level: Trace, 109 | traceEnabled: true, 110 | }, 111 | args: args{ 112 | level: Info, 113 | }, 114 | want: false, 115 | }, 116 | { 117 | name: "DebugTest", 118 | fields: fields{ 119 | level: Debug, 120 | warnEnabled: true, 121 | }, 122 | args: args{ 123 | level: Debug, 124 | }, 125 | want: true, 126 | }, 127 | { 128 | name: "DebugTest_Fail", 129 | fields: fields{ 130 | level: Trace, 131 | traceEnabled: true, 132 | }, 133 | args: args{ 134 | level: Debug, 135 | }, 136 | want: false, 137 | }, 138 | { 139 | name: "TraceTest", 140 | fields: fields{ 141 | level: Trace, 142 | warnEnabled: true, 143 | }, 144 | args: args{ 145 | level: Trace, 146 | }, 147 | want: true, 148 | }, 149 | } 150 | for _, tt := range tests { 151 | t.Run(tt.name, func(t *testing.T) { 152 | l := &BaseLogger{ 153 | level: tt.fields.level, 154 | pkgName: tt.fields.pkgName, 155 | errorEnabled: tt.fields.errorEnabled, 156 | warnEnabled: tt.fields.warnEnabled, 157 | infoEnabled: tt.fields.infoEnabled, 158 | debugEnabled: tt.fields.debugEnabled, 159 | traceEnabled: tt.fields.traceEnabled, 160 | includeFunction: tt.fields.includeFunction, 161 | includeLine: tt.fields.includeLine, 162 | } 163 | if got := l.IsEnabled(tt.args.level); got != tt.want { 164 | t.Errorf("IsEnabled() = %v, want %v", got, tt.want) 165 | } 166 | }) 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /l3/l3_config.go: -------------------------------------------------------------------------------- 1 | package l3 2 | 3 | // LogConfig - Configuration & Settings for the logger. 4 | type LogConfig struct { 5 | 6 | //Format of the log. valid values are text,json 7 | //Default is text 8 | Format string `json:"format,omitempty" yaml:"format,omitempty"` 9 | //Async Flag to indicate if the writing of the flag is asynchronous. 10 | //Default value is false 11 | Async bool `json:"async,omitempty" yaml:"async,omitempty"` 12 | //QueueSize to indicate the number log routines that can be queued to use in background 13 | //This value is used only if the async value is set to true. 14 | //Default value for the number items to be in queue 512 15 | QueueSize int `json:"queue_size,omitempty" yaml:"queueSize,omitempty"` 16 | //Date - Defaults to time.RFC3339 pattern 17 | DatePattern string `json:"datePattern,omitempty" yaml:"datePattern,omitempty"` 18 | //IncludeFunction will include the calling function name in the log entries 19 | //Default value : false 20 | IncludeFunction bool `json:"includeFunction,omitempty" yaml:"includeFunction,omitempty"` 21 | //IncludeLineNum ,includes Line number for the log file 22 | //If IncludeFunction Line is set to false this config is ignored 23 | IncludeLineNum bool `json:"includeLineNum,omitempty" yaml:"includeLineNum,omitempty"` 24 | //DefaultLvl that will be used as default 25 | DefaultLvl string `json:"defaultLvl" yaml:"defaultLvl"` 26 | //PackageConfig that can be used to 27 | PkgConfigs []*PackageConfig `json:"pkgConfigs" yaml:"pkgConfigs"` 28 | //Writers writers for the logger. Need one for all levels 29 | //If a writer is not found for a specific level it will fallback to os.Stdout if the level is greater then Warn and os.Stderr otherwise 30 | Writers []*WriterConfig `json:"writers" yaml:"writers"` 31 | } 32 | 33 | // PackageConfig configuration 34 | type PackageConfig struct { 35 | //PackageName 36 | PackageName string `json:"pkgName" yaml:"pkgName"` 37 | //Level to be set valid values : OFF,ERROR,WARN,INFO,DEBUG,TRACE 38 | Level string `json:"level" yaml:"level"` 39 | } 40 | 41 | // WriterConfig struct 42 | type WriterConfig struct { 43 | //File reference. Non mandatory but one of file or console logger is required. 44 | File *FileConfig `json:"file,omitempty" yaml:"file,omitempty"` 45 | //Console reference 46 | Console *ConsoleConfig `json:"console,omitempty" yaml:"console,omitempty"` 47 | } 48 | 49 | // FileConfig - Configuration of file based logging 50 | type FileConfig struct { 51 | //FilePath for the file based log writer 52 | DefaultPath string `json:"defaultPath" yaml:"defaultPath"` 53 | ErrorPath string `json:"errorPath" yaml:"errorPath"` 54 | WarnPath string `json:"warnPath" yaml:"warnPath"` 55 | InfoPath string `json:"infoPath" yaml:"infoPath"` 56 | DebugPath string `json:"debugPath" yaml:"debugPath"` 57 | TracePath string `json:"tracePath" yaml:"tracePath"` 58 | //RollType must indicate one for the following(case sensitive). SIZE,DAILY 59 | RollType string `json:"rollType" yaml:"rollType"` 60 | //Max Size of the of the file. Only takes into effect when the RollType="SIZE" 61 | MaxSize int64 `json:"maxSize" yaml:"maxSize"` 62 | //CompressOldFile is taken into effect if file rolling is enabled by setting a RollType. 63 | //Default implementation will just do a GZIP of the file leaving the file with .gz 64 | CompressOldFile bool `json:"compressOldFile" yaml:"compressOldFile"` 65 | } 66 | 67 | // ConsoleConfig - Configuration of console based logging. All Log Levels except ERROR and WARN are written to os.Stdout 68 | // The ERROR and WARN log levels can be written to os.Stdout or os.Stderr, By default they go to os.Stderr 69 | type ConsoleConfig struct { 70 | //WriteErrToStdOut write error messages to os.Stdout . 71 | WriteErrToStdOut bool `json:"errToStdOut" yaml:"errToStdOut"` 72 | //WriteWarnToStdOut write warn messages to os.Stdout . 73 | WriteWarnToStdOut bool `json:"warnToStdOut" yaml:"warnToStdOut"` 74 | } 75 | -------------------------------------------------------------------------------- /lifecycle/README.md: -------------------------------------------------------------------------------- 1 | # golly/lifecycle 2 | 3 | [![Go Reference](https://pkg.go.dev/badge/oss.nandlabs.io/golly/lifecycle.svg)](https://pkg.go.dev/oss.nandlabs.io/golly/lifecycle) 4 | 5 | ## Overview 6 | 7 | The `golly/lifecycle` package provides a lifecycle management library for Go 8 | applications. It allows you to easily manage the lifecycle of your application 9 | components, such as starting and stopping them in a controlled manner. 10 | 11 | ## Installation 12 | 13 | To install the package, use the `go get` command: 14 | 15 | ```sh 16 | go get oss.nandlabs.io/golly/lifecycle 17 | ``` 18 | 19 | ## Usage 20 | 21 | The `golly/lifecycle` package provides a simple and flexible way to manage the lifecycle of your application components. It allows you to start and stop components in a controlled manner, ensuring that they are properly initialized and cleaned up. 22 | 23 | ### Simple Component 24 | 25 | The `simple_component.go` file contains a simple implementation of a component that can be used with the `golly/lifecycle` package. This component demonstrates the basic structure and behavior of a component. 26 | 27 | To use the `SimpleComponent`, follow these steps: 28 | 29 | 1. Import the `golly/lifecycle` package and the `simple_component.go` file. 30 | 31 | ```go 32 | import ( 33 | "oss.nandlabs.io/golly/lifecycle" 34 | "oss.nandlabs.io/golly/lifecycle/examples/simple_component" 35 | ) 36 | ``` 37 | 38 | 2. Create an instance of the `SimpleComponent` struct. 39 | 40 | ```go 41 | simpleComponent := &simple_component.SimpleComponent{} 42 | ``` 43 | 44 | 3. Add the `SimpleComponent` to the `Lifecycle` struct. 45 | 46 | ```go 47 | lifecycle.AddComponent(simpleComponent) 48 | ``` 49 | 50 | 4. Start and stop the components using the `Start` and `Stop` methods of the `Lifecycle` struct. 51 | 52 | ```go 53 | err := lifecycle.Start() 54 | if err != nil { 55 | // handle start error 56 | } 57 | 58 | // ... 59 | 60 | err = lifecycle.Stop() 61 | if err != nil { 62 | // handle stop error 63 | } 64 | ``` 65 | 66 | By following these steps, you can use the `SimpleComponent` in your application and manage its lifecycle along with other components in the `golly/lifecycle` package. 67 | 68 | ## Cusom Components 69 | 70 | The `component.go` file contains the interfaces that define the behavior of components in the `golly/lifecycle` package. These interfaces allow you to create custom components and integrate them into the lifecycle management system. 71 | 72 | To use the `component.go` interfaces, follow these steps: 73 | 74 | 1. Implement the `Component` interface in your custom component struct. This interface defines the `Start` and `Stop` methods that will be called when the component is started or stopped. 75 | 76 | ```go 77 | type MyComponent struct { 78 | // component fields 79 | } 80 | 81 | func (c *MyComponent) Start() error { 82 | // implementation of start logic 83 | return nil 84 | } 85 | 86 | func (c *MyComponent) Stop() error { 87 | // implementation of stop logic 88 | return nil 89 | } 90 | ``` 91 | 92 | 2. Create an instance of your custom component and add it to the `Lifecycle` struct. The `Lifecycle` struct manages the lifecycle of all registered components. 93 | 94 | ```go 95 | lifecycle := &lifecycle.Lifecycle{} 96 | 97 | myComponent := &MyComponent{} 98 | lifecycle.AddComponent(myComponent) 99 | ``` 100 | 101 | 3. Start and stop the components using the `Start` and `Stop` methods of the `Lifecycle` struct. 102 | 103 | ```go 104 | err := lifecycle.Start() 105 | if err != nil { 106 | // handle start error 107 | } 108 | 109 | // ... 110 | 111 | err = lifecycle.Stop() 112 | if err != nil { 113 | // handle stop error 114 | } 115 | ``` 116 | 117 | By following these steps, you can integrate your custom components into the `golly/lifecycle` package and manage their lifecycle in a controlled manner. 118 | 119 | For more information, refer to the [GoDoc](https://pkg.go.dev/oss.nandlabs.io/golly/lifecycle) documentation. 120 | -------------------------------------------------------------------------------- /messaging/local_provider.go: -------------------------------------------------------------------------------- 1 | package messaging 2 | 3 | import ( 4 | "math/rand" 5 | "net/url" 6 | "sync" 7 | 8 | "oss.nandlabs.io/golly/ioutils" 9 | ) 10 | 11 | const ( 12 | LocalMsgScheme = "chan" 13 | unnamedListeners = "__unnamed_listeners__" 14 | ) 15 | 16 | var localProviderSchemes = []string{LocalMsgScheme} 17 | 18 | // LocalProvider is an implementation of the Provider interface 19 | type LocalProvider struct { 20 | mutex sync.Mutex 21 | destinations map[string]chan Message 22 | listeners map[string]map[string][]func(msg Message) 23 | } 24 | 25 | func (lp *LocalProvider) Id() string { 26 | return "local-channel" 27 | } 28 | 29 | func (lp *LocalProvider) NewMessage(scheme string, options ...Option) (msg Message, err error) { 30 | msg, err = NewLocalMessage() 31 | return 32 | } 33 | 34 | func (lp *LocalProvider) getChan(url *url.URL) (result chan Message) { 35 | var ok bool 36 | result, ok = lp.destinations[url.Host] 37 | if !ok { 38 | lp.mutex.Lock() 39 | defer lp.mutex.Unlock() 40 | localMsgChannel := make(chan Message) 41 | lp.destinations[url.Host] = localMsgChannel 42 | result = localMsgChannel 43 | } 44 | return 45 | } 46 | 47 | func (lp *LocalProvider) Send(url *url.URL, msg Message, options ...Option) (err error) { 48 | destination := lp.getChan(url) 49 | go func() { 50 | logger.TraceF("sending message to channel %s", url.Host) 51 | destination <- msg 52 | }() 53 | return 54 | } 55 | 56 | func (lp *LocalProvider) SendBatch(url *url.URL, msgs []Message, options ...Option) (err error) { 57 | for _, message := range msgs { 58 | err = lp.Send(url, message) 59 | if err != nil { 60 | return 61 | } 62 | } 63 | return 64 | } 65 | 66 | func (lp *LocalProvider) Receive(url *url.URL, options ...Option) (msg Message, err error) { 67 | receiver := lp.getChan(url) 68 | for m := range receiver { 69 | msg = m 70 | } 71 | return 72 | } 73 | 74 | func (lp *LocalProvider) ReceiveBatch(url *url.URL, options ...Option) (msgs []Message, err error) { 75 | receiver := lp.getChan(url) 76 | for m := range receiver { 77 | msgs = append(msgs, m) 78 | } 79 | return 80 | } 81 | 82 | func (lp *LocalProvider) AddListener(url *url.URL, listener func(msg Message), options ...Option) (err error) { 83 | // Get channel first before locking to avoid dead locl 84 | channel := lp.getChan(url) 85 | lp.mutex.Lock() 86 | defer lp.mutex.Unlock() 87 | createListener := false 88 | if _, ok := lp.listeners[url.Host]; !ok { 89 | 90 | lp.listeners[url.Host] = make(map[string][]func(msg Message)) 91 | 92 | createListener = true 93 | } 94 | 95 | optionsResolver := NewOptionsResolver(options...) 96 | namedListener, hasNamed := ResolveOptValue[string]("NamedListener", optionsResolver) 97 | if hasNamed { 98 | lp.listeners[url.Host][namedListener] = append(lp.listeners[url.Host][namedListener], listener) 99 | } else { 100 | lp.listeners[url.Host][unnamedListeners] = append(lp.listeners[url.Host][unnamedListeners], listener) 101 | } 102 | 103 | if createListener { 104 | go func() { 105 | 106 | for m := range channel { 107 | for name, listener := range lp.listeners[url.Host] { 108 | if name == unnamedListeners { 109 | for _, l := range listener { 110 | go l(m) 111 | } 112 | } else { 113 | lIDx := rand.Intn(len(listener)) 114 | go listener[lIDx](m) 115 | } 116 | } 117 | } 118 | }() 119 | } 120 | return 121 | } 122 | 123 | func (lp *LocalProvider) Setup() (err error) { 124 | lp.mutex = sync.Mutex{} 125 | lp.destinations = make(map[string]chan Message) 126 | lp.listeners = make(map[string]map[string][]func(msg Message)) 127 | return nil 128 | } 129 | 130 | func (lp *LocalProvider) Close() (err error) { 131 | for dest, ch := range lp.destinations { 132 | logger.TraceF("closing channel for desination %s", dest) 133 | ioutils.CloseChannel[Message](ch) 134 | } 135 | return 136 | } 137 | 138 | func (lp *LocalProvider) Schemes() (schemes []string) { 139 | schemes = localProviderSchemes 140 | return 141 | } 142 | --------------------------------------------------------------------------------