hightorrent_api 0.3.0

Highlevel torrent API client, supporting Bittorrent v1, v2 and hybrid torrents
Documentation
use hightorrent::{MagnetLink, TorrentFile};

use std::boxed::Box;

use crate::api_error::*;

/// `ApiAdd` is implemented by torrent API clients to add new torrents/magnets to the Bittorrent client.
///
/// It takes a single `AddBuilder` with an `AddSource`, and adds it to the torrent client, which is
/// a fallible operation.
///
/// This is the trait that backends should implement, but for convenience, `AddBuilder::send` will
/// perform the operation seamlessly:
///
/// ```ignore
/// let res = api.add().magnet(magnet_link).send().await;
/// ```
#[async_trait]
pub trait ApiAdd<'a>: Send + Sync {
    async fn api_add_send(&self, add: AddBuilder<'a, AddSource>) -> Result<(), ApiError>;
}

/// Dummy newtype used as `AddBuilder` generic type to prevent compilation
/// when no source has been added (typed builder pattern).
pub struct NoAddSource;

/// A source for torrent data to add to the Bittorrent backend.
pub enum AddSource {
    Magnet(Box<MagnetLink>),
    Torrent(Box<TorrentFile>),
}

pub struct AddBuilder<'a, T> {
    api: &'a dyn ApiAdd<'a>,
    #[allow(dead_code)]
    pub source: T,
    pub save_path: Option<String>,
    pub paused: Option<bool>,
    pub tags: Option<Vec<String>>,
}

impl<'a> AddBuilder<'a, NoAddSource> {
    pub fn new(api: &'a dyn ApiAdd<'a>) -> Self {
        AddBuilder {
            api,
            source: NoAddSource,
            save_path: None,
            paused: None,
            tags: None,
        }
    }
}

impl<'a> AddBuilder<'a, NoAddSource> {
    pub fn magnet(self, magnet: MagnetLink) -> AddBuilder<'a, AddSource> {
        let Self {
            api,
            save_path,
            paused,
            tags,
            ..
        } = self;
        AddBuilder {
            api,
            source: AddSource::Magnet(Box::new(magnet)),
            save_path,
            paused,
            tags,
        }
    }

    pub fn torrent(self, torrent: TorrentFile) -> AddBuilder<'a, AddSource> {
        let Self {
            api,
            save_path,
            paused,
            tags,
            ..
        } = self;
        AddBuilder {
            api,
            source: AddSource::Torrent(Box::new(torrent)),
            save_path,
            paused,
            tags,
        }
    }
}

impl<'a, S> AddBuilder<'a, S> {
    pub fn paused(mut self, p: bool) -> AddBuilder<'a, S> {
        self.paused = Some(p);
        self
    }

    pub fn tags(mut self, t: Vec<String>) -> AddBuilder<'a, S> {
        self.tags = Some(t);
        self
    }

    pub fn save_path(mut self, s: &str) -> AddBuilder<'a, S> {
        self.save_path = Some(s.to_string());
        self
    }
}

impl AddBuilder<'_, AddSource> {
    pub async fn send(self) -> Result<(), ApiError> {
        self.api.api_add_send(self).await
    }
}