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#[cfg_attr(feature = "schema-version", derive(schemars::JsonSchema))]
22#[serde(transparent)]
23pub struct Date<S>(pub S);
24
25impl<S> Lint for Date<S>
26where
27    S: Debug + Display + AsRef<str>,
28{
29    fn lint<'a>(&self, slug: &'a str, ctx: &Context<'a, '_>) -> Result<(), Error> {
30        let field = match ctx.preamble().by_name(self.0.as_ref()) {
31            None => return Ok(()),
32            Some(s) => s,
33        };
34
35        let value = field.value().trim();
36
37        let mut error = None;
38
39        let lengths: Vec<_> = value.split('-').map(str::len).collect();
40        if lengths != [4, 2, 2] {
41            error = Some("invalid length".to_string());
42        }
43
44        if let Err(e) = NaiveDate::parse_from_str(value, "%Y-%m-%d") {
45            error = Some(e.to_string());
46        }
47
48        let slice_label = match error {
49            Some(e) => e,
50            None => return Ok(()),
51        };
52
53        let label = format!(
54            "preamble header `{}` is not a date in the `YYYY-MM-DD` format",
55            self.0
56        );
57
58        let name_count = field.name().len();
59        let value_count = field.value().len();
60
61        ctx.report(
62            ctx.annotation_level().title(&label).id(slug).snippet(
63                Snippet::source(field.source())
64                    .fold(false)
65                    .line_start(field.line_start())
66                    .origin_opt(ctx.origin())
67                    .annotation(
68                        ctx.annotation_level()
69                            .span_utf8(field.source(), name_count + 1, value_count)
70                            .label(&slice_label),
71                    ),
72            ),
73        )?;
74
75        Ok(())
76    }
77}