Signed-off-by: Sagi Dayan <sagidayan@gmail.com>
This commit is contained in:
commit
2ca1e7826a
1804 changed files with 3967683 additions and 0 deletions
27
.forgejo/workflows/codespell.yml
Normal file
27
.forgejo/workflows/codespell.yml
Normal file
|
@ -0,0 +1,27 @@
|
|||
name: Codespell
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
codespell:
|
||||
name: Check for spelling errors
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Annotate locations with typos
|
||||
uses: "https://github.com/codespell-project/codespell-problem-matcher@v1"
|
||||
- name: Codespell
|
||||
uses: "https://github.com/codespell-project/actions-codespell@v2"
|
||||
with:
|
||||
check_filenames: true
|
||||
check_hidden: true
|
||||
exclude_file: vendor/
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
build/
|
||||
|
||||
# test secrets
|
||||
.secrets
|
46
Makefile
Normal file
46
Makefile
Normal file
|
@ -0,0 +1,46 @@
|
|||
BINARY_NAME=envoid
|
||||
VERSION=1.0.0-alpha
|
||||
BUILD_FOLDER=build
|
||||
|
||||
GO_BUILD_LD_FLAGS=-ldflags="-s -w -X 'git.dayanhub.com/sagi/envoid/internal/variables.Commit=$(shell git rev-parse --short HEAD)' \
|
||||
-X 'git.dayanhub.com/sagi/envoid/internal/variables.Version=${VERSION}'"
|
||||
.PHONY: build
|
||||
build:
|
||||
GOARCH=amd64 GOOS=linux go build ${GO_BUILD_LD_FLAGS} -o ${BUILD_FOLDER}/${BINARY_NAME}-linux main.go
|
||||
GOARCH=amd64 GOOS=darwin go build ${GO_BUILD_LD_FLAGS} -o ${BUILD_FOLDER}/${BINARY_NAME}-darwin main.go
|
||||
GOARCH=amd64 GOOS=windows go build ${GO_BUILD_LD_FLAGS} -o ${BUILD_FOLDER}/${BINARY_NAME}-windows main.go
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
go clean
|
||||
rm -rf ${BUILD_FOLDER}
|
||||
rm -rf ./docs
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test ./...
|
||||
|
||||
.PHONY: test_coverage
|
||||
test_coverage:
|
||||
go test ./... -coverprofile=coverage.out
|
||||
|
||||
.PHONY: dep
|
||||
dep:
|
||||
go mod tidy
|
||||
go mod vendor
|
||||
|
||||
.PHONY: vet
|
||||
vet:
|
||||
go vet
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
golangci-lint run --enable-all
|
||||
|
||||
|
||||
.PHONY: doc-gen
|
||||
doc-gen:
|
||||
rm -rf ./docs
|
||||
mkdir ./docs
|
||||
go run . doc
|
||||
|
7
README.md
Normal file
7
README.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Envoid
|
||||
|
||||
##### The `.env`(s) manager you never knew you needed
|
||||
|
||||
⚠ WIP !
|
||||
|
||||
[Documentation](docs/envoid.md)
|
145
cmd/env.go
Normal file
145
cmd/env.go
Normal file
|
@ -0,0 +1,145 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.dayanhub.com/sagi/envoid/internal/datastore"
|
||||
"git.dayanhub.com/sagi/envoid/internal/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type envCmdFlags struct {
|
||||
rmEnvName *string
|
||||
addEnvName *string
|
||||
baseEnvName *string
|
||||
}
|
||||
|
||||
var envFlags = &envCmdFlags{}
|
||||
|
||||
var envCmd = &cobra.Command{
|
||||
Use: "env",
|
||||
Short: "manage environments",
|
||||
Long: "",
|
||||
}
|
||||
|
||||
var rmEnvCmd = &cobra.Command{
|
||||
Use: "rm",
|
||||
Short: "removes an environment",
|
||||
Long: "",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := initProject(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ds, err := datastore.NewDataStore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ds.Close()
|
||||
err = ds.RemoveEnv(*envFlags.rmEnvName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
project.RemoveEnv(*envFlags.rmEnvName)
|
||||
configuration.Save()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var addEnvCmd = &cobra.Command{
|
||||
Use: "add",
|
||||
Short: "adds an environment",
|
||||
Long: "",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := initProject(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(strings.TrimSpace(*envFlags.addEnvName)) == 0 {
|
||||
return errors.NewInvalidFlagValueError("environment", *envFlags.addEnvName)
|
||||
}
|
||||
|
||||
if _, err := project.GetEnv(*envFlags.addEnvName); err == nil {
|
||||
return errors.NewEnvironmentExistsError(*envFlags.addEnvName)
|
||||
}
|
||||
|
||||
ds, err := datastore.NewDataStore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ds.Close()
|
||||
|
||||
if len(*envFlags.baseEnvName) != 0 {
|
||||
_, err = project.GetEnv(*envFlags.baseEnvName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ds.CreateEnvOffExsisting(*envFlags.addEnvName, *envFlags.baseEnvName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = ds.CreateEnv(*envFlags.addEnvName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = project.NewEnv(*envFlags.addEnvName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
configuration.Save()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var lsEnvCmd = &cobra.Command{
|
||||
Use: "ls",
|
||||
Short: "list all environments in this project",
|
||||
Long: "",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := initProject(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if project.IsEmpty() {
|
||||
return errors.NewProjectEmptyError(project.Name)
|
||||
}
|
||||
|
||||
for _, env := range project.Environments {
|
||||
fmt.Printf("%s\n", env.Name)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
// add
|
||||
envFlags.addEnvName = addEnvCmd.Flags().StringP("environment", "e", "", "environment name")
|
||||
err := addEnvCmd.MarkFlagRequired("environment")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
envFlags.baseEnvName = addEnvCmd.Flags().StringP("base-environment", "b", "", "base environment name (copy data from base)")
|
||||
err = addEnvCmd.RegisterFlagCompletionFunc("base-environment", validEnvironmentNamesComplete)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
//rm
|
||||
envFlags.rmEnvName = rmEnvCmd.Flags().StringP("environment", "e", "", "environment name")
|
||||
err = rmEnvCmd.MarkFlagRequired("environment")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = rmEnvCmd.RegisterFlagCompletionFunc("environment", validEnvironmentNamesComplete)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// ls
|
||||
envCmd.AddCommand(addEnvCmd)
|
||||
envCmd.AddCommand(rmEnvCmd)
|
||||
envCmd.AddCommand(lsEnvCmd)
|
||||
}
|
81
cmd/get.go
Normal file
81
cmd/get.go
Normal file
|
@ -0,0 +1,81 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"errors"
|
||||
"git.dayanhub.com/sagi/envoid/internal/datastore"
|
||||
intErrors "git.dayanhub.com/sagi/envoid/internal/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type getCmdFlags struct {
|
||||
envName *string
|
||||
}
|
||||
|
||||
var getFlags = getCmdFlags{}
|
||||
|
||||
var getCmd = &cobra.Command{
|
||||
Use: "get <key>",
|
||||
Short: "Gets a variable",
|
||||
Long: "",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := initProject()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if project.IsEmpty() {
|
||||
return intErrors.NewProjectEmptyError(project.Name)
|
||||
}
|
||||
|
||||
if len(args) != 1 {
|
||||
return intErrors.NewInvalidCommandError("expected 1 args. <key>")
|
||||
}
|
||||
|
||||
key := args[0]
|
||||
|
||||
env := project.Environments[0]
|
||||
err = checkAmbiguousEnv(*getFlags.envName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(*getFlags.envName) > 0 {
|
||||
e, err := project.GetEnv(*getFlags.envName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
env = e
|
||||
}
|
||||
|
||||
ds, err := datastore.NewDataStore()
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %e", err)
|
||||
}
|
||||
|
||||
defer ds.Close()
|
||||
|
||||
envVar, err := ds.GetVar(env.Name, key)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %s\n", err.Error())
|
||||
if errors.Is(err, &intErrors.NoKeyFoundError{}) {
|
||||
os.Exit(1)
|
||||
} else if errors.Is(err, intErrors.NewInvalidPasswordError()) {
|
||||
os.Exit(1)
|
||||
} else {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
fmt.Println(envVar.Value)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
getFlags.envName = getCmd.Flags().StringP("environment", "e", "", "environment name")
|
||||
err := getCmd.RegisterFlagCompletionFunc("environment", validEnvironmentNamesComplete)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
77
cmd/import.go
Normal file
77
cmd/import.go
Normal file
|
@ -0,0 +1,77 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"git.dayanhub.com/sagi/envoid/internal/datastore"
|
||||
"git.dayanhub.com/sagi/envoid/internal/errors"
|
||||
"git.dayanhub.com/sagi/envoid/internal/types"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type importCmdFlags struct {
|
||||
envName *string
|
||||
}
|
||||
|
||||
var importFlags = importCmdFlags{}
|
||||
|
||||
var importCmd = &cobra.Command{
|
||||
Use: "import <file>",
|
||||
Short: "import a .env file into environment(s)",
|
||||
Long: "This will not encrypt any value. You can then use `set encrypt KEY_NAME` to encrypt",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := initProject()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if project.IsEmpty() {
|
||||
return errors.NewProjectEmptyError(project.Name)
|
||||
}
|
||||
|
||||
envs := project.Environments
|
||||
if len(*importFlags.envName) != 0 {
|
||||
e, err := project.GetEnv(*importFlags.envName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
envs = []*types.Environment{e}
|
||||
}
|
||||
if len(args) != 1 {
|
||||
return fmt.Errorf("Needs a file to parse")
|
||||
}
|
||||
|
||||
file, err := os.Open(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m, err := godotenv.Parse(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ds, err := datastore.NewDataStore()
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %e", err)
|
||||
}
|
||||
|
||||
defer ds.Close()
|
||||
for k, v := range m {
|
||||
err = ds.SetValue(k, v, nil, envs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
importFlags.envName = importCmd.Flags().StringP("environment", "e", "", "environments name")
|
||||
err := importCmd.RegisterFlagCompletionFunc("environment", validEnvironmentNamesComplete)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
79
cmd/init.go
Normal file
79
cmd/init.go
Normal file
|
@ -0,0 +1,79 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.dayanhub.com/sagi/envoid/internal/datastore"
|
||||
"git.dayanhub.com/sagi/envoid/internal/prompt"
|
||||
"git.dayanhub.com/sagi/envoid/internal/variables"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var initCmd = &cobra.Command{
|
||||
Use: "init",
|
||||
Short: "creates a new project for this path. default env is set to `local`",
|
||||
Long: "",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
_, err := configuration.GetProject(workingDir)
|
||||
if err == nil {
|
||||
fmt.Printf("Project already exists. please remove if you wish to override\n")
|
||||
return nil
|
||||
}
|
||||
ds, err := datastore.NewDataStore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ds.Close()
|
||||
|
||||
var name string
|
||||
var pass string
|
||||
var envNames []string
|
||||
|
||||
//check if we are importing an existing file
|
||||
if ds.DoesFileExists() {
|
||||
fmt.Printf("Project was not found locally. But found %s file in path.\n", variables.DBFileName)
|
||||
fmt.Printf("Importing Project for %s\n", workingDir)
|
||||
|
||||
name = prompt.StringPrompt("Project Name:")
|
||||
pass = prompt.PasswordPrompt("Project Password (Same password that was used originaly):")
|
||||
|
||||
envNames, err = ds.ListEnvironments()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Found %d environments. importing %v\n", len(envNames), envNames)
|
||||
|
||||
} else {
|
||||
// New Project
|
||||
fmt.Printf("Creating new Project @ %s\n", workingDir)
|
||||
name = prompt.StringPrompt("Project Name:")
|
||||
pass = prompt.PasswordPrompt("Project Password:")
|
||||
envs := strings.TrimSpace(prompt.StringPrompt("Please provide environment names seperated by `,`. (stage,prod):"))
|
||||
if len(envs) == 0 {
|
||||
envs = "local"
|
||||
}
|
||||
envNames = strings.Split(envs, ",")
|
||||
}
|
||||
|
||||
project, err = configuration.NewProject(name, workingDir, pass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, eName := range envNames {
|
||||
if err := project.NewEnv(eName); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ds.CreateEnv(eName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
configuration.Save()
|
||||
fmt.Println("✅ Done")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
}
|
81
cmd/printenv.go
Normal file
81
cmd/printenv.go
Normal file
|
@ -0,0 +1,81 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.dayanhub.com/sagi/envoid/internal/datastore"
|
||||
intErrors "git.dayanhub.com/sagi/envoid/internal/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type printenvCmdFlags struct {
|
||||
envName *string
|
||||
}
|
||||
|
||||
var printenvFlags = printenvCmdFlags{}
|
||||
|
||||
var printenvCmd = &cobra.Command{
|
||||
Use: "printenv",
|
||||
Short: "prints the whole environment in a .env format",
|
||||
Long: "",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := initProject()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if project.IsEmpty() {
|
||||
return intErrors.NewProjectEmptyError(project.Name)
|
||||
}
|
||||
|
||||
if len(args) != 0 {
|
||||
return intErrors.NewInvalidCommandError("expected 0 args.")
|
||||
}
|
||||
err = checkAmbiguousEnv(*printenvFlags.envName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
env := project.Environments[0]
|
||||
if len(*printenvFlags.envName) > 0 {
|
||||
e, err := project.GetEnv(*printenvFlags.envName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
env = e
|
||||
}
|
||||
datastore, err := datastore.NewDataStore()
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %e", err)
|
||||
}
|
||||
|
||||
defer datastore.Close()
|
||||
vars, err := datastore.GetAll(env.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range vars {
|
||||
if v.Encrypted {
|
||||
fmt.Println("# SENSITIVE VAR BELOW")
|
||||
}
|
||||
if len(v.Value) != len(strings.ReplaceAll(v.Value, " ", "")) {
|
||||
// value contain spaces. need to wrap with ""
|
||||
fmt.Printf("%s=\"%s\"\n", v.Key, v.Value)
|
||||
} else {
|
||||
fmt.Printf("%s=%s\n", v.Key, v.Value)
|
||||
}
|
||||
if v.Encrypted {
|
||||
fmt.Println("###")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
printenvFlags.envName = printenvCmd.Flags().StringP("environment", "e", "", "environments name")
|
||||
err := printenvCmd.RegisterFlagCompletionFunc("environment", validEnvironmentNamesComplete)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
57
cmd/project.go
Normal file
57
cmd/project.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type projectCmdFlags struct {
|
||||
projectPath *string
|
||||
}
|
||||
|
||||
var projectFlags = projectCmdFlags{}
|
||||
|
||||
var projectCmd = &cobra.Command{
|
||||
Use: "project",
|
||||
Short: "manage project",
|
||||
Long: "",
|
||||
}
|
||||
|
||||
var lsProjectCmd = &cobra.Command{
|
||||
Use: "ls",
|
||||
Short: "list all projects for this user",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
for _, proj := range configuration.Projects {
|
||||
fmt.Printf("%s\t%s\n", proj.Name, proj.Path)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
var rmProjectCmd = &cobra.Command{
|
||||
Use: "rm",
|
||||
Short: "remove a project definition. the `.envoid` file will not be removed",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
project, err := configuration.GetProject(*projectFlags.projectPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
configuration.RemoveProject(project)
|
||||
configuration.Save()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
projectFlags.projectPath = rmProjectCmd.Flags().StringP("project-path", "p", "", "project path to remove")
|
||||
err := rmProjectCmd.MarkFlagRequired("project-path")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = rmProjectCmd.RegisterFlagCompletionFunc("project-path", validProjectPathComplete)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
projectCmd.AddCommand(lsProjectCmd)
|
||||
projectCmd.AddCommand(rmProjectCmd)
|
||||
}
|
62
cmd/rm.go
Normal file
62
cmd/rm.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.dayanhub.com/sagi/envoid/internal/datastore"
|
||||
"git.dayanhub.com/sagi/envoid/internal/errors"
|
||||
"git.dayanhub.com/sagi/envoid/internal/types"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type rmCmdFlags struct {
|
||||
envName *string
|
||||
}
|
||||
|
||||
var rmFlags = rmCmdFlags{}
|
||||
|
||||
var rmCmd = &cobra.Command{
|
||||
Use: "rm <key>",
|
||||
Short: "removes a variable from environment(s)",
|
||||
Long: "",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := initProject()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if project.IsEmpty() {
|
||||
return errors.NewProjectEmptyError(project.Name)
|
||||
}
|
||||
if len(args) != 1 {
|
||||
return errors.NewInvalidCommandError("expected 1 args. <key>")
|
||||
}
|
||||
|
||||
key := args[0]
|
||||
|
||||
ds, err := datastore.NewDataStore()
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %e", err)
|
||||
}
|
||||
defer ds.Close()
|
||||
|
||||
envs := project.Environments
|
||||
if len(*rmFlags.envName) != 0 {
|
||||
e, err := project.GetEnv(*rmFlags.envName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
envs = []*types.Environment{e}
|
||||
}
|
||||
ds.RemoveVar(key, envs)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rmFlags.envName = rmCmd.Flags().StringP("environment", "e", "", "environments name")
|
||||
err := rmCmd.RegisterFlagCompletionFunc("environment", validEnvironmentNamesComplete)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
99
cmd/root.go
Normal file
99
cmd/root.go
Normal file
|
@ -0,0 +1,99 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"git.dayanhub.com/sagi/envoid/internal/config"
|
||||
"git.dayanhub.com/sagi/envoid/internal/types"
|
||||
"git.dayanhub.com/sagi/envoid/internal/variables"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/cobra/doc"
|
||||
)
|
||||
|
||||
var configuration *config.Config = nil
|
||||
var workingDir string
|
||||
var project *types.Project = nil
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "envoid [command]",
|
||||
Short: "envoid is an easy to use .env manager for personal (non production) use",
|
||||
Long: `envoid is an easy to use .env manager for personal (non production) use.
|
||||
|
||||
envoid works offline and creates different encrypted environments for each project. It's mainly used to store,encrypt (with a passphrase) and share
|
||||
.env variables`,
|
||||
Version: fmt.Sprintf("%s commit %s", variables.Version, variables.Commit),
|
||||
}
|
||||
|
||||
var docCmd = &cobra.Command{
|
||||
Use: "doc",
|
||||
Hidden: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := doc.GenMarkdownTree(rootCmd, "./docs")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func checkAmbiguousEnv(envName string) error {
|
||||
if len(envName) == 0 && len(project.Environments) > 1 {
|
||||
return fmt.Errorf("You have more than 1 environment. please provide environment name")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func initProject() error {
|
||||
p, err := configuration.GetProject(workingDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
project = p
|
||||
return nil
|
||||
}
|
||||
|
||||
func validEnvironmentNamesComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
envs := []string{}
|
||||
if err := initProject(); err == nil {
|
||||
for _, e := range project.Environments {
|
||||
envs = append(envs, e.Name)
|
||||
}
|
||||
}
|
||||
return envs, cobra.ShellCompDirectiveDefault
|
||||
}
|
||||
|
||||
func validProjectPathComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
paths := []string{}
|
||||
for _, p := range configuration.Projects {
|
||||
paths = append(paths, fmt.Sprintf("%s\t%s", p.Path, p.Name))
|
||||
}
|
||||
return paths, cobra.ShellCompDirectiveDefault
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(docCmd)
|
||||
rootCmd.AddCommand(initCmd)
|
||||
rootCmd.AddCommand(setCmd)
|
||||
rootCmd.AddCommand(rmCmd)
|
||||
rootCmd.AddCommand(getCmd)
|
||||
rootCmd.AddCommand(envCmd)
|
||||
rootCmd.AddCommand(printenvCmd)
|
||||
rootCmd.AddCommand(importCmd)
|
||||
rootCmd.AddCommand(projectCmd)
|
||||
|
||||
configuration = config.GetConfig()
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
fmt.Printf("[error] %s. please run 'ssecret init'\n", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
workingDir = pwd
|
||||
}
|
125
cmd/set.go
Normal file
125
cmd/set.go
Normal file
|
@ -0,0 +1,125 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.dayanhub.com/sagi/envoid/internal/common"
|
||||
"git.dayanhub.com/sagi/envoid/internal/datastore"
|
||||
"git.dayanhub.com/sagi/envoid/internal/errors"
|
||||
"git.dayanhub.com/sagi/envoid/internal/types"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type setCmdFlags struct {
|
||||
envName *string
|
||||
encrypt *bool
|
||||
}
|
||||
|
||||
var setFlags = setCmdFlags{}
|
||||
|
||||
var setCmd = &cobra.Command{
|
||||
Use: "set [flags] <key> <value>",
|
||||
Short: "sets a varibale in environment(s)",
|
||||
Long: "",
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 2 {
|
||||
return errors.NewInvalidCommandError("expected 2 args. <key> <value>")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := initProject()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if project.IsEmpty() {
|
||||
return errors.NewProjectEmptyError(project.Name)
|
||||
}
|
||||
key := args[0]
|
||||
val := args[1]
|
||||
|
||||
ds, err := datastore.NewDataStore()
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %e", err)
|
||||
}
|
||||
|
||||
defer ds.Close()
|
||||
envs := project.Environments
|
||||
if len(*setFlags.envName) != 0 {
|
||||
e, err := project.GetEnv(*setFlags.envName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
envs = []*types.Environment{e}
|
||||
}
|
||||
|
||||
err = ds.SetValue(key, val, setFlags.encrypt, envs)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %s\n", err.Error())
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var setEncryptCmd = &cobra.Command{
|
||||
Use: "encrypt <key>",
|
||||
Short: "encrypts an existing variable in envrironment(s)",
|
||||
Long: "",
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != 1 {
|
||||
return errors.NewInvalidCommandError("expected 1 args. <key>")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := initProject()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if project.IsEmpty() {
|
||||
return errors.NewProjectEmptyError(project.Name)
|
||||
}
|
||||
key := args[0]
|
||||
|
||||
ds, err := datastore.NewDataStore()
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %e", err)
|
||||
}
|
||||
|
||||
defer ds.Close()
|
||||
envs := project.Environments
|
||||
if len(*setFlags.envName) != 0 {
|
||||
e, err := project.GetEnv(*setFlags.envName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
envs = []*types.Environment{e}
|
||||
}
|
||||
|
||||
for _, env := range envs {
|
||||
// Get value
|
||||
v, err := ds.GetVar(env.Name, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !v.Encrypted {
|
||||
err := ds.SetValue(v.Key, v.Value, common.BoolP(true), []*types.Environment{env})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
setFlags.envName = setCmd.PersistentFlags().StringP("environment", "e", "", "environments name")
|
||||
setFlags.encrypt = setCmd.Flags().BoolP("secret", "s", false, "value is a secret. encrypt this value")
|
||||
err := setCmd.RegisterFlagCompletionFunc("environment", validEnvironmentNamesComplete)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
setCmd.AddCommand(setEncryptCmd)
|
||||
}
|
30
docs/envoid.md
Normal file
30
docs/envoid.md
Normal file
|
@ -0,0 +1,30 @@
|
|||
## envoid
|
||||
|
||||
envoid is an easy to use .env manager for personal (non production) use
|
||||
|
||||
### Synopsis
|
||||
|
||||
envoid is an easy to use .env manager for personal (non production) use.
|
||||
|
||||
envoid works offline and creates different encrypted environments for each project. It's mainly used to store,encrypt (with a passphrase) and share
|
||||
.env variables
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for envoid
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [envoid completion](envoid_completion.md) - Generate the autocompletion script for the specified shell
|
||||
* [envoid env](envoid_env.md) - manage environments
|
||||
* [envoid get](envoid_get.md) - Gets a variable
|
||||
* [envoid import](envoid_import.md) - import a .env file into environment(s)
|
||||
* [envoid init](envoid_init.md) - creates a new project for this path. default env is set to `local`
|
||||
* [envoid printenv](envoid_printenv.md) - prints the whole environment in a .env format
|
||||
* [envoid project](envoid_project.md) - manage project
|
||||
* [envoid rm](envoid_rm.md) - removes a variable from environment(s)
|
||||
* [envoid set](envoid_set.md) - sets a varibale in environment(s)
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Nov-2024
|
25
docs/envoid_completion.md
Normal file
25
docs/envoid_completion.md
Normal file
|
@ -0,0 +1,25 @@
|
|||
## envoid completion
|
||||
|
||||
Generate the autocompletion script for the specified shell
|
||||
|
||||
### Synopsis
|
||||
|
||||
Generate the autocompletion script for envoid for the specified shell.
|
||||
See each sub-command's help for details on how to use the generated script.
|
||||
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for completion
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [envoid](envoid.md) - envoid is an easy to use .env manager for personal (non production) use
|
||||
* [envoid completion bash](envoid_completion_bash.md) - Generate the autocompletion script for bash
|
||||
* [envoid completion fish](envoid_completion_fish.md) - Generate the autocompletion script for fish
|
||||
* [envoid completion powershell](envoid_completion_powershell.md) - Generate the autocompletion script for powershell
|
||||
* [envoid completion zsh](envoid_completion_zsh.md) - Generate the autocompletion script for zsh
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Nov-2024
|
44
docs/envoid_completion_bash.md
Normal file
44
docs/envoid_completion_bash.md
Normal file
|
@ -0,0 +1,44 @@
|
|||
## envoid completion bash
|
||||
|
||||
Generate the autocompletion script for bash
|
||||
|
||||
### Synopsis
|
||||
|
||||
Generate the autocompletion script for the bash shell.
|
||||
|
||||
This script depends on the 'bash-completion' package.
|
||||
If it is not installed already, you can install it via your OS's package manager.
|
||||
|
||||
To load completions in your current shell session:
|
||||
|
||||
source <(envoid completion bash)
|
||||
|
||||
To load completions for every new session, execute once:
|
||||
|
||||
#### Linux:
|
||||
|
||||
envoid completion bash > /etc/bash_completion.d/envoid
|
||||
|
||||
#### macOS:
|
||||
|
||||
envoid completion bash > $(brew --prefix)/etc/bash_completion.d/envoid
|
||||
|
||||
You will need to start a new shell for this setup to take effect.
|
||||
|
||||
|
||||
```
|
||||
envoid completion bash
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for bash
|
||||
--no-descriptions disable completion descriptions
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [envoid completion](envoid_completion.md) - Generate the autocompletion script for the specified shell
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Nov-2024
|
35
docs/envoid_completion_fish.md
Normal file
35
docs/envoid_completion_fish.md
Normal file
|
@ -0,0 +1,35 @@
|
|||
## envoid completion fish
|
||||
|
||||
Generate the autocompletion script for fish
|
||||
|
||||
### Synopsis
|
||||
|
||||
Generate the autocompletion script for the fish shell.
|
||||
|
||||
To load completions in your current shell session:
|
||||
|
||||
envoid completion fish | source
|
||||
|
||||
To load completions for every new session, execute once:
|
||||
|
||||
envoid completion fish > ~/.config/fish/completions/envoid.fish
|
||||
|
||||
You will need to start a new shell for this setup to take effect.
|
||||
|
||||
|
||||
```
|
||||
envoid completion fish [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for fish
|
||||
--no-descriptions disable completion descriptions
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [envoid completion](envoid_completion.md) - Generate the autocompletion script for the specified shell
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Nov-2024
|
32
docs/envoid_completion_powershell.md
Normal file
32
docs/envoid_completion_powershell.md
Normal file
|
@ -0,0 +1,32 @@
|
|||
## envoid completion powershell
|
||||
|
||||
Generate the autocompletion script for powershell
|
||||
|
||||
### Synopsis
|
||||
|
||||
Generate the autocompletion script for powershell.
|
||||
|
||||
To load completions in your current shell session:
|
||||
|
||||
envoid completion powershell | Out-String | Invoke-Expression
|
||||
|
||||
To load completions for every new session, add the output of the above command
|
||||
to your powershell profile.
|
||||
|
||||
|
||||
```
|
||||
envoid completion powershell [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for powershell
|
||||
--no-descriptions disable completion descriptions
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [envoid completion](envoid_completion.md) - Generate the autocompletion script for the specified shell
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Nov-2024
|
46
docs/envoid_completion_zsh.md
Normal file
46
docs/envoid_completion_zsh.md
Normal file
|
@ -0,0 +1,46 @@
|
|||
## envoid completion zsh
|
||||
|
||||
Generate the autocompletion script for zsh
|
||||
|
||||
### Synopsis
|
||||
|
||||
Generate the autocompletion script for the zsh shell.
|
||||
|
||||
If shell completion is not already enabled in your environment you will need
|
||||
to enable it. You can execute the following once:
|
||||
|
||||
echo "autoload -U compinit; compinit" >> ~/.zshrc
|
||||
|
||||
To load completions in your current shell session:
|
||||
|
||||
source <(envoid completion zsh)
|
||||
|
||||
To load completions for every new session, execute once:
|
||||
|
||||
#### Linux:
|
||||
|
||||
envoid completion zsh > "${fpath[1]}/_envoid"
|
||||
|
||||
#### macOS:
|
||||
|
||||
envoid completion zsh > $(brew --prefix)/share/zsh/site-functions/_envoid
|
||||
|
||||
You will need to start a new shell for this setup to take effect.
|
||||
|
||||
|
||||
```
|
||||
envoid completion zsh [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for zsh
|
||||
--no-descriptions disable completion descriptions
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [envoid completion](envoid_completion.md) - Generate the autocompletion script for the specified shell
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Nov-2024
|
18
docs/envoid_env.md
Normal file
18
docs/envoid_env.md
Normal file
|
@ -0,0 +1,18 @@
|
|||
## envoid env
|
||||
|
||||
manage environments
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for env
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [envoid](envoid.md) - envoid is an easy to use .env manager for personal (non production) use
|
||||
* [envoid env add](envoid_env_add.md) - adds an environment
|
||||
* [envoid env ls](envoid_env_ls.md) - list all environments in this project
|
||||
* [envoid env rm](envoid_env_rm.md) - removes an environment
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Nov-2024
|
21
docs/envoid_env_add.md
Normal file
21
docs/envoid_env_add.md
Normal file
|
@ -0,0 +1,21 @@
|
|||
## envoid env add
|
||||
|
||||
adds an environment
|
||||
|
||||
```
|
||||
envoid env add [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-b, --base-environment string base environment name (copy data from base)
|
||||
-e, --environment string environment name
|
||||
-h, --help help for add
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [envoid env](envoid_env.md) - manage environments
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Nov-2024
|
19
docs/envoid_env_ls.md
Normal file
19
docs/envoid_env_ls.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
## envoid env ls
|
||||
|
||||
list all environments in this project
|
||||
|
||||
```
|
||||
envoid env ls [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for ls
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [envoid env](envoid_env.md) - manage environments
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Nov-2024
|
20
docs/envoid_env_rm.md
Normal file
20
docs/envoid_env_rm.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
## envoid env rm
|
||||
|
||||
removes an environment
|
||||
|
||||
```
|
||||
envoid env rm [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-e, --environment string environment name
|
||||
-h, --help help for rm
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [envoid env](envoid_env.md) - manage environments
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Nov-2024
|
20
docs/envoid_get.md
Normal file
20
docs/envoid_get.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
## envoid get
|
||||
|
||||
Gets a variable
|
||||
|
||||
```
|
||||
envoid get <key> [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-e, --environment string environment name
|
||||
-h, --help help for get
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [envoid](envoid.md) - envoid is an easy to use .env manager for personal (non production) use
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Nov-2024
|
24
docs/envoid_import.md
Normal file
24
docs/envoid_import.md
Normal file
|
@ -0,0 +1,24 @@
|
|||
## envoid import
|
||||
|
||||
import a .env file into environment(s)
|
||||
|
||||
### Synopsis
|
||||
|
||||
This will not encrypt any value. You can then use `set encrypt KEY_NAME` to encrypt
|
||||
|
||||
```
|
||||
envoid import <file> [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-e, --environment string environments name
|
||||
-h, --help help for import
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [envoid](envoid.md) - envoid is an easy to use .env manager for personal (non production) use
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Nov-2024
|
19
docs/envoid_init.md
Normal file
19
docs/envoid_init.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
## envoid init
|
||||
|
||||
creates a new project for this path. default env is set to `local`
|
||||
|
||||
```
|
||||
envoid init [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for init
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [envoid](envoid.md) - envoid is an easy to use .env manager for personal (non production) use
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Nov-2024
|
20
docs/envoid_printenv.md
Normal file
20
docs/envoid_printenv.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
## envoid printenv
|
||||
|
||||
prints the whole environment in a .env format
|
||||
|
||||
```
|
||||
envoid printenv [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-e, --environment string environments name
|
||||
-h, --help help for printenv
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [envoid](envoid.md) - envoid is an easy to use .env manager for personal (non production) use
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Nov-2024
|
17
docs/envoid_project.md
Normal file
17
docs/envoid_project.md
Normal file
|
@ -0,0 +1,17 @@
|
|||
## envoid project
|
||||
|
||||
manage project
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for project
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [envoid](envoid.md) - envoid is an easy to use .env manager for personal (non production) use
|
||||
* [envoid project ls](envoid_project_ls.md) - list all projects for this user
|
||||
* [envoid project rm](envoid_project_rm.md) - remove a project definition. the `.envoid` file will not be removed
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Nov-2024
|
19
docs/envoid_project_ls.md
Normal file
19
docs/envoid_project_ls.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
## envoid project ls
|
||||
|
||||
list all projects for this user
|
||||
|
||||
```
|
||||
envoid project ls [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for ls
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [envoid project](envoid_project.md) - manage project
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Nov-2024
|
20
docs/envoid_project_rm.md
Normal file
20
docs/envoid_project_rm.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
## envoid project rm
|
||||
|
||||
remove a project definition. the `.envoid` file will not be removed
|
||||
|
||||
```
|
||||
envoid project rm [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for rm
|
||||
-p, --project-path string project path to remove
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [envoid project](envoid_project.md) - manage project
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Nov-2024
|
20
docs/envoid_rm.md
Normal file
20
docs/envoid_rm.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
## envoid rm
|
||||
|
||||
removes a variable from environment(s)
|
||||
|
||||
```
|
||||
envoid rm <key> [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-e, --environment string environments name
|
||||
-h, --help help for rm
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [envoid](envoid.md) - envoid is an easy to use .env manager for personal (non production) use
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Nov-2024
|
22
docs/envoid_set.md
Normal file
22
docs/envoid_set.md
Normal file
|
@ -0,0 +1,22 @@
|
|||
## envoid set
|
||||
|
||||
sets a varibale in environment(s)
|
||||
|
||||
```
|
||||
envoid set [flags] <key> <value>
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-e, --environment string environments name
|
||||
-h, --help help for set
|
||||
-s, --secret value is a secret. encrypt this value
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [envoid](envoid.md) - envoid is an easy to use .env manager for personal (non production) use
|
||||
* [envoid set encrypt](envoid_set_encrypt.md) - encrypts an existing variable in envrironment(s)
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Nov-2024
|
25
docs/envoid_set_encrypt.md
Normal file
25
docs/envoid_set_encrypt.md
Normal file
|
@ -0,0 +1,25 @@
|
|||
## envoid set encrypt
|
||||
|
||||
encrypts an existing variable in envrironment(s)
|
||||
|
||||
```
|
||||
envoid set encrypt <key> [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for encrypt
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
-e, --environment string environments name
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [envoid set](envoid_set.md) - sets a varibale in environment(s)
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Nov-2024
|
30
go.mod
Normal file
30
go.mod
Normal file
|
@ -0,0 +1,30 @@
|
|||
module git.dayanhub.com/sagi/envoid
|
||||
|
||||
go 1.23.3
|
||||
|
||||
require (
|
||||
github.com/creasty/defaults v1.8.0
|
||||
github.com/glebarez/go-sqlite v1.22.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/spf13/cobra v1.8.1
|
||||
golang.org/x/crypto v0.29.0
|
||||
golang.org/x/sync v0.9.0
|
||||
golang.org/x/term v0.26.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/google/uuid v1.5.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
golang.org/x/sys v0.27.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.37.6 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.7.2 // indirect
|
||||
modernc.org/sqlite v1.28.0 // indirect
|
||||
)
|
47
go.sum
Normal file
47
go.sum
Normal file
|
@ -0,0 +1,47 @@
|
|||
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creasty/defaults v1.8.0 h1:z27FJxCAa0JKt3utc0sCImAEb+spPucmKoOdLHvHYKk=
|
||||
github.com/creasty/defaults v1.8.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
|
||||
github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
||||
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
|
||||
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
modernc.org/libc v1.37.6 h1:orZH3c5wmhIQFTXF+Nt+eeauyd+ZIt2BX6ARe+kD+aw=
|
||||
modernc.org/libc v1.37.6/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE=
|
||||
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
|
||||
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
|
||||
modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ=
|
||||
modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0=
|
9
internal/common/pointers.go
Normal file
9
internal/common/pointers.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package common
|
||||
|
||||
func BoolP(val bool) *bool {
|
||||
return &val
|
||||
}
|
||||
|
||||
func StringP(val string) *string {
|
||||
return &val
|
||||
}
|
10
internal/common/str_convert.go
Normal file
10
internal/common/str_convert.go
Normal file
|
@ -0,0 +1,10 @@
|
|||
package common
|
||||
|
||||
import "strings"
|
||||
|
||||
func StrToSnakeCase(s string) string {
|
||||
snake := strings.ToLower(s)
|
||||
snake = strings.ReplaceAll(snake, " ", "_")
|
||||
snake = strings.ReplaceAll(snake, "\t", "_")
|
||||
return snake
|
||||
}
|
145
internal/config/config.go
Normal file
145
internal/config/config.go
Normal file
|
@ -0,0 +1,145 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"git.dayanhub.com/sagi/envoid/internal/errors"
|
||||
"git.dayanhub.com/sagi/envoid/internal/types"
|
||||
"github.com/creasty/defaults"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
PWD string `json:"-"`
|
||||
Projects []*types.Project `json:"projects" default:"[]"`
|
||||
}
|
||||
|
||||
var configPath string
|
||||
var configStruct *Config
|
||||
|
||||
func (c *Config) GetProject(projectPath string) (*types.Project, error) {
|
||||
var p *types.Project
|
||||
for _, project := range configStruct.Projects {
|
||||
if project.Path == projectPath {
|
||||
p = project
|
||||
break
|
||||
}
|
||||
}
|
||||
if p == nil {
|
||||
return nil, errors.NewProjectNotFoundError(projectPath)
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (c *Config) RemoveProject(project *types.Project) {
|
||||
projects := []*types.Project{}
|
||||
for _, p := range c.Projects {
|
||||
if p.Name != project.Name && p.Path != project.Path {
|
||||
projects = append(projects, p)
|
||||
}
|
||||
}
|
||||
c.Projects = projects
|
||||
}
|
||||
|
||||
func (c *Config) NewProject(name string, path string, password string) (*types.Project, error) {
|
||||
encPass := base64.RawStdEncoding.EncodeToString([]byte(password))
|
||||
|
||||
p := &types.Project{
|
||||
Name: name,
|
||||
Path: path,
|
||||
Password: encPass,
|
||||
Environments: []*types.Environment{},
|
||||
}
|
||||
configStruct.Projects = append(configStruct.Projects, p)
|
||||
SaveConfig()
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func GetConfig() *Config {
|
||||
return configStruct
|
||||
}
|
||||
|
||||
func (c *Config) Save() {
|
||||
SaveConfig()
|
||||
}
|
||||
|
||||
func CreateConfigIfNotExists() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
userConfigDir, err := os.UserConfigDir()
|
||||
configDir := path.Join(userConfigDir, "envoid")
|
||||
configPath = path.Join(configDir, "config.json")
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("[ERROR] Failed to fetch user config directory. %e\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if _, err := os.Stat(configDir); os.IsNotExist(err) {
|
||||
err := os.MkdirAll(configDir, 0700)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
var configFile *os.File
|
||||
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
||||
configFile, err = os.Create(configPath)
|
||||
defer func() {
|
||||
err = configFile.Close()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
fmt.Printf("[ERROR] Failed to create config file @ %s. %e\n", configPath, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
configStruct, err = loadConfig()
|
||||
if err != nil {
|
||||
fmt.Printf("[ERROR] Failed to load config file @ %s. %e\n", configPath, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func loadConfig() (*Config, error) {
|
||||
c := &Config{}
|
||||
err := defaults.Set(c)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
file, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(file) == 0 {
|
||||
return c, nil
|
||||
}
|
||||
if err = json.Unmarshal(file, c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.PWD = pwd
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func SaveConfig() {
|
||||
yml, err := json.Marshal(configStruct)
|
||||
if err != nil {
|
||||
fmt.Printf("[ERROR] Failed to convert config to json. %e\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = os.WriteFile(configPath, yml, 0600)
|
||||
if err != nil {
|
||||
fmt.Printf("[ERROR] Failed to save config file @ %s. %e\n", configPath, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
223
internal/datastore/datastore.go
Normal file
223
internal/datastore/datastore.go
Normal file
|
@ -0,0 +1,223 @@
|
|||
package datastore
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"git.dayanhub.com/sagi/envoid/internal/common"
|
||||
"git.dayanhub.com/sagi/envoid/internal/config"
|
||||
intErrors "git.dayanhub.com/sagi/envoid/internal/errors"
|
||||
"git.dayanhub.com/sagi/envoid/internal/types"
|
||||
"git.dayanhub.com/sagi/envoid/internal/variables"
|
||||
"golang.org/x/crypto/scrypt"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type datastore struct {
|
||||
db *db
|
||||
}
|
||||
|
||||
func NewDataStore() (*datastore, error) {
|
||||
db, err := newDB()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &datastore{
|
||||
db: db,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *datastore) CreateEnv(name string) error {
|
||||
table_name := envNameToTableName(name)
|
||||
return d.db.createTableIfNotExists(table_name)
|
||||
}
|
||||
|
||||
func (d *datastore) DoesFileExists() bool {
|
||||
pwd := config.GetConfig().PWD
|
||||
filePath := fmt.Sprintf("%s/%s", pwd, variables.DBFileName)
|
||||
if _, err := os.Stat(filePath); errors.Is(err, os.ErrNotExist) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (d *datastore) ListEnvironments() ([]string, error) {
|
||||
return d.db.listTables()
|
||||
}
|
||||
|
||||
func (d *datastore) CreateEnvOffExsisting(new_env string, base_env string) error {
|
||||
table_name_new := envNameToTableName(new_env)
|
||||
table_name_base := envNameToTableName(base_env)
|
||||
err := d.CreateEnv(new_env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.db.copyContentFromTo(table_name_base, table_name_new)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *datastore) Close() error {
|
||||
return d.db.close()
|
||||
}
|
||||
|
||||
func (d *datastore) SetValue(key string, value string, encypted *bool, envs []*types.Environment) error {
|
||||
if encypted == nil {
|
||||
encypted = common.BoolP(false)
|
||||
}
|
||||
if *encypted {
|
||||
v, err := enc(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
value = *v
|
||||
}
|
||||
for _, env := range envs {
|
||||
table_name := envNameToTableName(env.Name)
|
||||
if err := d.db.setVar(table_name, key, value, *encypted); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *datastore) GetAll(envName string) ([]*types.EnvVar, error) {
|
||||
table_name := envNameToTableName(envName)
|
||||
vars, err := d.db.getAll(table_name)
|
||||
if err != nil {
|
||||
return vars, err
|
||||
}
|
||||
g := new(errgroup.Group)
|
||||
for _, v := range vars {
|
||||
g.Go(func() error {
|
||||
if v.Encrypted {
|
||||
if v.Value, err = dec(v.Value); err != nil {
|
||||
return &intErrors.InvalidPasswordError{}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
if err := g.Wait(); err != nil {
|
||||
return vars, err
|
||||
}
|
||||
sort.SliceStable(vars, func(i, j int) bool {
|
||||
return vars[i].Key < vars[j].Key
|
||||
})
|
||||
return vars, nil
|
||||
}
|
||||
|
||||
func (d *datastore) RemoveVar(key string, envs []*types.Environment) {
|
||||
for _, env := range envs {
|
||||
table_name := envNameToTableName(env.Name)
|
||||
d.db.rmVar(table_name, key)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *datastore) GetVar(envName string, key string) (*types.EnvVar, error) {
|
||||
table_name := envNameToTableName(envName)
|
||||
v, err := d.db.getVar(table_name, key)
|
||||
if err != nil {
|
||||
return v, err
|
||||
}
|
||||
if v.Encrypted {
|
||||
if v.Value, err = dec(v.Value); err != nil {
|
||||
return v, &intErrors.InvalidPasswordError{}
|
||||
}
|
||||
}
|
||||
return v, err
|
||||
}
|
||||
|
||||
func (d *datastore) RemoveEnv(envName string) error {
|
||||
table_name := envNameToTableName(envName)
|
||||
return d.db.deleteTable(table_name)
|
||||
}
|
||||
|
||||
func enc(s string) (*string, error) {
|
||||
conf := config.GetConfig()
|
||||
proj, _ := conf.GetProject(conf.PWD)
|
||||
key, salt, err := deriveKey([]byte(proj.Password), nil)
|
||||
data := []byte(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blockCipher, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(blockCipher)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err = rand.Read(nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ciphertext := gcm.Seal(nonce, nonce, data, nil)
|
||||
|
||||
ciphertext = append(ciphertext, salt...)
|
||||
str := string(ciphertext)
|
||||
|
||||
return &str, nil
|
||||
}
|
||||
|
||||
func dec(s string) (string, error) {
|
||||
data := []byte(s)
|
||||
salt, data := data[len(data)-32:], data[:len(data)-32]
|
||||
|
||||
conf := config.GetConfig()
|
||||
proj, _ := conf.GetProject(conf.PWD)
|
||||
key, _, err := deriveKey([]byte(proj.Password), salt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
blockCipher, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(blockCipher)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
nonce, ciphertext := data[:gcm.NonceSize()], data[gcm.NonceSize():]
|
||||
|
||||
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
str := string(plaintext)
|
||||
|
||||
return str, nil
|
||||
}
|
||||
|
||||
func deriveKey(password, salt []byte) ([]byte, []byte, error) {
|
||||
if salt == nil {
|
||||
salt = make([]byte, 32)
|
||||
if _, err := rand.Read(salt); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
key, err := scrypt.Key(password, salt, 1048576, 8, 1, 32)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return key, salt, nil
|
||||
}
|
135
internal/datastore/db.go
Normal file
135
internal/datastore/db.go
Normal file
|
@ -0,0 +1,135 @@
|
|||
package datastore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"database/sql"
|
||||
|
||||
"git.dayanhub.com/sagi/envoid/internal/common"
|
||||
"git.dayanhub.com/sagi/envoid/internal/errors"
|
||||
"git.dayanhub.com/sagi/envoid/internal/types"
|
||||
"git.dayanhub.com/sagi/envoid/internal/variables"
|
||||
_ "github.com/glebarez/go-sqlite"
|
||||
)
|
||||
|
||||
type db struct {
|
||||
con *sql.DB
|
||||
}
|
||||
|
||||
func newDB() (*db, error) {
|
||||
con, err := sql.Open("sqlite", variables.DBFileName)
|
||||
if err != nil {
|
||||
fmt.Printf("%v\n", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &db{
|
||||
con: con,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *db) close() error {
|
||||
return d.con.Close()
|
||||
}
|
||||
|
||||
func (d *db) createTableIfNotExists(table_name string) error {
|
||||
_, err := d.con.Exec(fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (key TEXT PRIMARY KEY NOT NULL, value BLOB NOT NULL, encrypted BOOL NOT NULL);", table_name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *db) listTables() ([]string, error) {
|
||||
tables := []string{}
|
||||
q := "SELECT name FROM sqlite_schema WHERE type ='table' AND name NOT LIKE 'sqlite_%' AND name LIKE ?"
|
||||
rows, err := d.con.Query(q, fmt.Sprintf("%s_%%", variables.DBTablePrefix))
|
||||
if err != nil {
|
||||
return tables, err
|
||||
}
|
||||
for rows.Next() {
|
||||
var table string
|
||||
if err := rows.Scan(&table); err != nil {
|
||||
return tables, err
|
||||
}
|
||||
tables = append(tables, tableNameToEnvName(table))
|
||||
}
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
func (d *db) copyContentFromTo(table_name_target string, table_name_dest string) error {
|
||||
_, err := d.con.Exec(fmt.Sprintf("INSERT INTO %s (key, value, encrypted) SELECT * FROM %s", table_name_dest, table_name_target))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *db) deleteTable(table_name string) error {
|
||||
_, err := d.con.Exec(fmt.Sprintf("DROP TABLE IF EXISTS %s", table_name))
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *db) getVar(table_name string, key string) (*types.EnvVar, error) {
|
||||
q := fmt.Sprintf("SELECT * FROM %s WHERE key=?", table_name)
|
||||
row := d.con.QueryRow(q, key)
|
||||
envVar := &types.EnvVar{}
|
||||
err := row.Scan(&envVar.Key, &envVar.Value, &envVar.Encrypted)
|
||||
if err != nil {
|
||||
return envVar, errors.NewNoKeyFoundError(key)
|
||||
}
|
||||
|
||||
return envVar, nil
|
||||
|
||||
}
|
||||
|
||||
func (d *db) getAll(table_name string) ([]*types.EnvVar, error) {
|
||||
enrties := []*types.EnvVar{}
|
||||
rows, err := d.con.Query(fmt.Sprintf("SELECT * FROM %s", table_name))
|
||||
if err != nil {
|
||||
return enrties, err
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
key := ""
|
||||
value := ""
|
||||
enc := common.BoolP(false)
|
||||
err := rows.Scan(&key, &value, enc)
|
||||
if err != nil {
|
||||
return enrties, err
|
||||
}
|
||||
e := &types.EnvVar{
|
||||
Key: key,
|
||||
Value: value,
|
||||
Encrypted: *enc,
|
||||
}
|
||||
enrties = append(enrties, e)
|
||||
}
|
||||
return enrties, nil
|
||||
|
||||
}
|
||||
|
||||
func (d *db) setVar(table_name string, key string, value string, encrypted bool) error {
|
||||
q := fmt.Sprintf("INSERT INTO %s (key, value, encrypted) values ( ? , ? , ? ) ON CONFLICT (key) DO UPDATE SET value = ?, encrypted = ?", table_name)
|
||||
_, err := d.con.Exec(q, key, value, encrypted, value, encrypted)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return errors.NewSecretExsistsError(key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *db) rmVar(table_name string, key string) {
|
||||
q := fmt.Sprintf("DELETE FROM %s WHERE key = ?", table_name)
|
||||
_, _ = d.con.Exec(q, key)
|
||||
}
|
||||
|
||||
func envNameToTableName(envName string) string {
|
||||
envSanke := common.StrToSnakeCase(envName)
|
||||
return fmt.Sprintf("%s_%s", variables.DBTablePrefix, envSanke)
|
||||
}
|
||||
|
||||
func tableNameToEnvName(tableName string) string {
|
||||
// remove '<prefix>_'
|
||||
return tableName[len(variables.DBTablePrefix)+1:]
|
||||
}
|
15
internal/errors/env_already_exists.go
Normal file
15
internal/errors/env_already_exists.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package errors
|
||||
|
||||
import "fmt"
|
||||
|
||||
type EnvironmentExistsError struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (e *EnvironmentExistsError) Error() string {
|
||||
return fmt.Sprintf("environment %s already exists", e.name)
|
||||
}
|
||||
|
||||
func NewEnvironmentExistsError(name string) *EnvironmentExistsError {
|
||||
return &EnvironmentExistsError{name: name}
|
||||
}
|
16
internal/errors/env_not_found.go
Normal file
16
internal/errors/env_not_found.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
package errors
|
||||
|
||||
import "fmt"
|
||||
|
||||
type EnvironmentNotFoundError struct {
|
||||
path string
|
||||
name string
|
||||
}
|
||||
|
||||
func (e *EnvironmentNotFoundError) Error() string {
|
||||
return fmt.Sprintf("No env '%s' definition for path %s. Did you initialize?", e.name, e.path)
|
||||
}
|
||||
|
||||
func NewEnvironmentNotFoundError(path string, name string) *EnvironmentNotFoundError {
|
||||
return &EnvironmentNotFoundError{path: path, name: name}
|
||||
}
|
15
internal/errors/invalid_command.go
Normal file
15
internal/errors/invalid_command.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package errors
|
||||
|
||||
import "fmt"
|
||||
|
||||
type InvalidCommandError struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e *InvalidCommandError) Error() string {
|
||||
return fmt.Sprintf("invalid command. %v", e.msg)
|
||||
}
|
||||
|
||||
func NewInvalidCommandError(msg string) *InvalidCommandError {
|
||||
return &InvalidCommandError{msg: msg}
|
||||
}
|
16
internal/errors/invalid_flag_value.go
Normal file
16
internal/errors/invalid_flag_value.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
package errors
|
||||
|
||||
import "fmt"
|
||||
|
||||
type InvalidFlagValueError struct {
|
||||
flag string
|
||||
invalidValue string
|
||||
}
|
||||
|
||||
func (e *InvalidFlagValueError) Error() string {
|
||||
return fmt.Sprintf("invalid value '%s' for flag --%s", e.invalidValue, e.flag)
|
||||
}
|
||||
|
||||
func NewInvalidFlagValueError(flag string, invalidValue string) *InvalidFlagValueError {
|
||||
return &InvalidFlagValueError{flag: flag, invalidValue: invalidValue}
|
||||
}
|
12
internal/errors/invalid_password.go
Normal file
12
internal/errors/invalid_password.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package errors
|
||||
|
||||
type InvalidPasswordError struct {
|
||||
}
|
||||
|
||||
func (e *InvalidPasswordError) Error() string {
|
||||
return "invalid password. is your environment set correctly?"
|
||||
}
|
||||
|
||||
func NewInvalidPasswordError() *InvalidPasswordError {
|
||||
return &InvalidPasswordError{}
|
||||
}
|
15
internal/errors/key_not_found.go
Normal file
15
internal/errors/key_not_found.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package errors
|
||||
|
||||
import "fmt"
|
||||
|
||||
type NoKeyFoundError struct {
|
||||
key string
|
||||
}
|
||||
|
||||
func (e *NoKeyFoundError) Error() string {
|
||||
return fmt.Sprintf("key %s not found", e.key)
|
||||
}
|
||||
|
||||
func NewNoKeyFoundError(key string) *NoKeyFoundError {
|
||||
return &NoKeyFoundError{key: key}
|
||||
}
|
15
internal/errors/no_config_found.go
Normal file
15
internal/errors/no_config_found.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package errors
|
||||
|
||||
import "fmt"
|
||||
|
||||
type NoConfigFoundError struct {
|
||||
path string
|
||||
}
|
||||
|
||||
func (e *NoConfigFoundError) Error() string {
|
||||
return fmt.Sprintf("no config file found in %s", e.path)
|
||||
}
|
||||
|
||||
func NewNoConfigFoundError(path string) *NoConfigFoundError {
|
||||
return &NoConfigFoundError{path: path}
|
||||
}
|
15
internal/errors/project_is_empty.go
Normal file
15
internal/errors/project_is_empty.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package errors
|
||||
|
||||
import "fmt"
|
||||
|
||||
type ProjectEmptyError struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (e *ProjectEmptyError) Error() string {
|
||||
return fmt.Sprintf("Project %s does not have any environments.", e.name)
|
||||
}
|
||||
|
||||
func NewProjectEmptyError(name string) *ProjectEmptyError {
|
||||
return &ProjectEmptyError{name: name}
|
||||
}
|
15
internal/errors/project_not_found.go
Normal file
15
internal/errors/project_not_found.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package errors
|
||||
|
||||
import "fmt"
|
||||
|
||||
type ProjectNotFoundError struct {
|
||||
path string
|
||||
}
|
||||
|
||||
func (e *ProjectNotFoundError) Error() string {
|
||||
return fmt.Sprintf("No project found for path '%s'. Did you initialize?", e.path)
|
||||
}
|
||||
|
||||
func NewProjectNotFoundError(path string) *ProjectNotFoundError {
|
||||
return &ProjectNotFoundError{path: path}
|
||||
}
|
15
internal/errors/secret_exists.go
Normal file
15
internal/errors/secret_exists.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package errors
|
||||
|
||||
import "fmt"
|
||||
|
||||
type SecretExistsError struct {
|
||||
secret string
|
||||
}
|
||||
|
||||
func (e *SecretExistsError) Error() string {
|
||||
return fmt.Sprintf("secret %s already exists", e.secret)
|
||||
}
|
||||
|
||||
func NewSecretExsistsError(secret string) *SecretExistsError {
|
||||
return &SecretExistsError{secret: secret}
|
||||
}
|
23
internal/prompt/password.go
Normal file
23
internal/prompt/password.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
package prompt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
func PasswordPrompt(label string) string {
|
||||
var s string
|
||||
for {
|
||||
fmt.Fprint(os.Stderr, label+" ")
|
||||
b, _ := term.ReadPassword(int(syscall.Stdin))
|
||||
s = string(b)
|
||||
if s != "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
fmt.Println()
|
||||
return s
|
||||
}
|
21
internal/prompt/string.go
Normal file
21
internal/prompt/string.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package prompt
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func StringPrompt(label string) string {
|
||||
var s string
|
||||
r := bufio.NewReader(os.Stdin)
|
||||
for {
|
||||
fmt.Fprint(os.Stderr, label+" ")
|
||||
s, _ = r.ReadString('\n')
|
||||
if s != "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
return strings.TrimSpace(s)
|
||||
}
|
7
internal/types/env_var.go
Normal file
7
internal/types/env_var.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
package types
|
||||
|
||||
type EnvVar struct {
|
||||
Key string
|
||||
Value string
|
||||
Encrypted bool
|
||||
}
|
6
internal/types/environment.go
Normal file
6
internal/types/environment.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
package types
|
||||
|
||||
type Environment struct {
|
||||
Name string `json:"name"`
|
||||
Password string `json:"-"`
|
||||
}
|
58
internal/types/project.go
Normal file
58
internal/types/project.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"git.dayanhub.com/sagi/envoid/internal/errors"
|
||||
)
|
||||
|
||||
type Project struct {
|
||||
Path string `json:"path"`
|
||||
Name string `json:"name"`
|
||||
Password string `json:"password"`
|
||||
Environments []*Environment `json:"envorinments" default:"[]"`
|
||||
}
|
||||
|
||||
func (p *Project) GetEnv(name string) (*Environment, error) {
|
||||
var e *Environment
|
||||
for _, env := range p.Environments {
|
||||
if env.Name == name {
|
||||
passByte, err := base64.RawStdEncoding.DecodeString(p.Password)
|
||||
if err != nil {
|
||||
fmt.Printf("[ERROR] Failed to decode project password @ %s. %e\n", p.Path, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
e = &Environment{
|
||||
Name: env.Name,
|
||||
Password: string(passByte),
|
||||
}
|
||||
}
|
||||
}
|
||||
if e == nil {
|
||||
return nil, errors.NewEnvironmentNotFoundError(p.Path, name)
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
func (p *Project) NewEnv(name string) error {
|
||||
e := &Environment{
|
||||
Name: name,
|
||||
}
|
||||
p.Environments = append(p.Environments, e)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Project) RemoveEnv(name string) {
|
||||
environments := []*Environment{}
|
||||
for _, env := range p.Environments {
|
||||
if env.Name != name {
|
||||
environments = append(environments, env)
|
||||
}
|
||||
}
|
||||
p.Environments = environments
|
||||
}
|
||||
|
||||
func (p *Project) IsEmpty() bool {
|
||||
return len(p.Environments) == 0
|
||||
}
|
7
internal/variables/constants.go
Normal file
7
internal/variables/constants.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
package variables
|
||||
|
||||
const (
|
||||
DBFileName = ".envoid"
|
||||
DBTablePrefix = "envoid"
|
||||
ConfigFolder = "envoid"
|
||||
)
|
6
internal/variables/variables.go
Normal file
6
internal/variables/variables.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
package variables
|
||||
|
||||
var (
|
||||
Commit string = "HEAD"
|
||||
Version string = "development"
|
||||
)
|
9
main.go
Normal file
9
main.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"git.dayanhub.com/sagi/envoid/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd.Execute()
|
||||
}
|
21
vendor/github.com/cpuguy83/go-md2man/v2/LICENSE.md
generated
vendored
Normal file
21
vendor/github.com/cpuguy83/go-md2man/v2/LICENSE.md
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Brian Goff
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
16
vendor/github.com/cpuguy83/go-md2man/v2/md2man/md2man.go
generated
vendored
Normal file
16
vendor/github.com/cpuguy83/go-md2man/v2/md2man/md2man.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
package md2man
|
||||
|
||||
import (
|
||||
"github.com/russross/blackfriday/v2"
|
||||
)
|
||||
|
||||
// Render converts a markdown document into a roff formatted document.
|
||||
func Render(doc []byte) []byte {
|
||||
renderer := NewRoffRenderer()
|
||||
|
||||
return blackfriday.Run(doc,
|
||||
[]blackfriday.Option{
|
||||
blackfriday.WithRenderer(renderer),
|
||||
blackfriday.WithExtensions(renderer.GetExtensions()),
|
||||
}...)
|
||||
}
|
382
vendor/github.com/cpuguy83/go-md2man/v2/md2man/roff.go
generated
vendored
Normal file
382
vendor/github.com/cpuguy83/go-md2man/v2/md2man/roff.go
generated
vendored
Normal file
|
@ -0,0 +1,382 @@
|
|||
package md2man
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/russross/blackfriday/v2"
|
||||
)
|
||||
|
||||
// roffRenderer implements the blackfriday.Renderer interface for creating
|
||||
// roff format (manpages) from markdown text
|
||||
type roffRenderer struct {
|
||||
extensions blackfriday.Extensions
|
||||
listCounters []int
|
||||
firstHeader bool
|
||||
firstDD bool
|
||||
listDepth int
|
||||
}
|
||||
|
||||
const (
|
||||
titleHeader = ".TH "
|
||||
topLevelHeader = "\n\n.SH "
|
||||
secondLevelHdr = "\n.SH "
|
||||
otherHeader = "\n.SS "
|
||||
crTag = "\n"
|
||||
emphTag = "\\fI"
|
||||
emphCloseTag = "\\fP"
|
||||
strongTag = "\\fB"
|
||||
strongCloseTag = "\\fP"
|
||||
breakTag = "\n.br\n"
|
||||
paraTag = "\n.PP\n"
|
||||
hruleTag = "\n.ti 0\n\\l'\\n(.lu'\n"
|
||||
linkTag = "\n\\[la]"
|
||||
linkCloseTag = "\\[ra]"
|
||||
codespanTag = "\\fB"
|
||||
codespanCloseTag = "\\fR"
|
||||
codeTag = "\n.EX\n"
|
||||
codeCloseTag = ".EE\n" // Do not prepend a newline character since code blocks, by definition, include a newline already (or at least as how blackfriday gives us on).
|
||||
quoteTag = "\n.PP\n.RS\n"
|
||||
quoteCloseTag = "\n.RE\n"
|
||||
listTag = "\n.RS\n"
|
||||
listCloseTag = "\n.RE\n"
|
||||
dtTag = "\n.TP\n"
|
||||
dd2Tag = "\n"
|
||||
tableStart = "\n.TS\nallbox;\n"
|
||||
tableEnd = ".TE\n"
|
||||
tableCellStart = "T{\n"
|
||||
tableCellEnd = "\nT}\n"
|
||||
tablePreprocessor = `'\" t`
|
||||
)
|
||||
|
||||
// NewRoffRenderer creates a new blackfriday Renderer for generating roff documents
|
||||
// from markdown
|
||||
func NewRoffRenderer() *roffRenderer { // nolint: golint
|
||||
var extensions blackfriday.Extensions
|
||||
|
||||
extensions |= blackfriday.NoIntraEmphasis
|
||||
extensions |= blackfriday.Tables
|
||||
extensions |= blackfriday.FencedCode
|
||||
extensions |= blackfriday.SpaceHeadings
|
||||
extensions |= blackfriday.Footnotes
|
||||
extensions |= blackfriday.Titleblock
|
||||
extensions |= blackfriday.DefinitionLists
|
||||
return &roffRenderer{
|
||||
extensions: extensions,
|
||||
}
|
||||
}
|
||||
|
||||
// GetExtensions returns the list of extensions used by this renderer implementation
|
||||
func (r *roffRenderer) GetExtensions() blackfriday.Extensions {
|
||||
return r.extensions
|
||||
}
|
||||
|
||||
// RenderHeader handles outputting the header at document start
|
||||
func (r *roffRenderer) RenderHeader(w io.Writer, ast *blackfriday.Node) {
|
||||
// We need to walk the tree to check if there are any tables.
|
||||
// If there are, we need to enable the roff table preprocessor.
|
||||
ast.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
|
||||
if node.Type == blackfriday.Table {
|
||||
out(w, tablePreprocessor+"\n")
|
||||
return blackfriday.Terminate
|
||||
}
|
||||
return blackfriday.GoToNext
|
||||
})
|
||||
|
||||
// disable hyphenation
|
||||
out(w, ".nh\n")
|
||||
}
|
||||
|
||||
// RenderFooter handles outputting the footer at the document end; the roff
|
||||
// renderer has no footer information
|
||||
func (r *roffRenderer) RenderFooter(w io.Writer, ast *blackfriday.Node) {
|
||||
}
|
||||
|
||||
// RenderNode is called for each node in a markdown document; based on the node
|
||||
// type the equivalent roff output is sent to the writer
|
||||
func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
|
||||
walkAction := blackfriday.GoToNext
|
||||
|
||||
switch node.Type {
|
||||
case blackfriday.Text:
|
||||
escapeSpecialChars(w, node.Literal)
|
||||
case blackfriday.Softbreak:
|
||||
out(w, crTag)
|
||||
case blackfriday.Hardbreak:
|
||||
out(w, breakTag)
|
||||
case blackfriday.Emph:
|
||||
if entering {
|
||||
out(w, emphTag)
|
||||
} else {
|
||||
out(w, emphCloseTag)
|
||||
}
|
||||
case blackfriday.Strong:
|
||||
if entering {
|
||||
out(w, strongTag)
|
||||
} else {
|
||||
out(w, strongCloseTag)
|
||||
}
|
||||
case blackfriday.Link:
|
||||
// Don't render the link text for automatic links, because this
|
||||
// will only duplicate the URL in the roff output.
|
||||
// See https://daringfireball.net/projects/markdown/syntax#autolink
|
||||
if !bytes.Equal(node.LinkData.Destination, node.FirstChild.Literal) {
|
||||
out(w, string(node.FirstChild.Literal))
|
||||
}
|
||||
// Hyphens in a link must be escaped to avoid word-wrap in the rendered man page.
|
||||
escapedLink := strings.ReplaceAll(string(node.LinkData.Destination), "-", "\\-")
|
||||
out(w, linkTag+escapedLink+linkCloseTag)
|
||||
walkAction = blackfriday.SkipChildren
|
||||
case blackfriday.Image:
|
||||
// ignore images
|
||||
walkAction = blackfriday.SkipChildren
|
||||
case blackfriday.Code:
|
||||
out(w, codespanTag)
|
||||
escapeSpecialChars(w, node.Literal)
|
||||
out(w, codespanCloseTag)
|
||||
case blackfriday.Document:
|
||||
break
|
||||
case blackfriday.Paragraph:
|
||||
// roff .PP markers break lists
|
||||
if r.listDepth > 0 {
|
||||
return blackfriday.GoToNext
|
||||
}
|
||||
if entering {
|
||||
out(w, paraTag)
|
||||
} else {
|
||||
out(w, crTag)
|
||||
}
|
||||
case blackfriday.BlockQuote:
|
||||
if entering {
|
||||
out(w, quoteTag)
|
||||
} else {
|
||||
out(w, quoteCloseTag)
|
||||
}
|
||||
case blackfriday.Heading:
|
||||
r.handleHeading(w, node, entering)
|
||||
case blackfriday.HorizontalRule:
|
||||
out(w, hruleTag)
|
||||
case blackfriday.List:
|
||||
r.handleList(w, node, entering)
|
||||
case blackfriday.Item:
|
||||
r.handleItem(w, node, entering)
|
||||
case blackfriday.CodeBlock:
|
||||
out(w, codeTag)
|
||||
escapeSpecialChars(w, node.Literal)
|
||||
out(w, codeCloseTag)
|
||||
case blackfriday.Table:
|
||||
r.handleTable(w, node, entering)
|
||||
case blackfriday.TableHead:
|
||||
case blackfriday.TableBody:
|
||||
case blackfriday.TableRow:
|
||||
// no action as cell entries do all the nroff formatting
|
||||
return blackfriday.GoToNext
|
||||
case blackfriday.TableCell:
|
||||
r.handleTableCell(w, node, entering)
|
||||
case blackfriday.HTMLSpan:
|
||||
// ignore other HTML tags
|
||||
case blackfriday.HTMLBlock:
|
||||
if bytes.HasPrefix(node.Literal, []byte("<!--")) {
|
||||
break // ignore comments, no warning
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "WARNING: go-md2man does not handle node type "+node.Type.String())
|
||||
default:
|
||||
fmt.Fprintln(os.Stderr, "WARNING: go-md2man does not handle node type "+node.Type.String())
|
||||
}
|
||||
return walkAction
|
||||
}
|
||||
|
||||
func (r *roffRenderer) handleHeading(w io.Writer, node *blackfriday.Node, entering bool) {
|
||||
if entering {
|
||||
switch node.Level {
|
||||
case 1:
|
||||
if !r.firstHeader {
|
||||
out(w, titleHeader)
|
||||
r.firstHeader = true
|
||||
break
|
||||
}
|
||||
out(w, topLevelHeader)
|
||||
case 2:
|
||||
out(w, secondLevelHdr)
|
||||
default:
|
||||
out(w, otherHeader)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *roffRenderer) handleList(w io.Writer, node *blackfriday.Node, entering bool) {
|
||||
openTag := listTag
|
||||
closeTag := listCloseTag
|
||||
if node.ListFlags&blackfriday.ListTypeDefinition != 0 {
|
||||
// tags for definition lists handled within Item node
|
||||
openTag = ""
|
||||
closeTag = ""
|
||||
}
|
||||
if entering {
|
||||
r.listDepth++
|
||||
if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
|
||||
r.listCounters = append(r.listCounters, 1)
|
||||
}
|
||||
out(w, openTag)
|
||||
} else {
|
||||
if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
|
||||
r.listCounters = r.listCounters[:len(r.listCounters)-1]
|
||||
}
|
||||
out(w, closeTag)
|
||||
r.listDepth--
|
||||
}
|
||||
}
|
||||
|
||||
func (r *roffRenderer) handleItem(w io.Writer, node *blackfriday.Node, entering bool) {
|
||||
if entering {
|
||||
if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
|
||||
out(w, fmt.Sprintf(".IP \"%3d.\" 5\n", r.listCounters[len(r.listCounters)-1]))
|
||||
r.listCounters[len(r.listCounters)-1]++
|
||||
} else if node.ListFlags&blackfriday.ListTypeTerm != 0 {
|
||||
// DT (definition term): line just before DD (see below).
|
||||
out(w, dtTag)
|
||||
r.firstDD = true
|
||||
} else if node.ListFlags&blackfriday.ListTypeDefinition != 0 {
|
||||
// DD (definition description): line that starts with ": ".
|
||||
//
|
||||
// We have to distinguish between the first DD and the
|
||||
// subsequent ones, as there should be no vertical
|
||||
// whitespace between the DT and the first DD.
|
||||
if r.firstDD {
|
||||
r.firstDD = false
|
||||
} else {
|
||||
out(w, dd2Tag)
|
||||
}
|
||||
} else {
|
||||
out(w, ".IP \\(bu 2\n")
|
||||
}
|
||||
} else {
|
||||
out(w, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
func (r *roffRenderer) handleTable(w io.Writer, node *blackfriday.Node, entering bool) {
|
||||
if entering {
|
||||
out(w, tableStart)
|
||||
// call walker to count cells (and rows?) so format section can be produced
|
||||
columns := countColumns(node)
|
||||
out(w, strings.Repeat("l ", columns)+"\n")
|
||||
out(w, strings.Repeat("l ", columns)+".\n")
|
||||
} else {
|
||||
out(w, tableEnd)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *roffRenderer) handleTableCell(w io.Writer, node *blackfriday.Node, entering bool) {
|
||||
if entering {
|
||||
var start string
|
||||
if node.Prev != nil && node.Prev.Type == blackfriday.TableCell {
|
||||
start = "\t"
|
||||
}
|
||||
if node.IsHeader {
|
||||
start += strongTag
|
||||
} else if nodeLiteralSize(node) > 30 {
|
||||
start += tableCellStart
|
||||
}
|
||||
out(w, start)
|
||||
} else {
|
||||
var end string
|
||||
if node.IsHeader {
|
||||
end = strongCloseTag
|
||||
} else if nodeLiteralSize(node) > 30 {
|
||||
end = tableCellEnd
|
||||
}
|
||||
if node.Next == nil && end != tableCellEnd {
|
||||
// Last cell: need to carriage return if we are at the end of the
|
||||
// header row and content isn't wrapped in a "tablecell"
|
||||
end += crTag
|
||||
}
|
||||
out(w, end)
|
||||
}
|
||||
}
|
||||
|
||||
func nodeLiteralSize(node *blackfriday.Node) int {
|
||||
total := 0
|
||||
for n := node.FirstChild; n != nil; n = n.FirstChild {
|
||||
total += len(n.Literal)
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// because roff format requires knowing the column count before outputting any table
|
||||
// data we need to walk a table tree and count the columns
|
||||
func countColumns(node *blackfriday.Node) int {
|
||||
var columns int
|
||||
|
||||
node.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
|
||||
switch node.Type {
|
||||
case blackfriday.TableRow:
|
||||
if !entering {
|
||||
return blackfriday.Terminate
|
||||
}
|
||||
case blackfriday.TableCell:
|
||||
if entering {
|
||||
columns++
|
||||
}
|
||||
default:
|
||||
}
|
||||
return blackfriday.GoToNext
|
||||
})
|
||||
return columns
|
||||
}
|
||||
|
||||
func out(w io.Writer, output string) {
|
||||
io.WriteString(w, output) // nolint: errcheck
|
||||
}
|
||||
|
||||
func escapeSpecialChars(w io.Writer, text []byte) {
|
||||
scanner := bufio.NewScanner(bytes.NewReader(text))
|
||||
|
||||
// count the number of lines in the text
|
||||
// we need to know this to avoid adding a newline after the last line
|
||||
n := bytes.Count(text, []byte{'\n'})
|
||||
idx := 0
|
||||
|
||||
for scanner.Scan() {
|
||||
dt := scanner.Bytes()
|
||||
if idx < n {
|
||||
idx++
|
||||
dt = append(dt, '\n')
|
||||
}
|
||||
escapeSpecialCharsLine(w, dt)
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func escapeSpecialCharsLine(w io.Writer, text []byte) {
|
||||
for i := 0; i < len(text); i++ {
|
||||
// escape initial apostrophe or period
|
||||
if len(text) >= 1 && (text[0] == '\'' || text[0] == '.') {
|
||||
out(w, "\\&")
|
||||
}
|
||||
|
||||
// directly copy normal characters
|
||||
org := i
|
||||
|
||||
for i < len(text) && text[i] != '\\' {
|
||||
i++
|
||||
}
|
||||
if i > org {
|
||||
w.Write(text[org:i]) // nolint: errcheck
|
||||
}
|
||||
|
||||
// escape a character
|
||||
if i >= len(text) {
|
||||
break
|
||||
}
|
||||
|
||||
w.Write([]byte{'\\', text[i]}) // nolint: errcheck
|
||||
}
|
||||
}
|
1
vendor/github.com/creasty/defaults/.gitignore
generated
vendored
Normal file
1
vendor/github.com/creasty/defaults/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
.DS_Store
|
22
vendor/github.com/creasty/defaults/LICENSE
generated
vendored
Normal file
22
vendor/github.com/creasty/defaults/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
Copyright (c) 2017-present Yuki Iwanaga
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
29
vendor/github.com/creasty/defaults/Makefile
generated
vendored
Normal file
29
vendor/github.com/creasty/defaults/Makefile
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
SHELL := /bin/bash -eu -o pipefail
|
||||
|
||||
GO_TEST_FLAGS := -v
|
||||
|
||||
PACKAGE_DIRS := $(shell go list ./... 2> /dev/null | grep -v /vendor/)
|
||||
SRC_FILES := $(shell find . -name '*.go' -not -path './vendor/*')
|
||||
|
||||
|
||||
# Tasks
|
||||
#-----------------------------------------------
|
||||
.PHONY: lint
|
||||
lint:
|
||||
@gofmt -e -d -s $(SRC_FILES) | awk '{ e = 1; print $0 } END { if (e) exit(1) }'
|
||||
@golangci-lint --disable errcheck,unused run
|
||||
|
||||
.PHONY: test
|
||||
test: lint
|
||||
@go test $(GO_TEST_FLAGS) $(PACKAGE_DIRS)
|
||||
|
||||
.PHONY: ci-test
|
||||
ci-test: lint
|
||||
@echo > coverage.txt
|
||||
@for d in $(PACKAGE_DIRS); do \
|
||||
go test -coverprofile=profile.out -covermode=atomic -race -v $$d; \
|
||||
if [ -f profile.out ]; then \
|
||||
cat profile.out >> coverage.txt; \
|
||||
rm profile.out; \
|
||||
fi; \
|
||||
done
|
160
vendor/github.com/creasty/defaults/README.md
generated
vendored
Normal file
160
vendor/github.com/creasty/defaults/README.md
generated
vendored
Normal file
|
@ -0,0 +1,160 @@
|
|||
defaults
|
||||
========
|
||||
|
||||
[![CircleCI](https://circleci.com/gh/creasty/defaults/tree/master.svg?style=svg)](https://circleci.com/gh/creasty/defaults/tree/master)
|
||||
[![codecov](https://codecov.io/gh/creasty/defaults/branch/master/graph/badge.svg)](https://codecov.io/gh/creasty/defaults)
|
||||
[![GitHub release](https://img.shields.io/github/release/creasty/defaults.svg)](https://github.com/creasty/defaults/releases)
|
||||
[![License](https://img.shields.io/github/license/creasty/defaults.svg)](./LICENSE)
|
||||
|
||||
Initialize structs with default values
|
||||
|
||||
- Supports almost all kind of types
|
||||
- Scalar types
|
||||
- `int/8/16/32/64`, `uint/8/16/32/64`, `float32/64`
|
||||
- `uintptr`, `bool`, `string`
|
||||
- Complex types
|
||||
- `map`, `slice`, `struct`
|
||||
- Nested types
|
||||
- `map[K1]map[K2]Struct`, `[]map[K1]Struct[]`
|
||||
- Aliased types
|
||||
- `time.Duration`
|
||||
- e.g., `type Enum string`
|
||||
- Pointer types
|
||||
- e.g., `*SampleStruct`, `*int`
|
||||
- Recursively initializes fields in a struct
|
||||
- Dynamically sets default values by [`defaults.Setter`](./setter.go) interface
|
||||
- Preserves non-initial values from being reset with a default value
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/creasty/defaults"
|
||||
)
|
||||
|
||||
type Gender string
|
||||
|
||||
type Sample struct {
|
||||
Name string `default:"John Smith"`
|
||||
Age int `default:"27"`
|
||||
Gender Gender `default:"m"`
|
||||
Working bool `default:"true"`
|
||||
|
||||
SliceInt []int `default:"[1, 2, 3]"`
|
||||
SlicePtr []*int `default:"[1, 2, 3]"`
|
||||
SliceString []string `default:"[\"a\", \"b\"]"`
|
||||
|
||||
MapNull map[string]int `default:"{}"`
|
||||
Map map[string]int `default:"{\"key1\": 123}"`
|
||||
MapOfStruct map[string]OtherStruct `default:"{\"Key2\": {\"Foo\":123}}"`
|
||||
MapOfPtrStruct map[string]*OtherStruct `default:"{\"Key3\": {\"Foo\":123}}"`
|
||||
MapOfStructWithTag map[string]OtherStruct `default:"{\"Key4\": {\"Foo\":123}}"`
|
||||
|
||||
Struct OtherStruct `default:"{\"Foo\": 123}"`
|
||||
StructPtr *OtherStruct `default:"{\"Foo\": 123}"`
|
||||
|
||||
NoTag OtherStruct // Recurses into a nested struct by default
|
||||
NoOption OtherStruct `default:"-"` // no option
|
||||
}
|
||||
|
||||
type OtherStruct struct {
|
||||
Hello string `default:"world"` // Tags in a nested struct also work
|
||||
Foo int `default:"-"`
|
||||
Random int `default:"-"`
|
||||
}
|
||||
|
||||
// SetDefaults implements defaults.Setter interface
|
||||
func (s *OtherStruct) SetDefaults() {
|
||||
if defaults.CanUpdate(s.Random) { // Check if it's a zero value (recommended)
|
||||
s.Random = rand.Int() // Set a dynamic value
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
obj := &Sample{}
|
||||
if err := defaults.Set(obj); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
out, err := json.MarshalIndent(obj, "", " ")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(out))
|
||||
|
||||
// Output:
|
||||
// {
|
||||
// "Name": "John Smith",
|
||||
// "Age": 27,
|
||||
// "Gender": "m",
|
||||
// "Working": true,
|
||||
// "SliceInt": [
|
||||
// 1,
|
||||
// 2,
|
||||
// 3
|
||||
// ],
|
||||
// "SlicePtr": [
|
||||
// 1,
|
||||
// 2,
|
||||
// 3
|
||||
// ],
|
||||
// "SliceString": [
|
||||
// "a",
|
||||
// "b"
|
||||
// ],
|
||||
// "MapNull": {},
|
||||
// "Map": {
|
||||
// "key1": 123
|
||||
// },
|
||||
// "MapOfStruct": {
|
||||
// "Key2": {
|
||||
// "Hello": "world",
|
||||
// "Foo": 123,
|
||||
// "Random": 5577006791947779410
|
||||
// }
|
||||
// },
|
||||
// "MapOfPtrStruct": {
|
||||
// "Key3": {
|
||||
// "Hello": "world",
|
||||
// "Foo": 123,
|
||||
// "Random": 8674665223082153551
|
||||
// }
|
||||
// },
|
||||
// "MapOfStructWithTag": {
|
||||
// "Key4": {
|
||||
// "Hello": "world",
|
||||
// "Foo": 123,
|
||||
// "Random": 6129484611666145821
|
||||
// }
|
||||
// },
|
||||
// "Struct": {
|
||||
// "Hello": "world",
|
||||
// "Foo": 123,
|
||||
// "Random": 4037200794235010051
|
||||
// },
|
||||
// "StructPtr": {
|
||||
// "Hello": "world",
|
||||
// "Foo": 123,
|
||||
// "Random": 3916589616287113937
|
||||
// },
|
||||
// "NoTag": {
|
||||
// "Hello": "world",
|
||||
// "Foo": 0,
|
||||
// "Random": 6334824724549167320
|
||||
// },
|
||||
// "NoOption": {
|
||||
// "Hello": "",
|
||||
// "Foo": 0,
|
||||
// "Random": 0
|
||||
// }
|
||||
// }
|
||||
}
|
||||
```
|
244
vendor/github.com/creasty/defaults/defaults.go
generated
vendored
Normal file
244
vendor/github.com/creasty/defaults/defaults.go
generated
vendored
Normal file
|
@ -0,0 +1,244 @@
|
|||
package defaults
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidType = errors.New("not a struct pointer")
|
||||
)
|
||||
|
||||
const (
|
||||
fieldName = "default"
|
||||
)
|
||||
|
||||
// Set initializes members in a struct referenced by a pointer.
|
||||
// Maps and slices are initialized by `make` and other primitive types are set with default values.
|
||||
// `ptr` should be a struct pointer
|
||||
func Set(ptr interface{}) error {
|
||||
if reflect.TypeOf(ptr).Kind() != reflect.Ptr {
|
||||
return errInvalidType
|
||||
}
|
||||
|
||||
v := reflect.ValueOf(ptr).Elem()
|
||||
t := v.Type()
|
||||
|
||||
if t.Kind() != reflect.Struct {
|
||||
return errInvalidType
|
||||
}
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
if defaultVal := t.Field(i).Tag.Get(fieldName); defaultVal != "-" {
|
||||
if err := setField(v.Field(i), defaultVal); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
callSetter(ptr)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MustSet function is a wrapper of Set function
|
||||
// It will call Set and panic if err not equals nil.
|
||||
func MustSet(ptr interface{}) {
|
||||
if err := Set(ptr); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func setField(field reflect.Value, defaultVal string) error {
|
||||
if !field.CanSet() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !shouldInitializeField(field, defaultVal) {
|
||||
return nil
|
||||
}
|
||||
|
||||
isInitial := isInitialValue(field)
|
||||
if isInitial {
|
||||
if unmarshalByInterface(field, defaultVal) {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch field.Kind() {
|
||||
case reflect.Bool:
|
||||
if val, err := strconv.ParseBool(defaultVal); err == nil {
|
||||
field.Set(reflect.ValueOf(val).Convert(field.Type()))
|
||||
}
|
||||
case reflect.Int:
|
||||
if val, err := strconv.ParseInt(defaultVal, 0, strconv.IntSize); err == nil {
|
||||
field.Set(reflect.ValueOf(int(val)).Convert(field.Type()))
|
||||
}
|
||||
case reflect.Int8:
|
||||
if val, err := strconv.ParseInt(defaultVal, 0, 8); err == nil {
|
||||
field.Set(reflect.ValueOf(int8(val)).Convert(field.Type()))
|
||||
}
|
||||
case reflect.Int16:
|
||||
if val, err := strconv.ParseInt(defaultVal, 0, 16); err == nil {
|
||||
field.Set(reflect.ValueOf(int16(val)).Convert(field.Type()))
|
||||
}
|
||||
case reflect.Int32:
|
||||
if val, err := strconv.ParseInt(defaultVal, 0, 32); err == nil {
|
||||
field.Set(reflect.ValueOf(int32(val)).Convert(field.Type()))
|
||||
}
|
||||
case reflect.Int64:
|
||||
if val, err := time.ParseDuration(defaultVal); err == nil {
|
||||
field.Set(reflect.ValueOf(val).Convert(field.Type()))
|
||||
} else if val, err := strconv.ParseInt(defaultVal, 0, 64); err == nil {
|
||||
field.Set(reflect.ValueOf(val).Convert(field.Type()))
|
||||
}
|
||||
case reflect.Uint:
|
||||
if val, err := strconv.ParseUint(defaultVal, 0, strconv.IntSize); err == nil {
|
||||
field.Set(reflect.ValueOf(uint(val)).Convert(field.Type()))
|
||||
}
|
||||
case reflect.Uint8:
|
||||
if val, err := strconv.ParseUint(defaultVal, 0, 8); err == nil {
|
||||
field.Set(reflect.ValueOf(uint8(val)).Convert(field.Type()))
|
||||
}
|
||||
case reflect.Uint16:
|
||||
if val, err := strconv.ParseUint(defaultVal, 0, 16); err == nil {
|
||||
field.Set(reflect.ValueOf(uint16(val)).Convert(field.Type()))
|
||||
}
|
||||
case reflect.Uint32:
|
||||
if val, err := strconv.ParseUint(defaultVal, 0, 32); err == nil {
|
||||
field.Set(reflect.ValueOf(uint32(val)).Convert(field.Type()))
|
||||
}
|
||||
case reflect.Uint64:
|
||||
if val, err := strconv.ParseUint(defaultVal, 0, 64); err == nil {
|
||||
field.Set(reflect.ValueOf(val).Convert(field.Type()))
|
||||
}
|
||||
case reflect.Uintptr:
|
||||
if val, err := strconv.ParseUint(defaultVal, 0, strconv.IntSize); err == nil {
|
||||
field.Set(reflect.ValueOf(uintptr(val)).Convert(field.Type()))
|
||||
}
|
||||
case reflect.Float32:
|
||||
if val, err := strconv.ParseFloat(defaultVal, 32); err == nil {
|
||||
field.Set(reflect.ValueOf(float32(val)).Convert(field.Type()))
|
||||
}
|
||||
case reflect.Float64:
|
||||
if val, err := strconv.ParseFloat(defaultVal, 64); err == nil {
|
||||
field.Set(reflect.ValueOf(val).Convert(field.Type()))
|
||||
}
|
||||
case reflect.String:
|
||||
field.Set(reflect.ValueOf(defaultVal).Convert(field.Type()))
|
||||
|
||||
case reflect.Slice:
|
||||
ref := reflect.New(field.Type())
|
||||
ref.Elem().Set(reflect.MakeSlice(field.Type(), 0, 0))
|
||||
if defaultVal != "" && defaultVal != "[]" {
|
||||
if err := json.Unmarshal([]byte(defaultVal), ref.Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
field.Set(ref.Elem().Convert(field.Type()))
|
||||
case reflect.Map:
|
||||
ref := reflect.New(field.Type())
|
||||
ref.Elem().Set(reflect.MakeMap(field.Type()))
|
||||
if defaultVal != "" && defaultVal != "{}" {
|
||||
if err := json.Unmarshal([]byte(defaultVal), ref.Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
field.Set(ref.Elem().Convert(field.Type()))
|
||||
case reflect.Struct:
|
||||
if defaultVal != "" && defaultVal != "{}" {
|
||||
if err := json.Unmarshal([]byte(defaultVal), field.Addr().Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case reflect.Ptr:
|
||||
field.Set(reflect.New(field.Type().Elem()))
|
||||
}
|
||||
}
|
||||
|
||||
switch field.Kind() {
|
||||
case reflect.Ptr:
|
||||
if isInitial || field.Elem().Kind() == reflect.Struct {
|
||||
setField(field.Elem(), defaultVal)
|
||||
callSetter(field.Interface())
|
||||
}
|
||||
case reflect.Struct:
|
||||
if err := Set(field.Addr().Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
case reflect.Slice:
|
||||
for j := 0; j < field.Len(); j++ {
|
||||
if err := setField(field.Index(j), ""); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case reflect.Map:
|
||||
for _, e := range field.MapKeys() {
|
||||
var v = field.MapIndex(e)
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Ptr:
|
||||
switch v.Elem().Kind() {
|
||||
case reflect.Struct, reflect.Slice, reflect.Map:
|
||||
if err := setField(v.Elem(), ""); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case reflect.Struct, reflect.Slice, reflect.Map:
|
||||
ref := reflect.New(v.Type())
|
||||
ref.Elem().Set(v)
|
||||
if err := setField(ref.Elem(), ""); err != nil {
|
||||
return err
|
||||
}
|
||||
field.SetMapIndex(e, ref.Elem().Convert(v.Type()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func unmarshalByInterface(field reflect.Value, defaultVal string) bool {
|
||||
asText, ok := field.Addr().Interface().(encoding.TextUnmarshaler)
|
||||
if ok && defaultVal != "" {
|
||||
// if field implements encode.TextUnmarshaler, try to use it before decode by kind
|
||||
if err := asText.UnmarshalText([]byte(defaultVal)); err == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
asJSON, ok := field.Addr().Interface().(json.Unmarshaler)
|
||||
if ok && defaultVal != "" && defaultVal != "{}" && defaultVal != "[]" {
|
||||
// if field implements json.Unmarshaler, try to use it before decode by kind
|
||||
if err := asJSON.UnmarshalJSON([]byte(defaultVal)); err == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isInitialValue(field reflect.Value) bool {
|
||||
return reflect.DeepEqual(reflect.Zero(field.Type()).Interface(), field.Interface())
|
||||
}
|
||||
|
||||
func shouldInitializeField(field reflect.Value, tag string) bool {
|
||||
switch field.Kind() {
|
||||
case reflect.Struct:
|
||||
return true
|
||||
case reflect.Ptr:
|
||||
if !field.IsNil() && field.Elem().Kind() == reflect.Struct {
|
||||
return true
|
||||
}
|
||||
case reflect.Slice:
|
||||
return field.Len() > 0 || tag != ""
|
||||
case reflect.Map:
|
||||
return field.Len() > 0 || tag != ""
|
||||
}
|
||||
|
||||
return tag != ""
|
||||
}
|
||||
|
||||
// CanUpdate returns true when the given value is an initial value of its type
|
||||
func CanUpdate(v interface{}) bool {
|
||||
return isInitialValue(reflect.ValueOf(v))
|
||||
}
|
12
vendor/github.com/creasty/defaults/setter.go
generated
vendored
Normal file
12
vendor/github.com/creasty/defaults/setter.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
package defaults
|
||||
|
||||
// Setter is an interface for setting default values
|
||||
type Setter interface {
|
||||
SetDefaults()
|
||||
}
|
||||
|
||||
func callSetter(v interface{}) {
|
||||
if ds, ok := v.(Setter); ok {
|
||||
ds.SetDefaults()
|
||||
}
|
||||
}
|
21
vendor/github.com/dustin/go-humanize/.travis.yml
generated
vendored
Normal file
21
vendor/github.com/dustin/go-humanize/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
sudo: false
|
||||
language: go
|
||||
go_import_path: github.com/dustin/go-humanize
|
||||
go:
|
||||
- 1.13.x
|
||||
- 1.14.x
|
||||
- 1.15.x
|
||||
- 1.16.x
|
||||
- stable
|
||||
- master
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: master
|
||||
fast_finish: true
|
||||
install:
|
||||
- # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step).
|
||||
script:
|
||||
- diff -u <(echo -n) <(gofmt -d -s .)
|
||||
- go vet .
|
||||
- go install -v -race ./...
|
||||
- go test -v -race ./...
|
21
vendor/github.com/dustin/go-humanize/LICENSE
generated
vendored
Normal file
21
vendor/github.com/dustin/go-humanize/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
Copyright (c) 2005-2008 Dustin Sallings <dustin@spy.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
<http://www.opensource.org/licenses/mit-license.php>
|
124
vendor/github.com/dustin/go-humanize/README.markdown
generated
vendored
Normal file
124
vendor/github.com/dustin/go-humanize/README.markdown
generated
vendored
Normal file
|
@ -0,0 +1,124 @@
|
|||
# Humane Units [![Build Status](https://travis-ci.org/dustin/go-humanize.svg?branch=master)](https://travis-ci.org/dustin/go-humanize) [![GoDoc](https://godoc.org/github.com/dustin/go-humanize?status.svg)](https://godoc.org/github.com/dustin/go-humanize)
|
||||
|
||||
Just a few functions for helping humanize times and sizes.
|
||||
|
||||
`go get` it as `github.com/dustin/go-humanize`, import it as
|
||||
`"github.com/dustin/go-humanize"`, use it as `humanize`.
|
||||
|
||||
See [godoc](https://pkg.go.dev/github.com/dustin/go-humanize) for
|
||||
complete documentation.
|
||||
|
||||
## Sizes
|
||||
|
||||
This lets you take numbers like `82854982` and convert them to useful
|
||||
strings like, `83 MB` or `79 MiB` (whichever you prefer).
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
fmt.Printf("That file is %s.", humanize.Bytes(82854982)) // That file is 83 MB.
|
||||
```
|
||||
|
||||
## Times
|
||||
|
||||
This lets you take a `time.Time` and spit it out in relative terms.
|
||||
For example, `12 seconds ago` or `3 days from now`.
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
fmt.Printf("This was touched %s.", humanize.Time(someTimeInstance)) // This was touched 7 hours ago.
|
||||
```
|
||||
|
||||
Thanks to Kyle Lemons for the time implementation from an IRC
|
||||
conversation one day. It's pretty neat.
|
||||
|
||||
## Ordinals
|
||||
|
||||
From a [mailing list discussion][odisc] where a user wanted to be able
|
||||
to label ordinals.
|
||||
|
||||
0 -> 0th
|
||||
1 -> 1st
|
||||
2 -> 2nd
|
||||
3 -> 3rd
|
||||
4 -> 4th
|
||||
[...]
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
fmt.Printf("You're my %s best friend.", humanize.Ordinal(193)) // You are my 193rd best friend.
|
||||
```
|
||||
|
||||
## Commas
|
||||
|
||||
Want to shove commas into numbers? Be my guest.
|
||||
|
||||
0 -> 0
|
||||
100 -> 100
|
||||
1000 -> 1,000
|
||||
1000000000 -> 1,000,000,000
|
||||
-100000 -> -100,000
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
fmt.Printf("You owe $%s.\n", humanize.Comma(6582491)) // You owe $6,582,491.
|
||||
```
|
||||
|
||||
## Ftoa
|
||||
|
||||
Nicer float64 formatter that removes trailing zeros.
|
||||
|
||||
```go
|
||||
fmt.Printf("%f", 2.24) // 2.240000
|
||||
fmt.Printf("%s", humanize.Ftoa(2.24)) // 2.24
|
||||
fmt.Printf("%f", 2.0) // 2.000000
|
||||
fmt.Printf("%s", humanize.Ftoa(2.0)) // 2
|
||||
```
|
||||
|
||||
## SI notation
|
||||
|
||||
Format numbers with [SI notation][sinotation].
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
humanize.SI(0.00000000223, "M") // 2.23 nM
|
||||
```
|
||||
|
||||
## English-specific functions
|
||||
|
||||
The following functions are in the `humanize/english` subpackage.
|
||||
|
||||
### Plurals
|
||||
|
||||
Simple English pluralization
|
||||
|
||||
```go
|
||||
english.PluralWord(1, "object", "") // object
|
||||
english.PluralWord(42, "object", "") // objects
|
||||
english.PluralWord(2, "bus", "") // buses
|
||||
english.PluralWord(99, "locus", "loci") // loci
|
||||
|
||||
english.Plural(1, "object", "") // 1 object
|
||||
english.Plural(42, "object", "") // 42 objects
|
||||
english.Plural(2, "bus", "") // 2 buses
|
||||
english.Plural(99, "locus", "loci") // 99 loci
|
||||
```
|
||||
|
||||
### Word series
|
||||
|
||||
Format comma-separated words lists with conjuctions:
|
||||
|
||||
```go
|
||||
english.WordSeries([]string{"foo"}, "and") // foo
|
||||
english.WordSeries([]string{"foo", "bar"}, "and") // foo and bar
|
||||
english.WordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar and baz
|
||||
|
||||
english.OxfordWordSeries([]string{"foo", "bar", "baz"}, "and") // foo, bar, and baz
|
||||
```
|
||||
|
||||
[odisc]: https://groups.google.com/d/topic/golang-nuts/l8NhI74jl-4/discussion
|
||||
[sinotation]: http://en.wikipedia.org/wiki/Metric_prefix
|
31
vendor/github.com/dustin/go-humanize/big.go
generated
vendored
Normal file
31
vendor/github.com/dustin/go-humanize/big.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
package humanize
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// order of magnitude (to a max order)
|
||||
func oomm(n, b *big.Int, maxmag int) (float64, int) {
|
||||
mag := 0
|
||||
m := &big.Int{}
|
||||
for n.Cmp(b) >= 0 {
|
||||
n.DivMod(n, b, m)
|
||||
mag++
|
||||
if mag == maxmag && maxmag >= 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag
|
||||
}
|
||||
|
||||
// total order of magnitude
|
||||
// (same as above, but with no upper limit)
|
||||
func oom(n, b *big.Int) (float64, int) {
|
||||
mag := 0
|
||||
m := &big.Int{}
|
||||
for n.Cmp(b) >= 0 {
|
||||
n.DivMod(n, b, m)
|
||||
mag++
|
||||
}
|
||||
return float64(n.Int64()) + (float64(m.Int64()) / float64(b.Int64())), mag
|
||||
}
|
189
vendor/github.com/dustin/go-humanize/bigbytes.go
generated
vendored
Normal file
189
vendor/github.com/dustin/go-humanize/bigbytes.go
generated
vendored
Normal file
|
@ -0,0 +1,189 @@
|
|||
package humanize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var (
|
||||
bigIECExp = big.NewInt(1024)
|
||||
|
||||
// BigByte is one byte in bit.Ints
|
||||
BigByte = big.NewInt(1)
|
||||
// BigKiByte is 1,024 bytes in bit.Ints
|
||||
BigKiByte = (&big.Int{}).Mul(BigByte, bigIECExp)
|
||||
// BigMiByte is 1,024 k bytes in bit.Ints
|
||||
BigMiByte = (&big.Int{}).Mul(BigKiByte, bigIECExp)
|
||||
// BigGiByte is 1,024 m bytes in bit.Ints
|
||||
BigGiByte = (&big.Int{}).Mul(BigMiByte, bigIECExp)
|
||||
// BigTiByte is 1,024 g bytes in bit.Ints
|
||||
BigTiByte = (&big.Int{}).Mul(BigGiByte, bigIECExp)
|
||||
// BigPiByte is 1,024 t bytes in bit.Ints
|
||||
BigPiByte = (&big.Int{}).Mul(BigTiByte, bigIECExp)
|
||||
// BigEiByte is 1,024 p bytes in bit.Ints
|
||||
BigEiByte = (&big.Int{}).Mul(BigPiByte, bigIECExp)
|
||||
// BigZiByte is 1,024 e bytes in bit.Ints
|
||||
BigZiByte = (&big.Int{}).Mul(BigEiByte, bigIECExp)
|
||||
// BigYiByte is 1,024 z bytes in bit.Ints
|
||||
BigYiByte = (&big.Int{}).Mul(BigZiByte, bigIECExp)
|
||||
// BigRiByte is 1,024 y bytes in bit.Ints
|
||||
BigRiByte = (&big.Int{}).Mul(BigYiByte, bigIECExp)
|
||||
// BigQiByte is 1,024 r bytes in bit.Ints
|
||||
BigQiByte = (&big.Int{}).Mul(BigRiByte, bigIECExp)
|
||||
)
|
||||
|
||||
var (
|
||||
bigSIExp = big.NewInt(1000)
|
||||
|
||||
// BigSIByte is one SI byte in big.Ints
|
||||
BigSIByte = big.NewInt(1)
|
||||
// BigKByte is 1,000 SI bytes in big.Ints
|
||||
BigKByte = (&big.Int{}).Mul(BigSIByte, bigSIExp)
|
||||
// BigMByte is 1,000 SI k bytes in big.Ints
|
||||
BigMByte = (&big.Int{}).Mul(BigKByte, bigSIExp)
|
||||
// BigGByte is 1,000 SI m bytes in big.Ints
|
||||
BigGByte = (&big.Int{}).Mul(BigMByte, bigSIExp)
|
||||
// BigTByte is 1,000 SI g bytes in big.Ints
|
||||
BigTByte = (&big.Int{}).Mul(BigGByte, bigSIExp)
|
||||
// BigPByte is 1,000 SI t bytes in big.Ints
|
||||
BigPByte = (&big.Int{}).Mul(BigTByte, bigSIExp)
|
||||
// BigEByte is 1,000 SI p bytes in big.Ints
|
||||
BigEByte = (&big.Int{}).Mul(BigPByte, bigSIExp)
|
||||
// BigZByte is 1,000 SI e bytes in big.Ints
|
||||
BigZByte = (&big.Int{}).Mul(BigEByte, bigSIExp)
|
||||
// BigYByte is 1,000 SI z bytes in big.Ints
|
||||
BigYByte = (&big.Int{}).Mul(BigZByte, bigSIExp)
|
||||
// BigRByte is 1,000 SI y bytes in big.Ints
|
||||
BigRByte = (&big.Int{}).Mul(BigYByte, bigSIExp)
|
||||
// BigQByte is 1,000 SI r bytes in big.Ints
|
||||
BigQByte = (&big.Int{}).Mul(BigRByte, bigSIExp)
|
||||
)
|
||||
|
||||
var bigBytesSizeTable = map[string]*big.Int{
|
||||
"b": BigByte,
|
||||
"kib": BigKiByte,
|
||||
"kb": BigKByte,
|
||||
"mib": BigMiByte,
|
||||
"mb": BigMByte,
|
||||
"gib": BigGiByte,
|
||||
"gb": BigGByte,
|
||||
"tib": BigTiByte,
|
||||
"tb": BigTByte,
|
||||
"pib": BigPiByte,
|
||||
"pb": BigPByte,
|
||||
"eib": BigEiByte,
|
||||
"eb": BigEByte,
|
||||
"zib": BigZiByte,
|
||||
"zb": BigZByte,
|
||||
"yib": BigYiByte,
|
||||
"yb": BigYByte,
|
||||
"rib": BigRiByte,
|
||||
"rb": BigRByte,
|
||||
"qib": BigQiByte,
|
||||
"qb": BigQByte,
|
||||
// Without suffix
|
||||
"": BigByte,
|
||||
"ki": BigKiByte,
|
||||
"k": BigKByte,
|
||||
"mi": BigMiByte,
|
||||
"m": BigMByte,
|
||||
"gi": BigGiByte,
|
||||
"g": BigGByte,
|
||||
"ti": BigTiByte,
|
||||
"t": BigTByte,
|
||||
"pi": BigPiByte,
|
||||
"p": BigPByte,
|
||||
"ei": BigEiByte,
|
||||
"e": BigEByte,
|
||||
"z": BigZByte,
|
||||
"zi": BigZiByte,
|
||||
"y": BigYByte,
|
||||
"yi": BigYiByte,
|
||||
"r": BigRByte,
|
||||
"ri": BigRiByte,
|
||||
"q": BigQByte,
|
||||
"qi": BigQiByte,
|
||||
}
|
||||
|
||||
var ten = big.NewInt(10)
|
||||
|
||||
func humanateBigBytes(s, base *big.Int, sizes []string) string {
|
||||
if s.Cmp(ten) < 0 {
|
||||
return fmt.Sprintf("%d B", s)
|
||||
}
|
||||
c := (&big.Int{}).Set(s)
|
||||
val, mag := oomm(c, base, len(sizes)-1)
|
||||
suffix := sizes[mag]
|
||||
f := "%.0f %s"
|
||||
if val < 10 {
|
||||
f = "%.1f %s"
|
||||
}
|
||||
|
||||
return fmt.Sprintf(f, val, suffix)
|
||||
|
||||
}
|
||||
|
||||
// BigBytes produces a human readable representation of an SI size.
|
||||
//
|
||||
// See also: ParseBigBytes.
|
||||
//
|
||||
// BigBytes(82854982) -> 83 MB
|
||||
func BigBytes(s *big.Int) string {
|
||||
sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", "RB", "QB"}
|
||||
return humanateBigBytes(s, bigSIExp, sizes)
|
||||
}
|
||||
|
||||
// BigIBytes produces a human readable representation of an IEC size.
|
||||
//
|
||||
// See also: ParseBigBytes.
|
||||
//
|
||||
// BigIBytes(82854982) -> 79 MiB
|
||||
func BigIBytes(s *big.Int) string {
|
||||
sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "RiB", "QiB"}
|
||||
return humanateBigBytes(s, bigIECExp, sizes)
|
||||
}
|
||||
|
||||
// ParseBigBytes parses a string representation of bytes into the number
|
||||
// of bytes it represents.
|
||||
//
|
||||
// See also: BigBytes, BigIBytes.
|
||||
//
|
||||
// ParseBigBytes("42 MB") -> 42000000, nil
|
||||
// ParseBigBytes("42 mib") -> 44040192, nil
|
||||
func ParseBigBytes(s string) (*big.Int, error) {
|
||||
lastDigit := 0
|
||||
hasComma := false
|
||||
for _, r := range s {
|
||||
if !(unicode.IsDigit(r) || r == '.' || r == ',') {
|
||||
break
|
||||
}
|
||||
if r == ',' {
|
||||
hasComma = true
|
||||
}
|
||||
lastDigit++
|
||||
}
|
||||
|
||||
num := s[:lastDigit]
|
||||
if hasComma {
|
||||
num = strings.Replace(num, ",", "", -1)
|
||||
}
|
||||
|
||||
val := &big.Rat{}
|
||||
_, err := fmt.Sscanf(num, "%f", val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extra := strings.ToLower(strings.TrimSpace(s[lastDigit:]))
|
||||
if m, ok := bigBytesSizeTable[extra]; ok {
|
||||
mv := (&big.Rat{}).SetInt(m)
|
||||
val.Mul(val, mv)
|
||||
rv := &big.Int{}
|
||||
rv.Div(val.Num(), val.Denom())
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unhandled size name: %v", extra)
|
||||
}
|
143
vendor/github.com/dustin/go-humanize/bytes.go
generated
vendored
Normal file
143
vendor/github.com/dustin/go-humanize/bytes.go
generated
vendored
Normal file
|
@ -0,0 +1,143 @@
|
|||
package humanize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// IEC Sizes.
|
||||
// kibis of bits
|
||||
const (
|
||||
Byte = 1 << (iota * 10)
|
||||
KiByte
|
||||
MiByte
|
||||
GiByte
|
||||
TiByte
|
||||
PiByte
|
||||
EiByte
|
||||
)
|
||||
|
||||
// SI Sizes.
|
||||
const (
|
||||
IByte = 1
|
||||
KByte = IByte * 1000
|
||||
MByte = KByte * 1000
|
||||
GByte = MByte * 1000
|
||||
TByte = GByte * 1000
|
||||
PByte = TByte * 1000
|
||||
EByte = PByte * 1000
|
||||
)
|
||||
|
||||
var bytesSizeTable = map[string]uint64{
|
||||
"b": Byte,
|
||||
"kib": KiByte,
|
||||
"kb": KByte,
|
||||
"mib": MiByte,
|
||||
"mb": MByte,
|
||||
"gib": GiByte,
|
||||
"gb": GByte,
|
||||
"tib": TiByte,
|
||||
"tb": TByte,
|
||||
"pib": PiByte,
|
||||
"pb": PByte,
|
||||
"eib": EiByte,
|
||||
"eb": EByte,
|
||||
// Without suffix
|
||||
"": Byte,
|
||||
"ki": KiByte,
|
||||
"k": KByte,
|
||||
"mi": MiByte,
|
||||
"m": MByte,
|
||||
"gi": GiByte,
|
||||
"g": GByte,
|
||||
"ti": TiByte,
|
||||
"t": TByte,
|
||||
"pi": PiByte,
|
||||
"p": PByte,
|
||||
"ei": EiByte,
|
||||
"e": EByte,
|
||||
}
|
||||
|
||||
func logn(n, b float64) float64 {
|
||||
return math.Log(n) / math.Log(b)
|
||||
}
|
||||
|
||||
func humanateBytes(s uint64, base float64, sizes []string) string {
|
||||
if s < 10 {
|
||||
return fmt.Sprintf("%d B", s)
|
||||
}
|
||||
e := math.Floor(logn(float64(s), base))
|
||||
suffix := sizes[int(e)]
|
||||
val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10
|
||||
f := "%.0f %s"
|
||||
if val < 10 {
|
||||
f = "%.1f %s"
|
||||
}
|
||||
|
||||
return fmt.Sprintf(f, val, suffix)
|
||||
}
|
||||
|
||||
// Bytes produces a human readable representation of an SI size.
|
||||
//
|
||||
// See also: ParseBytes.
|
||||
//
|
||||
// Bytes(82854982) -> 83 MB
|
||||
func Bytes(s uint64) string {
|
||||
sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"}
|
||||
return humanateBytes(s, 1000, sizes)
|
||||
}
|
||||
|
||||
// IBytes produces a human readable representation of an IEC size.
|
||||
//
|
||||
// See also: ParseBytes.
|
||||
//
|
||||
// IBytes(82854982) -> 79 MiB
|
||||
func IBytes(s uint64) string {
|
||||
sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}
|
||||
return humanateBytes(s, 1024, sizes)
|
||||
}
|
||||
|
||||
// ParseBytes parses a string representation of bytes into the number
|
||||
// of bytes it represents.
|
||||
//
|
||||
// See Also: Bytes, IBytes.
|
||||
//
|
||||
// ParseBytes("42 MB") -> 42000000, nil
|
||||
// ParseBytes("42 mib") -> 44040192, nil
|
||||
func ParseBytes(s string) (uint64, error) {
|
||||
lastDigit := 0
|
||||
hasComma := false
|
||||
for _, r := range s {
|
||||
if !(unicode.IsDigit(r) || r == '.' || r == ',') {
|
||||
break
|
||||
}
|
||||
if r == ',' {
|
||||
hasComma = true
|
||||
}
|
||||
lastDigit++
|
||||
}
|
||||
|
||||
num := s[:lastDigit]
|
||||
if hasComma {
|
||||
num = strings.Replace(num, ",", "", -1)
|
||||
}
|
||||
|
||||
f, err := strconv.ParseFloat(num, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
extra := strings.ToLower(strings.TrimSpace(s[lastDigit:]))
|
||||
if m, ok := bytesSizeTable[extra]; ok {
|
||||
f *= float64(m)
|
||||
if f >= math.MaxUint64 {
|
||||
return 0, fmt.Errorf("too large: %v", s)
|
||||
}
|
||||
return uint64(f), nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("unhandled size name: %v", extra)
|
||||
}
|
116
vendor/github.com/dustin/go-humanize/comma.go
generated
vendored
Normal file
116
vendor/github.com/dustin/go-humanize/comma.go
generated
vendored
Normal file
|
@ -0,0 +1,116 @@
|
|||
package humanize
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Comma produces a string form of the given number in base 10 with
|
||||
// commas after every three orders of magnitude.
|
||||
//
|
||||
// e.g. Comma(834142) -> 834,142
|
||||
func Comma(v int64) string {
|
||||
sign := ""
|
||||
|
||||
// Min int64 can't be negated to a usable value, so it has to be special cased.
|
||||
if v == math.MinInt64 {
|
||||
return "-9,223,372,036,854,775,808"
|
||||
}
|
||||
|
||||
if v < 0 {
|
||||
sign = "-"
|
||||
v = 0 - v
|
||||
}
|
||||
|
||||
parts := []string{"", "", "", "", "", "", ""}
|
||||
j := len(parts) - 1
|
||||
|
||||
for v > 999 {
|
||||
parts[j] = strconv.FormatInt(v%1000, 10)
|
||||
switch len(parts[j]) {
|
||||
case 2:
|
||||
parts[j] = "0" + parts[j]
|
||||
case 1:
|
||||
parts[j] = "00" + parts[j]
|
||||
}
|
||||
v = v / 1000
|
||||
j--
|
||||
}
|
||||
parts[j] = strconv.Itoa(int(v))
|
||||
return sign + strings.Join(parts[j:], ",")
|
||||
}
|
||||
|
||||
// Commaf produces a string form of the given number in base 10 with
|
||||
// commas after every three orders of magnitude.
|
||||
//
|
||||
// e.g. Commaf(834142.32) -> 834,142.32
|
||||
func Commaf(v float64) string {
|
||||
buf := &bytes.Buffer{}
|
||||
if v < 0 {
|
||||
buf.Write([]byte{'-'})
|
||||
v = 0 - v
|
||||
}
|
||||
|
||||
comma := []byte{','}
|
||||
|
||||
parts := strings.Split(strconv.FormatFloat(v, 'f', -1, 64), ".")
|
||||
pos := 0
|
||||
if len(parts[0])%3 != 0 {
|
||||
pos += len(parts[0]) % 3
|
||||
buf.WriteString(parts[0][:pos])
|
||||
buf.Write(comma)
|
||||
}
|
||||
for ; pos < len(parts[0]); pos += 3 {
|
||||
buf.WriteString(parts[0][pos : pos+3])
|
||||
buf.Write(comma)
|
||||
}
|
||||
buf.Truncate(buf.Len() - 1)
|
||||
|
||||
if len(parts) > 1 {
|
||||
buf.Write([]byte{'.'})
|
||||
buf.WriteString(parts[1])
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// CommafWithDigits works like the Commaf but limits the resulting
|
||||
// string to the given number of decimal places.
|
||||
//
|
||||
// e.g. CommafWithDigits(834142.32, 1) -> 834,142.3
|
||||
func CommafWithDigits(f float64, decimals int) string {
|
||||
return stripTrailingDigits(Commaf(f), decimals)
|
||||
}
|
||||
|
||||
// BigComma produces a string form of the given big.Int in base 10
|
||||
// with commas after every three orders of magnitude.
|
||||
func BigComma(b *big.Int) string {
|
||||
sign := ""
|
||||
if b.Sign() < 0 {
|
||||
sign = "-"
|
||||
b.Abs(b)
|
||||
}
|
||||
|
||||
athousand := big.NewInt(1000)
|
||||
c := (&big.Int{}).Set(b)
|
||||
_, m := oom(c, athousand)
|
||||
parts := make([]string, m+1)
|
||||
j := len(parts) - 1
|
||||
|
||||
mod := &big.Int{}
|
||||
for b.Cmp(athousand) >= 0 {
|
||||
b.DivMod(b, athousand, mod)
|
||||
parts[j] = strconv.FormatInt(mod.Int64(), 10)
|
||||
switch len(parts[j]) {
|
||||
case 2:
|
||||
parts[j] = "0" + parts[j]
|
||||
case 1:
|
||||
parts[j] = "00" + parts[j]
|
||||
}
|
||||
j--
|
||||
}
|
||||
parts[j] = strconv.Itoa(int(b.Int64()))
|
||||
return sign + strings.Join(parts[j:], ",")
|
||||
}
|
41
vendor/github.com/dustin/go-humanize/commaf.go
generated
vendored
Normal file
41
vendor/github.com/dustin/go-humanize/commaf.go
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
//go:build go1.6
|
||||
// +build go1.6
|
||||
|
||||
package humanize
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/big"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BigCommaf produces a string form of the given big.Float in base 10
|
||||
// with commas after every three orders of magnitude.
|
||||
func BigCommaf(v *big.Float) string {
|
||||
buf := &bytes.Buffer{}
|
||||
if v.Sign() < 0 {
|
||||
buf.Write([]byte{'-'})
|
||||
v.Abs(v)
|
||||
}
|
||||
|
||||
comma := []byte{','}
|
||||
|
||||
parts := strings.Split(v.Text('f', -1), ".")
|
||||
pos := 0
|
||||
if len(parts[0])%3 != 0 {
|
||||
pos += len(parts[0]) % 3
|
||||
buf.WriteString(parts[0][:pos])
|
||||
buf.Write(comma)
|
||||
}
|
||||
for ; pos < len(parts[0]); pos += 3 {
|
||||
buf.WriteString(parts[0][pos : pos+3])
|
||||
buf.Write(comma)
|
||||
}
|
||||
buf.Truncate(buf.Len() - 1)
|
||||
|
||||
if len(parts) > 1 {
|
||||
buf.Write([]byte{'.'})
|
||||
buf.WriteString(parts[1])
|
||||
}
|
||||
return buf.String()
|
||||
}
|
49
vendor/github.com/dustin/go-humanize/ftoa.go
generated
vendored
Normal file
49
vendor/github.com/dustin/go-humanize/ftoa.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
package humanize
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func stripTrailingZeros(s string) string {
|
||||
if !strings.ContainsRune(s, '.') {
|
||||
return s
|
||||
}
|
||||
offset := len(s) - 1
|
||||
for offset > 0 {
|
||||
if s[offset] == '.' {
|
||||
offset--
|
||||
break
|
||||
}
|
||||
if s[offset] != '0' {
|
||||
break
|
||||
}
|
||||
offset--
|
||||
}
|
||||
return s[:offset+1]
|
||||
}
|
||||
|
||||
func stripTrailingDigits(s string, digits int) string {
|
||||
if i := strings.Index(s, "."); i >= 0 {
|
||||
if digits <= 0 {
|
||||
return s[:i]
|
||||
}
|
||||
i++
|
||||
if i+digits >= len(s) {
|
||||
return s
|
||||
}
|
||||
return s[:i+digits]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Ftoa converts a float to a string with no trailing zeros.
|
||||
func Ftoa(num float64) string {
|
||||
return stripTrailingZeros(strconv.FormatFloat(num, 'f', 6, 64))
|
||||
}
|
||||
|
||||
// FtoaWithDigits converts a float to a string but limits the resulting string
|
||||
// to the given number of decimal places, and no trailing zeros.
|
||||
func FtoaWithDigits(num float64, digits int) string {
|
||||
return stripTrailingZeros(stripTrailingDigits(strconv.FormatFloat(num, 'f', 6, 64), digits))
|
||||
}
|
8
vendor/github.com/dustin/go-humanize/humanize.go
generated
vendored
Normal file
8
vendor/github.com/dustin/go-humanize/humanize.go
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
Package humanize converts boring ugly numbers to human-friendly strings and back.
|
||||
|
||||
Durations can be turned into strings such as "3 days ago", numbers
|
||||
representing sizes like 82854982 into useful strings like, "83 MB" or
|
||||
"79 MiB" (whichever you prefer).
|
||||
*/
|
||||
package humanize
|
192
vendor/github.com/dustin/go-humanize/number.go
generated
vendored
Normal file
192
vendor/github.com/dustin/go-humanize/number.go
generated
vendored
Normal file
|
@ -0,0 +1,192 @@
|
|||
package humanize
|
||||
|
||||
/*
|
||||
Slightly adapted from the source to fit go-humanize.
|
||||
|
||||
Author: https://github.com/gorhill
|
||||
Source: https://gist.github.com/gorhill/5285193
|
||||
|
||||
*/
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
renderFloatPrecisionMultipliers = [...]float64{
|
||||
1,
|
||||
10,
|
||||
100,
|
||||
1000,
|
||||
10000,
|
||||
100000,
|
||||
1000000,
|
||||
10000000,
|
||||
100000000,
|
||||
1000000000,
|
||||
}
|
||||
|
||||
renderFloatPrecisionRounders = [...]float64{
|
||||
0.5,
|
||||
0.05,
|
||||
0.005,
|
||||
0.0005,
|
||||
0.00005,
|
||||
0.000005,
|
||||
0.0000005,
|
||||
0.00000005,
|
||||
0.000000005,
|
||||
0.0000000005,
|
||||
}
|
||||
)
|
||||
|
||||
// FormatFloat produces a formatted number as string based on the following user-specified criteria:
|
||||
// * thousands separator
|
||||
// * decimal separator
|
||||
// * decimal precision
|
||||
//
|
||||
// Usage: s := RenderFloat(format, n)
|
||||
// The format parameter tells how to render the number n.
|
||||
//
|
||||
// See examples: http://play.golang.org/p/LXc1Ddm1lJ
|
||||
//
|
||||
// Examples of format strings, given n = 12345.6789:
|
||||
// "#,###.##" => "12,345.67"
|
||||
// "#,###." => "12,345"
|
||||
// "#,###" => "12345,678"
|
||||
// "#\u202F###,##" => "12 345,68"
|
||||
// "#.###,###### => 12.345,678900
|
||||
// "" (aka default format) => 12,345.67
|
||||
//
|
||||
// The highest precision allowed is 9 digits after the decimal symbol.
|
||||
// There is also a version for integer number, FormatInteger(),
|
||||
// which is convenient for calls within template.
|
||||
func FormatFloat(format string, n float64) string {
|
||||
// Special cases:
|
||||
// NaN = "NaN"
|
||||
// +Inf = "+Infinity"
|
||||
// -Inf = "-Infinity"
|
||||
if math.IsNaN(n) {
|
||||
return "NaN"
|
||||
}
|
||||
if n > math.MaxFloat64 {
|
||||
return "Infinity"
|
||||
}
|
||||
if n < (0.0 - math.MaxFloat64) {
|
||||
return "-Infinity"
|
||||
}
|
||||
|
||||
// default format
|
||||
precision := 2
|
||||
decimalStr := "."
|
||||
thousandStr := ","
|
||||
positiveStr := ""
|
||||
negativeStr := "-"
|
||||
|
||||
if len(format) > 0 {
|
||||
format := []rune(format)
|
||||
|
||||
// If there is an explicit format directive,
|
||||
// then default values are these:
|
||||
precision = 9
|
||||
thousandStr = ""
|
||||
|
||||
// collect indices of meaningful formatting directives
|
||||
formatIndx := []int{}
|
||||
for i, char := range format {
|
||||
if char != '#' && char != '0' {
|
||||
formatIndx = append(formatIndx, i)
|
||||
}
|
||||
}
|
||||
|
||||
if len(formatIndx) > 0 {
|
||||
// Directive at index 0:
|
||||
// Must be a '+'
|
||||
// Raise an error if not the case
|
||||
// index: 0123456789
|
||||
// +0.000,000
|
||||
// +000,000.0
|
||||
// +0000.00
|
||||
// +0000
|
||||
if formatIndx[0] == 0 {
|
||||
if format[formatIndx[0]] != '+' {
|
||||
panic("RenderFloat(): invalid positive sign directive")
|
||||
}
|
||||
positiveStr = "+"
|
||||
formatIndx = formatIndx[1:]
|
||||
}
|
||||
|
||||
// Two directives:
|
||||
// First is thousands separator
|
||||
// Raise an error if not followed by 3-digit
|
||||
// 0123456789
|
||||
// 0.000,000
|
||||
// 000,000.00
|
||||
if len(formatIndx) == 2 {
|
||||
if (formatIndx[1] - formatIndx[0]) != 4 {
|
||||
panic("RenderFloat(): thousands separator directive must be followed by 3 digit-specifiers")
|
||||
}
|
||||
thousandStr = string(format[formatIndx[0]])
|
||||
formatIndx = formatIndx[1:]
|
||||
}
|
||||
|
||||
// One directive:
|
||||
// Directive is decimal separator
|
||||
// The number of digit-specifier following the separator indicates wanted precision
|
||||
// 0123456789
|
||||
// 0.00
|
||||
// 000,0000
|
||||
if len(formatIndx) == 1 {
|
||||
decimalStr = string(format[formatIndx[0]])
|
||||
precision = len(format) - formatIndx[0] - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generate sign part
|
||||
var signStr string
|
||||
if n >= 0.000000001 {
|
||||
signStr = positiveStr
|
||||
} else if n <= -0.000000001 {
|
||||
signStr = negativeStr
|
||||
n = -n
|
||||
} else {
|
||||
signStr = ""
|
||||
n = 0.0
|
||||
}
|
||||
|
||||
// split number into integer and fractional parts
|
||||
intf, fracf := math.Modf(n + renderFloatPrecisionRounders[precision])
|
||||
|
||||
// generate integer part string
|
||||
intStr := strconv.FormatInt(int64(intf), 10)
|
||||
|
||||
// add thousand separator if required
|
||||
if len(thousandStr) > 0 {
|
||||
for i := len(intStr); i > 3; {
|
||||
i -= 3
|
||||
intStr = intStr[:i] + thousandStr + intStr[i:]
|
||||
}
|
||||
}
|
||||
|
||||
// no fractional part, we can leave now
|
||||
if precision == 0 {
|
||||
return signStr + intStr
|
||||
}
|
||||
|
||||
// generate fractional part
|
||||
fracStr := strconv.Itoa(int(fracf * renderFloatPrecisionMultipliers[precision]))
|
||||
// may need padding
|
||||
if len(fracStr) < precision {
|
||||
fracStr = "000000000000000"[:precision-len(fracStr)] + fracStr
|
||||
}
|
||||
|
||||
return signStr + intStr + decimalStr + fracStr
|
||||
}
|
||||
|
||||
// FormatInteger produces a formatted number as string.
|
||||
// See FormatFloat.
|
||||
func FormatInteger(format string, n int) string {
|
||||
return FormatFloat(format, float64(n))
|
||||
}
|
25
vendor/github.com/dustin/go-humanize/ordinals.go
generated
vendored
Normal file
25
vendor/github.com/dustin/go-humanize/ordinals.go
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
package humanize
|
||||
|
||||
import "strconv"
|
||||
|
||||
// Ordinal gives you the input number in a rank/ordinal format.
|
||||
//
|
||||
// Ordinal(3) -> 3rd
|
||||
func Ordinal(x int) string {
|
||||
suffix := "th"
|
||||
switch x % 10 {
|
||||
case 1:
|
||||
if x%100 != 11 {
|
||||
suffix = "st"
|
||||
}
|
||||
case 2:
|
||||
if x%100 != 12 {
|
||||
suffix = "nd"
|
||||
}
|
||||
case 3:
|
||||
if x%100 != 13 {
|
||||
suffix = "rd"
|
||||
}
|
||||
}
|
||||
return strconv.Itoa(x) + suffix
|
||||
}
|
127
vendor/github.com/dustin/go-humanize/si.go
generated
vendored
Normal file
127
vendor/github.com/dustin/go-humanize/si.go
generated
vendored
Normal file
|
@ -0,0 +1,127 @@
|
|||
package humanize
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var siPrefixTable = map[float64]string{
|
||||
-30: "q", // quecto
|
||||
-27: "r", // ronto
|
||||
-24: "y", // yocto
|
||||
-21: "z", // zepto
|
||||
-18: "a", // atto
|
||||
-15: "f", // femto
|
||||
-12: "p", // pico
|
||||
-9: "n", // nano
|
||||
-6: "µ", // micro
|
||||
-3: "m", // milli
|
||||
0: "",
|
||||
3: "k", // kilo
|
||||
6: "M", // mega
|
||||
9: "G", // giga
|
||||
12: "T", // tera
|
||||
15: "P", // peta
|
||||
18: "E", // exa
|
||||
21: "Z", // zetta
|
||||
24: "Y", // yotta
|
||||
27: "R", // ronna
|
||||
30: "Q", // quetta
|
||||
}
|
||||
|
||||
var revSIPrefixTable = revfmap(siPrefixTable)
|
||||
|
||||
// revfmap reverses the map and precomputes the power multiplier
|
||||
func revfmap(in map[float64]string) map[string]float64 {
|
||||
rv := map[string]float64{}
|
||||
for k, v := range in {
|
||||
rv[v] = math.Pow(10, k)
|
||||
}
|
||||
return rv
|
||||
}
|
||||
|
||||
var riParseRegex *regexp.Regexp
|
||||
|
||||
func init() {
|
||||
ri := `^([\-0-9.]+)\s?([`
|
||||
for _, v := range siPrefixTable {
|
||||
ri += v
|
||||
}
|
||||
ri += `]?)(.*)`
|
||||
|
||||
riParseRegex = regexp.MustCompile(ri)
|
||||
}
|
||||
|
||||
// ComputeSI finds the most appropriate SI prefix for the given number
|
||||
// and returns the prefix along with the value adjusted to be within
|
||||
// that prefix.
|
||||
//
|
||||
// See also: SI, ParseSI.
|
||||
//
|
||||
// e.g. ComputeSI(2.2345e-12) -> (2.2345, "p")
|
||||
func ComputeSI(input float64) (float64, string) {
|
||||
if input == 0 {
|
||||
return 0, ""
|
||||
}
|
||||
mag := math.Abs(input)
|
||||
exponent := math.Floor(logn(mag, 10))
|
||||
exponent = math.Floor(exponent/3) * 3
|
||||
|
||||
value := mag / math.Pow(10, exponent)
|
||||
|
||||
// Handle special case where value is exactly 1000.0
|
||||
// Should return 1 M instead of 1000 k
|
||||
if value == 1000.0 {
|
||||
exponent += 3
|
||||
value = mag / math.Pow(10, exponent)
|
||||
}
|
||||
|
||||
value = math.Copysign(value, input)
|
||||
|
||||
prefix := siPrefixTable[exponent]
|
||||
return value, prefix
|
||||
}
|
||||
|
||||
// SI returns a string with default formatting.
|
||||
//
|
||||
// SI uses Ftoa to format float value, removing trailing zeros.
|
||||
//
|
||||
// See also: ComputeSI, ParseSI.
|
||||
//
|
||||
// e.g. SI(1000000, "B") -> 1 MB
|
||||
// e.g. SI(2.2345e-12, "F") -> 2.2345 pF
|
||||
func SI(input float64, unit string) string {
|
||||
value, prefix := ComputeSI(input)
|
||||
return Ftoa(value) + " " + prefix + unit
|
||||
}
|
||||
|
||||
// SIWithDigits works like SI but limits the resulting string to the
|
||||
// given number of decimal places.
|
||||
//
|
||||
// e.g. SIWithDigits(1000000, 0, "B") -> 1 MB
|
||||
// e.g. SIWithDigits(2.2345e-12, 2, "F") -> 2.23 pF
|
||||
func SIWithDigits(input float64, decimals int, unit string) string {
|
||||
value, prefix := ComputeSI(input)
|
||||
return FtoaWithDigits(value, decimals) + " " + prefix + unit
|
||||
}
|
||||
|
||||
var errInvalid = errors.New("invalid input")
|
||||
|
||||
// ParseSI parses an SI string back into the number and unit.
|
||||
//
|
||||
// See also: SI, ComputeSI.
|
||||
//
|
||||
// e.g. ParseSI("2.2345 pF") -> (2.2345e-12, "F", nil)
|
||||
func ParseSI(input string) (float64, string, error) {
|
||||
found := riParseRegex.FindStringSubmatch(input)
|
||||
if len(found) != 4 {
|
||||
return 0, "", errInvalid
|
||||
}
|
||||
mag := revSIPrefixTable[found[2]]
|
||||
unit := found[3]
|
||||
|
||||
base, err := strconv.ParseFloat(found[1], 64)
|
||||
return base * mag, unit, err
|
||||
}
|
117
vendor/github.com/dustin/go-humanize/times.go
generated
vendored
Normal file
117
vendor/github.com/dustin/go-humanize/times.go
generated
vendored
Normal file
|
@ -0,0 +1,117 @@
|
|||
package humanize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Seconds-based time units
|
||||
const (
|
||||
Day = 24 * time.Hour
|
||||
Week = 7 * Day
|
||||
Month = 30 * Day
|
||||
Year = 12 * Month
|
||||
LongTime = 37 * Year
|
||||
)
|
||||
|
||||
// Time formats a time into a relative string.
|
||||
//
|
||||
// Time(someT) -> "3 weeks ago"
|
||||
func Time(then time.Time) string {
|
||||
return RelTime(then, time.Now(), "ago", "from now")
|
||||
}
|
||||
|
||||
// A RelTimeMagnitude struct contains a relative time point at which
|
||||
// the relative format of time will switch to a new format string. A
|
||||
// slice of these in ascending order by their "D" field is passed to
|
||||
// CustomRelTime to format durations.
|
||||
//
|
||||
// The Format field is a string that may contain a "%s" which will be
|
||||
// replaced with the appropriate signed label (e.g. "ago" or "from
|
||||
// now") and a "%d" that will be replaced by the quantity.
|
||||
//
|
||||
// The DivBy field is the amount of time the time difference must be
|
||||
// divided by in order to display correctly.
|
||||
//
|
||||
// e.g. if D is 2*time.Minute and you want to display "%d minutes %s"
|
||||
// DivBy should be time.Minute so whatever the duration is will be
|
||||
// expressed in minutes.
|
||||
type RelTimeMagnitude struct {
|
||||
D time.Duration
|
||||
Format string
|
||||
DivBy time.Duration
|
||||
}
|
||||
|
||||
var defaultMagnitudes = []RelTimeMagnitude{
|
||||
{time.Second, "now", time.Second},
|
||||
{2 * time.Second, "1 second %s", 1},
|
||||
{time.Minute, "%d seconds %s", time.Second},
|
||||
{2 * time.Minute, "1 minute %s", 1},
|
||||
{time.Hour, "%d minutes %s", time.Minute},
|
||||
{2 * time.Hour, "1 hour %s", 1},
|
||||
{Day, "%d hours %s", time.Hour},
|
||||
{2 * Day, "1 day %s", 1},
|
||||
{Week, "%d days %s", Day},
|
||||
{2 * Week, "1 week %s", 1},
|
||||
{Month, "%d weeks %s", Week},
|
||||
{2 * Month, "1 month %s", 1},
|
||||
{Year, "%d months %s", Month},
|
||||
{18 * Month, "1 year %s", 1},
|
||||
{2 * Year, "2 years %s", 1},
|
||||
{LongTime, "%d years %s", Year},
|
||||
{math.MaxInt64, "a long while %s", 1},
|
||||
}
|
||||
|
||||
// RelTime formats a time into a relative string.
|
||||
//
|
||||
// It takes two times and two labels. In addition to the generic time
|
||||
// delta string (e.g. 5 minutes), the labels are used applied so that
|
||||
// the label corresponding to the smaller time is applied.
|
||||
//
|
||||
// RelTime(timeInPast, timeInFuture, "earlier", "later") -> "3 weeks earlier"
|
||||
func RelTime(a, b time.Time, albl, blbl string) string {
|
||||
return CustomRelTime(a, b, albl, blbl, defaultMagnitudes)
|
||||
}
|
||||
|
||||
// CustomRelTime formats a time into a relative string.
|
||||
//
|
||||
// It takes two times two labels and a table of relative time formats.
|
||||
// In addition to the generic time delta string (e.g. 5 minutes), the
|
||||
// labels are used applied so that the label corresponding to the
|
||||
// smaller time is applied.
|
||||
func CustomRelTime(a, b time.Time, albl, blbl string, magnitudes []RelTimeMagnitude) string {
|
||||
lbl := albl
|
||||
diff := b.Sub(a)
|
||||
|
||||
if a.After(b) {
|
||||
lbl = blbl
|
||||
diff = a.Sub(b)
|
||||
}
|
||||
|
||||
n := sort.Search(len(magnitudes), func(i int) bool {
|
||||
return magnitudes[i].D > diff
|
||||
})
|
||||
|
||||
if n >= len(magnitudes) {
|
||||
n = len(magnitudes) - 1
|
||||
}
|
||||
mag := magnitudes[n]
|
||||
args := []interface{}{}
|
||||
escaped := false
|
||||
for _, ch := range mag.Format {
|
||||
if escaped {
|
||||
switch ch {
|
||||
case 's':
|
||||
args = append(args, lbl)
|
||||
case 'd':
|
||||
args = append(args, diff/mag.DivBy)
|
||||
}
|
||||
escaped = false
|
||||
} else {
|
||||
escaped = ch == '%'
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf(mag.Format, args...)
|
||||
}
|
23
vendor/github.com/glebarez/go-sqlite/AUTHORS
generated
vendored
Normal file
23
vendor/github.com/glebarez/go-sqlite/AUTHORS
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
# This file lists authors for copyright purposes. This file is distinct from
|
||||
# the CONTRIBUTORS files. See the latter for an explanation.
|
||||
#
|
||||
# Names should be added to this file as:
|
||||
# Name or Organization <email address>
|
||||
#
|
||||
# The email address is not required for organizations.
|
||||
#
|
||||
# Please keep the list sorted.
|
||||
|
||||
Artyom Pervukhin <github@artyom.dev>
|
||||
Dan Peterson <danp@danp.net>
|
||||
David Walton <david@davidwalton.com>
|
||||
Davsk Ltd Co <skinner.david@gmail.com>
|
||||
Jaap Aarts <jaap.aarts1@gmail.com>
|
||||
Jan Mercl <0xjnml@gmail.com>
|
||||
Josh Bleecher Snyder <josharian@gmail.com>
|
||||
Logan Snow <logansnow@protonmail.com>
|
||||
Michael Hoffmann <mhoffm@posteo.de>
|
||||
Ross Light <ross@zombiezen.com>
|
||||
Saed SayedAhmed <saadmtsa@gmail.com>
|
||||
Steffen Butzer <steffen(dot)butzer@outlook.com>
|
||||
Michael Rykov <mrykov@gmail.com>
|
28
vendor/github.com/glebarez/go-sqlite/CONTRIBUTORS
generated
vendored
Normal file
28
vendor/github.com/glebarez/go-sqlite/CONTRIBUTORS
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
# This file lists people who contributed code to this repository. The AUTHORS
|
||||
# file lists the copyright holders; this file lists people.
|
||||
#
|
||||
# Names should be added to this file like so:
|
||||
# Name <email address>
|
||||
#
|
||||
# Please keep the list sorted.
|
||||
|
||||
Alexander Menzhinsky <amenzhinsky@gmail.com>
|
||||
Artyom Pervukhin <github@artyom.dev>
|
||||
Dan Peterson <danp@danp.net>
|
||||
David Skinner <skinner.david@gmail.com>
|
||||
David Walton <david@davidwalton.com>
|
||||
Elle Mouton <elle.mouton@gmail.com>
|
||||
FlyingOnion <731677080@qq.com>
|
||||
Gleb Sakhnov <gleb.sakhnov@gmail.com>
|
||||
Jaap Aarts <jaap.aarts1@gmail.com>
|
||||
Jan Mercl <0xjnml@gmail.com>
|
||||
Josh Bleecher Snyder <josharian@gmail.com>
|
||||
Logan Snow <logansnow@protonmail.com>
|
||||
Matthew Gabeler-Lee <fastcat@gmail.com>
|
||||
Michael Hoffmann <mhoffm@posteo.de>
|
||||
Ross Light <ross@zombiezen.com>
|
||||
Saed SayedAhmed <saadmtsa@gmail.com>
|
||||
Steffen Butzer <steffen(dot)butzer@outlook.com>
|
||||
Yaacov Akiba Slama <ya@slamail.org>
|
||||
Saed SayedAhmed <saadmtsa@gmail.com>
|
||||
Michael Rykov <mrykov@gmail.com>
|
26
vendor/github.com/glebarez/go-sqlite/LICENSE
generated
vendored
Normal file
26
vendor/github.com/glebarez/go-sqlite/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
Copyright (c) 2017 The Sqlite Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
45
vendor/github.com/glebarez/go-sqlite/README.md
generated
vendored
Normal file
45
vendor/github.com/glebarez/go-sqlite/README.md
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
[![Tests](https://github.com/glebarez/go-sqlite/actions/workflows/tests.yml/badge.svg)](https://github.com/glebarez/go-sqlite/actions/workflows/tests.yml)
|
||||
![badge](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/glebarez/0fd7561eb29baf31d5362ffee1ae1702/raw/badge-sqlite-version-with-date.json)
|
||||
|
||||
# go-sqlite
|
||||
This is a pure-Go SQLite driver for Golang's native [database/sql](https://pkg.go.dev/database/sql) package.
|
||||
The driver has [Go-based implementation of SQLite](https://gitlab.com/cznic/sqlite) embedded in itself (so, you don't need to install SQLite separately)
|
||||
|
||||
# Usage
|
||||
|
||||
## Example
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
|
||||
_ "github.com/glebarez/go-sqlite"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// connect
|
||||
db, err := sql.Open("sqlite", ":memory:")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// get SQLite version
|
||||
_ := db.QueryRow("select sqlite_version()")
|
||||
}
|
||||
```
|
||||
|
||||
## Connection string examples
|
||||
- in-memory SQLite: ```":memory:"```
|
||||
- on-disk SQLite: ```"path/to/some.db"```
|
||||
- Foreign-key constraint activation: ```":memory:?_pragma=foreign_keys(1)"```
|
||||
|
||||
## Settings PRAGMAs in connection string
|
||||
Any SQLIte pragma can be preset for a Database connection using ```_pragma``` query parameter. Examples:
|
||||
- [journal mode](https://www.sqlite.org/pragma.html#pragma_journal_mode): ```path/to/some.db?_pragma=journal_mode(WAL)```
|
||||
- [busy timeout](https://www.sqlite.org/pragma.html#pragma_busy_timeout): ```:memory:?_pragma=busy_timeout(5000)```
|
||||
|
||||
Multiple PRAGMAs can be specified, e.g.:<br>
|
||||
```path/to/some.db?_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL)```
|
25
vendor/github.com/glebarez/go-sqlite/SQLITE-LICENSE
generated
vendored
Normal file
25
vendor/github.com/glebarez/go-sqlite/SQLITE-LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
SQLite Is Public Domain
|
||||
|
||||
All of the code and documentation in SQLite has been dedicated to the public
|
||||
domain by the authors. All code authors, and representatives of the companies
|
||||
they work for, have signed affidavits dedicating their contributions to the
|
||||
public domain and originals of those signed affidavits are stored in a firesafe
|
||||
at the main offices of Hwaci. Anyone is free to copy, modify, publish, use,
|
||||
compile, sell, or distribute the original SQLite code, either in source code
|
||||
form or as a compiled binary, for any purpose, commercial or non-commercial,
|
||||
and by any means.
|
||||
|
||||
The previous paragraph applies to the deliverable code and documentation in
|
||||
SQLite - those parts of the SQLite library that you actually bundle and ship
|
||||
with a larger application. Some scripts used as part of the build process (for
|
||||
example the "configure" scripts generated by autoconf) might fall under other
|
||||
open-source licenses. Nothing from these build scripts ever reaches the final
|
||||
deliverable SQLite library, however, and so the licenses associated with those
|
||||
scripts should not be a factor in assessing your rights to copy and use the
|
||||
SQLite library.
|
||||
|
||||
All of the deliverable code in SQLite has been written from scratch. No code
|
||||
has been taken from other projects or from the open internet. Every line of
|
||||
code can be traced back to its original author, and all of those authors have
|
||||
public domain dedications on file. So the SQLite code base is clean and is
|
||||
uncontaminated with licensed code from other projects.
|
BIN
vendor/github.com/glebarez/go-sqlite/embed.db
generated
vendored
Normal file
BIN
vendor/github.com/glebarez/go-sqlite/embed.db
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/github.com/glebarez/go-sqlite/embed2.db
generated
vendored
Normal file
BIN
vendor/github.com/glebarez/go-sqlite/embed2.db
generated
vendored
Normal file
Binary file not shown.
23
vendor/github.com/glebarez/go-sqlite/mutex.go
generated
vendored
Normal file
23
vendor/github.com/glebarez/go-sqlite/mutex.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2019 The Sqlite Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sqlite // import "modernc.org/sqlite"
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"modernc.org/libc"
|
||||
"modernc.org/libc/sys/types"
|
||||
)
|
||||
|
||||
type mutex struct {
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func mutexAlloc(tls *libc.TLS) uintptr {
|
||||
return libc.Xcalloc(tls, 1, types.Size_t(unsafe.Sizeof(mutex{})))
|
||||
}
|
||||
|
||||
func mutexFree(tls *libc.TLS, m uintptr) { libc.Xfree(tls, m) }
|
10
vendor/github.com/glebarez/go-sqlite/norlimit.go
generated
vendored
Normal file
10
vendor/github.com/glebarez/go-sqlite/norlimit.go
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
// Copyright 2021 The Sqlite Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package sqlite // import "modernc.org/sqlite"
|
||||
|
||||
func setMaxOpenFiles(n int) error { return nil }
|
19
vendor/github.com/glebarez/go-sqlite/rlimit.go
generated
vendored
Normal file
19
vendor/github.com/glebarez/go-sqlite/rlimit.go
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2021 The Sqlite Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build freebsd
|
||||
// +build freebsd
|
||||
|
||||
package sqlite // import "modernc.org/sqlite"
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func setMaxOpenFiles(n int64) error {
|
||||
var rLimit unix.Rlimit
|
||||
rLimit.Max = n
|
||||
rLimit.Cur = n
|
||||
return unix.Setrlimit(unix.RLIMIT_NOFILE, &rLimit)
|
||||
}
|
19
vendor/github.com/glebarez/go-sqlite/rulimit.go
generated
vendored
Normal file
19
vendor/github.com/glebarez/go-sqlite/rulimit.go
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2021 The Sqlite Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build linux || darwin || netbsd || openbsd
|
||||
// +build linux darwin netbsd openbsd
|
||||
|
||||
package sqlite // import "modernc.org/sqlite"
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func setMaxOpenFiles(n int64) error {
|
||||
var rLimit unix.Rlimit
|
||||
rLimit.Max = uint64(n)
|
||||
rLimit.Cur = uint64(n)
|
||||
return unix.Setrlimit(unix.RLIMIT_NOFILE, &rLimit)
|
||||
}
|
1747
vendor/github.com/glebarez/go-sqlite/sqlite.go
generated
vendored
Normal file
1747
vendor/github.com/glebarez/go-sqlite/sqlite.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
49
vendor/github.com/glebarez/go-sqlite/sqlite_go18.go
generated
vendored
Normal file
49
vendor/github.com/glebarez/go-sqlite/sqlite_go18.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Copyright 2017 The Sqlite Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.8
|
||||
// +build go1.8
|
||||
|
||||
package sqlite // import "modernc.org/sqlite"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql/driver"
|
||||
)
|
||||
|
||||
// Ping implements driver.Pinger
|
||||
func (c *conn) Ping(ctx context.Context) error {
|
||||
_, err := c.ExecContext(ctx, "select 1", nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// BeginTx implements driver.ConnBeginTx
|
||||
func (c *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
|
||||
return c.begin(ctx, opts)
|
||||
}
|
||||
|
||||
// PrepareContext implements driver.ConnPrepareContext
|
||||
func (c *conn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
|
||||
return c.prepare(ctx, query)
|
||||
}
|
||||
|
||||
// ExecContext implements driver.ExecerContext
|
||||
func (c *conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
|
||||
return c.exec(ctx, query, args)
|
||||
}
|
||||
|
||||
// QueryContext implements driver.QueryerContext
|
||||
func (c *conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
|
||||
return c.query(ctx, query, args)
|
||||
}
|
||||
|
||||
// ExecContext implements driver.StmtExecContext
|
||||
func (s *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
|
||||
return s.exec(ctx, args)
|
||||
}
|
||||
|
||||
// QueryContext implements driver.StmtQueryContext
|
||||
func (s *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
|
||||
return s.query(ctx, args)
|
||||
}
|
28
vendor/github.com/google/uuid/CHANGELOG.md
generated
vendored
Normal file
28
vendor/github.com/google/uuid/CHANGELOG.md
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
# Changelog
|
||||
|
||||
## [1.5.0](https://github.com/google/uuid/compare/v1.4.0...v1.5.0) (2023-12-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Validate UUID without creating new UUID ([#141](https://github.com/google/uuid/issues/141)) ([9ee7366](https://github.com/google/uuid/commit/9ee7366e66c9ad96bab89139418a713dc584ae29))
|
||||
|
||||
## [1.4.0](https://github.com/google/uuid/compare/v1.3.1...v1.4.0) (2023-10-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* UUIDs slice type with Strings() convenience method ([#133](https://github.com/google/uuid/issues/133)) ([cd5fbbd](https://github.com/google/uuid/commit/cd5fbbdd02f3e3467ac18940e07e062be1f864b4))
|
||||
|
||||
### Fixes
|
||||
|
||||
* Clarify that Parse's job is to parse but not necessarily validate strings. (Documents current behavior)
|
||||
|
||||
## [1.3.1](https://github.com/google/uuid/compare/v1.3.0...v1.3.1) (2023-08-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Use .EqualFold() to parse urn prefixed UUIDs ([#118](https://github.com/google/uuid/issues/118)) ([574e687](https://github.com/google/uuid/commit/574e6874943741fb99d41764c705173ada5293f0))
|
||||
|
||||
## Changelog
|
26
vendor/github.com/google/uuid/CONTRIBUTING.md
generated
vendored
Normal file
26
vendor/github.com/google/uuid/CONTRIBUTING.md
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
# How to contribute
|
||||
|
||||
We definitely welcome patches and contribution to this project!
|
||||
|
||||
### Tips
|
||||
|
||||
Commits must be formatted according to the [Conventional Commits Specification](https://www.conventionalcommits.org).
|
||||
|
||||
Always try to include a test case! If it is not possible or not necessary,
|
||||
please explain why in the pull request description.
|
||||
|
||||
### Releasing
|
||||
|
||||
Commits that would precipitate a SemVer change, as described in the Conventional
|
||||
Commits Specification, will trigger [`release-please`](https://github.com/google-github-actions/release-please-action)
|
||||
to create a release candidate pull request. Once submitted, `release-please`
|
||||
will create a release.
|
||||
|
||||
For tips on how to work with `release-please`, see its documentation.
|
||||
|
||||
### Legal requirements
|
||||
|
||||
In order to protect both you and ourselves, you will need to sign the
|
||||
[Contributor License Agreement](https://cla.developers.google.com/clas).
|
||||
|
||||
You may have already signed it for other Google projects.
|
9
vendor/github.com/google/uuid/CONTRIBUTORS
generated
vendored
Normal file
9
vendor/github.com/google/uuid/CONTRIBUTORS
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
Paul Borman <borman@google.com>
|
||||
bmatsuo
|
||||
shawnps
|
||||
theory
|
||||
jboverfelt
|
||||
dsymonds
|
||||
cd1
|
||||
wallclockbuilder
|
||||
dansouza
|
27
vendor/github.com/google/uuid/LICENSE
generated
vendored
Normal file
27
vendor/github.com/google/uuid/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2009,2014 Google Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
21
vendor/github.com/google/uuid/README.md
generated
vendored
Normal file
21
vendor/github.com/google/uuid/README.md
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
# uuid
|
||||
The uuid package generates and inspects UUIDs based on
|
||||
[RFC 4122](https://datatracker.ietf.org/doc/html/rfc4122)
|
||||
and DCE 1.1: Authentication and Security Services.
|
||||
|
||||
This package is based on the github.com/pborman/uuid package (previously named
|
||||
code.google.com/p/go-uuid). It differs from these earlier packages in that
|
||||
a UUID is a 16 byte array rather than a byte slice. One loss due to this
|
||||
change is the ability to represent an invalid UUID (vs a NIL UUID).
|
||||
|
||||
###### Install
|
||||
```sh
|
||||
go get github.com/google/uuid
|
||||
```
|
||||
|
||||
###### Documentation
|
||||
[![Go Reference](https://pkg.go.dev/badge/github.com/google/uuid.svg)](https://pkg.go.dev/github.com/google/uuid)
|
||||
|
||||
Full `go doc` style documentation for the package can be viewed online without
|
||||
installing this package by using the GoDoc site here:
|
||||
http://pkg.go.dev/github.com/google/uuid
|
80
vendor/github.com/google/uuid/dce.go
generated
vendored
Normal file
80
vendor/github.com/google/uuid/dce.go
generated
vendored
Normal file
|
@ -0,0 +1,80 @@
|
|||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// A Domain represents a Version 2 domain
|
||||
type Domain byte
|
||||
|
||||
// Domain constants for DCE Security (Version 2) UUIDs.
|
||||
const (
|
||||
Person = Domain(0)
|
||||
Group = Domain(1)
|
||||
Org = Domain(2)
|
||||
)
|
||||
|
||||
// NewDCESecurity returns a DCE Security (Version 2) UUID.
|
||||
//
|
||||
// The domain should be one of Person, Group or Org.
|
||||
// On a POSIX system the id should be the users UID for the Person
|
||||
// domain and the users GID for the Group. The meaning of id for
|
||||
// the domain Org or on non-POSIX systems is site defined.
|
||||
//
|
||||
// For a given domain/id pair the same token may be returned for up to
|
||||
// 7 minutes and 10 seconds.
|
||||
func NewDCESecurity(domain Domain, id uint32) (UUID, error) {
|
||||
uuid, err := NewUUID()
|
||||
if err == nil {
|
||||
uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2
|
||||
uuid[9] = byte(domain)
|
||||
binary.BigEndian.PutUint32(uuid[0:], id)
|
||||
}
|
||||
return uuid, err
|
||||
}
|
||||
|
||||
// NewDCEPerson returns a DCE Security (Version 2) UUID in the person
|
||||
// domain with the id returned by os.Getuid.
|
||||
//
|
||||
// NewDCESecurity(Person, uint32(os.Getuid()))
|
||||
func NewDCEPerson() (UUID, error) {
|
||||
return NewDCESecurity(Person, uint32(os.Getuid()))
|
||||
}
|
||||
|
||||
// NewDCEGroup returns a DCE Security (Version 2) UUID in the group
|
||||
// domain with the id returned by os.Getgid.
|
||||
//
|
||||
// NewDCESecurity(Group, uint32(os.Getgid()))
|
||||
func NewDCEGroup() (UUID, error) {
|
||||
return NewDCESecurity(Group, uint32(os.Getgid()))
|
||||
}
|
||||
|
||||
// Domain returns the domain for a Version 2 UUID. Domains are only defined
|
||||
// for Version 2 UUIDs.
|
||||
func (uuid UUID) Domain() Domain {
|
||||
return Domain(uuid[9])
|
||||
}
|
||||
|
||||
// ID returns the id for a Version 2 UUID. IDs are only defined for Version 2
|
||||
// UUIDs.
|
||||
func (uuid UUID) ID() uint32 {
|
||||
return binary.BigEndian.Uint32(uuid[0:4])
|
||||
}
|
||||
|
||||
func (d Domain) String() string {
|
||||
switch d {
|
||||
case Person:
|
||||
return "Person"
|
||||
case Group:
|
||||
return "Group"
|
||||
case Org:
|
||||
return "Org"
|
||||
}
|
||||
return fmt.Sprintf("Domain%d", int(d))
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue