Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Emulator000 committed Jul 8, 2024
0 parents commit 5cd111c
Show file tree
Hide file tree
Showing 10 changed files with 348 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
5 changes: 5 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions .idea/fallible_map.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "fallible_map"
version = "0.1.0"
authors = ["Dario Cancelliere <[email protected]>"]
edition = "2018"
license = "MIT"
description = "Utilities for fallible mapping over `Option` and iterators using functions that can return `Result`s."
repository = "https://github.com/tifaremosapere/fallible_map"
homepage = "https://github.com/tifaremosapere/fallible_map"
documentation = "https://docs.rs/fallible_map_ext"
readme = "README.md"
keywords = ["fallible", "mapping", "option", "result", "error-handling"]
categories = ["data-structures", "algorithms", "rust-patterns"]
exclude = ["/target"]

[dependencies]

[features]
default = []
9 changes: 9 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
MIT License

Copyright (c) 2024 Ti faremo sapere

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
125 changes: 125 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Fallible Map

[![Crates.io](https://img.shields.io/crates/v/fallible_map.svg)](https://crates.io/crates/fallible_map)
![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)
![Version](https://img.shields.io/badge/version-0.1.0-green)
[![Repository](https://img.shields.io/badge/github-repository-orange)](https://github.com/tifaremosapere/fallible_map)
[![Homepage](https://img.shields.io/badge/homepage-Ti%20faremo%20sapere-brightgreen)](https://tifaremosapere.it)

`fallible_map` provides utilities for fallible mapping over `Option` types and iterators, allowing the use of functions that can return `Result`s.

This library includes traits to enhance `Option` and `Iterator` types with methods to handle fallible operations gracefully.

## Overview

This crate offers extensions for optional values and iterators to perform fallible mapping operations, returning results that properly reflect potential errors during computation.

These extensions can be useful in scenarios where operations may fail, and error handling is required.

## Features

- **ExtractOption trait:** A helper trait to extract the inner value of an optional container;
- **FallibleMapExt trait:** Extends `Option` with methods for fallible operations, such as `try_map`, `try_unwrap_or`, and `try_and_then`;
- **TryMapIteratorExt trait:** Extends iterators with a `try_map` method, allowing the use of functions that return `Result`s during iteration.

## Installation

Add this to your `Cargo.toml`:

```toml
[dependencies]
fallible_map = "^0.1"
```

## Usage

### Examples

#### Using FallibleMapExt with `Option`

```rust
use fallible_map::FallibleMapExt;

fn main() -> Result<(), String> {
let some_number: Option<i32> = Some(2);

let result: Result<Option<i32>, String> = some_number.try_map(|num| {
if num % 2 == 0 {
Ok(num * 2)
} else {
Err("Odd number".to_string())
}
});

assert_eq!(result, Ok(Some(4)));

Ok(())
}
```

#### Using TryMapIteratorExt with `Iterator`

```rust
use fallible_map::TryMapIteratorExt;

fn main() -> Result<(), String> {
let numbers = vec![1, 2, 3, 4, 5];

let mapped_numbers: Result<Vec<i32>, String> = numbers.into_iter().try_map(|x| {
if x % 2 == 0 {
Ok(x * 2)
} else {
Err(format!("Failed to process {}", x))
}
});

match mapped_numbers {
Ok(nums) => println!("Mapped successfully: {:?}", nums),
Err(e) => println!("Error occurred: {}", e),
}

Ok(())
}
```

#### Using FallibleMapExt with `try_and_then`

```rust
use fallible_map::FallibleMapExt;

fn main() -> Result<(), String> {
let some_number: Option<i32> = Some(2);

let result: Result<Option<i32>, String> = some_number.try_and_then(|num| {
if num % 2 == 0 {
Ok(Some(num * 2))
} else {
Err("Odd number".to_string())
}
});

assert_eq!(result, Ok(Some(4)));

let none_number: Option<i32> = None;

let result = none_number.try_and_then(|num| {
if num % 2 == 0 {
Ok(Some(num * 2))
} else {
Err("Odd number".to_string())
}
});

assert_eq!(result, Ok(None));

Ok(())
}
```

## License

This project is licensed under the MIT License. See the [LICENSE](LICENSE.md) file for details.

## Contribution

Contributions are welcome! Please feel free to submit a pull request, open an issue, or suggest features and improvements.
157 changes: 157 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/// `fallible_map_ext` provides utilities for fallible mapping over `Option`
/// types and iterators, allowing the use of functions that can return `Result`s.
/// A helper trait to extract the inner value of an optional container.
pub trait ExtractOption<T> {
/// Extract the inner value as an `Option`.
fn extract(self) -> Option<T>;
}

/// Implementation of `ExtractOption` for `Option`.
impl<T> ExtractOption<T> for Option<T> {
fn extract(self) -> Option<T> {
self
}
}

/// Extend `Option` with fallible methods.
///
/// Useful for mapping fallible operations (i.e., operations that return `Result`),
/// over an optional type. The result will be `Result<Option<U>>`, making it easy
/// to handle errors originating from inside the closure being mapped.
///
/// # Type Parameters
///
/// - `C`: The container type that implements `ExtractOption`
/// - `T`: The input container's value type
/// - `U`: The output container's value type
/// - `E`: The possible error type during the mapping
pub trait FallibleMapExt<T, U, E> {
/// Attempt to map a function over an optional value.
///
/// # Parameters
///
/// - `f`: A function that takes a value of type `T` and returns a `Result<U, E>`.
///
/// # Returns
///
/// A `Result` containing an `Option<U>`, or an error `E`.
fn try_map<F>(self, f: F) -> Result<Option<U>, E>
where
F: FnOnce(T) -> Result<U, E>;

/// Unwrap an optional value or compute a fallback.
///
/// # Parameters
///
/// - `f`: A function that returns a `Result<T, E>`.
///
/// # Returns
///
/// A `Result` containing a value of type `T`, or an error `E`.
fn try_unwrap_or<F>(self, f: F) -> Result<T, E>
where
F: FnOnce() -> Result<T, E>;

/// Chain computation that returns another optional value.
///
/// # Parameters
///
/// - `f`: A function that takes a value of type `T` and returns a `Result<Option<U>, E>`.
///
/// # Returns
///
/// A `Result` containing an `Option<U>`, or an error `E`.
fn try_and_then<F>(self, f: F) -> Result<Option<U>, E>
where
F: FnOnce(T) -> Result<Option<U>, E>;
}

/// Implementation of `FallibleMapExt` for types implementing `ExtractOption`.
impl<C, T, U, E> FallibleMapExt<T, U, E> for C
where
C: ExtractOption<T>,
{
fn try_map<F>(self, f: F) -> Result<Option<U>, E>
where
F: FnOnce(T) -> Result<U, E>,
{
match self.extract() {
Some(x) => f(x).map(Some),
None => Ok(None),
}
}

fn try_unwrap_or<F>(self, f: F) -> Result<T, E>
where
F: FnOnce() -> Result<T, E>,
{
match self.extract() {
Some(x) => Ok(x),
None => f(),
}
}

fn try_and_then<F>(self, f: F) -> Result<Option<U>, E>
where
F: FnOnce(T) -> Result<Option<U>, E>,
{
match self.extract() {
Some(x) => f(x),
None => Ok(None),
}
}
}

/// Extend iterator with fallible map functionality.
///
/// This trait provides a fallible version of the `map` method, allowing
/// the use of functions that return `Result`s during iteration.
///
/// # Example
///
/// ```
/// use my_crate::TryMapIteratorExt;
///
/// let numbers = vec![1, 2, 3, 4, 5];
/// let result: Result<Vec<_>, _> = numbers.into_iter().try_map(|num| {
/// if num % 2 == 0 {
/// Ok(num * 2)
/// } else {
/// Err("Odd number")
/// }
/// });
/// assert_eq!(result, Err("Odd number"));
/// ```
pub trait TryMapIteratorExt: Iterator {
/// Attempt to map a function over an iterator.
///
/// # Parameters
///
/// - `f`: A function that takes an item and returns a `Result<B, E>`.
///
/// # Returns
///
/// A `Result` containing a `Vec<B>`, or an error `E`.
fn try_map<B, F, E>(self, f: F) -> Result<Vec<B>, E>
where
Self: Sized,
F: FnMut(Self::Item) -> Result<B, E>;
}

/// Implementation of `TryMapIteratorExt` for all iterators.
impl<I> TryMapIteratorExt for I
where
I: Iterator,
{
fn try_map<B, F, E>(mut self, mut f: F) -> Result<Vec<B>, E>
where
Self: Sized,
F: FnMut(Self::Item) -> Result<B, E>,
{
self.try_fold(Vec::new(), |mut results, item| {
results.push(f(item)?);
Ok(results)
})
}
}

0 comments on commit 5cd111c

Please sign in to comment.