├── 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 | [](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 | [](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 |
--------------------------------------------------------------------------------