Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Implement *os.File handling
  • Loading branch information
surma committed Oct 5, 2012
1 parent 1431bd3 commit 29a57e6
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 25 deletions.
1 change: 1 addition & 0 deletions flag.go
Expand Up @@ -15,6 +15,7 @@ type Flag struct {
Obligatory bool
WasSpecified bool
value reflect.Value
optionMeta map[string]interface{}
DefaultValue interface{}
}

Expand Down
73 changes: 73 additions & 0 deletions options.go
Expand Up @@ -2,7 +2,9 @@ package goptions

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

Expand All @@ -17,9 +19,31 @@ var (
"obligatory": obligatory,
"mutexgroup": mutexgroup,
},
reflect.TypeOf(new(*os.File)).Elem(): optionMap{
"create": initOptionMeta(file_create, "file_mode", 0),
"append": initOptionMeta(file_append, "file_mode", 0),
"rdonly": initOptionMeta(file_rdonly, "file_mode", 0),
"wronly": initOptionMeta(file_wronly, "file_mode", 0),
"rdwr": initOptionMeta(file_rdwr, "file_mode", 0),
"excl": initOptionMeta(file_excl, "file_mode", 0),
"sync": initOptionMeta(file_sync, "file_mode", 0),
"trunc": initOptionMeta(file_trunc, "file_mode", 0),
"perm": file_perm,
},
}
)

// Wraps another optionFunc and inits optionMeta[field] with value if it does
// not have one already.
func initOptionMeta(fn optionFunc, field string, init_value interface{}) optionFunc {
return func(f *Flag, option, value string) error {
if _, ok := f.optionMeta[field]; !ok {
f.optionMeta[field] = init_value
}
return fn(f, option, value)
}
}

func description(f *Flag, option, value string) error {
f.Description = strings.Replace(value, `\`, ``, -1)
return nil
Expand All @@ -40,6 +64,55 @@ func mutexgroup(f *Flag, option, value string) error {
return nil
}

func file_create(f *Flag, option, value string) error {
f.optionMeta["file_mode"] = f.optionMeta["file_mode"].(int) | os.O_CREATE
return nil
}

func file_append(f *Flag, option, value string) error {
f.optionMeta["file_mode"] = f.optionMeta["file_mode"].(int) | os.O_APPEND
return nil
}

func file_rdonly(f *Flag, option, value string) error {
f.optionMeta["file_mode"] = f.optionMeta["file_mode"].(int) | os.O_RDONLY
return nil
}

func file_wronly(f *Flag, option, value string) error {
f.optionMeta["file_mode"] = f.optionMeta["file_mode"].(int) | os.O_WRONLY
return nil
}

func file_rdwr(f *Flag, option, value string) error {
f.optionMeta["file_mode"] = f.optionMeta["file_mode"].(int) | os.O_RDWR
return nil
}

func file_excl(f *Flag, option, value string) error {
f.optionMeta["file_mode"] = f.optionMeta["file_mode"].(int) | os.O_EXCL
return nil
}

func file_sync(f *Flag, option, value string) error {
f.optionMeta["file_mode"] = f.optionMeta["file_mode"].(int) | os.O_SYNC
return nil
}

func file_trunc(f *Flag, option, value string) error {
f.optionMeta["file_mode"] = f.optionMeta["file_mode"].(int) | os.O_TRUNC
return nil
}

func file_perm(f *Flag, option, value string) error {
perm, err := strconv.ParseInt(value, 8, 32)
if err != nil {
return err
}
f.optionMeta["file_perm"] = uint32(perm)
return nil
}

func optionMapForType(t reflect.Type) optionMap {
g := typeOptionMap[nil]
m, _ := typeOptionMap[t]
Expand Down
21 changes: 21 additions & 0 deletions parse_test.go
@@ -1,6 +1,7 @@
package goptions

import (
"os"
"testing"
)

Expand Down Expand Up @@ -333,3 +334,23 @@ func TestParse_Array(t *testing.T) {
t.Fatalf("Unexpected value: %#v", options)
}
}

func TestParse_File(t *testing.T) {
var args []string
var err error
var fs *FlagSet
var options struct {
Output *os.File `goptions:"-o, create, trunc, wronly"`
}

args = []string{"-o", "testfile"}
fs = NewFlagSet("goptions", &options)
err = fs.Parse(args)
if err != nil {
t.Fatalf("Parsing failed: %s", err)
}
if !(options.Output != nil) {
t.Fatalf("Unexpected value: %#v", options)
}
options.Output.Close()
}
35 changes: 20 additions & 15 deletions parsetag_test.go
Expand Up @@ -13,13 +13,11 @@ func TestParseTag_Minimal(t *testing.T) {
t.Fatalf("Tag parsing failed: %s", e)
}
expected := &Flag{
Long: "name",
Short: "n",
Description: "Some name",
value: f.value,
DefaultValue: f.DefaultValue,
Long: "name",
Short: "n",
Description: "Some name",
}
if !reflect.DeepEqual(f, expected) {
if !flagequal(f, expected) {
t.Fatalf("Expected %#v, got %#v", expected, f)
}
}
Expand All @@ -32,15 +30,13 @@ func TestParseTag_More(t *testing.T) {
t.Fatalf("Tag parsing failed: %s", e)
}
expected := &Flag{
Long: "name",
Short: "n",
Description: "Some name",
MutexGroups: []string{"selector"},
Obligatory: true,
value: f.value,
DefaultValue: f.DefaultValue,
}
if !reflect.DeepEqual(f, expected) {
Long: "name",
Short: "n",
Description: "Some name",
MutexGroups: []string{"selector"},
Obligatory: true,
}
if !flagequal(f, expected) {
t.Fatalf("Expected %#v, got %#v", expected, f)
}
}
Expand All @@ -60,3 +56,12 @@ func TestParseTag_MultipleFlags(t *testing.T) {
t.Fatalf("Parsing should have failed")
}
}

func flagequal(f1, f2 *Flag) bool {
return f1.Short == f2.Short &&
f1.Long == f2.Long &&
reflect.DeepEqual(f1.MutexGroups, f2.MutexGroups) &&
f1.Description == f2.Description &&
f1.Obligatory == f2.Obligatory &&
f1.WasSpecified == f2.WasSpecified
}
1 change: 1 addition & 0 deletions tagparser.go
Expand Up @@ -22,6 +22,7 @@ func parseStructField(fieldValue reflect.Value, tag string) (*Flag, error) {
f := &Flag{
value: fieldValue,
DefaultValue: fieldValue.Interface(),
optionMeta: make(map[string]interface{}),
}
for {
tag = strings.TrimSpace(tag)
Expand Down
44 changes: 34 additions & 10 deletions valueparser.go
Expand Up @@ -2,18 +2,20 @@ package goptions

import (
"fmt"
"os"
"reflect"
"strconv"
)

type valueParser func(val string) (reflect.Value, error)
type valueParser func(f *Flag, val string) (reflect.Value, error)

var (
parserMap = map[reflect.Type]valueParser{
reflect.TypeOf(new(bool)).Elem(): boolValueParser,
reflect.TypeOf(new(string)).Elem(): stringValueParser,
reflect.TypeOf(new(int)).Elem(): intValueParser,
reflect.TypeOf(new(Help)).Elem(): helpValueParser,
reflect.TypeOf(new(bool)).Elem(): boolValueParser,
reflect.TypeOf(new(string)).Elem(): stringValueParser,
reflect.TypeOf(new(int)).Elem(): intValueParser,
reflect.TypeOf(new(Help)).Elem(): helpValueParser,
reflect.TypeOf(new(*os.File)).Elem(): fileValueParser,
}
)

Expand All @@ -39,7 +41,7 @@ func (f *Flag) setValue(s string) (err error) {
vtype = f.value.Type().Elem()
}
if parser, ok := parserMap[vtype]; ok {
val, err := parser(s)
val, err := parser(f, s)
if err != nil {
return err
}
Expand All @@ -55,19 +57,41 @@ func (f *Flag) setValue(s string) (err error) {
panic("Invalid execution path")
}

func boolValueParser(val string) (reflect.Value, error) {
func boolValueParser(f *Flag, val string) (reflect.Value, error) {
return reflect.ValueOf(true), nil
}

func stringValueParser(val string) (reflect.Value, error) {
func stringValueParser(f *Flag, val string) (reflect.Value, error) {
return reflect.ValueOf(val), nil
}

func intValueParser(val string) (reflect.Value, error) {
func intValueParser(f *Flag, val string) (reflect.Value, error) {
intval, err := strconv.ParseInt(val, 10, 64)
return reflect.ValueOf(int(intval)), err
}

func helpValueParser(val string) (reflect.Value, error) {
func fileValueParser(f *Flag, val string) (reflect.Value, error) {
mode := 0
if v, ok := f.optionMeta["file_mode"].(int); ok {
mode = v
}
if val == "-" {
if mode&os.O_RDONLY > 0 {
return reflect.ValueOf(os.Stdin), nil
} else if mode&os.O_WRONLY > 0 {
return reflect.ValueOf(os.Stdout), nil
}
} else {
perm := uint32(0644)
if v, ok := f.optionMeta["file_perm"].(uint32); ok {
perm = v
}
f, e := os.OpenFile(val, mode, os.FileMode(perm))
return reflect.ValueOf(f), e
}
panic("Invalid execution path")
}

func helpValueParser(f *Flag, val string) (reflect.Value, error) {
return reflect.Value{}, ErrHelpRequest
}

0 comments on commit 29a57e6

Please sign in to comment.