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 // ---------------------------------------------------------------------------