about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2021-04-30T20·39+0200
committertazjin <mail@tazj.in>2021-05-03T13·57+0000
commitd311af9bc059253a4b7adf41027a60b141c6fc53 (patch)
tree830d328103de70bc98ba69567b4a1630a61d8495
parent713f06629757c507607cf5dc88399a84d54f2469 (diff)
refactor(cheddar): Split out a library with rendering logic r/2556
Splits `main.rs` into `lib.rs` and `bin/cheddar.rs`, which enables
reuse of cheddar's rendering logic in other Rust applications.

Change-Id: Ifd1a44a8d1620c595550a0a497a25b0563e917ca
Reviewed-on: https://cl.tvl.fyi/c/depot/+/3060
Tested-by: BuildkiteCI
Reviewed-by: flokli <flokli@flokli.de>
-rw-r--r--tools/cheddar/src/bin/cheddar.rs135
-rw-r--r--tools/cheddar/src/lib.rs (renamed from tools/cheddar/src/main.rs)144
2 files changed, 148 insertions, 131 deletions
diff --git a/tools/cheddar/src/bin/cheddar.rs b/tools/cheddar/src/bin/cheddar.rs
new file mode 100644
index 0000000000..58ef32a1b4
--- /dev/null
+++ b/tools/cheddar/src/bin/cheddar.rs
@@ -0,0 +1,135 @@
+//! This file defines the binary for cheddar, which can be interacted
+//! with in two different ways:
+//!
+//! 1. As a CLI tool that acts as a cgit filter.
+//! 2. As a long-running HTTP server that handles rendering requests
+//!    (matching the SourceGraph protocol).
+use clap::{App, Arg};
+use rouille::Response;
+use rouille::{router, try_or_400};
+use serde::Deserialize;
+use serde_json::json;
+use std::collections::HashMap;
+use std::io;
+
+use cheddar::{THEMES, format_code, format_markdown};
+
+// Server endpoint for rendering the syntax of source code. This
+// replaces the 'syntect_server' component of Sourcegraph.
+fn code_endpoint(request: &rouille::Request) -> rouille::Response {
+    #[derive(Deserialize)]
+    struct SourcegraphQuery {
+        filepath: String,
+        theme: String,
+        code: String,
+    }
+
+    let query: SourcegraphQuery = try_or_400!(rouille::input::json_input(request));
+    let mut buf: Vec<u8> = Vec::new();
+
+    // We don't use syntect with the sourcegraph themes bundled
+    // currently, so let's fall back to something that is kind of
+    // similar (tm).
+    let theme = &THEMES.themes[match query.theme.as_str() {
+        "Sourcegraph (light)" => "Solarized (light)",
+        _ => "Solarized (dark)",
+    }];
+
+    format_code(theme, &mut query.code.as_bytes(), &mut buf, &query.filepath);
+
+    Response::json(&json!({
+        "is_plaintext": false,
+        "data": String::from_utf8_lossy(&buf)
+    }))
+}
+
+// Server endpoint for rendering a Markdown file.
+fn markdown_endpoint(request: &rouille::Request) -> rouille::Response {
+    let mut texts: HashMap<String, String> = try_or_400!(rouille::input::json_input(request));
+
+    for text in texts.values_mut() {
+        let mut buf: Vec<u8> = Vec::new();
+        format_markdown(&mut text.as_bytes(), &mut buf);
+        *text = String::from_utf8_lossy(&buf).to_string();
+    }
+
+    Response::json(&texts)
+}
+
+fn highlighting_server(listen: &str) {
+    println!("Starting syntax highlighting server on '{}'", listen);
+
+    rouille::start_server(listen, move |request| {
+        router!(request,
+                // Markdown rendering route
+                (POST) (/markdown) => {
+                    markdown_endpoint(request)
+                },
+
+                // Code rendering route
+                (POST) (/) => {
+                    code_endpoint(request)
+                },
+
+                _ => {
+                    rouille::Response::empty_404()
+                },
+        )
+    });
+}
+
+fn main() {
+    // Parse the command-line flags passed to cheddar to determine
+    // whether it is running in about-filter mode (`--about-filter`)
+    // and what file extension has been supplied.
+    let matches = App::new("cheddar")
+        .about("TVL's syntax highlighter")
+        .arg(
+            Arg::with_name("about-filter")
+                .help("Run as a cgit about-filter (renders Markdown)")
+                .long("about-filter")
+                .takes_value(false),
+        )
+        .arg(
+            Arg::with_name("sourcegraph-server")
+                .help("Run as a Sourcegraph compatible web-server")
+                .long("sourcegraph-server")
+                .takes_value(false),
+        )
+        .arg(
+            Arg::with_name("listen")
+                .help("Address to listen on")
+                .long("listen")
+                .takes_value(true),
+        )
+        .arg(Arg::with_name("filename").help("File to render").index(1))
+        .get_matches();
+
+    if matches.is_present("sourcegraph-server") {
+        highlighting_server(
+            matches
+                .value_of("listen")
+                .expect("Listening address is required for server mode"),
+        );
+        return;
+    }
+
+    let filename = matches.value_of("filename").expect("filename is required");
+
+    let stdin = io::stdin();
+    let mut in_handle = stdin.lock();
+
+    let stdout = io::stdout();
+    let mut out_handle = stdout.lock();
+
+    if matches.is_present("about-filter") && filename.ends_with(".md") {
+        format_markdown(&mut in_handle, &mut out_handle);
+    } else {
+        format_code(
+            &THEMES.themes["InspiredGitHub"],
+            &mut in_handle,
+            &mut out_handle,
+            filename,
+        );
+    }
+}
diff --git a/tools/cheddar/src/main.rs b/tools/cheddar/src/lib.rs
index 9f6d1a25db..5e88aaaf61 100644
--- a/tools/cheddar/src/main.rs
+++ b/tools/cheddar/src/lib.rs
@@ -1,12 +1,10 @@
-use clap::{App, Arg};
+//! This file implements the rendering logic of cheddar with public
+//! functions for syntax-highlighting code and for turning Markdown
+//! into HTML with TVL extensions.
 use comrak::arena_tree::Node;
 use comrak::nodes::{Ast, AstNode, NodeCodeBlock, NodeHtmlBlock, NodeValue};
 use comrak::{format_html, parse_document, Arena, ComrakOptions};
 use lazy_static::lazy_static;
-use rouille::Response;
-use rouille::{router, try_or_400};
-use serde::Deserialize;
-use serde_json::json;
 use std::cell::RefCell;
 use std::collections::HashMap;
 use std::env;
@@ -29,11 +27,15 @@ use syntect::html::{
 mod tests;
 
 lazy_static! {
-    // Load syntaxes & themes lazily. Initialisation might not be
-    // required in the case of Markdown rendering (if there's no code
-    // blocks within the document).
+    // Load syntaxes lazily. Initialisation might not be required in
+    // the case of Markdown rendering (if there's no code blocks
+    // within the document).
+    //
+    // Note that the syntax set is included from the path pointed to
+    // by the BAT_SYNTAXES environment variable at compile time. This
+    // variable is populated by Nix and points to TVL's syntax set.
     static ref SYNTAXES: SyntaxSet = from_binary(include_bytes!(env!("BAT_SYNTAXES")));
-    static ref THEMES: ThemeSet = ThemeSet::load_defaults();
+    pub static ref THEMES: ThemeSet = ThemeSet::load_defaults();
 
     // Configure Comrak's Markdown rendering with all the bells &
     // whistles!
@@ -183,7 +185,7 @@ fn format_callout_paragraph(callout: Callout) -> NodeValue {
     NodeValue::HtmlBlock(block)
 }
 
-fn format_markdown<R: BufRead, W: Write>(reader: &mut R, writer: &mut W) {
+pub fn format_markdown<R: BufRead, W: Write>(reader: &mut R, writer: &mut W) {
     let document = {
         let mut buffer = String::new();
         reader
@@ -245,7 +247,7 @@ fn find_syntax_for_file(filename: &str) -> &'static SyntaxReference {
         .unwrap_or_else(|| SYNTAXES.find_syntax_plain_text())
 }
 
-fn format_code<R: BufRead, W: Write>(
+pub fn format_code<R: BufRead, W: Write>(
     theme: &Theme,
     reader: &mut R,
     writer: &mut W,
@@ -287,123 +289,3 @@ fn format_code<R: BufRead, W: Write>(
 
     writeln!(writer, "</pre>").expect("write should not fail");
 }
-
-// Server endpoint for rendering the syntax of source code. This
-// replaces the 'syntect_server' component of Sourcegraph.
-fn code_endpoint(request: &rouille::Request) -> rouille::Response {
-    #[derive(Deserialize)]
-    struct SourcegraphQuery {
-        filepath: String,
-        theme: String,
-        code: String,
-    }
-
-    let query: SourcegraphQuery = try_or_400!(rouille::input::json_input(request));
-    let mut buf: Vec<u8> = Vec::new();
-
-    // We don't use syntect with the sourcegraph themes bundled
-    // currently, so let's fall back to something that is kind of
-    // similar (tm).
-    let theme = &THEMES.themes[match query.theme.as_str() {
-        "Sourcegraph (light)" => "Solarized (light)",
-        _ => "Solarized (dark)",
-    }];
-
-    format_code(theme, &mut query.code.as_bytes(), &mut buf, &query.filepath);
-
-    Response::json(&json!({
-        "is_plaintext": false,
-        "data": String::from_utf8_lossy(&buf)
-    }))
-}
-
-// Server endpoint for rendering a Markdown file.
-fn markdown_endpoint(request: &rouille::Request) -> rouille::Response {
-    let mut texts: HashMap<String, String> = try_or_400!(rouille::input::json_input(request));
-
-    for text in texts.values_mut() {
-        let mut buf: Vec<u8> = Vec::new();
-        format_markdown(&mut text.as_bytes(), &mut buf);
-        *text = String::from_utf8_lossy(&buf).to_string();
-    }
-
-    Response::json(&texts)
-}
-
-fn highlighting_server(listen: &str) {
-    println!("Starting syntax highlighting server on '{}'", listen);
-
-    rouille::start_server(listen, move |request| {
-        router!(request,
-                // Markdown rendering route
-                (POST) (/markdown) => {
-                    markdown_endpoint(request)
-                },
-
-                // Code rendering route
-                (POST) (/) => {
-                    code_endpoint(request)
-                },
-
-                _ => {
-                    rouille::Response::empty_404()
-                },
-        )
-    });
-}
-
-fn main() {
-    // Parse the command-line flags passed to cheddar to determine
-    // whether it is running in about-filter mode (`--about-filter`)
-    // and what file extension has been supplied.
-    let matches = App::new("cheddar")
-        .about("TVL's syntax highlighter")
-        .arg(
-            Arg::with_name("about-filter")
-                .help("Run as a cgit about-filter (renders Markdown)")
-                .long("about-filter")
-                .takes_value(false),
-        )
-        .arg(
-            Arg::with_name("sourcegraph-server")
-                .help("Run as a Sourcegraph compatible web-server")
-                .long("sourcegraph-server")
-                .takes_value(false),
-        )
-        .arg(
-            Arg::with_name("listen")
-                .help("Address to listen on")
-                .long("listen")
-                .takes_value(true),
-        )
-        .arg(Arg::with_name("filename").help("File to render").index(1))
-        .get_matches();
-
-    if matches.is_present("sourcegraph-server") {
-        highlighting_server(
-            matches
-                .value_of("listen")
-                .expect("Listening address is required for server mode"),
-        );
-        return;
-    }
-
-    let filename = matches.value_of("filename").expect("filename is required");
-
-    let stdin = io::stdin();
-    let mut in_handle = stdin.lock();
-
-    let stdout = io::stdout();
-    let mut out_handle = stdout.lock();
-
-    if matches.is_present("about-filter") && filename.ends_with(".md") {
-        format_markdown(&mut in_handle, &mut out_handle);
-    } else {
-        format_code(
-            &THEMES.themes["InspiredGitHub"],
-            &mut in_handle,
-            &mut out_handle,
-            filename,
-        );
-    }
-}