@@ -4,6 +4,7 @@ use thiserror::Error;
44
55/// Error type for `rust_template` operations.
66#[ derive( Error , Debug ) ]
7+ #[ non_exhaustive]
78pub enum Error {
89 /// Invalid input was provided.
910 #[ error( "invalid input: {0}" ) ]
@@ -55,11 +56,12 @@ pub const fn add(a: i64, b: i64) -> i64 {
5556///
5657/// # Returns
5758///
58- /// The quotient, or an error if `divisor` is zero.
59+ /// The quotient, or an error if `divisor` is zero or the operation overflows .
5960///
6061/// # Errors
6162///
6263/// Returns [`Error::InvalidInput`] if `divisor` is zero.
64+ /// Returns [`Error::OperationFailed`] if the division overflows.
6365///
6466/// # Examples
6567///
@@ -73,18 +75,60 @@ pub fn divide(dividend: i64, divisor: i64) -> Result<i64> {
7375 if divisor == 0 {
7476 return Err ( Error :: InvalidInput ( "divisor cannot be zero" . to_string ( ) ) ) ;
7577 }
76- Ok ( dividend / divisor)
78+
79+ dividend. checked_div ( divisor) . ok_or_else ( || Error :: OperationFailed {
80+ operation : "divide" . to_string ( ) ,
81+ cause : "overflow when dividing i64 values" . to_string ( ) ,
82+ } )
83+ }
84+
85+ /// Parses `input` as a non-negative integer and returns it.
86+ ///
87+ /// # Arguments
88+ ///
89+ /// * `input` - A string slice to parse as an `i64`.
90+ ///
91+ /// # Returns
92+ ///
93+ /// The parsed non-negative integer value on success.
94+ ///
95+ /// # Errors
96+ ///
97+ /// - Returns [`Error::InvalidInput`] if `input` cannot be parsed as an `i64`.
98+ /// - Returns [`Error::OperationFailed`] if the parsed value is negative.
99+ ///
100+ /// # Examples
101+ ///
102+ /// ```rust
103+ /// use rust_template::process;
104+ ///
105+ /// assert_eq!(process("42").unwrap(), 42);
106+ /// assert_eq!(process("0").unwrap(), 0);
107+ /// assert!(process("abc").is_err());
108+ /// assert!(process("-1").is_err());
109+ /// ```
110+ pub fn process ( input : & str ) -> Result < i64 > {
111+ let value = input
112+ . parse :: < i64 > ( )
113+ . map_err ( |e| Error :: InvalidInput ( format ! ( "not a valid integer: {e}" ) ) ) ?;
114+
115+ if value < 0 {
116+ return Err ( Error :: OperationFailed {
117+ operation : "process" . to_string ( ) ,
118+ cause : format ! ( "value {value} is negative" ) ,
119+ } ) ;
120+ }
121+
122+ Ok ( value)
77123}
78124
79125/// Configuration for the crate.
80126#[ derive( Debug , Clone ) ]
127+ #[ non_exhaustive]
81128pub struct Config {
82- /// Enable verbose logging.
83- pub verbose : bool ,
84- /// Maximum number of retries.
85- pub max_retries : u32 ,
86- /// Timeout in seconds.
87- pub timeout_secs : u64 ,
129+ verbose : bool ,
130+ max_retries : u32 ,
131+ timeout_secs : u64 ,
88132}
89133
90134impl Default for Config {
@@ -104,6 +148,24 @@ impl Config {
104148 }
105149 }
106150
151+ /// Returns whether verbose logging is enabled.
152+ #[ must_use]
153+ pub const fn verbose ( & self ) -> bool {
154+ self . verbose
155+ }
156+
157+ /// Returns the maximum number of retries.
158+ #[ must_use]
159+ pub const fn max_retries ( & self ) -> u32 {
160+ self . max_retries
161+ }
162+
163+ /// Returns the timeout in seconds.
164+ #[ must_use]
165+ pub const fn timeout_secs ( & self ) -> u64 {
166+ self . timeout_secs
167+ }
168+
107169 /// Sets the verbose flag.
108170 #[ must_use]
109171 pub const fn with_verbose ( mut self , verbose : bool ) -> Self {
@@ -148,7 +210,6 @@ mod tests {
148210 #[ test]
149211 fn test_divide_by_zero ( ) {
150212 let result = divide ( 10 , 0 ) ;
151- assert ! ( result. is_err( ) ) ;
152213 assert ! ( matches!( result, Err ( Error :: InvalidInput ( ref msg) ) if msg. contains( "zero" ) ) ) ;
153214 }
154215
@@ -159,17 +220,39 @@ mod tests {
159220 . with_max_retries ( 5 )
160221 . with_timeout ( 60 ) ;
161222
162- assert ! ( config. verbose) ;
163- assert_eq ! ( config. max_retries, 5 ) ;
164- assert_eq ! ( config. timeout_secs, 60 ) ;
223+ assert ! ( config. verbose( ) ) ;
224+ assert_eq ! ( config. max_retries( ) , 5 ) ;
225+ assert_eq ! ( config. timeout_secs( ) , 60 ) ;
165226 }
166227
167228 #[ test]
168229 fn test_config_default ( ) {
169230 let config = Config :: default ( ) ;
170- assert ! ( !config. verbose) ;
171- assert_eq ! ( config. max_retries, 3 ) ;
172- assert_eq ! ( config. timeout_secs, 30 ) ;
231+ assert ! ( !config. verbose( ) ) ;
232+ assert_eq ! ( config. max_retries( ) , 3 ) ;
233+ assert_eq ! ( config. timeout_secs( ) , 30 ) ;
234+ }
235+
236+ #[ test]
237+ fn test_process_valid ( ) {
238+ assert_eq ! ( process( "42" ) . unwrap( ) , 42 ) ;
239+ assert_eq ! ( process( "0" ) . unwrap( ) , 0 ) ;
240+ assert_eq ! ( process( "100" ) . unwrap( ) , 100 ) ;
241+ }
242+
243+ #[ test]
244+ fn test_process_invalid_input ( ) {
245+ let result = process ( "abc" ) ;
246+ assert ! ( matches!( result, Err ( Error :: InvalidInput ( _) ) ) ) ;
247+ }
248+
249+ #[ test]
250+ fn test_process_negative ( ) {
251+ let result = process ( "-1" ) ;
252+ assert ! ( matches!(
253+ result,
254+ Err ( Error :: OperationFailed { ref operation, .. } ) if operation == "process"
255+ ) ) ;
173256 }
174257
175258 #[ test]
0 commit comments