oofs

This library is WIP.

Attribute args need to be implemented as well as well as features.

You can still check it out, play with it, and expand the macro to see how things work.

Stay tuned for a next minor release!

Crates.io MIT licensed

This library provides #[oofs], an attribute that generates and injects an error context into all instances of try operator ? in fn or impl methods.

Basic Example

Below is an example from oofs/tests/basic.rs

```rust use oofs::{oofs, Oof};

[oofs]

fn mainfn() -> Result<(), Oof> { let mystruct = MyStruct { field0: 123, field1: "hello world".to_owned(), };

my_struct.some_method(321, "watermelon sugar")?;

Ok(())

}

[derive(Debug)]

struct MyStruct { field0: usize, field1: String, }

[oofs]

impl MyStruct { fn somemethod(&self, x: usize, y: &str) -> Result { somefn(y)?;

    Ok(x)
}

}

[oofs]

fn some_fn(text: &str) -> Result<(), Oof> { let _ = text.parse::()?;

Ok(())

} ```

In the above example, if we were to print out an error from main_fn, it would look something like this:

`` my_struct.some_method($0, $1) failed atoofs/tests/basic.rs:10:5`

Parameters: $0: usize = 321 $1: &str = "watermelon sugar"

Caused by: 0: some_fn($0) failed at oofs/tests/basic.rs:29:9

   Parameters:
       $0: &str = "watermelon sugar"


1: text.parse() failed
       at `oofs/tests/basic.rs:39:13`

2: invalid digit found in string

```

I don't know about you, but, this looks really nice for something you get for free!

You get the information about the chains of methods that fail and their locations in code, the parameters' names, types and their debug info!

Handling Error Cases

Yes, those error messages look wonderful, but how can we handle for different error cases in code? Since Oof does wrap previous errors in a Box, it is still hard to handle different errors, or is it?

That is why I introduced tagging to Oof.

Take the same example from above, but I change this one line in some_fn(...):

```rust struct NoRetry;

[oofs]

fn some_fn(text: &str) -> Result<(), Oof> { let _ = text.parse::().tag::()?;

Ok(())

} ```

Now, in any functions or methods that use some_fn(...), you can search for the tag!

```rust struct NoRetry;

[oofs]

fn mainfn() -> Result<(), Oof> { let mystruct = MyStruct { field0: 123, field1: "hello world".to_owned(), };

if let Err(err) = my_struct.some_method(321, "watermelon sugar") {
    if err.tagged_nested::<NoRetry>() {
        ...some action
    } else {
        ...some other action
    }
}

Ok(())

}

...

[oofs]

fn some_fn(text: &str) -> Result<(), Oof> { let _ = text.parse::().tag::()?;

Ok(())

} ```

With tagging, you do not need to handle every different error cases; instead, you can group them by tags, and just handle for different tags!

You can also tag multiple times to the same error. Underneath, it's just a HashSet<TypeId>.

I think this is especially useful when categorizing different errors is necessary, like returning HTTP status codes, or retry or no_retry, etc.

If you want to tag all try operator instances in a given function, simply add a tag attribute:

```rust struct NoRetry;

[oofs]

[oofs(tag(NoRetry))]

fn some_fn(text: &str) -> Result<(), Oof> { let _ = text.parse::()?;

some_other_fn(...)?;

Ok(())

} ```

This will tag NoRetry to both parse() and some_other_fn() and any other instances of try operators.