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}