use async_trait::async_trait;
use energyml_core::reflect::TypeInfo;
use energyml_core::registry::DynObject;
use crate::array::{ArrayData, ArrayLocation, ArrayMetadata};
use crate::error::Result;
use crate::resource::{Dataspace, ObjectHeader, ObjectSummary};
#[cfg(target_arch = "wasm32")]
pub use async_trait::async_trait as storage_async_trait;
#[allow(clippy::double_must_use)]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
pub trait EnergymlStorage {
fn store_kind(&self) -> &'static str;
async fn dataspaces(&self) -> Result<Vec<Dataspace>>;
async fn resources(&self) -> Result<Vec<ObjectSummary>>;
async fn headers(&self, uris: &[String]) -> Vec<Result<ObjectHeader>>;
async fn get_objects(&self, uris: &[String]) -> Vec<Result<Box<dyn DynObject>>>;
async fn supported_types(&self) -> Vec<&'static TypeInfo>;
fn arrays_of(&self, object: &dyn energyml_core::value::Reflect) -> Vec<ArrayLocation>;
async fn array_metadata(&self, location: &ArrayLocation) -> Result<ArrayMetadata>;
async fn read_array(&self, location: &ArrayLocation) -> Result<ArrayData>;
fn diagnostics(&self) -> Vec<crate::error::Diagnostic>;
fn capabilities(&self) -> crate::capabilities::Capabilities {
crate::capabilities::Capabilities::default()
}
async fn get_raw(
&self,
uris: &[String],
format: crate::raw::RawFormat,
) -> Vec<Result<crate::raw::RawObject>> {
let _ = format;
uris.iter()
.map(|_| {
Err(crate::error::Error::Unsupported {
operation: "reading objects as bytes",
store: self.store_kind(),
})
})
.collect()
}
async fn resources_in(&self, context: &crate::context::Context) -> Result<crate::context::Found> {
let dataspaces = self.dataspaces().await?;
let [dataspace] = dataspaces.as_slice() else {
return Err(crate::error::Error::Unsupported {
operation: "walking a context over several dataspaces",
store: self.store_kind(),
});
};
let resources = self.resources().await?;
let edges = self.edges(&[]).await?;
crate::context::discover(&dataspace.uri, resources, edges, context)
}
async fn deleted_resources(
&self,
filter: &crate::context::DeletedFilter,
) -> Result<Vec<crate::context::DeletedResource>> {
let _ = filter;
Err(crate::error::Error::Unsupported {
operation: "listing deleted objects",
store: self.store_kind(),
})
}
async fn get_object(&self, uri: &str) -> Result<Box<dyn DynObject>> {
let mut results = self.get_objects(std::slice::from_ref(&uri.to_string())).await;
results
.pop()
.unwrap_or_else(|| Err(crate::error::Error::NotFound(uri.to_string())))
}
async fn header(&self, uri: &str) -> Result<ObjectHeader> {
let mut results = self.headers(std::slice::from_ref(&uri.to_string())).await;
results
.pop()
.unwrap_or_else(|| Err(crate::error::Error::NotFound(uri.to_string())))
}
async fn references(&self, uris: &[String]) -> Result<Vec<crate::graph::Edge>> {
Ok(self.derived_graph(uris).await?.edges)
}
async fn edges(&self, uris: &[String]) -> Result<Vec<crate::graph::Edge>> {
let held: std::collections::HashSet<String> = self
.resources()
.await?
.into_iter()
.map(|summary| summary.uri)
.collect();
Ok(self
.references(uris)
.await?
.into_iter()
.filter(|edge| held.contains(&edge.to))
.collect())
}
async fn graph(&self, uris: &[String]) -> Result<crate::graph::Graph> {
let all = self.resources().await?;
let wanted: Vec<ObjectSummary> = if uris.is_empty() {
all
} else {
all.into_iter().filter(|s| uris.contains(&s.uri)).collect()
};
let edges = self.references(uris).await?;
let of_wanted: Vec<String> = wanted.iter().map(|s| s.uri.clone()).collect();
let headers = self.headers(&of_wanted).await;
let titles: std::collections::HashMap<String, String> = headers
.into_iter()
.flatten()
.filter_map(|header| {
header
.title
.filter(|t| !t.trim().is_empty())
.map(|title| (header.summary.uri, title))
})
.collect();
Ok(crate::graph::assemble(
wanted.iter(),
&|uri: &str| titles.get(uri).cloned(),
edges,
))
}
async fn derived_graph(&self, uris: &[String]) -> Result<crate::graph::Graph> {
let all = self.resources().await?;
let wanted: Vec<ObjectSummary> = if uris.is_empty() {
all
} else {
all.into_iter().filter(|s| uris.contains(&s.uri)).collect()
};
let of_wanted: Vec<String> = wanted.iter().map(|s| s.uri.clone()).collect();
let objects = self.get_objects(&of_wanted).await;
let held: Vec<(ObjectSummary, Box<dyn DynObject>)> = wanted
.into_iter()
.zip(objects)
.filter_map(|(summary, object)| object.ok().map(|object| (summary, object)))
.collect();
Ok(crate::graph::build(
held.iter()
.map(|(summary, object)| (summary, object.as_reflect())),
))
}
}
pub trait LocalPackage {
fn attachments(&self) -> Vec<String>;
fn read_attachment(&self, part: &str) -> Result<Vec<u8>>;
fn relationships(&self, part: &str) -> Result<Vec<crate::resource::Relationship>>;
}