Config TOML, CLI options

This commit is contained in:
Pascal Engélibert 2026-03-14 10:50:03 +01:00
commit da6fb1c3c8
5 changed files with 189 additions and 2 deletions

56
Cargo.lock generated
View file

@ -41,6 +41,27 @@ version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "argp"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7409aa6f1dd8464eac2e56cf538e1e5f7f79678caa32f198d214a3db8d5075c1"
dependencies = [
"argp_derive",
]
[[package]]
name = "argp_derive"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d9b949411282939e3f7d8923127e3f18aa474b46da4e8bb0ddf2cb8c81f963a"
dependencies = [
"proc-macro2",
"pulldown-cmark",
"quote",
"syn",
]
[[package]]
name = "askama"
version = "0.12.1"
@ -259,8 +280,10 @@ checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
name = "blindforge"
version = "0.1.0"
dependencies = [
"argp",
"askama",
"base64-turbo",
"boml",
"form_urlencoded",
"giallo",
"log",
@ -299,6 +322,12 @@ dependencies = [
"piper",
]
[[package]]
name = "boml"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a96d68a303a5dd009292d8fea60bf27916d734d6841b53a53c685ad80182526d"
[[package]]
name = "cc"
version = "1.2.56"
@ -627,6 +656,15 @@ dependencies = [
"version_check",
]
[[package]]
name = "getopts"
version = "0.2.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df"
dependencies = [
"unicode-width",
]
[[package]]
name = "getrandom"
version = "0.3.4"
@ -1208,6 +1246,18 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "pulldown-cmark"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b"
dependencies = [
"bitflags",
"getopts",
"memchr",
"unicase",
]
[[package]]
name = "quote"
version = "1.0.45"
@ -1905,6 +1955,12 @@ version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-width"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
[[package]]
name = "unicode-xid"
version = "0.2.6"

View file

@ -4,10 +4,17 @@ version = "0.1.0"
edition = "2024"
[dependencies]
# CLI options
argp = "0.4.0"
# Templates
askama = "0.12.1"
base64-turbo = "0.1.3"
# TOML parser
boml = "1.0.2"
#flate2 = "1.1.8"
# request parsing
form_urlencoded = "1.2.2"
# Code highlight
giallo = { version = "0.3.1", features = ["dump"] }
log = "0.4.29"
rand = "0.9.2"
@ -15,6 +22,7 @@ serde = { version = "1.0.228", features = ["derive"] }
sha2 = "0.10.9"
simplelog = "0.12.2"
#tar = "0.4.44"
# Web server
trillium = "0.2.20"
trillium-askama = "0.3.2"
trillium-caching-headers = "0.2.3"

31
example_config.toml Normal file
View file

@ -0,0 +1,31 @@
# This is an example config file for blindforge.
# It contains all default values in comments.
# The server will listen to this address.
#listen_host = "127.0.0.1"
# The server will listen to this port.
#listen_port = 44617
# Server's permanent data will be stored in this directory.
#data_dir = "/var/lib/blindforge"
data_dir = "/dev/shm/blindforge"
# Client will connect to the Git forge API using this User-Agent.
#api_client_user_agent = "Blindforge"
# Client will download at most this number of pages.
#api_client_max_page = 32
# Maximum number of entries (files and directories) in a repository.
#max_entries = 1024
# Bigger files in repositories will be omitted. (bytes)
# Default: 8 MB
#max_file_size = 8388608
# Files with a longer path in a repository will be omitted. (characters)
#max_file_path_len = 8192
# Maximum length of an URL for the client. (characters)
#api_client_max_url_len = 256

View file

@ -34,3 +34,69 @@ impl Default for Config {
}
}
}
impl Config {
pub fn from_toml(input: &[u8]) -> Self {
let toml = boml::parse(str::from_utf8(input).expect("Config is not valid UTF-8"))
.expect("Config is not valid TOML");
let mut config = Self::default();
if let Some(v) = toml.get("listen_host") {
config.listen_host = v
.as_string()
.expect("Config: invalid listen_host")
.to_string();
}
if let Some(v) = toml.get("listen_port") {
config.listen_port = v
.as_integer()
.expect("Config: invalid listen_port")
.try_into()
.expect("Config: invalid listen_port");
}
if let Some(v) = toml.get("data_dir") {
config.data_dir = v.as_string().expect("Config: invalid data_dir").to_string();
}
if let Some(v) = toml.get("api_client_user_agent") {
config.api_client_user_agent = v
.as_string()
.expect("Config: invalid api_client_user_agent")
.to_string();
}
if let Some(v) = toml.get("api_client_max_page") {
config.api_client_max_page = v
.as_integer()
.expect("Config: invalid api_client_max_page")
.try_into()
.expect("Config: invalid api_client_max_page");
}
if let Some(v) = toml.get("max_entries") {
config.max_entries = v
.as_integer()
.expect("Config: invalid max_entries")
.try_into()
.expect("Config: invalid max_entries");
}
if let Some(v) = toml.get("max_file_size") {
config.max_file_size = v
.as_integer()
.expect("Config: invalid max_file_size")
.try_into()
.expect("Config: invalid max_file_size");
}
if let Some(v) = toml.get("max_file_path_len") {
config.max_file_path_len = v
.as_integer()
.expect("Config: invalid max_file_path_len")
.try_into()
.expect("Config: invalid max_file_path_len");
}
if let Some(v) = toml.get("api_client_max_url_len") {
config.api_client_max_url_len = v
.as_integer()
.expect("Config: invalid api_client_max_url_len")
.try_into()
.expect("Config: invalid api_client_max_url_len");
}
config
}
}

View file

@ -8,17 +8,43 @@ mod repo;
mod server;
mod templates;
use std::str::FromStr;
const SUBDIR_REPOS: &str = "repos";
fn log_level_from_str(input: &str) -> Result<log::LevelFilter, String> {
log::LevelFilter::from_str(input).map_err(|_e| String::from("Invalid log level"))
}
/// Git repository anonymization server
#[derive(argp::FromArgs)]
struct Blindforge {
/// Path to the config file
#[argp(positional)]
config: String,
/// Log verbosity level (off|error|warn|info|debug|trace) (default: info)
#[argp(
option,
short = 'v',
default = "log::LevelFilter::Info",
from_str_fn(log_level_from_str)
)]
verbosity: log::LevelFilter,
}
fn main() {
let cli: Blindforge = argp::parse_args_or_exit(argp::DEFAULT);
simplelog::TermLogger::init(
simplelog::LevelFilter::Debug,
cli.verbosity,
simplelog::Config::default(),
simplelog::TerminalMode::Stderr,
simplelog::ColorChoice::Auto,
)
.unwrap();
let config = Arc::new(config::Config::default());
let config = Arc::new(config::Config::from_toml(
&std::fs::read(cli.config).expect("Cannot read config file"),
));
std::fs::DirBuilder::new()
.recursive(true)
.create(PathBuf::from(&config.data_dir).join(SUBDIR_REPOS))