1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
use forge::config::Config;
use forge::web::{self, AppState};
use std::path::PathBuf;
const USAGE: &str = "usage: forge [CONFIG.toml] [--listen ADDR]
Serves the git repositories listed in CONFIG.toml (default: forge.toml)
as a read-only website, and allows `git clone` over HTTP.";
#[tokio::main]
async fn main() {
if let Err(e) = run().await {
eprintln!("forge: {e}");
std::process::exit(1);
}
}
async fn run() -> Result<(), String> {
let mut config_path = PathBuf::from("forge.toml");
let mut listen_override = None;
let mut args = std::env::args().skip(1);
while let Some(arg) = args.next() {
match arg.as_str() {
"-h" | "--help" => {
println!("{USAGE}");
return Ok(());
}
"--listen" => listen_override = Some(args.next().ok_or("--listen needs an address")?),
other if other.starts_with('-') => return Err(format!("unknown option {other}\n\n{USAGE}")),
other => config_path = PathBuf::from(other),
}
}
let mut config = Config::load(&config_path)?;
if let Some(listen) = listen_override {
config.server.listen = listen;
}
let repos = config.validate()?;
let listen = config.server.listen.clone();
let state = AppState::new(config.server, repos);
let listener = tokio::net::TcpListener::bind(&listen)
.await
.map_err(|e| format!("cannot listen on {listen}: {e}"))?;
eprintln!(
"forge v{} serving {} repo(s) on http://{listen}/",
env!("CARGO_PKG_VERSION"),
state.repos.len()
);
axum::serve(listener, web::router(state))
.with_graceful_shutdown(async {
let _ = tokio::signal::ctrl_c().await;
})
.await
.map_err(|e| e.to_string())
}
|