Skip to content
This repository was archived by the owner on Apr 1, 2025. It is now read-only.
This repository was archived by the owner on Apr 1, 2025. It is now read-only.

Allow recursive references #215

@purpleidea

Description

@purpleidea

YAML has this excellent anchor/reference syntax to identify two parts of the YAML syntax that refer to the same element. This maps perfectly to a language that has pointers! Unfortunately there is a bug and the values are copied over instead of copying the pointer value! This is the correct behaviour if we're using a string, but not when it's a *string.

Here is some reproducing code:

package main

import (
    "errors"
    "log"

    "gopkg.in/yaml.v2"
)

type NoopRes struct {
    Path    string  `yaml:"path"`
    Content *string `yaml:"content"` // XXX *string
}

type GraphConfig struct {
    Graph     string `yaml:"graph"`
    Resources struct {
        Noop []*NoopRes `yaml:"noop"`
    } `yaml:"resources"`
    Comment string `yaml:"comment"`
}

func (c *GraphConfig) Parse(data []byte) error {
    if err := yaml.Unmarshal(data, c); err != nil {
        return err
    }
    if c.Graph == "" {
        return errors.New("Graph config: invalid `graph`")
    }
    return nil
}

func main() {
    log.Printf("Hello!")
    str :=
        `

---
graph: mygraph
comment: my comment
resources:
  noop:
  - name: noop1
    path: foo
    content: &ptr "hello"
  - name: noop2
    path: bar
    content: *ptr
  - name: noop3
    path: baz
    content: *ptr
  unused:
  - foo: bar
`

    data := []byte(str)
    var config GraphConfig
    if err := config.Parse(data); err != nil {
        log.Printf("Config: Error: ParseConfigFromFile: Parse: %v", err)
        return
    }

    log.Printf("Success!")
    log.Printf("%+v", &config)
    log.Printf("%+v", config.Resources.Noop[0].Content)
    if x := config.Resources.Noop[0].Content; x != nil {
        log.Printf("inside: %+v", *config.Resources.Noop[0].Content)
    }

    log.Printf("================")

    log.Printf("%+v", config.Resources.Noop[1].Content)
    if x := config.Resources.Noop[1].Content; x != nil {
        log.Printf("inside: %+v", *x)
    } else {
        log.Printf("nothing inside!")
    }

    if config.Resources.Noop[0].Content != config.Resources.Noop[1].Content {
        log.Printf("different pointers! :(")
    } else {
        log.Printf("same pointers! :)")
    }

    log.Printf("================")

    log.Printf("%+v", config.Resources.Noop[2].Content)
    if x := config.Resources.Noop[2].Content; x != nil {
        log.Printf("inside: %+v", *x)
    } else {
        log.Printf("nothing inside!")
    }

    if config.Resources.Noop[1].Content != config.Resources.Noop[2].Content {
        log.Printf("different pointers! :(")
    } else {
        log.Printf("same pointers! :)")
    }
}

Sample output is:

$ go run yamlptrtest4.go
2016/10/22 13:16:46 Hello!
2016/10/22 13:16:46 Success!
2016/10/22 13:16:46 &{Graph:mygraph Resources:{Noop:[0xc82000ea80 0xc82000eaa0 0xc82000eac0]} Comment:my comment}
2016/10/22 13:16:46 0xc82000ae30
2016/10/22 13:16:46 inside: hello
2016/10/22 13:16:46 ================
2016/10/22 13:16:46 0xc82000aeb0
2016/10/22 13:16:46 inside: hello
2016/10/22 13:16:46 different pointers! :(
2016/10/22 13:16:46 ================
2016/10/22 13:16:46 0xc82000af30
2016/10/22 13:16:46 inside: hello
2016/10/22 13:16:46 different pointers! :(

This is a pretty crucial feature. I don't know the go-yaml code base, but if someone could point me in the right direction, I would like to try to patch this. Obviously, I'd prefer to have help though. :)

Thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions