tikz-gallery-generator

Custum build of stapix for tikz.pablopie.xyz

Commit
e54035f0f5f55ef3a1553a722efd08125c9d8ec6
Parent
33f7b82e03a1701d96d87b6cbeee75bc6fa01b55
Author
Pablo <pablo-pie@riseup.net>
Date

Revamped the logging macros

Reimplemented the logging macros to make them concurrent safe

Also made it so that the macros can read variables from the environment by using the std::format_args! macro

Pulling changes from upstream

Diffstat

2 files changed, 239 insertions, 123 deletions

Status File Name N° Changes Insertions Deletions
Added src/log.rs 203 203 0
Modified src/main.rs 159 36 123
diff --git a/src/log.rs b/src/log.rs
@@ -0,0 +1,203 @@
+//! Macros for logging.
+//!
+//! This implementation should be thread safe (unlink an implementation using
+//! `println!` and `eprintln!`) because access to the global stdout/stderr
+//! handle is syncronized using a lock.
+use crossterm::style::Stylize;
+use std::{io::{self, Write}, fmt::Arguments};
+
+#[derive(Clone, Copy, Debug)]
+pub(crate) enum Level {
+    Error,
+    Info,
+    Warn,
+}
+
+pub(crate) fn log(level: Level, args: &Arguments<'_>, newline: bool) {
+    match level {
+        Level::Error => {
+            let mut stderr = io::stderr();
+            let _ = write!(stderr, "{} ", "[ERROR]".red().bold());
+            let _ = if newline {
+                writeln!(stderr, "{}", args)
+            } else {
+                write!(stderr, "{}", args)
+            };
+            if !newline { let _ = stderr.flush(); }
+        }
+        Level::Info => {
+            let mut stdout = io::stdout().lock();
+            let _ = write!(stdout, "{} ", "[INFO]".green().bold());
+            let _ = if newline {
+                writeln!(stdout, "{}", args)
+            } else {
+                write!(stdout, "{}", args)
+            };
+            if !newline { let _ = stdout.flush(); }
+        }
+        Level::Warn => {
+            let mut stdout = io::stdout().lock();
+            let _ = write!(stdout, "{} ", "[WARNING]".yellow().bold());
+            let _ = if newline {
+                writeln!(stdout, "{}", args)
+            } else {
+                write!(stdout, "{}", args)
+            };
+            if !newline { let _ = stdout.flush(); }
+        }
+    }
+}
+
+#[macro_export]
+macro_rules! info {
+    // info!(key1:? = 42, key2 = true; "a {} event", "log");
+    ($($key:tt $(:$capture:tt)? $(= $value:expr)?),+; $($arg:tt)+) => ({
+        $crate::log::log(
+            $crate::log::Level::Info,
+            &std::format_args!($($arg)+),
+            false,
+        );
+    });
+    // info!("a {} event", "log");
+    ($($arg:tt)+) => ({
+        $crate::log::log(
+            $crate::log::Level::Info,
+            &std::format_args!($($arg)+),
+            false,
+        );
+    });
+}
+
+#[macro_export]
+macro_rules! infoln {
+    // infoln!(key1:? = 42, key2 = true; "a {} event", "log");
+    ($($key:tt $(:$capture:tt)? $(= $value:expr)?),+; $($arg:tt)+) => ({
+        $crate::log::log(
+            $crate::log::Level::Info,
+            &std::format_args!($($arg)+),
+            true,
+        );
+    });
+    // infoln!("a {} event", "log");
+    ($($arg:tt)+) => ({
+        $crate::log::log(
+            $crate::log::Level::Info,
+            &std::format_args!($($arg)+),
+            true,
+        );
+    });
+}
+
+#[macro_export]
+macro_rules! info_done {
+    () => { 
+        let _ = writeln!(io::stdout().lock(), " Done!");
+    };
+}
+
+#[macro_export]
+macro_rules! error {
+    // error!(key1:? = 42, key2 = true; "a {} event", "log");
+    ($($key:tt $(:$capture:tt)? $(= $value:expr)?),+; $($arg:tt)+) => ({
+        $crate::log::log(
+            $crate::log::Level::Error,
+            &std::format_args!($($arg)+),
+            false,
+        );
+    });
+    // info!("a {} event", "log");
+    ($($arg:tt)+) => ({
+        $crate::log::log(
+            $crate::log::Level::Error,
+            &std::format_args!($($arg)+),
+            false,
+        );
+    });
+}
+
+#[macro_export]
+macro_rules! errorln {
+    // errorln!(key1:? = 42, key2 = true; "a {} event", "log");
+    ($($key:tt $(:$capture:tt)? $(= $value:expr)?),+; $($arg:tt)+) => ({
+        $crate::log::log(
+            $crate::log::Level::Error,
+            &std::format_args!($($arg)+),
+            true,
+        );
+    });
+    // errorln!("a {} event", "log");
+    ($($arg:tt)+) => ({
+        $crate::log::log(
+            $crate::log::Level::Error,
+            &std::format_args!($($arg)+),
+            true,
+        );
+    });
+}
+
+#[macro_export]
+macro_rules! warnln {
+    // info!(key1:? = 42, key2 = true; "a {} event", "log");
+    ($($key:tt $(:$capture:tt)? $(= $value:expr)?),+; $($arg:tt)+) => ({
+        $crate::log::log(
+            $crate::log::Level::Warn,
+            &std::format_args!($($arg)+),
+            true,
+        );
+    });
+    // info!("a {} event", "log");
+    ($($arg:tt)+) => ({
+        $crate::log::log(
+            $crate::log::Level::Warn,
+            &std::format_args!($($arg)+),
+            true,
+        );
+    });
+}
+
+#[macro_export]
+macro_rules! usage {
+    ($program:expr) => {
+        let mut stderr = io::stderr();
+        let _ = writeln!(
+            stderr,
+            "{usage_header_msg} {} config.yml",
+            $program,
+            usage_header_msg = "[USAGE]".yellow().bold()
+        );
+    };
+}
+
+#[macro_export]
+macro_rules! usage_config {
+    () => {
+        let mut stderr = io::stderr();
+
+        let _ = writeln!(
+            stderr,
+            "{usage_header_msg} The YAML configuration file should look like this:",
+            usage_header_msg = "[USAGE]".yellow().bold()
+        );
+        let _ = writeln!(
+            stderr,
+            "    - {path_attr} examples/images/grothendieck-riemann-roch.tex
+      {license_attr} PD
+      {author_attr} Alexander Grothendieck
+      {alt_attr} A hand-drawn commutative diagram surrounded by fire and devils carrying forks
+      {caption_attr} The infamous commutative diagram from Gothendieck's 1971 manuscript
+
+    - {path_attr} examples/images/caleb-yau.png
+      {license_attr} CC-BY-SA-2.5
+      {author_attr} Lunch
+      {alt_attr} A convoluted self-intersecting surface in pastel shades of pink and blue
+      {caption_attr} A visual representation of the Calabi-Yau manifold",
+            path_attr = "path:".green(),
+            license_attr = "license:".green(),
+            author_attr = "author:".green(),
+            alt_attr = "alt:".green(),
+            caption_attr = "caption:".green()
+        );
+
+        let _ = stderr.flush();
+    }
+}
diff --git a/src/main.rs b/src/main.rs
@@ -14,6 +14,8 @@ use std::{
 use gallery_entry::{GalleryEntry, FileFormat, LicenseType};
 use threadpool::ThreadPool;
 
+#[macro_use]
+mod log;
 mod gallery_entry;
 
 /// A wrapper for displaying the path for the thumbnail of a given path
@@ -43,98 +45,8 @@ const WEBP_IMAGE_QUALITY: f32 = 90.0;
 /// Target height of the thumbnails
 const THUMB_HEIGHT: u32 = 500;
 
-// TODO: Make it so that all of the logging macros work asyncronously on
-// different threads
-macro_rules! log {
-    ($fmt:literal) => {
-        print!(concat!("{info_header_msg} ", $fmt),
-               info_header_msg = "[INFO]".green().bold());
-        let _ = io::stdout().flush();
-    };
-    ($fmt:literal, $($x:ident = $y:expr),+ $(,)?) => {
-        print!(concat!("{info_header_msg} ", $fmt), $($x = $y),+,
-               info_header_msg = "[INFO]".green().bold());
-        let _ = io::stdout().flush();
-    };
-}
-
-macro_rules! logln {
-    ($fmt:literal) => {
-        println!(concat!("{info_header_msg} ", $fmt),
-               info_header_msg = "[INFO]".green().bold());
-    };
-    ($fmt:literal, $($x:ident = $y:expr),+ $(,)?) => {
-        println!(concat!("{info_header_msg} ", $fmt), $($x = $y),+,
-               info_header_msg = "[INFO]".green().bold());
-    };
-}
-
-macro_rules! log_done {
-    () => { println!(" Done!"); };
-}
-
-macro_rules! errorln {
-    ($fmt:literal) => {
-        eprintln!(concat!("\n{error_header_msg} ", $fmt),
-                  error_header_msg = "[ERROR]".red().bold());
-    };
-    ($fmt:literal, $($x:ident = $y:expr),+ $(,)?) => {
-        eprintln!(concat!("\n{error_header_msg} ", $fmt), $($x = $y),+,
-                  error_header_msg = "[ERROR]".red().bold());
-    };
-    ($fmt:literal; newline = false) => {
-        eprintln!(concat!("{error_header_msg} ", $fmt),
-                  error_header_msg = "[ERROR]".red().bold());
-    };
-    ($fmt:literal, $($x:ident = $y:expr),+ ; newline = false) => {
-        eprintln!(concat!("{error_header_msg} ", $fmt), $($x = $y),+,
-                  error_header_msg = "[ERROR]".red().bold());
-    };
-}
-
-macro_rules! warningln {
-    ($fmt:literal) => {
-        println!(concat!("{warning_header_msg} ", $fmt),
-                 warning_header_msg = "[WARNING]".yellow().bold());
-    };
-    ($fmt:literal, $($x:ident = $y:expr),+ $(,)?) => {
-        println!(concat!("{warning_header_msg} ", $fmt), $($x = $y),+,
-                 warning_header_msg = "[WARNING]".yellow().bold());
-    };
-}
-
-macro_rules! usage {
-    ($program:expr) => {
-        eprintln!("{usage_header_msg} {} config.yml",
-                  $program, usage_header_msg = "[USAGE]".yellow().bold());
-    };
-}
-
-macro_rules! usage_config {
-    () => {
-        eprintln!("{usage_header_msg} The YAML configuration file should look like this:",
-                  usage_header_msg = "[USAGE]".yellow().bold());
-        eprintln!("    - {path_attr} examples/images/grothendieck-riemann-roch.tex
-      {license_attr} PD
-      {author_attr} Alexander Grothendieck
-      {alt_attr} A hand-drawn commutative diagram surrounded by fire and devils carrying forks
-      {caption_attr} The infamous commutative diagram from Gothendieck's 1971 manuscript
-
-    - {path_attr} examples/images/caleb-yau.png
-      {license_attr} CC-BY-SA-2.5
-      {author_attr} Lunch
-      {alt_attr} A convoluted self-intersecting surface in pastel shades of pink and blue
-      {caption_attr} A visual representation of the Calabi-Yau manifold",
-                  path_attr = "path:".green(),
-                  license_attr = "license:".green(),
-                  author_attr = "author:".green(),
-                  alt_attr = "alt:".green(),
-                  caption_attr = "caption:".green());
-    }
-}
-
 fn main() -> ExitCode {
-    logln!("Running {package} version {version}",
+    infoln!("Running {package} version {version}",
            package = env!("CARGO_PKG_NAME"),
            version = env!("CARGO_PKG_VERSION"));
 
@@ -143,14 +55,12 @@ fn main() -> ExitCode {
     let (program, config) = match &args[..] {
         [program, config] => (program, config),
         [program] => {
-            errorln!("Expected 1 command line argument, found none";
-                   newline = false);
+            error!("Expected 1 command line argument, found none");
             usage!(program);
             return ExitCode::FAILURE;
         }
         [program, ..] => {
-            errorln!("Expected 1 command line argument, found many";
-                   newline = false);
+            error!("Expected 1 command line argument, found many");
             usage!(program);
             return ExitCode::FAILURE;
         }
@@ -161,15 +71,13 @@ fn main() -> ExitCode {
     match f.map(serde_yaml::from_reader::<_, Vec<GalleryEntry>>) {
         // Error opening the config file
         Err(err) => {
-            errorln!("Couldn't open {config:?}: {err}",
-                   config = config, err = err; newline = false);
+            error!("Couldn't open {config:?}: {err}");
             usage!(program);
             ExitCode::FAILURE
         }
         // Error parsing the config file
         Ok(Err(err)) => {
-            errorln!("Couldn't parse {config:?}: {err}",
-                   config = config, err = err; newline = false);
+            error!("Couldn't parse {config:?}: {err}");
             usage_config!();
             ExitCode::FAILURE
         }
@@ -179,7 +87,7 @@ fn main() -> ExitCode {
 
 /// Coordinates the rendering of all the pages and file conversions
 fn render_gallery(pics: Vec<GalleryEntry>) -> ExitCode {
-    log!("Copying image files to the target directory...");
+    info!("Copying image files to the target directory...");
 
     for pic in &pics {
         let mut target_path = PathBuf::from(TARGET_PATH);
@@ -187,18 +95,21 @@ fn render_gallery(pics: Vec<GalleryEntry>) -> ExitCode {
         target_path.push(&pic.file_name);
 
         if let Err(err) = fs::copy(&pic.path, &target_path) {
-            errorln!("Couldn't copy file {src:?} to {target:?}: {err}",
-                   src = pic.path, target = target_path, err = err);
+            errorln!(
+                "Couldn't copy file {src:?} to {target:?}: {err}",
+                src = pic.path,
+                target = target_path
+            );
             return ExitCode::FAILURE;
         }
     }
 
-    log_done!();
+    info_done!();
 
     // ========================================================================
     for pic in &pics {
         if pic.alt.is_empty() {
-            warningln!(
+            warnln!(
                 "Empty text alternative was specified for the file {name:?}",
                 name = pic.file_name
             );
@@ -213,7 +124,7 @@ fn render_gallery(pics: Vec<GalleryEntry>) -> ExitCode {
     );
     let (sender, reciever) = mpsc::channel();
 
-    logln!("Started generating thumbnails (using {n} threads)", n = num_threads);
+    infoln!( "Started generating thumbnails (using {num_threads} threads)");
 
     for pic in &pics {
         let sender = sender.clone();
@@ -230,21 +141,21 @@ fn render_gallery(pics: Vec<GalleryEntry>) -> ExitCode {
         }
     }
 
-    logln!("Done generating thumbnails!");
+    infoln!("Done generating thumbnails!");
 
     // ========================================================================
-    log!("Rendering index.html...");
+    info!("Rendering index.html...");
     if render_index(&pics).is_err() {
         return ExitCode::FAILURE;
     }
-    log_done!();
+    info_done!();
 
     for pic in pics {
-        log!("Rendering HTML page for {name:?}...", name = pic.file_name);
+        info!("Rendering HTML page for {name:?}...", name = pic.file_name);
         if render_pic_page(&pic).is_err() {
             return ExitCode::FAILURE;
         }
-        log_done!();
+        info_done!();
     }
 
     ExitCode::SUCCESS
@@ -321,8 +232,7 @@ fn render_pic_page(pic: &GalleryEntry) -> io::Result<()> {
     let mut f = match File::create(&path) {
         Ok(file) => file,
         Err(err) => {
-            errorln!("Could not open file {path:?}: {err}",
-                   path = path, err = err);
+            errorln!("Could not open file {path:?}: {err}");
             return Err(err);
         }
     };
@@ -494,8 +404,11 @@ fn render_thumbnail(pic: GalleryEntry) -> bool {
             .expect("os should support file modification date");
 
         if thumb_mod_date > img_mod_date {
-            warningln!("Skipped generating the thumbnail for {name:?} (update {path:?} to overwrite)",
-                     name = pic.file_name, path = pic.path);
+            warnln!(
+                "Skipped generating the thumbnail for {name:?} (update {path:?} to overwrite)",
+                name = pic.file_name,
+                path = pic.path
+            );
             return true;
         }
     }
@@ -535,7 +448,7 @@ fn render_thumbnail(pic: GalleryEntry) -> bool {
                     return false;
                 }
                 Err(err) => {
-                    errorln!("Failed to run tikztosvg: {err}", err = err);
+                    errorln!("Failed to run tikztosvg: {err}");
                     return false;
                 }
                 _ => {}
@@ -566,7 +479,6 @@ fn render_thumbnail(pic: GalleryEntry) -> bool {
                     "Failed to create symlink {thumb:?} -> {src:?}: {err}",
                     thumb = thumb_path,
                     src = src_path,
-                    err = err
                 );
                 return false;
             }
@@ -575,8 +487,9 @@ fn render_thumbnail(pic: GalleryEntry) -> bool {
             let mut thumb_file = match File::create(&thumb_path) {
                 Ok(f)    => f,
                 Err(err) => {
-                    errorln!("Couldn't open thumbnail file {thumb_path:?}: {err}",
-                           thumb_path = thumb_path, err = err);
+                    errorln!(
+                        "Couldn't open thumbnail file {thumb_path:?}: {err}"
+                    );
                     return false;
                 }
             };
@@ -587,7 +500,6 @@ fn render_thumbnail(pic: GalleryEntry) -> bool {
                     errorln!(
                         "Couldn't open file {path:?} to render thumbnail: {err}",
                         path = pic.file_name,
-                        err = err
                     );
                     return false;
                 }
@@ -599,7 +511,6 @@ fn render_thumbnail(pic: GalleryEntry) -> bool {
                     errorln!(
                         "Faileded to decode image file {name:?}: {err}",
                         name = pic.file_name,
-                        err = err
                     );
                     return false;
                 }
@@ -616,14 +527,16 @@ fn render_thumbnail(pic: GalleryEntry) -> bool {
                 .encode(WEBP_IMAGE_QUALITY);
 
             if let Err(err) = thumb_file.write_all(&mem) {
-                errorln!("Couldn't write thumnail to file {path:?}: {err}",
-                       path = thumb_path, err = err);
+                errorln!(
+                    "Couldn't write thumnail to file {path:?}: {err}",
+                    path = thumb_path
+                );
                 return false;
             }
         }
     }
 
-    logln!("Rendered thumbnail for {name:?}", name = pic.file_name);
+    infoln!("Rendered thumbnail for {name:?}", name = pic.file_name);
     true
 }