# Contributing to Musket

## Requirements
Last stable Rust toolchain. Use [Rustup](https://rustup.rs/) to install it.

## Guidelines

* Use [Conventional Commits](https://www.conventionalcommits.org/).
* Use [Feature Branch](https://www.atlassian.com/git/tutorials/comparing-workflows/feature-branch-workflow) creating a pull request to main.
* Use [Semantic Versioning](https://semver.org/).

## Adding destinations

To add new destinations you must follow the next steps:

> Info: Use the existing destinations code files as a source of information.

### 1. Create the destination module

Create a file with the name of the new destination inside [`destinations`](./src/destinations/) folder and import `Destination`, `DestinationError` and `Serde`.

```rust
use super::{Destination, DestinationError};
use serde::{Deserialize, Serialize};
```

#### 1.1. Define the configuration

Add a public `struct` with the name of the destination plus the `Configuration` word. Add as many `pub` fields as you want to be saved in the [configuration file](./README.md#2--create-the-configuration-file). This `struct` must derive `Clone`, `Default`, `Serialize` and `Deserialize`. For example:

```rust
#[derive(Clone, Default, Serialize, Deserialize)]
pub struct BlueskyConfiguration {
    pub identifier: String,
    pub password: String,
    pub commentary: String,
    pub enabled: bool,
}
```

#### 1.2. Develop the destination logic

Add a public `struct` with the name of the destination. Add as many `pub` fields as you needed to use the destination. One of the fields must contain the configuration created before. For example: 

```rust
pub struct Bluesky {
    pub configuration: BlueskyConfiguration,
    pub url: String,
    pub tags: Vec<String>,
    pub commentary: String,
}
```

Next, add all the logic needed to send the URL and the tags to the destination in the `fire` method through the `Destination` trait implementation.

```rust
impl Destination for Bluesky {
    async fn fire(&self) -> Result<(), DestinationError> {
        ...
```

Obviously, import as many crates as you need. For example:

```rust
use bsky_sdk::{api::types::string::Datetime, api::xrpc, rich_text::RichText, BskyAgent};
```

#### 1.3. Handle the errors

In the [`errors.rs`](./src/destinations/errors.rs) file, add the new destination as a variant of the enum `DestinationError` and add the new destination in the pattern matching in the `Display` trait implementation of the `DestinationError` .

Back in the module file, implement as many `From` traits for `DestinationError` as the destination needs. For example:

```rust
impl From<bsky_sdk::Error> for DestinationError {
    fn from(e: bsky_sdk::Error) -> Self {
        DestinationError::LinkedIn {
            message: format!("The url cannot be sent to Bluesky due to {e}."),
        }
    }
}
```

#### 1.4. Enable the destination module

Add the new module as a public module inside the [`mod.rs`](./src/destinations/mod.rs) file.

> Info: Add the modules in alphabetical order.

```rust
pub mod bluesky;
pub mod linkedin;
pub mod mastodon;
pub mod turso;
// Add the new destination module
```

Next, add the new destination as a variant of the enum `Destinations`.

> Info: Add the modules in alphabetical order.

```rust
pub enum Destinations {
    All,
    Bluesky,
    LinkedIn,
    Mastodon,
    Turso,
    // Add here the new destination
}
```

### 2. Define the configuration

In the [`config.rs`](./src/config.rs) file add a field in the `Configuration` `struct` with the destination as a `name` and the destination configuration as a `type`:

> Info: Add the field in alphabetical order.

```rust
#[derive(Default, Debug, Serialize, Deserialize)]
pub struct Configuration {
    pub bluesky: BlueskyConfiguration,
    pub linkedin: LinkedinConfiguration,
    pub turso: TursoConfiguration,
    pub mastodon: MastodonConfiguration,
    // Add the new destination configuration
}
```

Obviously, add the new destination configuration `struct` to the imports.

```rust
use crate::destinations::{
    bluesky::BlueskyConfiguration, linkedin::LinkedinConfiguration,
    mastodon::MastodonConfiguration, turso::TursoConfiguration, // Add the new destination configuration
};
```

### 3. Manage new destination from the lib

The ['lib.rs`](./src/lib.rs) file runs the main logic of the application.

Inside the `run` function, add the new destination as a pattern matching in the `Command::Fire` using the `Destinations` `enum` variants.

```rust
for target in destinations {
    match target {
        Destinations::Bluesky => {
        }
    ...
    }
}
```

Instead of put all the logic that calls the [`Fire` method of the `Destination`](#12-develop-the-destination-logic) inside the _match arm_, you must put it in a `pub` function inside [`shooters.rs`](./src/shooters.rs) file. Name this function using the name of the destination plus `shooter` word.

```rust
success_messages.push(
    bluesky_shooter(&cfg, &url, tags.clone(), commentary.as_ref()).await?,
);
```

Remember to add the command to the `Destinations::All` match as well.

> Info: Add the destinations in alphabetical order.

### 4. Update the documentation

In the [`README.md`](./README.md) file, add a documentation about how to configure the new destination inside the section [Configure the destinations](./README.md#3--configure-the-destinations).