1 /*******************************************************************************
2 
3     Helper function for formatting a SmartUnion's active member to a string.
4 
5     This function is not placed in the SmartUnion class itself so as to avoid
6     spreading the dependency on ocean.text.convert.Formatter throughout the
7     codebase.
8 
9     Copyright:
10         Copyright (c) 2018 dunnhumby Germany GmbH.
11         All rights reserved.
12 
13     License:
14         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
15         Alternatively, this file may be distributed under the terms of the Tango
16         3-Clause BSD License (see LICENSE_BSD.txt for details).
17 
18 *******************************************************************************/
19 
20 module ocean.text.formatter.SmartUnion;
21 
22 import ocean.meta.types.Qualifiers;
23 import ocean.core.SmartUnion;
24 import ocean.core.Verify;
25 import ocean.text.convert.Formatter;
26 
27 /*******************************************************************************
28 
29     Gets a wrapper struct with a toString method to format the provided smart
30     union.
31 
32     Params:
33         SU = type of smart union to get formatting wrapper for
34         smart_union = smart union to get formatting wrapper for
35         include_name = flag to toggle formatting of the name of the active union
36             member. If true, format output will look like
37             "<activename>: value". If false, format output will look like
38             "value"
39 
40 *******************************************************************************/
41 
42 public SmartUnionFormatter!(SU) asActiveField ( SU ) ( SU smart_union,
43     bool include_name = false )
44 {
45     return SmartUnionFormatter!(SU)(smart_union, include_name);
46 }
47 
48 ///
49 unittest
50 {
51     union U
52     {
53         int i;
54         cstring s;
55     }
56 
57     SmartUnion!(U) su;
58 
59     // format from ocean.text.convert.Formatter
60     format("union active member: {}", asActiveField(su));
61 }
62 
63 /*******************************************************************************
64 
65     Struct that wraps a smart union with a toString method, for Formatter
66     compatibility.
67 
68     Params:
69         SU = type of smart union to format
70 
71 *******************************************************************************/
72 
73 private struct SmartUnionFormatter ( SU )
74 {
75     import ocean.meta.types.Templates : TemplateInstanceArgs;
76 
77     static assert(is(TemplateInstanceArgs!(SmartUnion, SU)));
78 
79     alias typeof(this) This;
80 
81     /// Smart union to format.
82     private SU smart_union;
83 
84     /// Flag to toggle formatting of the name of the active union member. If
85     /// true, format output will look like "<activename>: value". If false,
86     /// format output will look like "value".
87     private bool include_name;
88 
89     /// Formatting sink delegate, passed to toString and used by
90     /// formatUnionMember.
91     static private FormatterSink sink;
92 
93     /***************************************************************************
94 
95         Formats the smart union as a string to the provided sink delegate
96         (suitable for use with ocean.text.convert.Formatter). The formatted text
97         depends on whether a field of the union is active or not and whether
98         the `include_name` member is true or false:
99             * No union field active; `include_name` is:
100                 - true:  "<none>".
101                 - false: "".
102             * Union field active; `include_name` is:
103                 - true:  "<name>: value".
104                 - false: "value".
105 
106         Params:
107             sink = formatter sink delegate to use
108 
109     ***************************************************************************/
110 
111     public void toString ( scope FormatterSink sink )
112     {
113         if ( this.smart_union.active )
114         {
115             if ( this.include_name )
116                 sformat(sink, "<{}>: ", this.smart_union.active_name);
117 
118             This.sink = sink;
119             scope ( exit ) This.sink = null;
120 
121             callWithActive!(formatUnionMember)(this.smart_union);
122         }
123         else
124         {
125             if ( this.include_name )
126                 sformat(sink, "<{}>", this.smart_union.active_name);
127         }
128     }
129 
130     /***************************************************************************
131 
132         Formats the specified argument with the static sink delegate.
133 
134         Note: this function has to be public, so that it can be accessed by the
135         callWithActive function in ocean.core.SmartUnion. It is not intended to
136         be called by users.
137 
138         Params:
139             T = type of union member to format
140             union_member = union member to format
141 
142     ***************************************************************************/
143 
144     // FIXME_IN_D2: `package ocean.core`
145     static public void formatUnionMember ( T ) ( T union_member )
146     {
147         verify(This.sink !is null);
148         sformat(sink, "{}", union_member);
149     }
150 }
151 
152 version (unittest)
153 {
154     import ocean.core.Test;
155 }
156 
157 unittest
158 {
159     union U
160     {
161         int i;
162         cstring s;
163     }
164 
165     SmartUnion!(U) su;
166 
167     // Inactive union.
168     test!("==")(format("{}", asActiveField(su)), "");
169 
170     // Inactive union, plus name formatting.
171     test!("==")(format("{}", asActiveField(su, true)), "<none>");
172 
173     // i active.
174     su.i = 23;
175     test!("==")(format("{}", asActiveField(su)), "23");
176 
177     // s active.
178     su.s = "hello";
179     test!("==")(format("{}", asActiveField(su)), "hello");
180 
181     // s active, plus name formatting.
182     test!("==")(format("{}", asActiveField(su, true)), "<s>: hello");
183 }