Skip to content

Instantly share code, notes, and snippets.

@Gankra
Created December 5, 2023 02:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Gankra/348d080d6d5e609074a326ad73fdd96a to your computer and use it in GitHub Desktop.
Save Gankra/348d080d6d5e609074a326ad73fdd96a to your computer and use it in GitHub Desktop.
rustblog
{
"articles":
[
{
"path": "oops-that-was-important",
"toc": false,
"title": "Compiler Optimizations Are Hard Because They Forget",
"date": "Sat, 24 Sep 2022 06:06:58 +0000"
},
{
"path": "defaults-affect-inference",
"toc": true,
"title": "Defaults Affect Inference in Rust: Expressions Instead Of Types",
"date": "Sun, 10 Apr 2022 23:00:13 +0000"
},
{
"path": "tower-of-weakenings",
"toc": true,
"title": "The Tower of Weakenings: Memory Models For Everyone",
"date": "Tue, 05 Apr 2022 20:07:14 +0000"
},
{
"path": "dsts-are-polymorphic-generics",
"toc": false,
"title": "DSTs Are Just Polymorphically Compiled Generics",
"date": "Tue, 30 Mar 2022 22:13:17 +0000"
},
{
"path": "fix-rust-pointers",
"toc": true,
"title": "Rust's Unsafe Pointer Types Need An Overhaul",
"date": "Sat, 19 Mar 2022 22:13:17 +0000"
},
{
"path": "c-isnt-a-language",
"toc": false,
"title": "C Isn't A Programming Language Anymore",
"date": "Wed, 16 Mar 2022 21:24:08 +0000"
},
{
"path": "faultlore",
"toc": false,
"title": "Faultlore: Learning Through Errors",
"date": "Sun, 27 Feb 2022 18:18:56 +0000"
},
{
"path": "deinitialize-me-maybe",
"toc": true,
"title": "Destroy All Values: Designing Deinitialization in Programming Languages",
"date": "Sun, 23 Jan 2022 00:00:00 +0000"
},
{
"path": "compact-unwinding",
"toc": true,
"title": "The Apple Compact Unwinding Format: Documented and Explained",
"date": "Wed, 09 Jun 2021 00:00:00 +0000"
},
{
"path": "swift-abi",
"toc": true,
"title": "How Swift Achieved Dynamic Linking Where Rust Couldn't",
"date": "Thu, 07 Nov 2019 00:00:00 +0000"
},
{
"path": "text-hates-you",
"toc": true,
"title": "Text Rendering Hates You",
"date": "Sat, 28 Sep 2019 00:00:00 +0000"
},
{
"path": "hashbrown-tldr",
"toc": false,
"title": "Swisstable, a Quick and Dirty Description",
"date": "Sat, 27 Jul 2019 00:00:00 +0000",
"aux_date": "Rust Nightly 1.35.0"
},
{
"path": "initialize-me-maybe",
"toc": false,
"title": "Here's My Type, So Initialize Me Maybe",
"date": "Tue, 21 May 2019 00:00:00 +0000",
"aux_date": "Rust Nightly 1.36.0"
},
{
"path": "hashbrown-insert",
"toc": false,
"title": "Why Hashbrown Does A Double-Lookup",
"date": "Wed, 20 Mar 2019 00:00:00 +0000",
"aux_date": "Rust Nightly 1.36.0"
},
{
"path": "impl-defined",
"toc": false,
"title": "The Kinds of Implementation-Defined?",
"date": "Tue, 13 Nov 2018 00:00:00 +0000"
},
{
"path": "rust-layouts-and-abis",
"toc": true,
"title": "Notes on Type Layouts and ABIs in Rust",
"date": "Tue, 09 Oct 2018 00:00:00 +0000",
"aux_date": "Rust Nightly 1.30.0"
},
{
"path": "only-in-rust",
"toc": false,
"title": "Bugs You'll Probably Only Have In Rust",
"date": "Wed, 14 Jun 2017 00:00:00 +0000"
},
{
"path": "linear-rust",
"toc": true,
"title": "The Pain Of Linear Types In Rust",
"date": "Mon, 08 May 2017 00:00:00 +0000"
},
{
"path": "robinhood-part-1",
"toc": false,
"title": "A HashMap in Rust - What's a HashMap?",
"date": "Thu, 15 Jan 2015 00:00:00 +0000",
"aux_date": "Rust Nightly 1.0.0"
},
{
"path": "rust-btree-case",
"toc": false,
"title": "Rust Collections Case Study: BTreeMap",
"date": "Fri, 05 Jun 2015 00:00:00 +0000",
"aux_date": "Rust Nightly 1.0.0"
},
{
"path": "rust-generics-and-collections",
"toc": false,
"title": "Rust, Generics, and Collections",
"date": "Wed, 03 Jun 2015 00:00:00 +0000",
"aux_date": "Rust Nightly 0.12.0"
},
{
"path": "everyone-poops",
"toc": false,
"title": "Pre-Pooping Your Pants With Rust",
"date": "Mon, 27 Apr 2015 00:00:00 +0000",
"aux_date": "Rust Nightly 0.12.0"
},
{
"path": "rust-lifetimes-and-collections",
"toc": false,
"title": "Rust, Lifetimes, and Collections",
"date": "Sun, 09 Nov 2014 00:00:00 +0000",
"aux_date": "Rust Nightly 0.12.0"
},
{
"path": "homestuck-memories",
"toc": false,
"title": "Memories of Working on Homestuck",
"date": "Wed, 06 May 2020 00:00:00 +0000"
}
]
}
[package]
name = "rustblog"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
linked-hash-map = "0.5.4"
// Assumes a bunch of random css/js files exist in parent dirs
use serde::Deserialize;
use std::error::Error;
use std::fs::{self, File};
use std::io::BufReader;
use std::io::prelude::*;
use std::env;
use std::process::Command;
use std::time::Duration;
#[derive(Deserialize)]
struct Manifest {
articles: Vec<Article>,
}
#[allow(dead_code)]
#[derive(Deserialize)]
struct Article {
path: String,
toc: bool,
title: String,
date: String,
aux_date: Option<String>,
}
fn main() -> Result<(), Box<dyn Error>> {
env::set_current_dir("../../")?;
let file = File::open("articles.json")?;
let reader = BufReader::new(file);
let manifest: Manifest = serde_json::from_reader(reader)?;
let rss_base = r#"
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Faultlore</title>
<link>https://gankra.github.io/blah/</link>
<atom:link href="http://gankra.github.io/rss.xml" rel="self" type="application/rss+xml"/>
<description>Aria's Feverdreams</description>
<language>en-us</language>
<image>
<url>https://gankra.github.io/blah/img/error-base.png</url>
<title>Faultlore</title>
<link>https://gankra.github.io/blah/</link>
</image>
"#;
let rss_end = r#"
</channel>
</rss>
"#;
let mut rss = String::new();
rss.push_str(rss_base);
for article in &manifest.articles {
print!("processing {}...", article.path);
assert!(!article.title.contains("\""));
assert!(!article.path.contains("\""));
assert!(!article.title.contains("<"));
assert!(!article.path.contains("<"));
assert!(!article.title.contains(">"));
assert!(!article.path.contains(">"));
assert!(!article.title.contains("\\"));
assert!(!article.path.contains("\\"));
let true_title = format!("{} - Faultlore", article.title);
let true_link = format!("https://gankra.github.io/blah/{}", article.path);
let true_desc = format!("{}", article.title);
env::set_current_dir(&article.path)?;
fs::create_dir_all("doc")?;
fs::copy("../rustblog/head.html", "./head.html")?;
{
// Add new meta headers for robots
let title_meta = format!("\n<meta property=\"og:title\" content=\"{}\">", &true_title);
let url_meta = format!("\n<meta property=\"og:url\" content=\"{}\">", &true_link);
let mut header = File::options()
.read(true)
.append(true)
.open("./head.html")?;
header.write_all(title_meta.as_bytes())?;
header.write_all(url_meta.as_bytes())?;
header.sync_all()?;
}
std::thread::sleep(Duration::from_millis(75));
let mut command = Command::new("rustdoc");
if article.toc {
// Do nothing
} else {
command.arg("--markdown-no-toc");
}
command
.arg("--html-in-header")
.arg("./head.html")
.arg("--html-before-content")
.arg("../rustblog/before.html")
.arg("--html-after-content")
.arg("../rustblog/after.html")
.arg("index.md")
.status()?;
std::thread::sleep(Duration::from_millis(75));
fs::rename("doc/index.html", "./index.html")?;
fs::remove_file("./head.html")?;
fs::remove_dir_all("doc")?;
std::thread::sleep(Duration::from_millis(75));
{
let html = fs::read_to_string("./index.html")?;
let suffix = &*html;
// Replace the title
let title_start = suffix.find("<title>").expect("could not find title");
let (part0, suffix) = suffix.split_at(title_start);
let title_end = suffix.find("</title>").expect("could not find title");
let (_title, suffix) = suffix.split_at(title_end);
let new_title = format!("<title>{}</title>", &true_title);
let new_html = if article.toc {
// rustdoc puts the TOC before our author/date header, so we need to hackily
// pull the header out and put it back before the header.
let article_start = suffix.find("<article").expect("could not find article");
let (part1, suffix) = suffix.split_at(article_start);
let nav_start = suffix.find("<nav").expect("could not find nav");
let (part2, suffix) = suffix.split_at(nav_start);
let hdr_start = suffix.find("<header>").expect("could not find standard author header");
let (nav, suffix) = suffix.split_at(hdr_start);
let hdr_end = suffix.find("</header>").expect("could not find header end");
let (hdr, suffix) = suffix.split_at(hdr_end);
format!("{}{}{}{}{}{}{}", part0, new_title, part1, part2, hdr, nav, suffix)
} else {
format!("{}{}{}", part0, new_title, suffix)
};
let mut html_file = File::create("./index.html")?;
html_file.write_all(new_html.as_bytes())?;
}
let rss_item = format!(r#"
<item>
<title>{}</title>
<link>{}</link>
<description>{}</description>
<pubDate>{}</pubDate>
</item>
"#,
true_title,
true_link,
true_desc,
article.date);
rss.push_str(&rss_item);
env::set_current_dir("../")?;
println!(" done!");
}
{
// Finalize the RSS feed
rss.push_str(rss_end);
let mut rss_file = File::create("./rss.xml")?;
rss_file.write_all(rss.as_bytes())?;
}
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment