1extern crate file_lock;
30use std::fs::{OpenOptions,File};
31use std::io;
32use std::io::{Seek,SeekFrom,Write,Read};
33use std::fmt;
34use std::path::Path;
35use std::convert::AsRef;
36use std::os::unix::io::AsRawFd;
37use std::error;
38use std::convert::From;
39use std::result;
40use file_lock::{Lock,AccessMode,LockKind};
41use std::string::FromUtf8Error;
42use std::iter::Extend;
43
44#[derive(Debug)]
45pub struct StringLines {
47 file: File,
48 lock: Lock,
49}
50
51#[derive(Debug)]
52pub enum Error {
54 FileError(io::Error),
56 LockError(file_lock::Error),
58 Utf8Error(FromUtf8Error),
60}
61
62impl fmt::Display for Error {
63 fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
64 match self {
65 &Error::FileError(ref error) => {
66 let _ = try!(write!(f,"File error: {}",error));
67 },
68 &Error::LockError(ref error) => {
69 let _ = try!(write!(f,"Locking error: {:?}",error));
70 },
71 &Error::Utf8Error(ref error) => {
72 let _ = try!(write!(f,"UTF8 error: {:?}",error));
73 },
74 }
75 return Ok(());
76 }
77}
78
79impl error::Error for Error {
80 fn description(&self) -> &str {
81 match self {
82 &Error::FileError(..) => {
83 "File error"
84 },
85 &Error::LockError(..) => {
86 "Locking error"
87 },
88 &Error::Utf8Error(..) => {
89 "UTF8 error"
90 },
91 }
92 }
93}
94
95impl From<io::Error> for Error {
96 fn from(error: io::Error) -> Self {
97 return Error::FileError(error);
98 }
99}
100
101impl From<file_lock::Error> for Error {
102 fn from(error: file_lock::Error) -> Self {
103 return Error::LockError(error);
104 }
105}
106
107impl From<FromUtf8Error> for Error {
108 fn from(error: FromUtf8Error) -> Self {
109 return Error::Utf8Error(error);
110 }
111}
112
113pub type Result<T> = result::Result<T,Error>;
114
115impl StringLines {
116 pub fn open<P: AsRef<Path>>(path: P) -> Result<StringLines> {
118 let mut options = OpenOptions::new();
119 options.read(true);
120 options.write(true);
121 options.create(true);
122 let file = try!(options.open(path));
123 let lock = Lock::new(file.as_raw_fd());
124 Ok(StringLines {
125 file: file,
126 lock: lock,
127 })
128 }
129
130 pub fn push(&mut self,s:&str) -> Result<()> {
132 let s = format!("{}\n",s);
133 let _ = try!(self.lock.lock(LockKind::Blocking, AccessMode::Write));
134 let _ = try!(self.file.seek(SeekFrom::End(0)));
135 let _ = try!(self.file.write(s.as_bytes()));
136 let _ = try!(self.lock.unlock());
137 return Ok(());
138 }
139
140 fn pop_inner(&mut self,mut offset:i64,mut data:Vec<u8>) -> Result<Option<String>> {
141 if offset <= 0 {
142 offset = 0;
143 }
144 let _ = try!(self.file.set_len(offset as u64));
145 let _ = try!(self.lock.unlock());
146 if data.len() > 0 {
147 data.reverse();
148 let result = try!(String::from_utf8(data));
149 return Ok(Some(result));
150 } else {
151 return Ok(None);
152 }
153 }
154
155 pub fn pop(&mut self) -> Result<Option<String>> {
157 let _ = try!(self.lock.lock(LockKind::Blocking, AccessMode::Write));
158 let len = try!(self.file.metadata()).len() as i64;
159 let mut offset: i64 = len - 1024;
160 let mut data: Vec<u8> = Vec::with_capacity(1024);
161 if offset < 0 {
162 offset = 0;
163 }
164 loop {
165 let _ = try!(self.file.seek(SeekFrom::Start(offset as u64)));
166 let mut buf = [0; 1024];
167 match self.file.read(&mut buf) {
168 Ok(0) => {
169 break;
170 }
171 Ok(readed) => {
172 let buf = &buf[..readed];
173 let mut lines: Vec<&[u8]> = buf.split(|c| { *c == 0x0A}).collect();
174 let lines_len = lines.len() as i32;
175 if lines_len >= 2 {
176 let mut char_offset: i64 = 0;
177 for _ in 0..lines_len - 1 {
178 if let Some(last_line) = lines.pop() {
179 char_offset = char_offset + 1;
180 if last_line.len() >= 1 {
181 data.extend(last_line.iter().rev());
182 return self.pop_inner(
183 len - data.len() as i64 - char_offset,
184 data
185 );
186 }
187 }
188 }
189 }
190 offset = offset - buf.len() as i64;
191 data.extend(buf.iter().rev());
192 },
193 Err(error) => {
194 let _ = try!(self.lock.unlock());
195 return Err(Error::from(error));
196 },
197 }
198 if offset < 0 {
199 break;
200 }
201 }
202 return self.pop_inner(offset,data);
203 }
204
205 pub fn clear(&mut self) -> Result<()> {
207 let _ = try!(self.lock.lock(LockKind::Blocking, AccessMode::Write));
208 let _ = try!(self.file.set_len(0));
209 let _ = try!(self.lock.unlock());
210 return Ok(());
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217 use std::fs::remove_file;
218
219 #[test]
220 fn test_push_pop() {
221 let path = "target/test_push_pop.test";
222 let _ = remove_file(&path);
223 let mut lines = StringLines::open(path).expect("Unable to open file");
224 let mut items = vec![];
225 for i in 1..101 {
226 let line = format!("line {}",i);
227 let _ = lines.push(&line).expect("Unable to push line");
228 items.push(line);
229 }
230 loop {
231 match items.pop() {
232 Some(item_line) => {
233 let line = lines.pop().expect("Unable to pop line");
234 assert_eq!(Some(item_line), line);
235 },
236 None => {
237 break;
238 },
239 }
240 }
241 let line = lines.pop().expect("Unable to pop line");
242 assert_eq!(line, None);
243 let line = lines.pop().expect("Unable to pop line");
244 assert_eq!(line, None);
245 let line = lines.pop().expect("Unable to pop line");
246 assert_eq!(line, None);
247 }
248
249 #[test]
250 fn test_len() {
251 let path = "target/test_len.test";
252 let _ = remove_file(&path);
253 let _ = remove_file(&path);
254 let mut lines = StringLines::open(path).expect("Unable to open file");
255 let mut items = vec![];
256 for i in 1..101 {
257 let line = format!("line {}",i);
258 let _ = lines.push(&line).expect("Unable to push line");
259 items.push(line);
260 }
261 lines.clear().expect("Unable to clear collection");
262 let line = lines.pop().expect("Unable to pop line");
263 assert_eq!(line, None);
264 let line = lines.pop().expect("Unable to pop line");
265 assert_eq!(line, None);
266 let line = lines.pop().expect("Unable to pop line");
267 assert_eq!(line, None);
268 }
269}