1 /++
2 $(H4 High level YAML serialization API)
3 
4 Macros:
5 IONREF = $(REF_ALTTEXT $(TT $2), $2, mir, ion, $1)$(NBSP)
6 +/
7 module mir.ser.yaml;
8 
9 import mir.serde: SerdeTarget;
10 import mir.algebraic_alias.yaml: YamlAlgebraic;
11 
12 /++
13 YAML Serialization Params
14 +/
15 struct YamlSerializationParams
16 {
17     import mir.algebraic_alias.yaml: YamlScalarStyle, YamlCollectionStyle;
18 
19     /// Default style for scalar nodes. If style is $(D YamlScalarStyle.none), the _style is chosen automatically.
20     YamlScalarStyle defaultScalarStyle;
21     /// Default style for collection nodes. If style is $(D YamlCollectionStyle.none), the _style is chosen automatically.
22     YamlCollectionStyle defaultCollectionStyle;
23     /// Always explicitly write document start? Default is no explicit start.
24     bool explicitStart;
25     /// Always explicitly write document end? Default is no explicit end.
26     bool explicitEnd;
27     /// Write scalars in canonical form?
28     bool canonical;
29     /// Indentation width
30     ubyte indent = 2;
31     /// Preferred text width.
32     uint textWidth = 80;
33     /// YAML version string. Default value is null.
34     string yamlVersion;
35 }
36 
37 /++
38 Ion serialization function.
39 
40 Params:
41     value = value to serializa
42     params = (optional) serialization param
43     serdeTarget = (optional) serialization target ID
44 Returns:
45     UTF-8 YAML text
46 +/
47 @safe
48 string serializeYaml(V)(auto scope ref const V value, YamlSerializationParams params = YamlSerializationParams.init, int serdeTarget = SerdeTarget.yaml)
49 {
50     static if (is(V == YamlAlgebraic))
51     {
52         alias node = value;
53     }
54     else
55     static if (is(typeof(const YamlAlgebraic(value))))
56     {
57         scope node = const YamlAlgebraic(value);
58     }
59     else
60     static if (is(typeof(cast(const YamlAlgebraic) value)))
61     {
62         scope node = cast(const YamlAlgebraic) value;
63     }
64     else
65     {
66         import mir.ion.conv: serde;
67         auto node = serde!YamlAlgebraic(value, serdeTarget);
68     }
69 
70     return serializeYamlValues((()@trusted=>(&node)[0 .. 1])(), params);
71 }
72 
73 ///
74 @safe pure
75 unittest
76 {
77     import mir.test: should;
78 
79     static struct S
80     {
81         string foo;
82         uint bar;
83     }
84 
85     S("str", 4).serializeYaml.should ==
86 `{foo: str, bar: 4}
87 `;
88 }
89 
90 /// Tags (annotations) support
91 @safe pure
92 unittest
93 {
94     import mir.test: should;
95     import mir.algebraic: Variant;
96     import mir.serde: serdeAlgebraicAnnotation;
97 
98     import mir.test: should;
99     import mir.algebraic: Variant;
100     import mir.serde: serdeAlgebraicAnnotation, serdeAnnotation, serdeOptional;
101 
102     @serdeAlgebraicAnnotation("!S")
103     static struct S
104     {
105         string foo;
106         uint bar;
107     }
108 
109     @serdeAlgebraicAnnotation("rgb")
110     static struct RGB
111     {
112         @serdeAnnotation @serdeOptional
113         string name;
114 
115         ubyte r, g, b;
116     }
117 
118     alias V = Variant!(S, RGB);
119 
120     S("str", 4).serializeYaml.should == "{foo: str, bar: 4}\n";
121     V("str", 4).serializeYaml.should == "!S {foo: str, bar: 4}\n";
122 
123     // Multiple Ion annotations represented in a single tag using `::`.
124     V(RGB("dark_blue", 23, 25, 55)).serializeYaml.should ==
125 `!<rgb::dark_blue> {r: 23, g: 25, b: 55}
126 `;
127 }
128 
129 /// YAML-specific serialization
130 @safe pure
131 unittest
132 {
133     import mir.algebraic_alias.yaml: YamlAlgebraic, YamlCollectionStyle;
134     import mir.test: should;
135 
136     auto value = YamlAlgebraic(["foo".YamlAlgebraic, 123.9.YamlAlgebraic, "bar".YamlAlgebraic]);
137 
138     value.serializeYaml.should == "[foo, 123.9, bar]\n";
139 
140     value.collectionStyle = YamlCollectionStyle.block;
141     value.serializeYaml.should == "- foo\n- 123.9\n- bar\n";
142 }
143 
144 /// User API for YAML-specific serialization
145 @safe pure
146 version(none)
147 unittest
148 {
149     import mir.algebraic_alias.yaml: YamlAlgebraic;
150     import mir.test: should;
151 
152     static struct MyYamlStruct
153     {
154         int b;
155 
156         // has to be scope const
157         auto opCast(T : YamlAlgebraic)() scope const @safe pure
158         {
159             return b.YamlAlgebraic;
160         }
161     }
162 
163     MyYamlStruct(40).serializeYaml.should == "40\n";
164 }
165 
166 /++
167 Params:
168     nodes = Algebraic nodes (documents)
169     params = (optional) serialization param
170 Returns:
171     UTF-8 YAML text
172 See_Also:
173     $(IONREF, conv, serde).
174 +/
175 @safe pure
176 string serializeYamlValues(scope const YamlAlgebraic[] nodes, YamlSerializationParams params = YamlSerializationParams.init)
177 {
178     import mir.yaml.internal.dumper;
179     import std.array: appender;
180 
181     auto app = appender!string;
182     auto dumper = dumper(params);
183     foreach (ref node; nodes)
184         dumper.dump(app, node);
185     return app.data;
186 }