use crate::{
Lint, Token, TokenKind, TokenStringExt,
expr::{Expr, SequenceExpr},
linting::{ExprLinter, LintKind, Suggestion, expr_linter::Chunk},
};
pub struct ForFreeOfCharge {
expr: SequenceExpr,
}
impl Default for ForFreeOfCharge {
fn default() -> Self {
Self {
expr: SequenceExpr::aco("for")
.t_ws()
.t_aco("free")
.t_ws_h()
.t_aco("of")
.t_ws_h()
.t_aco("charge")
.then_any_of(vec![
Box::new(SequenceExpr::default().then_kind_any(&[
TokenKind::is_sentence_terminator,
TokenKind::is_comma,
TokenKind::is_quote,
])),
Box::new(SequenceExpr::whitespace().then_kind_any_but_not(
&[
TokenKind::is_conjunction,
TokenKind::is_preposition,
TokenKind::is_verb,
],
TokenKind::is_noun,
)),
]),
}
}
}
impl ExprLinter for ForFreeOfCharge {
type Unit = Chunk;
fn match_to_lint(&self, matched_tokens: &[Token], source: &[char]) -> Option<Lint> {
let span = matched_tokens[0..7].span()?;
Some(Lint {
span,
lint_kind: LintKind::Redundancy,
suggestions: vec![
Suggestion::replace_with_match_case_str("for free", span.get_content(source)),
Suggestion::replace_with_match_case_str("free of charge", span.get_content(source)),
],
message: "Use only either `for free` or `free of charge`".to_string(),
..Default::default()
})
}
fn expr(&self) -> &dyn Expr {
&self.expr
}
fn description(&self) -> &str {
"Corrects `for free of charge` to either `for free` or `free of charge`."
}
}
#[cfg(test)]
mod tests {
use crate::linting::tests::{assert_good_and_bad_suggestions, assert_no_lints};
use super::ForFreeOfCharge;
#[test]
fn fix_for_foc_question_mark() {
assert_good_and_bad_suggestions(
"Where to check my paper grammars for free of charge?",
ForFreeOfCharge::default(),
&[
"Where to check my paper grammars for free?",
"Where to check my paper grammars free of charge?",
],
&[],
);
}
#[test]
fn fix_for_foc_for() {
assert_good_and_bad_suggestions(
"In Hungary, restaurants are required by law to provide tap water for free of charge for any customers upon request.",
ForFreeOfCharge::default(),
&[
"In Hungary, restaurants are required by law to provide tap water for free for any customers upon request.",
"In Hungary, restaurants are required by law to provide tap water free of charge for any customers upon request.",
],
&[],
);
}
#[test]
fn fix_for_foc_comma_not() {
assert_good_and_bad_suggestions(
"I provide this software in its entirety for free of charge, and support via GitHub is also free.",
ForFreeOfCharge::default(),
&[
"I provide this software in its entirety for free, and support via GitHub is also free.",
"I provide this software in its entirety free of charge, and support via GitHub is also free.",
],
&[],
);
}
#[test]
fn fix_for_foc_quote() {
assert_good_and_bad_suggestions(
"Copyright protects copying, not \"giving things away for free of charge\" or anything like that.",
ForFreeOfCharge::default(),
&[
"Copyright protects copying, not \"giving things away for free\" or anything like that.",
"Copyright protects copying, not \"giving things away free of charge\" or anything like that.",
],
&[],
);
}
#[test]
fn fix_for_foc_otherwise() {
assert_good_and_bad_suggestions(
"For Wizzair, if you have purchased a separate ticket for your infant, then only car seat is allowed for free of charge otherwise its not.",
ForFreeOfCharge::default(),
&[
"For Wizzair, if you have purchased a separate ticket for your infant, then only car seat is allowed for free otherwise its not.",
"For Wizzair, if you have purchased a separate ticket for your infant, then only car seat is allowed free of charge otherwise its not.",
],
&[],
);
}
#[test]
fn fix_for_foc_comma_but() {
assert_good_and_bad_suggestions(
"user may use the Software for free of charge, but the Software is basically paid software",
ForFreeOfCharge::default(),
&[
"user may use the Software for free, but the Software is basically paid software",
"user may use the Software free of charge, but the Software is basically paid software",
],
&[],
);
}
#[test]
fn fix_for_foc_period() {
assert_good_and_bad_suggestions(
"...giving them away for free of charge. :-) SCNR",
ForFreeOfCharge::default(),
&[
"...giving them away for free. :-) SCNR",
"...giving them away free of charge. :-) SCNR",
],
&[],
);
}
#[test]
fn fix_for_foc_are() {
assert_good_and_bad_suggestions(
"Also what they offer for free of charge are very very powerful machines for unlimited amount of time.",
ForFreeOfCharge::default(),
&[
"Also what they offer for free are very very powerful machines for unlimited amount of time.",
"Also what they offer free of charge are very very powerful machines for unlimited amount of time.",
],
&[],
);
}
#[test]
fn allows_foc_as_adj_tier() {
assert_no_lints(
"We prioritize interactive notebook compute for free of charge tier users.",
ForFreeOfCharge::default(),
);
}
#[test]
fn allows_foc_as_adj_product() {
assert_no_lints(
"This is a cost for free of charge product.",
ForFreeOfCharge::default(),
);
}
#[test]
fn allows_foc_as_adj_feedback() {
assert_no_lints(
"... but if you are not a student in a university I don't know where you should turn for free of charge feedback",
ForFreeOfCharge::default(),
);
}
#[test]
fn allows_foc_as_adj_place() {
assert_no_lints(
"you see how many free places are in a particular garage, so you decide either to occupy it or search for free of charge place somewhere else",
ForFreeOfCharge::default(),
);
}
#[test]
fn allows_foc_adj_events() {
assert_no_lints(
"Create reservation for free-of-charge events should not require any approval of the reservation",
ForFreeOfCharge::default(),
);
}
#[test]
fn allows_working_for_free_of_charge() {
assert_no_lints(
"I have a client who I’m working for free of charge",
ForFreeOfCharge::default(),
);
}
}