Related
I have a struct where one of the fields is a map with the key being another struct. I am using Go version 16.1 and the map created that way is supposedly supported in this version (albeit unsure when field is a map). Here is the relevant code and test that fails with unsupported type error.
package model
import (
"encoding/json"
slog "github.com/go-eden/slf4go"
)
type User struct{
ID string `json:"id" bson:"_id,omitempty"`
FName string `json:"fName"`
MName string `json:"mName"`
LName string `json:"lName"`
Jobs map[*Job]float64 `json:"jobs,omitempty"`
Password string `json:"password"`
IsAdmin bool `json:"isAdmin"`
}
func (u *User)IsModel()bool{
return true
}
func(u * User)ToJSON()string{
//var jsonUser string;
b,err := json.Marshal(u)
if err !=nil {
slog.Info(err)
return err.Error()
}
return string(b)
}
func (u * User)FromJSON(jsonString string)bool{
err := json.Unmarshal([]byte(jsonString),u)
if err != nil{
return false
}
return true
}
and the struct job is:
package model
import "encoding/json"
type Job struct{
ID string `json:"id" bson:"_id,omitempty"`
Name string `json:"name"`
}
func(j *Job)IsModel()bool{
return true
}
func(j *Job) ToJSON()string{
b,err := json.Marshal(j)
if err !=nil {
return err.Error()
}
return string(b)
}
func(j *Job) FromJSON(jsonString string)bool{
err := json.Unmarshal([]byte(jsonString),j)
if err != nil{
return false
}
return true
}
Finally the test that fails is the following (marshalling fails after I add the job to the jobs map):
package model
import (
"fmt"
"reflect"
"testing"
)
func TestUser_ToJSON(t *testing.T) {
user := &User{
ID: "",
FName: "Mike",
MName: "",
LName: "Clovis",
Jobs: make(map[*Job]float64),
Password: "xyz",
IsAdmin: true,
}
newUser := &User{}
fmt.Println(user.ToJSON())
job := &Job{
ID: "1",
Name: "Cashier",
}
user.Jobs[job] = 12.99
fmt.Println("User as json")
fmt.Println(user.ToJSON())
newUser.FromJSON(user.ToJSON())
fmt.Println(newUser)
if reflect.DeepEqual(user,newUser) {
fmt.Println(newUser)
}else{
fmt.Println(user)
fmt.Println(newUser)
t.Error("Cannot marshal and unmarshal User struct")
}
dUser := *user
fmt.Println(dUser)
}
By my reading of the documentation this should work! If anyone has a suggestion or workaround I would appreciate it. As an FYI I have tried making the map with the key being both a job and as a pointer to the job with the same results.
I try to convert a make(map[string]string) into a yaml like that:
Yaml Output desire:
items:
keys1:value1
keys2:value2
keys3:value3
keys4:value4
The keys,values are this listKey map of string. J = string = {"key1":"value1","key2":"value2" }
type Items struct {
items string
ItemsValues map[string][]string
}
func ConvertToYelm(j string){
y := Items{}
var dataJson map[string]string
err := json.Unmarshal([]byte(j), &dataJson)
if err != nil {
fmt.Println(err)
return
}
listKey := make(map[string]string)
for k := range dataJson{
listKey[k] = k
}
yelm, err := yaml.Marshal(listKey)
if err != nil {
fmt.Println(err)
return
}
err = yaml.Unmarshal(yelm, Items)
if err != nil {
fmt.Println(err)
return
}
yeml2, err := yaml.Marshal(&yelm)
fmt.Printf ("%s", string(yeml2))
To be honest, I'm a little bit lost here, thank you for the help
To get the exact YAML from your post:
items:
keys1:value1
keys2:value2
keys3:value3
keys4:value4
You can do this (Go Playground):
package main
import (
"fmt"
"gopkg.in/yaml.v2"
)
type ItemsStruct struct {
Items map[string]string `yaml:"items"`
}
func main() {
itms := &ItemsStruct{Items: map[string]string{
"keys1": "value1",
"keys2": "value2",
"keys3": "value3",
"keys4": "value4"}}
yamlBytes, err := yaml.Marshal(itms)
if err != nil {
//handle error
}
fmt.Println(string(yamlBytes))
}
And just to add, I see your code is decoding this JSON {"key1":"value1", "key2":"value2", ... } and then encoding it as YAML in your specified format. Here is the Go Playground for that.
I cannot figure out how to make the requests for "access", "clock" and "audit" with the required parameters. Could you give me some guidance on how to make these requests?
package main
import (
"crypto/sha256"
"crypto/subtle"
"encoding/base64"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"sync"
"time"
)
const secret = "galumphing"
// Required parameters for the request
type params struct {
Act string
Nonce string
Signature string
Timeout int64
Offset int
}
var (
auditLock sync.Mutex
auditBase int
auditLog []string
)
var (
seenLock sync.Mutex
seenMap = map[string]bool{}
)
var (
clockAsk = make(chan bool)
clockTime = make(chan int64, 1)
)
func main() {
// Endpoints available
http.HandleFunc("/903/access", handleAccess)
http.HandleFunc("/903/clock", handleClock)
http.HandleFunc("/903/audit", handleAudit)
err := http.ListenAndServe(os.Args[1], nil)
if err != nil {
log.Fatal(err)
}
}
func checkCapacity(w http.ResponseWriter) (ok bool) {
auditLock.Lock()
defer auditLock.Unlock()
if len(auditLog) > 10 {
w.WriteHeader(http.StatusServiceUnavailable)
return
}
ok = true
return
}
func audit(r *http.Request, params params, ok bool) {
auditLock.Lock()
defer auditLock.Unlock()
auditLog = append(auditLog, fmt.Sprintf("%v %q %q", ok, r.URL.Path, params.Act))
}
func parse(w http.ResponseWriter, r *http.Request) (params params, ok bool) {
defer func() {
if !ok {
audit(r, params, false)
}
}()
w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Max-Age", "3600")
if r.Method != "POST" {
w.Header().Set("Allow", "POST, OPTIONS")
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
} else {
w.WriteHeader(http.StatusMethodNotAllowed)
}
return
}
err := json.NewDecoder(r.Body).Decode(¶ms)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
log.Printf("%s: %v", r.URL.Path, err)
return
}
h := sha256.New()
fmt.Fprintf(h, "%s\r\n%s\r\n%s\r\n%s", r.URL.Path, params.Act, params.Nonce, secret)
sig := base64.StdEncoding.EncodeToString(h.Sum(nil))
if subtle.ConstantTimeCompare([]byte(sig), []byte(params.Signature)) != 1 {
w.WriteHeader(http.StatusForbidden)
return
}
seenLock.Lock()
seen := seenMap[params.Signature]
if !seen {
seenMap[params.Signature] = true
}
seenLock.Unlock()
if seen {
w.WriteHeader(http.StatusForbidden)
return
}
ok = true
return
}
func handleAccess(w http.ResponseWriter, r *http.Request) {
if !checkCapacity(w) {
return
}
params, ok := parse(w, r)
if !ok {
return
}
switch params.Act {
case "begin":
if params.Timeout < 0 || params.Timeout > 250000 {
w.WriteHeader(http.StatusBadRequest)
audit(r, params, false)
return
}
timer := time.NewTimer(time.Duration(params.Timeout) * time.Microsecond)
select {
case clockAsk <- true: // https://golang.org/ref/spec#Send_statements
w.WriteHeader(http.StatusNoContent)
audit(r, params, true)
case <-timer.C:
w.WriteHeader(http.StatusConflict)
audit(r, params, false)
return
}
go func() {
<-timer.C
select {
case <-clockTime:
default:
}
}()
case "end":
if params.Timeout < 0 {
w.WriteHeader(http.StatusBadRequest)
audit(r, params, false)
return
}
timer := time.NewTimer(time.Duration(params.Timeout) * time.Microsecond)
select {
case value := <-clockTime: // https://golang.org/ref/spec#Receive_operator
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, value)
audit(r, params, true)
case <-timer.C:
w.WriteHeader(http.StatusConflict)
audit(r, params, false)
}
default:
w.WriteHeader(http.StatusBadRequest)
audit(r, params, false)
}
}
func handleClock(w http.ResponseWriter, r *http.Request) {
if !checkCapacity(w) {
return
}
params, ok := parse(w, r)
if !ok {
return
}
switch params.Act {
case "observe":
if params.Timeout != 0 {
w.WriteHeader(http.StatusBadRequest)
audit(r, params, false)
return
}
select {
case <-clockAsk: // https://golang.org/ref/spec#Receive_operator
select {
case clockTime <- time.Now().Unix(): // https://golang.org/ref/spec#Send_statements
default:
}
default:
}
w.WriteHeader(http.StatusNoContent)
audit(r, params, true)
default:
w.WriteHeader(http.StatusBadRequest)
audit(r, params, false)
}
}
func handleAudit(w http.ResponseWriter, r *http.Request) {
params, ok := parse(w, r)
if !ok {
return
}
ok = false
func() {
auditLock.Lock()
defer auditLock.Unlock()
switch params.Act {
case "":
if params.Offset != 0 {
w.WriteHeader(http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, auditBase)
case "burble":
if params.Offset < auditBase || params.Offset > auditBase+len(auditLog) {
w.WriteHeader(http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
for i := params.Offset - auditBase; i < len(auditLog); i++ {
fmt.Fprintf(w, "%d %s\n", auditBase+i, auditLog[i])
}
case "chortle":
if params.Offset > auditBase+len(auditLog) {
w.WriteHeader(http.StatusBadRequest)
return
}
if params.Offset > auditBase {
auditLog = auditLog[params.Offset-auditBase:] // https://golang.org/ref/spec#Slice_expressions
auditBase = params.Offset
}
w.WriteHeader(http.StatusNoContent)
default:
w.WriteHeader(http.StatusBadRequest)
return
}
ok = true
}()
audit(r, params, ok)
}
There are a few ways to go about this. You can either use a tool like Postman, or simply just use curl from the command line to send a post request to your endpoint. Here is the curl command that I used to hit the endpoint (I am using port 8080 for my testing), the bit after the -d flag is the request body:
curl -X POST http://localhost:8080/903/access -d '{"Act": "act", "Nonce": "nonce", "Signature": "signature", "TimeOut": 64, "Offset": 0}'
I did modify the code to cut out most of the logic. This request just shows an example of how the current code can parse the request body. If you are interested more in how some of the other logic works in the handler functions, let me know and I can edit my response. Here is the code that I used for my testing:
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
)
// Required parameters for the request
type params struct {
Act string
Nonce string
Signature string
Timeout int64
Offset int
}
func main() {
// Endpoints available
http.HandleFunc("/903/access", handleAccess)
err := http.ListenAndServe(os.Args[1], nil)
if err != nil {
log.Fatal(err)
}
}
func parse(w http.ResponseWriter, r *http.Request) (params params, ok bool) {
w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Max-Age", "3600")
if r.Method != "POST" {
w.Header().Set("Allow", "POST, OPTIONS")
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
} else {
w.WriteHeader(http.StatusMethodNotAllowed)
}
return
}
err := json.NewDecoder(r.Body).Decode(¶ms)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
log.Printf("%s: %v", r.URL.Path, err)
return
}
return
}
func handleAccess(w http.ResponseWriter, r *http.Request) {
params, _ := parse(w, r)
fmt.Printf("%+v\n", params)
}
Here is the output that I received when all is said and done:
{Act:act Nonce:nonce Signature:signature Timeout:64 Offset:0}
How can I get all addresses and masks from local interfaces in golang?
I need the actual network mask configured along with every IP address.
This code does not show the network masks in Windows 7:
package main
import (
"fmt"
"log"
"net"
)
func localAddresses() {
ifaces, err := net.Interfaces()
if err != nil {
log.Print(fmt.Errorf("localAddresses: %v\n", err.Error()))
return
}
for _, i := range ifaces {
addrs, err := i.Addrs()
if err != nil {
log.Print(fmt.Errorf("localAddresses: %v\n", err.Error()))
continue
}
for _, a := range addrs {
log.Printf("%v %v\n", i.Name, a)
}
}
}
func main() {
localAddresses()
}
UPDATE: This issue has been fixed in Go: https://github.com/golang/go/issues/5395
There are multiply types of addresses that a net.Interface might have. The Addr is just an interface which may contain a net.IPAddr. But with a type assertion or type switch you can access the actual address type:
package main
import (
"fmt"
"net"
)
func localAddresses() {
ifaces, err := net.Interfaces()
if err != nil {
fmt.Print(fmt.Errorf("localAddresses: %+v\n", err.Error()))
return
}
for _, i := range ifaces {
addrs, err := i.Addrs()
if err != nil {
fmt.Print(fmt.Errorf("localAddresses: %+v\n", err.Error()))
continue
}
for _, a := range addrs {
switch v := a.(type) {
case *net.IPAddr:
fmt.Printf("%v : %s (%s)\n", i.Name, v, v.IP.DefaultMask())
}
}
}
}
func main() {
localAddresses()
}
Edit
Unfortunately the net package doesn't return the Mask of the address. So, you will have to do the low level syscalls that the net package does. The code below is an example, but parsing of the ip and the mask still needs to be done:
package main
import (
"fmt"
"net"
"os"
"syscall"
"unsafe"
)
func getAdapterList() (*syscall.IpAdapterInfo, error) {
b := make([]byte, 1000)
l := uint32(len(b))
a := (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0]))
// TODO(mikio): GetAdaptersInfo returns IP_ADAPTER_INFO that
// contains IPv4 address list only. We should use another API
// for fetching IPv6 stuff from the kernel.
err := syscall.GetAdaptersInfo(a, &l)
if err == syscall.ERROR_BUFFER_OVERFLOW {
b = make([]byte, l)
a = (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0]))
err = syscall.GetAdaptersInfo(a, &l)
}
if err != nil {
return nil, os.NewSyscallError("GetAdaptersInfo", err)
}
return a, nil
}
func localAddresses() error {
ifaces, err := net.Interfaces()
if err != nil {
return err
}
aList, err := getAdapterList()
if err != nil {
return err
}
for _, ifi := range ifaces {
for ai := aList; ai != nil; ai = ai.Next {
index := ai.Index
if ifi.Index == int(index) {
ipl := &ai.IpAddressList
for ; ipl != nil; ipl = ipl.Next {
fmt.Printf("%s: %s (%s)\n", ifi.Name, ipl.IpAddress, ipl.IpMask)
}
}
}
}
return err
}
func main() {
err := localAddresses()
if err != nil {
panic(err)
}
}
Some code shamelessly borrowed from interface_windows.go. Even comments are left intact
I'm modifying #ANisus answer and get all interfaces & masks (tested on Windows 10 & WSL in it (Microsoft Ubuntu 16.04.5 LTS):
package main
import (
"fmt"
"net"
)
func localAddresses() {
ifaces, err := net.Interfaces()
if err != nil {
fmt.Print(fmt.Errorf("localAddresses: %+v\n", err.Error()))
return
}
for _, i := range ifaces {
addrs, err := i.Addrs()
if err != nil {
fmt.Print(fmt.Errorf("localAddresses: %+v\n", err.Error()))
continue
}
for _, a := range addrs {
switch v := a.(type) {
case *net.IPAddr:
fmt.Printf("%v : %s (%s)\n", i.Name, v, v.IP.DefaultMask())
case *net.IPNet:
fmt.Printf("%v : %s [%v/%v]\n", i.Name, v, v.IP, v.Mask)
}
}
}
}
func main() {
localAddresses()
}
This should give you the ipnet you're looking for.
ip, ipnet, err := net.ParseCIDR(a.String())
I know this post for Windows 7, but if you use Mac OS X hope this could help you.
Just Call GetNetMask("en0")
func GetNetMask(deviceName string) string {
switch runtime.GOOS {
case "darwin":
cmd := exec.Command("ipconfig", "getoption", deviceName, "subnet_mask")
out, err := cmd.CombinedOutput()
if err != nil {
return ""
}
nm := strings.Replace(string(out), "\n", "", -1)
log.Println("netmask=", nm, " OS=", runtime.GOOS)
return nm
default:
return ""
}
return ""
}
I have Go program that has a function defined. I also have a map that should have a key for each function. How can I do that?
I have tried this, but this doesn't work.
func a(param string) {
}
m := map[string] func {
'a_func': a,
}
for key, value := range m {
if key == 'a_func' {
value(param)
}
}
Are you trying to do something like this? I've revised the example to use varying types and numbers of function parameters.
package main
import "fmt"
func f(p string) {
fmt.Println("function f parameter:", p)
}
func g(p string, q int) {
fmt.Println("function g parameters:", p, q)
}
func main() {
m := map[string]interface{}{
"f": f,
"g": g,
}
for k, v := range m {
switch k {
case "f":
v.(func(string))("astring")
case "g":
v.(func(string, int))("astring", 42)
}
}
}
m := map[string]func(string, string)
Works if you know the signature (and all the funcs have the same signature)
I think this is cleaner/safer than using interface{}
You can define a type if functions are same interface.
package main
import "log"
type fn func (string)
func foo(msg string) {
log.Printf("foo! Message is %s", msg)
}
func bar(msg string) {
log.Printf("bar! Message is %s", msg)
}
func main() {
m := map[string] fn {
"f": foo,
"b": bar,
}
log.Printf("map is %v", m)
m["f"]("Hello")
m["b"]("World")
}
#Seth Hoenig's answer helped me best, but I just wanted to add that Go accepts functions with defined return value as well:
package main
func main() {
m := map[string]func(string) string{
"foo": func(s string) string { return s + "nurf" },
}
m["foo"]("baz") // "baznurf"
}
If you think it's ugly, you could always use a type (see #smagch's answer).
I used a map[string]func (a type, b *type) I passed a string to search the map and a pointer to modify the slice.
Hope that helps!
var Exceptions map[string]func(step string, item *structs.Item)
func SetExceptions() {
Exceptions = map[string]func(a string, i *structs.Item){
"step1": step1,
}
}
func RunExceptions(state string, item *structs.Item) {
method, methBool := Exceptions[state]
if methBool {
method(state, item)
}
}
func step1(step string, item *structs.Item) {
item.Title = "Modified"
}
Here is the way I made it work in my case:
package main
import (
"fmt"
)
var routes map[string]func() string
func main() {
routes = map[string]func() string{
"GET /": homePage,
"GET /about": aboutPage,
}
fmt.Println("GET /", pageContent("GET /"))
fmt.Println("GET /about", pageContent("GET /about"))
fmt.Println("GET /unknown", pageContent("GET /unknown"))
// Output:
// GET / Home page
// GET /about About page
// GET /unknown 404: Page Not Found
}
func pageContent(route string) string {
page, ok := routes[route]
if ok {
return page()
} else {
return notFoundPage()
}
}
func homePage() string {
return "Home page"
}
func aboutPage() string {
return "About page"
}
func notFoundPage() string {
return "404: Page Not Found"
}
https://play.golang.org/p/8_g6Di1OKZS
Hope this works for you(you can use interface{} instead any)
package main
import (
"fmt"
)
func toon(v any) {
fmt.Println(v)
}
func main() {
names := map[string]any{
"Function": toon,
}
names["Function"].(func(any))("a")
}