flyx / NimYAML

YAML implementation for Nim
https://nimyaml.org
Other
186 stars 36 forks source link

broken enum #110

Closed zhenyamega closed 2 years ago

zhenyamega commented 2 years ago

1.

This object "Data" detection as Map

import yaml/serialization, streams

type
    Data = object
        data: string

var node: Data

let s = newFileStream("in.yaml")
load(s, node)
s.close()

but this object "Data" detection as Seq

import yaml/serialization, streams

type
    DataType = enum
        Default
        Sequence

    Data = object
        data: string
        case kind: DataType
        of Default: value: string
        of Sequence: valueSeq: seq[string]

var node: Data

let s = newFileStream("in.yaml")
load(s, node)
s.close()

error: Error: unhandled exception: While constructing Data: Expected yamlStartSeq, got yamlStartMap [YamlConstructionError]

Why did it become a sequence?

2.

with 1 enum we can work

main.nim

import yaml/serialization, streams

type
    DataType = enum
        Default
        Sequence

    Data = object
        data: string
        case kind: DataType
        of Default: value: string
        of Sequence: valueSeq: seq[string]

var node: Data

let s = newFileStream("in.yaml")
load(s, node)
s.close()

echo node.value

in.yaml

- data: test
- kind: Default
- value: test

It work and output test

but with 2 enum

main.nim

import yaml/serialization, streams

type
    DataType = enum
        Default
        Sequence

    Data = object
        data: string
        case kind1 : DataType
        of Default: value1: string
        of Sequence: valueSeq1: seq[string]
        case kind2 : DataType
        of Default: value2: string
        of Sequence: valueSeq2: seq[string]

var node: Data

let s = newFileStream("in.yaml")
load(s, node)
s.close()

echo node.value1

in.yaml

- data: test
- kind1: Default
- value1: test
- kind2: Default
- value2: test

error /root/.nimble/pkgs/yaml-0.16.0/yaml/serialization.nim(739, 16) Error: Unexpected kind of field 'kind2': nnkRecCase

flyx commented 2 years ago

See the docs on variant object types, specifically this:

The value of a discriminator field must be loaded before any value of a field that depends on it. Therefore, a YAML mapping cannot be used to serialize variant object types - the YAML specification explicitly states that the order of key-value pairs in a mapping must not be used to convey content information. So, any variant object type is serialized as a list of key-value pairs.

This should answer 1.

Concerning 2, multiple case constructs in an object are currently not supported. I am not entirely sure why, since it has been a long time since I wrote that code. I can't promise a timely fix but suggest you have a look at {.implicit.} and check if that can solve your problem. Your example would become:

import yaml/serialization, streams

type
    DataType = enum
        Default
        Sequence

    Inner1 {.implicit.} = object
        case kind: DataType
        of Default: value: string
        of Sequence: valueSeq: seq[string]

    Inner2 {.implicit.} = object
        case kind : DataType
        of Default: value: string
        of Sequence: valueSeq: seq[string]

    Data = object
        data: string
        inner1: Inner1
        inner2: Inner2

setTag(seq[string], "!list".Tag)

var node: Data

let s = newFileStream("in.yaml")
load(s, node)
s.close()

This can load the following YAML:

data: test
inner1: test # sets kind to Default and value to "test"
inner2: !list [test2, test3] # sets kind to Sequence.
zhenyamega commented 2 years ago

I gave incorrect variable naming in the examples. I don't need variant object types. I need variant object fields and these variants may be several.

type
    DataType = enum
        Default
        Type1
        Type2

    Config = object
        name: string

        case kind: DataType
        of Type1:
            param1: string
            param2: string
        of Type2:
            param3: string
            param4: seq[string]
        else: discard
name: some_confing
kind: Type2
param3: str
param4:
  - str1
  - str2
flyx commented 2 years ago

That Config type is loadable but you must put the fields in a sequence for the reason quoted above from the docs. It's either that or use an implicit type and wrap the inner params into objects.