github.com/filecoin-project/bacalhau@v0.3.23-0.20230228154132-45c989550ace/cmd/bacalhau/flags.go (about) 1 package bacalhau 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/filecoin-project/bacalhau/pkg/job" 8 "github.com/filecoin-project/bacalhau/pkg/logger" 9 "github.com/filecoin-project/bacalhau/pkg/model" 10 "github.com/filecoin-project/bacalhau/pkg/storage/url/urldownload" 11 "github.com/spf13/pflag" 12 ) 13 14 // A Parser is a function that can convert a string into a native object. 15 type Parser[T any] func(string) (T, error) 16 17 // A KeyValueParser is like a Parser except that it returns two values 18 // representing a key and a value. 19 type KeyValueParser[K comparable, V any] func(string) (K, V, error) 20 21 // A Stringer is a function that can convert a native object into a string. 22 type Stringer[T any] func(*T) string 23 24 // A KeyValueStringer is like a Stringer except that it converts native objects 25 // representing a key and a value into a string. 26 type KeyValueStringer[K comparable, V any] func(*K, *V) string 27 28 // A ValueFlag is a pflag.Value that knows how to take a command line value 29 // represented as a string and set it as a native object into a struct. 30 type ValueFlag[T any] struct { 31 // A pointer to a variable that will be set by this flag. 32 // This will be a pointer to some struct value we want to set. 33 value *T 34 35 // A Parser to turn the command line string into a native value. 36 parser Parser[T] 37 38 // A Stringer to turn the default value for the flag back into a native 39 // string, to be printed as help. 40 stringer Stringer[T] 41 42 // How the value should be described in the help string. (e.g. string, int) 43 typeStr string 44 } 45 46 // Set implements pflag.Value 47 func (s *ValueFlag[T]) Set(input string) error { 48 value, err := s.parser(input) 49 *s.value = value 50 return err 51 } 52 53 // String implements pflag.Value 54 func (s *ValueFlag[T]) String() string { 55 return s.stringer(s.value) 56 } 57 58 // Type implements pflag.Value 59 func (s *ValueFlag[T]) Type() string { 60 return s.typeStr 61 } 62 63 var _ pflag.Value = (*ValueFlag[int])(nil) 64 65 // An ArrayValueFlag is like a ValueFlag except it will add the command line 66 // value into a slice of values, and hence can be used for flags that are meant 67 // to appear multiple times. 68 type ArrayValueFlag[T any] struct { 69 // A pointer to a variable that will be set by this flag. 70 // This will be a pointer to some struct value we want to set. 71 value *[]T 72 73 // A Parser to turn the command line string into a native value. 74 parser Parser[T] 75 76 // A Stringer to turn the default value for the flag back into a native 77 // string, to be printed as help. 78 stringer Stringer[T] 79 80 // How the value should be described in the help string. (e.g. string, int) 81 typeStr string 82 } 83 84 // Set implements pflag.Value 85 func (s *ArrayValueFlag[T]) Set(input string) error { 86 value, err := s.parser(input) 87 *s.value = append(*s.value, value) 88 return err 89 } 90 91 // String implements pflag.Value 92 func (s *ArrayValueFlag[T]) String() string { 93 strs := make([]string, 0, len(*s.value)) 94 for _, spec := range *s.value { 95 spec := spec 96 strs = append(strs, s.stringer(&spec)) 97 } 98 return strings.Join(strs, ", ") 99 } 100 101 // Type implements pflag.Value 102 func (s *ArrayValueFlag[T]) Type() string { 103 return s.typeStr 104 } 105 106 var _ pflag.Value = (*ArrayValueFlag[int])(nil) 107 108 // A MapValueFlag is like a ValueFlag except it will add the command line 109 // value into a map of values, and hence can be used for flags that are meant 110 // to appear multiple times and represent a key-value structure. 111 type MapValueFlag[K comparable, V any] struct { 112 // A pointer to a variable that will be set by this flag. 113 // This will be a pointer to some struct value we want to set. 114 value *map[K]V 115 116 // A Parser to turn the command line string into a native value. 117 parser KeyValueParser[K, V] 118 119 // A Stringer to turn the default value for the flag back into a native 120 // string, to be printed as help. 121 stringer KeyValueStringer[K, V] 122 123 // How the value should be described in the help string. (e.g. string, int) 124 typeStr string 125 } 126 127 // Set implements pflag.Value 128 func (s *MapValueFlag[K, V]) Set(input string) error { 129 key, value, err := s.parser(input) 130 (*s.value)[key] = value 131 return err 132 } 133 134 // String implements pflag.Value 135 func (s *MapValueFlag[K, V]) String() string { 136 strs := make([]string, len(*s.value)) 137 for key, value := range *s.value { 138 key, value := key, value 139 strs = append(strs, s.stringer(&key, &value)) 140 } 141 return strings.Join(strs, ", ") 142 } 143 144 // Type implements pflag.Value 145 func (s *MapValueFlag[K, V]) Type() string { 146 return s.typeStr 147 } 148 149 var _ pflag.Value = (*MapValueFlag[int, int])(nil) 150 151 func separatorParser(sep string) KeyValueParser[string, string] { 152 return func(input string) (string, string, error) { 153 slices := strings.Split(input, sep) 154 if len(slices) != 2 { 155 return "", "", fmt.Errorf("%q should contain exactly one %s", input, sep) 156 } 157 return slices[0], slices[1], nil 158 } 159 } 160 161 func parseIPFSStorageSpec(input string) (model.StorageSpec, error) { 162 cid, path, err := separatorParser(":")(input) 163 return model.StorageSpec{ 164 StorageSource: model.StorageSourceIPFS, 165 CID: cid, 166 Path: path, 167 }, err 168 } 169 170 func storageSpecToIPFSMount(input *model.StorageSpec) string { 171 return fmt.Sprintf("%s:%s", input.CID, input.Path) 172 } 173 174 func NewIPFSStorageSpecArrayFlag(value *[]model.StorageSpec) *ArrayValueFlag[model.StorageSpec] { 175 return &ArrayValueFlag[model.StorageSpec]{ 176 value: value, 177 parser: parseIPFSStorageSpec, 178 stringer: storageSpecToIPFSMount, 179 typeStr: "cid:path", 180 } 181 } 182 183 func parseURLStorageSpec(inputURL string) (model.StorageSpec, error) { 184 u, err := urldownload.IsURLSupported(inputURL) 185 if err != nil { 186 return model.StorageSpec{}, err 187 } 188 return model.StorageSpec{ 189 StorageSource: model.StorageSourceURLDownload, 190 URL: u.String(), 191 Path: "/inputs", 192 }, nil 193 } 194 195 func NewURLStorageSpecArrayFlag(value *[]model.StorageSpec) *ArrayValueFlag[model.StorageSpec] { 196 return &ArrayValueFlag[model.StorageSpec]{ 197 value: value, 198 parser: parseURLStorageSpec, 199 stringer: func(s *model.StorageSpec) string { return s.URL }, 200 typeStr: "url", 201 } 202 } 203 204 func VerifierFlag(value *model.Verifier) *ValueFlag[model.Verifier] { 205 return &ValueFlag[model.Verifier]{ 206 value: value, 207 parser: model.ParseVerifier, 208 stringer: func(v *model.Verifier) string { return v.String() }, 209 typeStr: "verifier", 210 } 211 } 212 213 func PublisherFlag(value *model.Publisher) *ValueFlag[model.Publisher] { 214 return &ValueFlag[model.Publisher]{ 215 value: value, 216 parser: model.ParsePublisher, 217 stringer: func(p *model.Publisher) string { return p.String() }, 218 typeStr: "publisher", 219 } 220 } 221 222 func NetworkFlag(value *model.Network) *ValueFlag[model.Network] { 223 return &ValueFlag[model.Network]{ 224 value: value, 225 parser: model.ParseNetwork, 226 stringer: func(n *model.Network) string { return n.String() }, 227 typeStr: "network-type", 228 } 229 } 230 231 func LoggingFlag(value *logger.LogMode) *ValueFlag[logger.LogMode] { 232 return &ValueFlag[logger.LogMode]{ 233 value: value, 234 parser: logger.ParseLogMode, 235 stringer: func(p *logger.LogMode) string { return string(*p) }, 236 typeStr: "logging-mode", 237 } 238 } 239 240 func EnvVarMapFlag(value *map[string]string) *MapValueFlag[string, string] { 241 return &MapValueFlag[string, string]{ 242 value: value, 243 parser: separatorParser("="), 244 stringer: func(k *string, v *string) string { return fmt.Sprintf("%s=%s", *k, *v) }, 245 typeStr: "key=value", 246 } 247 } 248 249 func parseTag(s string) (string, error) { 250 var err error 251 if !job.IsSafeAnnotation(s) { 252 err = fmt.Errorf("%q is not a valid tag", s) 253 } 254 return s, err 255 } 256 257 func IncludedTagFlag(value *[]model.IncludedTag) *ArrayValueFlag[model.IncludedTag] { 258 return &ArrayValueFlag[model.IncludedTag]{ 259 value: value, 260 parser: func(s string) (model.IncludedTag, error) { 261 s, err := parseTag(s) 262 return model.IncludedTag(s), err 263 }, 264 stringer: func(t *model.IncludedTag) string { return string(*t) }, 265 typeStr: "tag", 266 } 267 } 268 269 func ExcludedTagFlag(value *[]model.ExcludedTag) *ArrayValueFlag[model.ExcludedTag] { 270 return &ArrayValueFlag[model.ExcludedTag]{ 271 value: value, 272 parser: func(s string) (model.ExcludedTag, error) { 273 s, err := parseTag(s) 274 return model.ExcludedTag(s), err 275 }, 276 stringer: func(t *model.ExcludedTag) string { return string(*t) }, 277 typeStr: "tag", 278 } 279 }