github.com/cayleygraph/cayley@v0.7.7/schema/schema.go (about) 1 // Package schema contains helpers to map Go objects to quads and vise-versa. 2 // 3 // This package is not a full schema library. It will not save or force any 4 // RDF schema constrains, it only provides a mapping. 5 package schema 6 7 import ( 8 "fmt" 9 "reflect" 10 "strings" 11 "sync" 12 "unicode" 13 "unicode/utf8" 14 15 "github.com/cayleygraph/cayley/graph" 16 "github.com/cayleygraph/cayley/graph/path" 17 "github.com/cayleygraph/quad" 18 "github.com/cayleygraph/quad/voc/rdf" 19 ) 20 21 var reflQuadValue = reflect.TypeOf((*quad.Value)(nil)).Elem() 22 23 type ErrReqFieldNotSet struct { 24 Field string 25 } 26 27 func (e ErrReqFieldNotSet) Error() string { 28 return fmt.Sprintf("required field is not set: %s", e.Field) 29 } 30 31 // IRIMode controls how IRIs are processed. 32 type IRIMode int 33 34 const ( 35 // IRINative applies no transformation to IRIs. 36 IRINative = IRIMode(iota) 37 // IRIShort will compact all IRIs with known namespaces. 38 IRIShort 39 // IRIFull will expand all IRIs with known namespaces. 40 IRIFull 41 ) 42 43 // NewConfig creates a new schema config. 44 func NewConfig() *Config { 45 return &Config{ 46 IRIs: IRINative, 47 } 48 } 49 50 // Config controls behavior of schema package. 51 type Config struct { 52 // IRIs set a conversion mode for all IRIs. 53 IRIs IRIMode 54 55 // GenerateID is called when any object without an ID field is being saved. 56 GenerateID func(_ interface{}) quad.Value 57 58 // Label will be added to all quads written. Does not affect queries. 59 Label quad.Value 60 61 rulesForTypeMu sync.RWMutex 62 rulesForType map[reflect.Type]fieldRules 63 } 64 65 func (c *Config) genID(o interface{}) quad.Value { 66 gen := c.GenerateID 67 if gen == nil { 68 gen = GenerateID 69 } 70 if gen == nil { 71 gen = func(_ interface{}) quad.Value { 72 return quad.RandomBlankNode() 73 } 74 } 75 return gen(o) 76 } 77 78 type rule interface { 79 isRule() 80 } 81 82 type constraintRule struct { 83 Pred quad.IRI 84 Val quad.IRI 85 Rev bool 86 } 87 88 func (constraintRule) isRule() {} 89 90 type saveRule struct { 91 Pred quad.IRI 92 Rev bool 93 Opt bool 94 } 95 96 func (saveRule) isRule() {} 97 98 type idRule struct{} 99 100 func (idRule) isRule() {} 101 102 const iriType = quad.IRI(rdf.Type) 103 104 func (c *Config) iri(v quad.IRI) quad.IRI { 105 switch c.IRIs { 106 case IRIShort: 107 v = v.Short() 108 case IRIFull: 109 v = v.Full() 110 } 111 return v 112 } 113 114 func (c *Config) toIRI(s string) quad.IRI { 115 var v quad.IRI 116 if s == "@type" { 117 v = iriType 118 } else { 119 v = quad.IRI(s) 120 } 121 return c.iri(v) 122 } 123 124 var reflEmptyStruct = reflect.TypeOf(struct{}{}) 125 126 func (c Config) fieldRule(fld reflect.StructField) (rule, error) { 127 tag := fld.Tag.Get("quad") 128 sub := strings.Split(tag, ",") 129 tag, sub = sub[0], sub[1:] 130 const ( 131 trim = ` ` 132 spo, ops = `>`, `<` 133 any, none = `*`, `-` 134 this = `@id` 135 ) 136 tag = strings.Trim(tag, trim) 137 jsn := false 138 if tag == "" { 139 tag = strings.SplitN(fld.Tag.Get("json"), ",", 2)[0] 140 jsn = true 141 } 142 if tag == "" || tag == none { 143 return nil, nil // ignore 144 } 145 rule := strings.Trim(tag, trim) 146 if rule == this { 147 return idRule{}, nil 148 } 149 opt := false 150 req := false 151 for _, s := range sub { 152 if s == "opt" || s == "optional" { 153 opt = true 154 } 155 if s == "req" || s == "required" { 156 req = true 157 } 158 } 159 if req { 160 opt = false 161 } else if fld.Type.Kind() == reflect.Slice { 162 opt = true 163 } 164 165 rev := strings.Contains(rule, ops) 166 var tri []string 167 if jsn { 168 tri = []string{rule} 169 } else if rev { // o<p-s 170 tri = strings.SplitN(rule, ops, 3) 171 if len(tri) != 2 { 172 return nil, fmt.Errorf("wrong quad tag format: '%s'", rule) 173 } 174 } else { // s-p>o // default 175 tri = strings.SplitN(rule, spo, 3) 176 if len(tri) > 2 { //len(tri) != 2 { 177 return nil, fmt.Errorf("wrong quad tag format: '%s'", rule) 178 } 179 } 180 var ps, vs string 181 if rev { 182 ps, vs = strings.Trim(tri[0], trim), strings.Trim(tri[1], trim) 183 } else { 184 ps, vs = strings.Trim(tri[0], trim), any 185 if len(tri) > 1 { 186 vs = strings.Trim(tri[1], trim) 187 } 188 } 189 if ps == "" { 190 return nil, fmt.Errorf("wrong quad format: '%s': no predicate", rule) 191 } 192 p := c.toIRI(ps) 193 if vs == "" || vs == any && fld.Type != reflEmptyStruct { 194 return saveRule{Pred: p, Rev: rev, Opt: opt}, nil 195 } else { 196 return constraintRule{Pred: p, Val: c.toIRI(vs), Rev: rev}, nil 197 } 198 } 199 200 func checkFieldType(ftp reflect.Type) error { 201 for ftp.Kind() == reflect.Ptr || ftp.Kind() == reflect.Slice { 202 ftp = ftp.Elem() 203 } 204 switch ftp.Kind() { 205 case reflect.Array: // TODO: support arrays 206 return fmt.Errorf("array fields are not supported yet") 207 case reflect.Func, reflect.Invalid: 208 return fmt.Errorf("%v fields are not supported", ftp.Kind()) 209 default: 210 } 211 return nil 212 } 213 214 var ( 215 typesMu sync.RWMutex 216 typeToIRI = make(map[reflect.Type]quad.IRI) 217 iriToType = make(map[quad.IRI]reflect.Type) 218 ) 219 220 func getTypeIRI(rt reflect.Type) quad.IRI { 221 typesMu.RLock() 222 iri := typeToIRI[rt] 223 typesMu.RUnlock() 224 return iri 225 } 226 227 // RegisterType associates an IRI with a given Go type. 228 // 229 // All queries and writes will require or add a type triple. 230 func RegisterType(iri quad.IRI, obj interface{}) { 231 var rt reflect.Type 232 if obj != nil { 233 if t, ok := obj.(reflect.Type); ok { 234 rt = t 235 } else { 236 rt = reflect.TypeOf(obj) 237 if rt.Kind() == reflect.Ptr { 238 rt = rt.Elem() 239 } 240 } 241 } 242 full := iri.Full() 243 typesMu.Lock() 244 defer typesMu.Unlock() 245 if obj == nil { 246 tp := iriToType[full] 247 delete(typeToIRI, tp) 248 delete(iriToType, full) 249 return 250 } 251 if _, exists := typeToIRI[rt]; exists { 252 panic(fmt.Errorf("type %v is already registered", rt)) 253 } 254 if _, exists := iriToType[full]; exists { 255 panic(fmt.Errorf("IRI %v is already registered", iri)) 256 } 257 typeToIRI[rt] = iri 258 iriToType[full] = rt 259 } 260 261 // PathForType builds a path (morphism) for a given Go type. 262 func (c *Config) PathForType(rt reflect.Type) (*path.Path, error) { 263 l := c.newLoader(nil) 264 return l.makePathForType(rt, "", false) 265 } 266 267 func anonFieldType(fld reflect.StructField) (reflect.Type, bool) { 268 ft := fld.Type 269 if ft.Kind() == reflect.Ptr { 270 ft = ft.Elem() 271 } 272 if ft.Kind() == reflect.Struct { 273 return ft, true 274 } 275 return ft, false 276 } 277 278 func (c *Config) rulesForStructTo(out fieldRules, pref string, rt reflect.Type) error { 279 for i := 0; i < rt.NumField(); i++ { 280 f := rt.Field(i) 281 name := f.Name 282 if f.Anonymous { 283 if ft, ok := anonFieldType(f); !ok { 284 return fmt.Errorf("anonymous fields of type %v are not supported", ft) 285 } else if err := c.rulesForStructTo(out, pref+name+".", ft); err != nil { 286 return err 287 } 288 continue 289 } 290 rules, err := c.fieldRule(f) 291 if err != nil { 292 return err 293 } 294 if rules != nil { 295 out[pref+name] = rules 296 } 297 } 298 return nil 299 } 300 301 // rulesFor 302 // 303 // Returned map should not be changed. 304 func (c *Config) rulesFor(rt reflect.Type) (fieldRules, error) { 305 // if rt.Kind() != reflect.Struct { 306 // return nil, fmt.Errorf("expected struct, got: %v", rt) 307 // } 308 c.rulesForTypeMu.RLock() 309 rules, ok := c.rulesForType[rt] 310 c.rulesForTypeMu.RUnlock() 311 if ok { 312 return rules, nil 313 } 314 out := make(fieldRules) 315 if err := c.rulesForStructTo(out, "", rt); err != nil { 316 return nil, err 317 } 318 c.rulesForTypeMu.Lock() 319 if c.rulesForType == nil { 320 c.rulesForType = make(map[reflect.Type]fieldRules) 321 } 322 c.rulesForType[rt] = out 323 c.rulesForTypeMu.Unlock() 324 return out, nil 325 } 326 327 type fieldsCtxKey struct{} 328 type fieldRules map[string]rule 329 330 type ValueConverter interface { 331 SetValue(dst reflect.Value, src reflect.Value) error 332 } 333 334 type ValueConverterFunc func(dst reflect.Value, src reflect.Value) error 335 336 func (f ValueConverterFunc) SetValue(dst reflect.Value, src reflect.Value) error { return f(dst, src) } 337 338 var DefaultConverter ValueConverter 339 340 type ErrTypeConversionFailed struct { 341 From reflect.Type 342 To reflect.Type 343 } 344 345 func (e ErrTypeConversionFailed) Error() string { 346 return fmt.Sprintf("cannot convert %v to %v", e.From, e.To) 347 } 348 349 func init() { 350 DefaultConverter = ValueConverterFunc(func(dst reflect.Value, src reflect.Value) error { 351 dt, st := dst.Type(), src.Type() 352 if dt == st || (dt.Kind() == reflect.Interface && st.Implements(dt)) { 353 dst.Set(src) 354 return nil 355 } else if st.ConvertibleTo(dt) { 356 dst.Set(src.Convert(dt)) 357 return nil 358 } else if dt.Kind() == reflect.Ptr { 359 v := reflect.New(dt.Elem()) 360 if err := DefaultConverter.SetValue(v.Elem(), src); err != nil { 361 return err 362 } 363 dst.Set(v) 364 return nil 365 } else if dt.Kind() == reflect.Slice { 366 v := reflect.New(dt.Elem()) 367 if err := DefaultConverter.SetValue(v.Elem(), src); err != nil { 368 return err 369 } 370 dst.Set(reflect.Append(dst, v.Elem())) 371 return nil 372 } 373 return ErrTypeConversionFailed{From: src.Type(), To: dst.Type()} 374 }) 375 } 376 377 func isNative(rt reflect.Type) bool { // TODO(dennwc): replace 378 _, ok := quad.AsValue(reflect.Zero(rt).Interface()) 379 return ok 380 } 381 382 func keysEqual(v1, v2 graph.Ref) bool { 383 type key interface { 384 Key() interface{} 385 } 386 e1, ok1 := v1.(key) 387 e2, ok2 := v2.(key) 388 if ok1 != ok2 { 389 return false 390 } 391 if ok1 && ok2 { 392 return e1.Key() == e2.Key() 393 } 394 return v1 == v2 395 } 396 397 func isExported(name string) bool { 398 ch, _ := utf8.DecodeRuneInString(name) 399 return unicode.IsUpper(ch) 400 } 401 402 func isZero(rv reflect.Value) bool { 403 switch rv.Kind() { 404 case reflect.Ptr: 405 return rv.IsNil() 406 case reflect.Slice, reflect.Map: 407 return rv.IsNil() || rv.Len() == 0 408 case reflect.Struct: 409 // have to be careful here - struct may contain slice fields, 410 // so we cannot compare them directly 411 rt := rv.Type() 412 exported := 0 413 for i := 0; i < rt.NumField(); i++ { 414 f := rt.Field(i) 415 if !isExported(f.Name) { 416 continue 417 } 418 exported++ 419 if !isZero(rv.Field(i)) { 420 return false 421 } 422 } 423 if exported != 0 { 424 return true 425 } 426 // opaque type - compare directly 427 } 428 // primitive types 429 return rv.Interface() == reflect.Zero(rv.Type()).Interface() 430 } 431 432 func (c *Config) idFor(rules fieldRules, rt reflect.Type, rv reflect.Value, pref string) (id quad.Value, err error) { 433 hasAnon := false 434 for i := 0; i < rt.NumField(); i++ { 435 fld := rt.Field(i) 436 hasAnon = hasAnon || fld.Anonymous 437 if _, ok := rules[pref+fld.Name].(idRule); ok { 438 vid := rv.Field(i).Interface() 439 switch vid := vid.(type) { 440 case quad.IRI: 441 id = c.iri(vid) 442 case quad.BNode: 443 id = vid 444 case string: 445 id = c.toIRI(vid) 446 default: 447 err = fmt.Errorf("unsupported type for id field: %T", vid) 448 } 449 return 450 } 451 } 452 if !hasAnon { 453 return 454 } 455 // second pass - look for anonymous fields 456 for i := 0; i < rt.NumField(); i++ { 457 fld := rt.Field(i) 458 if !fld.Anonymous { 459 continue 460 } 461 id, err = c.idFor(rules, fld.Type, rv.Field(i), pref+fld.Name+".") 462 if err != nil || id != nil { 463 return 464 } 465 } 466 return 467 }