eipw_lint/lints/preamble/
date.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5 */
6
7use eipw_snippets::Snippet;
8
9use chrono::NaiveDate;
10
11use crate::{
12    lints::{Context, Error, Lint},
13    LevelExt, SnippetExt,
14};
15
16use serde::{Deserialize, Serialize};
17
18use std::fmt::{Debug, Display};
19
20#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
21#[serde(transparent)]
22pub struct Date<S>(pub S);
23
24impl<S> Lint for Date<S>
25where
26    S: Debug + Display + AsRef<str>,
27{
28    fn lint<'a>(&self, slug: &'a str, ctx: &Context<'a, '_>) -> Result<(), Error> {
29        let field = match ctx.preamble().by_name(self.0.as_ref()) {
30            None => return Ok(()),
31            Some(s) => s,
32        };
33
34        let value = field.value().trim();
35
36        let mut error = None;
37
38        let lengths: Vec<_> = value.split('-').map(str::len).collect();
39        if lengths != [4, 2, 2] {
40            error = Some("invalid length".to_string());
41        }
42
43        if let Err(e) = NaiveDate::parse_from_str(value, "%Y-%m-%d") {
44            error = Some(e.to_string());
45        }
46
47        let slice_label = match error {
48            Some(e) => e,
49            None => return Ok(()),
50        };
51
52        let label = format!(
53            "preamble header `{}` is not a date in the `YYYY-MM-DD` format",
54            self.0
55        );
56
57        let name_count = field.name().len();
58        let value_count = field.value().len();
59
60        ctx.report(
61            ctx.annotation_level().title(&label).id(slug).snippet(
62                Snippet::source(field.source())
63                    .fold(false)
64                    .line_start(field.line_start())
65                    .origin_opt(ctx.origin())
66                    .annotation(
67                        ctx.annotation_level()
68                            .span_utf8(field.source(), name_count + 1, value_count)
69                            .label(&slice_label),
70                    ),
71            ),
72        )?;
73
74        Ok(())
75    }
76}