Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: Go native types #22

Open
SpencerC opened this issue Mar 31, 2022 · 1 comment
Open

Feature request: Go native types #22

SpencerC opened this issue Mar 31, 2022 · 1 comment

Comments

@SpencerC
Copy link
Contributor

SpencerC commented Mar 31, 2022

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()},
	}
}
@mpenick
Copy link
Contributor

mpenick commented Mar 31, 2022

That looks great! Huge improvement in usability and much more concise.

Happy to PR these, or wait and PR them once this is a bit more baked.

If you're up for it a PR would be most welcome even if it's not fully "baked", but it's up to you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants