use axum::Json;
use axum::Router;
use axum::extract::Path;
use axum::http::{StatusCode, header};
use axum::response::{IntoResponse, Response};
use axum::routing::get;
use include_dir::{Dir, include_dir};
use serde_json::json;
pub const LEXICON_BUNDLE: Dir<'static> =
include_dir!("$CARGO_MANIFEST_DIR/lexicons/tools/cairn/admin");
pub const LEXICON_BUNDLE_PUBLIC: Dir<'static> =
include_dir!("$CARGO_MANIFEST_DIR/lexicons/tools/cairn/public");
pub const LEXICON_NSID_PREFIX: &str = "tools.cairn.admin";
pub const LEXICON_NSID_PREFIX_PUBLIC: &str = "tools.cairn.public";
pub const EXPECTED_LEXICON_STEMS: &[&str] = &[
"applyLabel",
"confirmPendingAction",
"defs",
"dismissPendingAction",
"flagReporter",
"getAuditLog",
"getPendingAction",
"getReport",
"getSubjectHistory",
"getSubjectStrikes",
"getTrustChain",
"listAuditLog",
"listLabels",
"listPendingActions",
"listReports",
"negateLabel",
"recordAction",
"resolveReport",
"retentionSweep",
"revokeAction",
];
pub const EXPECTED_LEXICON_STEMS_PUBLIC: &[&str] = &["getMyStrikeState"];
pub fn wellknown_router() -> Router {
Router::new()
.route(
"/.well-known/lexicons/tools/cairn/admin/{name}",
get(serve_admin_lexicon),
)
.route(
"/.well-known/lexicons/tools/cairn/public/{name}",
get(serve_public_lexicon),
)
}
async fn serve_admin_lexicon(Path(name): Path<String>) -> Response {
serve_from_bundle(&LEXICON_BUNDLE, &name)
}
async fn serve_public_lexicon(Path(name): Path<String>) -> Response {
serve_from_bundle(&LEXICON_BUNDLE_PUBLIC, &name)
}
fn serve_from_bundle(bundle: &Dir<'static>, name: &str) -> Response {
let Some(stem) = name.strip_suffix(".json") else {
return not_found();
};
let file_name = format!("{stem}.json");
let Some(file) = bundle.get_file(&file_name) else {
return not_found();
};
(
StatusCode::OK,
[(header::CONTENT_TYPE, "application/json")],
file.contents().to_vec(),
)
.into_response()
}
fn not_found() -> Response {
(
StatusCode::NOT_FOUND,
Json(json!({
"error": "NotFound",
"message": "lexicon not found",
})),
)
.into_response()
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::Value;
use std::collections::BTreeSet;
#[test]
fn bundle_contains_exactly_the_expected_set() {
let have: BTreeSet<String> = LEXICON_BUNDLE
.files()
.map(|f| {
f.path()
.file_name()
.expect("file in bundle")
.to_string_lossy()
.into_owned()
})
.collect();
let want: BTreeSet<String> = EXPECTED_LEXICON_STEMS
.iter()
.map(|s| format!("{s}.json"))
.collect();
assert_eq!(
have, want,
"embedded lexicon set drifted from EXPECTED_LEXICON_STEMS"
);
}
#[test]
fn every_bundled_file_parses_as_json() {
for file in LEXICON_BUNDLE.files() {
let contents = file.contents();
serde_json::from_slice::<Value>(contents).unwrap_or_else(|e| {
panic!("file {} does not parse as JSON: {e}", file.path().display())
});
}
}
#[test]
fn every_lexicon_id_matches_its_filename_stem() {
for file in LEXICON_BUNDLE.files() {
let stem = file
.path()
.file_stem()
.expect("file has stem")
.to_string_lossy()
.into_owned();
let doc: Value = serde_json::from_slice(file.contents()).unwrap();
let id = doc
.get("id")
.and_then(Value::as_str)
.unwrap_or_else(|| panic!("file {stem}.json missing top-level id"));
let expected = format!("{LEXICON_NSID_PREFIX}.{stem}");
assert_eq!(
id, expected,
"lexicon file {stem}.json has id {id:?}; expected {expected:?}"
);
}
}
#[test]
fn public_bundle_contains_exactly_the_expected_set() {
let have: BTreeSet<String> = LEXICON_BUNDLE_PUBLIC
.files()
.map(|f| {
f.path()
.file_name()
.expect("file in public bundle")
.to_string_lossy()
.into_owned()
})
.collect();
let want: BTreeSet<String> = EXPECTED_LEXICON_STEMS_PUBLIC
.iter()
.map(|s| format!("{s}.json"))
.collect();
assert_eq!(
have, want,
"public lexicon set drifted from EXPECTED_LEXICON_STEMS_PUBLIC"
);
}
#[test]
fn every_public_bundled_file_parses_as_json() {
for file in LEXICON_BUNDLE_PUBLIC.files() {
let contents = file.contents();
serde_json::from_slice::<Value>(contents).unwrap_or_else(|e| {
panic!(
"public lexicon {} does not parse as JSON: {e}",
file.path().display()
)
});
}
}
#[test]
fn every_public_lexicon_id_matches_its_filename_stem() {
for file in LEXICON_BUNDLE_PUBLIC.files() {
let stem = file
.path()
.file_stem()
.expect("file has stem")
.to_string_lossy()
.into_owned();
let doc: Value = serde_json::from_slice(file.contents()).unwrap();
let id = doc
.get("id")
.and_then(Value::as_str)
.unwrap_or_else(|| panic!("public file {stem}.json missing top-level id"));
let expected = format!("{LEXICON_NSID_PREFIX_PUBLIC}.{stem}");
assert_eq!(
id, expected,
"public lexicon file {stem}.json has id {id:?}; expected {expected:?}"
);
}
}
}