Skip to main content

scrybe_cli/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 Shawn Hartsock and contributors
3
4//! Scrybe CLI — library of extracted, testable logic.
5//!
6//! `main.rs` contains the thin clap shell; the real work lives here so it
7//! can be unit-tested without spawning a process.
8
9pub mod lint;
10pub mod rpc_client;
11pub mod wrap;
12
13// Re-export the primary public API for convenience.
14pub use lint::{lint_document, BrokenLink, LintReport};
15pub use wrap::wrap_full_html;
16
17// ---------------------------------------------------------------------------
18// Version info
19// ---------------------------------------------------------------------------
20
21/// Returns the crate version string.
22pub fn version_string() -> &'static str {
23    env!("CARGO_PKG_VERSION")
24}
25
26/// Returns a list of active Cargo feature flags (as a static string).
27pub fn active_features() -> &'static str {
28    "(no optional features)"
29}
30
31// ---------------------------------------------------------------------------
32// Tests
33// ---------------------------------------------------------------------------
34
35#[cfg(test)]
36mod tests {
37    use super::lint::lint_document;
38    use super::wrap::wrap_full_html;
39    use scrybe_core::Document;
40    use scrybe_render::{render_html, Theme};
41
42    #[test]
43    fn test_lint_word_count() {
44        let doc = Document::new("Hello world foo bar");
45        let r = lint_document(&doc);
46        assert_eq!(r.word_count, 4);
47    }
48
49    #[test]
50    fn test_lint_headings() {
51        let doc = Document::new("# H1\n\n## H2\n\n### H3\n");
52        let r = lint_document(&doc);
53        assert_eq!(r.heading_count, 3);
54        assert_eq!(r.max_heading_depth, 3);
55    }
56
57    #[test]
58    fn test_lint_broken_links() {
59        let doc = Document::new("[empty]() and [hash](#) and [ok](https://example.com)");
60        let r = lint_document(&doc);
61        assert_eq!(r.broken_links.len(), 2);
62    }
63
64    #[test]
65    fn test_lint_code_blocks() {
66        let doc = Document::new("```rust\nfn main(){}\n```\n\n```python\npass\n```");
67        let r = lint_document(&doc);
68        assert_eq!(r.code_block_count, 2);
69        assert!(r.code_block_langs.contains(&"rust".to_string()));
70    }
71
72    #[test]
73    fn test_wrap_full_html_has_doctype() {
74        let doc = Document::new("# Hi");
75        let out = render_html(&doc, Theme::Default);
76        let html = wrap_full_html(&out, "Test");
77        assert!(html.starts_with("<!DOCTYPE html>"));
78        assert!(html.contains("katex"));
79        assert!(html.contains("mermaid"));
80    }
81
82    #[test]
83    fn test_lint_detects_mermaid() {
84        let doc = Document::new("```mermaid\ngraph TD; A-->B;\n```\n");
85        let r = lint_document(&doc);
86        assert!(r.has_mermaid);
87        assert!(!r.code_block_langs.contains(&"mermaid".to_string()));
88    }
89
90    #[test]
91    fn test_lint_detects_math() {
92        let doc = Document::new("Here is $x^2$.\n");
93        let r = lint_document(&doc);
94        assert!(r.has_math);
95    }
96
97    #[test]
98    fn test_lint_clean_document() {
99        let doc = Document::new("# Title\n\nSome [link](https://example.com).\n");
100        let r = lint_document(&doc);
101        assert!(r.is_clean());
102    }
103}