pub mod bookshelf;
pub mod build;
pub mod check;
pub mod completions;
pub mod download;
pub mod info;
pub mod read;
pub mod real_cugan;
pub mod search;
pub mod sign;
pub mod template;
pub mod transform;
pub mod unzip;
pub mod update;
pub mod zip;
use std::{collections::HashMap, path::Path, process, sync::Arc};
use clap::ValueEnum;
use fluent_templates::Loader;
use novel_api::Client;
use ratatui::{
buffer::Buffer,
layout::{Rect, Size},
text::Text,
widgets::{block::Title, Block, Paragraph, StatefulWidget, Widget, Wrap},
};
use strum::AsRefStr;
use tokio::signal;
use tracing::warn;
use tui_scrollview::{ScrollView, ScrollViewState};
use url::Url;
use crate::{LANG_ID, LOCALES};
const DEFAULT_PROXY: &str = "http://127.0.0.1:8080";
#[must_use]
#[derive(Clone, PartialEq, ValueEnum, AsRefStr)]
pub enum Source {
#[strum(serialize = "sfacg")]
Sfacg,
#[strum(serialize = "ciweimao")]
Ciweimao,
#[strum(serialize = "ciyuanji")]
Ciyuanji,
}
#[must_use]
#[derive(Clone, PartialEq, ValueEnum, AsRefStr)]
pub enum Format {
Pandoc,
Mdbook,
}
#[must_use]
#[derive(Clone, PartialEq, ValueEnum, AsRefStr)]
pub enum Convert {
S2T,
T2S,
JP2T2S,
CUSTOM,
}
#[inline]
#[must_use]
fn default_cert_path() -> String {
novel_api::home_dir_path()
.unwrap()
.join(".mitmproxy")
.join("mitmproxy-ca-cert.pem")
.display()
.to_string()
}
fn set_options<T, E>(client: &mut T, proxy: &Option<Url>, no_proxy: &bool, cert: &Option<E>)
where
T: Client,
E: AsRef<Path>,
{
if let Some(proxy) = proxy {
client.proxy(proxy.clone());
}
if *no_proxy {
client.no_proxy();
}
if let Some(cert) = cert {
client.cert(cert.as_ref().to_path_buf())
}
}
fn handle_ctrl_c<T>(client: &Arc<T>)
where
T: Client + Send + Sync + 'static,
{
let client = Arc::clone(client);
tokio::spawn(async move {
signal::ctrl_c().await.unwrap();
warn!("Download terminated, login data will be saved");
client.shutdown().await.unwrap();
process::exit(128 + libc::SIGINT);
});
}
fn cert_help_msg() -> String {
let args = {
let mut map = HashMap::new();
map.insert(String::from("cert_path"), default_cert_path().into());
map
};
LOCALES.lookup_with_args(&LANG_ID, "cert", &args)
}
#[derive(Default, PartialEq)]
enum Mode {
#[default]
Running,
Quit,
}
pub struct ScrollableParagraph<'a> {
title: Option<Title<'a>>,
text: Text<'a>,
}
impl<'a> ScrollableParagraph<'a> {
pub fn new<T>(text: T) -> Self
where
T: Into<Text<'a>>,
{
ScrollableParagraph {
title: None,
text: text.into(),
}
}
pub fn title<T>(self, title: T) -> Self
where
T: Into<Title<'a>>,
{
ScrollableParagraph {
title: Some(title.into()),
..self
}
}
}
impl<'a> StatefulWidget for ScrollableParagraph<'a> {
type State = ScrollViewState;
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
let mut block = Block::bordered();
if self.title.is_some() {
block = block.title(self.title.as_ref().unwrap().clone());
}
let paragraph = Paragraph::new(self.text.clone()).wrap(Wrap { trim: false });
let mut scroll_view = ScrollView::new(Size::new(area.width - 1, 100));
let mut block_area = block.inner(scroll_view.buf().area);
let scroll_height = paragraph.line_count(block_area.width) as u16
+ (scroll_view.buf().area.height - block_area.height);
let scroll_width = if area.height >= scroll_height {
area.width
} else {
area.width - 1
};
scroll_view = ScrollView::new(Size::new(scroll_width, scroll_height));
let scroll_view_buf = scroll_view.buf_mut();
block_area = block.inner(scroll_view_buf.area);
Widget::render(block, scroll_view_buf.area, scroll_view_buf);
Widget::render(paragraph, block_area, scroll_view_buf);
StatefulWidget::render(scroll_view, area, buf, state);
}
}