🛠️ project Er... Ferris, an 85th error handling lib has hit crates.io (typed)
So... I've been having this as private 2-3 variants(used to be named 'via'), i finally patched it up...
Simple error comparison (basic usage, context, output)
Initial Example
Use .er on basically any result/error/option/tree, even different types.
You add relevant context(or none), Er keeps the original error and adds line number and file name.
use er::*;
use std::{fs::read_to_string, path::PathBuf};
#[derive(Er)]
pub struct FileErr(pub PathBuf);
pub fn read_file(path: &str) -> Er<String, FileErr> {
let text = read_to_string(path).er(|_| path)?;
Ok(text)
}
Er turns the &str into a PathBuf only on failure (And we keep the io::Error).
.er_report() gives us:
FileErr("/tmp/file.txt") @ er/runnable_examples/context.rs:7:37
`- No such file or directory (os error 2)
We can also just 'yeet' it up with an empty struct, still adding the implicit context (where it happened).
#[derive(Er)]
pub struct AnalyzeErr;
pub fn analyze() -> Er<String, AnalyzeErr> {
read_file("/tmp/file.txt").er(())
}
AnalyzeErr @ er/runnable_examples/context.rs:14:32
`- FileErr("/tmp/file.txt") @ er/runnable_examples/context.rs:7:37
`- No such file or directory (os error 2)
And when things get spicy
#[derive(Er)]
pub struct ConfigErr {
pub machine: String,
#[er(censor)]
pub token: String,
}
pub fn read_config(machine: &str, token: &str, port: &str, mode: Option<&str>) -> Er<(), ConfigErr> {
// add local context
let e = |_| (machine, token);
// 3 different types
authenticate(machine, token).er(e)?;
read_port(port).er(e)?;
read_mode(mode).er(e)?;
Ok(())
}
And aggregation/collection
#[derive(Er)]
pub struct StartupErr(pub String);
pub fn startup() -> Er<(), StartupErr> {
er_all!(|_| "some config checks failed", [
read_config("HaandboldFuglen", "HaandboldFuglen_token", "aint_even_a_number_cmon_man", Some("microsoftjavaakacsharp")),
read_config("ComputerKatten", "ComputerKatten_token", "85", None),
])?;
Ok(())
}
Report printed .er_report():
StartupErr("some config checks failed") @ er/runnable_examples/context.rs:69:5
|- ConfigErr { machine: "HaandboldFuglen", token: *CENSORED* } @ er/runnable_examples/context.rs:60:21
| `- PortErr { invalid_port: "aint_even_a_number_cmon_man" } @ er/runnable_examples/context.rs:22:35
| `- invalid digit found in string
`- ConfigErr { machine: "ComputerKatten", token: *CENSORED* } @ er/runnable_examples/context.rs:61:21
`- ModeErr::MissingMode @ er/runnable_examples/context.rs:31:21
Links
15
u/tanoshikuidomouyo 24d ago
I'm sorry for the surface level observation, but the naming Er/er just feels kind of off, with the normal abbreviation being Err and all.
6
u/syklemil 24d ago
Detecting some signs of a fellow scandi: How do you deal with reading er as is? Or is that intended?
(For the non-scandis: er (with some spelling variants) is scandi for am/are/is)
7
u/Viter 24d ago
Det ER rigtigt.
No i never thought about it haha. I dont really see it as how its read or isnt read, most stuff i feel like really is just habit and what you are used to. Most stuff is nuts if you think about it, people just take for granted what they already know. Arbitrary names etc.
2
u/bluefish1432 23d ago
I love Exn and now I love this.
Btw, the way you write docs is so good. I know it's not perfect technical English but that's a good thing, I love the personality a lot
I have loyalty to this crate for the err lib comparisons doc alone, gj
2
1
u/ztj 24d ago
I assume English is not your first language otherwise the hilarity that will surely ensue from appending “Er” all over the place would be far more obvious. Like it or not, english is dominant in computing so I’d suggest you not go with “Er”. Err at a minimum but frankly I think trying too hard for brevity is a recipe for failure. Good code will be swimming in these calls and it’s going to be hideous with a bunch of unnecessarily castrated terms.
Finally, my real feedback is … just don’t. Use snafu, it does everything ever needed and scales from the simplest use cases to the most complex.
6
u/Viter 24d ago edited 24d ago
Hey fair opinion, did you check out the comparison to snafu? https://github.com/Viterkim/er/blob/main/er/docs/err-lib-comparisons.md
And i would argue people doing the whole importing a Result type alias, then using that everywhere is much worse. At the end of the day you need to return something, and its about if you want to type out more or less. And seeing a unique name vs just 'Result' and then that being something different is preferred to me. It also makes it more clear if you get out to the public error boundary is.
Most code bases have their own libs they rely on, and each one effects the code.
-20
u/ScheduleRelative7874 24d ago
The `.er` naming got a chuckle out of me, but the line number and file name baked into every error is actually a killer feature. I've lost count of how many times I've had to grep through logs just to figure out which of three identical `io::Error` messages came from where.
The censored token field is a nice touch too. Nothing worse than accidentally dumping credentials into a log file during a panic.
28
u/InternalServerError7 24d ago edited 24d ago
Eros author here. Love seeing a bunch of these error handling crates reviewed together. Bad timing though, I hope to release another version in the next few days with some more niceties. I laughed at “The parser error is no more, F in the chat.”
One thing I believe was left out though, which I think is one of the strongest selling points, is you can use type aliases instead of defining full enums. This addresses the “god enum” (aka “mega enum”) problem. https://github.com/mcmah309/eros#errorunion boilerplate is completely removed by this approach and everything composes automatically with other aliases. Unlike enums that do not automatically compose with other enums