Can anyone help to parse HCL? - go

I'm going to parse HCL configuration file using this repository.
package main
import (
"fmt"
hclParser "github.com/hashicorp/hcl/hcl/parser"
)
const (
EXAMPLE_CONFIG_STRING = "log_dir = \"/var/log\""
)
func main() {
// parse HCL configuration
if astFile, err := hclParser.Parse([]byte(EXAMPLE_CONFIG_STRING)); err == nil {
fmt.Println(astFile)
} else {
fmt.Println("Parsing failed.")
}
}
How can I parse log_dir in this case?

github.com/hashicorp/hcl/hcl/parser is a low-level package. Use the high-level API instead:
package main
import (
"fmt"
"github.com/hashicorp/hcl"
)
type T struct {
LogDir string `hcl:"log_dir"`
}
func main() {
var t T
err := hcl.Decode(&t, `log_dir = "/var/log"`)
fmt.Println(t.LogDir, err)
}
There is also DecodeObject available if you really want to deal with the AST yourself.

Related

How can I get JSON from BSON without my keys all being named "Key"?

I'm trying to read from a database and then return the result to the user as json.
What's happening is that I'm getting output like:
[{"Key":"foo","Value":"bar"}]
When I would like to get:
"{"foo":"bar"}"
How do I get the former?
Example: (reading from the db and converting Raw to string are not shown)
package main
import (
"encoding/json"
"fmt"
"go.mongodb.org/mongo-driver/bson"
)
func main() {
var data = "{\"foo\":\"bar\"}"
var testInterface interface{}
e := bson.UnmarshalExtJSON([]byte(data), false, &testInterface)
if e != nil {
fmt.Println("err is ", e)
}
out, _ := json.Marshal(testInterface)
fmt.Println(string(out))
// prints: [{"Key":"foo","Value":"bar"}]
}
You need to provide a map instead of an interface to get value as a key-value pair.
Try:
package main
import (
"encoding/json"
"fmt"
"go.mongodb.org/mongo-driver/bson"
)
func main() {
var data = "{\"foo\":\"bar\"}"
var testInterface map[string]string
e := bson.UnmarshalExtJSON([]byte(data), false, &testInterface)
if e != nil {
fmt.Println("err is ", e)
}
out, _ := json.Marshal(testInterface)
fmt.Println(string(out))
// prints: {"foo":"bar"}
}

How to find full package import from CallExpr

The following method extracts all public method calls from the AST of a file. I need to find out the full package from the CallExpr, for example: ast.Inspect() is imported from "go/ast". I want to match the list of pkgsInclude strings with the imported package name:
func functionCalls(path string, node *ast.File, pkgsInclude []string) int {
fCalls := 0
ast.Inspect(node, func(n ast.Node) bool {
switch fCall := n.(type) {
case *ast.CallExpr:
if fun, ok := fCall.Fun.(*ast.SelectorExpr); ok {
if fun.Sel.IsExported() {
fCalls += 1
}
}
}
return true
})
return fCalls
}
To get fully qualified names, the code has to be type checked with the go/types package.
The go/types article by Alan Donovan goes into great detail on how to use the type checker properly, but here is the gist of it. I left a few type assertions in the Visit method for brevity. In production code you shouldn't assume specific node types.
package main
import (
"fmt"
"go/ast"
"go/importer"
"go/parser"
"go/token"
"go/types"
"log"
)
// code to parse. It includes two variants of calling a package function.
var code = `package main
import (
foo "io/ioutil"
. "io/ioutil"
)
func main() {
foo.ReadFile("")
ReadFile("")
}
`
func main() {
fset := &token.FileSet{}
f, err := parser.ParseFile(fset, "", code, 0)
if err != nil {
log.Fatal(err)
}
// info.Uses allows to lookup import paths for identifiers.
info := &types.Info{
Uses: make(map[*ast.Ident]types.Object),
}
// Type check the parsed code using the default importer.
// Use golang.org/x/tools/go/loader to check a program
// consisting of multiple packages.
conf := types.Config{Importer: importer.Default()}
pkg, err := conf.Check("main", fset, []*ast.File{f}, info)
if err != nil {
log.Fatal(err)
}
// Do something with ast, info, and possibly pkg
var _ = pkg
ast.Walk(v{info}, f)
}
type v struct {
info *types.Info
}
func (v v) Visit(node ast.Node) (w ast.Visitor) {
switch node := node.(type) {
case *ast.CallExpr:
// Get some kind of *ast.Ident for the CallExpr that represents the
// package. Then we can look it up in v.info. Where exactly it sits in
// the ast depends on the form of the function call.
switch node := node.Fun.(type) {
case *ast.SelectorExpr: // foo.ReadFile
pkgID := node.X.(*ast.Ident)
fmt.Println(v.info.Uses[pkgID].(*types.PkgName).Imported().Path())
case *ast.Ident: // ReadFile
pkgID := node
fmt.Println(v.info.Uses[pkgID].Pkg().Path())
}
}
return v
}
// Output:
// io/ioutil
// io/ioutil

go/types.Identical cannot compare two same types in other package?

I write some code to get special type by "go/types"
import (
"go/ast"
"go/importer"
"go/parser"
"go/token"
"go/types"
"log"
)
const (
Src = `
package types
import (
"io"
"net/http"
)
var (
IOReader io.Reader
Err error
StatusCode int
Request *http.Request
Response *http.Response
)
`
)
const (
TypeIOReader = "IOReader"
TypeErr = "Err"
TypeStatusCode = "StatusCode"
TypeRequest = "Request"
TypeResponse = "Response"
)
func GetType(name string) types.Type {
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "types.go", Src, 0)
if err != nil {
log.Fatal(err)
}
conf := types.Config{Importer: importer.Default()}
pkg, err := conf.Check("impler/types", fset, []*ast.File{file}, nil)
return pkg.Scope().Lookup(name).Type()
}
The function GetType(name string) types.Type can make it.
And when I compare two types, I get some strange results
log.Println(types.Identical(GetType(TypeResponse), GetType(TypeResponse)))
log.Println(types.Identical(GetType(TypeIOReader), GetType(TypeIOReader)))
log.Println(types.Identical(GetType(TypeStatusCode), GetType(TypeStatusCode)))
log.Println(types.Identical(GetType(TypeErr), GetType(TypeErr)))
The result is
false
false
true
true
It seems types.Identical cannot compare two same types in other package?
How can I compare them?
Is types.TypeString(typ1, nil) == types.TypeString(typ2, nil) robust enough?
The problem is that you're re-parsing the source every time, creating a new instance of types.Package and its associated types.Scope which results in the named types originating from different declarations (link).
Builtin types like int and error for which you got true are basic types that are treated differently by the comparison (link). They are also declared in the "universe" scope which, I believe, is always the same no matter how many times you re-parse the Src (link).
To fix your problem you have to parse the source once, and then share the resulting *types.Package.
package main
import (
"go/ast"
"go/importer"
"go/parser"
"go/token"
"go/types"
"log"
)
const Src = `
package types
import (
"io"
"net/http"
)
var (
IOReader io.Reader
Response *http.Response
)
`
const (
TypeIOReader = "IOReader"
TypeResponse = "Response"
)
func GetType(name string, pkg *types.Package) types.Type {
return pkg.Scope().Lookup(name).Type()
}
func main() {
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "types.go", Src, 0)
if err != nil {
panic(err)
}
conf := types.Config{Importer: importer.Default()}
pkg, err := conf.Check("impler/types", fset, []*ast.File{file}, nil)
if err != nil {
panic(err)
}
log.Println(types.Identical(GetType(TypeResponse, pkg), GetType(TypeResponse, pkg)))
log.Println(types.Identical(GetType(TypeIOReader, pkg), GetType(TypeIOReader, pkg)))
}
Types in Go with different names are treated as different, even they have the same structure. Here's more information about golang type rules: https://golang.org/ref/spec#Type_identity
If you wish to compare them you may to convert one value to another type:
type A {...} // some declaration
type B {...} // the same declaration
if a == A(b) {...}

How to run Binary Files inside GoLang Program?

I want to execute Binary Files inside GoLang Program.
Here is my code:
package main
import (
"fmt"
"os/exec"
)
func main() {
output, _ := exec.Command("/home/user/Golang/bin/hello").Output()
fmt.Println(output)
}
But I get the output as: []
Thanks in advance.
I can get the output.
package main
import (
"fmt"
"os/exec"
)
func main() {
output, err := exec.Command("/Users/duguying/gopath/bin/test").Output()
if err!=nil {
fmt.Println(err.Error())
}
fmt.Println(string(output))
}
check you binary file first or binary filepath is correcting. try to print out your error message.
When I'm looking at the source of the exec.Command() it doesnt return an error but only returns Cmd which is struct in the package exe :
source
....
func Command(name string, arg ...string) *Cmd {
cmd := &Cmd{
Path: name,
Args: append([]string{name}, arg...),
}
if filepath.Base(name) == name {
if lp, err := LookPath(name); err != nil {
cmd.lookPathErr = err
} else {
cmd.Path = lp
}
}
return cmd
}
....
I have succesfully got the binary file running using this code :
package main
import (
"fmt"
"os/exec"
)
func main() {
command:= exec.Command("Your binary file path")
// set var to get the output
var out bytes.Buffer
// set the output to our variable
command.Stdout = &out
err = command.Run()
if err != nil {
log.Println(err)
}
fmt.Println(out.String())
}
This one works for me for running a binary file that will print some random string.

How to get the name of a function in Go?

Given a function, is it possible to get its name? Say:
func foo() {
}
func GetFunctionName(i interface{}) string {
// ...
}
func main() {
// Will print "name: foo"
fmt.Println("name:", GetFunctionName(foo))
}
I was told that runtime.FuncForPC would help, but I failed to understand how to use it.
I found a solution:
package main
import (
"fmt"
"reflect"
"runtime"
)
func foo() {
}
func GetFunctionName(i interface{}) string {
return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
}
func main() {
// This will print "name: main.foo"
fmt.Println("name:", GetFunctionName(foo))
}
Not exactly what you want, because it logs the filename and the line number, but here is how I do it in my Tideland Common Go Library (http://tideland-cgl.googlecode.com/) using the "runtime" package:
// Debug prints a debug information to the log with file and line.
func Debug(format string, a ...interface{}) {
_, file, line, _ := runtime.Caller(1)
info := fmt.Sprintf(format, a...)
log.Printf("[cgl] debug %s:%d %v", file, line, info)
I found a better solution, in this function down here you just simply pass a function and the output is gonna be simple and straight.
package main
import (
"reflect"
"runtime"
"strings"
)
func GetFunctionName(temp interface{}) string {
strs := strings.Split((runtime.FuncForPC(reflect.ValueOf(temp).Pointer()).Name()), ".")
return strs[len(strs)-1]
}
And this is an example of how you use this:
package main
import "fmt"
func main() {
fmt.Println(GetFunctionName(main))
}
And this is the answer you should expect:
main
By getting the function name of the previous caller:
import (
"os"
"runtime"
)
func currentFunction() string {
counter, _, _, success := runtime.Caller(1)
if !success {
println("functionName: runtime.Caller: failed")
os.Exit(1)
}
return runtime.FuncForPC(counter).Name()
}

Resources