Skip to content

Commit

Permalink
Merge branch 'main' into os_agnostic_configdir
Browse files Browse the repository at this point in the history
  • Loading branch information
dogancanbakir committed Sep 28, 2023
2 parents c49e6d6 + 4ad2d20 commit 4af426c
Show file tree
Hide file tree
Showing 16 changed files with 666 additions and 47 deletions.
40 changes: 20 additions & 20 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,32 @@
version: 2
updates:

# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
target-branch: "dev"
commit-message:
prefix: "chore"
include: "scope"

# Maintain dependencies for go modules
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "weekly"
target-branch: "dev"
target-branch: "main"
commit-message:
prefix: "chore"
include: "scope"

# Maintain dependencies for docker
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
target-branch: "dev"
commit-message:
prefix: "chore"
include: "scope"
# # Maintain dependencies for docker
# - package-ecosystem: "docker"
# directory: "/"
# schedule:
# interval: "weekly"
# target-branch: "dev"
# commit-message:
# prefix: "chore"
# include: "scope"
#
# # Maintain dependencies for GitHub Actions
# - package-ecosystem: "github-actions"
# directory: "/"
# schedule:
# interval: "weekly"
# target-branch: "dev"
# commit-message:
# prefix: "chore"
# include: "scope"
17 changes: 17 additions & 0 deletions .github/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
changelog:
exclude:
authors:
- dependabot
categories:
- title: 🎉 New Features
labels:
- "Type: Enhancement"
- title: 🐞 Bugs Fixes
labels:
- "Type: Bug"
- title: 🔨 Maintenance
labels:
- "Type: Maintenance"
- title: Other Changes
labels:
- "*"
8 changes: 6 additions & 2 deletions .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
name: 🔨 Build Test

on:
pull_request:
paths:
- '**.go'
- '**.mod'
workflow_dispatch:

jobs:
Expand All @@ -9,14 +13,14 @@ jobs:
strategy:
matrix:
go-version: [1.20.x]
os: [ubuntu-latest, windows-latest, macOS-13]
os: [ubuntu-latest, windows-latest, macOS-latest]

runs-on: ${{ matrix.os }}
steps:
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.20.x
go-version: ${{ matrix.go-version }}

- name: Check out code
uses: actions/checkout@v3
Expand Down
26 changes: 26 additions & 0 deletions .github/workflows/dep-auto-merge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: 🤖 dep auto merge

on:
pull_request:
branches:
- main
workflow_dispatch:

permissions:
pull-requests: write
issues: write
repository-projects: write

jobs:
automerge:
runs-on: ubuntu-latest
if: github.actor == 'dependabot[bot]'
steps:
- uses: actions/checkout@v3
with:
token: ${{ secrets.DEPENDABOT_PAT }}

- uses: ahmadnassri/action-dependabot-auto-merge@v2
with:
github-token: ${{ secrets.DEPENDABOT_PAT }}
target: all
4 changes: 4 additions & 0 deletions .github/workflows/lint-test.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
name: 🙏🏻 Lint Test

on:
pull_request:
paths:
- '**.go'
- '**.mod'
workflow_dispatch:

jobs:
Expand Down
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# IDE Settings
/.idea
/.vscode
/.vs

examples/basic/basic
examples/basic/basic.exe

.devcontainer
124 changes: 124 additions & 0 deletions dynamic_var.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package goflags

import (
"errors"
"fmt"
"reflect"
"strconv"
"strings"
)

type dynamicFlag struct {
field interface{}
defaultValue interface{}
name string
}

func (df *dynamicFlag) Set(value string) error {
fieldKind := reflect.TypeOf(df.field).Elem().Kind()
var isBoolValue bool
if _, err := strconv.ParseBool(value); err == nil {
isBoolValue = true
}
if fieldKind == reflect.Bool && isBoolValue {
boolField := df.field.(*bool)
*boolField = true
return nil
}
switch fieldKind {
case reflect.Int:
intField := df.field.(*int)
if isBoolValue {
*intField = df.defaultValue.(int)
return nil
}
newValue, err := strconv.Atoi(value)
if err != nil {
return err
}
*intField = newValue
case reflect.Float64:
floatField := df.field.(*float64)
if isBoolValue {
*floatField = df.defaultValue.(float64)
return nil
}
newValue, err := strconv.ParseFloat(value, 64)
if err != nil {
return err
}
*floatField = newValue
case reflect.String:
stringField := df.field.(*string)
if isBoolValue {
*stringField = df.defaultValue.(string)
return nil
}
*stringField = value
case reflect.Slice:
sliceField := df.field.(*[]string)
if isBoolValue {
*sliceField = df.defaultValue.([]string)
return nil
}
*sliceField = append(*sliceField, strings.Split(value, ",")...)
default:
return errors.New("unsupported type")
}
return nil
}

func (df *dynamicFlag) IsBoolFlag() bool {
return true
}

func (df *dynamicFlag) String() string {
return df.name
}

// DynamicVar acts as flag with a default value or a option with value
// example:
//
// var titleSize int
// flagSet.DynamicVar(&titleSize, "title", 50, "first N characters of the title")
//
// > go run ./examples/basic -title or go run ./examples/basic -title=100
// In case of `go run ./examples/basic -title` it will use default value 50
func (flagSet *FlagSet) DynamicVar(field interface{}, long string, defaultValue interface{}, usage string) *FlagData {
return flagSet.DynamicVarP(field, long, "", defaultValue, usage)
}

// DynamicVarP same as DynamicVar but with short name
func (flagSet *FlagSet) DynamicVarP(field interface{}, long, short string, defaultValue interface{}, usage string) *FlagData {
// validate field and defaultValue
if reflect.TypeOf(field).Kind() != reflect.Ptr {
panic(fmt.Errorf("-%v flag field must be a pointer", long))
}
if reflect.TypeOf(field).Elem().Kind() != reflect.TypeOf(defaultValue).Kind() {
panic(fmt.Errorf("-%v flag field and defaultValue mismatch: fied type is %v and defaultValue Type is %T", long, reflect.TypeOf(field).Elem().Kind(), defaultValue))
}
if field == nil {
panic(fmt.Errorf("field cannot be nil for flag -%v", long))
}

var dynamicFlag dynamicFlag
dynamicFlag.field = field
dynamicFlag.name = long
if defaultValue != nil {
dynamicFlag.defaultValue = defaultValue
}

flagData := &FlagData{
usage: usage,
long: long,
defaultValue: defaultValue,
}
if short != "" {
flagData.short = short
flagSet.CommandLine.Var(&dynamicFlag, short, usage)
flagSet.flagKeys.Set(short, flagData)
}
flagSet.CommandLine.Var(&dynamicFlag, long, usage)
flagSet.flagKeys.Set(long, flagData)
return flagData
}
30 changes: 30 additions & 0 deletions enum_slice_var.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package goflags

import (
"fmt"
"strings"
)

type EnumSliceVar struct {
allowedTypes AllowdTypes
value *[]string
}

func (e *EnumSliceVar) String() string {
if e.value != nil {
return strings.Join(*e.value, ",")
}
return ""
}

func (e *EnumSliceVar) Set(value string) error {
values := strings.Split(value, ",")
for _, v := range values {
_, ok := e.allowedTypes[v]
if !ok {
return fmt.Errorf("allowed values are %v", e.allowedTypes.String())
}
}
*e.value = values
return nil
}
61 changes: 61 additions & 0 deletions enum_slice_var_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package goflags

import (
"os"
"os/exec"
"testing"

"github.com/stretchr/testify/assert"
)

var enumSliceData []string

func TestEnumSliceVar(t *testing.T) {
t.Run("Test with single value", func(t *testing.T) {
flagSet := NewFlagSet()
flagSet.EnumSliceVar(&enumSliceData, "enum", []EnumVariable{Type1}, "enum", AllowdTypes{"type1": Type1, "type2": Type2})
os.Args = []string{
os.Args[0],
"--enum", "type1",
}
err := flagSet.Parse()
assert.Nil(t, err)
assert.Equal(t, []string{"type1"}, enumSliceData)
tearDown(t.Name())
})

t.Run("Test with multiple value", func(t *testing.T) {
flagSet := NewFlagSet()
flagSet.EnumSliceVar(&enumSliceData, "enum", []EnumVariable{Type1}, "enum", AllowdTypes{"type1": Type1, "type2": Type2})
os.Args = []string{
os.Args[0],
"--enum", "type1,type2",
}
err := flagSet.Parse()
assert.Nil(t, err)
assert.Equal(t, []string{"type1", "type2"}, enumSliceData)
tearDown(t.Name())
})

t.Run("Test with invalid value", func(t *testing.T) {
if os.Getenv("IS_SUB_PROCESS") == "1" {
flagSet := NewFlagSet()

flagSet.EnumSliceVar(&enumSliceData, "enum", []EnumVariable{Nil}, "enum", AllowdTypes{"type1": Type1, "type2": Type2})
os.Args = []string{
os.Args[0],
"--enum", "type3",
}
_ = flagSet.Parse()
return
}
cmd := exec.Command(os.Args[0], "-test.run=TestFailEnumVar")
cmd.Env = append(os.Environ(), "IS_SUB_PROCESS=1")
err := cmd.Run()
if e, ok := err.(*exec.ExitError); ok && !e.Success() {
return
}
t.Fatalf("process ran with err %v, want exit error", err)
tearDown(t.Name())
})
}
Loading

0 comments on commit 4af426c

Please sign in to comment.