Index: .efiles ================================================================== --- .efiles +++ .efiles @@ -8,9 +8,13 @@ src/str.rs src/binu64count.rs src/binu64size.rs src/decu64count.rs src/decu64size.rs +src/binusizecount.rs +src/binusizesize.rs +src/decusizecount.rs +src/decusizesize.rs src/dur.rs src/relabslim.rs src/select.rs src/percent.rs Index: Cargo.toml ================================================================== --- Cargo.toml +++ Cargo.toml @@ -1,8 +1,8 @@ [package] name = "strval" -version = "0.1.0" +version = "0.1.1" edition = "2024" license = "0BSD" # https://crates.io/category_slugs categories = ["text-processing"] keywords = ["parsing", "string", "rusqlite"] ADDED src/binusizecount.rs Index: src/binusizecount.rs ================================================================== --- /dev/null +++ src/binusizecount.rs @@ -0,0 +1,219 @@ +use std::{marker::PhantomData, str::FromStr}; + +#[cfg(feature = "rusqlite")] +use rusqlite::{ + ToSql, + types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, ValueRef} +}; + +use parse_size::ByteSuffix; + +use crate::{AnyUsize, Controller, StrVal, err::Error}; + + +/// A `usize` value that supports suffixes, like `1k`, `2.5k`, etc. +/// +/// This variant treats k as 1024. See [`DecUsizeCount`](super::DecUsizeCount) +/// for a decimal variant. +#[derive(Debug, Clone)] +pub struct BinUsizeCount { + sval: String, + val: usize, + _marker: PhantomData +} + +impl Default for BinUsizeCount { + fn default() -> Self { + Self { + sval: "0".into(), + val: 0, + _marker: PhantomData + } + } +} + +impl StrVal for BinUsizeCount +where + C: Controller +{ + type Type = usize; + + fn set(&mut self, sval: &str) -> Result { + let dv = sval.parse::()?; + + C::validate(&dv.val)?; + + self.sval = sval.to_string(); + self.val = dv.val; + Ok(dv.val) + } + + fn get(&self) -> Self::Type { + self.val + } + + fn val_str(&self) -> Option { + Some(self.val.to_string()) + } +} + +impl AsRef for BinUsizeCount { + fn as_ref(&self) -> &str { + &self.sval + } +} + +impl FromStr for BinUsizeCount { + type Err = Error; + + fn from_str(s: &str) -> Result { + let val = parse_size::Config::default() + .with_binary() + .with_byte_suffix(ByteSuffix::Deny) + .parse_size(s) + .map_err(|e| Error::Invalid(e.to_string()))?; + let val = + usize::try_from(val).map_err(|e| Error::OutOfBounds(e.to_string()))?; + Ok(Self { + sval: s.to_string(), + val, + _marker: PhantomData + }) + } +} + +#[cfg(feature = "rusqlite")] +#[cfg_attr(docsrs, doc(cfg(feature = "rusqlite")))] +impl ToSql for BinUsizeCount { + fn to_sql(&self) -> Result, rusqlite::Error> { + self.sval.to_sql() + } +} + +#[cfg(feature = "rusqlite")] +#[cfg_attr(docsrs, doc(cfg(feature = "rusqlite")))] +impl FromSql for BinUsizeCount { + #[inline] + fn column_result(value: ValueRef<'_>) -> FromSqlResult { + let s = String::column_result(value)?; + s.parse::() + .map_err(|e| FromSqlError::Other(Box::new(e))) + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(feature = "rusqlite")] + use rusqlite::{Connection, params}; + + struct SomeUsizeBound {} + + impl Controller for SomeUsizeBound { + type Type = usize; + fn def() -> String { + String::from("10") + } + fn validate(val: &Self::Type) -> Result<(), Error> { + if *val < 10 || *val > 256 * 1024 { + Err(Error::OutOfBounds("Must be 10 - 256K".into())) + } else { + Ok(()) + } + } + } + + #[test] + fn default1() { + #[allow(clippy::default_trait_access)] + let mut val: BinUsizeCount = Default::default(); + + let v = val.set("64k").unwrap(); + assert_eq!(v, 65536); + assert_eq!(val.get(), 65536); + } + + #[test] + fn default2() { + let mut val = ::default(); + + let v = val.set("64k").unwrap(); + assert_eq!(v, 65536); + assert_eq!(val.get(), 65536); + } + + #[test] + fn set_get() { + let mut val = BinUsizeCount::::default(); + + let v = val.set("64k").unwrap(); + assert_eq!(v, 65536); + assert_eq!(val.get(), 65536); + } + + #[test] + #[should_panic(expected = "OutOfBounds(\"Must be 10 - 256K\")")] + fn oob() { + let mut val = BinUsizeCount::::default(); + + let _v = val.set("512k").unwrap(); + } + + #[cfg(feature = "rusqlite")] + fn memdb() -> Result { + 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(); + + let v = "64k".parse::().unwrap(); + conn + .execute("INSERT INTO tbl (id, txtval) VALUES (?, ?);", params![1, v]) + .unwrap(); + + let v = "256".parse::().unwrap(); + conn + .execute("INSERT INTO tbl (id, txtval) VALUES (?, ?);", params![2, v]) + .unwrap(); + + + let mut stmt = conn.prepare("SELECT txtval FROM tbl WHERE id=?;").unwrap(); + + let v: BinUsizeCount = stmt.query_one([1], |row| row.get(0)).unwrap(); + assert_eq!(v.get(), 65536); + + let v: BinUsizeCount = stmt.query_one([2], |row| row.get(0)).unwrap(); + assert_eq!(v.get(), 256); + } + + + #[cfg(feature = "rusqlite")] + #[test] + #[should_panic(expected = "FromSqlConversionFailure(0, Text, \ + Invalid(\"invalid digit found in string\"))")] + fn bad_query() { + let conn = memdb().unwrap(); + + conn + .execute( + "INSERT INTO tbl (id, txtval) VALUES (?, ?);", + params![1, "notanumber"] + ) + .unwrap(); + + let mut stmt = conn.prepare("SELECT txtval FROM tbl WHERE id=?;").unwrap(); + + let _v: BinUsizeCount = stmt.query_one([1], |row| row.get(0)).unwrap(); + } +} + +// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 : ADDED src/binusizesize.rs Index: src/binusizesize.rs ================================================================== --- /dev/null +++ src/binusizesize.rs @@ -0,0 +1,219 @@ +use std::{marker::PhantomData, str::FromStr}; + +#[cfg(feature = "rusqlite")] +use rusqlite::{ + ToSql, + types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, ValueRef} +}; + +use parse_size::ByteSuffix; + +use crate::{AnyUsize, Controller, StrVal, err::Error}; + + +/// A `usize` value that supports suffixes, like `1kb`, `2.5kb`, etc. +/// +/// This variant treats k as 1024. See [`DecUsizeSize`](super::DecUsizeSize) +/// for a decimal variant. +#[derive(Debug, Clone)] +pub struct BinUsizeSize { + sval: String, + val: usize, + _marker: PhantomData +} + +impl Default for BinUsizeSize { + fn default() -> Self { + Self { + sval: "0".into(), + val: 0, + _marker: PhantomData + } + } +} + +impl StrVal for BinUsizeSize +where + C: Controller +{ + type Type = usize; + + fn set(&mut self, sval: &str) -> Result { + let dv = sval.parse::()?; + + C::validate(&dv.val)?; + + self.sval = sval.to_string(); + self.val = dv.val; + Ok(dv.val) + } + + fn get(&self) -> Self::Type { + self.val + } + + fn val_str(&self) -> Option { + Some(self.val.to_string()) + } +} + +impl AsRef for BinUsizeSize { + fn as_ref(&self) -> &str { + &self.sval + } +} + +impl FromStr for BinUsizeSize { + type Err = Error; + + fn from_str(s: &str) -> Result { + let val = parse_size::Config::default() + .with_binary() + .with_byte_suffix(ByteSuffix::Require) + .parse_size(s) + .map_err(|e| Error::Invalid(e.to_string()))?; + let val = + usize::try_from(val).map_err(|e| Error::OutOfBounds(e.to_string()))?; + Ok(Self { + sval: s.to_string(), + val, + _marker: PhantomData + }) + } +} + +#[cfg(feature = "rusqlite")] +#[cfg_attr(docsrs, doc(cfg(feature = "rusqlite")))] +impl ToSql for BinUsizeSize { + fn to_sql(&self) -> Result, rusqlite::Error> { + self.sval.to_sql() + } +} + +#[cfg(feature = "rusqlite")] +#[cfg_attr(docsrs, doc(cfg(feature = "rusqlite")))] +impl FromSql for BinUsizeSize { + #[inline] + fn column_result(value: ValueRef<'_>) -> FromSqlResult { + let s = String::column_result(value)?; + s.parse::() + .map_err(|e| FromSqlError::Other(Box::new(e))) + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(feature = "rusqlite")] + use rusqlite::{Connection, params}; + + struct SomeUsizeBound {} + + impl Controller for SomeUsizeBound { + type Type = usize; + fn def() -> String { + String::from("10") + } + fn validate(val: &Self::Type) -> Result<(), Error> { + if *val < 10 || *val > 256 * 1024 { + Err(Error::OutOfBounds("Must be 10 - 256K".into())) + } else { + Ok(()) + } + } + } + + #[test] + fn default1() { + #[allow(clippy::default_trait_access)] + let mut val: BinUsizeSize = Default::default(); + + let v = val.set("64KB").unwrap(); + assert_eq!(v, 65536); + assert_eq!(val.get(), 65536); + } + + #[test] + fn default2() { + let mut val = ::default(); + + let v = val.set("64kb").unwrap(); + assert_eq!(v, 65536); + assert_eq!(val.get(), 65536); + } + + #[test] + fn set_get() { + let mut val = BinUsizeSize::::default(); + + let v = val.set("64kb").unwrap(); + assert_eq!(v, 65536); + assert_eq!(val.get(), 65536); + } + + #[test] + #[should_panic(expected = "OutOfBounds(\"Must be 10 - 256K\")")] + fn oob() { + let mut val = BinUsizeSize::::default(); + + let _v = val.set("512KB").unwrap(); + } + + + #[cfg(feature = "rusqlite")] + fn memdb() -> Result { + 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(); + + let v = "64kb".parse::().unwrap(); + conn + .execute("INSERT INTO tbl (id, txtval) VALUES (?, ?);", params![1, v]) + .unwrap(); + + let v = "256b".parse::().unwrap(); + conn + .execute("INSERT INTO tbl (id, txtval) VALUES (?, ?);", params![2, v]) + .unwrap(); + + + let mut stmt = conn.prepare("SELECT txtval FROM tbl WHERE id=?;").unwrap(); + + let v: BinUsizeSize = stmt.query_one([1], |row| row.get(0)).unwrap(); + assert_eq!(v.get(), 65536); + + let v: BinUsizeSize = stmt.query_one([2], |row| row.get(0)).unwrap(); + assert_eq!(v.get(), 256); + } + + #[cfg(feature = "rusqlite")] + #[test] + #[should_panic(expected = "FromSqlConversionFailure(0, Text, \ + Invalid(\"invalid digit found in string\"))")] + fn bad_query() { + let conn = memdb().unwrap(); + + conn + .execute( + "INSERT INTO tbl (id, txtval) VALUES (?, ?);", + params![1, "notanumber"] + ) + .unwrap(); + + let mut stmt = conn.prepare("SELECT txtval FROM tbl WHERE id=?;").unwrap(); + + let _v: BinUsizeSize = stmt.query_one([1], |row| row.get(0)).unwrap(); + } +} + +// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 : ADDED src/decusizecount.rs Index: src/decusizecount.rs ================================================================== --- /dev/null +++ src/decusizecount.rs @@ -0,0 +1,219 @@ +use std::{marker::PhantomData, str::FromStr}; + +#[cfg(feature = "rusqlite")] +use rusqlite::{ + ToSql, + types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, ValueRef} +}; + +use parse_size::ByteSuffix; + +use crate::{AnyUsize, Controller, StrVal, err::Error}; + + +/// A `usize` value that supports suffixes, like `1k`, `2.5k`, etc. +/// +/// This variant treats k as 1000. See [`BinUsizeCount`](super::BinUsizeCount) +/// for a binary variant. +#[derive(Debug, Clone)] +pub struct DecUsizeCount { + sval: String, + val: usize, + _marker: PhantomData +} + +impl Default for DecUsizeCount { + fn default() -> Self { + Self { + sval: "0".into(), + val: 0, + _marker: PhantomData + } + } +} + +impl StrVal for DecUsizeCount +where + C: Controller +{ + type Type = usize; + + fn set(&mut self, sval: &str) -> Result { + let dv = sval.parse::()?; + + C::validate(&dv.val)?; + + self.sval = sval.to_string(); + self.val = dv.val; + Ok(dv.val) + } + + fn get(&self) -> Self::Type { + self.val + } + + fn val_str(&self) -> Option { + Some(self.val.to_string()) + } +} + +impl AsRef for DecUsizeCount { + fn as_ref(&self) -> &str { + &self.sval + } +} + +impl FromStr for DecUsizeCount { + type Err = Error; + + fn from_str(s: &str) -> Result { + let val = parse_size::Config::default() + .with_decimal() + .with_byte_suffix(ByteSuffix::Deny) + .parse_size(s) + .map_err(|e| Error::Invalid(e.to_string()))?; + let val = + usize::try_from(val).map_err(|e| Error::OutOfBounds(e.to_string()))?; + Ok(Self { + sval: s.to_string(), + val, + _marker: PhantomData + }) + } +} + +#[cfg(feature = "rusqlite")] +#[cfg_attr(docsrs, doc(cfg(feature = "rusqlite")))] +impl ToSql for DecUsizeCount { + fn to_sql(&self) -> Result, rusqlite::Error> { + self.sval.to_sql() + } +} + +#[cfg(feature = "rusqlite")] +#[cfg_attr(docsrs, doc(cfg(feature = "rusqlite")))] +impl FromSql for DecUsizeCount { + #[inline] + fn column_result(value: ValueRef<'_>) -> FromSqlResult { + let s = String::column_result(value)?; + s.parse::() + .map_err(|e| FromSqlError::Other(Box::new(e))) + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(feature = "rusqlite")] + use rusqlite::{Connection, params}; + + struct SomeUsizeBound {} + + impl Controller for SomeUsizeBound { + type Type = usize; + fn def() -> String { + String::from("10") + } + fn validate(val: &Self::Type) -> Result<(), Error> { + if *val < 10 || *val > 256 * 1000 { + Err(Error::OutOfBounds("Must be 10 - 256K".into())) + } else { + Ok(()) + } + } + } + + #[test] + fn default1() { + #[allow(clippy::default_trait_access)] + let mut val: DecUsizeCount = Default::default(); + + let v = val.set("64k").unwrap(); + assert_eq!(v, 64_000); + assert_eq!(val.get(), 64_000); + } + + #[test] + fn default2() { + let mut val = ::default(); + + let v = val.set("64k").unwrap(); + assert_eq!(v, 64_000); + assert_eq!(val.get(), 64_000); + } + + #[test] + fn set_get() { + let mut val = DecUsizeCount::::default(); + + let v = val.set("64k").unwrap(); + assert_eq!(v, 64_000); + assert_eq!(val.get(), 64_000); + } + + #[test] + #[should_panic(expected = "OutOfBounds(\"Must be 10 - 256K\")")] + fn oob() { + let mut val = DecUsizeCount::::default(); + + let _v = val.set("512k").unwrap(); + } + + #[cfg(feature = "rusqlite")] + fn memdb() -> Result { + 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(); + + let v = "64k".parse::().unwrap(); + conn + .execute("INSERT INTO tbl (id, txtval) VALUES (?, ?);", params![1, v]) + .unwrap(); + + let v = "256".parse::().unwrap(); + conn + .execute("INSERT INTO tbl (id, txtval) VALUES (?, ?);", params![2, v]) + .unwrap(); + + + let mut stmt = conn.prepare("SELECT txtval FROM tbl WHERE id=?;").unwrap(); + + let v: DecUsizeCount = stmt.query_one([1], |row| row.get(0)).unwrap(); + assert_eq!(v.get(), 64_000); + + let v: DecUsizeCount = stmt.query_one([2], |row| row.get(0)).unwrap(); + assert_eq!(v.get(), 256); + } + + + #[cfg(feature = "rusqlite")] + #[test] + #[should_panic(expected = "FromSqlConversionFailure(0, Text, \ + Invalid(\"invalid digit found in string\"))")] + fn bad_query() { + let conn = memdb().unwrap(); + + conn + .execute( + "INSERT INTO tbl (id, txtval) VALUES (?, ?);", + params![1, "notanumber"] + ) + .unwrap(); + + let mut stmt = conn.prepare("SELECT txtval FROM tbl WHERE id=?;").unwrap(); + + let _v: DecUsizeCount = stmt.query_one([1], |row| row.get(0)).unwrap(); + } +} + +// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 : ADDED src/decusizesize.rs Index: src/decusizesize.rs ================================================================== --- /dev/null +++ src/decusizesize.rs @@ -0,0 +1,215 @@ +use std::{marker::PhantomData, str::FromStr}; + +use parse_size::ByteSuffix; + +#[cfg(feature = "rusqlite")] +use rusqlite::{ + ToSql, + types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, ValueRef} +}; + +use crate::{AnyUsize, Controller, StrVal, err::Error}; + +/// A `usize` value that supports suffixes, like `1kb`, `2.5kb`, etc. +/// +/// This variant treats k as 1000. See [`BinUsizeize`](super::BinUsizeSize) +/// for a binary variant. +#[derive(Debug, Clone)] +pub struct DecUsizeSize { + sval: String, + val: usize, + _marker: PhantomData +} + +impl Default for DecUsizeSize { + fn default() -> Self { + Self { + sval: "0".into(), + val: 0, + _marker: PhantomData + } + } +} + +impl StrVal for DecUsizeSize +where + C: Controller +{ + type Type = usize; + + fn set(&mut self, sval: &str) -> Result { + let dv = sval.parse::()?; + + C::validate(&dv.val)?; + + self.sval = sval.to_string(); + self.val = dv.val; + Ok(dv.val) + } + + fn get(&self) -> Self::Type { + self.val + } + + fn val_str(&self) -> Option { + Some(self.val.to_string()) + } +} + +impl AsRef for DecUsizeSize { + fn as_ref(&self) -> &str { + &self.sval + } +} + +impl FromStr for DecUsizeSize { + type Err = Error; + + fn from_str(s: &str) -> Result { + let val = parse_size::Config::default() + .with_decimal() + .with_byte_suffix(ByteSuffix::Require) + .parse_size(s) + .map_err(|e| Error::Invalid(e.to_string()))?; + let val = + usize::try_from(val).map_err(|e| Error::OutOfBounds(e.to_string()))?; + Ok(Self { + sval: s.to_string(), + val, + _marker: PhantomData + }) + } +} + +#[cfg(feature = "rusqlite")] +#[cfg_attr(docsrs, doc(cfg(feature = "rusqlite")))] +impl ToSql for DecUsizeSize { + fn to_sql(&self) -> Result, rusqlite::Error> { + self.sval.to_sql() + } +} + +#[cfg(feature = "rusqlite")] +#[cfg_attr(docsrs, doc(cfg(feature = "rusqlite")))] +impl FromSql for DecUsizeSize { + #[inline] + fn column_result(value: ValueRef<'_>) -> FromSqlResult { + let s = String::column_result(value)?; + s.parse::() + .map_err(|e| FromSqlError::Other(Box::new(e))) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(feature = "rusqlite")] + use rusqlite::{Connection, params}; + + struct SomeUsizeBound {} + + impl Controller for SomeUsizeBound { + type Type = usize; + fn def() -> String { + String::from("10") + } + fn validate(val: &Self::Type) -> Result<(), Error> { + if *val < 10 || *val > 256 * 1000 { + Err(Error::OutOfBounds("Must be 10 - 256K".into())) + } else { + Ok(()) + } + } + } + + #[test] + fn default1() { + #[allow(clippy::default_trait_access)] + let mut val: DecUsizeSize = Default::default(); + + let v = val.set("64kb").unwrap(); + assert_eq!(v, 64000); + assert_eq!(val.get(), 64000); + } + + #[test] + fn default2() { + let mut val = ::default(); + + let v = val.set("64kb").unwrap(); + assert_eq!(v, 64000); + assert_eq!(val.get(), 64000); + } + + #[test] + fn set_get() { + let mut val = DecUsizeSize::::default(); + + let v = val.set("64kb").unwrap(); + assert_eq!(v, 64000); + assert_eq!(val.get(), 64000); + } + + #[test] + #[should_panic(expected = "OutOfBounds(\"Must be 10 - 256K\")")] + fn oob() { + let mut val = DecUsizeSize::::default(); + + let _v = val.set("512kb").unwrap(); + } + + #[cfg(feature = "rusqlite")] + fn memdb() -> Result { + 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(); + + let v = "10kb".parse::().unwrap(); + conn + .execute("INSERT INTO tbl (id, txtval) VALUES (?, ?);", params![1, v]) + .unwrap(); + + let v = "256b".parse::().unwrap(); + conn + .execute("INSERT INTO tbl (id, txtval) VALUES (?, ?);", params![2, v]) + .unwrap(); + + let mut stmt = conn.prepare("SELECT txtval FROM tbl WHERE id=?;").unwrap(); + + let v: DecUsizeSize = stmt.query_one([1], |row| row.get(0)).unwrap(); + assert_eq!(v.get(), 10_000); + + let v: DecUsizeSize = stmt.query_one([2], |row| row.get(0)).unwrap(); + assert_eq!(v.get(), 256); + } + + #[cfg(feature = "rusqlite")] + #[test] + #[should_panic(expected = "FromSqlConversionFailure(0, Text, \ + Invalid(\"invalid digit found in string\"))")] + fn bad_query() { + let conn = memdb().unwrap(); + + conn + .execute( + "INSERT INTO tbl (id, txtval) VALUES (?, ?);", + params![1, "notanumber"] + ) + .unwrap(); + + let mut stmt = conn.prepare("SELECT txtval FROM tbl WHERE id=?;").unwrap(); + + let _v: DecUsizeSize = stmt.query_one([1], |row| row.get(0)).unwrap(); + } +} + +// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 : Index: src/lib.rs ================================================================== --- src/lib.rs +++ src/lib.rs @@ -39,13 +39,17 @@ #![cfg_attr(docsrs, feature(doc_cfg))] mod binu64count; mod binu64size; +mod binusizecount; +mod binusizesize; mod boolean; mod decu64count; mod decu64size; +mod decusizecount; +mod decusizesize; mod dur; mod err; mod percent; mod relabslim; mod select; @@ -52,13 +56,16 @@ mod str; use std::str::FromStr; pub use { - binu64count::BinU64Count, binu64size::BinU64Size, boolean::Bool, - decu64count::DecU64Count, decu64size::DecU64Size, dur::Dur, err::Error, - percent::Percent, relabslim::RelAbsLim, select::Select, str::Str + binu64count::BinU64Count, binu64size::BinU64Size, + binusizecount::BinUsizeCount, binusizesize::BinUsizeSize, boolean::Bool, + decu64count::DecU64Count, decu64size::DecU64Size, + decusizecount::DecUsizeCount, decusizesize::DecUsizeSize, dur::Dur, + err::Error, percent::Percent, relabslim::RelAbsLim, select::Select, + str::Str }; /// Traits for values that can be set from strings. pub trait StrVal: AsRef + FromStr { type Type; @@ -83,16 +90,34 @@ } /// Accept any `u64`. pub struct AnyU64; + impl Controller for AnyU64 { type Type = u64; + + fn def() -> String { + String::from("0") + } + + fn validate(_val: &Self::Type) -> Result<(), Error> { + Ok(()) + } +} + +/// Accept any `usize`. +pub struct AnyUsize; + +impl Controller for AnyUsize { + type Type = usize; + fn def() -> String { String::from("0") } + fn validate(_val: &Self::Type) -> Result<(), Error> { Ok(()) } } // vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 : Index: www/changelog.md ================================================================== --- www/changelog.md +++ www/changelog.md @@ -10,10 +10,20 @@ ### Changed ### Removed +--- + +## [0.1.1] - 2025-07-09 + +[Details](/vdiff?from=strval-0.1.0&to=trunk) + +### Added + +- `BinUsizeCount`, `BinUsizeSize`, `DecUsizeCount`, `DecUsizeSize` + --- ## [0.1.0] - 2025-07-09 Initial release