diff options
author | Lars Wirzenius <liw@liw.fi> | 2021-02-03 09:11:49 +0200 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2021-02-04 09:14:01 +0200 |
commit | a2adcb5a90c15b473a2fcf114555443fba8a20ce (patch) | |
tree | 7ec36f244daa105b0da774d6705ef736f9135f64 /src/client.rs | |
parent | bf08ea67ca035fc0e78364450599cefff7cd9bc6 (diff) | |
download | obnam2-a2adcb5a90c15b473a2fcf114555443fba8a20ce.tar.gz |
refactor: have per-module error enums
This means that a function that parses step bindings can't return an
error that the document is missing a title. Such an error return would
be nonsensical, and we use the Rust type system to prevent it, at a
small cost of being a bit verbose.
Additional benefit is that the library portion of Obnam doesn't return
anyhow::Result values anymore.
Diffstat (limited to 'src/client.rs')
-rw-r--r-- | src/client.rs | 88 |
1 files changed, 62 insertions, 26 deletions
diff --git a/src/client.rs b/src/client.rs index 515b8c9..f9c325c 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,15 +1,13 @@ use crate::checksummer::sha256; use crate::chunk::DataChunk; -use crate::chunk::GenerationChunk; -use crate::chunker::Chunker; +use crate::chunk::{GenerationChunk, GenerationChunkError}; +use crate::chunker::{Chunker, ChunkerError}; use crate::chunkid::ChunkId; use crate::chunkmeta::ChunkMeta; -use crate::error::ObnamError; use crate::fsentry::{FilesystemEntry, FilesystemKind}; -use crate::generation::{FinishedGeneration, LocalGeneration}; +use crate::generation::{FinishedGeneration, LocalGeneration, LocalGenerationError}; use crate::genlist::GenerationList; -use anyhow::Context; use chrono::{DateTime, Local}; use log::{debug, error, info, trace}; use reqwest::blocking::Client; @@ -26,11 +24,21 @@ pub struct ClientConfig { pub log: Option<PathBuf>, } +#[derive(Debug, thiserror::Error)] +pub enum ClientConfigError { + #[error(transparent)] + IoError(#[from] std::io::Error), + + #[error(transparent)] + SerdeYamlError(#[from] serde_yaml::Error), +} + +pub type ClientConfigResult<T> = Result<T, ClientConfigError>; + impl ClientConfig { - pub fn read_config(filename: &Path) -> anyhow::Result<Self> { + pub fn read_config(filename: &Path) -> ClientConfigResult<Self> { trace!("read_config: filename={:?}", filename); - let config = std::fs::read_to_string(filename) - .with_context(|| format!("reading configuration file {}", filename.display()))?; + let config = std::fs::read_to_string(filename)?; let config = serde_yaml::from_str(&config)?; Ok(config) } @@ -46,15 +54,47 @@ pub enum ClientError { #[error("Server does not have generation {0}")] GenerationNotFound(String), + + #[error(transparent)] + GenerationChunkError(#[from] GenerationChunkError), + + #[error(transparent)] + LocalGenerationError(#[from] LocalGenerationError), + + #[error(transparent)] + ChunkerError(#[from] ChunkerError), + + #[error("Server response did not have a 'chunk-meta' header for chunk {0}")] + NoChunkMeta(ChunkId), + + #[error("Wrong checksum for chunk {0}, got {1}, expected {2}")] + WrongChecksum(ChunkId, String, String), + + #[error(transparent)] + ReqwestError(#[from] reqwest::Error), + + #[error(transparent)] + ReqwestToStrError(#[from] reqwest::header::ToStrError), + + #[error(transparent)] + SerdeJsonError(#[from] serde_json::Error), + + #[error(transparent)] + SerdeYamlError(#[from] serde_yaml::Error), + + #[error(transparent)] + IoError(#[from] std::io::Error), } +pub type ClientResult<T> = Result<T, ClientError>; + pub struct BackupClient { client: Client, base_url: String, } impl BackupClient { - pub fn new(base_url: &str) -> anyhow::Result<Self> { + pub fn new(base_url: &str) -> ClientResult<Self> { let client = Client::builder() .danger_accept_invalid_certs(true) .build()?; @@ -68,7 +108,7 @@ impl BackupClient { &self, e: &FilesystemEntry, size: usize, - ) -> anyhow::Result<Vec<ChunkId>> { + ) -> ClientResult<Vec<ChunkId>> { info!("upload entry: {:?}", e); let ids = match e.kind() { FilesystemKind::Regular => self.read_file(e.pathbuf(), size)?, @@ -78,7 +118,7 @@ impl BackupClient { Ok(ids) } - pub fn upload_generation(&self, filename: &Path, size: usize) -> anyhow::Result<ChunkId> { + pub fn upload_generation(&self, filename: &Path, size: usize) -> ClientResult<ChunkId> { info!("upload SQLite {}", filename.display()); let ids = self.read_file(filename.to_path_buf(), size)?; let gen = GenerationChunk::new(ids); @@ -89,7 +129,7 @@ impl BackupClient { Ok(gen_id) } - fn read_file(&self, filename: PathBuf, size: usize) -> anyhow::Result<Vec<ChunkId>> { + fn read_file(&self, filename: PathBuf, size: usize) -> ClientResult<Vec<ChunkId>> { info!("upload file {}", filename.display()); let file = std::fs::File::open(filename)?; let chunker = Chunker::new(size, file); @@ -105,7 +145,7 @@ impl BackupClient { format!("{}/chunks", self.base_url()) } - pub fn has_chunk(&self, meta: &ChunkMeta) -> anyhow::Result<Option<ChunkId>> { + pub fn has_chunk(&self, meta: &ChunkMeta) -> ClientResult<Option<ChunkId>> { trace!("has_chunk: url={:?}", self.base_url()); let req = self .client @@ -136,7 +176,7 @@ impl BackupClient { Ok(has) } - pub fn upload_chunk(&self, meta: ChunkMeta, chunk: DataChunk) -> anyhow::Result<ChunkId> { + pub fn upload_chunk(&self, meta: ChunkMeta, chunk: DataChunk) -> ClientResult<ChunkId> { let res = self .client .post(&self.chunks_url()) @@ -155,11 +195,7 @@ impl BackupClient { Ok(chunk_id) } - pub fn upload_gen_chunk( - &self, - meta: ChunkMeta, - gen: GenerationChunk, - ) -> anyhow::Result<ChunkId> { + pub fn upload_gen_chunk(&self, meta: ChunkMeta, gen: GenerationChunk) -> ClientResult<ChunkId> { let res = self .client .post(&self.chunks_url()) @@ -178,7 +214,7 @@ impl BackupClient { Ok(chunk_id) } - pub fn upload_new_file_chunks(&self, chunker: Chunker) -> anyhow::Result<Vec<ChunkId>> { + pub fn upload_new_file_chunks(&self, chunker: Chunker) -> ClientResult<Vec<ChunkId>> { let mut chunk_ids = vec![]; for item in chunker { let (meta, chunk) = item?; @@ -195,7 +231,7 @@ impl BackupClient { Ok(chunk_ids) } - pub fn list_generations(&self) -> anyhow::Result<GenerationList> { + pub fn list_generations(&self) -> ClientResult<GenerationList> { let url = format!("{}?generation=true", &self.chunks_url()); trace!("list_generations: url={:?}", url); let req = self.client.get(&url).build()?; @@ -212,7 +248,7 @@ impl BackupClient { Ok(GenerationList::new(finished)) } - pub fn fetch_chunk(&self, chunk_id: &ChunkId) -> anyhow::Result<DataChunk> { + pub fn fetch_chunk(&self, chunk_id: &ChunkId) -> ClientResult<DataChunk> { info!("fetch chunk {}", chunk_id); let url = format!("{}/{}", &self.chunks_url(), chunk_id); @@ -227,7 +263,7 @@ impl BackupClient { let headers = res.headers(); let meta = headers.get("chunk-meta"); if meta.is_none() { - let err = ObnamError::NoChunkMeta(chunk_id.clone()); + let err = ClientError::NoChunkMeta(chunk_id.clone()); error!("fetching chunk {} failed: {}", chunk_id, err); return Err(err.into()); } @@ -241,7 +277,7 @@ impl BackupClient { let actual = sha256(&body); if actual != meta.sha256() { let err = - ObnamError::WrongChecksum(chunk_id.clone(), actual, meta.sha256().to_string()); + ClientError::WrongChecksum(chunk_id.clone(), actual, meta.sha256().to_string()); error!("fetching chunk {} failed: {}", chunk_id, err); return Err(err.into()); } @@ -251,14 +287,14 @@ impl BackupClient { Ok(chunk) } - fn fetch_generation_chunk(&self, gen_id: &str) -> anyhow::Result<GenerationChunk> { + fn fetch_generation_chunk(&self, gen_id: &str) -> ClientResult<GenerationChunk> { let chunk_id = ChunkId::from_str(gen_id); let chunk = self.fetch_chunk(&chunk_id)?; let gen = GenerationChunk::from_data_chunk(&chunk)?; Ok(gen) } - pub fn fetch_generation(&self, gen_id: &str, dbname: &Path) -> anyhow::Result<LocalGeneration> { + pub fn fetch_generation(&self, gen_id: &str, dbname: &Path) -> ClientResult<LocalGeneration> { let gen = self.fetch_generation_chunk(gen_id)?; // Fetch the SQLite file, storing it in the named file. |