~kmaasrud/twtxt

dc31a8f6921468b685d05a868711d477161a6b49 — Knut Magnus Aasrud 5 months ago 0af9a8f
feat: add line parser
3 files changed, 45 insertions(+), 9 deletions(-)

M src/dt.rs
M src/lib.rs
M src/main.rs
M src/dt.rs => src/dt.rs +5 -8
@@ 87,10 87,10 @@ where
                                nanosecond += 10u32.pow(8 - p as u32) * (c as u8 - b'0') as u32;
                            }
                            chars.next();
                            p += 1;
                        }
                        _ => break,
                    }
                    p += 1;
                }
            }
            'Z' | 'z' => break,


@@ 113,14 113,11 @@ where
                    return Err(Rfc3339Error::OffsetOutOfRange(offset_minutes));
                }
            }
            _ => break,
            '\t' => break,
            _ => return Err(Rfc3339Error::ExpectedTab),
        };
    }

    if let Some(c) = chars.next() {
        return Err(Rfc3339Error::UnexpectedChar(c));
    }

    Ok(Datetime {
        year,
        month,


@@ 172,11 169,11 @@ fn next_time_sep(chars: &mut impl Iterator<Item = char>) -> Result<(), Rfc3339Er
#[derive(Debug)]
pub enum Rfc3339Error {
    UnexpectedEnd,
    UnexpectedChar(char),
    ExpectedDigit,
    ExpectedDash,
    ExpectedColon,
    ExpectedTimeSep,
    ExpectedTab,
    MonthOutOfRange(u8),
    DayOutOfRange(u8),
    HourOutOfRange(u8),


@@ 193,11 190,11 @@ impl Display for Rfc3339Error {
        use Rfc3339Error::*;
        match self {
            UnexpectedEnd => write!(f, "unexpected end of string"),
            UnexpectedChar(c) => write!(f, "unexpected char `{}`", c),
            ExpectedDigit => write!(f, "expected digit"),
            ExpectedDash => write!(f, "expected dash"),
            ExpectedColon => write!(f, "expected colon"),
            ExpectedTimeSep => write!(f, "expected time separator"),
            ExpectedTab => write!(f, "expected tab"),
            MonthOutOfRange(m) => write!(f, "month out of range: {}", m),
            DayOutOfRange(d) => write!(f, "day out of range: {}", d),
            HourOutOfRange(h) => write!(f, "hour out of range: {}", h),

M src/lib.rs => src/lib.rs +29 -0
@@ 1,3 1,32 @@
mod dt;

use std::{
    error::Error,
    io::{BufRead, BufReader, Read},
};

pub use dt::Datetime;

#[derive(Debug)]
pub struct Twt {
    pub datetime: Datetime,
    pub body: String,
}

pub fn parse_twts<R: Read>(r: R) -> Result<Vec<Twt>, Box<dyn Error>> {
    BufReader::new(r)
        .lines()
        .filter(|line| line.as_ref().map(|line| !line.is_empty()).unwrap_or(true))
        .map(|line| parse_line(&line?))
        .collect()
}

pub fn parse_line(line: &str) -> Result<Twt, Box<dyn Error>> {
    let mut chars = line.chars();
    let dt = dt::parse_datetime(&mut chars)?;

    Ok(Twt {
        datetime: dt,
        body: chars.collect(),
    })
}

M src/main.rs => src/main.rs +11 -1
@@ 1,3 1,13 @@
use twtxt::parse_twts;

const TEST: &str = "
2016-02-04T13:30:00+01:00\tYou can really go crazy here! ┐(゚∀゚)┌
2016-02-03T23:05:00+01:00\t@<example http://example.org/twtxt.txt> welcome to twtxt!
2016-02-01T11:00:00+01:00\tThis is just another example.
2015-12-12T12:00:00+01:00\tFiat lux!
";

fn main() {
    println!("Hello, world!");
    let twt = parse_twts(TEST.as_bytes()).unwrap();
    println!("{:?}", twt);
}