Skip to main content

flash_font_ass/
lib.rs

1use std::fs;
2
3use anyhow::{Context, Result};
4use camino::{Utf8Path, Utf8PathBuf};
5use etcetera::{AppStrategy, AppStrategyArgs, choose_app_strategy};
6use flash_font_injector::FontManager;
7use inquire::Text;
8use serde::{Deserialize, Serialize};
9
10use crate::cli::*;
11
12pub mod cli;
13
14/// The name of the SQLite database file for fonts.
15const DB_FILE: &str = "fonts.db";
16
17/// Configuration data for the `flash-font-ass` CLI.
18#[derive(Deserialize, Serialize)]
19struct Config {
20    db_url: String,
21    font_root: Utf8PathBuf,
22}
23
24/// Parses the subtitle file to extract fonts, updates the font database,
25/// and temporarily loads the needed fonts.
26fn load_fonts(config: &Config, ass_path: impl AsRef<Utf8Path>) -> anyhow::Result<()> {
27    let s = ass_font::read_text_auto(ass_path.as_ref())?;
28
29    flash_font::update_font_database(&config.font_root, &config.db_url)?;
30
31    let to_load = ass_font::extract_fonts(&s)
32        .iter()
33        .filter_map(|f| flash_font::select_font_by_name(f, &config.db_url).ok())
34        .filter_map(|v| v.first().cloned())
35        .map(Utf8PathBuf::from)
36        .collect::<Vec<_>>();
37
38    let mut manager = FontManager::default();
39
40    println!("Fonts to load: {:#?}", to_load);
41
42    manager.load_all(to_load)?;
43
44    Ok(())
45}
46
47/// Initializes the configuration file and directory structure.
48fn init(
49    font_root: Utf8PathBuf,
50    strategy: impl AppStrategy,
51    config_file: Utf8PathBuf,
52) -> Result<()> {
53    let data_dir = Utf8PathBuf::try_from(strategy.data_dir())?;
54
55    fs::create_dir_all(
56        config_file
57            .parent()
58            .ok_or_else(|| anyhow::anyhow!("config_file must have a parent"))?,
59    )?;
60    fs::create_dir_all(&data_dir)?;
61
62    let db_path = data_dir.join(DB_FILE);
63    let config = Config {
64        db_url: db_path.into_string(),
65        font_root,
66    };
67
68    let toml = toml::to_string_pretty(&config)?;
69    fs::write(&config_file, toml)?;
70
71    println!("✅ Success! Config file saved to:\n  {}", config_file);
72    Ok(())
73}
74
75fn app_strategy() -> Result<impl AppStrategy> {
76    Ok(choose_app_strategy(AppStrategyArgs {
77        top_level_domain: "org".to_string(),
78        author: "OpenACGN".to_string(),
79        app_name: "Flash Font Ass".to_string(),
80    })?)
81}
82
83/// Main entry point for the CLI operations.
84pub fn run(cli: Cli) -> Result<()> {
85    let strategy = app_strategy()?;
86    let config_dir = Utf8PathBuf::try_from(strategy.config_dir())?;
87    let config_file = config_dir.join("config.toml");
88
89    match cli.command {
90        Commands::Load(args) => {
91            let config_toml = fs::read_to_string(&config_file).with_context(|| {
92                format!(
93                    "❌ Can't find config file: {config_file}\nPlease run `flash-font-ass init` first."
94                )
95            })?;
96
97            let config: Config = toml::from_str(&config_toml)?;
98            load_fonts(&config, &args.subtitle)?;
99        }
100        Commands::Init => {
101            let font_root =
102                Text::new("Please enter the full path to the font root directory:").prompt()?;
103            init(font_root.into(), strategy, config_file)?;
104        }
105    }
106
107    Ok(())
108}