Matching `Union` type into array
svex99 opened this issue · comments
I am trying to parse a configuration file for Bind9 DNS server. This file contains a different number of records, like NS, A, MX, etc. The order in which they are defined is not relevant. In order, I want to parse them into an array of type Record
.
Every record has different fields, so I defined them in separate structs and used participle.Union[Record](...)
to conform the union expression.
The problem is when trying to store them in an array []*Record
with the tag @@*
. After parsed, the array is always nil
.
I tried to parse a simple record with the union type, and it works, so I don't understand why the array behaves different and is always nil
. I guess it is related to the type system of Go, but not sure.
Any possible explanation is welcome. My final question is wow can I parse the different records?
Thanks in advance!
Here, an example of the file to parse:
$ORIGIN example.com.
$TTL 2d
@ IN SOA ns1 admin (
1
2
3
4
5
)
@ IN NS ns1
ns1 IN A 10.10.10.10
@ IN MX 100 email
email IN A 20.20.20.20
And my code:
package main
import (
"os"
"github.com/alecthomas/repr"
"github.com/alecthomas/participle/v2"
"github.com/alecthomas/participle/v2/lexer"
)
var (
domainConfLexer = lexer.MustSimple([]lexer.SimpleRule{
{Name: "Directive", Pattern: `\$(ORIGIN|TTL)`},
{Name: "Keyword", Pattern: `@|IN|SOA|NS|A|MX|TXT|PTR`},
{Name: "Domain", Pattern: `[a-zA-Z][\w\-]*\.[a-zA-Z]+`},
{Name: "Name", Pattern: `[a-zA-Z][\w\-]*`},
{Name: "Ttl", Pattern: `\d+[hdw]`},
{Name: "Ipv4", Pattern: `\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}`},
{Name: "Uint", Pattern: `\d+`},
{Name: "Punct", Pattern: `[\.\(\)]`},
{Name: "Comment", Pattern: `;[^\n]*`},
{Name: "Whitespace", Pattern: `[ \n\t\r]+`},
})
parser = participle.MustBuild[DomainConf](
participle.Lexer(domainConfLexer),
participle.Union[Record](NSRecord{}, ARecord{}, MXRecord{}),
participle.Elide("Whitespace", "Comment"),
participle.UseLookahead(2),
)
)
type DomainConf struct {
Origin string `parser:"'$ORIGIN' @Domain '.'"`
Ttl string `parser:"'$TTL' @Ttl"`
SOARecord *SOARecord `parser:"@@"`
Records []*Record `parser:"@@*"`
}
type Record interface{ value() }
type SOARecord struct {
NameServer string `parser:"'@' 'IN' 'SOA' @Name"`
Admin string `parser:"@Name"`
Serial uint `parser:"'(' @Uint"`
Refresh uint `parser:"@Uint"`
Retry uint `parser:"@Uint"`
Expire uint `parser:"@Uint"`
Minimum uint `parser:"@Uint ')'"`
}
type NSRecord struct {
NameServer string `parser:"'@' 'IN' 'NS' @Name"`
}
func (NSRecord) value() {}
type ARecord struct {
Name string `parser:"@Name"`
Ip string `parser:"'IN' 'A' @Ipv4"`
}
func (ARecord) value() {}
type MXRecord struct {
Priority uint `parser:"'@' 'IN' 'MX' @Uint"`
EmailServer string `parser:"@Name"`
}
func (MXRecord) value() {}
func main() {
reader, err := os.Open("test.txt")
if err != nil {
panic(err)
}
dConf, err := parser.Parse("", reader)
if err != nil {
panic(err)
}
repr.Println(dConf, repr.Indent(" "), repr.OmitEmpty(false))
}
The output after execute go run .
is:
&main.DomainConf{
Origin: "example.com",
Ttl: "2d",
SOARecord: &main.SOARecord{
NameServer: "ns1",
Admin: "admin",
Serial: 1,
Refresh: 2,
Retry: 3,
Expire: 4,
Minimum: 5,
},
Records: nil,
}
The problem is that you're trying parse into a pointer to an interface rather than just into the interface.
Change []*Record
to []Record
. Here it is on the Go playground.