//! The assets atgc.codes is built from, which are the ones only this tree //! knows. //! //! The site itself is an Astro project in `www/`, and its markup, copy and //! layout live there, where copy belongs. What lives here is everything the //! page cannot be trusted to write down for itself: //! //! - **The facts.** The version `cargo release` computed and the install //! line the README's own test guards, as `facts.json`. A marketing page is //! where a claim about software goes to rot, and these are the two claims //! a stranger acts on. //! - **The look.** `theme.css`, `field.css` and `mark.svg`, written out from //! [`crate::html::brand`] — *the same constants* the OAuth callback page //! is built from. That is the point rather than a convenience: the field //! of DNA behind `atgc auth login` and the field behind atgc.codes are the //! same bytes, because there is one palette in this project and neither //! side gets a copy of it. //! - **The drawings.** `field.html` and `terminal.html` come out of //! [`crate::art`], which is Rust and cannot be anything else: the field is //! nineteen thousand cells of generated art and the terminal is `atgc //! about`'s own rendering rather than a screenshot of it. //! - **The face.** The two Comic Neue weights, copied out so Astro can serve //! them from its own origin. //! //! Everything else — every heading, every feature line, the whole of the //! quickstart — is `.astro` in `www/src`, editable by anybody, with no Rust //! rebuild in the loop. use crate::art::{self, Canvas, PAD, panel_lines, width_of}; use crate::html::brand::{self, Scheme}; /// The one line somebody is meant to leave with. Stated once, here, and /// checked against the README's copy of it by the test at the bottom of this /// file — a second install line outside that check is a second thing to be /// wrong, and the one a stranger reads first. Astro is handed this rather /// than writing its own, and a check refuses any `cargo install` line that /// appears in the site's own sources. const INSTALL: &str = "cargo install --git https://tangled.org/permadeath.com/atgc"; /// Where the source is. Also `CARGO_PKG_REPOSITORY`, which is what the panel /// on the terminal prints, so the two cannot disagree. const REPO: &str = env!("CARGO_PKG_REPOSITORY"); /// Comic Neue, in the two weights the headings use. Compiled in so the /// emitter can write them out wherever Astro wants them; see /// `fonts/README.md` for where the bytes came from, and `fonts/OFL.txt` for /// the licence that lets them be here. const FONT_REGULAR: &[u8] = include_bytes!("fonts/ComicNeue-Regular.woff2"); const FONT_BOLD: &[u8] = include_bytes!("fonts/ComicNeue-Bold.woff2"); /// `atgc about`, drawn rather than photographed. /// /// The epic that asked for this wanted "a repeatable capture — a recorded /// session rendered to an image by a command". This is the shorter answer to /// the same problem: the command's own drawing code, called here, at a frame /// the page picks instead of one a terminal reported. There is no capture to /// re-run after a release and no cropped PNG to go stale, and the version on /// it is the version of the binary that built the page. /// /// The frame is fixed because a page has no terminal to ask. 88 columns /// leaves the panel about a helix of field either side, and 25 rows is a /// whole number of half turns ([`art::snap_rows`]), so the field is as wide /// along the bottom edge as along the top. const DEMO_COLUMNS: i32 = 88; const DEMO_ROWS: i32 = 25; fn about_demo() -> String { let lines = panel_lines(); let content = lines.iter().map(width_of).max().unwrap_or(0) as i32; let panel_w = content + 2 * PAD + 2; let panel_h = lines.len() as i32 + 2; let mut canvas = Canvas::new(DEMO_COLUMNS, DEMO_ROWS); art::draw_field(&mut canvas); art::draw_panel( &mut canvas, (DEMO_COLUMNS - panel_w) / 2, (DEMO_ROWS - panel_h) / 2, panel_w, &lines, ); super::field::panel(&canvas) } /// The facts the page states and the tree can check, as JSON for Astro to /// read at build time. /// /// Hand-rolled rather than `serde_json`, because escaping five known strings /// is not worth a derive: every value here is a version, a URL or an ASCII /// command line, and [`escape_json`] holds that. fn facts() -> String { let fields = [ ("version", env!("CARGO_PKG_VERSION")), ("install", INSTALL), ("repo", REPO), ("rustVersion", env!("CARGO_PKG_RUST_VERSION")), ("description", env!("CARGO_PKG_DESCRIPTION")), ]; let body: Vec = fields .iter() .map(|(key, value)| format!(" \"{key}\": \"{}\"", escape_json(value))) .collect(); format!("{{\n{}\n}}\n", body.join(",\n")) } /// The two characters that can end a JSON string, and the control range. /// Everything written through this is a value from `Cargo.toml` or a /// constant above, so this is a guard rather than a parser. fn escape_json(text: &str) -> String { let mut out = String::with_capacity(text.len()); for ch in text.chars() { match ch { '"' => out.push_str("\\\""), '\\' => out.push_str("\\\\"), c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)), c => out.push(c), } } out } /// Every file the site is built from, as a relative path and its bytes. /// /// One list, so the command that writes them has no opinions of its own and /// a test can walk the same set the build does. pub(crate) fn assets() -> Vec<(&'static str, Vec)> { vec![ ("facts.json", facts().into_bytes()), // The look, from the same constants the login page is built from. ( "theme.css", brand::theme_css(Scheme::DarkFirst).into_bytes(), ), ("field.css", brand::FIELD_CSS.as_bytes().to_vec()), ("mark.svg", brand::MARK_SVG.as_bytes().to_vec()), ("favicon.svg", brand::favicon_svg().into_bytes()), // The drawings, which are Rust and cannot be anything else. ("field.html", brand::field_markup().into_bytes()), ("terminal.html", about_demo().into_bytes()), // The face. ("fonts/ComicNeue-Regular.woff2", FONT_REGULAR.to_vec()), ("fonts/ComicNeue-Bold.woff2", FONT_BOLD.to_vec()), ] } #[cfg(test)] mod tests { use super::*; /// The install line the page shows is the install line in the README. /// /// This is the whole reason any of this is emitted by the binary rather /// than typed into a `.astro` file: a second copy of the one command a /// stranger types is a second thing to be wrong, and it is the copy they /// read first. The README is included only in a test build. #[test] fn the_install_line_is_the_readme_s() { let readme = include_str!("../../../README.md"); assert!( readme.contains(INSTALL), "the README does not contain {INSTALL:?}" ); assert!(facts().contains(INSTALL), "the facts dropped it"); } /// The version in the facts is the version of the binary that emitted /// them, which is what makes the page's claim about itself checkable. #[test] fn the_facts_name_the_running_version() { let facts = facts(); assert!(facts.contains(&format!("\"version\": \"{}\"", env!("CARGO_PKG_VERSION")))); assert!(facts.contains(env!("CARGO_PKG_RUST_VERSION")), "no MSRV"); assert!(facts.contains(REPO), "no repo"); // Parseable: every brace and quote balanced, no trailing comma. assert!(facts.starts_with("{\n") && facts.ends_with("}\n")); assert_eq!(facts.matches('"').count() % 2, 0, "odd quote count"); assert!(!facts.contains(",\n}"), "trailing comma"); } /// A value that would end its own string is escaped rather than trusted. /// Nothing in `Cargo.toml` does this today, which is exactly when a /// guard is worth writing. #[test] fn a_quote_cannot_break_out_of_the_facts() { assert_eq!(escape_json(r#"a"b"#), r#"a\"b"#); assert_eq!(escape_json(r"a\b"), r"a\\b"); assert_eq!(escape_json("a\nb"), "a\\u000ab"); } /// The site and the login page are drawn from one palette. Not "look the /// same" — the same bytes, because both come from `html::brand`, and a /// second copy of `--a` anywhere is the drift this arrangement exists to /// prevent. #[test] fn the_site_and_the_login_page_share_one_look() { let emitted = |name: &str| { assets() .into_iter() .find(|(path, _)| *path == name) .map(|(_, bytes)| String::from_utf8(bytes).expect("utf-8")) .unwrap_or_else(|| panic!("no {name} in the assets")) }; // The field's stylesheet and the mark go out verbatim. assert_eq!(emitted("field.css"), brand::FIELD_CSS); assert_eq!(emitted("mark.svg"), brand::MARK_SVG); // The palette differs only in which way round the schemes are // declared, and both blocks are in both. let theme = emitted("theme.css"); let login = brand::theme_css(Scheme::System); for block in theme.split("@media") { let vars: Vec<&str> = block .match_indices("--") .map(|(i, _)| &block[i..i + 5]) .collect(); for var in vars { assert!( login.contains(var), "the site declares {var:?} and the login page does not" ); } } // And the field itself is the art, not a picture of it. assert_eq!(emitted("field.html"), brand::field_markup()); } /// The terminal on the page is `atgc about`'s own drawing: the panel, /// the field it hangs on, and the muted border marked up so it reads /// quieter than the text inside it. #[test] fn the_terminal_is_the_command_and_not_a_picture_of_it() { let demo = about_demo(); assert!(demo.contains("atgc"), "no wordmark in the lid"); assert!(demo.contains('╭') && demo.contains('╯'), "no panel"); assert!(demo.contains(""), "the border is not muted"); assert!(demo.contains(REPO), "no repo line"); // The site line too, which is this page's own address: Astro knows // where it serves from and is handed no fact about it, but the panel // drawn here prints what the command prints. assert!(demo.contains(env!("CARGO_PKG_HOMEPAGE")), "no site line"); assert!( demo.contains(env!("CARGO_PKG_VERSION")), "the demo is stale" ); assert_eq!(demo.lines().count(), DEMO_ROWS as usize); } /// The site's own sources do not restate a fact the binary emits. /// /// This is the other half of `the_install_line_is_the_readme_s`: that /// one holds the emitted value to the README, and this one stops the /// Astro side quietly typing its own copy beside it. A page that says /// `cargo install …` in an `.astro` file is a page that will still say /// it after the URL changes, and no test would notice. /// /// Skipped when `www/` is not there, which is the packaged crate: the /// tarball ships `src/` and not the site. #[test] fn the_site_does_not_hardcode_what_the_binary_emits() { let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("www/src"); if !root.is_dir() { return; } let mut checked = 0; let mut stack = vec![root]; while let Some(dir) = stack.pop() { for entry in std::fs::read_dir(&dir).expect("read_dir").flatten() { let path = entry.path(); // `generated/` is the binary's own output, and is where // these values are *supposed* to appear. if path.is_dir() { if path.file_name().is_some_and(|n| n == "generated") { continue; } stack.push(path); continue; } if !path .extension() .is_some_and(|e| e == "astro" || e == "ts" || e == "js" || e == "css") { continue; } let text = std::fs::read_to_string(&path).expect("read"); checked += 1; for claim in ["cargo install", env!("CARGO_PKG_VERSION")] { assert!( !text.contains(claim), "{} hardcodes {claim:?}; read it from facts.json instead", path.display() ); } } } assert!(checked > 0, "no site sources were checked"); } /// Every asset has a path Astro can write to and bytes in it, and the /// set is the one the command writes — nothing is emitted twice and /// nothing escapes the directory it was pointed at. #[test] fn the_asset_list_is_a_set_of_relative_paths() { let assets = assets(); let mut paths: Vec<&str> = assets.iter().map(|(path, _)| *path).collect(); let count = paths.len(); paths.sort_unstable(); paths.dedup(); assert_eq!(paths.len(), count, "an asset is emitted twice"); for (path, bytes) in &assets { assert!(!path.starts_with('/') && !path.contains(".."), "{path}"); assert!(!bytes.is_empty(), "{path} is empty"); } // The two fonts are bytes, not text, and arrive whole. assert_eq!( assets .iter() .find(|(p, _)| p.ends_with("Bold.woff2")) .map(|(_, b)| b.len()), Some(FONT_BOLD.len()) ); } }