deeplx 2.2.6

A Rust package for unlimited DeepL translation
Documentation
use std::net::SocketAddr;
use std::{future::IntoFuture, sync::Arc};

use deeplx::{Config, DeepLX};
use tokio::sync::watch;
use tower_http::trace::TraceLayer;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};

use crate::server::{biz, conf, data, pkgs::exit::shutdown_signal, routes};
use crate::{Bootstrap, Result, error::Error};

pub fn run(args: Bootstrap) -> Result<()> {
    let manager = conf::Manager::new(args.conf.as_str());
    let config = manager.config();
    let conf::Config {
        debug,
        bind,
        concurrent,
        proxy,
        ..
    } = config
        .read()
        .expect("Config lock poisoned - this is a critical error")
        .clone();

    tracing_subscriber::registry()
        .with(
            tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| {
                format!(
                    "{}={}",
                    env!("CARGO_CRATE_NAME"),
                    if debug { "trace" } else { "info" }
                )
                .into()
            }),
        )
        .with(tracing_subscriber::fmt::layer())
        .init();

    let cpus = std::thread::available_parallelism()?;

    tracing::info!("OS: {}", std::env::consts::OS);
    tracing::info!("Arch: {}", std::env::consts::ARCH);
    tracing::info!("CPUs: {}", cpus);
    tracing::info!("Concurrent: {}", concurrent);

    let runtime = tokio::runtime::Builder::new_multi_thread()
        .enable_all()
        .worker_threads(cpus.into())
        .build()?;

    runtime.block_on(async move {
        let translator = Arc::new(DeepLX::new(Config {
            proxy,
            ..Config::default()
        }));
        let translate_repo = Arc::new(data::translate::TranslateRepo::new(translator));
        let translate_uc = Arc::new(biz::translate::TranslateUsecase::new(translate_repo));
        let state = routes::AppState {
            translate_uc,
            config,
        };
        let app = routes::router(state).layer(TraceLayer::new_for_http());

        let socket = match bind {
            SocketAddr::V4(_) => tokio::net::TcpSocket::new_v4()?,
            SocketAddr::V6(_) => tokio::net::TcpSocket::new_v6()?,
        };

        socket.set_reuseaddr(true)?;
        socket.bind(bind)?;

        let listener = socket.listen(concurrent)?;

        tracing::info!("listening on {}", listener.local_addr()?);

        let (tx, rx) = watch::channel(());
        let tx = Arc::new(tx);

        tokio::pin! {
            let serve_fut = axum::serve(listener, app)
                .with_graceful_shutdown(shutdown_signal(Arc::clone(&tx)))
                .into_future();

            let manager_fut = manager
                .with_watcher(shutdown_signal(Arc::clone(&tx)))
                .into_future();
        }

        tokio::select! {
            _ = &mut serve_fut => {
                drop(rx);
                let _ = &mut manager_fut.await;
            },
            _ = &mut manager_fut => {
                drop(rx);
                let _ = &mut serve_fut.await;
            },
        }

        Ok::<(), Error>(())
    })?;

    Ok(())
}