github.com/qiniu/dyn@v1.3.0/vars/context.go (about)

     1  package vars
     2  
     3  import (
     4  	"errors"
     5  	"reflect"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/qiniu/dyn/dyn"
    10  	"github.com/qiniu/dyn/text"
    11  	"github.com/qiniu/x/log"
    12  
    13  	"github.com/qiniu/dyn/proto"
    14  )
    15  
    16  const (
    17  	Fmttype_Invalid = proto.Fmttype_Invalid
    18  	Fmttype_Json    = proto.Fmttype_Json
    19  	Fmttype_Form    = proto.Fmttype_Form
    20  	Fmttype_Text    = proto.Fmttype_Text
    21  	Fmttype_Jsonstr = proto.Fmttype_Jsonstr
    22  )
    23  
    24  var (
    25  	ErrNotVar           = errors.New("assign to a non variable expression")
    26  	ErrUnmatchedValue   = errors.New("unmatched value")
    27  	ErrSliceLenNotEqual = errors.New("slice length not equal")
    28  )
    29  
    30  // ---------------------------------------------------------------------------
    31  
    32  type Context struct {
    33  	vars map[string]interface{}
    34  }
    35  
    36  func New() *Context {
    37  
    38  	vars := make(map[string]interface{})
    39  	return &Context{
    40  		vars: vars,
    41  	}
    42  }
    43  
    44  // ---------------------------------------------------------------------------
    45  
    46  func (p *Context) GetVars() map[string]interface{} {
    47  
    48  	return p.vars
    49  }
    50  
    51  func (p *Context) GetVar(key string) (v interface{}, ok bool) {
    52  
    53  	return dyn.Get(p.vars, key)
    54  }
    55  
    56  func (p *Context) DeleteVar(key string) {
    57  
    58  	v := p.vars
    59  	parts := strings.Split(key, ".")
    60  	ilast := len(parts) - 1
    61  
    62  	for i := 0; i < ilast; i++ {
    63  		v1, ok1 := v[parts[i]]
    64  		if !ok1 {
    65  			return
    66  		}
    67  		v2, ok2 := v1.(map[string]interface{})
    68  		if !ok2 || v2 == nil {
    69  			return
    70  		}
    71  		v = v2
    72  	}
    73  	delete(v, parts[ilast])
    74  }
    75  
    76  func (p *Context) MatchVar(key string, vreal interface{}) (err error) {
    77  
    78  	var v interface{}
    79  	var ok bool
    80  
    81  	parts := strings.Split(key, ".")
    82  	ilast := len(parts) - 1
    83  
    84  	v = p.vars
    85  	for i, part := range parts {
    86  		v1, ok1 := v.(map[string]interface{})
    87  		if !ok1 || v1 == nil {
    88  			return ErrUnmatchedValue
    89  		}
    90  		if v, ok = v1[part]; ok {
    91  			continue
    92  		}
    93  		if i == ilast {
    94  			v1[part] = vreal
    95  			return nil
    96  		}
    97  		v = make(map[string]interface{})
    98  		v1[part] = v
    99  	}
   100  
   101  	return p.Match(v, vreal)
   102  }
   103  
   104  // ---------------------------------------------------------------------------
   105  
   106  func (p *Context) Match(vexp, vreal interface{}) (err error) {
   107  
   108  	return p.doMatch(vexp, vreal, "")
   109  }
   110  
   111  func (p *Context) doMatch(vexp, vreal interface{}, field string) (err error) {
   112  
   113  	switch v := vexp.(type) {
   114  	case map[string]interface{}:
   115  		if v2, ok := vreal.(map[string]interface{}); ok {
   116  			sfield := field
   117  			if sfield != "" {
   118  				sfield += "."
   119  			}
   120  			for sk, sv := range v {
   121  				if sv2, ok2 := v2[sk]; ok2 {
   122  					err = p.doMatch(sv, sv2, sfield+sk)
   123  					if err == nil {
   124  						continue
   125  					}
   126  				} else {
   127  					err = errors.New("field not found: `" + sfield + sk + "`")
   128  				}
   129  				return
   130  			}
   131  			return nil
   132  		}
   133  		log.Debug("Match map object failed:", vexp, vreal)
   134  
   135  	case proto.Var:
   136  		err2 := p.MatchVar(v.Key, vreal)
   137  		if err2 != nil {
   138  			if field == "" {
   139  				return ErrUnmatchedValue
   140  			}
   141  			return errors.New("match field `" + field + "` failed: " + err2.Error())
   142  		}
   143  		return nil
   144  
   145  	case []interface{}:
   146  		v2 := reflect.ValueOf(vreal)
   147  		if v2.Kind() == reflect.Slice {
   148  			if len(v) != v2.Len() {
   149  				err = ErrSliceLenNotEqual
   150  				return
   151  			}
   152  			sfield := field
   153  			if sfield != "" {
   154  				sfield += "."
   155  			}
   156  			for i, sv := range v {
   157  				err = p.doMatch(sv, v2.Index(i).Interface(), sfield+strconv.Itoa(i))
   158  				if err != nil {
   159  					return
   160  				}
   161  			}
   162  			return nil
   163  		}
   164  		log.Debug("Match slice object failed:", vexp, vreal)
   165  
   166  	default:
   167  		if reflect.DeepEqual(vexp, vreal) {
   168  			return nil
   169  		}
   170  		log.Debug("Match value failed:", vexp, vreal)
   171  	}
   172  
   173  	if field == "" {
   174  		return ErrUnmatchedValue
   175  	}
   176  	return errors.New("unmatched field: `" + field + "`")
   177  }
   178  
   179  // ---------------------------------------------------------------------------
   180  
   181  func (p *Context) Let(vexp, vreal interface{}) (err error) {
   182  
   183  	if v, ok := vexp.(proto.Var); ok {
   184  		p.DeleteVar(v.Key)
   185  		return p.Match(vexp, vreal)
   186  	}
   187  	return ErrNotVar
   188  }
   189  
   190  // ---------------------------------------------------------------------------
   191  
   192  func (p *Context) Subst(vexp interface{}, ft int) (vres interface{}, err error) {
   193  
   194  	switch v := vexp.(type) {
   195  	case map[string]interface{}:
   196  		vres1 := make(map[string]interface{})
   197  		for sk, sv := range v {
   198  			vres2, err2 := p.Subst(sv, Fmttype_Invalid)
   199  			if err2 != nil {
   200  				return nil, err2
   201  			}
   202  			vres1[sk] = vres2
   203  		}
   204  		return vres1, nil
   205  
   206  	case string:
   207  		if ft == Fmttype_Invalid {
   208  			return vexp, nil
   209  		}
   210  		vres, err = p.SubstText(v, ft)
   211  		return
   212  
   213  	case proto.Var:
   214  		vres1, ok1 := dyn.Get(p.vars, v.Key)
   215  		if !ok1 {
   216  			return nil, errors.New("var `" + v.Key + "` not found")
   217  		}
   218  		return vres1, nil
   219  
   220  	case []interface{}:
   221  		n := len(v)
   222  		vres1 := make([]interface{}, n)
   223  		for i, sv := range v {
   224  			vres2, err2 := p.Subst(sv, Fmttype_Invalid)
   225  			if err2 != nil {
   226  				return nil, err2
   227  			}
   228  			vres1[i] = vres2
   229  		}
   230  		return vres1, nil
   231  
   232  	default:
   233  		return vexp, nil
   234  	}
   235  }
   236  
   237  func (p *Context) SubstText(exprvar string, ft int) (v string, err error) {
   238  
   239  	return text.Subst(exprvar, p.vars, ft, true)
   240  }
   241  
   242  // ---------------------------------------------------------------------------