use std::net::{SocketAddr, ToSocketAddrs};
use std::path::PathBuf;
use hex::FromHex;
use structopt::StructOpt;
use rudolfs::{Cache, LocalServerBuilder, S3ServerBuilder};
mod lfs;
mod lru;
mod sha256;
mod storage;
mod util;
static AFTER_HELP: &str = include_str!("help.md");
#[derive(StructOpt)]
#[structopt(after_help = AFTER_HELP)]
struct Args {
#[structopt(flatten)]
global: GlobalArgs,
#[structopt(subcommand)]
backend: Backend,
}
#[derive(StructOpt)]
enum Backend {
#[structopt(name = "s3")]
S3(S3Args),
#[structopt(name = "local")]
Local(LocalArgs),
}
#[derive(StructOpt)]
struct GlobalArgs {
#[structopt(long = "host", env = "RUDOLFS_HOST")]
host: Option<String>,
#[structopt(long = "port", default_value = "8080", env = "PORT")]
port: u16,
#[structopt(
long = "key",
parse(try_from_str = FromHex::from_hex),
env = "RUDOLFS_KEY"
)]
key: [u8; 32],
#[structopt(long = "cache-dir", env = "RUDOLFS_CACHE_DIR")]
cache_dir: Option<PathBuf>,
#[structopt(
long = "max-cache-size",
default_value = "50 GiB",
env = "RUDOLFS_MAX_CACHE_SIZE"
)]
max_cache_size: human_size::Size,
#[structopt(
long = "log-level",
default_value = "info",
env = "RUDOLFS_LOG"
)]
log_level: log::LevelFilter,
}
#[derive(StructOpt)]
struct S3Args {
#[structopt(long, env = "RUDOLFS_S3_BUCKET")]
bucket: String,
#[structopt(long, default_value = "lfs", env = "RUDOLFS_S3_PREFIX")]
prefix: String,
#[structopt(long = "cdn", env = "RUDOLFS_S3_CDN")]
cdn: Option<String>,
}
#[derive(StructOpt)]
struct LocalArgs {
#[structopt(long, env = "RUDOLFS_LOCAL_PATH")]
path: PathBuf,
}
impl Args {
async fn main(self) -> Result<(), Box<dyn std::error::Error>> {
let mut logger_builder = pretty_env_logger::formatted_timed_builder();
logger_builder.filter_module("rudolfs", self.global.log_level);
if let Ok(env) = std::env::var("RUST_LOG") {
logger_builder.parse_filters(&env);
}
logger_builder.init();
let addr = match self.global.host {
Some(ref host) => host
.to_socket_addrs()?
.next()
.unwrap_or_else(|| SocketAddr::from(([0, 0, 0, 0], 8080))),
None => SocketAddr::from(([0, 0, 0, 0], self.global.port)),
};
log::info!("Initializing storage...");
match self.backend {
Backend::S3(s3) => s3.run(addr, self.global).await?,
Backend::Local(local) => local.run(addr, self.global).await?,
}
Ok(())
}
}
impl S3Args {
async fn run(
self,
addr: SocketAddr,
global_args: GlobalArgs,
) -> Result<(), Box<dyn std::error::Error>> {
let mut builder = S3ServerBuilder::new(self.bucket, global_args.key);
builder.prefix(self.prefix);
if let Some(cdn) = self.cdn {
builder.cdn(cdn);
}
if let Some(cache_dir) = global_args.cache_dir {
let max_cache_size = global_args
.max_cache_size
.into::<human_size::Byte>()
.value() as u64;
builder.cache(Cache::new(cache_dir, max_cache_size));
}
builder.run(addr).await
}
}
impl LocalArgs {
async fn run(
self,
addr: SocketAddr,
global_args: GlobalArgs,
) -> Result<(), Box<dyn std::error::Error>> {
let mut builder = LocalServerBuilder::new(self.path, global_args.key);
if let Some(cache_dir) = global_args.cache_dir {
let max_cache_size = global_args
.max_cache_size
.into::<human_size::Byte>()
.value() as u64;
builder.cache(Cache::new(cache_dir, max_cache_size));
}
builder.run(addr).await
}
}
#[tokio::main]
async fn main() {
let exit_code = if let Err(err) = Args::from_args().main().await {
log::error!("{}", err);
1
} else {
0
};
std::process::exit(exit_code);
}