diff --git a/Cargo.lock b/Cargo.lock index 0550ccf..919e763 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,6 +90,22 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "ctor" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ffc71fcdcdb40d6f087edddf7f8f1f8f79e6cf922f555a9ee8779752d4819bd" +dependencies = [ + "ctor-proc-macro", + "dtor", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" + [[package]] name = "demo_cfg_attr_call" version = "0.4.0" @@ -166,6 +182,21 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" +[[package]] +name = "dtor" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "404d02eeb088a82cfd873006cb713fe411306c7d182c344905e101fb1167d301" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + [[package]] name = "duct" version = "1.1.1" @@ -650,6 +681,7 @@ version = "0.4.0" dependencies = [ "assert_cmd", "braces", + "ctor", "facet", "facet-args", "insta", @@ -666,6 +698,7 @@ name = "syncdoc-core" version = "0.4.0" dependencies = [ "braces", + "ctor", "duct", "insta", "itertools", @@ -684,6 +717,7 @@ name = "syncdoc-migrate" version = "0.4.0" dependencies = [ "braces", + "ctor", "duct", "imara-diff", "insta", diff --git a/syncdoc-core/Cargo.toml b/syncdoc-core/Cargo.toml index c834c1b..b4651b1 100644 --- a/syncdoc-core/Cargo.toml +++ b/syncdoc-core/Cargo.toml @@ -23,6 +23,7 @@ unsynn.workspace = true [dev-dependencies] braces = "0.2.6" +ctor = "0.6.1" duct = "1.1.1" insta.workspace = true itertools = "0.14.0" diff --git a/syncdoc-core/src/debug.rs b/syncdoc-core/src/debug.rs new file mode 100644 index 0000000..6511d6f --- /dev/null +++ b/syncdoc-core/src/debug.rs @@ -0,0 +1,46 @@ +//! Debug printer control for syncdoc. +//! +//! Provides a thread-safe atomic flag for debug logging via STDERR and a function +//! to enable it programmatically (runs automatically if compiled in `cfg(test)`). + +use std::env; +use std::sync::atomic::{AtomicBool, Ordering}; + +/// Atomic flag indicating whether debug output is enabled. +static DEBUG_ENABLED: AtomicBool = AtomicBool::new(false); + +/// Initialise the debug atomic from the `SYNCDOC_DEBUG` environment variable. +/// +/// - Treats `"0"`, `"false"`, `"no"`, `"off"` as false. +/// - Any other value is true. +/// - If the variable is unset, defaults to true for tests, false otherwise. +pub fn init_from_env() { + let enabled = match env::var("SYNCDOC_DEBUG") { + Ok(val) => { + let val = val.trim(); + !(val == "0" + || val.eq_ignore_ascii_case("false") + || val.eq_ignore_ascii_case("no") + || val.eq_ignore_ascii_case("off")) + } + Err(_) => cfg!(test), + }; + set_debug(enabled); +} + +/// Enable or disable debug output programmatically. +pub fn set_debug(enabled: bool) { + DEBUG_ENABLED.store(enabled, Ordering::Relaxed); +} + +/// Check whether debug output is enabled. +pub fn is_enabled() -> bool { + DEBUG_ENABLED.load(Ordering::Relaxed) +} + +/// Automatically enable debug output for tests, respecting the env var. +#[cfg(test)] +#[ctor::ctor] +fn init_debug_for_tests() { + init_from_env(); +} diff --git a/syncdoc-core/src/lib.rs b/syncdoc-core/src/lib.rs index 78bed32..58d3c2a 100644 --- a/syncdoc-core/src/lib.rs +++ b/syncdoc-core/src/lib.rs @@ -1,5 +1,6 @@ /// syncdoc-core: documentation injection helper macros pub mod config; +pub mod debug; mod doc_injector; mod omnibus; pub mod parse; @@ -9,10 +10,14 @@ pub mod token_processors; pub use doc_injector::{module_doc_impl, omnidoc_impl}; pub use omnibus::inject_all_docs_impl; +/// Macro for debug output in syncdoc. +/// +/// Prints to stderr only if debug output is enabled via the atomic flag (tests do this using ctor) +/// or the `SYNCDOC_DEBUG` environment variable at startup. #[macro_export] macro_rules! syncdoc_debug { ($($arg:tt)*) => { - if std::env::var("SYNCDOC_DEBUG").is_ok() { + if $crate::debug::is_enabled() { eprintln!("[SYNCDOC DEBUG] {}", format!($($arg)*)); } }; diff --git a/syncdoc-core/tests/helpers.rs b/syncdoc-core/tests/helpers.rs index 2ac0b06..0fa3092 100644 --- a/syncdoc-core/tests/helpers.rs +++ b/syncdoc-core/tests/helpers.rs @@ -11,6 +11,11 @@ use std::path::PathBuf; use std::process::Command; use tempfile::TempDir; +#[ctor::ctor] +fn init_debug() { + syncdoc_core::debug::init_from_env(); +} + pub struct TestCrate { _temp_dir: TempDir, root: PathBuf, diff --git a/syncdoc-migrate/Cargo.toml b/syncdoc-migrate/Cargo.toml index 5d9e298..cf09867 100644 --- a/syncdoc-migrate/Cargo.toml +++ b/syncdoc-migrate/Cargo.toml @@ -22,6 +22,7 @@ unsynn.workspace = true [dev-dependencies] braces = "0.2.1" +ctor = "0.6.1" insta.workspace = true rust-format.workspace = true tempfile.workspace = true diff --git a/syncdoc-migrate/src/lib.rs b/syncdoc-migrate/src/lib.rs index 1aabe47..433936b 100644 --- a/syncdoc-migrate/src/lib.rs +++ b/syncdoc-migrate/src/lib.rs @@ -8,6 +8,9 @@ pub mod restore; pub mod rewrite; pub mod write; +// Re-export core's macro +pub use syncdoc_core::syncdoc_debug; + pub use config::DocsPathMode; pub use discover::{discover_rust_files, get_or_create_docs_path, parse_file, ParsedFile}; pub use extract::{extract_doc_content, has_doc_attrs}; @@ -17,14 +20,5 @@ pub use write::{ extract_all_docs, find_expected_doc_paths, write_extractions, DocExtraction, WriteReport, }; -#[macro_export] -macro_rules! syncdoc_debug { - ($($arg:tt)*) => { - if std::env::var("SYNCDOC_DEBUG").is_ok() { - eprintln!("[SYNCDOC DEBUG] {}", format!($($arg)*)); - } - }; -} - #[cfg(test)] mod tests; diff --git a/syncdoc-migrate/src/tests/mod.rs b/syncdoc-migrate/src/tests/mod.rs index d85f644..de17885 100644 --- a/syncdoc-migrate/src/tests/mod.rs +++ b/syncdoc-migrate/src/tests/mod.rs @@ -9,3 +9,9 @@ mod restore; mod rewrite; mod strip; mod write; + +/// Automatically enable debug output for all tests +#[ctor::ctor] +fn init_debug() { + syncdoc_core::debug::set_debug(true); +} diff --git a/syncdoc-migrate/tests/helpers.rs b/syncdoc-migrate/tests/helpers.rs index 533572e..f8bf53a 100644 --- a/syncdoc-migrate/tests/helpers.rs +++ b/syncdoc-migrate/tests/helpers.rs @@ -10,6 +10,11 @@ use syncdoc_migrate::{ }; use tempfile::TempDir; +#[ctor::ctor] +fn init_debug() { + syncdoc_core::debug::set_debug(true); +} + pub fn setup_test_file(source: &str, filename: &str) -> (TempDir, PathBuf) { let temp_dir = TempDir::new().unwrap(); let file_path = temp_dir.path().join(filename); diff --git a/syncdoc/Cargo.toml b/syncdoc/Cargo.toml index 96fb3d3..d16c7f0 100644 --- a/syncdoc/Cargo.toml +++ b/syncdoc/Cargo.toml @@ -47,6 +47,7 @@ syncdoc-migrate = { optional = true, workspace = true } [dev-dependencies] assert_cmd.workspace = true braces = "0.2.6" +ctor = "0.6.1" insta.workspace = true regex = "1.12.2" rust-format.workspace = true diff --git a/syncdoc/tests/cli_integration.rs b/syncdoc/tests/cli_integration.rs index 22381c5..b170455 100644 --- a/syncdoc/tests/cli_integration.rs +++ b/syncdoc/tests/cli_integration.rs @@ -6,6 +6,11 @@ use std::fs; use std::path::Path; use tempfile::TempDir; +#[ctor::ctor] +fn init_debug() { + syncdoc_core::debug::set_debug(true); +} + fn to_braces(paths: &[&str]) -> String { let braces_config = BraceConfig::default(); brace_paths(paths, &braces_config).expect("Brace error") diff --git a/syncdoc/tests/roundtrip/helpers.rs b/syncdoc/tests/roundtrip/helpers.rs index 628b0f7..6bb4510 100644 --- a/syncdoc/tests/roundtrip/helpers.rs +++ b/syncdoc/tests/roundtrip/helpers.rs @@ -7,6 +7,11 @@ use std::path::{Path, PathBuf}; use std::process::Command; use tempfile::TempDir; +#[ctor::ctor] +fn init_debug() { + syncdoc_core::debug::set_debug(true); +} + /// Module configuration for test setup #[derive(Debug, Clone)] pub struct ModuleConfig {