Index: Cargo.toml ================================================================== --- Cargo.toml +++ Cargo.toml @@ -1,8 +1,8 @@ [package] name = "strval" -version = "0.3.1" +version = "0.3.2" edition = "2024" license = "0BSD" # https://crates.io/category_slugs categories = ["text-processing"] keywords = ["parsing", "string", "rusqlite"] Index: src/binu64count.rs ================================================================== --- src/binu64count.rs +++ src/binu64count.rs @@ -73,19 +73,23 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.sval) } } -impl FromStr for BinU64Count { +impl FromStr for BinU64Count +where + C: Controller +{ 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()))?; + C::validate(&val)?; Ok(Self { sval: s.to_string(), val, _marker: PhantomData }) @@ -92,19 +96,22 @@ } } #[cfg(feature = "rusqlite")] #[cfg_attr(docsrs, doc(cfg(feature = "rusqlite")))] -impl ToSql for BinU64Count { +impl ToSql for BinU64Count { fn to_sql(&self) -> Result, rusqlite::Error> { self.sval.to_sql() } } #[cfg(feature = "rusqlite")] #[cfg_attr(docsrs, doc(cfg(feature = "rusqlite")))] -impl FromSql for BinU64Count { +impl FromSql for BinU64Count +where + C: Controller +{ #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult { let s = String::column_result(value)?; s.parse::() .map_err(|e| FromSqlError::Other(Box::new(e))) @@ -168,10 +175,16 @@ fn oob() { let mut val = BinU64Count::::default(); let _v = val.set("512k").unwrap(); } + + #[test] + #[should_panic(expected = "OutOfBounds(\"Must be 10 - 256K\")")] + fn oob_parsed() { + let _v = "512k".parse::>().unwrap(); + } #[cfg(feature = "rusqlite")] fn memdb() -> Result { let conn = Connection::open_in_memory()?; conn.execute_batch( Index: src/binu64size.rs ================================================================== --- src/binu64size.rs +++ src/binu64size.rs @@ -73,19 +73,23 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.sval) } } -impl FromStr for BinU64Size { +impl FromStr for BinU64Size +where + C: Controller +{ 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()))?; + C::validate(&val)?; Ok(Self { sval: s.to_string(), val, _marker: PhantomData }) @@ -92,19 +96,22 @@ } } #[cfg(feature = "rusqlite")] #[cfg_attr(docsrs, doc(cfg(feature = "rusqlite")))] -impl ToSql for BinU64Size { +impl ToSql for BinU64Size { fn to_sql(&self) -> Result, rusqlite::Error> { self.sval.to_sql() } } #[cfg(feature = "rusqlite")] #[cfg_attr(docsrs, doc(cfg(feature = "rusqlite")))] -impl FromSql for BinU64Size { +impl FromSql for BinU64Size +where + C: Controller +{ #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult { let s = String::column_result(value)?; s.parse::() .map_err(|e| FromSqlError::Other(Box::new(e))) @@ -169,10 +176,15 @@ let mut val = BinU64Size::::default(); let _v = val.set("512KB").unwrap(); } + #[test] + #[should_panic(expected = "OutOfBounds(\"Must be 10 - 256K\")")] + fn oob_parsed() { + let _val = "512KB".parse::>().unwrap(); + } #[cfg(feature = "rusqlite")] fn memdb() -> Result { let conn = Connection::open_in_memory()?; conn.execute_batch( Index: src/binusizecount.rs ================================================================== --- src/binusizecount.rs +++ src/binusizecount.rs @@ -73,11 +73,14 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.sval) } } -impl FromStr for BinUsizeCount { +impl FromStr for BinUsizeCount +where + C: Controller +{ type Err = Error; fn from_str(s: &str) -> Result { let val = parse_size::Config::default() .with_binary() @@ -84,10 +87,11 @@ .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()))?; + C::validate(&val)?; Ok(Self { sval: s.to_string(), val, _marker: PhantomData }) @@ -94,19 +98,22 @@ } } #[cfg(feature = "rusqlite")] #[cfg_attr(docsrs, doc(cfg(feature = "rusqlite")))] -impl ToSql for BinUsizeCount { +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 { +impl FromSql for BinUsizeCount +where + C: Controller +{ #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult { let s = String::column_result(value)?; s.parse::() .map_err(|e| FromSqlError::Other(Box::new(e))) @@ -170,10 +177,16 @@ fn oob() { let mut val = BinUsizeCount::::default(); let _v = val.set("512k").unwrap(); } + + #[test] + #[should_panic(expected = "OutOfBounds(\"Must be 10 - 256K\")")] + fn oob_parsed() { + let _val = "512k".parse::>().unwrap(); + } #[cfg(feature = "rusqlite")] fn memdb() -> Result { let conn = Connection::open_in_memory()?; conn.execute_batch( Index: src/binusizesize.rs ================================================================== --- src/binusizesize.rs +++ src/binusizesize.rs @@ -73,11 +73,14 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.sval) } } -impl FromStr for BinUsizeSize { +impl FromStr for BinUsizeSize +where + C: Controller +{ type Err = Error; fn from_str(s: &str) -> Result { let val = parse_size::Config::default() .with_binary() @@ -84,10 +87,11 @@ .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()))?; + C::validate(&val)?; Ok(Self { sval: s.to_string(), val, _marker: PhantomData }) @@ -94,19 +98,22 @@ } } #[cfg(feature = "rusqlite")] #[cfg_attr(docsrs, doc(cfg(feature = "rusqlite")))] -impl ToSql for BinUsizeSize { +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 { +impl FromSql for BinUsizeSize +where + C: Controller +{ #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult { let s = String::column_result(value)?; s.parse::() .map_err(|e| FromSqlError::Other(Box::new(e))) @@ -171,10 +178,15 @@ let mut val = BinUsizeSize::::default(); let _v = val.set("512KB").unwrap(); } + #[test] + #[should_panic(expected = "OutOfBounds(\"Must be 10 - 256K\")")] + fn oob_parsed() { + let _val = "512KB".parse::>().unwrap(); + } #[cfg(feature = "rusqlite")] fn memdb() -> Result { let conn = Connection::open_in_memory()?; conn.execute_batch( Index: src/decu64count.rs ================================================================== --- src/decu64count.rs +++ src/decu64count.rs @@ -73,19 +73,23 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.sval) } } -impl FromStr for DecU64Count { +impl FromStr for DecU64Count +where + C: Controller +{ 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()))?; + C::validate(&val)?; Ok(Self { sval: s.to_string(), val, _marker: PhantomData }) @@ -100,11 +104,14 @@ } } #[cfg(feature = "rusqlite")] #[cfg_attr(docsrs, doc(cfg(feature = "rusqlite")))] -impl FromSql for DecU64Count { +impl FromSql for DecU64Count +where + C: Controller +{ #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult { let s = String::column_result(value)?; s.parse::() .map_err(|e| FromSqlError::Other(Box::new(e))) @@ -167,10 +174,16 @@ fn oob() { let mut val = DecU64Count::::default(); let _v = val.set("512k").unwrap(); } + + #[test] + #[should_panic(expected = "OutOfBounds(\"Must be 10 - 256K\")")] + fn oob_parsed() { + let _val = "512k".parse::>().unwrap(); + } #[cfg(feature = "rusqlite")] fn memdb() -> Result { let conn = Connection::open_in_memory()?; conn.execute_batch( Index: src/decu64size.rs ================================================================== --- src/decu64size.rs +++ src/decu64size.rs @@ -72,19 +72,23 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.sval) } } -impl FromStr for DecU64Size { +impl FromStr for DecU64Size +where + C: Controller +{ 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()))?; + C::validate(&val)?; Ok(Self { sval: s.to_string(), val, _marker: PhantomData }) @@ -99,11 +103,14 @@ } } #[cfg(feature = "rusqlite")] #[cfg_attr(docsrs, doc(cfg(feature = "rusqlite")))] -impl FromSql for DecU64Size { +impl FromSql for DecU64Size +where + C: Controller +{ #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult { let s = String::column_result(value)?; s.parse::() .map_err(|e| FromSqlError::Other(Box::new(e))) @@ -166,10 +173,16 @@ fn oob() { let mut val = DecU64Size::::default(); let _v = val.set("512kb").unwrap(); } + + #[test] + #[should_panic(expected = "OutOfBounds(\"Must be 10 - 256K\")")] + fn oob_parsed() { + let _val = "512kb".parse::>().unwrap(); + } #[cfg(feature = "rusqlite")] fn memdb() -> Result { let conn = Connection::open_in_memory()?; conn.execute_batch( Index: src/decusizecount.rs ================================================================== --- src/decusizecount.rs +++ src/decusizecount.rs @@ -73,11 +73,14 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.sval) } } -impl FromStr for DecUsizeCount { +impl FromStr for DecUsizeCount +where + C: Controller +{ type Err = Error; fn from_str(s: &str) -> Result { let val = parse_size::Config::default() .with_decimal() @@ -84,10 +87,11 @@ .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()))?; + C::validate(&val)?; Ok(Self { sval: s.to_string(), val, _marker: PhantomData }) @@ -94,19 +98,22 @@ } } #[cfg(feature = "rusqlite")] #[cfg_attr(docsrs, doc(cfg(feature = "rusqlite")))] -impl ToSql for DecUsizeCount { +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 { +impl FromSql for DecUsizeCount +where + C: Controller +{ #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult { let s = String::column_result(value)?; s.parse::() .map_err(|e| FromSqlError::Other(Box::new(e))) @@ -170,10 +177,16 @@ fn oob() { let mut val = DecUsizeCount::::default(); let _v = val.set("512k").unwrap(); } + + #[test] + #[should_panic(expected = "OutOfBounds(\"Must be 10 - 256K\")")] + fn oob_parsed() { + let _val = "512k".parse::>().unwrap(); + } #[cfg(feature = "rusqlite")] fn memdb() -> Result { let conn = Connection::open_in_memory()?; conn.execute_batch( Index: src/decusizesize.rs ================================================================== --- src/decusizesize.rs +++ src/decusizesize.rs @@ -72,11 +72,14 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.sval) } } -impl FromStr for DecUsizeSize { +impl FromStr for DecUsizeSize +where + C: Controller +{ type Err = Error; fn from_str(s: &str) -> Result { let val = parse_size::Config::default() .with_decimal() @@ -83,10 +86,11 @@ .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()))?; + C::validate(&val)?; Ok(Self { sval: s.to_string(), val, _marker: PhantomData }) @@ -101,11 +105,14 @@ } } #[cfg(feature = "rusqlite")] #[cfg_attr(docsrs, doc(cfg(feature = "rusqlite")))] -impl FromSql for DecUsizeSize { +impl FromSql for DecUsizeSize +where + C: Controller +{ #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult { let s = String::column_result(value)?; s.parse::() .map_err(|e| FromSqlError::Other(Box::new(e))) @@ -168,10 +175,16 @@ fn oob() { let mut val = DecUsizeSize::::default(); let _v = val.set("512kb").unwrap(); } + + #[test] + #[should_panic(expected = "OutOfBounds(\"Must be 10 - 256K\")")] + fn oob_parsed() { + let _val = "512kb".parse::>().unwrap(); + } #[cfg(feature = "rusqlite")] fn memdb() -> Result { let conn = Connection::open_in_memory()?; conn.execute_batch( Index: src/dur.rs ================================================================== --- src/dur.rs +++ src/dur.rs @@ -78,18 +78,22 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.sval) } } -impl FromStr for Dur { +impl FromStr for Dur +where + C: Controller +{ type Err = Error; fn from_str(s: &str) -> Result { let dur = s .parse::() .map_err(|e| Error::Invalid(e.to_string()))?; let dur = dur.unsigned_abs(); + C::validate(&dur)?; Ok(Self { sval: s.to_string(), val: dur, _marker: PhantomData }) @@ -104,11 +108,14 @@ } } #[cfg(feature = "rusqlite")] #[cfg_attr(docsrs, doc(cfg(feature = "rusqlite")))] -impl FromSql for Dur { +impl FromSql for Dur +where + C: Controller +{ #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult { let s = String::column_result(value)?; s.parse::() .map_err(|e| FromSqlError::Other(Box::new(e))) @@ -176,10 +183,15 @@ let mut val = Dur::::default(); let _v = val.set("100ms").unwrap(); } + #[test] + #[should_panic(expected = "OutOfBounds(\"1s..60s\")")] + fn oob_parsed() { + let _val = "1ms".parse::>().unwrap(); + } #[cfg(feature = "rusqlite")] fn memdb() -> Result { let conn = Connection::open_in_memory()?; conn.execute_batch( Index: src/relabslim.rs ================================================================== --- src/relabslim.rs +++ src/relabslim.rs @@ -109,30 +109,37 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.sval) } } -impl FromStr for RelAbsLim { +impl FromStr for RelAbsLim +where + C: Controller +{ type Err = Error; fn from_str(s: &str) -> Result { if let Some(s) = s.strip_suffix("%") { let f = s.parse::().unwrap(); let f = f / 100.0; + let val = Lim::Rel(f); + C::validate(&val)?; Ok(Self { sval: s.to_string(), - val: Lim::Rel(f), + val, _marker: PhantomData }) } else { let val = parse_size::Config::default() .with_binary() .parse_size(s) .map_err(|e| Error::Invalid(e.to_string()))?; + let val = Lim::Abs(val); + C::validate(&val)?; Ok(Self { sval: s.to_string(), - val: Lim::Abs(val), + val, _marker: PhantomData }) } } } @@ -146,11 +153,14 @@ } } #[cfg(feature = "rusqlite")] #[cfg_attr(docsrs, doc(cfg(feature = "rusqlite")))] -impl FromSql for RelAbsLim { +impl FromSql for RelAbsLim +where + C: Controller +{ #[inline] fn column_result(value: ValueRef<'_>) -> FromSqlResult { let s = String::column_result(value)?; s.parse::() .map_err(|e| FromSqlError::Other(Box::new(e))) Index: www/changelog.md ================================================================== --- www/changelog.md +++ www/changelog.md @@ -2,18 +2,28 @@ ⚠️ indicates a breaking change. ## [Unreleased] -[Details](/vdiff?from=strval-0.3.1&to=trunk) +[Details](/vdiff?from=strval-0.3.2&to=trunk) ### Added ### Changed ### Removed +--- + +## [0.3.2] - 2025-08-03 + +[Details](/vdiff?from=strval-0.3.1&to=strval-0.3.2) + +### Changed + +- Bugfix: Add missing parse validation to some `FromStr` implementations. + --- ## [0.3.1] - 2025-07-30 [Details](/vdiff?from=strval-0.3.0&to=strval-0.3.1)