use std::{fmt, str::FromStr};
#[cfg(feature = "rusqlite")]
use rusqlite::{
ToSql,
types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, ValueRef}
};
use crate::{StrVal, err::Error};
#[derive(Debug, Clone)]
pub struct Bool {
sval: String,
val: bool
}
impl Default for Bool {
fn default() -> Self {
Self {
sval: "false".into(),
val: false
}
}
}
impl StrVal for Bool {
type Type = bool;
fn set(&mut self, sval: &str) -> Result<Self::Type, Error> {
let dv = sval.parse::<Self>()?;
self.sval = sval.to_string();
self.val = dv.val;
Ok(dv.val)
}
fn get(&self) -> Self::Type {
self.val
}
fn val_str(&self) -> Option<String> {
if self.val {
Some(String::from("true"))
} else {
Some(String::from("false"))
}
}
fn get_str_vals(&self) -> (String, Option<String>) {
(self.sval.clone(), None)
}
}
impl AsRef<str> for Bool {
fn as_ref(&self) -> &str {
&self.sval
}
}
impl fmt::Display for Bool {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.sval)
}
}
impl FromStr for Bool {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let val = match s {
"0" | "false" | "f" | "off" => false,
"1" | "true" | "t" | "on" => true,
_ => return Err(Error::Invalid("Unknown boolean value".into()))
};
Ok(Self {
sval: s.to_string(),
val
})
}
}
#[cfg(feature = "rusqlite")]
#[cfg_attr(docsrs, doc(cfg(feature = "rusqlite")))]
impl ToSql for Bool {
fn to_sql(&self) -> Result<ToSqlOutput<'_>, rusqlite::Error> {
Ok(ToSqlOutput::from(self.as_ref()))
}
}
#[cfg(feature = "rusqlite")]
#[cfg_attr(docsrs, doc(cfg(feature = "rusqlite")))]
impl FromSql for Bool {
#[inline]
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
let s = String::column_result(value)?;
s.parse::<Self>()
.map_err(|e| FromSqlError::Other(Box::new(e)))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "rusqlite")]
use rusqlite::{Connection, params};
#[test]
#[should_panic(expected = "Invalid(\"Unknown boolean value\"")]
fn invalid() {
"hello".parse::<Bool>().unwrap();
}
#[test]
fn false_values() {
for s in ["0", "false", "f", "off"] {
let v = s.parse::<Bool>().unwrap();
assert_eq!(v.as_ref(), s);
assert!(!v.get());
}
}
#[test]
fn true_values() {
for s in ["1", "true", "t", "on"] {
let v = s.parse::<Bool>().unwrap();
assert_eq!(v.as_ref(), s);
assert!(v.get());
}
}
#[cfg(feature = "rusqlite")]
fn memdb() -> Result<Connection, rusqlite::Error> {
let conn = Connection::open_in_memory()?;
conn.execute_batch(
"CREATE TABLE tbl (id INTEGER PRIMARY KEY, txtval TEXT)"
)?;
Ok(conn)
}
#[cfg(feature = "rusqlite")]
#[test]
fn insert_query() {
let conn = memdb().unwrap();
for (idx, s) in ["1", "true", "t", "on"].iter().enumerate() {
let b = Bool::from_str(s).unwrap();
conn
.execute(
"INSERT INTO tbl (id, txtval) VALUES (?, ?);",
params![idx, b]
)
.unwrap();
let mut stmt =
conn.prepare("SELECT txtval FROM tbl WHERE id=?;").unwrap();
let b: Bool = stmt.query_one([idx], |row| row.get(0)).unwrap();
assert!(b.get());
}
conn.execute("DELETE FROM tbl;", []).unwrap();
for (idx, s) in ["0", "false", "f", "off"].iter().enumerate() {
let b = Bool::from_str(s).unwrap();
conn
.execute(
"INSERT INTO tbl (id, txtval) VALUES (?, ?);",
params![idx, b]
)
.unwrap();
let mut stmt =
conn.prepare("SELECT txtval FROM tbl WHERE id=?;").unwrap();
let b: Bool = stmt.query_one([idx], |row| row.get(0)).unwrap();
assert!(!b.get());
}
}
#[cfg(feature = "rusqlite")]
#[test]
#[should_panic(expected = "FromSqlConversionFailure(0, Text, \
Invalid(\"Unknown boolean value\"))")]
fn bad_query() {
let conn = memdb().unwrap();
conn
.execute(
"INSERT INTO tbl (id, txtval) VALUES (?, ?);",
params![1, "hello"]
)
.unwrap();
let mut stmt = conn.prepare("SELECT txtval FROM tbl WHERE id=?;").unwrap();
let _b: Bool = stmt.query_one([1], |row| row.get(0)).unwrap();
}
}