try_create/
lib.rs

1#![deny(rustdoc::broken_intra_doc_links)]
2
3
4// Re-export IntoInner for convenience, as it's a supertrait of TryNew.
5pub use into_inner::IntoInner;
6
7/// A trait for creating new instances of a type with fallible validation.
8///
9/// This trait provides a standardized way to create new instances of a type from an
10/// "inner" value, where the construction process might fail (e.g., due to validation rules).
11/// Implementors must also implement [`IntoInner`], which defines the `InnerType` used
12/// for construction.
13///
14/// # Associated Types
15///
16/// - `InnerType`: (From the [`IntoInner`] supertrait) The type of the input value used to
17///   attempt creation of a new instance of `Self`.
18/// - `Error`: The error type that is returned if `try_new` fails. This error type
19///   must implement `std::error::Error` if the `std` feature is enabled.
20///   If the `std` feature is not enabled, it must implement `core::fmt::Debug`.
21///
22/// # Examples
23///
24/// ```rust
25/// use try_create::{TryNew, IntoInner};
26/// # #[cfg(feature = "std")]
27/// # use std::error::Error;
28/// # #[cfg(feature = "std")]
29/// # use core::fmt;
30///
31/// // Define a custom error type
32/// #[derive(Debug, PartialEq)]
33/// struct NotPositiveError;
34///
35/// // Implement Display and Error for the custom error (required if using std::error::Error)
36/// # #[cfg(feature = "std")]
37/// impl fmt::Display for NotPositiveError {
38///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39///         write!(f, "Value must be positive")
40///     }
41/// }
42/// # #[cfg(feature = "std")]
43/// impl Error for NotPositiveError {}
44/// # #[cfg(not(feature = "std"))]
45/// # trait Error: core::fmt::Debug {} // Minimal Error trait for no_std
46/// # #[cfg(not(feature = "std"))]
47/// # impl Error for NotPositiveError {}
48///
49///
50/// // A struct that wraps an i32, ensuring it's positive.
51/// #[derive(Debug, PartialEq)]
52/// struct PositiveInteger {
53///     value: i32,
54/// }
55///
56/// // Implement IntoInner to define what `InnerType` is.
57/// impl IntoInner for PositiveInteger {
58///     type InnerType = i32;
59///
60///     fn into_inner(self) -> Self::InnerType {
61///         self.value
62///     }
63/// }
64///
65/// // Implement TryNew for PositiveInteger.
66/// impl TryNew for PositiveInteger {
67///     type Error = NotPositiveError;
68///     // `InnerType` is `i32`, inherited from `IntoInner`.
69///
70///     fn try_new(value: Self::InnerType) -> Result<Self, Self::Error> {
71///         if value > 0 {
72///             Ok(PositiveInteger { value })
73///         } else {
74///             Err(NotPositiveError)
75///         }
76///     }
77/// }
78///
79/// // Usage
80/// assert_eq!(PositiveInteger::try_new(10), Ok(PositiveInteger { value: 10 }));
81/// assert_eq!(PositiveInteger::try_new(0), Err(NotPositiveError));
82/// assert_eq!(PositiveInteger::try_new(-5), Err(NotPositiveError));
83///
84/// let positive_num = PositiveInteger::try_new(42).unwrap();
85/// assert_eq!(positive_num.into_inner(), 42);
86/// ```
87pub trait TryNew: Sized + IntoInner {
88    /// The error type that can be returned by the `try_new` method.
89    #[cfg(feature = "std")]
90    type Error: std::error::Error;
91#[cfg(not(feature = "std"))]
92    type Error: core::fmt::Debug; // Added for no_std consistency
93
94
95    /// Attempts to create a new instance of `Self` from `value`.
96    ///
97    /// # Parameters
98    ///
99    /// - `value`: The `InnerType` value from which to create the instance.
100    ///
101    /// # Returns
102    ///
103    /// - `Ok(Self)` if the instance is created successfully.
104    /// - `Err(Self::Error)` if creation fails (e.g., due to invalid input).
105    fn try_new(value: Self::InnerType) -> Result<Self, Self::Error>;
106}
107
108
109/// A trait for creating new instances of a type infallibly.
110///
111/// This trait provides a method for creating new instances of a type from `Self::InnerType`
112/// (defined by the `IntoInner` supertrait).
113///
114/// Implementors should ensure that the [`New`] method cannot fail in a way that would
115/// typically return a `Result`. If invalid input could lead to an unrecoverable state
116/// or violate invariants, [`New`] should panic. For fallible construction that returns
117/// a `Result`, use the [[`TryNew`]] trait.
118///
119/// # Examples
120///
121/// ```rust
122/// use try_create::{New, IntoInner, TryNew}; // Assuming TryNew is used for context or comparison
123/// # #[cfg(feature = "std")]
124/// # use std::error::Error;
125/// # #[cfg(feature = "std")]
126/// # use std::fmt;
127///
128/// // Example struct
129/// #[derive(Debug, PartialEq)]
130/// struct MyType(i32);
131///
132/// // Define a custom error for MyType for the TryNew example
133/// #[derive(Debug, PartialEq)]
134/// struct MyTypeError(String);
135///
136/// // Implement Display and Error for MyTypeError
137/// # #[cfg(feature = "std")]
138/// impl fmt::Display for MyTypeError {
139///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140///         write!(f, "MyTypeError: {}", self.0)
141///     }
142/// }
143///
144/// # #[cfg(feature = "std")]
145/// impl Error for MyTypeError {}
146/// // For no_std, MyTypeError already derives Debug, which is sufficient.
147///
148/// impl IntoInner for MyType {
149///     type InnerType = i32;
150///     fn into_inner(self) -> Self::InnerType { self.0 }
151/// }
152///
153/// // Example TryNew (optional here, but good for context)
154/// impl TryNew for MyType {
155///     type Error = MyTypeError; // Use the custom error type
156///     fn try_new(value: i32) -> Result<Self, Self::Error> {
157///         if value < 0 { Err(MyTypeError("Value cannot be negative".to_string())) }
158///         else { Ok(MyType(value)) }
159///     }
160/// }
161///
162/// impl New for MyType {
163///     fn new(value: i32) -> Self {
164///         if value < 0 {
165///             panic!("MyType::new: Value cannot be negative");
166///         }
167///         MyType(value)
168///     }
169/// }
170///
171/// assert_eq!(MyType::new(10), MyType(10));
172/// // The following would panic:
173/// // MyType::new(-5);
174/// ```
175pub trait New: Sized + IntoInner {
176/// Creates a new instance of `Self` from `value`.
177    ///
178    /// This method is expected to be infallible in terms of returning a `Result`.
179    /// If the provided `value` cannot produce a valid instance of `Self`
180    /// according to the type's invariants, this method should panic.
181    ///
182    /// # Parameters
183    ///
184    /// - `value`: The `Self::InnerType` value from which to create the instance.
185    fn new(value: Self::InnerType) -> Self;
186}
187
188
189// It's necessary to import Debug for the bound on TryNew's error type.
190// This is implicitly handled if `std::error::Error` is used (as it requires Debug)
191// or if `core::fmt::Debug` is directly used for no_std.
192// For clarity, if you were in a module that didn't automatically have `std::fmt::Debug`
193// or `core::fmt::Debug` in scope, you might need:
194// use core::fmt::Debug; // or std::fmt::Debug if std is assumed
195
196/// A trait for conditionally creating objects.
197///
198/// In debug mode, it uses `Self::try_new().expect()` to create an instance,
199/// panicking if `try_new` returns an error.
200/// In release mode, it uses `Self::new()` to create an instance.
201///
202/// This trait requires the type to also implement [`TryNew`] and [`New`].
203/// The error associated with [`TryNew`] (`<Self as TryNew>::Error`) must implement [`Debug`].
204/// 
205/// # Example
206/// ```rust
207/// use try_create::{ConditionallyCreate, TryNew, New, IntoInner};
208/// # // Copied from TryNew example for self-containment, adjust if shared
209/// # #[cfg(feature = "std")]
210/// # use std::error::Error;
211/// # #[cfg(feature = "std")]
212/// # use std::fmt;
213///
214/// #[derive(Debug, PartialEq)]
215/// struct NotPositiveError;
216///
217/// # #[cfg(feature = "std")]
218/// impl fmt::Display for NotPositiveError {
219///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220///         write!(f, "Value must be positive")
221///     }
222/// }
223/// # #[cfg(feature = "std")]
224/// impl Error for NotPositiveError {}
225/// # #[cfg(not(feature = "std"))]
226/// # trait Error: core::fmt::Debug {} // Minimal Error trait for no_std
227/// # #[cfg(not(feature = "std"))]
228/// # impl Error for NotPositiveError {}
229///
230///
231/// #[derive(Debug, PartialEq)]
232/// struct PositiveInteger { value: i32 }
233///
234/// impl IntoInner for PositiveInteger {
235///     type InnerType = i32;
236///     fn into_inner(self) -> Self::InnerType { self.value }
237/// }
238///
239/// impl TryNew for PositiveInteger {
240///     type Error = NotPositiveError;
241///     fn try_new(value: Self::InnerType) -> Result<Self, Self::Error> {
242///         if value > 0 { Ok(PositiveInteger { value }) }
243///         else { Err(NotPositiveError) }
244///     }
245/// }
246///
247/// // To use ConditionallyCreate, PositiveInteger must also implement New.
248/// impl New for PositiveInteger {
249///     fn new(value: Self::InnerType) -> Self {
250///         match Self::try_new(value) {
251///             Ok(instance) => instance,
252///             Err(e) => panic!("PositiveInteger::new failed for a non-positive value. Error: {:?}", e),
253///         }
254///     }
255/// }
256///
257/// // Now you can call:
258/// # fn example_usage() { // Encapsulate in a function for the doctest
259/// let val_ok = 10;
260/// let _p1 = PositiveInteger::create_conditionally(val_ok);
261/// assert_eq!(_p1, PositiveInteger { value: 10 });
262///
263/// // In debug, this would panic (difficult to test directly in doctest without specific harness):
264/// // let val_err = -5;
265/// // let _p2 = PositiveInteger::create_conditionally(val_err);
266/// # }
267/// # example_usage();
268/// ``` 
269pub trait ConditionallyCreate: Sized + TryNew + New
270where
271    <Self as TryNew>::Error: core::fmt::Debug, // Necessary for .expect() in debug
272{
273    /// Conditionally creates an instance of `Self`.
274    ///
275    /// # Parameters
276    ///
277    /// - `value`: The `Self::InnerType` value (defined by `IntoInner`)
278    ///   from which to create the instance.
279    ///
280    /// # Panics
281    ///
282    /// In debug mode, this method will panic if `Self::try_new(value)`
283    /// returns `Err`. The panic message will include the error's description.
284    /// In release mode, the panic behavior depends on the implementation
285    /// of `Self::new(value)`.
286    fn create_conditionally(value: Self::InnerType) -> Self {
287        #[cfg(debug_assertions)]
288        {
289            // In debug mode, use try_new and panic on error.
290            Self::try_new(value)
291                .expect("ConditionallyCreate: try_new() failed in debug mode")
292        }
293        #[cfg(not(debug_assertions))]
294        {
295            // In release mode, use new (which is assumed to be infallible
296            // or to handle failure internally, e.g., by panicking).
297            Self::new(value)
298        }
299    }
300}
301
302// Blanket implementation of `ConditionallyCreate` for all types `T`
303// that implement [`TryNew`] and [`New`], and whose `TryNew::Error` type
304// implements `Debug`.
305impl<T> ConditionallyCreate for T
306where
307    T: TryNew + New,
308    <T as TryNew>::Error: core::fmt::Debug, // This bound is inherited from the trait definition
309{
310    // The `create_conditionally` method already has a default implementation in the trait,
311    // so it doesn't need to be redefined here.
312}
313
314
315#[cfg(test)]
316mod tests {
317    use super::*; // Import traits from the parent module (your library)
318
319    // --- Test setup for PositiveInteger ---
320    #[derive(Debug, PartialEq)]
321    struct TestNotPositiveError;
322
323    // Implement Display and Error for TestNotPositiveError for std builds
324    #[cfg(feature = "std")]
325    impl std::fmt::Display for TestNotPositiveError {
326        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
327            write!(f, "Value must be positive (Test Error)")
328        }
329    }
330
331    #[cfg(feature = "std")]
332    impl std::error::Error for TestNotPositiveError {}
333
334    // For no_std, deriving Debug is sufficient as per TryNew's Error bound.
335
336    #[derive(Debug, PartialEq)]
337    struct TestPositiveInteger {
338        value: i32,
339    }
340
341    impl IntoInner for TestPositiveInteger {
342        type InnerType = i32;
343        fn into_inner(self) -> Self::InnerType {
344            self.value
345        }
346    }
347
348    impl TryNew for TestPositiveInteger {
349        type Error = TestNotPositiveError;
350        fn try_new(value: Self::InnerType) -> Result<Self, Self::Error> {
351            if value > 0 {
352                Ok(TestPositiveInteger { value })
353            } else {
354                Err(TestNotPositiveError)
355            }
356        }
357    }
358
359    impl New for TestPositiveInteger {
360        fn new(value: Self::InnerType) -> Self {
361            // Consistent with the example: panic if try_new would fail
362            match Self::try_new(value) {
363                Ok(instance) => instance,
364                Err(_) => panic!("TestPositiveInteger::new: Value must be positive."),
365            }
366        }
367    }
368
369    // --- Tests for PositiveInteger ---
370    #[test]
371    fn test_positive_integer_try_new_ok() {
372        assert_eq!(
373            TestPositiveInteger::try_new(10),
374            Ok(TestPositiveInteger { value: 10 })
375        );
376    }
377
378    #[test]
379    fn test_positive_integer_try_new_err_zero() {
380        assert_eq!(TestPositiveInteger::try_new(0), Err(TestNotPositiveError));
381    }
382
383    #[test]
384    fn test_positive_integer_try_new_err_negative() {
385        assert_eq!(TestPositiveInteger::try_new(-5), Err(TestNotPositiveError));
386    }
387
388    #[test]
389    fn test_positive_integer_new_ok() {
390        assert_eq!(TestPositiveInteger::new(10), TestPositiveInteger { value: 10 });
391    }
392
393    #[test]
394    #[should_panic(expected = "TestPositiveInteger::new: Value must be positive.")]
395    fn test_positive_integer_new_panic_zero() {
396        TestPositiveInteger::new(0);
397    }
398
399    #[test]
400    #[should_panic(expected = "TestPositiveInteger::new: Value must be positive.")]
401    fn test_positive_integer_new_panic_negative() {
402        TestPositiveInteger::new(-10);
403    }
404
405    #[test]
406    fn test_positive_integer_into_inner() {
407        let pi = TestPositiveInteger::new(42);
408        assert_eq!(pi.into_inner(), 42);
409    }
410
411    #[test]
412    #[cfg(debug_assertions)] // This test specifically targets the debug behavior
413    fn test_positive_integer_conditionally_create_debug_ok() {
414        assert_eq!(
415            TestPositiveInteger::create_conditionally(10),
416            TestPositiveInteger { value: 10 }
417        );
418    }
419
420    #[test]
421    #[cfg(debug_assertions)] // This test specifically targets the debug behavior
422    #[should_panic(expected = "ConditionallyCreate: try_new() failed in debug mode")]
423    fn test_positive_integer_conditionally_create_debug_panic() {
424        TestPositiveInteger::create_conditionally(-5);
425    }
426    
427    #[test]
428    #[cfg(not(debug_assertions))] // This test specifically targets the release behavior
429    fn test_positive_integer_conditionally_create_release_ok() {
430        assert_eq!(
431            TestPositiveInteger::create_conditionally(10),
432            TestPositiveInteger { value: 10 }
433        );
434    }
435
436    #[test]
437    #[cfg(not(debug_assertions))] // This test specifically targets the release behavior
438    #[should_panic(expected = "TestPositiveInteger::new: Value must be positive.")]
439    fn test_positive_integer_conditionally_create_release_panic() {
440        // In release, create_conditionally calls new(), which for TestPositiveInteger panics.
441        TestPositiveInteger::create_conditionally(-5);
442    }
443
444
445    // --- Test setup for MyType (from New example) ---
446    #[derive(Debug, PartialEq)]
447    struct TestMyTypeError(String);
448
449    #[cfg(feature = "std")]
450    impl std::fmt::Display for TestMyTypeError {
451        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
452            write!(f, "TestMyTypeError: {}", self.0)
453        }
454    }
455
456    #[cfg(feature = "std")]
457    impl std::error::Error for TestMyTypeError {}
458
459
460    #[derive(Debug, PartialEq)]
461    struct TestMyType(i32);
462
463    impl IntoInner for TestMyType {
464        type InnerType = i32;
465        fn into_inner(self) -> Self::InnerType {
466            self.0
467        }
468    }
469
470    impl TryNew for TestMyType {
471        type Error = TestMyTypeError;
472        fn try_new(value: i32) -> Result<Self, Self::Error> {
473            if value < 0 {
474                Err(TestMyTypeError("Value cannot be negative".to_string()))
475            } else {
476                Ok(TestMyType(value))
477            }
478        }
479    }
480    
481    impl New for TestMyType {
482        fn new(value: i32) -> Self {
483            if value < 0 {
484                panic!("TestMyType::new: Value cannot be negative");
485            }
486            TestMyType(value)
487        }
488    }
489
490    // --- Tests for MyType ---
491    #[test]
492    fn test_my_type_try_new_ok() {
493        assert_eq!(TestMyType::try_new(5), Ok(TestMyType(5)));
494    }
495
496    #[test]
497    fn test_my_type_try_new_err() {
498        assert_eq!(
499            TestMyType::try_new(-1),
500            Err(TestMyTypeError("Value cannot be negative".to_string()))
501        );
502    }
503    
504    #[test]
505    fn test_my_type_new_ok() {
506        assert_eq!(TestMyType::new(100), TestMyType(100));
507    }
508
509    #[test]
510    #[should_panic(expected = "TestMyType::new: Value cannot be negative")]
511    fn test_my_type_new_panic() {
512        TestMyType::new(-1);
513    }
514
515    #[test]
516    fn test_my_type_into_inner() {
517        let mt = TestMyType::new(7);
518        assert_eq!(mt.into_inner(), 7);
519    }
520
521    #[test]
522    #[cfg(debug_assertions)]
523    fn test_my_type_conditionally_create_debug_ok() {
524        assert_eq!(
525            TestMyType::create_conditionally(20),
526            TestMyType(20)
527        );
528    }
529
530    #[test]
531    #[cfg(debug_assertions)]
532    #[should_panic(expected = "ConditionallyCreate: try_new() failed in debug mode")]
533    fn test_my_type_conditionally_create_debug_panic() {
534        TestMyType::create_conditionally(-3);
535    }
536
537    #[test]
538    #[cfg(not(debug_assertions))]
539    fn test_my_type_conditionally_create_release_ok() {
540        assert_eq!(
541            TestMyType::create_conditionally(20),
542            TestMyType(20)
543        );
544    }
545
546    #[test]
547    #[cfg(not(debug_assertions))]
548    #[should_panic(expected = "TestMyType::new: Value cannot be negative")]
549    fn test_my_type_conditionally_create_release_panic() {
550        TestMyType::create_conditionally(-3);
551    }
552}