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

How to use IsDefined with array of tables? #169

Open
gorankor opened this issue Mar 24, 2017 · 5 comments
Open

How to use IsDefined with array of tables? #169

gorankor opened this issue Mar 24, 2017 · 5 comments

Comments

@gorankor
Copy link

I don't know if this is an issue or just me not knowing how to properly call it, so I apologize if I opened a new issue which is not an issue at all.

I have a config file where I want to specify one section several times:

[[methods]]
foo = "method1"
namespace = "ns1"

[[methods]]
foo = "method2"
namespace = "ns2"`

...

Here's a complete example:

package main

import (
        "fmt"
        "os"
        "github.com/BurntSushi/toml"
)

type confMethod struct {
    Foo         string     `toml:"foo,omitempty"`
    NameSpace   string     `toml:"namespace,omitempty"`
}

// Methods struct - used in include files
type confMethods struct {
    Methods []confMethod     `toml:"methods,omitempty"`
}

func main() {
    var mConf confMethods

    c := `[[methods]]
foo = "method1"
namespace = "root/cimv2"

[[methods]]
foo = "method2"
namespace = "root/cimv2"`

    md, err := toml.Decode(c, &mConf)
    if err != nil {
        fmt.Fprintf(os.Stderr, "Error: %s\n", err)
        os.Exit(1)
    }

    fmt.Printf("keys=%s\n", md.Keys())

    if md.IsDefined("methods", "namespace") {
        fmt.Printf("defined\n")
    } else {
        fmt.Printf("not defined\n")
    }
}

This returns "not defined". How can one use IsDefined() in this case? Is it possible at all?

I have tried different usages as the commend above IsDefined() in decode.go says:

md.IsDefined("methods", "namespace")
md.IsDefined("methods", "methods.namespace")
md.IsDefined("methods", "method", "namespace")
md.IsDefined("namespace")

but nothing works.

My real example is a bit more complex, and config parser works as expected when I just blindly assign values from config struct, but I'd like to only do that when something is present in the config file. I can't rely on what Keys() returns as that one is not specific enough, but it at least shows that the keys are there as expected (as the example prints it).

If I look at what is going on in decode.go and compare working (where I use just a simple struct) vs. non-working example, when it works, it gets this type:

map[string]interface {}

But for this failing example it gets:

[]map[string]interface {}

And then it fails here on second (might be third - depends how it gets called obviouslly) loop iteration:

if hash, ok = hashOrVal.(map[string]interface{}); !ok

@cespare
Copy link
Collaborator

cespare commented Mar 28, 2017

Yeah, there's no way at the moment. The API doesn't offer a clear way to add this feature, either.

Super hacky strawman proposal: md.IsDefined("methods", "0", "namespace"). Pretty awful.

Probably we should add a new method instead. I'm not even sure what the API should be, though. @BurntSushi any thoughts?

@BurntSushi
Copy link
Owner

I don't have any strong opinions, but it would be nice to make IsDefined("methods", "namespace") work as expected.

@cespare
Copy link
Collaborator

cespare commented Mar 29, 2017

@BurntSushi what do you expect, though?

@BurntSushi
Copy link
Owner

I suppose I'd expect it to return true because there exists a namespace key in at least one methods table. Now that I write it out, I see why that's potentially problematic, since the check would need to look at all entries in methods.

@gorankor
Copy link
Author

gorankor commented Mar 30, 2017

If you look at it that way, it certainly is not well defined.

Suppose I walk over methods this way:

for _, m := range mConf.Methods {

Then having an index into array would tell us which one to look at. I am thinking of what @cespare said earlier:

Super hacky strawman proposal: md.IsDefined("methods", "0", "namespace"). Pretty awful.

If I try that, it doesn't work for me now, but it would certainly be more than good enough.

More info:

When searching for some issue about using Cgo I stumbled upon a post which describes when to use string vs. *string in Go and it nicely gives out one reason to use the latter: you should probably use a *string for struct properties when deserializing json or yaml (or anything) into a structure.

I was not aware of this distinction, so I could simply convert my struct like so:

type confMethod struct {
    Foo         *string     `toml:"foo,omitempty"`
    NameSpace   *string     `toml:"namespace,omitempty"`
}

and then with the above for loop I can say:

if m.Foo != nil

So if you feel like not bothering with this, I am more than OK with closing this issue and I'll change my code to work this way.

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

No branches or pull requests

4 participants