• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zhiburt / tabled / 11433508586

21 Oct 2024 05:00AM UTC coverage: 74.214% (-0.006%) from 74.22%
11433508586

push

github

web-flow
Merge pull request #428 from welpo/fix/migrate-to-proc-macro-error2

fix(deps): migrate from proc-macro-error to proc-macro-error2

0 of 7 new or added lines in 2 files covered. (0.0%)

1 existing line in 1 file now uncovered.

12246 of 16501 relevant lines covered (74.21%)

2.2 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/static_table/src/static_table.rs
1
use std::{
2
    collections::{HashMap, HashSet},
3
    iter::FromIterator,
4
};
5

6
use quote::ToTokens;
7
use syn::{
8
    braced, bracketed,
9
    parse::{Parse, ParseStream},
10
    punctuated::Punctuated,
11
    token::{self, Brace},
12
    ExprLit, Ident, Lit, LitInt, LitStr, Result, Token,
13
};
14
use tabled::{
15
    builder::Builder,
16
    settings::{style::BorderSpanCorrection, Alignment, Margin, Modify, Padding, Span, Style},
17
    Table,
18
};
19

20
struct MatrixRow {
21
    #[allow(dead_code)]
22
    bracket_token: token::Bracket,
23
    elems: MatrixRowElements,
24
}
25

26
enum MatrixRowElements {
27
    List(Punctuated<ExprVal, Token![,]>),
28
    Static {
29
        elem: ExprVal,
30
        #[allow(dead_code)]
31
        semi_token: Token![;],
32
        len: LitInt,
33
    },
34
}
35

36
impl Parse for MatrixRow {
37
    fn parse(input: ParseStream<'_>) -> Result<Self> {
×
38
        let content;
×
39
        let bracket_token = bracketed!(content in input);
×
40

41
        if content.peek2(Token![;]) {
×
42
            return Ok(Self {
×
43
                bracket_token,
×
44
                elems: MatrixRowElements::Static {
×
45
                    elem: content.parse()?,
×
46
                    semi_token: content.parse()?,
×
47
                    len: content.parse()?,
×
48
                },
49
            });
50
        }
51

52
        let mut elems = Punctuated::new();
×
53
        while !content.is_empty() {
×
54
            let val = content.parse()?;
×
55
            elems.push_value(val);
×
56
            if content.is_empty() {
×
57
                break;
×
58
            }
59
            let punct = content.parse()?;
×
60
            elems.push_punct(punct);
×
61
        }
62

63
        Ok(Self {
×
64
            bracket_token,
×
65
            elems: MatrixRowElements::List(elems),
×
66
        })
67
    }
68
}
69

70
enum ExprVal {
71
    Lit(ExprLit),
72
    Scope {
73
        #[allow(dead_code)]
74
        brace_token: token::Brace,
75
        expr: Option<ScopeVal>,
76
    },
77
}
78

79
enum ScopeVal {
80
    Expr(ExprLit),
81
    List(Punctuated<ExprLit, Token![,]>),
82
    Sized {
83
        elem: ExprLit,
84
        #[allow(dead_code)]
85
        semi_token: Token![;],
86
        len: LitInt,
87
    },
88
}
89

90
impl Parse for ExprVal {
91
    fn parse(input: ParseStream<'_>) -> Result<Self> {
×
92
        if input.peek(Brace) {
×
93
            let content;
×
94
            let brace_token = braced!(content in input);
×
95

96
            if content.is_empty() {
×
97
                return Ok(ExprVal::Scope {
×
98
                    brace_token,
×
99
                    expr: None,
×
100
                });
101
            }
102

103
            if content.peek2(Token![;]) {
×
104
                return Ok(ExprVal::Scope {
×
105
                    brace_token,
×
106
                    expr: Some(ScopeVal::Sized {
×
107
                        elem: content.parse()?,
×
108
                        semi_token: content.parse()?,
×
109
                        len: content.parse()?,
×
110
                    }),
111
                });
112
            }
113

114
            let elem: ExprLit = content.parse()?;
×
115

116
            if content.is_empty() {
×
117
                return Ok(ExprVal::Scope {
×
118
                    brace_token,
×
119
                    expr: Some(ScopeVal::Expr(elem)),
×
120
                });
121
            }
122

123
            let mut elems = Punctuated::new();
×
124
            elems.push(elem);
×
125

126
            while !content.is_empty() {
×
127
                let punct: Token![,] = content.parse()?;
×
128
                elems.push_punct(punct);
×
129

130
                if content.is_empty() {
×
131
                    // trailing comma
132
                    break;
×
133
                }
134

135
                let val: ExprLit = content.parse()?;
×
136
                elems.push_value(val);
×
137
            }
138

139
            return Ok(ExprVal::Scope {
×
140
                brace_token,
×
141
                expr: Some(ScopeVal::List(elems)),
×
142
            });
143
        }
144

145
        Ok(Self::Lit(input.parse()?))
×
146
    }
147
}
148

149
struct MatrixInput {
150
    #[allow(dead_code)]
151
    bracket_token: token::Bracket,
152
    data: MatrixData,
153
}
154

155
enum MatrixData {
156
    List(Punctuated<MatrixRow, Token![,]>),
157
    Static {
158
        elem: MatrixRow,
159
        #[allow(dead_code)]
160
        semi_token: Token![;],
161
        len: LitInt,
162
    },
163
}
164

165
impl Parse for MatrixInput {
166
    fn parse(input: ParseStream<'_>) -> Result<Self> {
×
167
        let content;
×
168
        let bracket_token = bracketed!(content in input);
×
169

170
        if content.is_empty() {
×
171
            return Ok(Self {
×
172
                bracket_token,
×
173
                data: MatrixData::List(Punctuated::new()),
×
174
            });
175
        }
176

177
        let elem = content.parse()?;
×
178

179
        if content.peek(Token![;]) {
×
180
            return Ok(MatrixInput {
×
181
                bracket_token,
×
182
                data: MatrixData::Static {
×
183
                    elem,
×
184
                    semi_token: content.parse()?,
×
185
                    len: content.parse()?,
×
186
                },
187
            });
188
        }
189

190
        let mut elems = Punctuated::new();
×
191
        elems.push(elem);
×
192

193
        while !content.is_empty() {
×
194
            let punct: Token![,] = content.parse()?;
×
195
            elems.push_punct(punct);
×
196

197
            if content.is_empty() {
×
198
                // trailing comma
199
                break;
×
200
            }
201

202
            let val = content.parse()?;
×
203
            elems.push_value(val);
×
204
        }
205

206
        Ok(MatrixInput {
×
207
            bracket_token,
×
208
            data: MatrixData::List(elems),
×
209
        })
210
    }
211
}
212

213
struct KeyValue<V> {
214
    key: Ident,
215
    #[allow(dead_code)]
216
    token: Token!(=),
217
    value: V,
218
}
219

220
impl<V: Parse> Parse for KeyValue<V> {
221
    fn parse(input: ParseStream<'_>) -> Result<Self> {
×
222
        Ok(Self {
×
223
            key: input.parse()?,
×
224
            token: input.parse()?,
×
225
            value: input.parse()?,
×
226
        })
227
    }
228
}
229

230
pub(crate) struct TableStruct {
231
    matrix: MatrixInput,
232
    comma_token: Option<Token![,]>,
233
    settings: Punctuated<KeyValue<LitStr>, Token!(,)>,
234
}
235

236
impl Parse for TableStruct {
237
    fn parse(input: ParseStream<'_>) -> Result<Self> {
×
238
        let matrix = input.parse()?;
×
239
        let mut comma_token = None;
×
240
        let mut settings = Punctuated::new();
×
241

242
        if input.peek(Token![,]) {
×
243
            comma_token = Some(input.parse()?);
×
244
            while !input.is_empty() {
×
245
                let val = input.parse()?;
×
246
                settings.push_value(val);
×
247
                if input.is_empty() {
×
248
                    break;
×
249
                }
250
                let punct = input.parse()?;
×
251
                settings.push_punct(punct);
×
252
            }
253
        }
254

255
        Ok(Self {
×
256
            matrix,
×
257
            comma_token,
×
258
            settings,
×
259
        })
260
    }
261
}
262

263
struct Pad<T> {
264
    left: T,
265
    #[allow(dead_code)]
266
    comma1_tk: Token!(,),
267
    right: T,
268
    #[allow(dead_code)]
269
    comma2_tk: Token!(,),
270
    top: T,
271
    #[allow(dead_code)]
272
    comma3_tk: Token!(,),
273
    bottom: T,
274
}
275

276
impl<T: Parse> Parse for Pad<T> {
277
    fn parse(input: ParseStream<'_>) -> Result<Self> {
×
278
        Ok(Self {
×
279
            left: input.parse()?,
×
280
            comma1_tk: input.parse()?,
×
281
            right: input.parse()?,
×
282
            comma2_tk: input.parse()?,
×
283
            top: input.parse()?,
×
284
            comma3_tk: input.parse()?,
×
285
            bottom: input.parse()?,
×
286
        })
287
    }
288
}
289

290
fn expr_lit_to_string(expr_lit: &ExprLit) -> String {
×
291
    match &expr_lit.lit {
×
292
        Lit::Str(val) => val.value(),
×
293
        Lit::ByteStr(val) => format!("{:?}", val.value()),
×
294
        Lit::Int(val) => val.base10_digits().to_string(),
×
295
        Lit::Float(val) => val.base10_digits().to_string(),
×
296
        Lit::Char(val) => val.value().to_string(),
×
297
        Lit::Byte(val) => val.value().to_string(),
×
298
        Lit::Bool(val) => val.value().to_string(),
×
299
        Lit::Verbatim(val) => val.to_token_stream().to_string(),
×
300
    }
301
}
302

303
fn expr_val_to_list(expr_val: &ExprVal) -> Result<Vec<String>> {
×
304
    match expr_val {
×
305
        ExprVal::Lit(lit) => Ok(vec![expr_lit_to_string(lit)]),
×
306
        ExprVal::Scope { expr, .. } => match expr {
×
307
            Some(val) => match val {
×
308
                ScopeVal::Expr(lit) => Ok(vec![expr_lit_to_string(lit)]),
×
309
                ScopeVal::List(list) => Ok(list.into_iter().map(expr_lit_to_string).collect()),
×
310
                ScopeVal::Sized { elem, len, .. } => {
×
311
                    let len = len.base10_parse::<usize>()?;
×
312
                    let mut data = vec![String::new(); len];
×
313
                    if len > 0 {
×
314
                        data[0] = expr_lit_to_string(elem);
×
315
                    }
316

317
                    Ok(data)
×
318
                }
319
            },
320
            None => Ok(vec![String::new()]),
×
321
        },
322
    }
323
}
324

325
fn collect_matrix(matrix: &MatrixInput) -> Result<Vec<Vec<String>>> {
×
326
    match &matrix.data {
×
327
        MatrixData::List(list) => {
×
328
            let mut data = vec![];
×
329
            for row in list {
×
330
                let row = collect_row(&row.elems)?;
×
331
                data.push(row)
×
332
            }
333

334
            Ok(data)
×
335
        }
336
        MatrixData::Static { elem, len, .. } => {
×
337
            let data = collect_row(&elem.elems)?;
×
338
            let len = len.base10_parse::<usize>()?;
×
339

340
            Ok(vec![data; len])
×
341
        }
342
    }
343
}
344

345
fn collect_row(elems: &MatrixRowElements) -> Result<Vec<String>> {
×
346
    let mut row = vec![];
×
347

348
    match elems {
×
349
        MatrixRowElements::List(list) => {
×
350
            for val in list {
×
351
                let vals = expr_val_to_list(val)?;
×
352
                row.extend(vals);
×
353
            }
354
        }
355
        MatrixRowElements::Static { elem, len, .. } => {
×
356
            let len = len.base10_parse::<usize>()?;
×
357
            let elem = expr_val_to_list(elem)?;
×
358
            let iter = std::iter::repeat(elem).take(len).flatten();
×
359

360
            row.extend(iter);
×
361
        }
362
    }
363

364
    Ok(row)
×
365
}
366

367
fn collect_vspan(matrix: &MatrixInput) -> Result<HashMap<(usize, usize), usize>> {
×
368
    let mut spans = HashMap::new();
×
369

370
    match &matrix.data {
×
371
        MatrixData::List(list) => {
×
372
            for (row, e) in list.iter().enumerate() {
×
373
                match &e.elems {
×
374
                    MatrixRowElements::List(list) => {
×
375
                        let mut i = 0;
×
376
                        for e in list {
×
377
                            match e {
×
378
                                ExprVal::Lit(_) => i += 1,
×
379
                                ExprVal::Scope { expr, .. } => match expr {
×
380
                                    Some(val) => match val {
×
381
                                        ScopeVal::Expr(_) => i += 1,
×
382
                                        ScopeVal::List(list) => i += list.len(),
×
383
                                        ScopeVal::Sized { len, .. } => {
×
384
                                            let len = len.base10_parse::<usize>()?;
×
385
                                            if len > 0 {
×
386
                                                spans.insert((row, i), len);
×
387
                                                i += len;
×
388
                                            }
389
                                        }
390
                                    },
391
                                    None => i += 1,
×
392
                                },
393
                            }
394
                        }
395
                    }
396
                    MatrixRowElements::Static { elem, len, .. } => {
×
397
                        let arr_len = len.base10_parse::<usize>()?;
×
398
                        match elem {
×
399
                            ExprVal::Lit(_) => {}
400
                            ExprVal::Scope { expr, .. } => {
×
401
                                if let Some(val) = expr {
×
402
                                    match val {
×
403
                                        ScopeVal::Expr(_) => {}
404
                                        ScopeVal::List(_) => {}
405
                                        ScopeVal::Sized { len, .. } => {
×
406
                                            let len = len.base10_parse::<usize>()?;
×
407
                                            if len > 0 {
×
408
                                                let iter =
×
409
                                                    (0..arr_len).map(|i| ((row, i * len), len));
410
                                                spans.extend(iter);
×
411
                                            }
412
                                        }
413
                                    }
414
                                }
415
                            }
416
                        }
417
                    }
418
                }
419
            }
420
        }
421
        MatrixData::Static { elem, len, .. } => {
×
422
            let count_rows = len.base10_parse::<usize>()?;
×
423

424
            match &elem.elems {
×
425
                MatrixRowElements::List(list) => {
×
426
                    let mut i = 0;
×
427
                    for e in list {
×
428
                        match e {
×
429
                            ExprVal::Lit(_) => i += 1,
×
430
                            ExprVal::Scope { expr, .. } => match expr {
×
431
                                Some(val) => match val {
×
432
                                    ScopeVal::Expr(_) => i += 1,
×
433
                                    ScopeVal::List(list) => i += list.len(),
×
434
                                    ScopeVal::Sized { len, .. } => {
×
435
                                        let len = len.base10_parse::<usize>()?;
×
436
                                        if len > 0 {
×
437
                                            spans
×
438
                                                .extend((0..count_rows).map(|row| ((row, i), len)));
×
439
                                            i += len;
×
440
                                        }
441
                                    }
442
                                },
443
                                None => i += 1,
×
444
                            },
445
                        }
446
                    }
447
                }
448
                MatrixRowElements::Static { .. } => {}
449
            }
450
        }
451
    }
452

453
    Ok(spans)
×
454
}
455

456
fn collect_hspan(matrix: &MatrixInput) -> Result<HashMap<(usize, usize), usize>> {
×
457
    let mut filled = HashSet::new();
×
458
    let mut empties = HashSet::new();
×
459
    match &matrix.data {
×
460
        MatrixData::List(list) => {
×
461
            for (row, e) in list.iter().enumerate() {
×
462
                match &e.elems {
×
463
                    MatrixRowElements::List(list) => {
×
464
                        let mut col = 0;
×
465
                        for e in list {
×
466
                            match e {
×
467
                                ExprVal::Lit(_) => col += 1,
×
468
                                ExprVal::Scope { expr, .. } => match expr {
×
469
                                    Some(val) => match val {
×
470
                                        ScopeVal::List(list) => col += list.len(),
×
471
                                        ScopeVal::Expr(_) => {
×
472
                                            filled.insert((row, col));
×
473
                                            col += 1;
×
474
                                        }
475
                                        ScopeVal::Sized { len, .. } => {
×
476
                                            filled.insert((row, col));
×
477
                                            let len = len.base10_parse::<usize>()?;
×
478
                                            col += len;
×
479
                                        }
480
                                    },
481
                                    None => {
×
482
                                        empties.insert((row, col));
×
483
                                        col += 1;
×
484
                                    }
485
                                },
486
                            }
487
                        }
488
                    }
489
                    MatrixRowElements::Static { elem, len, .. } => {
×
490
                        let arr_len = len.base10_parse::<usize>()?;
×
491

492
                        match elem {
×
493
                            ExprVal::Lit(_) => {}
494
                            ExprVal::Scope { expr, .. } => match expr {
×
495
                                Some(val) => match val {
×
496
                                    ScopeVal::List(_) => {}
497
                                    ScopeVal::Expr(_) => {
498
                                        filled.extend((0..arr_len).map(|col| (row, col)));
×
499
                                    }
500
                                    ScopeVal::Sized { len, .. } => {
×
501
                                        let len = len.base10_parse::<usize>()?;
×
502
                                        filled.extend((0..arr_len).map(|col| (row, col * len)));
×
503
                                    }
504
                                },
505
                                None => {
506
                                    empties.extend((0..arr_len).map(|col| (row, col)));
×
507
                                }
508
                            },
509
                        }
510
                    }
511
                }
512
            }
513
        }
514
        MatrixData::Static { .. } => {}
515
    }
516

517
    let mut spans = HashMap::new();
×
518
    for (row, col) in filled {
×
519
        let mut size = 0;
×
520
        for row in row + 1.. {
×
521
            if empties.contains(&(row, col)) {
×
522
                size += 1;
×
523
            } else {
524
                break;
525
            }
526
        }
527

528
        if size > 0 {
×
529
            spans.insert((row, col), size + 1);
×
530
        }
531
    }
532

533
    Ok(spans)
×
534
}
535

536
// todo: export the constants from crate so they could be highlighted by language servers.
537
// Yet this is unstable to do.
538
fn is_supported_theme(name: &str) -> bool {
×
539
    matches!(
×
540
        name,
541
        "EMPTY"
542
            | "BLANK"
543
            | "ASCII"
544
            | "ASCII_ROUNDED"
545
            | "DOTS"
546
            | "MODERN"
547
            | "SHARP"
548
            | "ROUNDED"
549
            | "EXTENDED"
550
            | "RE_STRUCTURED_TEXT"
551
            | "MARKDOWN"
552
            | "PSQL"
553
    )
554
}
555

556
fn apply_theme(table: &mut Table, name: &str) {
×
557
    match name {
558
        "EMPTY" => table.with(Style::empty()),
×
559
        "BLANK" => table.with(Style::blank()),
×
560
        "ASCII" => table.with(Style::ascii()),
×
561
        "ASCII_ROUNDED" => table.with(Style::ascii_rounded()),
×
562
        "DOTS" => table.with(Style::dots()),
×
563
        "MODERN" => table.with(Style::modern()),
×
564
        "SHARP" => table.with(Style::sharp()),
×
565
        "ROUNDED" => table.with(Style::rounded()),
×
566
        "EXTENDED" => table.with(Style::extended()),
×
567
        "RE_STRUCTURED_TEXT" => table.with(Style::re_structured_text()),
×
568
        "MARKDOWN" => table.with(Style::markdown()),
×
569
        "PSQL" => table.with(Style::psql()),
×
570
        _ => unreachable!(),
571
    };
572
}
573

574
fn build_padding(pad: Pad<LitInt>) -> syn::Result<Padding> {
×
575
    let left = pad.left.base10_parse::<usize>()?;
×
576
    let right = pad.right.base10_parse::<usize>()?;
×
577
    let top = pad.top.base10_parse::<usize>()?;
×
578
    let bottom = pad.bottom.base10_parse::<usize>()?;
×
579

580
    Ok(Padding::new(left, right, top, bottom))
×
581
}
582

583
fn build_margin(pad: Pad<LitInt>) -> syn::Result<Margin> {
×
584
    let left = pad.left.base10_parse::<usize>()?;
×
585
    let right = pad.right.base10_parse::<usize>()?;
×
586
    let top = pad.top.base10_parse::<usize>()?;
×
587
    let bottom = pad.bottom.base10_parse::<usize>()?;
×
588

589
    Ok(Margin::new(left, right, top, bottom))
×
590
}
591

592
fn panic_not_supported_theme(ident: &LitStr) {
×
NEW
593
    proc_macro_error2::abort!(
×
594
        ident,
595
        "The given settings is not supported";
596
        note="custom themes are yet not supported";
597
        help = r#"Supported themes are [EMPTY, BLANK, ASCII, ASCII_ROUNDED, DOTS, MODERN, SHARP, ROUNDED, EXTENDED, RE_STRUCTURED_TEXT, MARKDOWN, PSQL]"#
598
    )
599
}
600

601
fn panic_not_supported_alignment(ident: &LitStr) {
×
NEW
602
    proc_macro_error2::abort!(
×
603
        ident,
604
        "The given settings is not supported";
605
        help = r#"Supported alignment are [LEFT, RIGHT, CENTER, CENTER_VERTICAL, TOP, BOTTOM]"#
606
    )
607
}
608

609
#[allow(dead_code)]
610
fn panic_not_supported_bool(ident: &LitStr) {
×
NEW
611
    proc_macro_error2::abort!(
×
612
        ident,
613
        "Unexpected bool value";
614
        help = r#"Expected to get [TRUE, FALSE]"#
615
    )
616
}
617

618
fn panic_not_supported_settings(ident: &Ident) {
×
NEW
619
    proc_macro_error2::abort!(
×
620
        ident,
621
        "The given settings is not supported";
622
        help = r#"Supported list is [THEME, PADDING, MARGIN]"#
623
    )
624
}
625

626
pub(crate) fn build_table(table_st: &TableStruct) -> Result<String> {
×
627
    let mut table = create_table(&table_st.matrix)?;
×
628

629
    if table_st.comma_token.is_some() {
×
630
        apply_settings(&mut table, &table_st.settings)?;
×
631
    }
632

633
    let has_spans = table.get_config().has_column_spans() || table.get_config().has_row_spans();
×
634
    if has_spans {
×
635
        table.with(BorderSpanCorrection);
×
636
    }
637

638
    Ok(table.to_string())
×
639
}
640

641
fn apply_settings(
×
642
    table: &mut Table,
643
    settings: &Punctuated<KeyValue<LitStr>, Token![,]>,
644
) -> Result<()> {
645
    for kv in settings {
×
646
        config_table(table, kv)?;
×
647
    }
648

649
    Ok(())
×
650
}
651

652
fn is_supported_alignment(name: &str) -> bool {
×
653
    matches!(
×
654
        name,
655
        "LEFT" | "RIGHT" | "CENTER" | "CENTER_VERTICAL" | "TOP" | "BOTTOM"
656
    )
657
}
658

659
fn apply_alignment(table: &mut Table, name: &str) {
×
660
    match name {
661
        "LEFT" => table.with(Alignment::left()),
×
662
        "RIGHT" => table.with(Alignment::right()),
×
663
        "CENTER" => table.with(Alignment::center()),
×
664
        "CENTER_VERTICAL" => table.with(Alignment::center_vertical()),
×
665
        "TOP" => table.with(Alignment::top()),
×
666
        "BOTTOM" => table.with(Alignment::bottom()),
×
667
        _ => unreachable!(),
668
    };
669
}
670

671
fn config_table(table: &mut Table, kv: &KeyValue<LitStr>) -> Result<()> {
×
672
    if kv.key == "THEME" {
×
673
        let theme = kv.value.value();
×
674
        if !is_supported_theme(&theme) {
×
675
            panic_not_supported_theme(&kv.value);
×
676
        }
677

678
        apply_theme(table, &theme);
×
679
    } else if kv.key == "PADDING" {
×
680
        let padding = kv.value.parse().and_then(build_padding)?;
×
681
        table.with(padding);
×
682
    } else if kv.key == "MARGIN" {
×
683
        let margin = kv.value.parse().and_then(build_margin)?;
×
684
        table.with(margin);
×
685
    } else if kv.key == "ALIGNMENT" {
×
686
        let alignment = kv.value.value();
×
687
        if !is_supported_alignment(&alignment) {
×
688
            panic_not_supported_alignment(&kv.value);
×
689
        }
690

691
        apply_alignment(table, &alignment);
×
692
    } else {
693
        panic_not_supported_settings(&kv.key);
×
694
    }
695

696
    Ok(())
×
697
}
698

699
fn create_table(mat: &MatrixInput) -> Result<Table> {
×
700
    let data = collect_matrix(mat)?;
×
701
    let vspan = collect_vspan(mat)?;
×
702
    let hspan = collect_hspan(mat)?;
×
703

704
    let builder = Builder::from_iter(data);
×
705
    let mut table = builder.build();
×
706

707
    for (pos, span) in vspan {
×
708
        table.with(Modify::new(pos).with(Span::column(span)));
×
709
    }
710

711
    for (pos, span) in hspan {
×
712
        table.with(Modify::new(pos).with(Span::row(span)));
×
713
    }
714

715
    Ok(table)
×
716
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc