diff --git a/src/lib.rs b/src/lib.rs index 33f26f0..f7bafde 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ use itertools::Itertools; use pest::{iterators::Pair, Parser}; use pest_derive::Parser; use range::Bounds; -use std::{collections::BTreeSet, fmt::Display, fs}; +use std::{collections::BTreeSet, fmt::Display, fs, path::Path}; use thiserror::Error; mod range; pub mod token; @@ -16,6 +16,8 @@ pub enum ParseError { Grammar(#[from] Box>), #[error("hotkey config must contain one and only one main section")] MainSection, + #[error("unable to read config file")] + ReadingConfig(#[from] std::io::Error), } #[derive(Parser)] @@ -37,17 +39,28 @@ pub struct SwhkdParser { pub modes: Vec, } +/// Input to the grammar parser. +/// Can be either a string or a path. +pub enum ParserInput<'a> { + Raw(&'a str), + Path(&'a Path), +} + impl SwhkdParser { - pub fn from(raw: &str) -> Result { + pub fn from(input: ParserInput) -> Result { let mut root_imports = BTreeSet::new(); - let mut root = Self::as_import(raw, &mut root_imports)?; + let mut root = Self::as_import(input, &mut root_imports)?; root.imports = root_imports; Ok(root) } - pub fn as_import(raw: &str, seen: &mut BTreeSet) -> Result { - let parse_result = SwhkdGrammar::parse(Rule::main, raw) - .map_err(|err| ParseError::Grammar(Box::new(err)))?; - // TODO: set the source for raw strings as `anonymous` + fn as_import(input: ParserInput, seen: &mut BTreeSet) -> Result { + let (raw, source) = match input { + ParserInput::Raw(s) => (s.to_string(), ""), + // TODO: Use mmap instead of fs::read_to_string + ParserInput::Path(p) => (fs::read_to_string(p)?, p.to_str().unwrap_or_default()), + }; + let parse_result = SwhkdGrammar::parse(Rule::main, &raw) + .map_err(|err| ParseError::Grammar(Box::new(err.with_path(source))))?; let Some(contents) = parse_result.into_iter().next() else { return Err(ParseError::MainSection); diff --git a/src/main.rs b/src/main.rs index 2fe7483..ec0b654 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,12 @@ use anyhow::{bail, Result}; -use std::fs; -use sweet::SwhkdParser; +use std::path::Path; +use sweet::{ParserInput, SwhkdParser}; fn main() -> Result<()> { let Some(arg) = std::env::args().nth(1) else { bail!("please supply a path to a hotkeys config file"); }; - let raw_content = fs::read_to_string(arg)?; - let parser = SwhkdParser::from(&raw_content)?; + let parser = SwhkdParser::from(ParserInput::Path(Path::new(&arg)))?; for binding in parser.bindings { println!("{}", binding); diff --git a/tests/tests.rs b/tests/tests.rs index d44a30b..babf2cb 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,6 +1,6 @@ use sweet::{ token::{Key, KeyAttribute, Modifier}, - Binding, Definition, ParseError, SwhkdParser, + Binding, Definition, ParseError, ParserInput, SwhkdParser, }; #[test] @@ -9,7 +9,7 @@ fn test_basic_keybind() -> Result<(), ParseError> { r alacritty "; - SwhkdParser::from(&contents)?; + SwhkdParser::from(ParserInput::Raw(&contents))?; Ok(()) } @@ -25,7 +25,7 @@ w t /bin/firefox "; - let parsed = SwhkdParser::from(&contents)?; + let parsed = SwhkdParser::from(ParserInput::Raw(&contents))?; let known = vec![ Binding { @@ -68,7 +68,7 @@ w #t #/bin/firefox "; - let parsed = SwhkdParser::from(&contents)?; + let parsed = SwhkdParser::from(ParserInput::Raw(&contents))?; let known = vec![ Binding { @@ -99,7 +99,7 @@ super + 5 alacritty "; - let parsed = SwhkdParser::from(&contents)?; + let parsed = SwhkdParser::from(ParserInput::Raw(&contents))?; let known = vec![Binding { definition: Definition { modifiers: vec![Modifier("super".to_string())], @@ -120,7 +120,7 @@ shift + k + m notify-send 'Hello world!' "; - assert!(SwhkdParser::from(&contents).is_err()); + assert!(SwhkdParser::from(ParserInput::Raw(&contents)).is_err()); } #[test] @@ -130,7 +130,7 @@ shift + k + alt notify-send 'Hello world!' "; - assert!(SwhkdParser::from(&contents).is_err()); + assert!(SwhkdParser::from(ParserInput::Raw(&contents)).is_err()); } #[test] @@ -141,7 +141,7 @@ fn test_unfinished_plus_sign() { shift + alt + notify-send 'Hello world!' "; - assert!(SwhkdParser::from(&contents).is_err()); + assert!(SwhkdParser::from(ParserInput::Raw(&contents)).is_err()); } #[test] @@ -151,7 +151,7 @@ fn test_plus_sign_at_start() { notify-send 'Hello world!' "; - assert!(SwhkdParser::from(&contents).is_err()); + assert!(SwhkdParser::from(ParserInput::Raw(&contents)).is_err()); } #[test] @@ -172,7 +172,7 @@ altgr + i super + z notify-send 'Hello world!' "; - let parsed = SwhkdParser::from(&contents)?; + let parsed = SwhkdParser::from(ParserInput::Raw(&contents))?; let command = "notify-send 'Hello world!'".to_string(); let known = vec![ @@ -224,7 +224,7 @@ fn test_command_with_many_spaces() -> Result<(), ParseError> { p xbacklight -inc 10 -fps 30 -time 200 "; - let parsed = SwhkdParser::from(&contents)?; + let parsed = SwhkdParser::from(ParserInput::Raw(&contents))?; let known = vec![Binding { definition: Definition { @@ -249,7 +249,7 @@ pesto xterm "; - assert!(SwhkdParser::from(&contents).is_err()); + assert!(SwhkdParser::from(ParserInput::Raw(&contents)).is_err()); } #[test] @@ -264,7 +264,7 @@ w "; - assert!(SwhkdParser::from(&contents).is_err()); + assert!(SwhkdParser::from(ParserInput::Raw(&contents)).is_err()); } #[test] @@ -292,7 +292,7 @@ ctrl + 0 super + minus play-song.sh album "; - let parsed = SwhkdParser::from(&contents)?; + let parsed = SwhkdParser::from(ParserInput::Raw(&contents))?; let known = vec![ Binding { @@ -352,7 +352,7 @@ k mpc ls | dmenu | \\ sed -i 's/foo/bar/g' "; - let parsed = SwhkdParser::from(&contents)?; + let parsed = SwhkdParser::from(ParserInput::Raw(&contents))?; let known = vec![Binding { definition: Definition {