github.com/cayleygraph/cayley@v0.7.7/schema/writer.go (about) 1 package schema 2 3 import ( 4 "fmt" 5 "reflect" 6 7 "github.com/cayleygraph/quad" 8 ) 9 10 // WriteAsQuads writes a single value in form of quads into specified quad writer. 11 // 12 // It returns an identifier of the object in the output sub-graph. If an object has 13 // an annotated ID field, it's value will be converted to quad.Value and returned. 14 // Otherwise, a new BNode will be generated using GenerateID function. 15 // 16 // See LoadTo for a list of quads mapping rules. 17 func (c *Config) WriteAsQuads(w quad.Writer, o interface{}) (quad.Value, error) { 18 wr := c.newWriter(w) 19 return wr.writeAsQuads(reflect.ValueOf(o)) 20 } 21 22 type writer struct { 23 c *Config 24 w quad.Writer 25 seen map[uintptr]quad.Value 26 } 27 28 func (c *Config) newWriter(w quad.Writer) *writer { 29 return &writer{c: c, w: w, seen: make(map[uintptr]quad.Value)} 30 } 31 32 func (w *writer) writeQuad(s, p, o quad.Value, rev bool) error { 33 if rev { 34 s, o = o, s 35 } 36 return w.w.WriteQuad(quad.Quad{Subject: s, Predicate: p, Object: o, Label: w.c.Label}) 37 } 38 39 // writeOneValReflect writes a set of quads corresponding to a value. It may omit writing quads if value is zero. 40 func (w *writer) writeOneValReflect(id quad.Value, pred quad.Value, rv reflect.Value, rev bool) error { 41 if isZero(rv) { 42 return nil 43 } 44 // write field value and get an ID 45 sid, err := w.writeAsQuads(rv) 46 if err != nil { 47 return err 48 } 49 // write a quad pointing to this value 50 return w.writeQuad(id, pred, sid, rev) 51 } 52 53 func (w *writer) writeTypeInfo(id quad.Value, rt reflect.Type) error { 54 iri := getTypeIRI(rt) 55 if iri == quad.IRI("") { 56 return nil 57 } 58 return w.writeQuad(id, w.c.iri(iriType), w.c.iri(iri), false) 59 } 60 61 func (w *writer) writeValueAs(id quad.Value, rv reflect.Value, pref string, rules fieldRules) error { 62 switch kind := rv.Kind(); kind { 63 case reflect.Ptr, reflect.Map: 64 ptr := rv.Pointer() 65 if _, ok := w.seen[ptr]; ok { 66 return nil 67 } 68 w.seen[ptr] = id 69 if kind == reflect.Ptr { 70 rv = rv.Elem() 71 } 72 } 73 rt := rv.Type() 74 if err := w.writeTypeInfo(id, rt); err != nil { 75 return err 76 } 77 for i := 0; i < rt.NumField(); i++ { 78 f := rt.Field(i) 79 if f.Anonymous { 80 if err := w.writeValueAs(id, rv.Field(i), pref+f.Name+".", rules); err != nil { 81 return err 82 } 83 continue 84 } 85 switch r := rules[pref+f.Name].(type) { 86 case constraintRule: 87 s, o := id, quad.Value(r.Val) 88 if r.Rev { 89 s, o = o, s 90 } 91 if err := w.writeQuad(s, r.Pred, o, false); err != nil { 92 return err 93 } 94 case saveRule: 95 if f.Type.Kind() == reflect.Slice { 96 sl := rv.Field(i) 97 for j := 0; j < sl.Len(); j++ { 98 if err := w.writeOneValReflect(id, r.Pred, sl.Index(j), r.Rev); err != nil { 99 return err 100 } 101 } 102 } else { 103 fv := rv.Field(i) 104 if !r.Opt && isZero(fv) { 105 return ErrReqFieldNotSet{Field: f.Name} 106 } 107 if err := w.writeOneValReflect(id, r.Pred, fv, r.Rev); err != nil { 108 return err 109 } 110 } 111 } 112 } 113 return nil 114 } 115 116 func (w *writer) writeAsQuads(rv reflect.Value) (quad.Value, error) { 117 rt := rv.Type() 118 // if node is a primitive - return directly 119 if rt.Implements(reflQuadValue) { 120 return rv.Interface().(quad.Value), nil 121 } 122 prv := rv 123 kind := rt.Kind() 124 // check if we've seen this node already 125 switch kind { 126 case reflect.Ptr, reflect.Map: 127 ptr := prv.Pointer() 128 if sid, ok := w.seen[ptr]; ok { 129 return sid, nil 130 } 131 if kind == reflect.Ptr { 132 rv = rv.Elem() 133 rt = rv.Type() 134 kind = rt.Kind() 135 } 136 } 137 // check if it's a type that quads package supports 138 // note, that it may be a struct such as time.Time 139 if val, ok := quad.AsValue(rv.Interface()); ok { 140 return val, nil 141 } else if kind == reflect.String { 142 return quad.String(rv.String()), nil 143 } else if kind == reflect.Int || kind == reflect.Uint || 144 kind == reflect.Int32 || kind == reflect.Uint32 || 145 kind == reflect.Int16 || kind == reflect.Uint16 || 146 kind == reflect.Int8 || kind == reflect.Uint8 { 147 return quad.Int(rv.Int()), nil 148 } else if kind == reflect.Float64 || kind == reflect.Float32 { 149 return quad.Float(rv.Float()), nil 150 } else if kind == reflect.Bool { 151 return quad.Bool(rv.Bool()), nil 152 } 153 // TODO(dennwc): support maps 154 if kind != reflect.Struct { 155 return nil, fmt.Errorf("unsupported type: %v", rt) 156 } 157 // get conversion rules for this struct type 158 rules, err := w.c.rulesFor(rt) 159 if err != nil { 160 return nil, fmt.Errorf("can't load rules: %v", err) 161 } 162 if len(rules) == 0 { 163 return nil, fmt.Errorf("no rules for struct: %v", rt) 164 } 165 // get an ID from the struct value 166 id, err := w.c.idFor(rules, rt, rv, "") 167 if err != nil { 168 return nil, err 169 } 170 if id == nil { 171 id = w.c.genID(prv.Interface()) 172 } 173 // save a node ID to avoid loops 174 switch prv.Kind() { 175 case reflect.Ptr, reflect.Map: 176 ptr := prv.Pointer() 177 w.seen[ptr] = id 178 } 179 if err = w.writeValueAs(id, rv, "", rules); err != nil { 180 return nil, err 181 } 182 return id, nil 183 }