#![allow(dead_code)]
use std::sync::Once;
static COLOR: Once = Once::new();
pub fn shape<C>(report: &error_stack::Report<C>) -> String {
COLOR.call_once(|| {
error_stack::Report::set_color_mode(error_stack::fmt::ColorMode::None);
});
let rendered = format!("{report:?}");
rendered
.lines()
.take_while(|line| !starts_the_backtrace_section(line))
.filter(|line| !is_source_location(line) && !is_backtrace_marker(line))
.map(normalise)
.collect::<Vec<_>>()
.join("\n")
.trim_end()
.to_string()
}
pub fn per_engine(backend: amethystate::store::builder::Backend, name: &str) -> String {
match backend.extension() {
"" => format!("{name}_{}", engine_name(backend)),
extension => format!("{name}_{extension}"),
}
}
#[allow(clippy::vec_init_then_push)]
pub fn enabled_backends() -> Vec<amethystate::store::builder::Backend> {
use amethystate::store::builder::Backend;
let mut enabled = Vec::new();
#[cfg(feature = "redb")]
enabled.push(Backend::Redb);
#[cfg(feature = "sqlite")]
enabled.push(Backend::Sqlite);
#[cfg(feature = "json")]
enabled.push(Backend::Json);
#[cfg(feature = "toml")]
enabled.push(Backend::Toml);
#[cfg(feature = "ron")]
enabled.push(Backend::Ron);
#[cfg(all(feature = "localstorage", target_arch = "wasm32"))]
enabled.push(Backend::LocalStorage);
enabled
}
pub fn engine_name(backend: amethystate::store::builder::Backend) -> &'static str {
use amethystate::store::builder::Backend;
match backend {
#[cfg(feature = "redb")]
Backend::Redb => "redb",
#[cfg(feature = "sqlite")]
Backend::Sqlite => "sqlite",
#[cfg(feature = "json")]
Backend::Json => "json",
#[cfg(feature = "toml")]
Backend::Toml => "toml",
#[cfg(feature = "ron")]
Backend::Ron => "ron",
#[cfg(feature = "localstorage")]
Backend::LocalStorage => "localstorage",
_ => "an engine this helper does not name",
}
}
#[allow(unused_macros)]
macro_rules! once_per_engine {
(@one $feature:literal, $engine:ident, $variant:ident, $($body:tt)*) => {
#[cfg(feature = $feature)]
mod $engine {
#![allow(dead_code, unused_imports)]
use super::*;
const BACKEND: ::amethystate::store::builder::Backend =
::amethystate::store::builder::Backend::$variant;
const ENGINE: &str = $feature;
$($body)*
}
};
(@page $feature:literal, $engine:ident, $variant:ident, $($body:tt)*) => {
#[cfg(all(feature = $feature, target_arch = "wasm32"))]
mod $engine {
#![allow(dead_code, unused_imports)]
use super::*;
use ::wasm_bindgen_test::wasm_bindgen_test as test;
const BACKEND: ::amethystate::store::builder::Backend =
::amethystate::store::builder::Backend::$variant;
const ENGINE: &str = $feature;
$($body)*
}
};
($($body:tt)*) => {
once_per_engine!(@one "redb", redb, Redb, $($body)*);
once_per_engine!(@one "sqlite", sqlite, Sqlite, $($body)*);
once_per_engine!(@one "json", json, Json, $($body)*);
once_per_engine!(@one "toml", toml, Toml, $($body)*);
once_per_engine!(@one "ron", ron, Ron, $($body)*);
once_per_engine!(@page "localstorage", localstorage, LocalStorage, $($body)*);
};
}
#[allow(unused_imports)]
pub(crate) use once_per_engine;
#[cfg(any(feature = "json", feature = "toml", feature = "ron"))]
pub fn text_backend() -> amethystate::store::builder::Backend {
use amethystate::store::builder::Backend;
#[cfg(feature = "json")]
{
Backend::Json
}
#[cfg(all(feature = "toml", not(feature = "json")))]
{
Backend::Toml
}
#[cfg(all(feature = "ron", not(feature = "json"), not(feature = "toml")))]
{
Backend::Ron
}
}
#[cfg(any(feature = "json", feature = "toml", feature = "ron"))]
pub fn text_engine() -> amethystate::store::engine::Text {
amethystate::store::engine::Text::try_from(text_backend()).unwrap()
}
#[cfg(any(feature = "json", feature = "toml", feature = "ron"))]
pub fn bookkeeping_of(data: &std::path::Path) -> std::path::PathBuf {
use amethystate::store::StoreLayout;
match StoreLayout::of(data, text_backend()) {
StoreLayout::Sidecars { meta, .. } => meta,
other => panic!("a text store keeps its bookkeeping beside it, not {other:?}"),
}
}
#[cfg(any(feature = "json", feature = "toml", feature = "ron"))]
pub fn text_backends() -> Vec<amethystate::store::builder::Backend> {
use amethystate::store::builder::Backend;
vec![
#[cfg(feature = "json")]
Backend::Json,
#[cfg(feature = "ron")]
Backend::Ron,
#[cfg(feature = "toml")]
Backend::Toml,
]
}
pub fn measured(fields: &[(&str, &str)]) {
let object: Vec<String> = fields
.iter()
.map(|(name, value)| format!("{}:{}", quoted(name), quoted(value)))
.collect();
println!("@measured {{{}}}", object.join(","));
}
fn quoted(text: &str) -> String {
let mut out = String::with_capacity(text.len() + 2);
out.push('"');
for c in text.chars() {
match c {
'"' => out.push_str("\\\""),
'\\' => out.push_str("\\\\"),
'\n' => out.push_str("\\n"),
'\r' => out.push_str("\\r"),
'\t' => out.push_str("\\t"),
c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
c => out.push(c),
}
}
out.push('"');
out
}
const PLATFORM_SAID: &str = "<what the platform said>";
fn content(line: &str) -> &str {
line.trim_start_matches(['│', '├', '╰', '╴', '─', '▶', ' '])
}
fn glyphs(line: &str) -> &str {
&line[..line.len() - content(line).len()]
}
fn is_source_location(line: &str) -> bool {
content(line).starts_with("at ")
}
fn is_backtrace_marker(line: &str) -> bool {
content(line).starts_with("backtrace (")
}
fn starts_the_backtrace_section(line: &str) -> bool {
line.starts_with('━') || line.starts_with("backtrace no.")
}
fn normalise(line: &str) -> String {
if line.contains("(os error ") {
return match line.rfind("error: ") {
Some(at) => format!("{}{PLATFORM_SAID}", &line[..at + "error: ".len()]),
None => format!("{}{PLATFORM_SAID}", glyphs(line)),
};
}
if !line.contains("amethystate-") {
return line.to_string();
}
match line.rfind(": ") {
Some(at) => format!("{}: <store>", &line[..at]),
None => "<store>".to_string(),
}
}