package behavior import ( "fmt" "gopkg.in/yaml.v3" ) // DescVariant is one alternative description, optionally gated by a condition. type DescVariant struct { Text string `yaml:"text"` Condition *Condition `yaml:"condition,omitempty"` } // DescList is an ordered list of description variants. The first variant whose // condition matches is shown. // // Content may author the common single, unconditional description two ways: // as a bare scalar (`description: "A dark room."`) or as a list of variants // (`description: [{text: "A dark room."}]`). MarshalYAML collapses a single // unconditional variant back to the scalar form so machine-written room files // stay close to what a human would type. type DescList []DescVariant func (d *DescList) UnmarshalYAML(value *yaml.Node) error { switch value.Kind { case yaml.ScalarNode: var text string if err := value.Decode(&text); err != nil { return err } if text == "" { *d = nil return nil } *d = DescList{{Text: text}} return nil case yaml.SequenceNode: out := make(DescList, 0, len(value.Content)) for _, item := range value.Content { // Accept a bare string element as shorthand for a variant. if item.Kind == yaml.ScalarNode { var text string if err := item.Decode(&text); err != nil { return err } out = append(out, DescVariant{Text: text}) continue } var variant DescVariant if err := item.Decode(&variant); err != nil { return err } out = append(out, variant) } *d = out return nil case yaml.AliasNode: return d.UnmarshalYAML(value.Alias) case 0: // An omitted/zero node leaves the list empty. *d = nil return nil default: return fmt.Errorf("description must be a string or a list of variants") } } func (d DescList) MarshalYAML() (any, error) { if len(d) == 0 { return []DescVariant{}, nil } if len(d) == 1 && d[0].Condition == nil { return d[0].Text, nil } return []DescVariant(d), nil }