use { crate::{ config::{ Status, context::Context, extractor::{Extractor, ExtractorError, opt, recover, str, val}, parser::{DataType, ParseResult, Parser, UnexpectedDataType}, parsers::exec::{ExecParser, ExecParserError}, }, toml::{ toml_span::{Span, Spanned, SpannedExt}, toml_value::Value, }, }, indexmap::IndexMap, jay_config::status::MessageFormat, thiserror::Error, }; #[derive(Debug, Error)] pub enum StatusParserError { #[error(transparent)] Expected(#[from] UnexpectedDataType), #[error(transparent)] Exec(#[from] ExecParserError), #[error(transparent)] Extract(#[from] ExtractorError), #[error("Expected `plain`, `pango`, or `i3bar` but found {0}")] UnknownFormat(String), } pub struct StatusParser<'a>(pub &'a Context<'a>); impl Parser for StatusParser<'_> { type Value = Status; type Error = StatusParserError; const EXPECTED: &'static [DataType] = &[DataType::Table]; fn parse_table( &mut self, span: Span, table: &IndexMap, Spanned>, ) -> ParseResult { let mut ext = Extractor::new(self.0, span, table); let (format, exec, separator) = ext.extract(( opt(str("format")), val("exec"), recover(opt(str("i3bar-separator"))), ))?; let format = match format { Some(f) => match f.value { "plain" => MessageFormat::Plain, "pango" => MessageFormat::Pango, "i3bar" => MessageFormat::I3Bar, _ => { return Err( StatusParserError::UnknownFormat(f.value.to_string()).spanned(f.span) ); } }, _ => MessageFormat::Plain, }; let exec = exec.parse_map(&mut ExecParser(self.0))?; let separator = match separator { None => None, Some(sep) if format == MessageFormat::I3Bar => Some(sep.value.to_string()), Some(sep) => { log::warn!( "Separator has no effect for format {format:?}: {}", self.0.error3(sep.span) ); None } }; Ok(Status { format, exec, separator, }) } }