use super::{
ByteSource, ByteSourceHandle, Error, FormatDescriptor, NamespaceSource, Result, SourceHints,
};
pub trait Driver: Send + Sync {
fn descriptor(&self) -> FormatDescriptor;
fn open(
&self, source: super::ByteSourceHandle, options: OpenOptions<'_>,
) -> Result<Box<dyn DataSource>>;
}
pub trait DataSource: Send + Sync {
fn descriptor(&self) -> FormatDescriptor;
fn facets(&self) -> DataSourceFacets;
fn byte_source(&self) -> Option<&dyn ByteSource> {
None
}
fn namespace(&self) -> Option<&dyn NamespaceSource> {
None
}
fn table_source(&self) -> Option<&dyn TableSource> {
None
}
fn views(&self) -> Result<Vec<DataViewRecord>> {
Ok(Vec::new())
}
fn open_view(
&self, _selector: &DataViewSelector<'_>, _options: OpenOptions<'_>,
) -> Result<Box<dyn DataSource>> {
Err(Error::unsupported(format!(
"{} does not expose child views",
self.descriptor().id
)))
}
fn reopen(&self, _options: OpenOptions<'_>) -> Result<Box<dyn DataSource>> {
Err(Error::unsupported(format!(
"{} does not support reopening with different options",
self.descriptor().id
)))
}
}
pub trait TableSource: Send + Sync {
fn telemetry_name(&self) -> &'static str {
std::any::type_name::<Self>()
}
}
pub struct ByteViewSource {
descriptor: FormatDescriptor,
bytes: ByteSourceHandle,
}
impl ByteViewSource {
pub fn new(descriptor: FormatDescriptor, bytes: ByteSourceHandle) -> Self {
Self { descriptor, bytes }
}
}
impl DataSource for ByteViewSource {
fn descriptor(&self) -> FormatDescriptor {
self.descriptor
}
fn facets(&self) -> DataSourceFacets {
DataSourceFacets::bytes()
}
fn byte_source(&self) -> Option<&dyn ByteSource> {
Some(self.bytes.as_ref())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct DataSourceFacets {
pub bytes: bool,
pub namespace: bool,
pub tables: bool,
pub views: bool,
}
impl DataSourceFacets {
pub const fn none() -> Self {
Self {
bytes: false,
namespace: false,
tables: false,
views: false,
}
}
pub const fn bytes() -> Self {
Self {
bytes: true,
namespace: false,
tables: false,
views: false,
}
}
pub const fn namespace() -> Self {
Self {
bytes: false,
namespace: true,
tables: false,
views: false,
}
}
pub const fn with_views(mut self) -> Self {
self.views = true;
self
}
pub const fn with_bytes(mut self) -> Self {
self.bytes = true;
self
}
pub const fn with_namespace(mut self) -> Self {
self.namespace = true;
self
}
pub const fn with_tables(mut self) -> Self {
self.tables = true;
self
}
}
#[derive(Clone, Copy, Default)]
pub struct OpenOptions<'a> {
pub hints: SourceHints<'a>,
pub credentials: &'a [Credential<'a>],
pub view: Option<DataViewSelector<'a>>,
pub verification: VerificationPolicy,
}
impl<'a> OpenOptions<'a> {
pub fn new() -> Self {
Self::default()
}
pub fn with_hints(mut self, hints: SourceHints<'a>) -> Self {
self.hints = hints;
self
}
pub fn with_credentials(mut self, credentials: &'a [Credential<'a>]) -> Self {
self.credentials = credentials;
self
}
pub fn with_view(mut self, view: DataViewSelector<'a>) -> Self {
self.view = Some(view);
self
}
pub fn with_verification(mut self, verification: VerificationPolicy) -> Self {
self.verification = verification;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Credential<'a> {
Password(&'a str),
RecoveryPassword(&'a str),
KeyData(&'a [u8]),
NamedKey(&'a str, &'a [u8]),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum VerificationPolicy {
#[default]
BestEffort,
Strict,
Full,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DataViewId(Box<[u8]>);
impl DataViewId {
pub fn from_bytes(bytes: impl Into<Vec<u8>>) -> Self {
Self(bytes.into().into_boxed_slice())
}
pub fn from_u64(value: u64) -> Self {
Self::from_bytes(value.to_le_bytes().to_vec())
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DataViewKind {
Partition,
Volume,
Snapshot,
Subvolume,
Dataset,
Other,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DataViewTag {
pub key: String,
pub value: String,
}
impl DataViewTag {
pub fn new(key: impl Into<String>, value: impl Into<String>) -> Self {
Self {
key: key.into(),
value: value.into(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DataViewRecord {
pub id: DataViewId,
pub kind: DataViewKind,
pub name: Option<String>,
pub parent_id: Option<DataViewId>,
pub facets: DataSourceFacets,
pub tags: Vec<DataViewTag>,
}
impl DataViewRecord {
pub fn new(id: DataViewId, kind: DataViewKind, facets: DataSourceFacets) -> Self {
Self {
id,
kind,
name: None,
parent_id: None,
facets,
tags: Vec::new(),
}
}
pub fn with_name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
pub fn with_parent_id(mut self, parent_id: DataViewId) -> Self {
self.parent_id = Some(parent_id);
self
}
pub fn with_tag(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.tags.push(DataViewTag::new(key, value));
self
}
pub fn tag_value(&self, key: &str) -> Option<&str> {
self
.tags
.iter()
.find(|tag| tag.key == key)
.map(|tag| tag.value.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DataViewSelector<'a> {
Id(&'a DataViewId),
Index(usize),
Name(&'a str),
Tag(&'a str, &'a str),
}
impl DataViewSelector<'_> {
pub fn matches(&self, view: &DataViewRecord) -> bool {
match self {
Self::Id(id) => view.id == **id,
Self::Index(index) => {
view
.tag_value("index")
.and_then(|value| value.parse::<usize>().ok())
== Some(*index)
}
Self::Name(name) => view.name.as_deref() == Some(*name),
Self::Tag(key, value) => view.tag_value(key) == Some(*value),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn selector_matches_index_name_and_tags() {
let view = DataViewRecord::new(
DataViewId::from_u64(7),
DataViewKind::Volume,
DataSourceFacets::namespace(),
)
.with_name("system")
.with_tag("index", "3")
.with_tag("role", "system");
assert!(DataViewSelector::Index(3).matches(&view));
assert!(DataViewSelector::Name("system").matches(&view));
assert!(DataViewSelector::Tag("role", "system").matches(&view));
}
}