I have similar setup as bellow, how can I access my extension values from XYZ enum using "github.com/golang/protobuf/proto"?
extend google.protobuf.EnumValueOptions {
Details meta = 50001;
}
message Details {
string description = 1;
}
enum MyEnum {
MY_ENUM_UNSPECIFIED = 0;
XYZ = 1 [deprecated=true, (meta) = {description: "lorem ipsum"}];
}
I'm aware of proto.GetExtension(proto.Message, proto.ExtensionDesc), however I wasn't able to figure out how it can be used for the enum...
Bit late but I've just run into the same; you can do it like this:
fd, _ := descriptor.ForMessage(&pb.Details{})
for _, e := range fd.EnumType {
if e.GetName() == "MyEnum" {
for _, v := range e.Value {
ext, err := proto.GetExtension(v.Options, pb.E_Meta)
if err == nil {
details := ext.(*pb.Details)
// do stuff with details
}
}
}
}
There might be a more direct way of getting the enum descriptor, although I haven't managed after a bit of wrangling.
Some of the methods used in current best answer is now deprecated and it is a bit lengthy.
Here is how I got it:
// pd is the module of your complied protobuf files
fd := pd.File_name_of_your_proto_file_proto
enumDesc := fd.Enums().ByName("MyEnum")
if enumDesc == nil {
panic()
}
enumValDesc := enumDesc.Values().ByName("XYZ")
if enumValDesc == nil {
panic()
}
ext := proto.GetExtension(enumValDesc.Options(), pd.E_Meta)
if enumValDesc == nil {
panic()
}
meta := ext.(*Details)
Let me know if there is a better way.
After many hours, I have found a method to access the description for the enum. Here is my implementation, I hope it helps.
In a file called enum.go in the same package as the generated .pb file, I added this method to the enum type that retrieves the description.
func (t MyEnum) GetValue() (*Details, error) {
tt, err := proto.GetExtension(proto.MessageV1(t.Descriptor().Values().ByNumber(t.Number()).Options()), E_Details)
if err != nil {
return nil, err
}
return tt.(*Details), nil
}
I am sure there is an easier way, but until somebody finds one, this should work.
Related
Im working with for each loop and var of information and filtering it by A) regex.matchString B)Timecomparrison. The filtering works well and I have the data I need but currently I'm outputting it to screen via fmt.Println in part of the loop. My goal is to take that data and build another var with the now filtered list. I guess I need to make a new variable and add to it? But how do I return that and something I can use later?
Any assistance is appreciated.
for _, thing := range things {
if thing.element1 != nil {
matched, err := regexp.MatchString(z, element1)
if err != nil {
fmt.Println(err)
}
if matched {
if timecomparrison(element2, a) {
// this is a section that needs to be added new var and returned as a var
fmt.Println("****")
fmt.Println("element1:", element1)
fmt.Println("element2:", element2)
}
}
}
}
}
I think you need something like this.
type Thing struct {
element1 string
element2 string
}
func filter() []Thing {
things := []Thing{
{element1: "element1", element2: "element2"},
}
var result []Thing
regex := "/{}d/"
date := time.Now
for _, thing := range things {
if thing.element1 != nil {
matched, err := regexp.MatchString(regex, thing.element1)
if err != nil {
fmt.Println(err)
}
if matched {
if timeComparison(thing.element2, date) {
// this is a section that needs to be added new var and returned as a var
fmt.Println("****")
fmt.Println("element1:", thing.element1)
fmt.Println("element2:", thing.element2)
result = append(result, thing)
}
}
}
}
return result
}
I cleaned the code, added a type and some data, fixed some issues and renamed some things, but you should get the idea :)
I have an integer query param "page", that defaults to 1. I can get it from Echo context like this:
var page int
pageString := c.QueryParam("page")
if pageString == "" {
page = 1
} else {
var err error
page, err = strconv.Atoi(pageString)
if err != nil {
page = 1
}
}
While this works, I would kind of prefer to do something like page := c.QueryParamInt("page", 1) but I couldn't find any equivalent in Echo docs. Should I just write my own utility function or is there a better way?
You can try something like:
qp := c.QueryParam("page")
page, err := strconv.Atoi(qp)
if err != nil {
page=1
}
You don't have to write that much ifelse.
Hope this helps.
If you plan on parsing many integer params then the best thing would be to write your own utility function:
func QueryParamInt(c echo.Context, name string, default int) int {
param := c.QueryParam(name)
result, err := strconv.Atoi(param)
if err != nil {
return default
}
return result
}
Then use it as you would like.
Other options would be to extend the echo context, more on that here.
I have created an object mapping in Go that is not relational, it is very simple.
I have several structs that looks like this:
type Message struct {
Id int64
Message string
ReplyTo sql.NullInt64 `db:"reply_to"`
FromId int64 `db:"from_id"`
ToId int64 `db:"to_id"`
IsActive bool `db:"is_active"`
SentTime int64 `db:"sent_time"`
IsViewed bool `db:"is_viewed"`
Method string `db:"-"`
AppendTo int64 `db:"-"`
}
To create a new message I just run this function:
func New() *Message {
return &Message{
IsActive: true,
SentTime: time.Now().Unix(),
Method: "new",
}
}
And then I have a message_crud.go file for this struct that looks like this:
To find a message by a unique column (for example by id) I run this function:
func ByUnique(column string, value interface{}) (*Message, error) {
query := fmt.Sprintf(`
SELECT *
FROM message
WHERE %s = ?
LIMIT 1;
`, column)
message := &Message{}
err := sql.DB.QueryRowx(query, value).StructScan(message)
if err != nil {
return nil, err
}
return message, nil
}
And to save a message (insert or update in the database) I run this method:
func (this *Message) save() error {
s := ""
if this.Id == 0 {
s = "INSERT INTO message SET %s;"
} else {
s = "UPDATE message SET %s WHERE id=:id;"
}
query := fmt.Sprintf(s, sql.PlaceholderPairs(this))
nstmt, err := sql.DB.PrepareNamed(query)
if err != nil {
return err
}
res, err := nstmt.Exec(*this)
if err != nil {
return err
}
if this.Id == 0 {
lastId, err := res.LastInsertId()
if err != nil {
return err
}
this.Id = lastId
}
return nil
}
The sql.PlaceholderPairs() function looks like this:
func PlaceholderPairs(i interface{}) string {
s := ""
val := reflect.ValueOf(i).Elem()
count := val.NumField()
for i := 0; i < count; i++ {
typeField := val.Type().Field(i)
tag := typeField.Tag
fname := strings.ToLower(typeField.Name)
if fname == "id" {
continue
}
if t := tag.Get("db"); t == "-" {
continue
} else if t != "" {
s += t + "=:" + t
} else {
s += fname + "=:" + fname
}
s += ", "
}
s = s[:len(s)-2]
return s
}
But every time I create a new struct, for example a User struct I have to copy paste the "crud section" above and create a user_crud.go file and replace the words "Message" with "User", and the words "message" with "user". I repeat alot of code and it is not very dry. Is there something I could do to not repeat this code for things I would reuse? I always have a save() method, and always have a function ByUnique() where I can return a struct and search by a unique column.
In PHP this was easy because PHP is not statically typed.
Is this possible to do in Go?
Your ByUnique is almost generic already. Just pull out the piece that varies (the table and destination):
func ByUnique(table string, column string, value interface{}, dest interface{}) error {
query := fmt.Sprintf(`
SELECT *
FROM %s
WHERE %s = ?
LIMIT 1;
`, table, column)
return sql.DB.QueryRowx(query, value).StructScan(dest)
}
func ByUniqueMessage(column string, value interface{}) (*Message, error) {
message := &Message{}
if err := ByUnique("message", column, value, &message); err != nil {
return nil, err
}
return message, error
}
Your save is very similar. You just need to make a generic save function along the lines of:
func Save(table string, identifier int64, source interface{}) { ... }
Then inside of (*Message)save, you'd just call the general Save() function. Looks pretty straightforward.
Side notes: do not use this as the name of the object inside a method. See the link from #OneOfOne for more on that. And do not get obsessed about DRY. It is not a goal in itself. Go focuses on code being simple, clear, and reliable. Do not create something complicated and fragile just to avoid typing a simple line of error handling. This doesn't mean that you shouldn't extract duplicated code. It just means that in Go it is usually better to repeat simple code a little bit rather than create complicated code to avoid it.
EDIT: If you want to implement Save using an interface, that's no problem. Just create an Identifier interface.
type Ider interface {
Id() int64
SetId(newId int64)
}
func (msg *Message) Id() int64 {
return msg.Id
}
func (msg *Message) SetId(newId int64) {
msg.Id = newId
}
func Save(table string, source Ider) error {
s := ""
if source.Id() == 0 {
s = fmt.Sprintf("INSERT INTO %s SET %%s;", table)
} else {
s = fmt.Sprintf("UPDATE %s SET %%s WHERE id=:id;", table)
}
query := fmt.Sprintf(s, sql.PlaceholderPairs(source))
nstmt, err := sql.DB.PrepareNamed(query)
if err != nil {
return err
}
res, err := nstmt.Exec(source)
if err != nil {
return err
}
if source.Id() == 0 {
lastId, err := res.LastInsertId()
if err != nil {
return err
}
source.SetId(lastId)
}
return nil
}
func (msg *Message) save() error {
return Save("message", msg)
}
The one piece that might blow up with this is the call to Exec. I don't know what package you're using, and it's possible that Exec won't work correctly if you pass it an interface rather than the actual struct, but it probably will work. That said, I'd probably just pass the identifier rather than adding this overhead.
You probably want to use an ORM.
They eliminate a lot of the boilerplate code you're describing.
See this question for "What is an ORM?"
Here is a list of ORMs for go: https://github.com/avelino/awesome-go#orm
I have never used one myself, so I can't recommend any. The main reason is that an ORM takes a lot of control from the developer and introduces a non-negligible performance overhead. You need to see for yourself if they fit your use-case and/or if you are comfortable with the "magic" that's going on in those libraries.
I don't recommend doing this, i personally would prefer being explicit about scanning into structs and creating queries.
But if you really want to stick to reflection you could do:
func ByUnique(obj interface{}, column string, value interface{}) error {
// ...
return sql.DB.QueryRowx(query, value).StructScan(obj)
}
// Call with
message := &Message{}
ByUnique(message, ...)
And for your save:
type Identifiable interface {
Id() int64
}
// Implement Identifiable for message, etc.
func Save(obj Identifiable) error {
// ...
}
// Call with
Save(message)
The approach i use and would recommend to you:
type Redirect struct {
ID string
URL string
CreatedAt time.Time
}
func FindByID(db *sql.DB, id string) (*Redirect, error) {
var redirect Redirect
err := db.QueryRow(
`SELECT "id", "url", "created_at" FROM "redirect" WHERE "id" = $1`, id).
Scan(&redirect.ID, &redirect.URL, &redirect.CreatedAt)
switch {
case err == sql.ErrNoRows:
return nil, nil
case err != nil:
return nil, err
}
return &redirect, nil
}
func Save(db *sql.DB, redirect *Redirect) error {
redirect.CreatedAt = time.Now()
_, err := db.Exec(
`INSERT INTO "redirect" ("id", "url", "created_at") VALUES ($1, $2, $3)`,
redirect.ID, redirect.URL, redirect.CreatedAt)
return err
}
This has the advantage of using the type system and mapping only things it should actually map.
What's the best signature for a function that returns an optional value and a possible error?
For example:
func findColor(name string) (RGB, error) {
...
}
(The empty RGB value is black, a valid color, so you can't use it to infer that no value was found. Assume the error might come from something like a database connection.)
The two options that seem best are a boolean return value:
func findColor(name string) (RGB, bool, error) {
...
}
c, ok, err := findColor(myname)
if !ok {
...
} else if err != nil {
...
}
...
Or a special error value:
var ColorNotFound = errors.New(...)
func findColor(name string) (RGB, error) {
...
}
c, err := findColor(...)
if err == ColorNotFound {
...
} else if err != nil {
...
}
...
(Making special errors seems like a pain.)
What's the most idiomatic approach?
The convention in Go is to return (value, error) and if error != nil then value is (or may be) invalid.
If you have special errors you need to do something with (like io.EOF) then making a specific error is normal practice. So I would say your 3rd example is the most idiomatic, if you want to do something different for ColorNotFound.
var ColorNotFound = errors.New(...)
func findColor(name string) (RGB, error) {
// ...
}
c, err := findColor(...)
if err == ColorNotFound {
// Do something special if ColorNotFound...
} else if err != nil {
// Some other kind of error...
}
You could make findColor return *RGB and then compare it to nil:
c, err := findColor(name)
if err != nil { /* Handle error. */ }
if c == nil { /* Handle no color. */ }
This is unsafe though, since if you try to call methods on a nil pointer, they can cause a panic.
I'd recommend sticking with a special ErrColorNotFound approach.
Ok, so I've been having difficulties with the type conversion of a string to byte write. This is the compiler error:
cannot use row[5] (type uint8) as type string in function argument
cannot use &v (type *Field) as type int in function argument
This is an example of row[5]: $15,000.00
Ive declared a struct:
type Field struct {
Eia uint8
}
here is the main implementation:
for {
record, err := reader.Read()
if err == io.EOF {
break
} else if err != nil {
panic(err)
}
var v Field
for _, row := range record {
eia, err := strconv.ParseInt(row[5], 8, &v) // Estimated Incoming Amount
if err == nil {
fmt.Printf("%+v\n", v)
} else {
fmt.Println(err)
fmt.Printf("%+v\n", v)
}
Can anyone please explain to me how strconv can convert the row to a integer?
If you made a complete example on http://play.golang.org/ it'd be easier to give you a complete solution.
ParseInt() takes the string (you might have to do string(row[5])), the base (you probably meant 10) and the bitsize (that's where you should put 8).
It retuns an int (eia), it doesn't put it into the struct as it looks like you are trying.
Instead do if err == nil { v.Eia = eia }.