aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/sync.rs206
1 files changed, 122 insertions, 84 deletions
diff --git a/src/sync.rs b/src/sync.rs
index 1635ad6..6e3d84b 100644
--- a/src/sync.rs
+++ b/src/sync.rs
@@ -4,11 +4,12 @@ use crate::{
};
use anyhow::Result;
use rusqlite::Connection;
+use std::collections::HashMap;
use std::collections::HashSet;
pub fn run(conn: &mut Connection, deck_path: &str) -> Result<()> {
let db_entries = db::all(conn)?;
- let lines = deck::read(deck_path)?;
+ let lines = deck::read_file(deck_path)?;
let Diff {
new,
deleted,
@@ -29,29 +30,23 @@ struct Diff {
}
fn diff(db_entries: Vec<DbEntry>, lines: Vec<Line>) -> Diff {
- let mut file_questions: HashSet<Question> = HashSet::new();
- let mut db_questions_not_deleted: HashSet<Question> = HashSet::new();
- let mut db_questions_deleted: HashSet<Question> = HashSet::new();
+ let mut file_questions = HashMap::<String, Vec<String>>::new();
+ let mut db_questions_not_deleted = HashSet::<Question>::new();
+ let mut db_questions_deleted = HashSet::<Question>::new();
for Line { part_1, part_2 } in lines {
- for question in part_1.clone() {
- let mut responses = part_2.clone();
- responses.sort();
- file_questions.insert(Question {
- question,
- responses,
- });
- }
- for question in part_2 {
- let mut responses = part_1.clone();
- responses.sort();
- file_questions.insert(Question {
- question,
- responses,
- });
- }
+ insert(&mut file_questions, part_1.clone(), part_2.clone());
+ insert(&mut file_questions, part_2, part_1);
}
+ let file_questions: HashSet<Question> = file_questions
+ .iter()
+ .map(|(question, responses)| Question {
+ question: question.to_string(),
+ responses: responses.to_vec(),
+ })
+ .collect();
+
for DbEntry {
question,
mut responses,
@@ -97,77 +92,120 @@ fn diff(db_entries: Vec<DbEntry>, lines: Vec<Line>) -> Diff {
}
}
+fn insert(map: &mut HashMap<String, Vec<String>>, questions: Vec<String>, responses: Vec<String>) {
+ for question in questions {
+ let mut responses = responses.clone();
+ responses.sort();
+ match map.get_mut(&question) {
+ Some(existing_responses) => existing_responses.append(&mut responses),
+ None => {
+ map.insert(question, responses);
+ }
+ };
+ }
+}
+
#[cfg(test)]
mod tests {
- use super::*;
+ use super::{deck, DbEntry, Diff, Question};
+ use std::collections::HashSet;
#[test]
- fn test_diff() {
- let db_entries = vec![
- DbEntry {
- question: "A".to_string(),
- responses: vec!["A".to_string()],
- deleted: None,
- },
- DbEntry {
- question: "B".to_string(),
- responses: vec!["B".to_string()],
- deleted: None,
- },
- DbEntry {
- question: "C".to_string(),
- responses: vec!["C".to_string()],
- deleted: Some(0),
- },
- DbEntry {
- question: "D".to_string(),
- responses: vec!["D".to_string()],
- deleted: Some(0),
- },
- ];
-
- let lines = vec![
- Line {
- part_1: vec!["A".to_string()],
- part_2: vec!["A".to_string()],
- },
- Line {
- part_1: vec!["C".to_string()],
- part_2: vec!["C".to_string()],
- },
- Line {
- part_1: vec!["E".to_string()],
- part_2: vec!["E".to_string()],
- },
- ];
-
- let Diff {
- new,
- deleted,
- undeleted,
- } = diff(db_entries, lines);
+ fn test_added() {
+ let diff = deck_diff("- A : a", "- A : a\n- B : b");
- assert_eq!(
- new,
- vec!(Question {
- question: "E".to_string(),
- responses: vec!("E".to_string())
- })
- );
- assert_eq!(
- deleted,
- vec!(Question {
- question: "B".to_string(),
- responses: vec!("B".to_string())
- })
+ has_questions(diff.new, vec![("B", vec!["b"]), ("b", vec!["B"])]);
+ assert!(diff.deleted.is_empty());
+ assert!(diff.undeleted.is_empty());
+ }
+
+ #[test]
+ fn test_updated() {
+ let diff = deck_diff("- A : a1", "- A : a2");
+
+ has_questions(diff.new, vec![("A", vec!["a2"]), ("a2", vec!["A"])]);
+ has_questions(diff.deleted, vec![("A", vec!["a1"]), ("a1", vec!["A"])]);
+ assert!(diff.undeleted.is_empty());
+ }
+
+ #[test]
+ fn test_deleted() {
+ let diff = deck_diff("- A : a", "");
+
+ assert!(diff.new.is_empty());
+ has_questions(diff.deleted, vec![("A", vec!["a"]), ("a", vec!["A"])]);
+ assert!(diff.undeleted.is_empty());
+ }
+
+ #[test]
+ fn test_undeleted() {
+ let db_entries = vec![DbEntry {
+ question: "A".to_string(),
+ responses: vec!["a".to_string()],
+ deleted: Some(0),
+ }];
+
+ let diff = super::diff(db_entries, deck::tests::read_string("- A : a").unwrap());
+
+ has_questions(diff.new, vec![("a", vec!["A"])]);
+ assert!(diff.deleted.is_empty());
+ has_questions(diff.undeleted, vec![("A", vec!["a"])]);
+ }
+ #[test]
+ fn regroup_same_question() {
+ let diff = deck_diff("", "- A : a\n- A | B : b");
+
+ has_questions(
+ diff.new,
+ vec![
+ ("A", vec!["a", "b"]),
+ ("B", vec!["b"]),
+ ("a", vec!["A"]),
+ ("b", vec!["A", "B"]),
+ ],
);
+ assert!(diff.deleted.is_empty());
+ assert!(diff.undeleted.is_empty());
+ }
+
+ fn deck_diff(from: &str, to: &str) -> Diff {
+ super::diff(db_entries(from), deck::tests::read_string(to).unwrap())
+ }
+
+ fn has_questions(questions: Vec<Question>, xs: Vec<(&str, Vec<&str>)>) {
assert_eq!(
- undeleted,
- vec!(Question {
- question: "C".to_string(),
- responses: vec!("C".to_string())
- })
- );
+ to_set(questions),
+ HashSet::from_iter(
+ xs.iter()
+ .map(|(y, ys)| Question {
+ question: y.to_string(),
+ responses: ys.iter().map(|z| z.to_string()).collect::<Vec<_>>()
+ })
+ .collect::<Vec<_>>()
+ )
+ )
+ }
+
+ fn to_set<A: std::cmp::Eq + std::hash::Hash + std::clone::Clone>(xs: Vec<A>) -> HashSet<A> {
+ xs.iter().cloned().collect()
+ }
+
+ fn db_entries(deck: &str) -> Vec<DbEntry> {
+ let lines = deck::tests::read_string(deck).unwrap();
+ let diff = super::diff(vec![], lines);
+ diff.new
+ .iter()
+ .map(
+ |Question {
+ question,
+ responses,
+ }| DbEntry {
+ question: question.to_string(),
+ responses: responses.to_vec(),
+ deleted: None,
+ },
+ )
+ .collect()
}
}