mod download;
mod peer_manager;
mod torrent;
mod upload;
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::RwLock;
use crate::error::{Error, ErrorKind};
use crate::metainfo::{Metainfo, Mode, from_bytes as parse_metainfo};
use crate::storage::FileStorage;
use self::torrent::TorrentHandle;
pub type InfoHash = [u8; 20];
pub struct Session {
config: SessionConfig,
torrents: RwLock<HashMap<InfoHash, TorrentHandle>>,
}
#[derive(Debug, Clone)]
pub struct SessionConfig {
pub listen_port: u16,
pub max_connections: u32,
pub max_uploads: u32,
pub download_dir: PathBuf,
pub enable_dht: bool,
}
impl Default for SessionConfig {
fn default() -> Self {
SessionConfig {
listen_port: 6881,
max_connections: 50,
max_uploads: 8,
download_dir: PathBuf::from("downloads"),
enable_dht: true,
}
}
}
#[derive(Debug, Clone)]
pub struct TorrentStatus {
pub info_hash: InfoHash,
pub name: String,
pub progress: f64,
pub download_rate: f64,
pub upload_rate: f64,
pub num_peers: usize,
pub num_seeds: usize,
pub state: TorrentState,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TorrentState {
Queued,
Downloading,
Seeding,
Paused,
Error,
}
impl Session {
pub async fn new(config: SessionConfig) -> Result<Self, Error> {
Ok(Session {
config,
torrents: RwLock::new(HashMap::new()),
})
}
pub async fn add_torrent(&self, meta: Metainfo) -> Result<InfoHash, Error> {
let info_hash = meta.info_hash();
let _name = match &meta.info.mode {
Mode::Single { name, .. } | Mode::Multiple { name, .. } => name.clone(),
};
let storage = Arc::new(FileStorage::new(&meta.info, &self.config.download_dir).await?);
let handle = TorrentHandle::new(meta, info_hash, storage, &self.config);
self.torrents.write().await.insert(info_hash, handle);
Ok(info_hash)
}
pub async fn add_torrent_bytes(&self, data: &[u8]) -> Result<InfoHash, Error> {
let meta = parse_metainfo(data)?;
self.add_torrent(meta).await
}
pub async fn remove_torrent(&self, info_hash: &InfoHash) -> Result<(), Error> {
let handle = self.torrents.write().await.remove(info_hash);
if let Some(mut h) = handle {
h.cancel().await;
}
Ok(())
}
pub async fn torrent_status(&self, info_hash: &InfoHash) -> Result<TorrentStatus, Error> {
let torrents = self.torrents.read().await;
let handle = torrents
.get(info_hash)
.ok_or(Error::new(ErrorKind::InvalidInput))?;
Ok(handle.status().await)
}
pub async fn active_torrents(&self) -> Vec<InfoHash> {
self.torrents.read().await.keys().copied().collect()
}
}