summaryrefslogtreecommitdiff
path: root/docs/src/main.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2024-02-28 11:37:52 +0100
committerGitHub <noreply@github.com>2024-02-28 10:37:52 +0000
commita518e2dd4d829b45b0887da28acb77d0568894ab (patch)
treee7387b27e299383a0bd1a26ad58f485630cb23b7 /docs/src/main.rs
parente16d3f5a67a31154797b4d56cdc6ed142ee2a7cf (diff)
Move docs generation code (#3519)
Diffstat (limited to 'docs/src/main.rs')
-rw-r--r--docs/src/main.rs148
1 files changed, 148 insertions, 0 deletions
diff --git a/docs/src/main.rs b/docs/src/main.rs
new file mode 100644
index 00000000..f4414b10
--- /dev/null
+++ b/docs/src/main.rs
@@ -0,0 +1,148 @@
+use std::fs;
+use std::path::{Path, PathBuf};
+
+use clap::Parser;
+use typst::model::Document;
+use typst::visualize::Color;
+use typst_docs::{provide, Html, Resolver};
+use typst_render::render;
+
+#[derive(Debug)]
+struct CliResolver<'a> {
+ assets_dir: &'a Path,
+ verbose: bool,
+ base: &'a str,
+}
+
+impl<'a> Resolver for CliResolver<'a> {
+ fn commits(&self, from: &str, to: &str) -> Vec<typst_docs::Commit> {
+ if self.verbose {
+ eprintln!("commits({from}, {to})");
+ }
+ vec![]
+ }
+
+ fn example(
+ &self,
+ hash: u128,
+ source: Option<Html>,
+ document: &Document,
+ ) -> typst_docs::Html {
+ if self.verbose {
+ eprintln!(
+ "example(0x{hash:x}, {:?} chars, Document)",
+ source.as_ref().map(|s| s.as_str().len())
+ );
+ }
+
+ let frame = &document.pages.first().expect("page 0").frame;
+ let pixmap = render(frame, 2.0, Color::WHITE);
+ let filename = format!("{hash:x}.png");
+ let path = self.assets_dir.join(&filename);
+ fs::create_dir_all(path.parent().expect("parent")).expect("create dir");
+ pixmap.save_png(path.as_path()).expect("save png");
+ let src = format!("{}assets/{filename}", self.base);
+ eprintln!("Generated example image {path:?}");
+
+ if let Some(code) = source {
+ let code_safe = code.as_str();
+ Html::new(format!(
+ r#"<div class="previewed-code"><pre>{code_safe}</pre><div class="preview"><img src="{src}" alt="Preview" /></div></div>"#
+ ))
+ } else {
+ Html::new(format!(
+ r#"<div class="preview"><img src="{src}" alt="Preview" /></div>"#
+ ))
+ }
+ }
+
+ fn image(&self, filename: &str, data: &[u8]) -> String {
+ if self.verbose {
+ eprintln!("image({filename}, {} bytes)", data.len());
+ }
+
+ let path = self.assets_dir.join(filename);
+ fs::create_dir_all(path.parent().expect("parent")).expect("create dir");
+ fs::write(&path, data).expect("write image");
+ eprintln!("Created {} byte image at {path:?}", data.len());
+
+ format!("{}assets/{filename}", self.base)
+ }
+
+ fn link(&self, link: &str) -> Option<String> {
+ if self.verbose {
+ eprintln!("link({link})");
+ }
+ None
+ }
+
+ fn base(&self) -> &str {
+ self.base
+ }
+}
+
+/// Generates the JSON representation of the documentation. This can be used to
+/// generate the HTML yourself. Be warned: the JSON structure is not stable and
+/// may change at any time.
+#[derive(Parser, Debug)]
+#[command(version, about, long_about = None)]
+struct Args {
+ /// The generation process can produce additional assets. Namely images.
+ /// This option controls where to spit them out. The HTML generation will
+ /// assume that this output directory is served at `${base_url}/assets/*`.
+ /// The default is `assets`. For example, if the base URL is `/docs/` then
+ /// the gemerated HTML might look like `<img src="/docs/assets/foo.png">`
+ /// even though the `--assets-dir` was set to `/tmp/images` or something.
+ #[arg(long, default_value = "assets")]
+ assets_dir: PathBuf,
+
+ /// Write the JSON output to this file. The default is `-` which is a
+ /// special value that means "write to standard output". If you want to
+ /// write to a file named `-` then use `./-`.
+ #[arg(long, default_value = "-")]
+ out_file: PathBuf,
+
+ /// The base URL for the documentation. This can be an absolute URL like
+ /// `https://example.com/docs/` or a relative URL like `/docs/`. This is
+ /// used as the base URL for the generated page's `.route` properties as
+ /// well as cross-page links. The default is `/`. If a `/` trailing slash is
+ /// not present then it will be added. This option also affects the HTML
+ /// asset references. For example: `--base /docs/` will generate
+ /// `<img src="/docs/assets/foo.png">`.
+ #[arg(long, default_value = "/")]
+ base: String,
+
+ /// Enable verbose logging. This will print out all the calls to the
+ /// resolver and the paths of the generated assets.
+ #[arg(long)]
+ verbose: bool,
+}
+
+fn main() -> Result<(), Box<dyn std::error::Error>> {
+ let args = Args::parse();
+ let mut base = args.base.clone();
+ if !base.ends_with('/') {
+ base.push('/');
+ }
+
+ let resolver = CliResolver {
+ assets_dir: &args.assets_dir,
+ verbose: args.verbose,
+ base: &base,
+ };
+ if args.verbose {
+ eprintln!("resolver: {resolver:?}");
+ }
+ let pages = provide(&resolver);
+
+ eprintln!("Be warned: the JSON structure is not stable and may change at any time.");
+ let json = serde_json::to_string_pretty(&pages)?;
+
+ if args.out_file.to_string_lossy() == "-" {
+ println!("{json}");
+ } else {
+ fs::write(&args.out_file, &*json)?;
+ }
+
+ Ok(())
+}