Add jiff instead of time for time parsing

Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
Marcel Müller 2025-01-30 21:59:59 +01:00
parent ac76c030c6
commit 51012c19a7
4 changed files with 97 additions and 26 deletions

View file

@ -9,10 +9,10 @@ license.workspace = true
camino = { version = "1.1.9", features = ["serde", "serde1"] }
clap = { version = "4.5.27", features = ["derive"] }
futures = "0.3.31"
jiff = "0.1.28"
kdl.workspace = true
miette = { version = "7.4.0", features = ["fancy", "syntect-highlighter"] }
owo-colors = "4.1.0"
time = { version = "0.3.37", features = ["macros", "parsing", "serde-well-known"] }
tokio = { version = "1.43.0", features = ["full"] }
tokio-stream = { version = "0.1.17", features = ["full"] }
tracing = "0.1.41"

View file

@ -6,13 +6,14 @@ use camino::Utf8PathBuf;
use clap::Parser;
use futures::StreamExt;
use futures::TryStreamExt;
use jiff::fmt::temporal::DateTimeParser;
use jiff::Timestamp;
use kdl::KdlDocument;
use kdl::KdlValue;
use miette::IntoDiagnostic;
use miette::LabeledSpan;
use miette::NamedSource;
use owo_colors::OwoColorize;
use time::OffsetDateTime;
use tokio_stream::wrappers::ReadDirStream;
use tracing::info;
@ -40,10 +41,28 @@ async fn main() -> miette::Result<()> {
#[derive(Debug)]
pub struct Record {
kind: String,
at: OffsetDateTime,
at: Timestamp,
fields: BTreeMap<String, KdlValue>,
}
fn parse_timestamp(value: &str) -> miette::Result<Timestamp> {
let parser = DateTimeParser::new();
parser
.parse_timestamp(value)
.or_else(|_| {
parser
.parse_datetime(value)
.and_then(|date| date.in_tz("UTC").map(|z| z.timestamp()))
})
.or_else(|_| {
parser
.parse_date(value)
.and_then(|date| date.in_tz("UTC").map(|z| z.timestamp()))
})
.into_diagnostic()
}
fn parse_record(
bytes: &str,
definitions: &BTreeMap<String, Vec<Definition>>,
@ -74,8 +93,7 @@ fn parse_record(
))?;
};
let Ok(at) = OffsetDateTime::parse(at, &time::format_description::well_known::Rfc3339)
else {
let Ok(at) = parse_timestamp(at) else {
return Err(miette::diagnostic!(
labels = vec![LabeledSpan::new_primary_with_span(None, at_entry.span())],
"This datetime should be a string formatted as RFC3339."
@ -100,9 +118,13 @@ fn parse_record(
let kind = &matching_def.fields[name.value()];
if !kind.is_valid(&val) {
return Err(miette::diagnostic!(
labels = vec![LabeledSpan::new_primary_with_span(None, name.span())],
if let Err(e) = kind.validate(&val) {
Err(miette::diagnostic!(
labels = vec![LabeledSpan::new_primary_with_span(
Some(String::from("here")),
name.span()
)],
help = e,
"This field has the wrong kind."
))?;
}
@ -143,9 +165,8 @@ async fn load_records(
})
.flat_map(|val| futures::stream::iter(val.transpose()))
.and_then(|(name, bytes)| async move {
Ok(parse_record(&bytes, definitions).map_err(|e| {
e.with_source_code(NamedSource::new(name, bytes).with_language("kdl"))
})?)
parse_record(&bytes, definitions)
.map_err(|e| e.with_source_code(NamedSource::new(name, bytes).with_language("kdl")))
})
.map(|val| val.map(|recs| futures::stream::iter(recs).map(Ok::<_, miette::Report>)))
.try_flatten()
@ -161,12 +182,17 @@ pub enum DefinitionKind {
OneOf(Vec<String>),
}
impl DefinitionKind {
fn is_valid(&self, val: &KdlValue) -> bool {
fn validate(&self, val: &KdlValue) -> Result<(), String> {
match self {
DefinitionKind::String => val.is_string(),
DefinitionKind::String => val
.is_string()
.then_some(())
.ok_or("Expected a string here".to_string()),
DefinitionKind::OneOf(options) => val
.as_string()
.is_some_and(|val| options.iter().any(|o| o == val)),
.is_some_and(|val| options.iter().any(|o| o == val))
.then_some(())
.ok_or_else(|| format!("Expected one of: {}", options.join(", "))),
}
}
}
@ -183,7 +209,7 @@ impl TryFrom<&str> for DefinitionKind {
#[derive(Debug)]
pub struct Definition {
since: OffsetDateTime,
since: Timestamp,
fields: HashMap<String, DefinitionKind>,
}
@ -215,10 +241,7 @@ fn parse_definition(bytes: &str) -> miette::Result<Vec<Definition>> {
))?;
};
let since = match OffsetDateTime::parse(
since,
&time::format_description::well_known::Rfc3339,
) {
let since = match parse_timestamp(since) {
Ok(since) => since,
Err(_err) => {
return Err(miette::diagnostic!(