mirror of
https://github.com/lise-henry/crowbook
synced 2024-05-05 11:56:10 +02:00
cd1739382b
Mainly `indicatif` and `clap` needed work
277 lines
8.2 KiB
Rust
277 lines
8.2 KiB
Rust
// Copyright (C) 2016-2022 Élisabeth HENRY.
|
|
//
|
|
// This file is part of Crowbook.
|
|
//
|
|
// Crowbook is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published
|
|
// by the Free Software Foundation, either version 2.1 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Crowbook is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with Crowbook. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
use crate::helpers::*;
|
|
|
|
use clap::ArgMatches;
|
|
|
|
use crowbook::Stats;
|
|
use crowbook::{Book, BookOptions, Result};
|
|
use crowbook_intl_runtime::set_lang;
|
|
use simplelog::{ConfigBuilder, LevelFilter, SimpleLogger, TermLogger, WriteLogger};
|
|
use std::env;
|
|
use std::fs::File;
|
|
use std::io;
|
|
use std::io::Read;
|
|
use std::process::exit;
|
|
use tempdir::TempDir;
|
|
use yaml_rust::Yaml;
|
|
|
|
/// Render a book to specific format
|
|
fn render_format(book: &mut Book, emoji: bool, matches: &ArgMatches, format: &str) {
|
|
let mut key = String::from("output.");
|
|
key.push_str(format);
|
|
|
|
let mut stdout = false;
|
|
let mut file = None;
|
|
|
|
if let Some(f) = matches.get_one::<String>("output") {
|
|
if f.as_str() == "-" {
|
|
stdout = true;
|
|
} else {
|
|
file = Some(String::from(f));
|
|
}
|
|
}
|
|
|
|
let res = book.options.get_path(&key);
|
|
|
|
let result = match (file, res, stdout) {
|
|
(Some(file), _, _) | (None, Ok(file), false) => book.render_format_to_file(format, file),
|
|
|
|
(None, Err(_), _) | (None, _, true) => book.render_format_to(format, &mut io::stdout()),
|
|
};
|
|
|
|
if let Err(err) = result {
|
|
print_error(&format!("{}", err), emoji)
|
|
}
|
|
}
|
|
|
|
pub fn try_main() -> Result<()> {
|
|
let lang = get_lang().or_else(|| match env::var("LANG") {
|
|
Ok(val) => Some(val),
|
|
Err(_) => None,
|
|
});
|
|
if let Some(val) = lang {
|
|
if val.starts_with("fr") {
|
|
set_lang("fr");
|
|
} else {
|
|
set_lang("en");
|
|
}
|
|
}
|
|
|
|
let mut fancy_ui = true;
|
|
let mut emoji = console::Term::stderr().features().wants_emoji();
|
|
|
|
let matches = create_matches();
|
|
|
|
if matches.get_flag("force-emoji") {
|
|
emoji = true;
|
|
}
|
|
|
|
if !matches.get_flag("quiet") {
|
|
display_header(emoji);
|
|
}
|
|
|
|
if matches.get_flag("list-options") {
|
|
println!("{}", BookOptions::description(false));
|
|
exit(0);
|
|
}
|
|
|
|
if matches.get_flag("no-fancy") || matches.get_flag("stats") {
|
|
fancy_ui = false;
|
|
emoji = false;
|
|
}
|
|
|
|
if matches.get_flag("list-options-md") {
|
|
println!("{}", BookOptions::description(true));
|
|
exit(0);
|
|
}
|
|
|
|
if let Some(template) = matches.get_one::<String>("print-template") {
|
|
let mut book = Book::new();
|
|
set_book_options(&mut book, &matches);
|
|
let result = book.get_template(template.as_ref());
|
|
match result {
|
|
Ok(s) => {
|
|
println!("{}", s);
|
|
exit(0);
|
|
}
|
|
Err(_) => print_error_and_exit(
|
|
&lformat!("{} is not a valid template name.", template),
|
|
emoji,
|
|
),
|
|
}
|
|
}
|
|
|
|
if matches.get_many::<String>("files").is_some() {
|
|
create_book(&matches);
|
|
}
|
|
let book = matches.get_one::<String>("BOOK");
|
|
if book.is_none() {
|
|
print_error_and_exit(
|
|
&lformat!(
|
|
"You must pass the file of a book configuration \
|
|
file.\nFor more information try --help."
|
|
),
|
|
emoji,
|
|
);
|
|
}
|
|
|
|
// ok to unwrap since clap checks it's there
|
|
let &s = book.as_ref().unwrap();
|
|
|
|
// Initalize logger
|
|
let mut builder = ConfigBuilder::new();
|
|
builder.set_target_level(LevelFilter::Off);
|
|
builder.set_location_level(LevelFilter::Off);
|
|
builder.set_time_level(LevelFilter::Off);
|
|
let verbosity = if matches.get_flag("verbose") && !matches.get_flag("stats") {
|
|
builder.set_time_level(LevelFilter::Error);
|
|
builder.set_target_level(LevelFilter::Error);
|
|
fancy_ui = false;
|
|
LevelFilter::Debug
|
|
} else if matches.get_flag("quiet") {
|
|
fancy_ui = false;
|
|
LevelFilter::Error
|
|
} else if fancy_ui {
|
|
LevelFilter::Warn
|
|
} else {
|
|
LevelFilter::Info
|
|
};
|
|
let log_config = builder.build();
|
|
|
|
let error_dir = TempDir::new("crowbook").unwrap();
|
|
let error_path = "error.log";
|
|
if fancy_ui {
|
|
let errors = File::create(error_dir.path().join(error_path)).unwrap();
|
|
let _ = WriteLogger::init(verbosity, log_config, errors);
|
|
} else if TermLogger::init(
|
|
verbosity,
|
|
log_config.clone(),
|
|
simplelog::TerminalMode::Stderr,
|
|
simplelog::ColorChoice::Auto,
|
|
)
|
|
.is_err()
|
|
{
|
|
// If it failed, not much we can do, we just won't display log
|
|
let _ = SimpleLogger::init(verbosity, log_config);
|
|
}
|
|
|
|
{
|
|
let mut book = Book::new();
|
|
if matches.get_flag("autograph") {
|
|
println!("{}", &lformat!("Enter autograph: "));
|
|
let mut autograph = String::new();
|
|
match io::stdin().read_to_string(&mut autograph) {
|
|
Ok(_) => {
|
|
book.options
|
|
.set_yaml(
|
|
Yaml::String("autograph".to_string()),
|
|
Yaml::String(autograph),
|
|
)
|
|
.unwrap();
|
|
}
|
|
Err(_) => print_error(&lformat!("could not read autograph from stdin"), emoji),
|
|
}
|
|
}
|
|
|
|
if fancy_ui {
|
|
book.add_progress_bar(emoji);
|
|
}
|
|
book.set_options(&get_book_options(&matches));
|
|
|
|
{
|
|
let res = if matches.get_flag("single") {
|
|
if s != "-" {
|
|
book.load_markdown_file(s)
|
|
} else {
|
|
book.read_markdown_config(io::stdin())
|
|
}
|
|
} else if s != "-" {
|
|
book.load_file(s)
|
|
} else {
|
|
book.read_config(io::stdin())
|
|
}
|
|
.map(|_| ());
|
|
|
|
match res {
|
|
Ok(..) => {}
|
|
Err(err) => {
|
|
book.set_error(&format!("{}", err));
|
|
return Err(err);
|
|
}
|
|
}
|
|
}
|
|
|
|
set_book_options(&mut book, &matches);
|
|
|
|
if matches.get_flag("stats") {
|
|
let stats = Stats::new(&book, matches.get_flag("verbose"));
|
|
println!("{}", stats);
|
|
exit(0);
|
|
}
|
|
|
|
if let Some(format) = matches.get_one::<String>("to") {
|
|
render_format(&mut book, emoji, &matches, format);
|
|
} else {
|
|
book.render_all();
|
|
}
|
|
}
|
|
if fancy_ui {
|
|
let mut errors = String::new();
|
|
let mut file = File::open(error_dir.path().join(error_path)).unwrap();
|
|
file.read_to_string(&mut errors).unwrap();
|
|
if !errors.is_empty() {
|
|
print_warning(
|
|
&lformat!("Crowbook exited successfully, but the following errors occurred:"),
|
|
emoji,
|
|
);
|
|
// Non-efficient dedup algorithm but we need to keep the order
|
|
let mut lines: Vec<String> = vec![];
|
|
for line in errors.lines() {
|
|
let mut contains = false;
|
|
for l in &lines {
|
|
if l == line {
|
|
contains = true;
|
|
break;
|
|
}
|
|
}
|
|
if !contains {
|
|
lines.push(line.to_string());
|
|
}
|
|
}
|
|
for line in &lines {
|
|
if line.starts_with("[ERROR]") {
|
|
let line = &line[8..];
|
|
print_error(line, emoji);
|
|
} else if line.starts_with("[WARN]") {
|
|
let line = &line[7..];
|
|
print_warning(line, emoji);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn real_main() {
|
|
if let Err(err) = try_main() {
|
|
print_error_and_exit(&format!("{}", err), false);
|
|
}
|
|
}
|