make interface to the flags themselves more public.
add visitor functions to scan the flags.
add a way to set a flag.
add a flag test.
R=rsc
DELTA=169 (99 added, 19 deleted, 51 changed)
OCL=25076
CL=25078
diff --git a/src/lib/flag.go b/src/lib/flag.go
index 35e18f9..541966d 100644
--- a/src/lib/flag.go
+++ b/src/lib/flag.go
@@ -115,11 +115,13 @@
return &boolValue(p)
}
-func (b *boolValue) set(val bool) {
- *b.p = val;
+func (b *boolValue) set(s string) bool {
+ v, ok := atob(s);
+ *b.p = v;
+ return ok
}
-func (b *boolValue) str() string {
+func (b *boolValue) String() string {
return fmt.Sprintf("%v", *b.p)
}
@@ -133,11 +135,13 @@
return &intValue(p)
}
-func (i *intValue) set(val int) {
- *i.p = val;
+func (i *intValue) set(s string) bool {
+ v, ok := atoi(s);
+ *i.p = int(v);
+ return ok
}
-func (i *intValue) str() string {
+func (i *intValue) String() string {
return fmt.Sprintf("%v", *i.p)
}
@@ -151,11 +155,13 @@
return &int64Value(p)
}
-func (i *int64Value) set(val int64) {
- *i.p = val;
+func (i *int64Value) set(s string) bool {
+ v, ok := atoi(s);
+ *i.p = v;
+ return ok;
}
-func (i *int64Value) str() string {
+func (i *int64Value) String() string {
return fmt.Sprintf("%v", *i.p)
}
@@ -169,11 +175,13 @@
return &uintValue(p)
}
-func (i *uintValue) set(val uint) {
- *i.p = val
+func (i *uintValue) set(s string) bool {
+ v, ok := atoi(s); // TODO(r): want unsigned
+ *i.p = uint(v);
+ return ok;
}
-func (i *uintValue) str() string {
+func (i *uintValue) String() string {
return fmt.Sprintf("%v", *i.p)
}
@@ -187,11 +195,13 @@
return &uint64Value(p)
}
-func (i *uint64Value) set(val uint64) {
- *i.p = val;
+func (i *uint64Value) set(s string) bool {
+ v, ok := atoi(s); // TODO(r): want unsigned
+ *i.p = uint64(v);
+ return ok;
}
-func (i *uint64Value) str() string {
+func (i *uint64Value) String() string {
return fmt.Sprintf("%v", *i.p)
}
@@ -205,25 +215,27 @@
return &stringValue(p)
}
-func (s *stringValue) set(val string) {
+func (s *stringValue) set(val string) bool {
*s.p = val;
+ return true;
}
-func (s *stringValue) str() string {
+func (s *stringValue) String() string {
return fmt.Sprintf("%#q", *s.p)
}
-// -- Value interface
-type _Value interface {
- str() string;
+// -- FlagValue interface
+type FlagValue interface {
+ String() string;
+ set(string) bool;
}
-// -- Flag structure (internal)
+// -- Flag structure
type Flag struct {
- name string; // name as it appears on command line
- usage string; // help message
- value _Value; // value as set
- defvalue string; // default value (as text); for usage message
+ Name string; // name as it appears on command line
+ Usage string; // help message
+ Value FlagValue; // value as set
+ DefValue string; // default value (as text); for usage message
}
type allFlags struct {
@@ -234,12 +246,47 @@
var flags *allFlags = &allFlags(make(map[string] *Flag), make(map[string] *Flag), 1)
-func PrintDefaults() {
+// Visit all flags, including those defined but not set.
+func VisitAll(fn func(*Flag)) {
for k, f := range flags.formal {
- print(" -", f.name, "=", f.defvalue, ": ", f.usage, "\n");
+ fn(f)
}
}
+// Visit only those flags that have been set
+func Visit(fn func(*Flag)) {
+ for k, f := range flags.actual {
+ fn(f)
+ }
+}
+
+func Lookup(name string) *Flag {
+ f, ok := flags.formal[name];
+ if !ok {
+ return nil
+ }
+ return f
+}
+
+func Set(name, value string) bool {
+ f, ok := flags.formal[name];
+ if !ok {
+ return false
+ }
+ ok = f.Value.set(value);
+ if !ok {
+ return false
+ }
+ flags.actual[name] = f;
+ return true;
+}
+
+func PrintDefaults() {
+ VisitAll(func(f *Flag) {
+ print(" -", f.Name, "=", f.DefValue, ": ", f.Usage, "\n");
+ })
+}
+
func Usage() {
if len(sys.Args) > 0 {
print("Usage of ", sys.Args[0], ": \n");
@@ -266,12 +313,9 @@
return len(sys.Args) - flags.first_arg
}
-func add(name string, value _Value, usage string) {
- f := new(Flag);
- f.name = name;
- f.usage = usage;
- f.value = value;
- f.defvalue = value.str(); // Remember the default value as a string; it won't change.
+func add(name string, value FlagValue, usage string) {
+ // Remember the default value as a string; it won't change.
+ f := &Flag(name, usage, value, value.String());
dummy, alreadythere := flags.formal[name];
if alreadythere {
print("flag redefined: ", name, "\n");
@@ -388,16 +432,14 @@
print("flag provided but not defined: -", name, "\n");
Usage();
}
- if f, ok := flag.value.(*boolValue); ok {
+ if f, ok := flag.Value.(*boolValue); ok { // special case: doesn't need an arg
if has_value {
- k, ok := atob(value);
- if !ok {
+ if !f.set(value) {
print("invalid boolean value ", value, " for flag: -", name, "\n");
Usage();
}
- f.set(k)
} else {
- f.set(true)
+ f.set("true")
}
} else {
// It must have a value, which might be the next argument.
@@ -411,24 +453,10 @@
print("flag needs an argument: -", name, "\n");
Usage();
}
- if f, ok := flag.value.(*stringValue); ok {
- f.set(value)
- } else {
- // It's an integer flag. TODO(r): check for overflow?
- k, ok := atoi(value);
- if !ok {
- print("invalid integer value ", value, " for flag: -", name, "\n");
+ ok = flag.Value.set(value);
+ if !ok {
+ print("invalid value ", value, " for flag: -", name, "\n");
Usage();
- }
- if f, ok := flag.value.(*intValue); ok {
- f.set(int(k));
- } else if f, ok := flag.value.(*int64Value); ok {
- f.set(k);
- } else if f, ok := flag.value.(*uintValue); ok {
- f.set(uint(k));
- } else if f, ok := flag.value.(*uint64Value); ok {
- f.set(uint64(k));
- }
}
}
flags.actual[name] = flag;
diff --git a/src/lib/flag_test.go b/src/lib/flag_test.go
new file mode 100644
index 0000000..1212cf8
--- /dev/null
+++ b/src/lib/flag_test.go
@@ -0,0 +1,56 @@
+// Copyright 2009 The Go 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 flag
+
+import (
+ "flag";
+ "fmt";
+ "testing";
+)
+
+var (
+ test_bool = flag.Bool("test_bool", true, "bool value");
+ test_int = flag.Int("test_int", 1, "int value");
+ test_int64 = flag.Int64("test_int64", 1, "int64 value");
+ test_uint = flag.Uint("test_uint", 1, "uint value");
+ test_uint64 = flag.Uint64("test_uint64", 1, "uint64 value");
+ test_string = flag.String("test_string", "1", "string value");
+)
+
+// Because this calls flag.Parse, it needs to be the only Test* function
+func TestEverything(t *testing.T) {
+ flag.Parse();
+ m := make(map[string] *flag.Flag);
+ visitor := func(f *flag.Flag) {
+ if len(f.Name) > 5 && f.Name[0:5] == "test_" {
+ m[f.Name] = f
+ }
+ };
+ flag.VisitAll(visitor);
+ if len(m) != 6 {
+ t.Error("flag.VisitAll misses some flags");
+ for k, v := range m {
+ t.Log(k, *v)
+ }
+ }
+ m = make(map[string] *flag.Flag);
+ flag.Visit(visitor);
+ if len(m) != 0 {
+ t.Errorf("flag.Visit sees unset flags");
+ for k, v := range m {
+ t.Log(k, *v)
+ }
+ }
+ // Now set some flags
+ flag.Set("test_bool", "false");
+ flag.Set("test_uint", "1234");
+ flag.Visit(visitor);
+ if len(m) != 2 {
+ t.Error("flag.Visit fails after set");
+ for k, v := range m {
+ t.Log(k, *v)
+ }
+ }
+}