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-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-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-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-did = `did:` must be followed by a valid deck id.
|
||||||
search-invalid-mid = `mid:` must be followed by a note type id.
|
search-invalid-mid = `mid:` must be followed by a note type id.
|
||||||
search-invalid-other = please check for typing mistakes.
|
search-invalid-other = please check for typing mistakes.
|
||||||
|
@ -163,6 +163,9 @@ impl AnkiError {
|
|||||||
SearchErrorKind::InvalidRatedEase(ctx) => i18n
|
SearchErrorKind::InvalidRatedEase(ctx) => i18n
|
||||||
.trn(TR::SearchInvalidRatedEase, tr_strs!["val"=>(ctx)])
|
.trn(TR::SearchInvalidRatedEase, tr_strs!["val"=>(ctx)])
|
||||||
.into(),
|
.into(),
|
||||||
|
SearchErrorKind::InvalidNumber(ctx) => i18n
|
||||||
|
.trn(TR::SearchInvalidNumber, tr_strs!["val"=>(ctx)])
|
||||||
|
.into(),
|
||||||
SearchErrorKind::InvalidResched => i18n
|
SearchErrorKind::InvalidResched => i18n
|
||||||
.trn(
|
.trn(
|
||||||
TR::SearchInvalidFollowedByPositiveDays,
|
TR::SearchInvalidFollowedByPositiveDays,
|
||||||
@ -454,6 +457,7 @@ pub enum SearchErrorKind {
|
|||||||
InvalidPropFloat(String),
|
InvalidPropFloat(String),
|
||||||
InvalidPropInteger(String),
|
InvalidPropInteger(String),
|
||||||
InvalidPropUnsigned(String),
|
InvalidPropUnsigned(String),
|
||||||
|
InvalidNumber(String),
|
||||||
InvalidDid,
|
InvalidDid,
|
||||||
InvalidMid,
|
InvalidMid,
|
||||||
Regex(String),
|
Regex(String),
|
||||||
|
@ -17,7 +17,11 @@ use nom::{
|
|||||||
sequence::{preceded, separated_pair},
|
sequence::{preceded, separated_pair},
|
||||||
};
|
};
|
||||||
use regex::{Captures, Regex};
|
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 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>>>;
|
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
|
/// 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, _>((
|
let (tail, prop) = alt::<_, _, ParseError, _>((
|
||||||
tag("ivl"),
|
tag("ivl"),
|
||||||
tag("due"),
|
tag("due"),
|
||||||
@ -372,8 +376,13 @@ fn parse_prop(s: &str) -> ParseResult<SearchNode> {
|
|||||||
tag("pos"),
|
tag("pos"),
|
||||||
tag("rated"),
|
tag("rated"),
|
||||||
tag("resched"),
|
tag("resched"),
|
||||||
))(s)
|
))(prop_clause)
|
||||||
.map_err(|_| parse_failure(s, FailKind::InvalidPropProperty(s.into())))?;
|
.map_err(|_| {
|
||||||
|
parse_failure(
|
||||||
|
prop_clause,
|
||||||
|
FailKind::InvalidPropProperty(prop_clause.into()),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
let (num, operator) = alt::<_, _, ParseError, _>((
|
let (num, operator) = alt::<_, _, ParseError, _>((
|
||||||
tag("<="),
|
tag("<="),
|
||||||
@ -383,87 +392,21 @@ fn parse_prop(s: &str) -> ParseResult<SearchNode> {
|
|||||||
tag("<"),
|
tag("<"),
|
||||||
tag(">"),
|
tag(">"),
|
||||||
))(tail)
|
))(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" {
|
let kind = match prop {
|
||||||
if let Ok(f) = num.parse::<f32>() {
|
"ease" => PropertyKind::Ease(parse_prop_float(num, prop_clause)?),
|
||||||
PropertyKind::Ease(f)
|
"due" => PropertyKind::Due(parse_prop_integer(num, prop_clause)?),
|
||||||
} else {
|
"rated" => parse_prop_rated(num, prop_clause)?,
|
||||||
return Err(parse_failure(
|
"resched" => PropertyKind::Rated(
|
||||||
s,
|
parse_prop_integer::<i32>(num, prop_clause)?.min(0),
|
||||||
FailKind::InvalidPropFloat(format!("{}{}", prop, operator)),
|
EaseKind::ManualReschedule,
|
||||||
));
|
),
|
||||||
}
|
"ivl" => PropertyKind::Interval(parse_prop_integer(num, prop_clause)?),
|
||||||
} else if prop == "due" {
|
"reps" => PropertyKind::Reps(parse_prop_integer(num, prop_clause)?),
|
||||||
if let Ok(i) = num.parse::<i32>() {
|
"lapses" => PropertyKind::Lapses(parse_prop_integer(num, prop_clause)?),
|
||||||
PropertyKind::Due(i)
|
"pos" => PropertyKind::Position(parse_prop_integer(num, prop_clause)?),
|
||||||
} 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),
|
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(parse_failure(
|
|
||||||
s,
|
|
||||||
FailKind::InvalidPropUnsigned(format!("{}{}", prop, operator)),
|
|
||||||
));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(SearchNode::Property {
|
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
|
/// eg added:1
|
||||||
fn parse_added(s: &str) -> ParseResult<SearchNode> {
|
fn parse_added(s: &str) -> ParseResult<SearchNode> {
|
||||||
if let Ok(days) = s.parse::<u32>() {
|
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:pos~1", InvalidPropOperator("pos".to_string()));
|
||||||
assert_err_kind("prop:reps10", InvalidPropOperator("reps".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>", InvalidNumber("ease>".to_string()));
|
||||||
assert_err_kind("prop:ease!=one", InvalidPropFloat("ease!=".to_string()));
|
assert_err_kind("prop:ease!=one", InvalidNumber("ease!=one".to_string()));
|
||||||
assert_err_kind("prop:ease<1,3", InvalidPropFloat("ease<".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>", InvalidNumber("due>".to_string()));
|
||||||
assert_err_kind("prop:due=0.5", InvalidPropInteger("due=".to_string()));
|
assert_err_kind("prop:due=0.5", InvalidNumber("due=0.5".to_string()));
|
||||||
assert_err_kind("prop:due<foo", InvalidPropInteger("due<".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:ivl>", InvalidNumber("ivl>".to_string()));
|
||||||
assert_err_kind("prop:reps=1.1", InvalidPropUnsigned("reps=".to_string()));
|
assert_err_kind("prop:reps=1.1", InvalidNumber("reps=1.1".to_string()));
|
||||||
assert_err_kind(
|
assert_err_kind("prop:lapses!=-1", InvalidNumber("lapses!=-1".to_string()));
|
||||||
"prop:lapses!=-1",
|
|
||||||
InvalidPropUnsigned("lapses!=".to_string()),
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user