From cc729181f777e792ec6d924379ac6be237f0cace Mon Sep 17 00:00:00 2001
From: zleyyij <75810274+zleyyij@users.noreply.github.com>
Date: Wed, 25 Sep 2024 20:16:59 -0600
Subject: [PATCH] feat: add bugcheck api
---
README.md | 50 +++-
databases/build.rs | 19 ++
databases/src/bugcheck/mod.rs | 23 ++
databases/src/lib.rs | 1 +
handlers/src/lib.rs | 53 ++++
parsing/src/bugcheck/input.md | 446 ++++++++++++++++++++++++++++++++++
parsing/src/bugcheck/mod.rs | 126 ++++++++++
parsing/src/lib.rs | 1 +
server/src/main.rs | 22 +-
9 files changed, 732 insertions(+), 9 deletions(-)
create mode 100644 databases/src/bugcheck/mod.rs
create mode 100644 parsing/src/bugcheck/input.md
create mode 100644 parsing/src/bugcheck/mod.rs
diff --git a/README.md b/README.md
index 6c0ae38..0f7fb10 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,7 @@ Current information fetched includes:
- CPU info (Intel ARK, AMD Product Database)
- USB info (VID/PID mapping)
- PCIe info (VID/PID/SUBSYS mapping)
+- Windows BugCheck error codes
## Project layout
The code is organized into 4 separate crates:
@@ -158,4 +159,51 @@ And here's example response (truncated):
"subsystem":null
},
]
-```
\ No newline at end of file
+```
+
+### BugCheck
+To interact with the bugcheck API, submit a `GET` request to `/api/bugcheck/?code=[BUGCHECK_CODE]`, where `[BUGCHECK_CODE]` is an integer value of a valid bugcheck code.
+
+The endpoint will return a structure that looks like this:
+```json
+{
+ "code": "number",
+ "name": "string",
+ "url": "string",
+}
+```
+
+Responses:
+| Code | Meaning |
+| -- | -- |
+| `404` | No bugcheck is associated with that code|
+
+
+Here's an example curl request:
+```
+curl http://127.0.0.1:3000/api/bugcheck/?code=1
+```
+
+For bulk processing, you may submit a `POST` request to the same endpoint with a `Content-Type` of `application/json` and a payload containing an array of bugcheck codes (as numbers).
+
+The endpoint will return an array of objects (same shape as the `GET` request), or if an identifier string was unable to be processed successfully, `null` will substitute in the response.
+
+Here's an example curl request:
+```
+curl -X POST http://127.0.0.1:3000/api/bugcheck/ -H "Content-Type: application/json" -d '[1, 2, 3, 4, 5]'
+```
+
+And here's an example response (truncated):
+```json
+[
+ {
+ "vendor":"SteelSeries ApS",
+ "device":null
+ },
+ {
+ "vendor":"Dell Computer Corp.",
+ "device":"Model L100 Keyboard"
+ },
+ null
+]
+```
diff --git a/databases/build.rs b/databases/build.rs
index a2c99f5..ec63e1d 100644
--- a/databases/build.rs
+++ b/databases/build.rs
@@ -1,3 +1,4 @@
+use parsing::bugcheck::CodeCache;
use parsing::cpu::IntermediateCpuCache;
use std::collections::HashSet;
use std::fs::File;
@@ -10,6 +11,24 @@ fn main() {
// println!("cargo::rerun-if-changed=build.rs");
gen_amd_cpus();
gen_intel_cpus();
+ gen_bugcheck();
+}
+
+/// Parse the windows bugcheck codes and generate `src/bugcheck/bugcheck_codegen.rs`
+fn gen_bugcheck() {
+ let destination = Path::new("src/bugcheck/").join("bugcheck_codegen.rs");
+ let mut generated_file = BufWriter::new(File::create(destination).unwrap());
+ let mut generated_map = phf_codegen::Map::new();
+ let cache = CodeCache::new();
+ for (code, (name, url)) in cache.iter() {
+ generated_map.entry(code, &format!("({name:?}, {url:?})"));
+ }
+ write!(
+ &mut generated_file,
+ "// This file was autogenerated by build.rs\n#[rustfmt::skip]\n#[allow(clippy::all)]\npub static BUGCHECK_CODES: phf::Map = {};",
+ generated_map.build()
+ )
+ .unwrap();
}
/// Parse the database for AMD cpus and generate `src/cpu/amd_codegen.rs`
diff --git a/databases/src/bugcheck/mod.rs b/databases/src/bugcheck/mod.rs
new file mode 100644
index 0000000..65f424f
--- /dev/null
+++ b/databases/src/bugcheck/mod.rs
@@ -0,0 +1,23 @@
+mod bugcheck_codegen;
+use bugcheck_codegen::BUGCHECK_CODES;
+
+#[derive(Clone)]
+pub struct BugCheckCache {}
+
+impl BugCheckCache {
+ /// Construct a new cache
+ pub fn new() -> Self {
+ Self {}
+ }
+
+ #[tracing::instrument(name = "bugcheck_lookup", skip(self))]
+ pub fn get(&self, code: u64) -> Option<&(&str, &str)> {
+ BUGCHECK_CODES.get(&code)
+ }
+}
+
+impl Default for BugCheckCache {
+ fn default() -> Self {
+ Self::new()
+ }
+}
diff --git a/databases/src/lib.rs b/databases/src/lib.rs
index c3fa358..2da67fc 100644
--- a/databases/src/lib.rs
+++ b/databases/src/lib.rs
@@ -1,4 +1,5 @@
//! This crate contains the interfaces used in production to store and lookup info.
+pub mod bugcheck;
pub mod cpu;
pub mod pcie;
pub mod usb;
diff --git a/handlers/src/lib.rs b/handlers/src/lib.rs
index dd8a1ae..4a1a213 100644
--- a/handlers/src/lib.rs
+++ b/handlers/src/lib.rs
@@ -2,6 +2,7 @@
use axum::extract::Query;
use axum::http::StatusCode;
use axum::{extract::State, Json};
+use databases::bugcheck::BugCheckCache;
use databases::cpu::Cpu;
use databases::{cpu::CpuCache, pcie::PcieCache, usb::UsbCache};
use serde::{Deserialize, Serialize};
@@ -12,6 +13,7 @@ pub struct AppState {
pub cpu_cache: CpuCache,
pub usb_cache: UsbCache,
pub pcie_cache: PcieCache,
+ pub bugcheck_cache: BugCheckCache,
}
#[derive(Debug, Deserialize, Serialize)]
@@ -156,3 +158,54 @@ pub async fn get_cpu_handler(
}
}
}
+
+#[derive(Serialize, Deserialize)]
+pub struct GetBugCheckQuery {
+ code: u64,
+}
+
+#[derive(Deserialize, Serialize)]
+pub struct BugCheckResponse {
+ code: u64,
+ name: String,
+ url: String,
+}
+/// This handler accepts a `GET` request to `/api/bugcheck/?code=[CODE]`
+pub async fn get_bugcheck_handler(
+ State(state): State,
+ Query(query): Query,
+) -> Result, StatusCode> {
+ if let Some((name, url)) = state.bugcheck_cache.get(query.code) {
+ Ok(Json(BugCheckResponse {
+ code: query.code,
+ name: name.to_string(),
+ url: url.to_string(),
+ }))
+ } else {
+ Err(StatusCode::NOT_FOUND)
+ }
+}
+
+/// This handler accepts a `POST` request to `/api/bugcheck/`, with a body containing a serialized array of bugcheck code numbers.
+/// Any unknown bugcheck codes will be substituted with `null`
+#[tracing::instrument(name = "bulk_bugcheck_handler", skip(state))]
+pub async fn post_bugcheck_handler(
+ State(state): State,
+ Json(query): Json>,
+) -> Result>>, StatusCode> {
+ let mut response: Vec