aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/db/db.rs160
-rw-r--r--src/db/mod.rs161
-rw-r--r--src/deck.rs8
-rw-r--r--src/gui/gui.rs98
-rw-r--r--src/gui/message.rs7
-rw-r--r--src/gui/mod.rs95
-rw-r--r--src/gui/question.rs90
-rw-r--r--src/gui/util.rs2
-rw-r--r--src/main.rs12
-rw-r--r--src/util/event.rs10
-rw-r--r--src/util/serialization.rs6
-rw-r--r--src/util/time.rs2
12 files changed, 318 insertions, 333 deletions
diff --git a/src/db/db.rs b/src/db/db.rs
deleted file mode 100644
index b42da3f..0000000
--- a/src/db/db.rs
+++ /dev/null
@@ -1,160 +0,0 @@
-use crate::{
- model::{card::Card, entry::Entry},
- space_repetition,
- util::serialization,
-};
-use anyhow::Result;
-use rusqlite::{params, Connection};
-use rusqlite_migration::{Migrations, M};
-
-use crate::util::time;
-
-pub fn init(database: String) -> Result<Connection> {
- let mut conn = Connection::open(database)?;
- let migrations = Migrations::new(vec![
- M::up(include_str!("sql/1-init.sql")),
- M::up(include_str!("sql/2-primary-key-question-responses.sql")),
- ]);
- migrations.to_latest(&mut conn)?;
- Ok(conn)
-}
-
-pub fn last_deck_read(conn: &Connection) -> Option<u64> {
- let mut stmt = conn
- .prepare("SELECT deck_read FROM cards ORDER BY deck_read DESC LIMIT 1")
- .ok()?;
-
- let mut rows = stmt.query([]).ok()?;
- let row = rows.next().ok()??;
- row.get(0).ok()?
-}
-
-/// Synchronize the DB with the deck:
-///
-/// - insert new cards,
-/// - keep existing cards,
-/// - hide unused cards (keep state in case the card is added back afterward).
-pub fn synchronize(conn: &Connection, entries: Vec<Entry>) -> Result<()> {
- let now = time::seconds_since_unix_epoch()?;
-
- let state = serde_json::to_string(&space_repetition::init())?;
-
- for entry in entries {
- let concat_1 = serialization::words_to_line(&entry.part_1);
- let concat_2 = serialization::words_to_line(&entry.part_2);
-
- for w in entry.part_1.iter() {
- insert(&conn, now, &w, &concat_2, &state)?;
- }
-
- for w in entry.part_2.iter() {
- insert(&conn, now, &w, &concat_1, &state)?;
- }
- }
-
- delete_read_before(&conn, now)?;
-
- Ok(())
-}
-
-fn insert(
- conn: &Connection,
- now: u64,
- question: &String,
- responses: &String,
- state: &String,
-) -> Result<()> {
- conn.execute(
- "
- INSERT INTO cards (question, responses, state, created, deck_read, ready)
- VALUES (?, ?, ?, ?, ?, ?)
- ON CONFLICT (question, responses) DO UPDATE SET deck_read = ?, deleted = null
- ",
- params![question, responses, state, now, now, now, now],
- )?;
-
- Ok(())
-}
-
-fn delete_read_before(conn: &Connection, t: u64) -> Result<()> {
- conn.execute(
- "UPDATE cards SET deleted = ? WHERE deck_read < ?",
- params![t, t],
- )?;
-
- Ok(())
-}
-
-pub fn pick_random_ready(conn: &Connection) -> Option<Card> {
- let now = time::seconds_since_unix_epoch().ok()?;
-
- let mut stmt = conn
- .prepare(
- "
- SELECT question, responses, state, ready
- FROM cards
- WHERE deleted IS NULL AND ready <= ?
- ORDER BY RANDOM()
- LIMIT 1
- ",
- )
- .ok()?;
-
- let mut rows = stmt.query([now]).ok()?;
- let row = rows.next().ok()??;
- let state_str: String = row.get(2).ok()?;
- let responses_str: String = row.get(1).ok()?;
-
- Some(Card {
- question: row.get(0).ok()?,
- responses: serialization::line_to_words(&responses_str),
- state: serde_json::from_str(&state_str).ok()?,
- ready: row.get(3).ok()?,
- })
-}
-
-pub fn next_ready(conn: &Connection) -> Option<u64> {
- let mut stmt = conn
- .prepare(
- "
- SELECT ready
- FROM cards
- WHERE deleted IS NULL
- ORDER BY ready
- LIMIT 1
- ",
- )
- .ok()?;
-
- let mut rows = stmt.query([]).ok()?;
- let row = rows.next().ok()??;
- row.get(0).ok()?
-}
-
-pub fn count_available(conn: &Connection) -> Option<i32> {
- let now = time::seconds_since_unix_epoch().ok()?;
- let mut stmt = conn
- .prepare("SELECT COUNT(*) FROM cards WHERE ready <= ? AND deleted IS NULL")
- .ok()?;
-
- let mut rows = stmt.query([now]).ok()?;
- let row = rows.next().ok()??;
- row.get(0).ok()?
-}
-
-pub fn update(conn: &Connection, question: &String, state: &space_repetition::State) -> Result<()> {
- let now = time::seconds_since_unix_epoch()?;
- let ready = now + state.get_interval_seconds();
- let state_str = serde_json::to_string(state)?;
-
- conn.execute(
- "
- UPDATE cards
- SET state = ?, updated = ?, ready = ?
- WHERE question = ?
- ",
- params![state_str, now, ready, question],
- )?;
-
- Ok(())
-}
diff --git a/src/db/mod.rs b/src/db/mod.rs
index dec1023..f59aad5 100644
--- a/src/db/mod.rs
+++ b/src/db/mod.rs
@@ -1 +1,160 @@
-pub mod db;
+use crate::{
+ model::{card::Card, entry::Entry},
+ space_repetition,
+ util::serialization,
+};
+use anyhow::Result;
+use rusqlite::{params, Connection};
+use rusqlite_migration::{Migrations, M};
+
+use crate::util::time;
+
+pub fn init(database: String) -> Result<Connection> {
+ let mut conn = Connection::open(database)?;
+ let migrations = Migrations::new(vec![
+ M::up(include_str!("sql/1-init.sql")),
+ M::up(include_str!("sql/2-primary-key-question-responses.sql")),
+ ]);
+ migrations.to_latest(&mut conn)?;
+ Ok(conn)
+}
+
+pub fn last_deck_read(conn: &Connection) -> Option<u64> {
+ let mut stmt = conn
+ .prepare("SELECT deck_read FROM cards ORDER BY deck_read DESC LIMIT 1")
+ .ok()?;
+
+ let mut rows = stmt.query([]).ok()?;
+ let row = rows.next().ok()??;
+ row.get(0).ok()?
+}
+
+/// Synchronize the DB with the deck:
+///
+/// - insert new cards,
+/// - keep existing cards,
+/// - hide unused cards (keep state in case the card is added back afterward).
+pub fn synchronize(conn: &Connection, entries: Vec<Entry>) -> Result<()> {
+ let now = time::seconds_since_unix_epoch()?;
+
+ let state = serde_json::to_string(&space_repetition::init())?;
+
+ for entry in entries {
+ let concat_1 = serialization::words_to_line(&entry.part_1);
+ let concat_2 = serialization::words_to_line(&entry.part_2);
+
+ for w in entry.part_1.iter() {
+ insert(conn, now, w, &concat_2, &state)?;
+ }
+
+ for w in entry.part_2.iter() {
+ insert(conn, now, w, &concat_1, &state)?;
+ }
+ }
+
+ delete_read_before(conn, now)?;
+
+ Ok(())
+}
+
+fn insert(
+ conn: &Connection,
+ now: u64,
+ question: &str,
+ responses: &str,
+ state: &str,
+) -> Result<()> {
+ conn.execute(
+ "
+ INSERT INTO cards (question, responses, state, created, deck_read, ready)
+ VALUES (?, ?, ?, ?, ?, ?)
+ ON CONFLICT (question, responses) DO UPDATE SET deck_read = ?, deleted = null
+ ",
+ params![question, responses, state, now, now, now, now],
+ )?;
+
+ Ok(())
+}
+
+fn delete_read_before(conn: &Connection, t: u64) -> Result<()> {
+ conn.execute(
+ "UPDATE cards SET deleted = ? WHERE deck_read < ?",
+ params![t, t],
+ )?;
+
+ Ok(())
+}
+
+pub fn pick_random_ready(conn: &Connection) -> Option<Card> {
+ let now = time::seconds_since_unix_epoch().ok()?;
+
+ let mut stmt = conn
+ .prepare(
+ "
+ SELECT question, responses, state, ready
+ FROM cards
+ WHERE deleted IS NULL AND ready <= ?
+ ORDER BY RANDOM()
+ LIMIT 1
+ ",
+ )
+ .ok()?;
+
+ let mut rows = stmt.query([now]).ok()?;
+ let row = rows.next().ok()??;
+ let state_str: String = row.get(2).ok()?;
+ let responses_str: String = row.get(1).ok()?;
+
+ Some(Card {
+ question: row.get(0).ok()?,
+ responses: serialization::line_to_words(&responses_str),
+ state: serde_json::from_str(&state_str).ok()?,
+ ready: row.get(3).ok()?,
+ })
+}
+
+pub fn next_ready(conn: &Connection) -> Option<u64> {
+ let mut stmt = conn
+ .prepare(
+ "
+ SELECT ready
+ FROM cards
+ WHERE deleted IS NULL
+ ORDER BY ready
+ LIMIT 1
+ ",
+ )
+ .ok()?;
+
+ let mut rows = stmt.query([]).ok()?;
+ let row = rows.next().ok()??;
+ row.get(0).ok()?
+}
+
+pub fn count_available(conn: &Connection) -> Option<i32> {
+ let now = time::seconds_since_unix_epoch().ok()?;
+ let mut stmt = conn
+ .prepare("SELECT COUNT(*) FROM cards WHERE ready <= ? AND deleted IS NULL")
+ .ok()?;
+
+ let mut rows = stmt.query([now]).ok()?;
+ let row = rows.next().ok()??;
+ row.get(0).ok()?
+}
+
+pub fn update(conn: &Connection, question: &str, state: &space_repetition::State) -> Result<()> {
+ let now = time::seconds_since_unix_epoch()?;
+ let ready = now + state.get_interval_seconds();
+ let state_str = serde_json::to_string(state)?;
+
+ conn.execute(
+ "
+ UPDATE cards
+ SET state = ?, updated = ?, ready = ?
+ WHERE question = ?
+ ",
+ params![state_str, now, ready, question],
+ )?;
+
+ Ok(())
+}
diff --git a/src/deck.rs b/src/deck.rs
index e0f9fab..a414a02 100644
--- a/src/deck.rs
+++ b/src/deck.rs
@@ -32,14 +32,14 @@ pub fn read(deck: &str) -> Result<Vec<Entry>> {
let line = line?;
let line = line.trim();
- if !line.starts_with("#") && !line.is_empty() {
- if !line.starts_with("-") {
+ if !line.starts_with('#') && !line.is_empty() {
+ if !line.starts_with('-') {
return Err(Error::from(ParseError {
line: index + 1,
message: "an entry should starts with “-”.".to_string(),
}));
} else {
- let translation = line[1..].trim().split(":").collect::<Vec<&str>>();
+ let translation = line[1..].trim().split(':').collect::<Vec<&str>>();
if translation.len() != 2 {
return Err(Error::from(ParseError {
line: index + 1,
@@ -68,7 +68,7 @@ pub fn read(deck: &str) -> Result<Vec<Entry>> {
Ok(entries)
}
-pub fn pp_from_path(path: &String) -> Option<String> {
+pub fn pp_from_path(path: &str) -> Option<String> {
Some(capitalize(
Path::new(&path).with_extension("").file_name()?.to_str()?,
))
diff --git a/src/gui/gui.rs b/src/gui/gui.rs
deleted file mode 100644
index 92b1a72..0000000
--- a/src/gui/gui.rs
+++ /dev/null
@@ -1,98 +0,0 @@
-use crate::deck;
-use crate::util::time;
-use crate::{db::db, gui::message, gui::question, space_repetition, util::event::Events};
-use anyhow::Result;
-use rusqlite::Connection;
-use std::{fs, io, time::Duration};
-use termion::{raw::IntoRawMode, raw::RawTerminal, screen::AlternateScreen};
-use tui::{backend::TermionBackend, Terminal};
-
-type Term = Terminal<TermionBackend<AlternateScreen<RawTerminal<io::Stdout>>>>;
-
-pub fn terminal() -> Result<Term> {
- let stdout = io::stdout().into_raw_mode()?;
- let stdout = AlternateScreen::from(stdout);
- let backend = TermionBackend::new(stdout);
- Ok(Terminal::new(backend)?)
-}
-
-pub fn synchronize(
- conn: &Connection,
- term: &mut Term,
- events: &Events,
- deck_path: &str,
- deck_name: &str,
-) -> Result<()> {
- let last_modified = time::seconds_since_unix_epoch_of(fs::metadata(deck_path)?.modified()?)?;
- let last_deck_read = db::last_deck_read(&conn);
- let must_synchronize = last_deck_read.map(|r| r < last_modified).unwrap_or(true);
-
- if must_synchronize {
- let _ = message::show(term, events, &deck_name, "Synchronization du deck…", false);
- time::wait_at_least(
- || db::synchronize(&conn, deck::read(&deck_path)?),
- Duration::from_secs(1),
- )?;
- }
-
- Ok(())
-}
-
-pub fn start(
- conn: &Connection,
- term: &mut Term,
- events: &Events,
- deck_name: &String,
-) -> Result<()> {
- let mut answers = 0;
-
- loop {
- let now = time::seconds_since_unix_epoch()?;
- let title = title(deck_name, answers, db::count_available(&conn).unwrap_or(0));
-
- match db::pick_random_ready(&conn) {
- Some(card) => {
- let difficulty = question::ask(term, events, &title, &card)?;
- answers += 1;
- db::update(
- &conn,
- &card.question,
- &space_repetition::update(card.state, difficulty),
- )?;
- }
- None => {
- let message = match db::next_ready(&conn) {
- Some(ready) => format!(
- "Prochaine carte disponible dans {}.",
- time::pp_duration(ready - now)
- ),
- None => format!("Aucune carte n’est disponible. Votre deck est-il vide ?"),
- };
- let _ = message::show(term, events, &title, &message, true);
- break;
- }
- }
- }
-
- Ok(())
-}
-
-fn title(deck_name: &String, answers: i32, available_cards: i32) -> String {
- if answers == 0 && available_cards == 0 {
- deck_name.to_string()
- } else if available_cards == 0 {
- format!(
- "{} ({} / {})",
- deck_name,
- answers,
- answers + available_cards
- )
- } else {
- format!(
- "{} ({} / {})",
- deck_name,
- answers + 1,
- answers + available_cards
- )
- }
-}
diff --git a/src/gui/message.rs b/src/gui/message.rs
index 28a1d2c..b938150 100644
--- a/src/gui/message.rs
+++ b/src/gui/message.rs
@@ -33,11 +33,8 @@ pub fn show<B: Backend>(
})?;
if wait {
- if let Event::Input(key) = events.next()? {
- match key {
- Key::Char('q') => break,
- _ => (),
- }
+ if let Event::Input(Key::Char('q')) = events.next()? {
+ break
}
} else {
break;
diff --git a/src/gui/mod.rs b/src/gui/mod.rs
index f351eba..92cd943 100644
--- a/src/gui/mod.rs
+++ b/src/gui/mod.rs
@@ -1,4 +1,97 @@
-pub mod gui;
pub mod message;
pub mod question;
pub mod util;
+
+use crate::deck;
+use crate::util::time;
+use crate::{db, space_repetition, util::event::Events};
+use anyhow::Result;
+use rusqlite::Connection;
+use std::{fs, io, time::Duration};
+use termion::{raw::IntoRawMode, raw::RawTerminal, screen::AlternateScreen};
+use tui::{backend::TermionBackend, Terminal};
+
+type Term = Terminal<TermionBackend<AlternateScreen<RawTerminal<io::Stdout>>>>;
+
+pub fn terminal() -> Result<Term> {
+ let stdout = io::stdout().into_raw_mode()?;
+ let stdout = AlternateScreen::from(stdout);
+ let backend = TermionBackend::new(stdout);
+ Ok(Terminal::new(backend)?)
+}
+
+pub fn synchronize(
+ conn: &Connection,
+ term: &mut Term,
+ events: &Events,
+ deck_path: &str,
+ deck_name: &str,
+) -> Result<()> {
+ let last_modified = time::seconds_since_unix_epoch_of(fs::metadata(deck_path)?.modified()?)?;
+ let last_deck_read = db::last_deck_read(conn);
+ let must_synchronize = last_deck_read.map(|r| r < last_modified).unwrap_or(true);
+
+ if must_synchronize {
+ let _ = message::show(term, events, deck_name, "Synchronization du deck…", false);
+ time::wait_at_least(
+ || db::synchronize(conn, deck::read(deck_path)?),
+ Duration::from_secs(1),
+ )?;
+ }
+
+ Ok(())
+}
+
+pub fn start(conn: &Connection, term: &mut Term, events: &Events, deck_name: &str) -> Result<()> {
+ let mut answers = 0;
+
+ loop {
+ let now = time::seconds_since_unix_epoch()?;
+ let title = title(deck_name, answers, db::count_available(conn).unwrap_or(0));
+
+ match db::pick_random_ready(conn) {
+ Some(card) => {
+ let difficulty = question::ask(term, events, &title, &card)?;
+ answers += 1;
+ db::update(
+ conn,
+ &card.question,
+ &space_repetition::update(card.state, difficulty),
+ )?;
+ }
+ None => {
+ let message = match db::next_ready(conn) {
+ Some(ready) => format!(
+ "Prochaine carte disponible dans {}.",
+ time::pp_duration(ready - now)
+ ),
+ None => "Aucune carte n’est disponible. Votre deck est-il vide ?".to_string(),
+ };
+ let _ = message::show(term, events, &title, &message, true);
+ break;
+ }
+ }
+ }
+
+ Ok(())
+}
+
+fn title(deck_name: &str, answers: i32, available_cards: i32) -> String {
+ if answers == 0 && available_cards == 0 {
+ deck_name.to_string()
+ } else if available_cards == 0 {
+ format!(
+ "{} ({} / {})",
+ deck_name,
+ answers,
+ answers + available_cards
+ )
+ } else {
+ format!(
+ "{} ({} / {})",
+ deck_name,
+ answers + 1,
+ answers + available_cards
+ )
+ }
+}
diff --git a/src/gui/question.rs b/src/gui/question.rs
index 211bcda..5f060e3 100644
--- a/src/gui/question.rs
+++ b/src/gui/question.rs
@@ -28,7 +28,7 @@ enum Answer {
pub fn ask<B: Backend>(
terminal: &mut Terminal<B>,
events: &Events,
- title: &String,
+ title: &str,
card: &Card,
) -> Result<Difficulty> {
let mut state = State {
@@ -59,7 +59,7 @@ pub fn ask<B: Backend>(
let question = Paragraph::new(util::center_vertically(chunks[1], &card.question))
.style(match state.answer {
Answer::Write => {
- if state.input == "" {
+ if state.input.is_empty() {
Style::default().fg(Color::Yellow)
} else {
Style::default()
@@ -86,47 +86,44 @@ pub fn ask<B: Backend>(
.wrap(Wrap { trim: true });
f.render_widget(answer, chunks[2]);
- match state.answer {
- Answer::Difficulty {
- difficulty: selected,
- } => {
- if !is_correct(&state.input, &card.responses) || card.responses.len() > 1 {
- let paragraph = Paragraph::new(util::center_vertically(
- chunks[3],
- &serialization::words_to_line(&card.responses),
- ))
- .alignment(Alignment::Center);
- f.render_widget(paragraph, chunks[3]);
- };
+ if let Answer::Difficulty {
+ difficulty: selected,
+ } = state.answer
+ {
+ if !is_correct(&state.input, &card.responses) || card.responses.len() > 1 {
+ let paragraph = Paragraph::new(util::center_vertically(
+ chunks[3],
+ &serialization::words_to_line(&card.responses),
+ ))
+ .alignment(Alignment::Center);
+ f.render_widget(paragraph, chunks[3]);
+ };
- let difficulties = card.state.difficulties();
- let l = difficulties.len();
- let sep = Span::styled(" • ", Style::default());
- let tabs = difficulties
- .iter()
- .enumerate()
- .map(|(i, d)| {
- let style = if *d == selected {
- Style::default()
- .fg(Color::Yellow)
- .add_modifier(Modifier::UNDERLINED)
- } else {
- Style::default().add_modifier(Modifier::DIM)
- };
- let d = Span::styled(difficulty::label(*d), style);
- if i < l - 1 {
- [d, sep.clone()].to_vec()
- } else {
- [d].to_vec()
- }
- })
- .collect::<Vec<Vec<Span>>>()
- .concat();
- let p =
- Paragraph::new(Text::from(Spans::from(tabs))).alignment(Alignment::Center);
- f.render_widget(p, chunks[4]);
- }
- _ => {}
+ let difficulties = card.state.difficulties();
+ let l = difficulties.len();
+ let sep = Span::styled(" • ", Style::default());
+ let tabs = difficulties
+ .iter()
+ .enumerate()
+ .map(|(i, d)| {
+ let style = if *d == selected {
+ Style::default()
+ .fg(Color::Yellow)
+ .add_modifier(Modifier::UNDERLINED)
+ } else {
+ Style::default().add_modifier(Modifier::DIM)
+ };
+ let d = Span::styled(difficulty::label(*d), style);
+ if i < l - 1 {
+ [d, sep.clone()].to_vec()
+ } else {
+ [d].to_vec()
+ }
+ })
+ .collect::<Vec<Vec<Span>>>()
+ .concat();
+ let p = Paragraph::new(Text::from(Spans::from(tabs))).alignment(Alignment::Center);
+ f.render_widget(p, chunks[4]);
}
})?;
@@ -171,15 +168,14 @@ pub fn ask<B: Backend>(
}
}
-fn is_correct(input: &String, responses: &Vec<String>) -> bool {
+fn is_correct(input: &str, responses: &[String]) -> bool {
responses
.iter()
- .map(|r| r.split("(").collect::<Vec<&str>>()[0].trim())
- .collect::<Vec<&str>>()
- .contains(&input.as_str())
+ .map(|r| r.split('(').collect::<Vec<&str>>()[0].trim())
+ .any(|x| x == input)
}
-fn relative_element<T: Clone + PartialEq>(xs: &Vec<T>, x: &T, ri: i32) -> Option<T> {
+fn relative_element<T: Clone + PartialEq>(xs: &[T], x: &T, ri: i32) -> Option<T> {
let i = xs.iter().position(|t| t == x)? as i32 + ri;
if i >= 0 && i < xs.len() as i32 {
Some(xs[i as usize].clone())
diff --git a/src/gui/util.rs b/src/gui/util.rs
index 38ed1e7..2314aba 100644
--- a/src/gui/util.rs
+++ b/src/gui/util.rs
@@ -12,7 +12,7 @@ pub fn title(str: &str) -> Paragraph {
)
}
-pub fn center_vertically(chunk: Rect, text: &String) -> String {
+pub fn center_vertically(chunk: Rect, text: &str) -> String {
let text_lines = text.lines().count();
let chunk_inner_lines: usize = (chunk.height - 2).into();
let blank_lines = chunk_inner_lines - text_lines;
diff --git a/src/main.rs b/src/main.rs
index 3e3e741..bed2ce1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -19,15 +19,15 @@ struct Opt {
fn main() -> Result<()> {
let deck_path = Opt::from_args().deck;
- let conn = db::db::init(db_path(&deck_path))?;
- let deck_name = deck::pp_from_path(&deck_path).unwrap_or("Deck".to_string());
- let mut term = gui::gui::terminal()?;
+ let conn = db::init(db_path(&deck_path))?;
+ let deck_name = deck::pp_from_path(&deck_path).unwrap_or_else(|| "Deck".to_string());
+ let mut term = gui::terminal()?;
let events = Events::new();
- gui::gui::synchronize(&conn, &mut term, &events, &deck_path, &deck_name)?;
- gui::gui::start(&conn, &mut term, &events, &deck_name)
+ gui::synchronize(&conn, &mut term, &events, &deck_path, &deck_name)?;
+ gui::start(&conn, &mut term, &events, &deck_name)
}
-fn db_path(deck_path: &String) -> String {
+fn db_path(deck_path: &str) -> String {
let mut path = PathBuf::from(deck_path);
path.set_extension("db");
path.to_string_lossy().to_string()
diff --git a/src/util/event.rs b/src/util/event.rs
index 33ee9ec..05d8581 100644
--- a/src/util/event.rs
+++ b/src/util/event.rs
@@ -43,12 +43,10 @@ impl Events {
let tx = tx.clone();
thread::spawn(move || {
let stdin = io::stdin();
- for evt in stdin.keys() {
- if let Ok(key) = evt {
- if let Err(err) = tx.send(Event::Input(key)) {
- eprintln!("{}", err);
- return;
- }
+ for key in stdin.keys().flatten() {
+ if let Err(err) = tx.send(Event::Input(key)) {
+ eprintln!("{}", err);
+ return;
}
}
})
diff --git a/src/util/serialization.rs b/src/util/serialization.rs
index cc2899f..61b3a83 100644
--- a/src/util/serialization.rs
+++ b/src/util/serialization.rs
@@ -1,10 +1,10 @@
-pub fn line_to_words(line: &String) -> Vec<String> {
- line.split("|")
+pub fn line_to_words(line: &str) -> Vec<String> {
+ line.split('|')
.map(|w| w.trim().to_string())
.filter(|w| !w.is_empty())
.collect()
}
-pub fn words_to_line(words: &Vec<String>) -> String {
+pub fn words_to_line(words: &[String]) -> String {
words.join(" | ")
}
diff --git a/src/util/time.rs b/src/util/time.rs
index d9a9f72..b8a85e6 100644
--- a/src/util/time.rs
+++ b/src/util/time.rs
@@ -3,7 +3,7 @@ use std::thread;
use std::time::SystemTime;
pub fn seconds_since_unix_epoch() -> Result<u64> {
- Ok(seconds_since_unix_epoch_of(SystemTime::now())?)
+ seconds_since_unix_epoch_of(SystemTime::now())
}
pub fn seconds_since_unix_epoch_of(time: SystemTime) -> Result<u64> {