diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc90457bb9..dd50566d62 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,10 +12,13 @@ jobs: - name: "Build Bevy Assets" run: cd generate-assets && ./generate_assets.sh + - name: "Build Bevy Error Codes" + run: cd generate-errors && ./generate_errors.sh + - name: "Build website" uses: shalzz/zola-deploy-action@master env: PAGES_BRANCH: gh-pages BUILD_DIR: . BUILD_ONLY: true - TOKEN: fake-secret \ No newline at end of file + TOKEN: fake-secret diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b330b9929b..7e59a26c7f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -16,6 +16,9 @@ jobs: - name: "Build Bevy Assets" run: cd generate-assets && ./generate_assets.sh + - name: "Build Bevy Error Codes" + run: cd generate-errors && ./generate_errors.sh + - name: "Build and deploy website" if: github.repository_owner == 'bevyengine' uses: shalzz/zola-deploy-action@master diff --git a/.gitignore b/.gitignore index 71da54c70d..110de283d1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ public **/.DS_Store .idea/ content/assets +content/learn/errors diff --git a/generate-errors/.gitignore b/generate-errors/.gitignore new file mode 100644 index 0000000000..586aa0f410 --- /dev/null +++ b/generate-errors/.gitignore @@ -0,0 +1,2 @@ +target/ +bevy/ diff --git a/generate-errors/Cargo.lock b/generate-errors/Cargo.lock new file mode 100644 index 0000000000..719f24270f --- /dev/null +++ b/generate-errors/Cargo.lock @@ -0,0 +1,114 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "generate-errors" +version = "0.1.0" +dependencies = [ + "pulldown-cmark", + "serde", + "toml", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "proc-macro2" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "pulldown-cmark" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8" +dependencies = [ + "bitflags", + "memchr", + "unicase", +] + +[[package]] +name = "quote" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" diff --git a/generate-errors/Cargo.toml b/generate-errors/Cargo.toml new file mode 100644 index 0000000000..c014a7f62d --- /dev/null +++ b/generate-errors/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "generate-errors" +version = "0.1.0" +authors = [ + "Bevy Contributors ", + "Carter Anderson ", +] +license = "MIT" +edition = "2018" + +[dependencies] +pulldown-cmark = { version = "0.8", default-features = false } +serde = { version = "1", features = [ "derive" ] } +toml = "0.5" diff --git a/generate-errors/generate_errors.sh b/generate-errors/generate_errors.sh new file mode 100755 index 0000000000..85ded34fb5 --- /dev/null +++ b/generate-errors/generate_errors.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +git init bevy +cd bevy +git remote add origin https://github.com/bevyengine/bevy +git sparse-checkout set "errors" +git pull --depth=1 origin latest +cd .. + +cargo run --bin generate -- bevy/errors ../content/learn diff --git a/generate-errors/src/bin/generate.rs b/generate-errors/src/bin/generate.rs new file mode 100644 index 0000000000..174e21533a --- /dev/null +++ b/generate-errors/src/bin/generate.rs @@ -0,0 +1,113 @@ +use generate_errors::{parse_errors, ErrorCode, FrontMatterErrorCode, Section}; +use serde::Serialize; +use std::fs::File; +use std::io::Write; +use std::path::Path; +use std::{fs, io}; + +fn main() -> io::Result<()> { + let errors_dir = std::env::args() + .nth(1) + .expect("First argument should specify the errors directory"); + let content_dir = std::env::args() + .nth(2) + .expect("Second argument should specify the content directory"); + let _ = fs::create_dir(content_dir.clone()); + let errors_root_section = parse_errors(&errors_dir)?; + + errors_root_section.write(Path::new(&content_dir), Path::new(""), 0) +} + +trait FrontMatterWriter { + fn write(&self, root_path: &Path, current_path: &Path, weight: usize) -> io::Result<()>; +} + +#[derive(Serialize)] +struct FrontMatterSection { + title: String, + sort_by: String, + template: Option, + weight: usize, + extra: FrontMatterSectionExtra, +} + +#[derive(Serialize)] +struct FrontMatterSectionExtra { + header_message: Option, + sort_order_reversed: bool, +} + +impl From<&Section> for FrontMatterSection { + fn from(section: &Section) -> Self { + FrontMatterSection { + title: section.name.clone(), + sort_by: "weight".to_string(), + template: section.template.clone(), + weight: section.order.unwrap_or(0), + extra: section.into(), + } + } +} + +impl From<&Section> for FrontMatterSectionExtra { + fn from(section: &Section) -> Self { + FrontMatterSectionExtra { + header_message: section.header.clone(), + sort_order_reversed: section.sort_order_reversed, + } + } +} + +impl FrontMatterWriter for Section { + fn write(&self, root_path: &Path, current_path: &Path, weight: usize) -> io::Result<()> { + let section_path = current_path.join(self.name.to_ascii_lowercase()); + let path = root_path.join(§ion_path); + fs::create_dir(path.clone())?; + + let mut frontmatter = FrontMatterSection::from(self); + if self.order.is_none() { + frontmatter.weight = weight; + } + + let mut file = File::create(path.join("_index.md"))?; + file.write_all( + format!( + r#"+++ +{} ++++ +"#, + toml::to_string(&frontmatter).unwrap(), + ) + .as_bytes(), + )?; + + for (i, content) in self.content.iter().enumerate() { + content.write(root_path, §ion_path, i)? + } + Ok(()) + } +} + +impl FrontMatterWriter for ErrorCode { + fn write(&self, root_path: &Path, current_path: &Path, weight: usize) -> io::Result<()> { + let path = root_path.join(¤t_path); + + let mut frontmatter = FrontMatterErrorCode::from(self); + frontmatter.weight = weight; + + let mut file = File::create(path.join(format!("{}.md", self.code)))?; + file.write_all( + format!( + r#"+++ +{} ++++ +{}"#, + toml::to_string(&frontmatter).unwrap(), + self.content + ) + .as_bytes(), + )?; + + Ok(()) + } +} diff --git a/generate-errors/src/lib.rs b/generate-errors/src/lib.rs new file mode 100644 index 0000000000..9dc3588da4 --- /dev/null +++ b/generate-errors/src/lib.rs @@ -0,0 +1,102 @@ +use serde::Serialize; +use std::fs::read_to_string; +use std::{fs, io, path::PathBuf, str::FromStr}; + +#[derive(Debug, Clone)] +pub struct Section { + pub name: String, + pub content: Vec, + pub template: Option, + pub header: Option, + pub order: Option, + pub sort_order_reversed: bool, +} + +#[derive(Debug, Clone)] +pub struct ErrorCode { + pub code: String, + pub content: String, +} + +#[derive(Serialize)] +pub struct FrontMatterErrorCode { + pub title: String, + pub weight: usize, +} + +impl From<&ErrorCode> for FrontMatterErrorCode { + fn from(asset: &ErrorCode) -> Self { + FrontMatterErrorCode { + title: asset.code.clone(), + weight: 0, + } + } +} + +fn visit_dirs(dir: PathBuf, section: &mut Section) -> io::Result<()> { + if !dir.is_dir() { + // Todo: after the 0.6 release, remove this if statement + // For now we will allow this to be able to point to the `latest` branch (0.5) + // which does not yet include error codes + return Ok(()); + } + assert!(dir.is_dir(), "The path to the errors is not a directory"); + for entry in fs::read_dir(dir)? { + let entry = entry?; + let path = entry.path(); + if path.file_name().unwrap() == ".git" || path.file_name().unwrap() == ".github" { + continue; + } + if !path.is_dir() { + if path.extension().unwrap() != "md" { + continue; + } + + let error_code = read_to_string(path.clone())?; + + let code = path + .file_name() + .unwrap() + .to_os_string() + .into_string() + .unwrap() + .trim_end_matches(".md") + .to_owned(); + section.content.push(ErrorCode { + content: error_code + .trim_start_matches(&format!("# {}", code.clone())) + .replace("```rust,*", "```rust") + .lines() + .map(|line| { + // throw away `should_panic` and `no_run` to fix code highlighting + if line.starts_with("```rust,") { + "```rust" + } else { + line + } + }) + .collect::>() + .join("\n"), + code, + }); + } + } + + Ok(()) +} + +pub fn parse_errors(errors_dir: &str) -> io::Result
{ + let mut errors_root_section = Section { + name: "Errors".to_string(), + content: vec![], + template: Some("errors.html".to_string()), + header: Some("Errors".to_string()), + order: None, + sort_order_reversed: false, + }; + visit_dirs( + PathBuf::from_str(&errors_dir).unwrap(), + &mut errors_root_section, + )?; + Ok(errors_root_section) +} diff --git a/templates/errors.html b/templates/errors.html new file mode 100644 index 0000000000..19b062b397 --- /dev/null +++ b/templates/errors.html @@ -0,0 +1,17 @@ +{% extends "base.html" %} + +{% block content %} +
+
+

This page documents Bevy's error codes for the current release. In case you are looking for the latest error + codes from Bevy's main branch, you can find them in the + repository.

+
+ {% for page in section.pages %} +

+ {{ page.title }}# +

+
{{ page.content | safe }}
+ {% endfor %} +
+{% endblock content %} diff --git a/templates/learn.html b/templates/learn.html index 55cb54f21b..ed5f35c818 100644 --- a/templates/learn.html +++ b/templates/learn.html @@ -56,4 +56,4 @@ -{% endblock content %} \ No newline at end of file +{% endblock content %}