Skip to content

Instantly share code, notes, and snippets.

@yuriks
Forked from anonymous/playground.rs
Created April 17, 2017 06:01
Show Gist options
  • Save yuriks/8ccb3534f389485660b5f91f7c5fb87e to your computer and use it in GitHub Desktop.
Save yuriks/8ccb3534f389485660b5f91f7c5fb87e to your computer and use it in GitHub Desktop.
Shared via Rust Playground
const TEST_DATA : &'static str = "Device: ID=1; Fw=16071801; Evt=2; Alarms: CoilRevesed=OFF; Power: Active=1753W; Reactive=279var; Appearent=403VA; Line: Current=7.35900021; Voltage=230.08V; Phase=-43,841rad; Peaks: 7.33199978;7.311999799999999;7.53000021;7.48400021;7.54300022;7.62900019;7.36499977;7.28599977;7.37200022;7.31899977; FFT Re: 9748;46;303;33;52;19;19;39;-455; FFT Img: 2712;6;-792;-59;1386;-19;963;33;462; UTC Time: 2016-10-4 16:47:50; hz: 49.87; WiFi Strength: -62; Dummy: 20";
fn partition(s: &str) -> (&str, Option<char>, &str) {
const TOKEN_SEPARATORS : &'static [char] = &[':', '=', ';'];
let idx = s.find(TOKEN_SEPARATORS).unwrap_or(s.len());
let (head, tail) = s.split_at(idx);
let mut tail_it = tail.chars();
let separator = tail_it.next();
(head, separator, tail_it.as_str())
}
#[derive(Debug, Eq, PartialEq)]
enum Token<'a> {
SectionName(&'a str),
AttributeName(&'a str),
Value(&'a str),
}
fn get_next_token<'a>(s: &mut &'a str) -> Token<'a> {
let (value, sep, rest) = partition(s);
let trimmed = value.trim();
*s = rest;
match sep {
Some(':') => Token::SectionName(trimmed),
Some('=') => Token::AttributeName(trimmed),
Some(';') | None => Token::Value(trimmed),
_ => unreachable!()
}
}
pub type Values<'a> = Vec<&'a str>;
#[derive(Debug)]
pub struct Attribute<'a> {
key: &'a str,
values: Values<'a>,
}
#[derive(Debug)]
pub struct Section<'a> {
key: &'a str,
values: Values<'a>,
attribs: Vec<Attribute<'a>>,
}
#[derive(Debug)]
pub struct Event<'a> {
sections: Vec<Section<'a>>,
}
struct SRoot<'a>(Vec<Section<'a>>);
struct SSection<'a>(Section<'a>, SRoot<'a>);
struct SAttribute<'a>(Attribute<'a>, SSection<'a>);
enum Parser<'a> {
Root(SRoot<'a>),
Section(SSection<'a>),
Attribute(SAttribute<'a>),
}
impl<'a> Parser<'a> {
fn push_token(self, token: Token<'a>) -> Self {
match self {
Parser::Root(root) => root.push_token(token),
Parser::Section(section) => section.push_token(token),
Parser::Attribute(attr) => attr.push_token(token),
}
}
fn finalize(self) -> Event<'a> {
match self {
Parser::Root(root) => root.finalize(),
Parser::Section(section) => section.pop().finalize(),
Parser::Attribute(attr) => attr.pop().pop().finalize(),
}
}
}
impl<'a> SRoot<'a> {
fn push_token(self, token: Token<'a>) -> Parser<'a> {
match token {
Token::SectionName(name) => Parser::Section(SSection(Section {
key: name,
values: vec![],
attribs: vec![],
}, self)),
Token::AttributeName(..) | Token::Value(..) => panic!("Missing section"),
}
}
fn finalize(self) -> Event<'a> { Event{ sections: self.0 } }
}
impl<'a> SSection<'a> {
fn push_token(mut self, token: Token<'a>) -> Parser<'a> {
match token {
Token::SectionName(..) => self.pop().push_token(token),
Token::AttributeName(name) => Parser::Attribute(SAttribute(Attribute {
key: name,
values: vec![],
}, self)),
Token::Value(value) => {
self.0.values.push(value);
Parser::Section(self)
},
}
}
fn pop(mut self) -> SRoot<'a> {
(self.1).0.push(self.0);
self.1
}
}
impl<'a> SAttribute<'a> {
fn push_token(mut self, token: Token<'a>) -> Parser<'a> {
match token {
Token::SectionName(..) => self.pop().pop().push_token(token),
Token::AttributeName(..) => self.pop().push_token(token),
Token::Value(value) => {
self.0.values.push(value);
Parser::Attribute(self)
},
}
}
fn pop(mut self) -> SSection<'a> {
(self.1).0.attribs.push(self.0);
self.1
}
}
pub fn parse_event_string(mut s: & str) -> Event {
let mut parser = Parser::Root(SRoot(vec![]));
while !s.is_empty() {
parser = parser.push_token(get_next_token(&mut s));
}
parser.finalize()
}
mod tests {
use super::*;
fn partition_splits_at_character() {
assert_eq!(partition("abá=123; xxx"), ("abá", Some('='), "123; xxx"));
}
fn partition_splits_at_eof() {
assert_eq!(partition("abá"), ("abá", None, ""));
}
fn token_retrieves_tokens() {
let mut s = "Section Name: Attrib Name=5W;6.4";
assert_eq!(get_next_token(&mut s), Token::SectionName("Section Name"));
assert_eq!(get_next_token(&mut s), Token::AttributeName("Attrib Name"));
assert_eq!(get_next_token(&mut s), Token::Value("5W"));
assert_eq!(get_next_token(&mut s), Token::Value("6.4"));
assert_eq!(s, "");
}
fn parse_event_string_works_on_small_input() {
let input = "Section Name: Attrib Name=5W;6.4";
let event = parse_event_string(&input);
println!("small:\n{:?}", event);
}
fn parse_event_string_works_on_large_input() {
let event = parse_event_string(&TEST_DATA);
println!("large:\n{:?}", event);
}
// Work-around for playground
pub fn invoke_all() {
partition_splits_at_character();
partition_splits_at_eof();
token_retrieves_tokens();
parse_event_string_works_on_small_input();
parse_event_string_works_on_large_input();
}
}
fn main() {
tests::invoke_all();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment