purple_ssh/providers/
mod.rs1pub mod config;
2mod digitalocean;
3mod hetzner;
4mod linode;
5pub mod sync;
6mod upcloud;
7mod vultr;
8
9use thiserror::Error;
10
11#[derive(Debug, Clone)]
13#[allow(dead_code)]
14pub struct ProviderHost {
15 pub server_id: String,
17 pub name: String,
19 pub ip: String,
21 pub tags: Vec<String>,
23}
24
25#[derive(Debug, Error)]
27pub enum ProviderError {
28 #[error("HTTP error: {0}")]
29 Http(String),
30 #[error("Failed to parse response: {0}")]
31 Parse(String),
32 #[error("Authentication failed. Check your API token.")]
33 AuthFailed,
34 #[error("Rate limited. Try again in a moment.")]
35 RateLimited,
36}
37
38pub trait Provider {
40 fn name(&self) -> &str;
42 fn short_label(&self) -> &str;
44 fn fetch_hosts(&self, token: &str) -> Result<Vec<ProviderHost>, ProviderError>;
46}
47
48pub const PROVIDER_NAMES: &[&str] = &["digitalocean", "vultr", "linode", "hetzner", "upcloud"];
50
51pub fn get_provider(name: &str) -> Option<Box<dyn Provider>> {
53 match name {
54 "digitalocean" => Some(Box::new(digitalocean::DigitalOcean)),
55 "vultr" => Some(Box::new(vultr::Vultr)),
56 "linode" => Some(Box::new(linode::Linode)),
57 "hetzner" => Some(Box::new(hetzner::Hetzner)),
58 "upcloud" => Some(Box::new(upcloud::UpCloud)),
59 _ => None,
60 }
61}
62
63fn map_ureq_error(err: ureq::Error) -> ProviderError {
65 match err {
66 ureq::Error::Status(401, _) | ureq::Error::Status(403, _) => ProviderError::AuthFailed,
67 ureq::Error::Status(429, _) => ProviderError::RateLimited,
68 ureq::Error::Status(code, _) => ProviderError::Http(format!("HTTP {}", code)),
69 ureq::Error::Transport(t) => ProviderError::Http(t.to_string()),
70 }
71}