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}