diff --git a/rslib/src/card/mod.rs b/rslib/src/card/mod.rs index 65898d638..6f86a6974 100644 --- a/rslib/src/card/mod.rs +++ b/rslib/src/card/mod.rs @@ -62,6 +62,15 @@ pub enum CardQueue { UserBuried = -3, } +/// Which of the blue/red/green numbers this card maps to. +pub enum CardQueueNumber { + New, + Learning, + Review, + /// Suspended/buried cards should not be included. + Invalid, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct Card { pub(crate) id: CardId, @@ -115,6 +124,33 @@ impl Default for Card { } impl Card { + pub fn id(&self) -> CardId { + self.id + } + + pub fn note_id(&self) -> NoteId { + self.note_id + } + + pub fn deck_id(&self) -> DeckId { + self.deck_id + } + + pub fn template_idx(&self) -> u16 { + self.template_idx + } + + pub fn queue_number(&self) -> CardQueueNumber { + match self.queue { + CardQueue::New => CardQueueNumber::New, + CardQueue::PreviewRepeat | CardQueue::Learn => CardQueueNumber::Learning, + CardQueue::DayLearn | CardQueue::Review => CardQueueNumber::Review, + CardQueue::Suspended | CardQueue::SchedBuried | CardQueue::UserBuried => { + CardQueueNumber::Invalid + } + } + } + pub fn set_modified(&mut self, usn: Usn) { self.mtime = TimestampSecs::now(); self.usn = usn; diff --git a/rslib/src/collection/mod.rs b/rslib/src/collection/mod.rs index d80b0798a..9829b7215 100644 --- a/rslib/src/collection/mod.rs +++ b/rslib/src/collection/mod.rs @@ -220,4 +220,8 @@ impl Collection { self.state.deck_cache.clear(); self.state.notetype_cache.clear(); } + + pub fn tr(&self) -> &I18n { + &self.tr + } } diff --git a/rslib/src/collection/timestamps.rs b/rslib/src/collection/timestamps.rs index 9f1615ba2..d67ace708 100644 --- a/rslib/src/collection/timestamps.rs +++ b/rslib/src/collection/timestamps.rs @@ -20,17 +20,21 @@ impl CollectionTimestamps { } impl Collection { - pub(crate) fn set_modified(&mut self) -> Result<()> { + /// This is done automatically when you call collection methods, so callers + /// outside this crate should only need this if you are manually + /// modifying the database. + pub fn set_modified(&mut self) -> Result<()> { let stamps = self.storage.get_collection_timestamps()?; self.set_modified_time_undoable(TimestampMillis::now(), stamps.collection_change) } - pub(crate) fn set_schema_modified(&mut self) -> Result<()> { + /// Forces the next sync in one direction. + pub fn set_schema_modified(&mut self) -> Result<()> { let stamps = self.storage.get_collection_timestamps()?; self.set_schema_modified_time_undoable(TimestampMillis::now(), stamps.schema_change) } - pub(crate) fn changed_since_last_backup(&self) -> Result { + pub fn changed_since_last_backup(&self) -> Result { let stamps = self.storage.get_collection_timestamps()?; Ok(self .state diff --git a/rslib/src/config/mod.rs b/rslib/src/config/mod.rs index b7023560e..2133b2e30 100644 --- a/rslib/src/config/mod.rs +++ b/rslib/src/config/mod.rs @@ -168,11 +168,13 @@ impl Collection { } } - pub(crate) fn get_configured_utc_offset(&self) -> Option { + /// In minutes west of UTC. + pub fn get_configured_utc_offset(&self) -> Option { self.get_config_optional(ConfigKey::LocalOffset) } - pub(crate) fn set_configured_utc_offset(&mut self, mins: i32) -> Result<()> { + /// In minutes west of UTC. + pub fn set_configured_utc_offset(&mut self, mins: i32) -> Result<()> { self.state.scheduler_info = None; self.set_config(ConfigKey::LocalOffset, &mins).map(|_| ()) } diff --git a/rslib/src/decks/mod.rs b/rslib/src/decks/mod.rs index f33f3b879..91c1a2206 100644 --- a/rslib/src/decks/mod.rs +++ b/rslib/src/decks/mod.rs @@ -80,7 +80,7 @@ impl Deck { } /// Returns deck config ID if deck is a normal deck. - pub(crate) fn config_id(&self) -> Option { + pub fn config_id(&self) -> Option { if let DeckKind::Normal(ref norm) = self.kind { Some(DeckConfigId(norm.config_id)) } else { diff --git a/rslib/src/notes/mod.rs b/rslib/src/notes/mod.rs index f26089a93..c0f28265a 100644 --- a/rslib/src/notes/mod.rs +++ b/rslib/src/notes/mod.rs @@ -117,7 +117,7 @@ impl NoteTags { } impl Note { - pub(crate) fn new(notetype: &Notetype) -> Self { + pub fn new(notetype: &Notetype) -> Self { Note { id: NoteId(0), guid: base91_u64(), @@ -156,7 +156,7 @@ impl Note { } } - pub(crate) fn fields_mut(&mut self) -> &mut Vec { + pub fn fields_mut(&mut self) -> &mut Vec { self.mark_dirty(); &mut self.fields } diff --git a/rslib/src/notetype/render.rs b/rslib/src/notetype/render.rs index 2d23377e8..4e3eb3439 100644 --- a/rslib/src/notetype/render.rs +++ b/rslib/src/notetype/render.rs @@ -9,6 +9,7 @@ use super::Notetype; use super::NotetypeKind; use crate::prelude::*; use crate::template::field_is_empty; +use crate::template::flatten_nodes; use crate::template::render_card; use crate::template::ParsedTemplate; use crate::template::RenderedNode; @@ -20,6 +21,18 @@ pub struct RenderCardOutput { pub latex_svg: bool, } +impl RenderCardOutput { + /// The question text, ignoring any unknown field replacements. + pub fn question(&self) -> Cow { + flatten_nodes(&self.qnodes) + } + + /// The answer text, ignoring any unknown field replacements. + pub fn answer(&self) -> Cow { + flatten_nodes(&self.anodes) + } +} + impl Collection { /// Render an existing card saved in the database. pub fn render_existing_card(&mut self, cid: CardId, browser: bool) -> Result { diff --git a/rslib/src/scheduler/answering/mod.rs b/rslib/src/scheduler/answering/mod.rs index 5c868e126..d5f1ca2d8 100644 --- a/rslib/src/scheduler/answering/mod.rs +++ b/rslib/src/scheduler/answering/mod.rs @@ -196,7 +196,7 @@ impl Collection { } /// Describe the next intervals, to display on the answer buttons. - pub fn describe_next_states(&mut self, choices: SchedulingStates) -> Result> { + pub fn describe_next_states(&mut self, choices: &SchedulingStates) -> Result> { let collapse_time = self.learn_ahead_secs(); let now = TimestampSecs::now(); let timing = self.timing_for_timestamp(now)?; diff --git a/rslib/src/scheduler/service/mod.rs b/rslib/src/scheduler/service/mod.rs index b59b42df4..e55c3201c 100644 --- a/rslib/src/scheduler/service/mod.rs +++ b/rslib/src/scheduler/service/mod.rs @@ -198,7 +198,7 @@ impl crate::services::SchedulerService for Collection { input: scheduler::SchedulingStates, ) -> Result { let states: SchedulingStates = input.into(); - self.describe_next_states(states).map(Into::into) + self.describe_next_states(&states).map(Into::into) } fn state_is_leech(&mut self, input: scheduler::SchedulingState) -> Result { diff --git a/rslib/src/storage/mod.rs b/rslib/src/storage/mod.rs index 6a0403bb3..f240555eb 100644 --- a/rslib/src/storage/mod.rs +++ b/rslib/src/storage/mod.rs @@ -34,7 +34,7 @@ impl SchemaVersion { } /// Write a list of IDs as '(x,y,...)' into the provided string. -pub(crate) fn ids_to_string(buf: &mut String, ids: I) +pub fn ids_to_string(buf: &mut String, ids: I) where D: std::fmt::Display, I: IntoIterator, diff --git a/rslib/src/storage/sqlite.rs b/rslib/src/storage/sqlite.rs index e6fcd69b1..ce48ce779 100644 --- a/rslib/src/storage/sqlite.rs +++ b/rslib/src/storage/sqlite.rs @@ -76,6 +76,14 @@ fn open_or_create_collection_db(path: &Path) -> Result { Ok(db) } +impl SqliteStorage { + /// This is provided as an escape hatch for when you need to do something + /// not directly supported by this library. Please exercise caution when + /// using it. + pub fn db(&self) -> &Connection { + &self.db + } +} /// Adds sql function field_at_index(flds, index) /// to split provided fields and return field at zero-based index. /// If out of range, returns empty string. diff --git a/rslib/src/template.rs b/rslib/src/template.rs index 0b4705253..3af2418f7 100644 --- a/rslib/src/template.rs +++ b/rslib/src/template.rs @@ -529,6 +529,24 @@ fn append_str_to_nodes(nodes: &mut Vec, text: &str) { } } +/// Return the resolved text of a list of nodes, ignoring any unknown +/// filters that were encountered. +pub(crate) fn flatten_nodes(nodes: &[RenderedNode]) -> Cow { + match nodes { + [RenderedNode::Text { text }] => text.into(), + nodes => { + let mut buf = String::new(); + for node in nodes { + match node { + RenderedNode::Text { text } => buf.push_str(text), + RenderedNode::Replacement { current_text, .. } => buf.push_str(current_text), + } + } + buf.into() + } + } +} + /// True if provided text contains only whitespace and/or empty BR/DIV tags. pub(crate) fn field_is_empty(text: &str) -> bool { lazy_static! {