simplify write_props() and associated translations
This is a work in progress; see associated PR discussion to follow.
This commit is contained in:
parent
a4ec467284
commit
0b83efb63e
@ -22,6 +22,7 @@ search-invalid-prop-operator = `prop:{ $val }` must be followed by one of the co
|
||||
search-invalid-prop-float = `prop:{ $val }` must be followed by a decimal number.
|
||||
search-invalid-prop-integer = `prop:{ $val }` must be followed by a whole number.
|
||||
search-invalid-prop-unsigned = `prop:{ $val }` must be followed by a non-negative whole number.
|
||||
search-invalid-number = `{ $val }`: invalid number provided
|
||||
search-invalid-did = `did:` must be followed by a valid deck id.
|
||||
search-invalid-mid = `mid:` must be followed by a note type id.
|
||||
search-invalid-other = please check for typing mistakes.
|
||||
|
@ -163,6 +163,9 @@ impl AnkiError {
|
||||
SearchErrorKind::InvalidRatedEase(ctx) => i18n
|
||||
.trn(TR::SearchInvalidRatedEase, tr_strs!["val"=>(ctx)])
|
||||
.into(),
|
||||
SearchErrorKind::InvalidNumber(ctx) => i18n
|
||||
.trn(TR::SearchInvalidNumber, tr_strs!["val"=>(ctx)])
|
||||
.into(),
|
||||
SearchErrorKind::InvalidResched => i18n
|
||||
.trn(
|
||||
TR::SearchInvalidFollowedByPositiveDays,
|
||||
@ -454,6 +457,7 @@ pub enum SearchErrorKind {
|
||||
InvalidPropFloat(String),
|
||||
InvalidPropInteger(String),
|
||||
InvalidPropUnsigned(String),
|
||||
InvalidNumber(String),
|
||||
InvalidDid,
|
||||
InvalidMid,
|
||||
Regex(String),
|
||||
|
@ -17,7 +17,11 @@ use nom::{
|
||||
sequence::{preceded, separated_pair},
|
||||
};
|
||||
use regex::{Captures, Regex};
|
||||
use std::borrow::Cow;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
num::{ParseFloatError, ParseIntError},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
type IResult<'a, O> = std::result::Result<(&'a str, O), nom::Err<ParseError<'a>>>;
|
||||
type ParseResult<'a, O> = std::result::Result<O, nom::Err<ParseError<'a>>>;
|
||||
@ -362,7 +366,7 @@ fn parse_resched(s: &str) -> ParseResult<SearchNode> {
|
||||
}
|
||||
|
||||
/// eg prop:ivl>3, prop:ease!=2.5
|
||||
fn parse_prop(s: &str) -> ParseResult<SearchNode> {
|
||||
fn parse_prop(prop_clause: &str) -> ParseResult<SearchNode> {
|
||||
let (tail, prop) = alt::<_, _, ParseError, _>((
|
||||
tag("ivl"),
|
||||
tag("due"),
|
||||
@ -372,8 +376,13 @@ fn parse_prop(s: &str) -> ParseResult<SearchNode> {
|
||||
tag("pos"),
|
||||
tag("rated"),
|
||||
tag("resched"),
|
||||
))(s)
|
||||
.map_err(|_| parse_failure(s, FailKind::InvalidPropProperty(s.into())))?;
|
||||
))(prop_clause)
|
||||
.map_err(|_| {
|
||||
parse_failure(
|
||||
prop_clause,
|
||||
FailKind::InvalidPropProperty(prop_clause.into()),
|
||||
)
|
||||
})?;
|
||||
|
||||
let (num, operator) = alt::<_, _, ParseError, _>((
|
||||
tag("<="),
|
||||
@ -383,87 +392,21 @@ fn parse_prop(s: &str) -> ParseResult<SearchNode> {
|
||||
tag("<"),
|
||||
tag(">"),
|
||||
))(tail)
|
||||
.map_err(|_| parse_failure(s, FailKind::InvalidPropOperator(prop.to_string())))?;
|
||||
.map_err(|_| parse_failure(prop_clause, FailKind::InvalidPropOperator(prop.to_string())))?;
|
||||
|
||||
let kind = if prop == "ease" {
|
||||
if let Ok(f) = num.parse::<f32>() {
|
||||
PropertyKind::Ease(f)
|
||||
} else {
|
||||
return Err(parse_failure(
|
||||
s,
|
||||
FailKind::InvalidPropFloat(format!("{}{}", prop, operator)),
|
||||
));
|
||||
}
|
||||
} else if prop == "due" {
|
||||
if let Ok(i) = num.parse::<i32>() {
|
||||
PropertyKind::Due(i)
|
||||
} else {
|
||||
return Err(parse_failure(
|
||||
s,
|
||||
FailKind::InvalidPropInteger(format!("{}{}", prop, operator)),
|
||||
));
|
||||
}
|
||||
} else if prop == "rated" {
|
||||
let mut it = num.splitn(2, ':');
|
||||
|
||||
let days: i32 = if let Ok(i) = it.next().unwrap().parse::<i32>() {
|
||||
i.min(0)
|
||||
} else {
|
||||
return Err(parse_failure(
|
||||
s,
|
||||
FailKind::InvalidPropInteger(format!("{}{}", prop, operator)),
|
||||
));
|
||||
};
|
||||
|
||||
let ease = match it.next() {
|
||||
Some(v) => {
|
||||
if let Ok(u) = v.parse::<u8>() {
|
||||
if (1..5).contains(&u) {
|
||||
EaseKind::AnswerButton(u)
|
||||
} else {
|
||||
return Err(parse_failure(
|
||||
s,
|
||||
FailKind::InvalidRatedEase(format!(
|
||||
"prop:{}{}{}",
|
||||
prop,
|
||||
operator,
|
||||
days.to_string()
|
||||
)),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(parse_failure(
|
||||
s,
|
||||
FailKind::InvalidPropInteger(format!("{}{}", prop, operator)),
|
||||
));
|
||||
}
|
||||
}
|
||||
None => EaseKind::AnyAnswerButton,
|
||||
};
|
||||
|
||||
PropertyKind::Rated(days, ease)
|
||||
} else if prop == "resched" {
|
||||
if let Ok(days) = num.parse::<i32>() {
|
||||
PropertyKind::Rated(days.min(0), EaseKind::ManualReschedule)
|
||||
} else {
|
||||
return Err(parse_failure(
|
||||
s,
|
||||
FailKind::InvalidPropInteger(format!("{}{}", prop, operator)),
|
||||
));
|
||||
}
|
||||
} else if let Ok(u) = num.parse::<u32>() {
|
||||
match prop {
|
||||
"ivl" => PropertyKind::Interval(u),
|
||||
"reps" => PropertyKind::Reps(u),
|
||||
"lapses" => PropertyKind::Lapses(u),
|
||||
"pos" => PropertyKind::Position(u),
|
||||
let kind = match prop {
|
||||
"ease" => PropertyKind::Ease(parse_prop_float(num, prop_clause)?),
|
||||
"due" => PropertyKind::Due(parse_prop_integer(num, prop_clause)?),
|
||||
"rated" => parse_prop_rated(num, prop_clause)?,
|
||||
"resched" => PropertyKind::Rated(
|
||||
parse_prop_integer::<i32>(num, prop_clause)?.min(0),
|
||||
EaseKind::ManualReschedule,
|
||||
),
|
||||
"ivl" => PropertyKind::Interval(parse_prop_integer(num, prop_clause)?),
|
||||
"reps" => PropertyKind::Reps(parse_prop_integer(num, prop_clause)?),
|
||||
"lapses" => PropertyKind::Lapses(parse_prop_integer(num, prop_clause)?),
|
||||
"pos" => PropertyKind::Position(parse_prop_integer(num, prop_clause)?),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
return Err(parse_failure(
|
||||
s,
|
||||
FailKind::InvalidPropUnsigned(format!("{}{}", prop, operator)),
|
||||
));
|
||||
};
|
||||
|
||||
Ok(SearchNode::Property {
|
||||
@ -472,6 +415,43 @@ fn parse_prop(s: &str) -> ParseResult<SearchNode> {
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_prop_float<'a, N>(num: &str, prop_clause: &'a str) -> ParseResult<'a, N>
|
||||
where
|
||||
N: FromStr,
|
||||
<N as FromStr>::Err: PartialEq<ParseFloatError>,
|
||||
{
|
||||
num.parse()
|
||||
.map_err(|_e| parse_failure(prop_clause, FailKind::InvalidNumber(prop_clause.to_owned())))
|
||||
}
|
||||
|
||||
fn parse_prop_integer<'a, N>(num: &str, prop_clause: &'a str) -> ParseResult<'a, N>
|
||||
where
|
||||
N: FromStr,
|
||||
<N as FromStr>::Err: PartialEq<ParseIntError>,
|
||||
{
|
||||
num.parse()
|
||||
.map_err(|_e| parse_failure(prop_clause, FailKind::InvalidNumber(prop_clause.to_owned())))
|
||||
}
|
||||
|
||||
fn parse_prop_rated<'a>(num: &str, prop_clause: &'a str) -> ParseResult<'a, PropertyKind> {
|
||||
let mut it = num.splitn(2, ':');
|
||||
let days = parse_prop_integer::<i32>(it.next().unwrap(), prop_clause)?.min(0);
|
||||
let ease = match it.next() {
|
||||
Some(v) => match parse_prop_integer(v, prop_clause)? {
|
||||
u @ 1..=5 => EaseKind::AnswerButton(u),
|
||||
_ => {
|
||||
return Err(parse_failure(
|
||||
prop_clause,
|
||||
FailKind::InvalidRatedEase(prop_clause.to_owned()),
|
||||
))
|
||||
}
|
||||
},
|
||||
None => EaseKind::AnyAnswerButton,
|
||||
};
|
||||
|
||||
Ok(PropertyKind::Rated(days, ease))
|
||||
}
|
||||
|
||||
/// eg added:1
|
||||
fn parse_added(s: &str) -> ParseResult<SearchNode> {
|
||||
if let Ok(days) = s.parse::<u32>() {
|
||||
@ -947,20 +927,17 @@ mod test {
|
||||
assert_err_kind("prop:pos~1", InvalidPropOperator("pos".to_string()));
|
||||
assert_err_kind("prop:reps10", InvalidPropOperator("reps".to_string()));
|
||||
|
||||
assert_err_kind("prop:ease>", InvalidPropFloat("ease>".to_string()));
|
||||
assert_err_kind("prop:ease!=one", InvalidPropFloat("ease!=".to_string()));
|
||||
assert_err_kind("prop:ease<1,3", InvalidPropFloat("ease<".to_string()));
|
||||
assert_err_kind("prop:ease>", InvalidNumber("ease>".to_string()));
|
||||
assert_err_kind("prop:ease!=one", InvalidNumber("ease!=one".to_string()));
|
||||
assert_err_kind("prop:ease<1,3", InvalidNumber("ease<1,3".to_string()));
|
||||
|
||||
assert_err_kind("prop:due>", InvalidPropInteger("due>".to_string()));
|
||||
assert_err_kind("prop:due=0.5", InvalidPropInteger("due=".to_string()));
|
||||
assert_err_kind("prop:due<foo", InvalidPropInteger("due<".to_string()));
|
||||
assert_err_kind("prop:due>", InvalidNumber("due>".to_string()));
|
||||
assert_err_kind("prop:due=0.5", InvalidNumber("due=0.5".to_string()));
|
||||
assert_err_kind("prop:due<foo", InvalidNumber("due<foo".to_string()));
|
||||
|
||||
assert_err_kind("prop:ivl>", InvalidPropUnsigned("ivl>".to_string()));
|
||||
assert_err_kind("prop:reps=1.1", InvalidPropUnsigned("reps=".to_string()));
|
||||
assert_err_kind(
|
||||
"prop:lapses!=-1",
|
||||
InvalidPropUnsigned("lapses!=".to_string()),
|
||||
);
|
||||
assert_err_kind("prop:ivl>", InvalidNumber("ivl>".to_string()));
|
||||
assert_err_kind("prop:reps=1.1", InvalidNumber("reps=1.1".to_string()));
|
||||
assert_err_kind("prop:lapses!=-1", InvalidNumber("lapses!=-1".to_string()));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user