Skip to content

Commit f66c2b8

Browse files
committed
Add time pre-filter as argument
1 parent 0c945fc commit f66c2b8

File tree

5 files changed

+100
-26
lines changed

5 files changed

+100
-26
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ cargo install --git https://github.com/tuplecats/1c-log-viewer
1818
### Параметры
1919
````
2020
-d, --directory=PATH Путь к директории с файлами логов
21-
(Также ищет файлы в поддиректориях)
21+
(Также ищет файлы в поддиректориях)
22+
23+
--from=TIME Временая точка начала чтения логов.
24+
Формат: now-{digit}{s/m/h/d/w}
25+
Пример: now-1d или now-30s
2226
````
2327

2428
````bash

src/app.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::{error::Error, time::Duration};
22
use std::cell::RefCell;
33
use std::rc::Rc;
4+
use chrono::NaiveDateTime;
45
use crossterm::{
56
event,
67
event::{Event, KeyCode},
@@ -30,9 +31,6 @@ enum ActiveWidget {
3031
}
3132

3233
pub struct App {
33-
// parse info
34-
pub filepath: String,
35-
3634
pub table: Rc<RefCell<TableView>>,
3735
pub search: Rc<RefCell<LineEdit>>,
3836
pub text: Rc<RefCell<KeyValueView>>,
@@ -44,14 +42,26 @@ pub struct App {
4442
}
4543

4644
impl App {
47-
pub fn new<T: Into<String>>(dir: T, widths: Vec<Constraint>) -> Self {
45+
pub fn new<T: Into<String>>(dir: T, date: Option<NaiveDateTime>) -> Self {
4846
let dir = dir.into();
49-
let log_data = Rc::new(RefCell::new(LogCollection::new(LogParser::parse(dir.clone()))));
47+
let widths = vec![
48+
Constraint::Percentage(20),
49+
Constraint::Percentage(20),
50+
Constraint::Percentage(20),
51+
Constraint::Percentage(20),
52+
Constraint::Percentage(20),
53+
];
54+
55+
let log_data = Rc::new(RefCell::new(
56+
LogCollection::new(
57+
LogParser::parse(dir, date)
58+
)
59+
));
60+
5061
let mut table_view = TableView::new(widths);
5162
table_view.set_model(log_data.clone());
5263

5364
let app = Self {
54-
filepath: dir.into(),
5565
table: Rc::new(RefCell::new(table_view)),
5666
search: Rc::new(RefCell::new(LineEdit::new("Filter".into()))),
5767
text: Rc::new(RefCell::new(KeyValueView::new())),

src/main.rs

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,37 +18,41 @@ use crossterm::{
1818
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
1919
};
2020
use std::error::Error;
21-
use tui::{backend::CrosstermBackend, layout::Constraint, Terminal};
21+
use tui::{backend::CrosstermBackend, Terminal};
2222

2323

2424
use parser::logdata::LogCollection;
25+
use crate::util::parse_date;
2526

2627
#[derive(Parser, Debug)]
27-
#[clap(author, version, about, long_about = None)]
28+
#[clap(author, version, about, long_about = None, verbatim_doc_comment)]
2829
struct Args {
29-
#[clap(short, long, value_parser)]
30+
/// Путь к директории с файлами логов
31+
/// (Также ищет файлы в поддиректориях)
32+
#[clap(short, long, value_parser, verbatim_doc_comment)]
3033
directory: String,
34+
35+
/// Временая точка начала чтения логов.
36+
/// Формат: now-{digit}{s/m/h/d/w}
37+
/// Пример: now-1d или now-30s
38+
#[clap(long, value_parser, verbatim_doc_comment)]
39+
from: Option<String>,
3140
}
3241

3342
fn main() -> Result<(), Box<dyn Error>> {
3443
let args = Args::parse();
44+
let date = match &args.from {
45+
Some(value) => Some(parse_date(value.as_str())?),
46+
None => None,
47+
};
3548

3649
enable_raw_mode()?;
3750
let mut stdout = std::io::stdout();
3851
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
3952
let backend = CrosstermBackend::new(stdout);
4053
let mut terminal = Terminal::new(backend)?;
41-
App::new(
42-
args.directory.as_str(),
43-
vec![
44-
Constraint::Percentage(20),
45-
Constraint::Percentage(20),
46-
Constraint::Percentage(20),
47-
Constraint::Percentage(20),
48-
Constraint::Percentage(20),
49-
],
50-
)
51-
.run(&mut terminal)?;
54+
55+
App::new(args.directory.as_str(), date).run(&mut terminal)?;
5256

5357
// restore terminal
5458
disable_raw_mode()?;

src/parser/mod.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -287,14 +287,14 @@ impl Iterator for LineIter {
287287
pub struct LogParser;
288288

289289
impl LogParser {
290-
pub fn parse(dir: String) -> Receiver<LogString> {
290+
pub fn parse(dir: String, date: Option<NaiveDateTime>) -> Receiver<LogString> {
291291
let (sender, receiver) = channel();
292-
std::thread::spawn(move || LogParser::parse_dir(dir, sender));
292+
std::thread::spawn(move || LogParser::parse_dir(dir, date, sender));
293293
receiver
294294
}
295295

296296
// А может сделать итератор, который парсит
297-
fn parse_dir(path: String, sender: Sender<LogString>) -> io::Result<()> {
297+
fn parse_dir(path: String, date: Option<NaiveDateTime>, sender: Sender<LogString>) -> io::Result<()> {
298298
let walk = WalkDir::new(path)
299299
.follow_links(true)
300300
.into_iter()
@@ -312,7 +312,12 @@ impl LogParser {
312312
let month = name[2..4].parse::<u32>().unwrap();
313313
let day = name[4..6].parse::<u32>().unwrap();
314314
let hour = name[6..8].parse::<u32>().unwrap();
315-
Some((e, NaiveDate::from_ymd(year, month, day).and_hms(hour, 0, 0)))
315+
316+
let date_time = NaiveDate::from_ymd(year, month, day).and_hms(hour, 0, 0);
317+
match date {
318+
Some(date) if date_time < date => None,
319+
_ => Some((e, date_time))
320+
}
316321
} else {
317322
None
318323
}
@@ -354,7 +359,18 @@ impl LogParser {
354359
continue
355360
}
356361

357-
lines[index] = data.next();
362+
loop {
363+
match data.next() {
364+
Some(line) => match date {
365+
Some(date) if line.time < date => {},
366+
_ => {
367+
lines[index] = Some(line);
368+
break
369+
}
370+
},
371+
_ => lines[index] = None
372+
}
373+
}
358374
}
359375

360376
let min = lines.iter()

src/util.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,43 @@
1+
use chrono::{Duration, Local, NaiveDateTime};
2+
use regex::Regex;
3+
4+
pub fn parse_date(value: &str) -> Result<NaiveDateTime, regex::Error> {
5+
let now = Local::now().naive_local();
6+
let regex = Regex::new(r#"^now-(\d+)([smhdw])$"#)?;
7+
8+
match regex.captures(value) {
9+
Some(captures) if captures.len() == 3 => {
10+
match (captures.get(1), captures.get(2)) {
11+
(Some(offset), Some(char)) => {
12+
let offset = offset.as_str().parse::<u64>()
13+
.map_err(|_| regex::Error::Syntax(String::from("Cannot parse number")))?;
14+
15+
match char.as_str() {
16+
"s" => {
17+
Ok(now - Duration::seconds(offset as i64))
18+
}
19+
"m" => {
20+
Ok(now - Duration::minutes(offset as i64))
21+
}
22+
"h" => {
23+
Ok(now - Duration::hours(offset as i64))
24+
}
25+
"d" => {
26+
Ok(now - Duration::days(offset as i64))
27+
}
28+
"w" => {
29+
Ok(now - Duration::weeks(offset as i64))
30+
}
31+
_ => unreachable!(),
32+
}
33+
},
34+
_ => Err(regex::Error::Syntax("Invalid captures".to_string()))
35+
}
36+
},
37+
_ => Err(regex::Error::Syntax("Invalid value".to_string()))
38+
}
39+
}
40+
141
pub fn sub_strings(string: &str, sub_len: usize) -> Vec<&str> {
242
let mut subs = Vec::with_capacity(string.len() * 2 / sub_len);
343
let mut iter = string.chars();

0 commit comments

Comments
 (0)