Can you create struct tags in a separate file? - go

This is not a duplicate of How can I override json tags in a Go struct? - that asks an entirely different question!
Given this XML struct:
type Foo struct {
Bar string `xml:"bar"`
}
I'd like to define xml:"bar" in a config' file, elsewhere. Something like:
import (
"github.com/foo/app/config"
)
type Foo struct {
Bar string config.Bar
}
Where github.com/foo/app/config contains the following:
package config
const (
Bar = `xml:"bar"`
)
That doesn't work, though - any help to achieve something similar would be greatly appreciated.

Can you create struct tags in a separate config file?
No.
You even cannot change them during runtime.

Related

parse simple terraform file using go

I now tried, everything but cant get this simple thing to work.
I got the following test_file.hcl:
variable "value" {
test = "ok"
}
I want to parse it using the following code:
package hcl
import (
"github.com/hashicorp/hcl/v2/hclsimple"
)
type Config struct {
Variable string `hcl:"test"`
}
func HclToStruct(path string) (*Config, error) {
var config Config
return &config, hclsimple.DecodeFile(path, nil, &config)
But I receive:
test_file.hcl:1,1-1: Missing required argument; The argument "test" is required, but no definition was found., and 1 other diagnostic(s)
I checked with other projects using the same library but I cannot find my mistake .. I just dont know anymore. Can someone guide me into the right direction?
The Go struct type you wrote here doesn't correspond with the shape of the file you want to parse. Notice that the input file has a variable block with a test argument inside it, but the Go type you wrote has only the test argument. For that reason, the HCL parser is expecting to find a file with just the test argument at the top-level, like this:
test = "ok"
To parse this Terraform-like structure with hclsimple will require you to write two struct types: one to represent the top level body containing variable blocks and another to represent the content of each of the blocks. For example:
type Config struct {
Variables []*Variable `hcl:"variable,block"`
}
type Variable struct {
Test *string `hcl:"test"`
}
With that said, I'll note that the Terraform language uses some features of HCL that hclsimple can't support, or at least can't support with direct decoding like this. For example, the type argument inside variable blocks is a special sort of expression called a type constraint which hclsimple doesn't support directly, and so parsing it will require using the lower-level HCL API. (That's what Terraform is doing internally.)
Thanks to #Martin Atkins, I now have the following code that works:
type Config struct {
Variable []Variable `hcl:"variable,block"`
}
type Variable struct {
Value string `hcl:"name,label"`
Test string `hcl:"test"`
}
With that I can parse a variables.tf like:
variable "projectname" {
default = "cicd-template"
}
variable "ansibleuser" {
default = "centos"
}

Can I allow arbitrary fields in a shared struct?

I would like to define an object in a shared libary,
type Common struct {
field_a string
custom interface{}
}
where custom is expected to hold additional fields that the user of this common object might define in their file, e.g.,
// module-1
type Mod1Customs struct {
abc string
}
From here, I would like to be able to set Common.custom to Mod1Customs so that I can access both field_a and abc in the same way from within module-1. I would like to do the same thing for other modules that may define an entirely different struct to assign to custom though.
The general idea is to create an object of pre-defined defaults and allowing a bucket of sorts for the users of the object to add their own custom fields.
Is this possible?
What you seem to be looking for is embedding:
type Common struct {
FieldA string
}
type Mod1Customs struct {
Common
Abc string
}
Then Mod1Customs gets access to all of Common fields directly:
c := Mod1Customs{}
fmt.Println(c.FieldA)
fmt.Println(c.Abc)
See Embedding in Effective Go for more details.
If you have some functions that deal with Common fields (no matter what the struct), then you would implement that with an interface that declares the fields you need. Mod1Customs would automatically conform to that interface.

Embedded structs get all tags and set values for all fields based on tag

I'm designing config component where I want to set ENV variable for each field on go struct.
e.g.
type SubStruct struct {
Field int `env:"ENV_INT"`
}
type Config struct {
Name string `env:"APP_NAME"`
Sub1 struct {
Sub11 struct {
Value string `env:"ENV_VAR"`
}
Sub12 SubStruct
}
}
What I want to achieve is to:
Get values of all env keys recursively
Set value based on field type
So there are basically two questions:
Is there any lib in go which exposes methods for setting values using reflection (e.g. like private methods in json package)?
Is there any method/function in go which can parse your struct recursively and get all tags from struct?
edit:
- updated structs
- renamed confusing field names (from embed to sub)

Enum as variable's property in go

Let's say I have something like this:
const (
FOO int = iota
BAR
BAZ
)
And I can access to variables by FOO, BAR and so on. But storing a big amount of constants in one namespace isn't good so I'm trying to hide this enum in structure or something like that so I can get value by typing actions.FOO in same namespace. I've tried many ways but didn't find anything like that. I would like to mention that easiest workaround, in this case, will be anonymous structure but I wanna keep auto indexing with iota.
The only way to assign some sort of enumerable behind the property without creating the separate package that I found is to use anonymous structure.
type someType int
var ConstantsList = struct {
FOO, BAR, BAZ someType
}{1, 2, 3}
There are few downsides of using it, it's not immutable, and doesn't have auto increment.

Conversion of Go struct fields with tags

I am really stuck here with a seemingly trivial problem in Go:
I have a Golang microservice, which outputs data in json format. Let's say I have a simple struct with json tags for this result:
type Result struct {
Name string `json:"name"`
Age int `json:"age"`
}
In the code part where the data is actually pulled out of the database, I have a quite similar struct, like this:
type ResultBackend struct {
Name string `bson:"fullName"`
Age int `bson:"age"`
}
The struct fields are similar, except the different tags. I would like to keep things simple, and return just one struct from the backend service (ResultBackend), which can then be send as a JSON response, like this:
func process() Result {
var result ResultBackend
... do a MongoDB query here and store results in result variable ...
return result
}
This certainly would not work, because we have two different structs here.
Of course one solution would be to embed both tags in one struct like so:
type Result struct {
Name string `json:"name" bson:"fullName"`
Age int `json:"age bson:"age"`
}
and then use this struct in the main code and the "process" function.
This works, but this seems like "poisoning" the Result struct of the main code with the bson tags. What, for instance, if the backend result is a XML file? I'd have to add xml tags to the struct as well. Or maybe someday tags some very obsure database adapter.
This doesn't seem to be the cleanest approach in my eyes. I'd rather have a clean Result struct in the main code, and simply to a conversion from one struct to another.
Is there any easy way doing that, or do I really have to copy all the fields of a ResultBackend struct to a new Result struct and return that? Or I am trying to over-simplify my code here? :)
Cheers!
What I'd do is create a separate type for every "serialisation format" if you will. This approach has several advantages:
Your concerns are separated.
JSON un/marshalling doesn't interfere with BSON/XML, so you can add any kind of additional struct fields (like xml.Name for example).
You can actually create several such types for different APIs that need different parameters.
The only disadvantage I see is that it's more code and even more code to move data between those. Ultimately it's up to you and your application design. If you're sure that your JSON/BSON will stay the same, you can use one type. Otherwise, I'd recommend specialisation.
To those who want to add bson tags in golang struct, here is a tool which can be helpful. I have spent a whole day searching for this and I want others to save their valuable time.
You can convert the following struct
type Result struct {
Name string `json:"name"`
Age int `json:"age"`
}
into
type Result struct {
Name string `json:"name" bson:"fullName"`
Age int `json:"age bson:"age"`
}
With this tool, you can not only add bson tags but also
XML tags,
Define validation and scope
Format tag values
Apply transformation
skip unexported fields.
Go Modify Tags: https://github.com/fatih/gomodifytags
Cheers,

Resources