mod durable;
mod fjall;
mod format_v1;
pub(crate) use durable::{DurableStore, DurableStoreOps};
pub(crate) use format_v1::stored_matching_quad;
use std::path::{Path, PathBuf};
use crate::{Error, Result};
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum StorageBackend {
Memory,
Fjall,
}
const KNOWN_OPTIONAL_BACKENDS: &[&str] = &[
"redb",
"rocksdb",
"sqlite",
"lmdb",
"sled",
"leveldb",
"mdbx",
"surrealkv",
];
const LEGACY_REDLAND_BACKENDS: &[&str] = &[
"hashes",
"file",
"mysql",
"postgresql",
"postgres",
"tstore",
"uri",
"virtuoso",
];
impl StorageBackend {
#[must_use]
pub const fn name(self) -> &'static str {
match self {
Self::Memory => "memory",
Self::Fjall => "fjall",
}
}
pub fn from_name(name: &str) -> Result<Self> {
let normalized = name.trim().to_ascii_lowercase();
match normalized.as_str() {
"memory" | "mem" => Ok(Self::Memory),
"fjall" => Ok(Self::Fjall),
other if KNOWN_OPTIONAL_BACKENDS.contains(&other) => Err(Error::Unsupported(format!(
"storage backend '{other}' is known but not compiled into this build"
))),
other if LEGACY_REDLAND_BACKENDS.contains(&other) => Err(Error::Unsupported(format!(
"legacy Redland storage backend '{name}' is unsupported; export to N-Quads and use memory or fjall (see docs/design/0.4-legacy-storage.md)"
))),
other => Err(Error::Unsupported(format!(
"storage backend '{other}' is not recognized"
))),
}
}
}
#[must_use]
pub fn compiled_backends() -> &'static [StorageBackend] {
&[StorageBackend::Memory, StorageBackend::Fjall]
}
#[must_use]
pub fn is_known_backend_name(name: &str) -> bool {
let normalized = name.trim().to_ascii_lowercase();
matches!(normalized.as_str(), "memory" | "mem" | "fjall")
|| KNOWN_OPTIONAL_BACKENDS.contains(&normalized.as_str())
|| LEGACY_REDLAND_BACKENDS.contains(&normalized.as_str())
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct OpenOptions {
backend: StorageBackend,
path: PathBuf,
read_only: bool,
create: bool,
}
impl OpenOptions {
#[must_use]
pub fn new(backend: StorageBackend, path: impl AsRef<Path>) -> Self {
Self {
backend,
path: path.as_ref().to_owned(),
read_only: false,
create: true,
}
}
#[must_use]
pub fn fjall(path: impl AsRef<Path>) -> Self {
Self::new(StorageBackend::Fjall, path)
}
#[must_use]
pub fn backend(&self) -> StorageBackend {
self.backend
}
#[must_use]
pub fn path(&self) -> &Path {
&self.path
}
#[must_use]
pub fn read_only(mut self, read_only: bool) -> Self {
self.read_only = read_only;
self
}
#[must_use]
pub fn create(mut self, create: bool) -> Self {
self.create = create;
self
}
#[must_use]
pub fn is_read_only(&self) -> bool {
self.read_only
}
#[must_use]
pub fn can_create(&self) -> bool {
self.create
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct StorageCapabilities {
pub backend: StorageBackend,
pub durable: bool,
pub transactions: bool,
pub sync: bool,
pub clear: bool,
pub read_only: bool,
pub bulk_load: bool,
}
impl StorageCapabilities {
#[must_use]
pub const fn memory() -> Self {
Self {
backend: StorageBackend::Memory,
durable: false,
transactions: true,
sync: true,
clear: true,
read_only: false,
bulk_load: true,
}
}
#[must_use]
pub const fn fjall(read_only: bool) -> Self {
Self {
backend: StorageBackend::Fjall,
durable: true,
transactions: !read_only,
sync: true,
clear: !read_only,
read_only,
bulk_load: !read_only,
}
}
}