mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04:00
add stock note types in backend
This commit is contained in:
parent
7811a04df8
commit
540892639f
10 changed files with 342 additions and 42 deletions
|
@ -533,8 +533,13 @@ message NoteTypeConfig {
|
|||
}
|
||||
|
||||
message CardRequirement {
|
||||
enum CardRequirementKind {
|
||||
None = 0;
|
||||
Any = 1;
|
||||
All = 2;
|
||||
}
|
||||
uint32 card_ord = 1;
|
||||
uint32 kind = 2;
|
||||
CardRequirementKind kind = 2;
|
||||
repeated uint32 field_ords = 3;
|
||||
}
|
||||
|
||||
|
@ -547,3 +552,11 @@ message NoteType {
|
|||
repeated NoteField fields = 8;
|
||||
repeated CardTemplate templates = 9;
|
||||
}
|
||||
|
||||
enum StockNoteType {
|
||||
StockNoteTypeBasic = 0;
|
||||
StockNoteTypeBasicAndReversed = 1;
|
||||
StockNoteTypeBasicOptionalReversed = 2;
|
||||
StockNoteTypeBasicTyping = 3;
|
||||
StockNoteTypeCloze = 4;
|
||||
}
|
||||
|
|
|
@ -13,13 +13,6 @@ from anki.dbproxy import DBProxy
|
|||
from anki.lang import _
|
||||
from anki.media import media_paths_from_col_path
|
||||
from anki.rsbackend import RustBackend
|
||||
from anki.stdmodels import (
|
||||
addBasicModel,
|
||||
addBasicTypingModel,
|
||||
addClozeModel,
|
||||
addForwardOptionalReverse,
|
||||
addForwardReverse,
|
||||
)
|
||||
from anki.utils import intTime
|
||||
|
||||
|
||||
|
@ -51,19 +44,13 @@ def Collection(
|
|||
db = DBProxy(weakref.proxy(backend), path)
|
||||
|
||||
# initial setup required?
|
||||
create = db.scalar("select models = '{}' from col")
|
||||
create = db.scalar("select decks = '{}' from col")
|
||||
if create:
|
||||
initial_db_setup(db)
|
||||
|
||||
# add db to col and do any remaining upgrades
|
||||
col = _Collection(db, backend=backend, server=server, log=should_log)
|
||||
if create:
|
||||
# add in reverse order so basic is default
|
||||
addClozeModel(col)
|
||||
addBasicTypingModel(col)
|
||||
addForwardOptionalReverse(col)
|
||||
addForwardReverse(col)
|
||||
addBasicModel(col)
|
||||
col.save()
|
||||
else:
|
||||
db.begin()
|
||||
|
|
|
@ -827,7 +827,7 @@ impl Backend {
|
|||
pb::set_config_json::Op::Val(val) => {
|
||||
// ensure it's a well-formed object
|
||||
let val: JsonValue = serde_json::from_slice(&val)?;
|
||||
col.set_config(&input.key, &val)
|
||||
col.set_config(input.key.as_str(), &val)
|
||||
}
|
||||
pb::set_config_json::Op::Remove(_) => col.remove_config(&input.key),
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
// Copyright: Ankitects Pty Ltd and contributors
|
||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
use crate::collection::Collection;
|
||||
use crate::decks::DeckID;
|
||||
use crate::err::Result;
|
||||
use crate::timestamp::TimestampSecs;
|
||||
use crate::{
|
||||
collection::Collection, decks::DeckID, err::Result, notetype::NoteTypeID,
|
||||
timestamp::TimestampSecs,
|
||||
};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use serde_aux::field_attributes::deserialize_bool_from_anything;
|
||||
use serde_derive::Deserialize;
|
||||
|
@ -38,6 +38,7 @@ pub(crate) enum ConfigKey {
|
|||
CreationOffset,
|
||||
Rollover,
|
||||
LocalOffset,
|
||||
CurrentNoteTypeID,
|
||||
}
|
||||
|
||||
impl From<ConfigKey> for &'static str {
|
||||
|
@ -49,6 +50,7 @@ impl From<ConfigKey> for &'static str {
|
|||
ConfigKey::CreationOffset => "creationOffset",
|
||||
ConfigKey::Rollover => "rollover",
|
||||
ConfigKey::LocalOffset => "localOffset",
|
||||
ConfigKey::CurrentNoteTypeID => "curModel",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,9 +85,12 @@ impl Collection {
|
|||
self.get_config_optional(key).unwrap_or_default()
|
||||
}
|
||||
|
||||
pub(crate) fn set_config<T: Serialize>(&self, key: &str, val: &T) -> Result<()> {
|
||||
pub(crate) fn set_config<'a, T: Serialize, K>(&self, key: K, val: &T) -> Result<()>
|
||||
where
|
||||
K: Into<&'a str>,
|
||||
{
|
||||
self.storage
|
||||
.set_config_value(key, val, self.usn()?, TimestampSecs::now())
|
||||
.set_config_value(key.into(), val, self.usn()?, TimestampSecs::now())
|
||||
}
|
||||
|
||||
pub(crate) fn remove_config(&self, key: &str) -> Result<()> {
|
||||
|
@ -117,6 +122,16 @@ impl Collection {
|
|||
pub(crate) fn get_rollover(&self) -> Option<i8> {
|
||||
self.get_config_optional(ConfigKey::Rollover)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn get_current_notetype_id(&self) -> Option<NoteTypeID> {
|
||||
self.get_config_optional(ConfigKey::CurrentNoteTypeID)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn set_current_notetype_id(&self, id: NoteTypeID) -> Result<()> {
|
||||
self.set_config(ConfigKey::CurrentNoteTypeID, &id)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, PartialEq, Debug)]
|
||||
|
|
|
@ -2,15 +2,20 @@
|
|||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
mod schema11;
|
||||
mod stock;
|
||||
|
||||
pub use crate::backend_proto::{
|
||||
CardRequirement, CardTemplate, CardTemplateConfig, NoteField, NoteFieldConfig, NoteType,
|
||||
NoteTypeConfig, NoteTypeKind,
|
||||
card_requirement::CardRequirementKind, CardRequirement, CardTemplate, CardTemplateConfig,
|
||||
NoteField, NoteFieldConfig, NoteType, NoteTypeConfig, NoteTypeKind,
|
||||
};
|
||||
pub use schema11::{CardTemplateSchema11, NoteFieldSchema11, NoteTypeSchema11};
|
||||
|
||||
use crate::{define_newtype, text::ensure_string_in_nfc};
|
||||
use std::collections::HashSet;
|
||||
use crate::{
|
||||
define_newtype,
|
||||
template::{without_legacy_template_directives, FieldRequirements, ParsedTemplate},
|
||||
text::ensure_string_in_nfc,
|
||||
};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use unicase::UniCase;
|
||||
|
||||
define_newtype!(NoteTypeID, i64);
|
||||
|
@ -36,9 +41,6 @@ pub(crate) const DEFAULT_LATEX_HEADER: &str = r#"\documentclass[12pt]{article}
|
|||
|
||||
pub(crate) const DEFAULT_LATEX_FOOTER: &str = r#"\end{document}"#;
|
||||
|
||||
// other: vec![], // fixme: ensure empty map converted to empty bytes
|
||||
// fixme: rollback savepoint when tags not changed
|
||||
|
||||
impl NoteType {
|
||||
pub fn new() -> Self {
|
||||
let mut nt = Self::default();
|
||||
|
@ -87,6 +89,53 @@ impl NoteType {
|
|||
}
|
||||
}
|
||||
|
||||
fn update_requirements(&mut self) {
|
||||
let field_map: HashMap<&str, u16> = self
|
||||
.fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, field)| (field.name.as_str(), idx as u16))
|
||||
.collect();
|
||||
let reqs: Vec<_> = self
|
||||
.templates
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(ord, tmpl)| {
|
||||
let conf = tmpl.config.as_ref().unwrap();
|
||||
let normalized = without_legacy_template_directives(&conf.q_format);
|
||||
if let Ok(tmpl) = ParsedTemplate::from_text(normalized.as_ref()) {
|
||||
let mut req = match tmpl.requirements(&field_map) {
|
||||
FieldRequirements::Any(ords) => CardRequirement {
|
||||
card_ord: ord as u32,
|
||||
kind: CardRequirementKind::Any as i32,
|
||||
field_ords: ords.into_iter().map(|n| n as u32).collect(),
|
||||
},
|
||||
FieldRequirements::All(ords) => CardRequirement {
|
||||
card_ord: ord as u32,
|
||||
kind: CardRequirementKind::All as i32,
|
||||
field_ords: ords.into_iter().map(|n| n as u32).collect(),
|
||||
},
|
||||
FieldRequirements::None => CardRequirement {
|
||||
card_ord: ord as u32,
|
||||
kind: CardRequirementKind::None as i32,
|
||||
field_ords: vec![],
|
||||
},
|
||||
};
|
||||
req.field_ords.sort_unstable();
|
||||
req
|
||||
} else {
|
||||
// template parsing failures make card unsatisfiable
|
||||
CardRequirement {
|
||||
card_ord: ord as u32,
|
||||
kind: CardRequirementKind::None as i32,
|
||||
field_ords: vec![],
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
self.config.as_mut().unwrap().reqs = reqs;
|
||||
}
|
||||
|
||||
pub(crate) fn normalize_names(&mut self) {
|
||||
ensure_string_in_nfc(&mut self.name);
|
||||
for f in &mut self.fields {
|
||||
|
@ -96,4 +145,37 @@ impl NoteType {
|
|||
ensure_string_in_nfc(&mut t.name);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_field<S: Into<String>>(&mut self, name: S) {
|
||||
let mut config = NoteFieldConfig::default();
|
||||
config.font_name = "Arial".to_string();
|
||||
config.font_size = 20;
|
||||
let mut field = NoteField::default();
|
||||
field.name = name.into();
|
||||
field.config = Some(config);
|
||||
self.fields.push(field);
|
||||
}
|
||||
|
||||
pub(crate) fn add_template<S1, S2, S3>(&mut self, name: S1, qfmt: S2, afmt: S3)
|
||||
where
|
||||
S1: Into<String>,
|
||||
S2: Into<String>,
|
||||
S3: Into<String>,
|
||||
{
|
||||
let mut config = CardTemplateConfig::default();
|
||||
config.q_format = qfmt.into();
|
||||
config.a_format = afmt.into();
|
||||
|
||||
let mut tmpl = CardTemplate::default();
|
||||
tmpl.name = name.into();
|
||||
tmpl.config = Some(config);
|
||||
|
||||
self.templates.push(tmpl);
|
||||
}
|
||||
|
||||
pub(crate) fn prepare_for_adding(&mut self) {
|
||||
self.normalize_names();
|
||||
self.ensure_names_unique();
|
||||
self.update_requirements();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ use serde_repr::{Deserialize_repr, Serialize_repr};
|
|||
use serde_tuple::Serialize_tuple;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::NoteTypeID;
|
||||
use super::{CardRequirementKind, NoteTypeID};
|
||||
|
||||
#[derive(Serialize_repr, Deserialize_repr, PartialEq, Debug, Clone)]
|
||||
#[repr(u8)]
|
||||
|
@ -101,7 +101,7 @@ impl From<NoteTypeSchema11> for NoteType {
|
|||
latex_post: nt.latex_post,
|
||||
latex_svg: nt.latexsvg,
|
||||
reqs: nt.req.0.into_iter().map(Into::into).collect(),
|
||||
other: serde_json::to_vec(&nt.other).unwrap(),
|
||||
other: other_to_bytes(&nt.other),
|
||||
}),
|
||||
fields: nt.flds.into_iter().map(Into::into).collect(),
|
||||
templates: nt.tmpls.into_iter().map(Into::into).collect(),
|
||||
|
@ -109,6 +109,29 @@ impl From<NoteTypeSchema11> for NoteType {
|
|||
}
|
||||
}
|
||||
|
||||
fn other_to_bytes(other: &HashMap<String, Value>) -> Vec<u8> {
|
||||
if other.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
serde_json::to_vec(other).unwrap_or_else(|e| {
|
||||
// theoretically should never happen
|
||||
println!("serialization failed for {:?}: {}", other, e);
|
||||
vec![]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn bytes_to_other(bytes: &[u8]) -> HashMap<String, Value> {
|
||||
if bytes.is_empty() {
|
||||
Default::default()
|
||||
} else {
|
||||
serde_json::from_slice(bytes).unwrap_or_else(|e| {
|
||||
println!("deserialization failed for other: {}", e);
|
||||
Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NoteType> for NoteTypeSchema11 {
|
||||
fn from(p: NoteType) -> Self {
|
||||
let c = p.config.unwrap();
|
||||
|
@ -135,7 +158,7 @@ impl From<NoteType> for NoteTypeSchema11 {
|
|||
latex_post: c.latex_post,
|
||||
latexsvg: c.latex_svg,
|
||||
req: CardRequirementsSchema11(c.reqs.into_iter().map(Into::into).collect()),
|
||||
other: serde_json::from_slice(&c.other).unwrap_or_default(),
|
||||
other: bytes_to_other(&c.other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -144,7 +167,11 @@ impl From<CardRequirementSchema11> for CardRequirement {
|
|||
fn from(r: CardRequirementSchema11) -> Self {
|
||||
CardRequirement {
|
||||
card_ord: r.card_ord as u32,
|
||||
kind: r.kind as u32,
|
||||
kind: match r.kind {
|
||||
FieldRequirementKindSchema11::Any => CardRequirementKind::Any,
|
||||
FieldRequirementKindSchema11::All => CardRequirementKind::All,
|
||||
FieldRequirementKindSchema11::None => CardRequirementKind::None,
|
||||
} as i32,
|
||||
field_ords: r.field_ords.into_iter().map(|n| n as u32).collect(),
|
||||
}
|
||||
}
|
||||
|
@ -154,10 +181,10 @@ impl From<CardRequirement> for CardRequirementSchema11 {
|
|||
fn from(p: CardRequirement) -> Self {
|
||||
CardRequirementSchema11 {
|
||||
card_ord: p.card_ord as u16,
|
||||
kind: match p.kind {
|
||||
0 => FieldRequirementKindSchema11::Any,
|
||||
1 => FieldRequirementKindSchema11::All,
|
||||
_ => FieldRequirementKindSchema11::None,
|
||||
kind: match p.kind() {
|
||||
CardRequirementKind::Any => FieldRequirementKindSchema11::Any,
|
||||
CardRequirementKind::All => FieldRequirementKindSchema11::All,
|
||||
CardRequirementKind::None => FieldRequirementKindSchema11::None,
|
||||
},
|
||||
field_ords: p.field_ords.into_iter().map(|n| n as u16).collect(),
|
||||
}
|
||||
|
@ -202,7 +229,7 @@ impl From<NoteFieldSchema11> for NoteField {
|
|||
rtl: f.rtl,
|
||||
font_name: f.font,
|
||||
font_size: f.size as u32,
|
||||
other: serde_json::to_vec(&f.other).unwrap(),
|
||||
other: other_to_bytes(&f.other),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -218,7 +245,7 @@ impl From<NoteField> for NoteFieldSchema11 {
|
|||
rtl: conf.rtl,
|
||||
font: conf.font_name,
|
||||
size: conf.font_size as u16,
|
||||
other: serde_json::from_slice(&conf.other).unwrap(),
|
||||
other: bytes_to_other(&conf.other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -259,7 +286,7 @@ impl From<CardTemplateSchema11> for CardTemplate {
|
|||
target_deck_id: t.did.unwrap_or(DeckID(0)).0,
|
||||
browser_font_name: t.bfont,
|
||||
browser_font_size: t.bsize as u32,
|
||||
other: serde_json::to_vec(&t.other).unwrap(),
|
||||
other: other_to_bytes(&t.other),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -282,7 +309,7 @@ impl From<CardTemplate> for CardTemplateSchema11 {
|
|||
},
|
||||
bfont: conf.browser_font_name,
|
||||
bsize: conf.browser_font_size as u8,
|
||||
other: serde_json::from_slice(&conf.other).unwrap(),
|
||||
other: bytes_to_other(&conf.other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
130
rslib/src/notetype/stock.rs
Normal file
130
rslib/src/notetype/stock.rs
Normal file
|
@ -0,0 +1,130 @@
|
|||
// Copyright: Ankitects Pty Ltd and contributors
|
||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
use super::NoteTypeKind;
|
||||
use crate::{
|
||||
config::ConfigKey, err::Result, i18n::I18n, i18n::TR, notetype::NoteType,
|
||||
storage::SqliteStorage, timestamp::TimestampSecs,
|
||||
};
|
||||
|
||||
pub use crate::backend_proto::StockNoteType;
|
||||
|
||||
impl SqliteStorage {
|
||||
pub(crate) fn add_stock_notetypes(&self, i18n: &I18n) -> Result<()> {
|
||||
for (idx, mut nt) in all_stock_notetypes(i18n).into_iter().enumerate() {
|
||||
self.add_new_notetype(&mut nt)?;
|
||||
if idx == StockNoteType::Basic as usize {
|
||||
self.set_config_value(
|
||||
ConfigKey::CurrentNoteTypeID.into(),
|
||||
&nt.id(),
|
||||
self.usn(false)?,
|
||||
TimestampSecs::now(),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// if changing this, make sure to update StockNoteType enum
|
||||
pub fn all_stock_notetypes(i18n: &I18n) -> Vec<NoteType> {
|
||||
vec![
|
||||
basic(i18n),
|
||||
basic_forward_reverse(i18n),
|
||||
basic_optional_reverse(i18n),
|
||||
basic_typing(i18n),
|
||||
cloze(i18n),
|
||||
]
|
||||
}
|
||||
|
||||
/// returns {{name}}
|
||||
fn fieldref<S: AsRef<str>>(name: S) -> String {
|
||||
format!("{{{{{}}}}}", name.as_ref())
|
||||
}
|
||||
|
||||
pub(crate) fn basic(i18n: &I18n) -> NoteType {
|
||||
let mut nt = NoteType::new();
|
||||
nt.name = i18n.tr(TR::NotetypesBasicName).into();
|
||||
let front = i18n.tr(TR::NotetypesFrontField);
|
||||
let back = i18n.tr(TR::NotetypesBackField);
|
||||
nt.add_field(front.as_ref());
|
||||
nt.add_field(back.as_ref());
|
||||
nt.add_template(
|
||||
i18n.tr(TR::NotetypesCard1Name),
|
||||
fieldref(front),
|
||||
format!(
|
||||
"{}\n\n<hr id=answer>\n\n{}",
|
||||
fieldref("FrontSide"),
|
||||
fieldref(back),
|
||||
),
|
||||
);
|
||||
nt.prepare_for_adding();
|
||||
nt
|
||||
}
|
||||
|
||||
pub(crate) fn basic_typing(i18n: &I18n) -> NoteType {
|
||||
let mut nt = basic(i18n);
|
||||
nt.name = i18n.tr(TR::NotetypesBasicTypeAnswerName).into();
|
||||
let front = i18n.tr(TR::NotetypesFrontField);
|
||||
let back = i18n.tr(TR::NotetypesBackField);
|
||||
let tmpl = nt.templates[0].config.as_mut().unwrap();
|
||||
tmpl.q_format = format!("{}\n\n{{{{type:{}}}}}", fieldref(front.as_ref()), back);
|
||||
tmpl.a_format = format!(
|
||||
"{}\n\n<hr id=answer>\n\n{{{{type:{}}}}}",
|
||||
fieldref(front),
|
||||
back
|
||||
);
|
||||
nt.prepare_for_adding();
|
||||
nt
|
||||
}
|
||||
|
||||
pub(crate) fn basic_forward_reverse(i18n: &I18n) -> NoteType {
|
||||
let mut nt = basic(i18n);
|
||||
nt.name = i18n.tr(TR::NotetypesBasicReversedName).into();
|
||||
let front = i18n.tr(TR::NotetypesFrontField);
|
||||
let back = i18n.tr(TR::NotetypesBackField);
|
||||
nt.add_template(
|
||||
i18n.tr(TR::NotetypesCard2Name),
|
||||
fieldref(back),
|
||||
format!(
|
||||
"{}\n\n<hr id=answer>\n\n{}",
|
||||
fieldref("FrontSide"),
|
||||
fieldref(front),
|
||||
),
|
||||
);
|
||||
nt.prepare_for_adding();
|
||||
nt
|
||||
}
|
||||
|
||||
pub(crate) fn basic_optional_reverse(i18n: &I18n) -> NoteType {
|
||||
let mut nt = basic_forward_reverse(i18n);
|
||||
nt.name = i18n.tr(TR::NotetypesBasicOptionalReversedName).into();
|
||||
let addrev = i18n.tr(TR::NotetypesAddReverseField);
|
||||
nt.add_field(addrev.as_ref());
|
||||
let tmpl = nt.templates[1].config.as_mut().unwrap();
|
||||
tmpl.q_format = format!("{{{{#{}}}}}{}{{{{/{}}}}}", addrev, tmpl.q_format, addrev);
|
||||
nt.prepare_for_adding();
|
||||
nt
|
||||
}
|
||||
|
||||
pub(crate) fn cloze(i18n: &I18n) -> NoteType {
|
||||
let mut nt = NoteType::new();
|
||||
nt.name = i18n.tr(TR::NotetypesClozeName).into();
|
||||
let text = i18n.tr(TR::NotetypesTextField);
|
||||
nt.add_field(text.as_ref());
|
||||
let fmt = format!("{{{{cloze:{}}}}}", text);
|
||||
nt.add_template(nt.name.clone(), fmt.clone(), fmt);
|
||||
let mut config = nt.config.as_mut().unwrap();
|
||||
config.kind = NoteTypeKind::Cloze as i32;
|
||||
config.css += "
|
||||
.cloze {
|
||||
font-weight: bold;
|
||||
color: blue;
|
||||
}
|
||||
.nightMode .cloze {
|
||||
color: lightblue;
|
||||
}
|
||||
";
|
||||
nt.prepare_for_adding();
|
||||
nt
|
||||
}
|
|
@ -1,3 +1,22 @@
|
|||
insert into notetypes (id, name, mtime_secs, usn, config)
|
||||
values
|
||||
(?, ?, ?, ?, ?)
|
||||
(
|
||||
(
|
||||
case
|
||||
when ?1 in (
|
||||
select
|
||||
id
|
||||
from notetypes
|
||||
) then (
|
||||
select
|
||||
max(id) + 1
|
||||
from notetypes
|
||||
)
|
||||
else ?1
|
||||
end
|
||||
),
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?
|
||||
);
|
|
@ -8,6 +8,7 @@ use crate::{
|
|||
},
|
||||
err::{AnkiError, DBErrorKind, Result},
|
||||
notetype::{NoteTypeID, NoteTypeSchema11},
|
||||
timestamp::TimestampMillis,
|
||||
};
|
||||
use prost::Message;
|
||||
use rusqlite::{params, Row, NO_PARAMS};
|
||||
|
@ -160,6 +161,27 @@ impl SqliteStorage {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn add_new_notetype(&self, nt: &mut NoteType) -> Result<()> {
|
||||
assert!(nt.id == 0);
|
||||
|
||||
let mut stmt = self.db.prepare_cached(include_str!("add_notetype.sql"))?;
|
||||
let mut config_bytes = vec![];
|
||||
nt.config.as_ref().unwrap().encode(&mut config_bytes)?;
|
||||
stmt.execute(params![
|
||||
TimestampMillis::now(),
|
||||
nt.name,
|
||||
nt.mtime_secs,
|
||||
nt.usn,
|
||||
config_bytes
|
||||
])?;
|
||||
nt.id = self.db.last_insert_rowid();
|
||||
|
||||
self.update_notetype_fields(nt.id(), &nt.fields)?;
|
||||
self.update_notetype_templates(nt.id(), &nt.templates)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Upgrading/downgrading
|
||||
|
||||
pub(crate) fn upgrade_notetypes_to_schema15(&self) -> Result<()> {
|
||||
|
@ -181,6 +203,7 @@ impl SqliteStorage {
|
|||
self.update_notetype_fields(ntid, &nt.fields)?;
|
||||
self.update_notetype_templates(ntid, &nt.templates)?;
|
||||
}
|
||||
self.db.execute("update col set models = ''", NO_PARAMS)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,9 @@ fn unicase_compare(s1: &str, s2: &str) -> Ordering {
|
|||
UniCase::new(s1).cmp(&UniCase::new(s2))
|
||||
}
|
||||
|
||||
// fixme: rollback savepoint when tags not changed
|
||||
// fixme: switch away from proto for top level struct
|
||||
|
||||
// currently public for dbproxy
|
||||
#[derive(Debug)]
|
||||
pub struct SqliteStorage {
|
||||
|
@ -195,6 +198,7 @@ impl SqliteStorage {
|
|||
|
||||
if create {
|
||||
storage.add_default_deck_config(i18n)?;
|
||||
storage.add_stock_notetypes(i18n)?;
|
||||
}
|
||||
|
||||
if create || upgrade {
|
||||
|
|
Loading…
Reference in a new issue