mirror of
https://github.com/ankitects/anki.git
synced 2025-09-25 01:06:35 -04:00
the start of note adding, and note type changes
This commit is contained in:
parent
7c23deb562
commit
f24dc05c8d
6 changed files with 177 additions and 9 deletions
|
@ -43,6 +43,8 @@ num_enum = "0.4.2"
|
|||
# pinned as any changes could invalidate sqlite indexes
|
||||
unicase = "=2.6.0"
|
||||
futures = "0.3.4"
|
||||
rand = "0.7.3"
|
||||
num-integer = "0.1.42"
|
||||
|
||||
# pinned until rusqlite 0.22 comes out
|
||||
[target.'cfg(target_vendor="apple")'.dependencies.rusqlite]
|
||||
|
|
|
@ -5,8 +5,9 @@ use crate::err::{AnkiError, Result};
|
|||
use crate::notetype::NoteTypeID;
|
||||
use crate::text::strip_html_preserving_image_filenames;
|
||||
use crate::timestamp::TimestampSecs;
|
||||
use crate::{define_newtype, types::Usn};
|
||||
use std::convert::TryInto;
|
||||
use crate::{collection::Collection, define_newtype, types::Usn};
|
||||
use num_integer::Integer;
|
||||
use std::{collections::HashSet, convert::TryInto};
|
||||
|
||||
define_newtype!(NoteID, i64);
|
||||
|
||||
|
@ -20,12 +21,26 @@ pub struct Note {
|
|||
pub mtime: TimestampSecs,
|
||||
pub usn: Usn,
|
||||
pub tags: Vec<String>,
|
||||
pub fields: Vec<String>,
|
||||
pub(crate) fields: Vec<String>,
|
||||
pub(crate) sort_field: Option<String>,
|
||||
pub(crate) checksum: Option<u32>,
|
||||
}
|
||||
|
||||
impl Note {
|
||||
pub(crate) fn new(ntid: NoteTypeID, field_count: usize) -> Self {
|
||||
Note {
|
||||
id: NoteID(0),
|
||||
guid: guid(),
|
||||
ntid,
|
||||
mtime: TimestampSecs(0),
|
||||
usn: Usn(0),
|
||||
tags: vec![],
|
||||
fields: vec!["".to_string(); field_count],
|
||||
sort_field: None,
|
||||
checksum: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fields(&self) -> &Vec<String> {
|
||||
&self.fields
|
||||
}
|
||||
|
@ -58,9 +73,23 @@ impl Note {
|
|||
self.sort_field = Some(sort_field.into());
|
||||
self.checksum = Some(checksum);
|
||||
self.mtime = TimestampSecs::now();
|
||||
// hard-coded for now
|
||||
self.usn = usn;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn nonempty_fields(&self) -> HashSet<u16> {
|
||||
self.fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(ord, s)| {
|
||||
if s.trim().is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(ord as u16)
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Text must be passed to strip_html_preserving_image_filenames() by
|
||||
|
@ -69,3 +98,51 @@ pub(crate) fn field_checksum(text: &str) -> u32 {
|
|||
let digest = sha1::Sha1::from(text).digest().bytes();
|
||||
u32::from_be_bytes(digest[..4].try_into().unwrap())
|
||||
}
|
||||
|
||||
fn guid() -> String {
|
||||
anki_base91(rand::random())
|
||||
}
|
||||
|
||||
fn anki_base91(mut n: u64) -> String {
|
||||
let table = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\
|
||||
0123456789!#$%&()*+,-./:;<=>?@[]^_`{|}~";
|
||||
let mut buf = String::new();
|
||||
while n > 0 {
|
||||
let (q, r) = n.div_rem(&(table.len() as u64));
|
||||
buf.push(table[r as usize] as char);
|
||||
n = q;
|
||||
}
|
||||
|
||||
buf.chars().rev().collect()
|
||||
}
|
||||
|
||||
impl Collection {
|
||||
pub fn add_note(&mut self, note: &mut Note) -> Result<()> {
|
||||
self.transact(None, |col| {
|
||||
println!("fixme: need to add cards, abort if no cards generated, etc");
|
||||
// fixme: proper index
|
||||
note.prepare_for_update(0, col.usn()?);
|
||||
col.storage.add_note(note)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{anki_base91, field_checksum};
|
||||
|
||||
#[test]
|
||||
fn test_base91() {
|
||||
// match the python implementation for now
|
||||
assert_eq!(anki_base91(0), "");
|
||||
assert_eq!(anki_base91(1), "b");
|
||||
assert_eq!(anki_base91(u64::max_value()), "Rj&Z5m[>Zp");
|
||||
assert_eq!(anki_base91(1234567890), "saAKk");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_field_checksum() {
|
||||
assert_eq!(field_checksum("test"), 2840236005);
|
||||
assert_eq!(field_checksum("今日"), 1464653051);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ use crate::{
|
|||
collection::Collection,
|
||||
define_newtype,
|
||||
err::{AnkiError, Result},
|
||||
notes::Note,
|
||||
template::{without_legacy_template_directives, FieldRequirements, ParsedTemplate},
|
||||
text::ensure_string_in_nfc,
|
||||
timestamp::TimestampSecs,
|
||||
|
@ -163,6 +164,10 @@ impl NoteType {
|
|||
self.ensure_names_unique();
|
||||
self.update_requirements();
|
||||
}
|
||||
|
||||
pub fn new_note(&self) -> Note {
|
||||
Note::new(self.id, self.fields.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NoteType> for NoteTypeProto {
|
||||
|
|
|
@ -35,7 +35,7 @@ impl Collection {
|
|||
Some(map) => map,
|
||||
};
|
||||
|
||||
let nids = self.search_notes(&format!("mid:{}", nt.id))?;
|
||||
let nids = self.search_notes_only(&format!("mid:{}", nt.id))?;
|
||||
let usn = self.usn()?;
|
||||
for nid in nids {
|
||||
let mut note = self.storage.get_note(nid)?.unwrap();
|
||||
|
@ -63,11 +63,35 @@ impl Collection {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::collection::open_test_collection;
|
||||
use crate::err::Result;
|
||||
|
||||
#[test]
|
||||
fn fields() {
|
||||
let mut _col = open_test_collection();
|
||||
fn fields() -> Result<()> {
|
||||
let mut col = open_test_collection();
|
||||
let mut nt = col
|
||||
.storage
|
||||
.get_full_notetype(col.get_current_notetype_id().unwrap())?
|
||||
.unwrap();
|
||||
let mut note = nt.new_note();
|
||||
assert_eq!(note.fields.len(), 2);
|
||||
note.fields = vec!["one".into(), "two".into()];
|
||||
col.add_note(&mut note)?;
|
||||
|
||||
// fixme: need note adding before we can check this
|
||||
nt.add_field("three");
|
||||
col.update_notetype(&mut nt)?;
|
||||
|
||||
let note = col.storage.get_note(note.id)?.unwrap();
|
||||
assert_eq!(
|
||||
note.fields,
|
||||
vec!["one".to_string(), "two".into(), "".into()]
|
||||
);
|
||||
|
||||
nt.fields.remove(1);
|
||||
col.update_notetype(&mut nt)?;
|
||||
|
||||
let note = col.storage.get_note(note.id)?.unwrap();
|
||||
assert_eq!(note.fields, vec!["one".to_string(), "".into()]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
40
rslib/src/storage/note/add.sql
Normal file
40
rslib/src/storage/note/add.sql
Normal file
|
@ -0,0 +1,40 @@
|
|||
insert into notes (
|
||||
id,
|
||||
guid,
|
||||
mid,
|
||||
mod,
|
||||
usn,
|
||||
tags,
|
||||
flds,
|
||||
sfld,
|
||||
csum,
|
||||
flags,
|
||||
data
|
||||
)
|
||||
values
|
||||
(
|
||||
(
|
||||
case
|
||||
when ?1 in (
|
||||
select
|
||||
id
|
||||
from notes
|
||||
) then (
|
||||
select
|
||||
max(id) + 1
|
||||
from notes
|
||||
)
|
||||
else ?1
|
||||
end
|
||||
),
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
0,
|
||||
""
|
||||
)
|
|
@ -5,6 +5,7 @@ use crate::{
|
|||
err::Result,
|
||||
notes::{Note, NoteID},
|
||||
tags::{join_tags, split_tags},
|
||||
timestamp::TimestampMillis,
|
||||
};
|
||||
use rusqlite::{params, OptionalExtension};
|
||||
|
||||
|
@ -40,6 +41,7 @@ impl super::SqliteStorage {
|
|||
|
||||
/// Caller must call note.prepare_for_update() prior to calling this.
|
||||
pub(crate) fn update_note(&self, note: &Note) -> Result<()> {
|
||||
assert!(note.id.0 != 0);
|
||||
let mut stmt = self.db.prepare_cached(include_str!("update.sql"))?;
|
||||
stmt.execute(params![
|
||||
note.guid,
|
||||
|
@ -47,11 +49,29 @@ impl super::SqliteStorage {
|
|||
note.mtime,
|
||||
note.usn,
|
||||
join_tags(¬e.tags),
|
||||
join_fields(¬e.fields),
|
||||
join_fields(¬e.fields()),
|
||||
note.sort_field.as_ref().unwrap(),
|
||||
note.checksum.unwrap(),
|
||||
note.id
|
||||
])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn add_note(&self, note: &mut Note) -> Result<()> {
|
||||
assert!(note.id.0 == 0);
|
||||
let mut stmt = self.db.prepare_cached(include_str!("add.sql"))?;
|
||||
stmt.execute(params![
|
||||
TimestampMillis::now(),
|
||||
note.guid,
|
||||
note.ntid,
|
||||
note.mtime,
|
||||
note.usn,
|
||||
join_tags(¬e.tags),
|
||||
join_fields(¬e.fields()),
|
||||
note.sort_field.as_ref().unwrap(),
|
||||
note.checksum.unwrap(),
|
||||
])?;
|
||||
note.id.0 = self.db.last_insert_rowid();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue