Skip to content

Feature request: Go native types #22

@SpencerC

Description

@SpencerC

Overview

It's great being able to interact with the gRPC message types directly! But sometimes it's more desirable to work with more simplified semantics, especially for testing. In addition, something like this could be used a a translation type for features like #20 if you guys want to go in that direction.

How it might look

Here's what developers currently have to do to generate test data:

id0, _ := uuid.MustParse("00000000-0000-0000-0000-000000000000").MarshalBinary()
id1, _ := uuid.MustParse("00000000-0000-0000-0000-000000000001").MarshalBinary()
res := &pb.Response{
	Result: &pb.Response_ResultSet{
		ResultSet: &pb.ResultSet{
			Columns: []*pb.ColumnSpec{
				{
					Type: &pb.TypeSpec{
						Spec: &pb.TypeSpec_Basic_{Basic: pb.TypeSpec_UUID},
					},
					Name: "id",
				},
				{
					Type: &pb.TypeSpec{
						Spec: &pb.TypeSpec_Basic_{Basic: pb.TypeSpec_TEXT},
					},
					Name: "name",
				},
				{
					Type: &pb.TypeSpec{
						Spec: &pb.TypeSpec_Basic_{Basic: pb.TypeSpec_INT},
					},
					Name: "age",
				},
			},
			Rows: []*pb.Row{
				{
					Values: []*pb.Value{
						{Inner: &pb.Value_Uuid{Uuid: &pb.Uuid{Value: id0}}},
						{Inner: &pb.Value_String_{String_: "Someone"}},
						{Inner: &pb.Value_Int{Int: 30}},
					},
				},
				{
					Values: []*pb.Value{
						{Inner: &pb.Value_Uuid{Uuid: &pb.Uuid{Value: id1}}},
						{Inner: &pb.Value_String_{String_: "Someone Else"}},
						{Inner: &pb.Value_Int{Int: 73}},
					},
				},
			},
		},
	},
}

Here's an approach to the same thing with native types:

id0 := uuid.MustParse("00000000-0000-0000-0000-000000000000")
id1 := uuid.MustParse("00000000-0000-0000-0000-000000000001")
sr := &StargateResponse{
	Result: &Result{
		Columns: []StargateColumnSpec{
			&BasicColumn{Name: "id", Type: &UUIDType{}},
			&BasicColumn{Name: "name", Type: &TextType{}},
			&BasicColumn{Name: "age", Type: &IntType{}},
		},
		Rows: []*Row{
			{
				&UUIDValue{id0}, &TextValue{"Someone"}, &IntValue{30},
			},
			{
				&UUIDValue{id1}, &TextValue{"Someone Else"}, &IntValue{73},
			},
		},
	},
}
dRes := sr.ToProto()

What I have so far

For my own use, I've created these types so far and intend to add more as needed. Happy to PR these, or wait and PR them once this is a bit more baked.

type StargateTypeSpec interface {
	ToProto() *pb.TypeSpec
}

type UUIDType struct{}

func (t *UUIDType) ToProto() *pb.TypeSpec {
	return &pb.TypeSpec{Spec: &pb.TypeSpec_Basic_{Basic: pb.TypeSpec_UUID}}
}

type TextType struct{}

func (t *TextType) ToProto() *pb.TypeSpec {
	return &pb.TypeSpec{Spec: &pb.TypeSpec_Basic_{Basic: pb.TypeSpec_TEXT}}
}

type StargateColumnSpec interface {
	ToProto() *pb.ColumnSpec
}

type BasicColumn struct {
	Name string
	Type StargateTypeSpec
}

func (c *BasicColumn) ToProto() *pb.ColumnSpec {
	return &pb.ColumnSpec{
		Type: c.Type.ToProto(),
		Name: c.Name,
	}
}

type ListColumn struct {
	Name string
	Type StargateTypeSpec
}

func (c *ListColumn) ToProto() *pb.ColumnSpec {
	return &pb.ColumnSpec{
		Type: &pb.TypeSpec{
			Spec: &pb.TypeSpec_List_{
				List: &pb.TypeSpec_List{Element: c.Type.ToProto()},
			},
		},
		Name: c.Name,
	}
}

type StargateValue interface {
	ToProto() *pb.Value
}

type UUIDValue struct{ uuid.UUID }

func (u *UUIDValue) ToProto() *pb.Value {
	b, _ := u.MarshalBinary()
	return &pb.Value{
		Inner: &pb.Value_Uuid{
			Uuid: &pb.Uuid{Value: b},
		},
	}
}

type TextValue struct{ string }

func (s *TextValue) ToProto() *pb.Value {
	return &pb.Value{
		Inner: &pb.Value_String_{
			String_: s.string,
		},
	}
}

type IntValue struct{ int64 }

func (i *IntValue) ToProto() *pb.Value {
	return &pb.Value{
		Inner: &pb.Value_Int{
			Int: i.int64,
		},
	}
}

type ListValue struct {
	Values []StargateValue
}

func (l *ListValue) ToProto() *pb.Value {
	values := make([]*pb.Value, len(l.Values))
	for i, v := range l.Values {
		values[i] = v.ToProto()
	}
	return &pb.Value{
		Inner: &pb.Value_Collection{
			Collection: &pb.Collection{
				Elements: values,
			},
		},
	}
}

type Row []StargateValue

func (r *Row) ToProto() *pb.Row {
	row := &pb.Row{
		Values: make([]*pb.Value, len(*r)),
	}
	for i, cell := range *r {
		row.Values[i] = cell.ToProto()
	}
	return row
}

type Result struct {
	Columns []StargateColumnSpec
	Rows    []*Row
}

func (r *Result) ToProto() *pb.ResultSet {
	result := &pb.ResultSet{
		Columns: make([]*pb.ColumnSpec, len(r.Columns)),
		Rows:    make([]*pb.Row, len(r.Rows)),
	}
	for i, col := range r.Columns {
		result.Columns[i] = col.ToProto()
	}
	for i, row := range r.Rows {
		result.Rows[i] = row.ToProto()
	}
	return result
}

type StargateResponse struct {
	Result *Result
}

func (r *StargateResponse) ToProto() *pb.Response {
	return &pb.Response{
		Result: &pb.Response_ResultSet{ResultSet: r.Result.ToProto()},
	}
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions