hotshot_query_service/
api.rs

1// Copyright (c) 2022 Espresso Systems (espressosys.com)
2// This file is part of the HotShot Query Service library.
3//
4// This program is free software: you can redistribute it and/or modify it under the terms of the GNU
5// General Public License as published by the Free Software Foundation, either version 3 of the
6// License, or (at your option) any later version.
7// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
8// even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9// General Public License for more details.
10// You should have received a copy of the GNU General Public License along with this program. If not,
11// see <https://www.gnu.org/licenses/>.
12
13use std::{fs, path::Path};
14
15use tide_disco::api::{Api, ApiError};
16use toml::{map::Entry, Value};
17use vbs::version::StaticVersionType;
18
19pub(crate) fn load_api<State: 'static, Error: 'static, Ver: StaticVersionType + 'static>(
20    path: Option<impl AsRef<Path>>,
21    default: &str,
22    extensions: impl IntoIterator<Item = Value>,
23) -> Result<Api<State, Error, Ver>, ApiError> {
24    let mut toml = match path {
25        Some(path) => load_toml(path.as_ref())?,
26        None => toml::from_str(default).map_err(|err| ApiError::CannotReadToml {
27            reason: err.to_string(),
28        })?,
29    };
30    for extension in extensions {
31        merge_toml(&mut toml, extension);
32    }
33    Api::new(toml)
34}
35
36fn merge_toml(into: &mut Value, from: Value) {
37    if let (Value::Table(into), Value::Table(from)) = (into, from) {
38        for (key, value) in from {
39            match into.entry(key) {
40                Entry::Occupied(mut entry) => merge_toml(entry.get_mut(), value),
41                Entry::Vacant(entry) => {
42                    entry.insert(value);
43                },
44            }
45        }
46    }
47}
48
49fn load_toml(path: &Path) -> Result<Value, ApiError> {
50    let bytes = fs::read(path).map_err(|err| ApiError::CannotReadToml {
51        reason: err.to_string(),
52    })?;
53    let string = std::str::from_utf8(&bytes).map_err(|err| ApiError::CannotReadToml {
54        reason: err.to_string(),
55    })?;
56    toml::from_str(string).map_err(|err| ApiError::CannotReadToml {
57        reason: err.to_string(),
58    })
59}