streams_rs/
lib.rs

1pub mod fn_stream;
2pub mod io_stream;
3pub type StreamGetFn<'a,T> = dyn FnMut() -> Option<T> + 'a; 
4
5pub enum StreamResult<T> {
6    Ok(T),
7    Err(StreamError)
8}
9
10impl<T: fmt::Debug> fmt::Debug for StreamResult<T> {
11    fn fmt(&self,f: &mut fmt::Formatter<'_>) -> fmt::Result {
12        match self {
13            Self::Ok(v) => write!(f,"Ok({:?})",v),
14            Self::Err(e) => write!(f,"Err({:?})",e),
15        }
16    }
17}
18
19#[derive(Clone,Debug,PartialEq,Eq,Hash)]
20pub enum StreamError {
21    EmptyStream,
22    NotHandledPattern,
23    Str(String),
24}
25
26use std::fmt;
27impl fmt::Display for StreamError {
28    fn fmt(&self,f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        match self {
30            StreamError::EmptyStream => write!(f,"empty stream"),
31            StreamError::NotHandledPattern => write!(f,"stream pattern not handled"),
32            StreamError::Str(s) => write!(f,"stream error: '{}'",s),
33        }
34    }
35}
36
37
38/// Suppose you need to process each line of a text file. One way to do this is to read the file in as a single large string and use something
39/// like `split` to turn it into a list. This works when the file is small, but because the entire file is loaded into memory, it does not
40/// scale well when the file is large.
41/// 
42/// More commonly, the read_line function can be used to read one line at a time from a channel. This typically looks like:
43/// ```rust 
44///     // LineReadStream is just example, you can implement your own stream for reading files.
45///     use streams_rs::*;
46///     use io_stream::*;
47///     let mut c = std::io::Cursor::new(b"a\n b\n c");
48///     let mut stream = LineReadStream::new(&mut c);
49///     let _ = smatch!(match (stream) {
50///         [a=> b=> c =>] => {
51///             println!("{:?} {:?} {:?}",a,b,c);
52///         } 
53///     }); 
54/// ```
55/// 
56pub trait Stream<'a> {
57    type Item;
58    /// Returns token at `x`. It's possible to cache values. FnStream for example caches all tokens returned from getter.
59    fn token(&mut self,x: usize) -> StreamResult<Self::Item>;
60    /// Basucally same as truncating vector.
61    fn junk(&mut self,x: usize);
62    /// Return stream position, does not used by macro or streams-rs, but might be usefull for someone.
63    fn pos(&self) -> usize;
64}
65
66/// Macro for matching on streams. 
67/// 
68/// This macro will not catch any error for you so your getter should return `StreamResult<Result>` if you might have any errors. 
69/// If value not matched then `StreamResult::Err(StreamError::EmptyStream)` is returned.
70/// 
71#[macro_export]
72macro_rules! smatch {
73    (@p $body: expr;$stream: expr,$cnt: expr;  -> $($pat: tt)*) => {{
74
75            smatch!(@p $body; ; $stream,$cnt    ;$($pat)*)
76
77    }
78    };
79    (@p $body: expr; $($assign: ident),*; $stream: expr,$cnt: expr; _ => $($rest:tt)*) => {
80        {let res = $stream.token($cnt);
81            match res {
82                StreamResult::Ok(_) => {smatch!(@p $body; $name; $stream,$cnt + 1;$($rest)*)},
83                _ => (None,false)
84            }
85        /*$(
86            let $assign = $assign;
87        )**/
88        
89
90        }
91    };
92    (@p $body: expr; $($assign: ident),*; $stream: expr,$cnt: expr; $name : ident => $($rest:tt)*) => {
93        {let p = $stream.token($cnt);
94        if let StreamResult::Ok($name) = p {
95            smatch!(@p $body; $name; $stream,$cnt + 1;$($rest)*)
96        } else {
97            (None,false)
98        }
99        }
100    };
101    (@p $body:expr;$($assign: ident),*;$stream: expr,$cnt: expr; $p: pat => $($rest:tt)*) => {{
102        let p = $stream.token($cnt);
103        if let StreamResult::Ok($p) = p {
104            
105            $(
106                let $assign = $assign;
107            )*
108            smatch!(@p $body; ; $stream,$cnt + 1;$($rest)*)
109        } else {
110            
111            (None,false)
112        }
113    }
114    };
115    /*(@p $body: expr;$($assign: ident),*;$stream: expr,$cnt: expr;  $name : ident = $p: pat => $($rest:tt)*) => {
116        {let x = $stream.token($cnt);
117        if let $p = x {
118            let $name = x;
119            $(
120                let $assign = $assign;
121            )*
122            smatch!(@p $body; $($assign),*$name; $stream,$cnt + 1;$($rest)*)
123        } else {
124            (None,false)
125        }}
126    };*/
127    (@p $body: expr; $($assign: ident),*; $stream: expr,$cnt: expr; $name : ident = $p: expr => $($rest:tt)*) => {
128        {let $name = $p;
129        /*$(
130            let $assign = $assign;
131        )**/
132        smatch!(@p $body; $name; $stream,$cnt + 1;$($rest)*)
133        }
134    };
135
136    (@p $body: expr; $($assign: ident),*;$stream: expr,$cnt: expr;) => {
137        {$stream.junk($cnt);
138        (Some($body),true)
139        }
140    };
141
142    (match ($s: expr) {
143        $(
144            [ $($p: tt)* ] => $body: expr
145        ),*
146    }) => {
147        loop {
148        $(
149            let res =  smatch!(@p $body; $s,0; -> $($p)*);
150            if let (Some(val),true) = res {
151                break $crate::StreamResult::Ok(val);
152            }
153        )*
154        break $crate::StreamResult::Err($crate::StreamError::EmptyStream)
155        }
156    };
157
158}