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  }