1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
use super::Package;
use crate::db_entries::{DeckDbEntry, ModelDbEntry};
use crate::error::{database_error, json_error};
use crate::model::Model;
use crate::note::Note;
use crate::Error;
use rusqlite::{params, Transaction};
use std::collections::HashMap;
use std::ops::RangeFrom;
/// A flashcard deck which can be written into an .apkg file.
#[derive(Clone)]
pub struct Deck {
id: i64,
name: String,
description: String,
notes: Vec<Note>,
models: HashMap<i64, Model>,
}
impl Deck {
/// Creates a new deck with an `id`, `name` and `description`.
///
/// `id` should always be unique when creating multiple decks.
pub fn new(id: i64, name: &str, description: &str) -> Self {
Self {
id,
name: name.to_string(),
description: description.to_string(),
notes: vec![],
models: HashMap::new(),
}
}
/// Adds a `note` (Flashcard) to the deck.
///
/// Example:
///
/// ```rust
/// use genanki_rs::{Deck, Note, basic_model};
///
/// let mut my_deck = Deck::new(1234, "Example deck", "This is an example deck");
/// my_deck.add_note(Note::new(basic_model(), vec!["What is the capital of France?", "Paris"])?);
/// ```
pub fn add_note(&mut self, note: Note) {
self.notes.push(note);
}
fn add_model(&mut self, model: Model) {
self.models.insert(model.id, model);
}
fn to_deck_db_entry(&self) -> DeckDbEntry {
DeckDbEntry {
collapsed: false,
conf: 1,
desc: self.description.clone(),
deck_db_entry_dyn: 0,
extend_new: 0,
extend_rev: 50,
id: self.id.clone(),
lrn_today: vec![163, 2],
deck_db_entry_mod: 1425278051,
name: self.name.clone(),
new_today: vec![163, 2],
rev_today: vec![163, 0],
time_today: vec![163, 23598],
usn: -1,
}
}
#[allow(dead_code)]
fn to_json(&self) -> String {
let db_entry: DeckDbEntry = self.to_deck_db_entry();
serde_json::to_string(&db_entry).expect("Should always serialize")
}
pub(super) fn write_to_db(
&mut self,
transaction: &Transaction,
timestamp: f64,
id_gen: &mut RangeFrom<usize>,
) -> Result<(), Error> {
let decks_json_str: String = transaction
.query_row("SELECT decks FROM col", [], |row| row.get(0))
.map_err(database_error)?;
let mut decks: HashMap<i64, DeckDbEntry> =
serde_json::from_str(&decks_json_str).map_err(json_error)?;
decks.insert(self.id, self.to_deck_db_entry());
transaction
.execute(
"UPDATE col SET decks = ?",
params![serde_json::to_string(&decks).map_err(json_error)?],
)
.map_err(database_error)?;
let models_json_str: String = transaction
.query_row("SELECT models FROM col", [], |row| row.get(0))
.map_err(database_error)?;
let mut models: HashMap<i64, ModelDbEntry> =
serde_json::from_str(&models_json_str).map_err(json_error)?;
for note in self.notes.clone().iter() {
self.add_model(note.model());
}
for (i, model) in &mut self.models {
models.insert(*i, model.to_model_db_entry(timestamp, self.id)?);
}
transaction
.execute(
"UPDATE col SET models = ?",
[serde_json::to_string(&models).map_err(json_error)?],
)
.map_err(database_error)?;
for note in &mut self.notes {
note.write_to_db(&transaction, timestamp, self.id, id_gen)?;
}
Ok(())
}
/// Packages a deck and writes it to a new `.apkg` file. This file can then be imported in Anki.
///
/// Returns `Err` if the file can not be created.
///
/// Example:
/// ```rust
/// use genanki_rs::{Deck, Note, basic_model};
///
/// let mut my_deck = Deck::new(1234, "Example deck", "This is an example deck");
/// my_deck.add_note(Note::new(basic_model(), vec!["What is the capital of France?", "Paris"])?);
///
/// my_deck.write_to_file("output.apkg")?;
/// ```
///
/// This is equivalent to:
/// ```rust
/// use genanki_rs::{Deck, Note, basic_model, Package};
///
/// let mut my_deck = Deck::new(1234, "Example deck", "This is an example deck");
/// my_deck.add_note(Note::new(basic_model(), vec!["What is the capital of France?", "Paris"])?);
///
/// Package::new(vec![my_deck], vec![])?.write_to_file("output.apkg")?;
/// ```
pub fn write_to_file(&self, file: &str) -> Result<(), Error> {
Package::new(vec![self.clone()], vec![])?.write_to_file(file)?;
Ok(())
}
}