sita 0.3.0

Sita: static site generator (SSG) for Markdown and HTML
# Errors using thiserror

For errors we use the crate `thiserror` because it is well-maintained, pragmatic, and provides detailed errors suitable for libraries. 

We removed the crate `error-chain` because it wasn't well-maintained.


## pub enum Error

The pattern for error definition looks like this:

```rust
#[derive(thiserror::Error, Debug)]
pub enum Error {

    #[error("AlfaBravo ➡ {0:?}")]
    AlfaBravo(std::io::Error),

    #[error("CharlieDelta ➡ echo: {echo:?}, foxtrot: {foxtrot:?}")]
    CharlieDelta {
        echo: String,
        foxtrot: String,
    }

}
```

### Our convention

Our convention for `pub enum Error`:

* The enum is named `Error`, not anything else. A side effect of this convention: in each file, the file's enum `Error` convention supersedes any other `Error` such as `std::error::Error`, i.e. in each file, any other `Error` must be fully qualified.

* The error message starts with the enum variant name and a Unicode right arrow.

* The error message for a struct includes each field and its debug representation, so long as it's practical, i.e. we want to behave akin to a library error, and we want print as much information as possible for developers who are debugging.

* For wrapping errors, prefer using the simple form.


## Catch errors

Our preferred pattern for handling a `Result` uses `map_or_else` to return the error `err` or unwrap the value `val` like this:

```rust
foo().map_or_else(
    |err| MyError::StdIoError(err),
    |val| val.ok()
)?;
```


### Catch errors in main

Our convention is a file `main.rs` where the setup happens:

```rust
fn main() {
    env_logger::init();
    match crate::app::run::run() {
        Ok(()) => {
            std::process::exit(0);
        }
        Err(err) => {
            error!("{:?}", err);
            std::process::exit(1);
        }
    }
}
```

Our convention is a file `run.rs` where the work happens:

```rust
pub(crate) fn run() -> Result<()> {
    Ok(())
}
```


## Wrap errors

Say you have a file `alfa.rs` with any typical function that can return an error such as:

```rust
pub fn positive(x: i8) -> Result<(), Error> {
    if x > 0 {
        return x
    } else {
        Error::Parma(x)
    }
}
    
#[derive(thiserror::Error, Debug)]
pub enum Error {
    #[error("Parma ➡ {0:?}")]
    Param(i8),
}
```

Say you also have a file `bravo.rs` that calls `alfa.rs` such as:

```rust
pub fn f(x: i8) -> Result<(), Error> {
    crate::alfa::positive(3)
    .map_or_else(
        |err| Error::Wrap(err),
        |val| val.ok()
    )?
}

#[derive(thiserror::Error, Debug)]
pub enum Error {
    #[error("Wrap ➡ {0:?}")]
    Wrap(crate::alfa::Error)
}
```