From e5a38bab67f790803ff98484fc5835adba7bf62a Mon Sep 17 00:00:00 2001 From: diogo464 Date: Fri, 23 Sep 2022 13:45:57 +0100 Subject: rewrite --- src/config/mod.rs | 97 ++++++++++++++++++++ src/config/parse.rs | 254 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/config/parser.rs | 20 ++++ 3 files changed, 371 insertions(+) create mode 100644 src/config/mod.rs create mode 100644 src/config/parse.rs create mode 100644 src/config/parser.rs (limited to 'src/config') diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 0000000..98ba9fb --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1,97 @@ +mod parse; + +use std::path::Path; + +pub struct Config { + groups: Vec, +} + +pub struct Group { + includes: Vec, + links: Vec, + copies: Vec, +} + +pub struct IncludeAction { + group: String, +} + +pub struct LinkAction { + source: String, + target: String, +} + +pub struct CopyAction { + source: String, + target: String, +} + +pub fn parse(content: &str) -> std::io::Result { + todo!() +} + +pub fn parse_path(path: impl AsRef) -> std::io::Result { + todo!() +} + +pub fn format(content: &str) -> std::io::Result { + todo!() +} + +pub fn format_path(path: impl AsRef) -> std::io::Result { + todo!() +} + +pub fn format_inplace(path: impl AsRef) -> std::io::Result<()> { + todo!() +} + +impl Config { + pub fn groups(&self) -> impl Iterator { + std::iter::empty() + } + + pub fn group(&self, name: &str) -> Option<&Group> { + todo!() + } +} + +impl Group { + pub fn groups(&self) -> impl Iterator { + std::iter::empty() + } + + pub fn links(&self) -> impl Iterator { + std::iter::empty() + } + + pub fn copies(&self) -> impl Iterator { + std::iter::empty() + } +} + +impl IncludeAction { + pub fn name(&self) -> &str { + todo!() + } +} + +impl LinkAction { + pub fn source(&self) -> &str { + todo!() + } + + pub fn dest(&self) -> &str { + todo!() + } +} + +impl CopyAction { + pub fn source(&self) -> &str { + todo!() + } + + pub fn dest(&self) -> &str { + todo!() + } +} diff --git a/src/config/parse.rs b/src/config/parse.rs new file mode 100644 index 0000000..f1e33b0 --- /dev/null +++ b/src/config/parse.rs @@ -0,0 +1,254 @@ +use nom::{ + branch::alt, + bytes::complete::{tag, take_while, take_while1}, + character::complete::{alphanumeric0, alphanumeric1, multispace0, space1}, + combinator::map, + multi::{many0, separated_list0}, + sequence::{delimited, preceded}, +}; + +type Span<'s> = nom_locate::LocatedSpan<&'s str>; +type IResult<'s, I, O, E = ParserError<'s>> = nom::IResult; + +#[derive(Debug, PartialEq, Eq)] +struct ParserError<'s> { + location: Span<'s>, + message: Option, +} + +#[derive(Debug)] +struct KeyValueParser<'s> { + span: Span<'s>, + kvs: Vec>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct KeyValue<'s> { + key: &'s str, + value: &'s str, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct LinkAction { + source: String, + target: String, +} + +enum RichAction { + Link(LinkAction), +} + +struct RichGroup { + name: String, + items: Vec, +} + +enum RichItem { + Group(RichGroup), +} + +struct RichConfig {} + +impl<'s> ParserError<'s> { + fn custom(location: Span<'s>, message: impl Into) -> Self { + Self { + location, + message: Some(message.into()), + } + } + + fn missing_key(span: Span<'s>, key: &'s str) -> Self { + Self::custom(span, format!("missing key: {key}")) + } +} + +impl<'s> From> for nom::Err> { + fn from(e: ParserError<'s>) -> Self { + Self::Failure(e) + } +} + +impl<'s> nom::error::ParseError> for ParserError<'s> { + fn from_error_kind(input: Span<'s>, kind: nom::error::ErrorKind) -> Self { + Self::custom(input, format!("error kind: {kind:?}")) + } + + fn append(input: Span, kind: nom::error::ErrorKind, other: Self) -> Self { + todo!() + } + + fn or(self, other: Self) -> Self { + other + } + + fn from_char(input: Span<'s>, c: char) -> Self { + Self::custom(input, format!("invalid character: {c}")) + } +} + +impl<'s> KeyValueParser<'s> { + fn new(span: Span<'s>, kvs: Vec>) -> Self { + Self { span, kvs } + } + + fn get(&self, key: &'static str) -> Option<&'s str> { + self.kvs.iter().find(|kv| kv.key == key).map(|kv| kv.value) + } + + fn expect(&self, key: &'static str) -> Result<&'s str, ParserError<'s>> { + self.get(key) + .ok_or(ParserError::missing_key(self.span, key)) + } +} + +fn is_value_char(c: char) -> bool { + c.is_alphanumeric() || c == '_' || c == '-' || c == '.' || c == '/' +} + +fn whitespace0(i: Span) -> IResult { + take_while(char::is_whitespace)(i) +} + +fn whitespace1(i: Span) -> IResult { + take_while1(char::is_whitespace)(i) +} + +fn linesep(i: Span) -> IResult { + take_while(|c: char| c.is_whitespace() && c != '\n')(i)?; + take_while(|c: char| c == '\n')(i)?; + take_while(char::is_whitespace)(i) +} + +fn keyvalue(i: Span) -> IResult { + let (i, key) = alphanumeric1(i)?; + let (i, _) = tag("=")(i)?; + let (i, val) = delimited(tag("\""), take_while(is_value_char), tag("\""))(i)?; + Ok(( + i, + KeyValue { + key: key.fragment(), + value: val.fragment(), + }, + )) +} + +fn keyvalues(i: Span) -> IResult> { + separated_list0(space1, keyvalue)(i) +} + +fn link_action(i: Span) -> IResult { + let (i, kvs) = preceded(tag("link"), preceded(space1, keyvalues))(i)?; + eprintln!("{kvs:#?}"); + eprintln!("{i:?}"); + let kvparser = KeyValueParser::new(i, kvs); + let src = kvparser.expect("src")?.to_string(); + let dst = kvparser.expect("dst")?.to_string(); + Ok(( + i, + LinkAction { + source: src, + target: dst, + }, + )) +} + +fn rich_action(i: Span) -> IResult { + todo!() +} + +fn rich_group(i: Span) -> IResult { + let mut header = preceded(tag("group"), preceded(multispace0, alphanumeric1)); + let mut open_bracket = delimited(multispace0, tag("{"), multispace0); + let mut close_bracket = preceded(multispace0, tag("}")); + let mut body = separated_list0(linesep, rich_item); + + let (i, name) = header(i)?; + let (i, _) = open_bracket(i)?; + let (i, items) = body(i)?; + let (i, _) = close_bracket(i)?; + + Ok(( + i, + RichGroup { + name: name.to_string(), + items, + }, + )) +} + +fn rich_item(i: Span) -> IResult { + alt((map(rich_group, RichItem::Group),))(i) +} + +fn config(i: Span) -> IResult { + let (_, groups) = many0(rich_group)(i)?; + todo!() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_keyvalue() { + let input = Span::new(r#"key="value""#); + let (rem, kv) = keyvalue(input).unwrap(); + assert!(rem.is_empty()); + assert_eq!( + kv, + KeyValue { + key: "key", + value: "value" + } + ); + } + + #[test] + fn parse_keyvalues() { + let kvs = vec![ + KeyValue { + key: "key1", + value: "value1", + }, + KeyValue { + key: "key2", + value: "value2", + }, + ]; + + let input = Span::new(r#"key1="value1" key2="value2""#); + let (rem, res) = keyvalues(input).unwrap(); + assert!(rem.is_empty()); + assert_eq!(res, kvs); + + let kvs = vec![ + KeyValue { + key: "src", + value: "tmux/", + }, + KeyValue { + key: "dst", + value: ".config/tmux", + }, + ]; + + let input = Span::new(r#"src="tmux/" dst=".config/tmux""#); + let (rem, res) = keyvalues(input).unwrap(); + assert!(rem.is_empty()); + assert_eq!(res, kvs); + } + + #[test] + fn parse_link_action() { + let input = Span::new(r#"link src="tmux/" dst=".config/tmux""#); + let (rem, res) = link_action(input).unwrap(); + assert!(rem.is_empty()); + assert_eq!( + res, + LinkAction { + source: "tmux/".to_string(), + target: ".config/tmux".to_string() + } + ); + } +} diff --git a/src/config/parser.rs b/src/config/parser.rs new file mode 100644 index 0000000..102f15a --- /dev/null +++ b/src/config/parser.rs @@ -0,0 +1,20 @@ +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +struct Location { + line: u32, + column: u32, +} + +#[derive(Debug, Clone)] +struct Scanner<'s> { + location: Location, + content: std::str::Chars<'s>, +} + +impl<'s> Scanner<'s> { + fn new(content: &'s str) -> Self { + Self { + location: Default::default(), + content: content.chars(), + } + } +} -- cgit