summaryrefslogtreecommitdiff
path: root/src/client.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/client.rs')
-rw-r--r--src/client.rs38
1 files changed, 38 insertions, 0 deletions
diff --git a/src/client.rs b/src/client.rs
index 5451dfb..ed6b86b 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -1,3 +1,5 @@
+//! Client to the Obnam server HTTP API.
+
use crate::chunk::{DataChunk, GenerationChunk, GenerationChunkError};
use crate::chunkid::ChunkId;
use crate::chunkmeta::ChunkMeta;
@@ -13,89 +15,118 @@ use std::fs::File;
use std::io::prelude::*;
use std::path::{Path, PathBuf};
+/// Possible errors when using the server API.
#[derive(Debug, thiserror::Error)]
pub enum ClientError {
+ /// No chunk id for uploaded chunk.
#[error("Server response claimed it had created a chunk, but lacked chunk id")]
NoCreatedChunkId,
+ /// Server claims to not have an entity.
#[error("Server does not have {0}")]
NotFound(String),
+ /// Server does not have a chunk.
#[error("Server does not have chunk {0}")]
ChunkNotFound(ChunkId),
+ /// Server does not have generation.
#[error("Server does not have generation {0}")]
GenerationNotFound(ChunkId),
+ /// Server didn't give us a chunk's metadata.
#[error("Server response did not have a 'chunk-meta' header for chunk {0}")]
NoChunkMeta(ChunkId),
+ /// Chunk has wrong checksum and may be corrupted.
#[error("Wrong checksum for chunk {0}, got {1}, expected {2}")]
WrongChecksum(ChunkId, String, String),
+ /// Client configuration is wrong.
#[error(transparent)]
ClientConfigError(#[from] ClientConfigError),
+ /// An error encrypting or decrypting chunks.
#[error(transparent)]
CipherError(#[from] CipherError),
+ /// An error regarding generation chunks.
#[error(transparent)]
GenerationChunkError(#[from] GenerationChunkError),
+ /// An error using a backup's local metadata.
#[error(transparent)]
LocalGenerationError(#[from] LocalGenerationError),
+ /// An error with the `chunk-meta` header.
#[error("couldn't convert response chunk-meta header to string: {0}")]
MetaHeaderToString(reqwest::header::ToStrError),
+ /// An error from the HTTP library.
#[error("error from reqwest library: {0}")]
ReqwestError(reqwest::Error),
+ /// Couldn't look up a chunk via checksum.
#[error("lookup by chunk checksum failed: {0}")]
ChunkExists(reqwest::Error),
+ /// Error parsing JSON.
#[error("failed to parse JSON: {0}")]
JsonParse(serde_json::Error),
+ /// Error generating JSON.
#[error("failed to generate JSON: {0}")]
JsonGenerate(serde_json::Error),
+ /// Error parsing YAML.
#[error("failed to parse YAML: {0}")]
YamlParse(serde_yaml::Error),
+ /// Failed to open a file.
#[error("failed to open file {0}: {1}")]
FileOpen(PathBuf, std::io::Error),
+ /// Failed to create a file.
#[error("failed to create file {0}: {1}")]
FileCreate(PathBuf, std::io::Error),
+ /// Failed to write a file.
#[error("failed to write to file {0}: {1}")]
FileWrite(PathBuf, std::io::Error),
}
+/// Client for the Obnam server HTTP API.
+///
+/// This is the async version.
pub struct AsyncBackupClient {
chunk_client: AsyncChunkClient,
}
impl AsyncBackupClient {
+ /// Create a new backup client.
pub fn new(config: &ClientConfig) -> Result<Self, ClientError> {
info!("creating backup client with config: {:#?}", config);
Ok(Self {
chunk_client: AsyncChunkClient::new(config)?,
})
}
+
+ /// Does the server have a chunk?
pub async fn has_chunk(&self, meta: &ChunkMeta) -> Result<Option<ChunkId>, ClientError> {
self.chunk_client.has_chunk(meta).await
}
+ /// Upload a data chunk to the srver.
pub async fn upload_chunk(&self, chunk: DataChunk) -> Result<ChunkId, ClientError> {
self.chunk_client.upload_chunk(chunk).await
}
+ /// List backup generations known by the server.
pub async fn list_generations(&self) -> Result<GenerationList, ClientError> {
self.chunk_client.list_generations().await
}
+ /// Fetch a data chunk from the server, given the chunk identifier.
pub async fn fetch_chunk(&self, chunk_id: &ChunkId) -> Result<DataChunk, ClientError> {
self.chunk_client.fetch_chunk(chunk_id).await
}
@@ -106,6 +137,7 @@ impl AsyncBackupClient {
Ok(gen)
}
+ /// Fetch a backup generation's metadata, given it's identifier.
pub async fn fetch_generation(
&self,
gen_id: &GenId,
@@ -129,6 +161,7 @@ impl AsyncBackupClient {
}
}
+/// Client for using chunk part of Obnam server HTTP API.
pub struct AsyncChunkClient {
client: reqwest::Client,
base_url: String,
@@ -136,6 +169,7 @@ pub struct AsyncChunkClient {
}
impl AsyncChunkClient {
+ /// Create a new chunk client.
pub fn new(config: &ClientConfig) -> Result<Self, ClientError> {
let pass = config.passwords()?;
@@ -158,6 +192,7 @@ impl AsyncChunkClient {
format!("{}/chunks", self.base_url())
}
+ /// Does server have a chunk?
pub async fn has_chunk(&self, meta: &ChunkMeta) -> Result<Option<ChunkId>, ClientError> {
let body = match self.get("", &[("sha256", meta.sha256())]).await {
Ok((_, body)) => body,
@@ -176,6 +211,7 @@ impl AsyncChunkClient {
Ok(has)
}
+ /// Upload a new chunk to the server.
pub async fn upload_chunk(&self, chunk: DataChunk) -> Result<ChunkId, ClientError> {
let enc = self.cipher.encrypt_chunk(&chunk)?;
let res = self
@@ -198,6 +234,7 @@ impl AsyncChunkClient {
Ok(chunk_id)
}
+ /// List all generation chunks on the server.
pub async fn list_generations(&self) -> Result<GenerationList, ClientError> {
let (_, body) = self.get("", &[("generation", "true")]).await?;
@@ -211,6 +248,7 @@ impl AsyncChunkClient {
Ok(GenerationList::new(finished))
}
+ /// Fetch a chunk from the server, given its id.
pub async fn fetch_chunk(&self, chunk_id: &ChunkId) -> Result<DataChunk, ClientError> {
let (headers, body) = self.get(&format!("/{}", chunk_id), &[]).await?;
let meta = self.get_chunk_meta_header(chunk_id, &headers)?;