Metadata-Version: 2.4
Name: sovic
Version: 0.1.0
Summary: Typed Python utilities for explicit error handling and composable helpers
Keywords: result,rust,error-handling,typing
Author: sylvain kadjo
Author-email: sylvain kadjo <sylvainka12@gmail.com>
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.12
Project-URL: Homepage, https://github.com/sylvain12/sovic
Project-URL: Repository, https://github.com/sylvain12/sovic
Project-URL: Issues, https://github.com/sylvain12/sovic/issues
Description-Content-Type: text/markdown

# Sovic

`sovic` is a typed Python utility package for explicit error handling,
collection helpers, and small composable tools. It starts with a Result-style
API for code that should return either a successful value or an error value
without raising at the call site.

The package is inspired by `Result` types commonly found in languages such as
Rust:

- `Ok(value)` represents a successful operation.
- `Err(error)` represents a failed operation.
- `Result[T, E]` is the union of `Ok[T] | Err[E]`.
- `ResultProtocol[T, E]` describes the shared Result interface.
- `to_result` converts exceptions raised by a function into `Err(Exception)`.
- `map`, `map_err`, and `and_then` help transform and chain `Result` values.

## Why Sovic?

Python exceptions are useful, but they can make expected failures implicit.
`sovic` is for functions where failure is part of the normal return path, such
as validation, parsing, request handling, or adapters around exception-raising
code.

Use `sovic` when you want:

- explicit success and error branches in function signatures;
- simple pattern matching with `Ok(value)` and `Err(error)`;
- typed error handling without adding runtime dependencies;
- small composable helpers for transforming and chaining results.

## Installation

This project is managed with `uv` and requires Python 3.12 or newer.

```bash
uv sync --all-groups
```

## Basic Usage

```python
from sovic import Err, Ok, Result


def parse_user_id(raw: str) -> Result[int, str]:
    if raw.isdigit():
        return Ok(int(raw))

    return Err("user id must be numeric")


result = parse_user_id("42")

if result.is_ok():
    user_id = result.ok()
else:
    message = result.err()
```

## Transforming Results

Use `map()` to transform a successful value, `map_err()` to transform an error,
and `and_then()` to chain another operation that returns a `Result`.

```python
from sovic import Err, Ok, Result


def parse_user_id(raw: str) -> Result[int, str]:
    if raw.isdigit():
        return Ok(int(raw))

    return Err("user id must be numeric")


def require_positive(user_id: int) -> Result[int, str]:
    if user_id > 0:
        return Ok(user_id)

    return Err("user id must be positive")


result = (
    parse_user_id("42")
    .map(lambda user_id: user_id + 1)
    .and_then(require_positive)
    .map_err(lambda error: f"invalid user id: {error}")
)
```

## Pattern Matching

`Ok` and `Err` can be used with Python pattern matching.

```python
from sovic import Err, Ok


match parse_user_id("abc"):
    case Ok(value):
        print(f"parsed id: {value}")
    case Err(error):
        print(f"invalid input: {error}")
```

## Converting Exceptions

Use `to_result` when you want a function that may raise an exception to return
a `Result` instead.

```python
from sovic import Err, Ok, to_result


@to_result
def divide(a: float, b: float) -> float:
    return a / b


match divide(8, 0):
    case Ok(value):
        print(value)
    case Err(error):
        print(f"failed: {error}")
```

`to_result` catches `Exception` and returns `Err(Exception)`. It does not catch
`BaseException`, so interrupts such as `KeyboardInterrupt` and `SystemExit` are
not swallowed.

## API Reference

### `Ok[T]`

Successful result container.

- `ok() -> T`: returns the contained value.
- `err() -> Never`: raises `ResultValueError`.
- `unwrap() -> T`: returns the contained value.
- `unwrap_err() -> Never`: raises `ResultValueError`.
- `is_ok() -> bool`: returns `True`.
- `is_err() -> bool`: returns `False`.
- `map(func) -> Ok[U]`: returns `Ok(func(value))`.
- `map_err(func) -> Ok[T]`: returns the original `Ok`.
- `and_then(func) -> Result[U, E]`: returns `func(value)`.

### `Err[E]`

Failed result container.

- `ok() -> Never`: raises `ResultValueError`.
- `err() -> E`: returns the contained error.
- `unwrap() -> Never`: raises `ResultValueError`.
- `unwrap_err() -> E`: returns the contained error.
- `is_ok() -> bool`: returns `False`.
- `is_err() -> bool`: returns `True`.
- `map(func) -> Err[E]`: returns the original `Err`.
- `map_err(func) -> Err[F]`: returns `Err(func(error))`.
- `and_then(func) -> Err[E]`: returns the original `Err`.

### `Result[T, E]`

Type alias for `Ok[T] | Err[E]`.

### `ResultProtocol[T, E]`

Structural protocol for objects that expose the same Result-style methods as
`Ok` and `Err`.

### `to_result`

Decorator that converts a callable from `Callable[P, T]` into
`Callable[P, Result[T, Exception]]`.

## Development

```bash
uv run pytest
uv run ruff check .
uv run ruff format .
uv run pyrefly check
```
