Repository: cayleygraph/cayley (go)

Issue:
schema: support defined types
<!--
The GitHub issue tracker is for bug reports and feature requests.
General support can be found at the following locations:

- Cayley Forum - https://discourse.cayley.io
- Slack - cayleygraph.slack.com, invites: https://cayley-slackin.herokuapp.com

Please answer these questions before submitting your issue. Thanks!
-->

**Description**

<!--
Briefly describe the problem you are having in a few paragraphs.
-->
Hi.  I'm new to Cayley, so please forgive any newbie-ness.  I'm considering whether to use Cayley for a project.  One of the things I hope to do is write structs into Cayley and read them back out.  I'm hoping the schema package works out for this.

One of my structs includes fields of defined types (https://golang.org/ref/spec#Type_definitions), i.e.

`type MyCustomType string`

This leads to Cayley failing to write with "unsupported type: MyCustomType"

**Steps to reproduce the issue:**
1. Apply patch (below) to examples/hello_schema/main.go
2. `go run main.go`
3. Observe the error

Here's the patch (`git diff`)

```
index e3fe2f1..2e6fb98 100644
--- a/examples/hello_schema/main.go
+++ b/examples/hello_schema/main.go
@@ -19,6 +19,8 @@ import (
        _ "github.com/cayleygraph/cayley/voc/core"
 )

+type TestString string
+
 type Person struct {
        // dummy field to enforce all object to have a <id> <rdf:type> <ex:Person> relation
        // means nothing for Go itself
@@ -26,6 +28,8 @@ type Person struct {
        ID      quad.IRI `json:"@id"`     // tag @id is a special one - graph node value will be stored in this field
        Name    string   `json:"ex:name"` // field name (predicate) may be written as json field name
        Age     int      `quad:"ex:age"`  // or in a quad tag
+
+       Test TestString `quad:"test"`
 }

 type Coords struct {
@@ -76,6 +80,7 @@ func main() {
        bob := Person{
                ID:   quad.IRI("ex:bob").Full().Short(),
                Name: "Bob", Age: 32,
+               Test: "foo",
        }
        fmt.Printf("saving: %+v\n", bob)
        id, err := sch.WriteAsQuads(qw, bob)

```


**Received results:**

<!--
Describe results you received.

Any additional information such as logs will help.
Enclose it into following markdown:
-->
```
2019/02/15 05:33:02 unsupported type: main.TestString
```

**Expected results:**


**Output of `cayley version` or commit hash:**

```
f0dd103fbc44c697e7a063279b5850ec4a7b403a
```



Pull request:
schema: Support typedefs when writing structs
Support custom types based on primitive Go types when writing quads with the schema library.

Fixes #768

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/cayleygraph/cayley/775)
<!-- Reviewable:end -->


Patch:
diff --git a/schema/writer.go b/schema/writer.go
index 057922f..2460f1e 100644
--- a/schema/writer.go
+++ b/schema/writer.go
@@ -138,6 +138,17 @@ func (w *writer) writeAsQuads(rv reflect.Value) (quad.Value, error) {
 	// note, that it may be a struct such as time.Time
 	if val, ok := quad.AsValue(rv.Interface()); ok {
 		return val, nil
+	} else if kind == reflect.String {
+		return quad.String(rv.String()), nil
+	} else if kind == reflect.Int || kind == reflect.Uint ||
+		kind == reflect.Int32 || kind == reflect.Uint32 ||
+		kind == reflect.Int16 || kind == reflect.Uint16 ||
+		kind == reflect.Int8 || kind == reflect.Uint8 {
+		return quad.Int(rv.Int()), nil
+	} else if kind == reflect.Float64 || kind == reflect.Float32 {
+		return quad.Float(rv.Float()), nil
+	} else if kind == reflect.Bool {
+		return quad.Bool(rv.Bool()), nil
 	}
 	// TODO(dennwc): support maps
 	if kind != reflect.Struct {


Test patch:
diff --git a/schema/loader_test.go b/schema/loader_test.go
index dc16167..498d53d 100644
--- a/schema/loader_test.go
+++ b/schema/loader_test.go
@@ -348,6 +348,16 @@ var testFillValueCases = []struct {
 			{iri("1234"), iri("num"), quad.Int(3), nil},
 		},
 	},
+	{
+		name: "typedef",
+		expect: genObjectTypedef{
+			ID:   "1234",
+			Name: "Obj",
+		},
+		quads: []quad.Quad{
+			{iri("1234"), iri("name"), quad.String("Obj"), nil},
+		},
+	},
 	{
 		name:   "coords",
 		expect: Coords{Lat: 12.3, Lng: 34.5},
diff --git a/schema/schema_test.go b/schema/schema_test.go
index 414b0d9..98654ef 100644
--- a/schema/schema_test.go
+++ b/schema/schema_test.go
@@ -95,6 +95,13 @@ type genObject struct {
 	Name string   `quad:"name"`
 }
 
+type MyString string
+
+type genObjectTypedef struct {
+	ID   quad.IRI `quad:"@id"`
+	Name MyString `quad:"name"`
+}
+
 type subObject struct {
 	genObject
 	Num int `quad:"num"`
diff --git a/schema/writer_test.go b/schema/writer_test.go
index bead521..ea6f988 100644
--- a/schema/writer_test.go
+++ b/schema/writer_test.go
@@ -172,6 +172,18 @@ var testWriteValueCases = []struct {
 		},
 		nil,
 	},
+	{
+		"typedef",
+		genObjectTypedef{
+			ID:   "1234",
+			Name: "Obj",
+		},
+		iri("1234"),
+		[]quad.Quad{
+			{iri("1234"), iri("name"), quad.String("Obj"), nil},
+		},
+		nil,
+	},
 	{
 		"simple object (embedded multiple levels)",
 		subSubObject{