use std::collections::HashMap;
use std::fmt;
use std::sync::{Arc, Mutex, RwLock};
use crate::Result;
use crate::factory;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum FeatureValue {
String(String),
Integer(i64),
Boolean(bool),
}
#[derive(Clone, Default)]
pub struct FeatureMap {
inner: Arc<RwLock<HashMap<String, FeatureValue>>>,
}
impl fmt::Debug for FeatureMap {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let map = self
.inner
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner);
f.debug_map().entries(map.iter()).finish()
}
}
impl FeatureMap {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn set(&self, iri: impl Into<String>, value: FeatureValue) {
self.inner
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.insert(iri.into(), value);
}
#[must_use]
pub fn get(&self, iri: &str) -> Option<FeatureValue> {
self.inner
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.get(iri)
.cloned()
}
}
pub type StorageFeatures = FeatureMap;
#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
pub enum LogLevel {
Debug,
Info,
#[default]
Warn,
Error,
}
impl LogLevel {
#[must_use]
pub const fn name(self) -> &'static str {
match self {
Self::Debug => "debug",
Self::Info => "info",
Self::Warn => "warn",
Self::Error => "error",
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum LogFacility {
General,
Model,
Io,
Query,
Utility,
}
impl LogFacility {
#[must_use]
pub const fn name(self) -> &'static str {
match self {
Self::General => "general",
Self::Model => "model",
Self::Io => "io",
Self::Query => "query",
Self::Utility => "utility",
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LogRecord {
pub level: LogLevel,
pub facility: LogFacility,
pub message: String,
}
impl fmt::Display for LogRecord {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"[{} {}] {}",
self.level.name(),
self.facility.name(),
self.message
)
}
}
type LogHandler = Arc<dyn Fn(&LogRecord) + Send + Sync>;
pub type BridgeToken = usize;
#[derive(Clone, Default)]
pub struct World {
features: FeatureMap,
min_level: Arc<RwLock<LogLevel>>,
handler: Arc<Mutex<Option<LogHandler>>>,
raptor: Arc<RwLock<Option<BridgeToken>>>,
raptor_init: Arc<RwLock<Option<BridgeToken>>>,
rasqal: Arc<RwLock<Option<BridgeToken>>>,
rasqal_init: Arc<RwLock<Option<BridgeToken>>>,
}
impl fmt::Debug for World {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("World")
.field(
"min_level",
&*self
.min_level
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner),
)
.field(
"handler_set",
&self
.handler
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.is_some(),
)
.field("raptor_set", &self.raptor().is_some())
.field("rasqal_set", &self.rasqal().is_some())
.finish_non_exhaustive()
}
}
impl World {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn set_feature(&self, iri: impl Into<String>, value: FeatureValue) {
self.features.set(iri, value);
}
#[must_use]
pub fn feature(&self, iri: &str) -> Option<FeatureValue> {
self.features.get(iri)
}
pub fn set_log_level(&self, level: LogLevel) {
*self
.min_level
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = level;
}
#[must_use]
pub fn log_level(&self) -> LogLevel {
*self
.min_level
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
pub fn set_log_handler<F>(&self, handler: F)
where
F: Fn(&LogRecord) + Send + Sync + 'static,
{
*self
.handler
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = Some(Arc::new(handler));
}
pub fn clear_log_handler(&self) {
*self
.handler
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner) = None;
}
pub fn log(&self, level: LogLevel, facility: LogFacility, message: impl Into<String>) {
if level < self.log_level() {
return;
}
let record = LogRecord {
level,
facility,
message: message.into(),
};
#[cfg(feature = "tracing")]
{
match level {
LogLevel::Debug => {
tracing::debug!(facility = record.facility.name(), "{}", record.message)
}
LogLevel::Info => {
tracing::info!(facility = record.facility.name(), "{}", record.message)
}
LogLevel::Warn => {
tracing::warn!(facility = record.facility.name(), "{}", record.message)
}
LogLevel::Error => {
tracing::error!(facility = record.facility.name(), "{}", record.message)
}
}
}
if let Some(handler) = self
.handler
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.as_ref()
.cloned()
{
handler(&record);
}
}
pub fn set_raptor(&self, token: Option<BridgeToken>) {
*self
.raptor
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = token;
}
#[must_use]
pub fn raptor(&self) -> Option<BridgeToken> {
*self
.raptor
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
pub fn set_raptor_bridge(&self, token: Option<BridgeToken>) {
self.set_raptor(token);
}
#[must_use]
pub fn raptor_bridge(&self) -> Option<BridgeToken> {
self.raptor()
}
pub fn set_raptor_init_handler(&self, token: Option<BridgeToken>) {
*self
.raptor_init
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = token;
}
#[must_use]
pub fn raptor_init_handler(&self) -> Option<BridgeToken> {
*self
.raptor_init
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
pub fn set_rasqal(&self, token: Option<BridgeToken>) {
*self
.rasqal
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = token;
}
#[must_use]
pub fn rasqal(&self) -> Option<BridgeToken> {
*self
.rasqal
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
pub fn set_rasqal_init_handler(&self, token: Option<BridgeToken>) {
*self
.rasqal_init
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner) = token;
}
#[must_use]
pub fn rasqal_init_handler(&self) -> Option<BridgeToken> {
*self
.rasqal_init
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
pub fn register_parser_factory(&self, name: &str) -> Result<()> {
factory::register_parser_factory(name)
}
pub fn register_serializer_factory(&self, name: &str) -> Result<()> {
factory::register_serializer_factory(name)
}
pub fn register_storage_factory(&self, name: &str) -> Result<()> {
factory::register_storage_factory(name)
}
pub fn register_query_factory(&self, name: &str) -> Result<()> {
factory::register_query_factory(name)
}
}