criterium 3.1.3

Lightweigt dynamic database queries for rusqlite.
Documentation
// SPDX-FileCopyrightText: 2025 Slatian
//
// SPDX-License-Identifier: LGPL-3.0-only

//! Integrates StringCriterium with rusqlite.

use rusqlite::types::Value;

use crate::rusqlite::ToRusqliteSingleField;
use crate::sql::Field;
use crate::NumberCriterium;
use crate::StringCriterium;

impl<F: Field> ToRusqliteSingleField<F> for StringCriterium {
	fn get_sql_where(&self, field_name: &str) -> String {
		match self {
			Self::Equals(_) => format!("{field_name} = ?"),
			Self::HasPrefix(_) | Self::HasSuffix(_) | Self::Contains(_) => {
				format!("{field_name} LIKE ? ESCAPE '\\'")
			}
			Self::Length(c) => <NumberCriterium<usize> as ToRusqliteSingleField<F>>::get_sql_where(
				c,
				&format!("length({})", field_name),
			),
			Self::IsNone => format!("{field_name} is NULL"),
		}
		.to_string()
	}

	fn get_inverted_sql_where(&self, field_name: &str) -> Option<String> {
		Some(match self {
			Self::Equals(_) => format!("{field_name} != ?"),
			Self::HasPrefix(_)|
			Self::HasSuffix(_)|
			Self::Contains(_) =>
				format!("{field_name} NOT LIKE ? ESCAPE '\\'"),
			Self::Length(c) =>
				return <NumberCriterium<usize> as ToRusqliteSingleField<F>>::get_inverted_sql_where(c, &format!("length({})", field_name)),
			Self::IsNone => format!("{field_name} is not NULL"),
		}.to_string())
	}

	fn get_where_values(&self, is_inverted: bool) -> Vec<Value> {
		vec![Value::Text(match self {
			Self::Equals(s) => s.clone(),
			Self::HasPrefix(s) => escape_like_with_backslash(s) + "%",
			Self::HasSuffix(s) => "%".to_owned() + &escape_like_with_backslash(s),
			Self::Contains(s) => "%".to_owned() + &escape_like_with_backslash(s) + "%",
			Self::Length(c) => {
				return <NumberCriterium<usize> as ToRusqliteSingleField<F>>::get_where_values(
					c,
					is_inverted,
				);
			}
			Self::IsNone => {
				return Vec::new();
			}
		})]
	}
}

fn escape_like_with_backslash(text: &str) -> String {
	text.replace("\\", "\\\\")
		.replace("%", "\\%")
		.replace("_", "\\_")
}