diff options
| author | Aleksandr Nogikh <nogikh@google.com> | 2021-08-24 15:50:19 +0000 |
|---|---|---|
| committer | Aleksandr Nogikh <wp32pw@gmail.com> | 2021-09-22 15:40:02 +0200 |
| commit | a7ce77be27d8e3728b97122a005bc5b23298cfc3 (patch) | |
| tree | 1f3adbdd7719ec9c6bf7eb4d48f308004410f775 /prog/encoding.go | |
| parent | 041a868956e51efc0dace9e3dff874332a8cdccc (diff) | |
all: introduce call properties
Call properties let us specify how each individual call within a program
must be executed. So far the only way to enforce extra rules was to pass
extra program-level properties (e.g. that is how fault injection was done).
However, it entangles the logic and not flexible enough.
Implement an ability to pass properties along with each individual call.
Diffstat (limited to 'prog/encoding.go')
| -rw-r--r-- | prog/encoding.go | 77 |
1 files changed, 75 insertions, 2 deletions
diff --git a/prog/encoding.go b/prog/encoding.go index de0e324b1..3f1c918a7 100644 --- a/prog/encoding.go +++ b/prog/encoding.go @@ -7,6 +7,7 @@ import ( "bytes" "encoding/hex" "fmt" + "reflect" "strconv" "strings" ) @@ -78,7 +79,36 @@ func (ctx *serializer) call(c *Call) { } ctx.arg(a) } - ctx.printf(")\n") + ctx.printf(")") + + defaultProps := DefaultCallProps() + anyChangedProps := false + c.Props.ForeachProp(func(name, key string, value reflect.Value) { + defaultValue := reflect.ValueOf(defaultProps).FieldByName(name) + if reflect.DeepEqual(value.Interface(), defaultValue.Interface()) { + return + } + + if !anyChangedProps { + ctx.printf(" (") + anyChangedProps = true + } else { + ctx.printf(", ") + } + + ctx.printf(key) + switch kind := value.Kind(); kind { + case reflect.Int: + ctx.printf(": %d", value.Int()) + default: + panic("unable to serialize call prop of type " + kind.String()) + } + }) + if anyChangedProps { + ctx.printf(")") + } + + ctx.printf("\n") } func (ctx *serializer) arg(arg Arg) { @@ -286,7 +316,13 @@ func (p *parser) parseProg() (*Prog, error) { } } p.Parse(')') - p.SkipWs() + + if !p.EOF() && p.Char() == '(' { + p.Parse('(') + c.Props = p.parseCallProps() + p.Parse(')') + } + if !p.EOF() { if p.Char() != '#' { return nil, fmt.Errorf("tailing data (line #%v)", p.l) @@ -314,6 +350,43 @@ func (p *parser) parseProg() (*Prog, error) { return prog, nil } +func (p *parser) parseCallProps() CallProps { + nameToValue := map[string]reflect.Value{} + callProps := DefaultCallProps() + callProps.ForeachProp(func(_, key string, value reflect.Value) { + nameToValue[key] = value + }) + + for p.e == nil && p.Char() != ')' { + propName := p.Ident() + value, ok := nameToValue[propName] + if !ok { + p.eatExcessive(true, "unknown call property: %s", propName) + if p.Char() == ',' { + p.Parse(',') + } + continue + } + switch kind := value.Kind(); kind { + case reflect.Int: + p.Parse(':') + strVal := p.Ident() + intV, err := strconv.ParseInt(strVal, 0, 64) + if err != nil { + p.strictFailf("invalid int value: %s", strVal) + } else { + value.SetInt(intV) + } + default: + panic("unable to handle call props of type " + kind.String()) + } + if p.Char() != ')' { + p.Parse(',') + } + } + return callProps +} + func (p *parser) parseArg(typ Type, dir Dir) (Arg, error) { r := "" if p.Char() == '<' { |
