mirror of
https://github.com/ankitects/anki.git
synced 2025-09-25 01:06:35 -04:00
Fix a crash when truncating note text
String::truncate() is a bit of a footgun, and I've hit this before too :-)
This commit is contained in:
parent
24231214c5
commit
cf9d490576
3 changed files with 30 additions and 6 deletions
|
@ -17,7 +17,10 @@ use crate::{
|
||||||
ImportProgress,
|
ImportProgress,
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
text::{replace_media_refs, strip_html_preserving_media_filenames, CowMapping},
|
text::{
|
||||||
|
replace_media_refs, strip_html_preserving_media_filenames, truncate_to_char_boundary,
|
||||||
|
CowMapping,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NoteContext<'a> {
|
struct NoteContext<'a> {
|
||||||
|
@ -73,7 +76,7 @@ impl Note {
|
||||||
let mut reduced = strip_html_preserving_media_filenames(&field)
|
let mut reduced = strip_html_preserving_media_filenames(&field)
|
||||||
.get_owned()
|
.get_owned()
|
||||||
.unwrap_or(field);
|
.unwrap_or(field);
|
||||||
reduced.truncate(80);
|
truncate_to_char_boundary(&mut reduced, 80);
|
||||||
reduced
|
reduced
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
|
|
@ -241,18 +241,18 @@ fn split_and_truncate_filename(fname: &str, max_bytes: usize) -> (&str, &str) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// cap extension to 10 bytes so stem_len can't be negative
|
// cap extension to 10 bytes so stem_len can't be negative
|
||||||
ext = truncate_to_char_boundary(ext, 10);
|
ext = truncated_to_char_boundary(ext, 10);
|
||||||
|
|
||||||
// cap stem, allowing for the . and a trailing _
|
// cap stem, allowing for the . and a trailing _
|
||||||
let stem_len = max_bytes - ext.len() - 2;
|
let stem_len = max_bytes - ext.len() - 2;
|
||||||
stem = truncate_to_char_boundary(stem, stem_len);
|
stem = truncated_to_char_boundary(stem, stem_len);
|
||||||
|
|
||||||
(stem, ext)
|
(stem, ext)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trim a string on a valid UTF8 boundary.
|
/// Return a substring on a valid UTF8 boundary.
|
||||||
/// Based on a funtion in the Rust stdlib.
|
/// Based on a funtion in the Rust stdlib.
|
||||||
fn truncate_to_char_boundary(s: &str, mut max: usize) -> &str {
|
fn truncated_to_char_boundary(s: &str, mut max: usize) -> &str {
|
||||||
if max >= s.len() {
|
if max >= s.len() {
|
||||||
s
|
s
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -206,6 +206,17 @@ pub fn strip_html_for_tts(html: &str) -> Cow<str> {
|
||||||
.map_cow(strip_html)
|
.map_cow(strip_html)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Truncate a String on a valid UTF8 boundary.
|
||||||
|
pub(crate) fn truncate_to_char_boundary(s: &mut String, mut max: usize) {
|
||||||
|
if max >= s.len() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while !s.is_char_boundary(max) {
|
||||||
|
max -= 1;
|
||||||
|
}
|
||||||
|
s.truncate(max);
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct MediaRef<'a> {
|
pub(crate) struct MediaRef<'a> {
|
||||||
pub full_ref: &'a str,
|
pub full_ref: &'a str,
|
||||||
|
@ -589,4 +600,14 @@ mod test {
|
||||||
"<img src=spam>[sound:spam]<img src=baz.jpg>",
|
"<img src=spam>[sound:spam]<img src=baz.jpg>",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn truncate() {
|
||||||
|
let mut s = "日本語".to_string();
|
||||||
|
truncate_to_char_boundary(&mut s, 6);
|
||||||
|
assert_eq!(&s, "日本");
|
||||||
|
let mut s = "日本語".to_string();
|
||||||
|
truncate_to_char_boundary(&mut s, 1);
|
||||||
|
assert_eq!(&s, "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue