neoneo-go/pkg/smartcontract/callflag/call_flags.go
Evgeniy Stratonikov 4aab85ef51 callflag: allow to marshal call flags in YAML
Signed-off-by: Evgeniy Stratonikov <evgeniy@nspcc.ru>
2022-02-21 10:43:35 +03:00

137 lines
2.9 KiB
Go

package callflag
import (
"encoding/json"
"errors"
"strings"
)
// CallFlag represents call flag.
type CallFlag byte
// Default flags.
const (
ReadStates CallFlag = 1 << iota
WriteStates
AllowCall
AllowNotify
States = ReadStates | WriteStates
ReadOnly = ReadStates | AllowCall
All = States | AllowCall | AllowNotify
NoneFlag CallFlag = 0
)
var flagString = map[CallFlag]string{
ReadStates: "ReadStates",
WriteStates: "WriteStates",
AllowCall: "AllowCall",
AllowNotify: "AllowNotify",
States: "States",
ReadOnly: "ReadOnly",
All: "All",
NoneFlag: "None",
}
// basicFlags are all flags except All and None. It's used to stringify CallFlag
// where its bits are matched against these values from values with sets of bits
// to simple flags which is important to produce proper string representation
// matching C# Enum handling.
var basicFlags = []CallFlag{ReadOnly, States, ReadStates, WriteStates, AllowCall, AllowNotify}
// FromString parses input string and returns corresponding CallFlag.
func FromString(s string) (CallFlag, error) {
flags := strings.Split(s, ",")
if len(flags) == 0 {
return NoneFlag, errors.New("empty flags")
}
if len(flags) == 1 {
for f, str := range flagString {
if s == str {
return f, nil
}
}
return NoneFlag, errors.New("unknown flag")
}
var res CallFlag
for _, flag := range flags {
var knownFlag bool
flag = strings.TrimSpace(flag)
for _, f := range basicFlags {
if flag == flagString[f] {
res |= f
knownFlag = true
break
}
}
if !knownFlag {
return NoneFlag, errors.New("unknown/inappropriate flag")
}
}
return res, nil
}
// Has returns true iff all bits set in cf are also set in f.
func (f CallFlag) Has(cf CallFlag) bool {
return f&cf == cf
}
// String implements Stringer interface.
func (f CallFlag) String() string {
if flagString[f] != "" {
return flagString[f]
}
var res string
for _, flag := range basicFlags {
if f.Has(flag) {
if len(res) != 0 {
res += ", "
}
res += flagString[flag]
f &= ^flag // Some "States" shouldn't be combined with "ReadStates".
}
}
return res
}
// MarshalJSON implements json.Marshaler interface.
func (f CallFlag) MarshalJSON() ([]byte, error) {
return []byte(`"` + f.String() + `"`), nil
}
// UnmarshalJSON implements json.Unmarshaler interface.
func (f *CallFlag) UnmarshalJSON(data []byte) error {
var js string
if err := json.Unmarshal(data, &js); err != nil {
return err
}
flag, err := FromString(js)
if err != nil {
return err
}
*f = flag
return nil
}
// MarshalYAML implements the YAML marshaler interface.
func (f CallFlag) MarshalYAML() (interface{}, error) {
return f.String(), nil
}
// UnmarshalYAML implements the YAML unmarshaler interface.
func (f *CallFlag) UnmarshalYAML(unmarshal func(interface{}) error) error {
var s string
err := unmarshal(&s)
if err != nil {
return err
}
*f, err = FromString(s)
return err
}