Skip to main content

pve_api/
lib.rs

1#![doc = include_str!("../README.md")]
2
3// SPDX-License-Identifier: MIT
4// Copyright (c) 2026 Luke Harding <luke@lukeh990.io>
5
6pub mod builder;
7pub mod simple;
8
9pub use builder::PveBuilder;
10use reqwest::{Client, StatusCode, Url};
11pub use simple::SimpleApi;
12use thiserror::Error;
13
14/// This is the main struct for this project. It manages all the state
15/// required. Usually built with [`PveBuilder`]
16#[derive(Debug, Clone)]
17pub struct Pve {
18    client: Client,
19    base_url: Url,
20}
21
22impl Pve {
23    /// Create a new builder for [`Pve`]
24    ///
25    /// For Example:
26    /// ```
27    /// var pve: Pve = Pve::builder()
28    ///     .base_url("<PVE_URL>")
29    ///     .api_token("<TOKEN_ID>", "<SECRET>")
30    ///     .build()?;
31    /// ```
32    pub fn builder() -> PveBuilder {
33        PveBuilder::new()
34    }
35
36    fn new(client: Client, base_url: Url) -> Self {
37        Pve { client, base_url }
38    }
39
40    fn process_err_resp(status: StatusCode, body: String) -> PveError {
41        if status == StatusCode::UNAUTHORIZED {
42            PveError::Unauthorized(body)
43        } else if status == StatusCode::NOT_FOUND {
44            PveError::NotFound(body)
45        } else if status.is_client_error() {
46            PveError::Client(body)
47        } else if status.is_server_error() {
48            PveError::Server(body)
49        } else {
50            PveError::Unknown(body)
51        }
52    }
53
54    /// Run connection tests against the configured remote server.
55    ///
56    /// For Example:
57    /// ```
58    /// var pve: Pve = ...; // See Pve::builder()
59    /// var test: Result<(), PveError> = pve.test()
60    /// ```
61    pub async fn test(&self) -> Result<(), PveError> {
62        let path = self.base_url.join("version")?;
63        let result = self.client.get(path).send().await?;
64        let status = result.status();
65
66        if status == StatusCode::OK {
67            Ok(())
68        } else {
69            Err(Self::process_err_resp(status, result.text().await?))
70        }
71    }
72}
73
74/// Used as a very broad error type encompassing serialization, request
75/// construction, parsing URLs, and response status codes. If you want to
76/// actually understand why something failed, you'd probably want to use a
77/// match statement.
78#[derive(Debug, Error)]
79pub enum PveError {
80    #[error("Failed to parse URL")]
81    UrlParse(#[from] url::ParseError),
82    #[error("Error constructing / sending request")]
83    Reqwest(#[from] reqwest::Error),
84    #[error("Unauthorized: {0}")]
85    Unauthorized(String),
86    #[error("Not Found: {0}")]
87    NotFound(String),
88    #[error("Client Error (4XX) : {0}")]
89    Client(String),
90    #[error("Server Error (5XX) : {0}")]
91    Server(String),
92    #[error("Unknown Error: {0}")]
93    Unknown(String),
94    #[error("Serialization Error")]
95    Serialize(serde_json::Error),
96    #[error("Deserialization Error")]
97    Deserialize(serde_json::Error),
98}