obnam/
server.rs

1//! Stuff related to the Obnam chunk server.
2
3use crate::chunk::DataChunk;
4use crate::chunkid::ChunkId;
5use crate::chunkmeta::ChunkMeta;
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::default::Default;
9use std::path::{Path, PathBuf};
10
11/// Server configuration.
12#[derive(Debug, Deserialize, Clone)]
13#[serde(deny_unknown_fields)]
14pub struct ServerConfig {
15    /// Path to directory where chunks are stored.
16    pub chunks: PathBuf,
17    /// Address where server is to listen.
18    pub address: String,
19    /// Path to TLS key.
20    pub tls_key: PathBuf,
21    /// Path to TLS certificate.
22    pub tls_cert: PathBuf,
23}
24
25/// Possible errors wittht server configuration.
26#[derive(Debug, thiserror::Error)]
27pub enum ServerConfigError {
28    /// The chunks directory doesn't exist.
29    #[error("Directory for chunks {0} does not exist")]
30    ChunksDirNotFound(PathBuf),
31
32    /// The TLS certificate doesn't exist.
33    #[error("TLS certificate {0} does not exist")]
34    TlsCertNotFound(PathBuf),
35
36    /// The TLS key doesn't exist.
37    #[error("TLS key {0} does not exist")]
38    TlsKeyNotFound(PathBuf),
39
40    /// Server address is wrong.
41    #[error("server address can't be resolved")]
42    BadServerAddress,
43
44    /// Failed to read configuration file.
45    #[error("failed to read configuration file {0}: {1}")]
46    Read(PathBuf, std::io::Error),
47
48    /// Failed to parse configuration file as YAML.
49    #[error("failed to parse configuration file as YAML: {0}")]
50    YamlParse(serde_yaml::Error),
51}
52
53impl ServerConfig {
54    /// Read, parse, and check the server configuration file.
55    pub fn read_config(filename: &Path) -> Result<Self, ServerConfigError> {
56        let config = match std::fs::read_to_string(filename) {
57            Ok(config) => config,
58            Err(err) => return Err(ServerConfigError::Read(filename.to_path_buf(), err)),
59        };
60        let config: Self = serde_yaml::from_str(&config).map_err(ServerConfigError::YamlParse)?;
61        config.check()?;
62        Ok(config)
63    }
64
65    /// Check the configuration.
66    pub fn check(&self) -> Result<(), ServerConfigError> {
67        if !self.chunks.exists() {
68            return Err(ServerConfigError::ChunksDirNotFound(self.chunks.clone()));
69        }
70        if !self.tls_cert.exists() {
71            return Err(ServerConfigError::TlsCertNotFound(self.tls_cert.clone()));
72        }
73        if !self.tls_key.exists() {
74            return Err(ServerConfigError::TlsKeyNotFound(self.tls_key.clone()));
75        }
76        Ok(())
77    }
78}
79
80/// Result of creating a chunk.
81#[derive(Debug, Serialize)]
82pub struct Created {
83    id: ChunkId,
84}
85
86impl Created {
87    /// Create a new created chunk id.
88    pub fn new(id: ChunkId) -> Self {
89        Created { id }
90    }
91
92    /// Convert to JSON.
93    pub fn to_json(&self) -> String {
94        serde_json::to_string(&self).unwrap()
95    }
96}
97
98/// Result of retrieving a chunk.
99#[derive(Debug, Serialize)]
100pub struct Fetched {
101    id: ChunkId,
102    chunk: DataChunk,
103}
104
105impl Fetched {
106    /// Create a new id for a fetched chunk.
107    pub fn new(id: ChunkId, chunk: DataChunk) -> Self {
108        Fetched { id, chunk }
109    }
110
111    /// Convert to JSON.
112    pub fn to_json(&self) -> String {
113        serde_json::to_string(&self).unwrap()
114    }
115}
116
117/// Result of a search.
118#[derive(Debug, Default, PartialEq, Deserialize, Serialize)]
119pub struct SearchHits {
120    map: HashMap<String, ChunkMeta>,
121}
122
123impl SearchHits {
124    /// Insert a new chunk id to search results.
125    pub fn insert(&mut self, id: ChunkId, meta: ChunkMeta) {
126        self.map.insert(id.to_string(), meta);
127    }
128
129    /// Convert from JSON.
130    pub fn from_json(s: &str) -> Result<Self, serde_json::Error> {
131        let map = serde_json::from_str(s)?;
132        Ok(SearchHits { map })
133    }
134
135    /// Convert to JSON.
136    pub fn to_json(&self) -> String {
137        serde_json::to_string(&self.map).unwrap()
138    }
139}
140
141#[cfg(test)]
142mod test_search_hits {
143    use super::{ChunkMeta, SearchHits};
144    use crate::label::Label;
145
146    #[test]
147    fn no_search_hits() {
148        let hits = SearchHits::default();
149        assert_eq!(hits.to_json(), "{}");
150    }
151
152    #[test]
153    fn one_search_hit() {
154        let id = "abc".parse().unwrap();
155        let sum = Label::sha256(b"123");
156        let meta = ChunkMeta::new(&sum);
157        let mut hits = SearchHits::default();
158        hits.insert(id, meta);
159        eprintln!("hits: {:?}", hits);
160        let json = hits.to_json();
161        let hits2 = SearchHits::from_json(&json).unwrap();
162        assert_eq!(hits, hits2);
163    }
164}