Difference in func with receiver and param golang - go

I try to create marshal func for struct Item.
So the question is, why the first example gives stackoverflow for goroutine and second works correctly?
Call method with receiver
type Item struct{
a int
}
func some(items []Item){
for _,i:=range items{
buf,err:=i.MarhsalBinary()
fmt.Println(buf.Bytes(), err)
}
func (i Item) MarshalBinary() ([]byte, error) {
var (
buf bytes.Buffer
err error
)
if err = gob.NewEncoder(&buf).Encode(i); err != nil { // endless loop here
return nil, err
}
return buf.Bytes(), err
}
This is pass by value.
type Item struct{
a int
}
func some(items []Item){
for _,i:=range items{
buf,err:=MarhsalBinary(i)
fmt.Println(buf.Bytes(), err)
}
func MarshalBinary(i Item) ([]byte, error) {
var (
buf bytes.Buffer
err error
)
if err = gob.NewEncoder(&buf).Encode(i); err != nil { // works correctly
return nil, err
}
return buf.Bytes(), err
}

Related

Default HTTP file server - modifications of the content

How can I compose the default Go HTTP file server (serve if exists, show file listing otherwise) with additional HTML?
Sample http.go with default file server:
package main
import "net/http"
func main() {
http.Handle("/", http.FileServer(http.Dir(".")))
http.ListenAndServe(":8090", nil)
}
Loading the default page (http://localhost:8090) gives something like:
<pre>LICENSE
README.md
studio.jpg
</pre>
I found it is declared at fs.go.
I want to keep that section, but with my own header and footer (preferably without copying the dirList function and making small changes):
<title>My files</title>
<pre>LICENSE
README.md
studio.jpg
</pre>
<p>And that's all, folks!</p>
Based on this answer, you can implement own FileSystem for a FileServer
This implementation is very buggy at best, and you should probably never ever use it, but it should show you how the FileSystem interface can be implemented for arbitrary 'files'.
type InMemoryFS map[string]http.File
type InMemoryFile struct {
at int64
Name string
data []byte
fs InMemoryFS
}
func NewFile(name string, data []byte) *InMemoryFile {
return &InMemoryFile{at: 0,
Name: name,
data: data,
fs: make(InMemoryFS)}
}
// Implements the http.File interface
func (f *InMemoryFile) Close() error {
return nil
}
func (f *InMemoryFile) Stat() (os.FileInfo, error) {
return &InMemoryFileInfo{f}, nil
}
func (f *InMemoryFile) Readdir(count int) ([]os.FileInfo, error) {
return nil, nil
}
func (f *InMemoryFile) Read(b []byte) (int, error) {
i := 0
for f.at < int64(len(f.data)) && i < len(b) {
b[i] = f.data[f.at]
i++
f.at++
}
return i, nil
}
func (f *InMemoryFile) Seek(offset int64, whence int) (int64, error) {
switch whence {
case 0:
f.at = offset
case 1:
f.at += offset
case 2:
f.at = int64(len(f.data)) + offset
}
return f.at, nil
}
type InMemoryFileInfo struct {
file *InMemoryFile
}
// Implements os.FileInfo
func (s *InMemoryFileInfo) Name() string { return s.file.Name }
func (s *InMemoryFileInfo) Size() int64 { return int64(len(s.file.data)) }
func (s *InMemoryFileInfo) Mode() os.FileMode { return os.ModeTemporary }
func (s *InMemoryFileInfo) ModTime() time.Time { return time.Time{} }
func (s *InMemoryFileInfo) IsDir() bool { return false }
func (s *InMemoryFileInfo) Sys() interface{} { return nil }
// CustomFsDecorator: is `http.FileSystem` decorator
type CustomFsDecorator struct {
http.FileSystem
}
func (fs CustomFsDecorator) Open(name string) (http.File, error) {
file, err := fs.FileSystem.Open(name)
if err != nil {
return nil, err
}
info, err := file.Stat()
if err != nil {
return nil, err
}
if info.IsDir() {
return file, nil
}
b, err := io.ReadAll(file)
if err != nil {
return nil, err
}
buf := new(bytes.Buffer)
// add header's lines
_, err = buf.Write([]byte("<title>My files</title>\n"))
if err != nil {
return nil, err
}
_, err = buf.Write(b)
if err != nil {
return nil, err
}
// add footer's lines
_, err = buf.Write([]byte("\n<p>And that's all, folks!</p>"))
if err != nil {
return nil, err
}
return NewFile(info.Name(), buf.Bytes()), nil
}
func Test(t *testing.T) {
cfsys := CustomFsDecorator{FileSystem: http.Dir("./static")}
fsys := http.FileServer(cfsys)
req := httptest.NewRequest(http.MethodGet, "/some.html", nil)
w := httptest.NewRecorder()
fsys.ServeHTTP(w, req)
res := w.Result()
defer func() {
_ = res.Body.Close()
}()
data, err := io.ReadAll(res.Body)
if err != nil {
t.Errorf("expected error to be nil got %v", err)
}
fmt.Println(string(data))
}
👇🏻
<title>My files</title>
<pre>LICENSE
README.md
studio.jpg
</pre>
<p>And that's all, folks!</p>
PLAYGROUND

How to set slice interface values with reflection

I would like to build a function that takes a generic pointer array and fill that list based on mongo results.
I don't know how to set the value I got from mongo into my pointer array. In the below attempt, program panics with following error : reflect.Set: value of type []interface {} is not assignable to type []Person
When I print total / documents found, it corresponds to what I am expecting. So I think question is about reflection.
func getListWithCount(ctx context.Context, receiver interface{}) (int, error) {
//my mongo query here
var mongoResp struct {
Total int `bson:"total"`
Documents interface{} `bson:"documents"`
}
if err := cursor.Decode(&mongoResp); err != nil {
return 0, err
}
receiverValue := reflect.ValueOf(receiver)
docs := []interface{}(mongoResp.Documents.(primitive.A))
receiverValue.Elem().Set(reflect.ValueOf(docs))
return mongoResp.Total, nil
}
type Person struct {
Name string `bson:"name"`
}
func main() {
var persons []Person
count, err := getListWithCount(context.Background(), &persons)
if err != nil {
log.Fatal(err)
}
fmt.Println(count)
fmt.Println(persons)
}
You should be able to decode first into bson.RawValue and then Unmarshal it into the receiver.
func getListWithCount(ctx context.Context, receiver interface{}) (int, error) {
//my mongo query here
var mongoResp struct {
Total int `bson:"total"`
Documents bson.RawValue `bson:"documents"`
}
if err := cursor.Decode(&mongoResp); err != nil {
return 0, err
}
if err := mongoResp.Documents.Unmarshal(receiver); err != nil {
return 0, err
}
return mongoResp.Total, nil
}
You can also implement it as a custom bson.Unmarshaler.
type MongoResp struct {
Total int `bson:"total"`
Documents interface{} `bson:"documents"`
}
func (r *MongoResp) UnmarshalBSON(data []byte) error {
var temp struct {
Total int `bson:"total"`
Documents bson.RawValue `bson:"documents"`
}
if err := bson.Unmarshal(data, &temp); err != nil {
return err
}
r.Total = temp.Total
return temp.Documents.Unmarshal(r.Documents)
}
With that you would use it in the function like so:
func getListWithCount(ctx context.Context, receiver interface{}) (int, error) {
//my mongo query here
mongoResp := MongoResp{Documents: receiver}
if err := cursor.Decode(&mongoResp); err != nil {
return 0, err
}
return mongoResp.Total, nil
}
Dynamically create a struct type that matches the queried document. See commentary below for details.
func getListWithCount(receiver interface{}) (int, error) {
dst := reflect.ValueOf(receiver).Elem()
// Your mongo query here
// Create a struct type that matches the document.
doct := reflect.StructOf([]reflect.StructField{
reflect.StructField{Name: "Total", Type: reflect.TypeOf(0), Tag: `bson:"total"`},
reflect.StructField{Name: "Documents", Type: dst.Type(), Tag: `bson:"documents"`},
})
// Decode to a value of the type.
docp := reflect.New(doct)
if err := cursor.Decode(docp.Interface()); err != nil {
return 0, err
}
docv := docp.Elem()
// Copy the Documents field to *receiver.
dst.Set(docv.Field(1))
// Return the total
return docv.Field(0).Interface().(int), nil
}
there is no need to use reflect here, you can decode it directly to your Person slices
func getPersons(ctx context.Context, coll *mongo.Collection, results interface{}) error {
cur, err := coll.Find(ctx, bson.D{})
if err != nil {
return err
}
err = cur.All(ctx, results)
if err != nil {
return err
}
return nil
}
and the len is the count of the results.
err = getPersons(ctx, coll, &persons)
require.NoError(t, err)
t.Logf("Got %d persons: %v", len(persons), persons)
see https://gist.github.com/xingyongtao/459f92490bdcbf7d5afe9f5d1ae6c04a

Mocking open in go…

I have been following this answer trying to mock open. I have got exactly no where.
This is the test code I have:
func (m mockedFS) Open(name string) (file, error) {
if m.reportErrOpen {
return nil, errors.New("Fake failure")
}
mockedFile := mockIORead{}
mockedFile.On("ReadAll", mock.AnythingOfType("[]uint8")).Return(0, fmt.Errorf("error reading"))
mockedFile.On("Read", mock.AnythingOfType("[]byte")).Return(0, errors.New("NON"))
return mockedFile, nil
}
type mockIORead struct {
mock.Mock
reportErr bool // Tells if this mocked FS should return error in our tests
reportSize int64 // Tells what size should Stat() report in our test
}
func (m mockIORead) Read(b []byte) (n int, err error) {
if m.reportErr {
return 0, errors.New("A fake failure")
}
s := "Fear the old blood"
copy(b[:], s)
return 0, nil
}
func (m mockIORead) Close() error {
return nil
}
func (m mockIORead) ReadAt([]byte, int64) (int, error) {
return 0, nil
}
func (m mockIORead) Seek(int64, int) (int64, error) {
return 0, nil
}
func (m mockIORead) Stat() (os.FileInfo, error) {
if m.reportErr {
return nil, os.ErrNotExist
}
return mockedFileInfo{size: m.reportSize}, nil
}
func TestOok(t *testing.T) {
oldFs := fs
// Create and "install" mocked fs:
mfs := &mockedFS{}
fs = mfs
// Make sure fs is restored after this test:
defer func() {
fs = oldFs
}()
mfs.reportErr = false
mfs.reportErrOpen = false
token, err := Ook("fake")
assert.NotNil(t, err)
assert.Equal(t, "Fear the old blood", token)
}
And this is the code under test:
func Ook(name string) (string, error) {
_, err := fs.Stat(name)
if err != nil {
return "", nil
}
file, err := fs.Open(name)
if err != nil {
return "", errors.Wrap(err, "Cannot open token file")
}
defer file.Close()
_, err = ioutil.ReadAll(file)
fmt.Print("PING\n")
if err != nil {
return "", errors.Wrap(err, "Could not read token")
}
return "Fear the old blood", nil
//return string(token), nil
}
What the hell am I doing wrong?
The first error is that your mockIORead.Read() returns wrong values. It must return the number of read bytes (bytes written to the slice argument) (e.g. what copy() would return).
Next, mockIORead.Read() must be stateful! Reader.Read() might be called several times, there is no guarantee the passed slice can accommodate all the data you want to return (via the passed b slice).
So mockIORead must store the data you want to return, and it must remember how much of them has been delivered so far, so the next Read() call can continue from there.
An easy implementation of this is to utilize bytes.Buffer:
type mockIORead struct {
mock.Mock
reportErr bool // Tells if this mocked FS should return error in our tests
reportSize int64 // Tells what size should Stat() report in our test
content *bytes.Buffer
}
When returning such a mockIORead, initialize content with the content you wish to return:
func (m mockedFS) Open(name string) (file, error) {
if m.reportErrOpen {
return nil, errors.New("Fake failure")
}
mockedFile := mockIORead{
content: bytes.NewBufferString("Fear the old blood"),
}
return mockedFile, nil
}
And thanks to the available bytes.Buffer.Read() method, the mockIORead.Read() implementation can be as simple as this:
func (m mockIORead) Read(b []byte) (n int, err error) {
if m.reportErr {
return 0, errors.New("A fake failure")
}
return m.content.Read(b)
}
The Ook() function itself should not try to "stat" as you haven't mocked it (and so calling the original os.Stat() will likely yield an error for the "fake" file name used in the test):
func Ook(name string) (string, error) {
file, err := fs.Open(name)
if err != nil {
return "", errors.Wrap(err, "Cannot open token file")
}
defer file.Close()
token, err := ioutil.ReadAll(file)
fmt.Print("PING\n")
if err != nil {
return "", errors.Wrap(err, "Could not read token")
}
return string(token), nil
}
And the testing code:
func TestOok(t *testing.T) {
oldFs := fs
// Create and "install" mocked fs:
mfs := &mockedFS{}
fs = mfs
// Make sure fs is restored after this test:
defer func() {
fs = oldFs
}()
mfs.reportErr = false
mfs.reportErrOpen = false
token, err := Ook("fake")
assert.Nil(t, err)
assert.Equal(t, "Fear the old blood", token)
}
Which yields a successful ("OK") test.

cannot use function (type func()) as type in argument

package main
import (
"log"
"strings"
"asl.com/asl"
)
/*
Trivial service to demonstrate chaining service together
Message starts in originator, travels through a couple formatters, and then gets back to originator
*/
type MessageTest struct {
Body string `json:"body"`
}
var s *asl.Service
func main() {
var (
err error
cid string
)
//var m MessageDelivery
var g asl.MessageHandler
g = UpperCaseHandler
// UpperCaser := asl.NewService("UpperCaser", "", false)
UpperCaser := asl.NewService("UpperCaser")
if err = UpperCaser.ConsumeFunc("asl-service-uc", []string{"asl-service-uc"},func() interface{} {
return ""
},g); err != nil {
log.Fatalf("Error starting consumer: %v", err)
}
// Repeater := asl.NewService("Repeater", "", false)
Repeater := asl.NewService("Repeater")
if err = Repeater.ConsumeFunc("asl-service-repeat", []string{"asl-service-repeat"}, func() interface{} {
return ""
}, RepeatHandler); err != nil {
//if err = Repeater.ConsumeFunc("asl-service-repeat", []string{"asl-service-repeat"}, mg asl.MessageGenerator, mh asl.MessageHandler); err != nil {
log.Fatalf("Error starting consumer: %v", err)
}
// originator := asl.NewService("Originator", "", false)
originator := asl.NewService("Originator")
deliveryChan := make(chan asl.MessageDelivery)
m := asl.MessagePublishing{
Message: MessageTest{"this is a test"},
RoutingKeys: []string{"asl-service-uc", "asl-service-repeat"},
}
if cid, err = originator.RPCPublish(m, deliveryChan); err != nil {
log.Fatalf("Failed to publish: %v", err)
}
message := <-deliveryChan
log.Printf("Originator Got: %+v", message.Message)
originator.RemoveQueue(cid)
UpperCaser.Wait()
}
func UpperCaseHandler(md asl.MessageDelivery) {
s.Reply(MessageTest{strings.ToUpper(md.Message.(string))}, md.Delivery)
}
func RepeatHandler(md asl.MessageDelivery) {
s.Reply(MessageTest{strings.Repeat(md.Message.(string), 5)}, md.Delivery)
}
package asl
Error
./chains.go:26:10: cannot use UpperCaseHandler (typefunc(asl.MessageDelivery)) as type asl.MessageHandler in
assignment
./chains.go:37:86: cannot use RepeatHandler (type func(asl.MessageDelivery)) as type asl.MessageHandler in
argument to Repeater.ConsumeFunc
type MessageDelivery struct {
Delivery amqp.Delivery
Message interface{}
Error error
Context *Context
}
type MessageGenerator func() interface{}
type MessageHandler func(MessageDelivery) (interface{}, error)
I tried running the code,where am i going wrong,how do i right pass function as argument to another function. The function returns
interface and error. Though function is taking MessageDelivery Struct as argument,function signature is same.where am i going wrong
You are passing the function as an argument correctly but they do not match the expected signature. Change your functions to:
func UpperCaseHandler(md busboy.MessageDelivery) (interface{}, error} {
s.Reply(MessageTest{strings.ToUpper(md.Message.(string))}, md.Delivery)
return nil, nil
}
func RepeatHandler(md busboy.MessageDelivery) (interface{}, error} {
s.Reply(MessageTest{strings.Repeat(md.Message.(string), 5)}, md.Delivery)
return nil, nil
}

Trying to test write file from goroutines in Go

Well, part of my code was working without a method approach, I'm trying to test
append text to a file and reading from goroutines, but I'm stuck here trying to
write it.
What is wrong? the file is created, but I can't append text to it, maybe something obvious, but seems I'm blind, maybe I'm failing understanding some language concepts...
package main
import (
"bufio"
"fmt"
"os"
"sync"
"time"
)
var w sync.WaitGroup
type Buffer struct {
F *os.File
}
func (buff *Buffer) Open(pathName string) (err error) {
buff.F, err = os.OpenFile(pathName, os.O_APPEND|os.O_CREATE, 0666)
if err != nil {
return
}
fmt.Println("Open() ok")
return nil
}
func (buff *Buffer) Close() (err error) {
err = buff.F.Close()
if err != nil {
return
}
fmt.Println("Close() ok")
return nil
}
func (buff *Buffer) Push(data string) (err error) {
w := bufio.NewWriter(buff.F)
_, err = fmt.Fprintf(w, "data=%s", data)
if err != nil {
return
}
w.Flush()
fmt.Println("Push() ok")
return nil
}
func checkErr(err error) {
if err != nil {
fmt.Println(err.Error())
}
}
func worker() {
var err error
buffer := new(Buffer)
err = buffer.Open("test")
checkErr(err)
err = buffer.Push("data\n")
checkErr(err)
time.Sleep(5 * time.Second)
err = buffer.Close()
checkErr(err)
w.Done()
}
func main() {
w.Add(2)
go worker()
go worker()
w.Wait()
}
Thanks
Open the file like this:
buff.F, err = os.OpenFile(pathName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
The write flag is required to write to the file.
You missed the write error because the return from bufio Flush is ignored. Change Push to:
func (buff *Buffer) Push(data string) (err error) {
w := bufio.NewWriter(buff.F)
_, err = fmt.Fprintf(w, "data=%s", data)
if err != nil {
return
}
err = w.Flush()
if err != nil {
return err
}
fmt.Println("Push() ok")
return nil
}
To cleanly append data without intermixing with other pushes, the data must be written with a single call to the file Write method. Use a bytes.Buffer instead of a bufio.Writer to ensure a single call to the file Write method:
func (buff *Buffer) Push(data string) (err error) {
var b bytes.Buffer
_, err = fmt.Fprintf(&b, "data=%s", data)
if err != nil {
return
}
_, err := buff.F.Write(b.Bytes())
if err != nil {
return err
}
fmt.Println("Push() ok")
return nil
}

Resources