doku/printers/json/print_enum/separated/multiline.rs
1use super::*;
2
3pub(super) fn print<'ty>(
4 ctxt: &mut Ctxt<'_, 'ty, '_>,
5 tag: Tag,
6 variants: &[&'ty Variant],
7) {
8 // Whether to additionally indent the variants or not.
9 //
10 // We want to add some extra indentation when we're during a field expansion
11 // so that we print:
12 //
13 // ```
14 // {
15 // "enum":
16 // variant 1
17 // variant 2
18 // variant 3
19 // }
20 // ```
21 //
22 // ... instead of:
23 //
24 // ```
25 // {
26 // "enum": variant 1
27 // variant 2
28 // variant 3
29 // }
30 // ```
31 let indent = ctxt.parent.map_or(false, |parent| {
32 matches!(parent.kind, TypeKind::Struct { .. }) && !ctxt.flat
33 });
34
35 if indent {
36 ctxt.out.ln();
37 ctxt.out.inc_indent();
38 }
39
40 // Whether to additionally separate the variants or not.
41 //
42 // We want to add some extra spacing when the variants are "somewhat large"
43 // so that the `// or` comment clearly separates each particular variant.
44 //
45 // There is no single best metric here, but in general we prefer:
46 //
47 // ```
48 // {
49 // "enum":
50 // // Specify site using the concrete URL
51 // {
52 // "url": "http://google.com"
53 // }
54 //
55 // // or
56 //
57 // // Specify site using just its name
58 // {
59 // "site": "google"
60 // }
61 // }
62 // ```
63 //
64 // ... over:
65 //
66 // ```
67 // {
68 // "enum":
69 // // Specify site using the concrete URL
70 // {
71 // "url": "http://google.com"
72 // }
73 // // or
74 // // Specify site using just its name
75 // {
76 // "site": "google"
77 // }
78 // }
79 // ```
80 let separate = match &ctxt.fmt.layout {
81 Layout::OneColumn => {
82 variants.iter().any(|variant| variant.comment.is_some())
83 }
84 Layout::TwoColumns { .. } => false,
85 };
86
87 for (variant_idx, variant) in variants.iter().enumerate() {
88 if variant_idx > 0 {
89 if separate {
90 ctxt.out.ln();
91 }
92
93 ctxt.out
94 .write(format!("\n{} or\n", ctxt.fmt.comments_style.separator));
95
96 if separate {
97 ctxt.out.ln();
98 }
99 }
100
101 if let Some(comment) = variant.comment {
102 ctxt.out.writeln_comment(comment);
103 }
104
105 print_variant(ctxt, tag, variant);
106 }
107
108 if indent {
109 ctxt.out.dec_indent();
110 }
111}
112
113fn print_variant<'ty>(
114 ctxt: &mut Ctxt<'_, 'ty, '_>,
115 tag: Tag,
116 variant: &'ty Variant,
117) {
118 if let Tag::Adjacent { .. } | Tag::External { .. } = tag {
119 // Adjacent and external variants can't be flattened because we'd try to
120 // print something nonsensical like:
121 //
122 // ```
123 // {
124 // "tag": "...",
125 // "content": "foo": "bar"
126 // }
127 // ```
128 //
129 // Note that this property can't be specified from the frontend anyway
130 // in here - what we're guarding against is us calling `.with_flat()`
131 // somewhere before and carrying this flag too deep.
132 ctxt.flat = false;
133 }
134
135 match tag {
136 Tag::Adjacent { tag, content } => {
137 ctxt.out.writeln("{");
138 ctxt.out.inc_indent();
139 ctxt.out.write_key_and_separator(tag);
140 ctxt.out.write(format!(r#""{}""#, variant.id));
141 ctxt.out.write_property_separator_ln();
142
143 if let Fields::Named { .. } | Fields::Unnamed { .. } =
144 variant.fields
145 {
146 ctxt.out.write_key_and_separator(content);
147 ctxt.print_fields(&variant.fields, None);
148 ctxt.out.ln();
149 }
150
151 ctxt.out.dec_indent();
152 ctxt.out.write("}");
153 }
154
155 Tag::Internal { tag } => {
156 ctxt.out.writeln("{");
157 ctxt.out.inc_indent();
158 ctxt.out.write_key_and_separator(tag);
159 ctxt.out.write(format!(r#""{}""#, variant.id));
160 ctxt.out.write_property_separator_ln();
161
162 if let Fields::Named { .. } | Fields::Unnamed { .. } =
163 variant.fields
164 {
165 ctxt.nested()
166 .with_flat()
167 .print_fields(&variant.fields, None);
168
169 ctxt.out.ln();
170 }
171
172 ctxt.out.dec_indent();
173 ctxt.out.write("}");
174 }
175
176 Tag::External => match variant.fields {
177 Fields::Named { .. } | Fields::Unnamed { .. } => {
178 ctxt.out.writeln("{");
179 ctxt.out.inc_indent();
180 ctxt.out.write_key_and_separator(variant.id);
181 ctxt.print_fields(&variant.fields, None);
182 ctxt.out.ln();
183 ctxt.out.dec_indent();
184 ctxt.out.write("}");
185 }
186
187 Fields::Unit => {
188 ctxt.out.write(format!(r#""{}""#, variant.id));
189 }
190 },
191
192 Tag::None => match variant.fields {
193 Fields::Named { .. } | Fields::Unnamed { .. } => {
194 ctxt.print_fields(&variant.fields, None);
195 }
196
197 Fields::Unit => {
198 ctxt.out.write("null");
199 }
200 },
201 }
202}