Convert crate list to be data-driven using a json file

This commit is contained in:
Nico Burns 2022-03-13 21:43:04 +00:00
parent f3dade3308
commit 555959cf26
6 changed files with 221 additions and 37 deletions

1
Cargo.lock generated
View file

@ -94,6 +94,7 @@ dependencies = [
"axum",
"once_cell",
"serde",
"serde_json",
"tera",
"tokio",
"tower-http",

View file

@ -9,6 +9,7 @@ edition = "2021"
axum = "0.4.8"
once_cell = "1.10.0"
serde = { version = "1.0.136", features = ["derive"] }
serde_json = "1.0.79"
tera = "1.15"
tokio = { version = "1.17", features = ["full"] }
tower-http = { version = "0.2.5", features = ["fs", "trace"] }

142
src/data/crates.json Normal file
View file

@ -0,0 +1,142 @@
{
"crate_groups": [
{
"name": "Core",
"description": "Very commonly used domain-specific tools",
"purposes": [
{
"name": "Random numbers",
"crates": [{
"name": "rand",
"notes": "De facto random number generation library split out from the standard library"
}]
},
{
"name": "Serialization (JSON, YAML, etc)",
"crates": [{
"name": "serde",
"notes": "De facto serialization library. Use in conjunction with sub-crates like serde_json for the specific format that you are using."
}]
},
{
"name": "Regular Expressions",
"crates": [{
"name": "regex",
"notes": "De facto regex library. Very fast, but does not support fancier features such as backtracking."
},
{
"name": "fancy_regex",
"notes": "Use if need features such as backtracking which regex doesn't support"
}]
},
{
"name": "Gzip (de)compression",
"crates": [{
"name": "flate2",
"notes": "Uses a pure-Rust implementation by default. Use feature flags to opt in to system zlib."
}]
},
{
"name": "Time & Date",
"crates": [{
"name": "time",
"notes": "Basic time manipulation."
}, {
"name": "chrono",
"notes": "Basic time manipulation."
}]
}
]
},
{
"name": "Error Handling",
"description": "Crates for more easily handling errors",
"purposes": [
{
"name": "For applications",
"crates": [{
"name": "anyhow",
"notes": "Provides a boxed error type that can hold any error, and helpers for generating an application-level stack trace."
}]
},
{
"name": "For libraries",
"crates": [{
"name": "thiserror",
"notes": "Helps with generating boilerplate for enum-style error types."
}]
}
]
},
{
"name": "Logging",
"description": "Crates for logging. Note that in general you will need a seperate crate for actually printing/storing the logs",
"purposes": [
{
"name": "Text-based logging",
"crates": [{
"name": "tracing",
"notes": "Tracing is now the go-to crate for logging."
}, {
"name": "log",
"notes": "An older and simpler crate if your needs are simple and you are not using any async code."
}]
},
{
"name": "Structed logging",
"crates": [{
"name": "tracing",
"notes": "Tracing is now the go-to crate for logging."
}, {
"name": "slog",
"notes": "Structed logging"
}]
}
]
},
{
"name": "Language Extensions",
"description": "General purpose utility crates that extend language and/or stdlib functionality.",
"purposes": [
{
"name": "Lazy static variable initialization",
"crates": [{
"name": "once_cell",
"notes": "Newer crate with more ergonomic API. On track to be incorporated into the standard library. Should be preferred for all new projects."
}, {
"name": "lazy_static",
"notes": "Older crate. API is less convenient, but crate is stable and maintained."
}]
},
{
"name": "Iterator helpers",
"crates": [{
"name": "itertools",
"notes": "A bunch of useful methods on iterators that aren't in the stdlib"
}]
},
{
"name": "Abstracting over different number types",
"crates": [{
"name": "num",
"notes": "Traits like Number, Add, etc that allow you write functions that are generic over the specific numeric type"
}]
},
{
"name": "Endian conversion",
"crates": [{
"name": "byteorder",
"notes": "Utility functions to convert between different endianness or read/write data with a specific endianness"
}]
},
{
"name": "Bitflags",
"crates": [{
"name": "bitflags",
"notes": "Strongly typed bitflag types"
}]
}
]
}
]
}

View file

@ -2,16 +2,57 @@ use axum::{
http::StatusCode,
response::{ IntoResponse, Html },
};
use serde::{Serialize, Deserialize};
use serde_json;
use tera::Context;
use crate::templates::TERA;
pub(crate) async fn run() -> impl IntoResponse {
let res = match TERA.render("routes/crates/list.html", &Context::new()) {
// Load crate data
const data_file_contents : &str = include_str!("../../data/crates.json");
// let data_file_contents = String::from_utf8(std::fs::read("data/crates.json").unwrap()).unwrap();
let data : CrateDefinitionFile = serde_json::from_str(&data_file_contents).unwrap();
// Render template
let mut context = Context::new();
context.insert("crate_groups", &data.crate_groups);
let rendered = TERA.render("routes/crates/list.html", &context);
// Handle template rendering errors
let res = match rendered {
Ok(res) => (StatusCode::OK, Html(res)),
Err(err) => {
tracing::debug!("listening on {}", err);
(StatusCode::INTERNAL_SERVER_ERROR, Html("".to_string()))
}
};
// Return response
(StatusCode::OK, res)
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct Crate {
name: String,
notes: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct Purpose {
name: String,
crates: Vec<Crate>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct CrateGroup {
name: String,
description: String,
purposes: Vec<Purpose>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct CrateDefinitionFile {
crate_groups: Vec<CrateGroup>,
}

View file

@ -21,6 +21,11 @@ h1.logo {
h3 {
font-size: 1.6em;
font-weight: normal;
margin-bottom: 0;
}
p.group-description {
color: #CCC;
margin-top: 0;
}
a, a:hover, a:visited, a:active {

View file

@ -13,40 +13,34 @@
<h1 class="logo">Blessed.rs</h1>
</center>
<h3>Core Crates</h3>
<table class="full-width">
<thead>
<tr>
<th width="200">Use Case</th>
<th>Recommendation</th>
<!-- <th>Latest Version</th> -->
<!-- <th>Last Updated</th> -->
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td>Random Numbers</td>
<td><a href="https://docs.rs/rand">rand</a></td>
<!-- <td>0.8.5</td> -->
<!-- <td>5 days ago</td> -->
<td>De facto random number generation library split out from the standard library</td>
</tr>
</tbody>
<tbody>
<tr>
<td rowspan="2" style="vertical-align: top">Lazily initialized static variables</td>
<td><a href="https://docs.rs/once_cell">once_cell</a></td>
<!-- <td>0.8.5</td> -->
<!-- <td>5 days ago</td> -->
<td>Newer crate with more ergonomic API. On track to be incorporated into the standard library. Should be preferred for all new projects.</td>
</tr>
<tr>
<td><a href="https://docs.rs/lazy_static">lazy_static</a></td>
<!-- <td>0.8.5</td> -->
<!-- <td>5 days ago</td> -->
<td>De facto random number generation library split out from the standard library</td>
</tr>
</tbody>
</table>
{% for group in crate_groups %}
<h3>{{ group.name }}</h3>
<p class="group-description">{{ group.description }}</p>
<table class="full-width">
<thead>
<tr>
<th width="200">Use Case</th>
<th>Recommendation</th>
<!-- <th>Latest Version</th> -->
<!-- <th>Last Updated</th> -->
<th>Notes</th>
</tr>
</thead>
<tbody>
{% for purpose in group.purposes %}
{% for crate in purpose.crates %}
<tr>
{% if loop.index0 == 0 %}<td rowspan="{{ purpose.crates | length }} ">{{ purpose.name }}</td>{% endif %}
<td><a href="https://docs.rs/{{ crate.name }}">{{ crate.name }}</a></td>
<!-- <td>0.8.5</td> -->
<!-- <td>5 days ago</td> -->
<td>{{ crate.notes }}</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
{% endfor %}
{% endblock content %}