try_create 0.1.0

A small library providing generic traits for fallible and infallible object creation.
Documentation
# Try Create

[![Crates.io](https://img.shields.io/crates/v/try_create.svg)](https://crates.io/crates/try_create) <!-- Replace with your actual crate name if different -->
[![Docs.rs](https://docs.rs/try_create/badge.svg)](https://docs.rs/try_create) <!-- Replace with your actual crate name if different -->

`try_create` is a small Rust utility library that provides generic traits for object creation, offering standardized ways to handle both fallible and infallible construction.

## Overview

This library introduces a set of traits to streamline the creation of new type instances:

*   **`TryNew`**: For fallible construction, where creating an instance might fail (e.g., due to validation rules). It returns a `Result`.
*   **`New`**: For infallible construction. If an invariant is violated, this method is expected to panic.
*   **`ConditionallyCreate`**: A utility trait that switches creation logic based on the build profile:
    *   In **debug** mode, it uses `TryNew::try_new().expect()`, panicking if `try_new` fails.
    *   In **release** mode, it uses `New::new()`.
*   **`IntoInner`**: A re-exported trait from the [`into_inner`]https://crates.io/crates/into_inner crate, used as a supertrait to define the input type for construction.

These traits are designed to be general-purpose and can be used for various types, promoting a consistent API for object instantiation. The library supports `no_std` environments.

## Installation

Add `try_create` to your `Cargo.toml`:

```toml
[dependencies]
try_create = "0.1.0" # Replace with the latest version
into_inner = "0.1.0" # try_create re-exports IntoInner, but you might depend on it directly too
```

## Usage

### 1. `IntoInner` Trait

Both `TryNew` and `New` require `IntoInner` to be implemented. This trait defines the `InnerType` that your constructor will accept and a way to retrieve this inner value from an instance.

```rust
use try_create::IntoInner;

#[derive(Debug, PartialEq)]
struct MyValueContainer {
    value: i32,
}

impl IntoInner for MyValueContainer {
    type InnerType = i32;

    fn into_inner(self) -> Self::InnerType {
        self.value
    }
}
```

### 2. `TryNew` Trait (Fallible Creation)

Use `TryNew` when the creation process can fail and you want to return a `Result`.

```rust
use try_create::{TryNew, IntoInner};
# #[cfg(feature = "std")]
# use std::fmt;
# #[cfg(feature = "std")]
# use std::error::Error;

// Define a custom error type
#[derive(Debug, PartialEq)]
struct NotPositiveError;

# #[cfg(feature = "std")]
impl fmt::Display for NotPositiveError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Value must be positive")
    }
}

# #[cfg(feature = "std")]
impl Error for NotPositiveError {}
// For no_std, NotPositiveError would just need to implement core::fmt::Debug,
// which it does via #[derive(Debug, PartialEq)].

// A struct that wraps an i32, ensuring it's positive.
#[derive(Debug, PartialEq)]
struct PositiveInteger {
    value: i32,
}

impl IntoInner for PositiveInteger {
    type InnerType = i32;
    fn into_inner(self) -> Self::InnerType { self.value }
}

impl TryNew for PositiveInteger {
    type Error = NotPositiveError;
    // `InnerType` is `i32`, inherited from `IntoInner`.

    fn try_new(value: Self::InnerType) -> Result<Self, Self::Error> {
        if value > 0 {
            Ok(PositiveInteger { value })
        } else {
            Err(NotPositiveError)
        }
    }
}

// Usage
assert_eq!(PositiveInteger::try_new(10), Ok(PositiveInteger { value: 10 }));
assert_eq!(PositiveInteger::try_new(0), Err(NotPositiveError));
assert_eq!(PositiveInteger::try_new(-5), Err(NotPositiveError));

let positive_num = PositiveInteger::try_new(42).unwrap();
assert_eq!(positive_num.into_inner(), 42);
```

### 3. `New` Trait (Infallible/Panicking Creation)

Use `New` when the creation process should not fail in a recoverable way. If invariants are violated, `New::new` should panic.

```rust
use try_create::{New, IntoInner, TryNew}; // TryNew often used to implement New
# #[cfg(feature = "std")]
# use std::fmt;
# #[cfg(feature = "std")]
# use std::error::Error;

// Example struct
#[derive(Debug, PartialEq)]
struct MyType(i32);

// Custom error for TryNew implementation (if used to implement New)
#[derive(Debug, PartialEq)]
struct MyTypeError(String);

# #[cfg(feature = "std")]
impl fmt::Display for MyTypeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "MyTypeError: {}", self.0)
    }
}
# #[cfg(feature = "std")]
impl Error for MyTypeError {}


impl IntoInner for MyType {
    type InnerType = i32;
    fn into_inner(self) -> Self::InnerType { self.0 }
}

// Optional TryNew, can be used to implement New
impl TryNew for MyType {
    type Error = MyTypeError;
    fn try_new(value: i32) -> Result<Self, Self::Error> {
        if value < 0 { Err(MyTypeError("Value cannot be negative".to_string())) }
        else { Ok(MyType(value)) }
    }
}

impl New for MyType {
    fn new(value: i32) -> Self {
        // Example: using try_new and panicking on error
        // match Self::try_new(value) {
        //     Ok(instance) => instance,
        //     Err(e) => panic!("MyType::new failed: {:?}", e),
        // }
        // Or direct implementation:
        if value < 0 {
            panic!("MyType::new: Value cannot be negative");
        }
        MyType(value)
    }
}

// Usage
assert_eq!(MyType::new(10), MyType(10));
// The following would panic:
// MyType::new(-5);
```

### 4. `ConditionallyCreate` Trait

This trait provides a `create_conditionally` method that behaves differently in debug and release builds.

```rust
use try_create::{ConditionallyCreate, TryNew, New, IntoInner};
# #[cfg(feature = "std")]
# use std::fmt;
# #[cfg(feature = "std")]
# use std::error::Error;

// Using PositiveInteger and NotPositiveError from the TryNew example above
# #[derive(Debug, PartialEq)]
# struct NotPositiveError;
# #[cfg(feature = "std")]
# impl fmt::Display for NotPositiveError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Value must be positive") } }
# #[cfg(feature = "std")]
# impl Error for NotPositiveError {}

# #[derive(Debug, PartialEq)]
# struct PositiveInteger { value: i32 }
# impl IntoInner for PositiveInteger { type InnerType = i32; fn into_inner(self) -> Self::InnerType { self.value } }
# impl TryNew for PositiveInteger {
#     type Error = NotPositiveError;
#     fn try_new(value: Self::InnerType) -> Result<Self, Self::Error> {
#         if value > 0 { Ok(PositiveInteger { value }) } else { Err(NotPositiveError) }
#     }
# }

// PositiveInteger must also implement New to use ConditionallyCreate
impl New for PositiveInteger {
    fn new(value: Self::InnerType) -> Self {
        match Self::try_new(value) {
            Ok(instance) => instance,
            Err(e) => panic!("PositiveInteger::new failed for a non-positive value. Error: {:?}", e),
        }
    }
}

// Usage of ConditionallyCreate
let p1 = PositiveInteger::create_conditionally(10);
assert_eq!(p1, PositiveInteger { value: 10 });

// If PositiveInteger::create_conditionally(-5) was called:
// - In debug mode: it would panic with "ConditionallyCreate: try_new() failed in debug mode".
// - In release mode: it would panic with "PositiveInteger::new failed...".
```

## `no_std` Support

The library is `no_std` compatible.
When the `std` feature is not enabled (default for `no_std` environments):
*   `TryNew::Error` only requires `core::fmt::Debug`.
*   You are responsible for defining error types that conform to this.

## Contributing

Contributions, issues, and feature requests are welcome!

## License

This project is licensed under the terms of the MIT license and the Apache License (Version 2.0). See [LICENSE-MIT](LICENSE-MIT) and [LICENSE-APACHE](LICENSE-APACHE) for details. (You'll need to add these license files to your project).