#[cfg(feature = "fs")]
mod fs;
mod integrity;
mod memory;
use std::collections::HashMap;
use std::fmt;
use indexmap::IndexMap;
use crate::json::ReadError;
use crate::model::{IfcxFile, ImportNode};
use crate::validate::ValidationReport;
#[cfg(feature = "fs")]
pub use fs::{FsError, FsResolver};
pub use integrity::{check_integrity, IntegrityError};
pub use memory::MemoryResolver;
pub trait LayerResolver {
type Error;
fn key(&mut self, uri: &str, importer: Option<&str>) -> Result<String, Self::Error> {
let _ = importer;
Ok(uri.to_owned())
}
fn load(&mut self, key: &str) -> Result<Option<Vec<u8>>, Self::Error>;
}
impl<R: LayerResolver + ?Sized> LayerResolver for &mut R {
type Error = R::Error;
fn key(&mut self, uri: &str, importer: Option<&str>) -> Result<String, Self::Error> {
(**self).key(uri, importer)
}
fn load(&mut self, key: &str) -> Result<Option<Vec<u8>>, Self::Error> {
(**self).load(key)
}
}
#[derive(Debug)]
pub enum LayerError<E> {
Missing {
uri: String,
importer: Option<String>,
},
Resolver {
uri: String,
importer: Option<String>,
source: E,
},
Read { key: String, source: ReadError },
Integrity {
uri: String,
importer: String,
source: IntegrityError,
},
Cycle { chain: Vec<String> },
}
impl<E: fmt::Display> fmt::Display for LayerError<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let from = |importer: &Option<String>| match importer {
Some(key) => format!(" imported by {key:?}"),
None => String::new(),
};
match self {
Self::Missing { uri, importer } => {
write!(f, "layer {uri:?}{} not found", from(importer))
}
Self::Resolver {
uri,
importer,
source,
} => write!(
f,
"could not resolve layer {uri:?}{}: {source}",
from(importer)
),
Self::Read { key, source } => write!(f, "layer {key:?}: {source}"),
Self::Integrity {
uri,
importer,
source,
} => write!(f, "layer {uri:?} imported by {importer:?}: {source}"),
Self::Cycle { chain } => write!(f, "import cycle: {}", chain.join(" -> ")),
}
}
}
impl<E> std::error::Error for LayerError<E>
where
E: std::error::Error + 'static,
{
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Resolver { source, .. } => Some(source),
Self::Read { source, .. } => Some(source),
Self::Integrity { source, .. } => Some(source),
Self::Missing { .. } | Self::Cycle { .. } => None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Layer {
key: String,
file: IfcxFile,
}
impl Layer {
pub fn key(&self) -> &str {
&self.key
}
pub fn file(&self) -> &IfcxFile {
&self.file
}
pub fn into_file(self) -> IfcxFile {
self.file
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct LayerStack {
layers: Vec<Layer>,
}
impl LayerStack {
pub fn main(&self) -> &Layer {
&self.layers[0]
}
pub fn layers(&self) -> &[Layer] {
&self.layers
}
pub fn keys(&self) -> impl Iterator<Item = &str> {
self.layers.iter().map(Layer::key)
}
pub fn into_layers(self) -> Vec<Layer> {
self.layers
}
pub fn federate(&self) -> IfcxFile {
federate(self.layers.iter().map(Layer::file)).expect("a layer stack is never empty")
}
pub fn into_federated(self) -> IfcxFile {
federate_owned(self.layers.into_iter().map(Layer::into_file))
.expect("a layer stack is never empty")
}
pub fn validate(&self) -> Result<(), ValidationReport> {
crate::validate::validate_files(self.layers.iter().map(Layer::file)).into_result()
}
}
pub fn federate<'a>(files: impl IntoIterator<Item = &'a IfcxFile>) -> Option<IfcxFile> {
let mut files = files.into_iter().peekable();
let header = files.peek()?.header.clone();
let mut schemas = IndexMap::new();
let mut data = Vec::new();
for file in files {
for (id, schema) in &file.schemas {
schemas.insert(id.clone(), schema.clone());
}
data.extend(file.data.iter().cloned());
}
Some(IfcxFile {
header,
imports: Vec::new(),
schemas,
data,
extra: Default::default(),
})
}
pub fn federate_owned(files: impl IntoIterator<Item = IfcxFile>) -> Option<IfcxFile> {
let mut files = files.into_iter();
let first = files.next()?;
let header = first.header;
let mut schemas = first.schemas;
let mut data = first.data;
for file in files {
schemas.extend(file.schemas);
data.extend(file.data);
}
Some(IfcxFile {
header,
imports: Vec::new(),
schemas,
data,
extra: Default::default(),
})
}
#[derive(Debug)]
pub struct LayerStackBuilder<R> {
resolver: R,
allow_cycles: bool,
}
impl<R: LayerResolver> LayerStackBuilder<R> {
pub fn new(resolver: R) -> Self {
Self {
resolver,
allow_cycles: false,
}
}
pub fn allow_cycles(mut self, allow: bool) -> Self {
self.allow_cycles = allow;
self
}
pub fn resolver(&self) -> &R {
&self.resolver
}
pub fn resolver_mut(&mut self) -> &mut R {
&mut self.resolver
}
pub fn into_resolver(self) -> R {
self.resolver
}
pub fn build(&mut self, main: &str) -> Result<LayerStack, LayerError<R::Error>> {
let key = self
.resolver
.key(main, None)
.map_err(|source| LayerError::Resolver {
uri: main.to_owned(),
importer: None,
source,
})?;
let mut build = Build::new(&mut self.resolver);
let root = build.load(main, None, key)?;
build.order.push(root);
build.satisfy(root)?;
Self::finish(self.allow_cycles, build)
}
pub fn build_all<I>(&mut self, uris: I) -> Result<LayerStack, LayerError<R::Error>>
where
I: IntoIterator,
I::Item: AsRef<str>,
{
let mut build = Build::new(&mut self.resolver);
let mut pending = Vec::new();
for uri in uris {
let uri = uri.as_ref();
let key = build
.resolver
.key(uri, None)
.map_err(|source| LayerError::Resolver {
uri: uri.to_owned(),
importer: None,
source,
})?;
if !build.index.contains_key(&key) {
pending.push(build.load(uri, None, key)?);
}
}
if pending.is_empty() {
return Err(LayerError::Missing {
uri: String::new(),
importer: None,
});
}
for i in pending {
build.order.push(i);
build.satisfy(i)?;
}
Self::finish(self.allow_cycles, build)
}
fn finish(allow_cycles: bool, build: Build<'_, R>) -> Result<LayerStack, LayerError<R::Error>> {
if !allow_cycles {
if let Some(chain) = find_cycle(&build.edges) {
let chain = chain
.into_iter()
.map(|i| build.layers[i].key.clone())
.collect();
return Err(LayerError::Cycle { chain });
}
}
let mut layers: Vec<Option<Layer>> = build.layers.into_iter().map(Some).collect();
let layers = build
.order
.iter()
.map(|&i| layers[i].take().expect("each layer is placed once"))
.collect();
Ok(LayerStack { layers })
}
}
struct Build<'r, R> {
resolver: &'r mut R,
layers: Vec<Layer>,
order: Vec<usize>,
bytes: HashMap<usize, Vec<u8>>,
index: HashMap<String, usize>,
edges: Vec<Vec<usize>>,
}
impl<'r, R: LayerResolver> Build<'r, R> {
fn new(resolver: &'r mut R) -> Self {
Self {
resolver,
layers: Vec::new(),
bytes: HashMap::new(),
index: HashMap::new(),
edges: Vec::new(),
order: Vec::new(),
}
}
fn load(
&mut self,
uri: &str,
importer: Option<&str>,
key: String,
) -> Result<usize, LayerError<R::Error>> {
let bytes = match self.resolver.load(&key) {
Ok(Some(bytes)) => bytes,
Ok(None) => {
return Err(LayerError::Missing {
uri: uri.to_owned(),
importer: importer.map(str::to_owned),
})
}
Err(source) => {
return Err(LayerError::Resolver {
uri: uri.to_owned(),
importer: importer.map(str::to_owned),
source,
})
}
};
let file = IfcxFile::from_json_slice(&bytes).map_err(|source| LayerError::Read {
key: key.clone(),
source,
})?;
let i = self.layers.len();
self.index.insert(key.clone(), i);
self.bytes.insert(i, bytes);
self.layers.push(Layer { key, file });
self.edges.push(Vec::new());
Ok(i)
}
fn satisfy(&mut self, layer: usize) -> Result<(), LayerError<R::Error>> {
let imports: Vec<ImportNode> = self.layers[layer].file.imports.clone();
let importer = self.layers[layer].key.clone();
let mut pending = Vec::new();
for import in &imports {
let key = self
.resolver
.key(&import.uri, Some(&importer))
.map_err(|source| LayerError::Resolver {
uri: import.uri.clone(),
importer: Some(importer.clone()),
source,
})?;
let target = match self.index.get(&key) {
Some(&i) => i,
None => {
let i = self.load(&import.uri, Some(&importer), key)?;
pending.push(i);
i
}
};
self.edges[layer].push(target);
if let Some(expected) = &import.integrity {
check_integrity(expected, &self.bytes[&target]).map_err(|source| {
LayerError::Integrity {
uri: import.uri.clone(),
importer: importer.clone(),
source,
}
})?;
}
}
for i in pending {
self.order.push(i);
self.satisfy(i)?;
}
Ok(())
}
}
fn find_cycle(edges: &[Vec<usize>]) -> Option<Vec<usize>> {
#[derive(Clone, Copy, PartialEq)]
enum State {
New,
Open,
Done,
}
let mut state = vec![State::New; edges.len()];
for start in 0..edges.len() {
if state[start] != State::New {
continue;
}
let mut path: Vec<(usize, usize)> = vec![(start, 0)];
state[start] = State::Open;
while let Some(&mut (node, ref mut next)) = path.last_mut() {
if let Some(&target) = edges[node].get(*next) {
*next += 1;
match state[target] {
State::Open => {
let from = path.iter().position(|&(n, _)| n == target).unwrap();
let mut chain: Vec<usize> = path[from..].iter().map(|&(n, _)| n).collect();
chain.push(target);
return Some(chain);
}
State::New => {
state[target] = State::Open;
path.push((target, 0));
}
State::Done => {}
}
} else {
state[node] = State::Done;
path.pop();
}
}
}
None
}
#[cfg(test)]
mod tests {
use super::find_cycle;
#[test]
fn finds_cycles() {
assert_eq!(find_cycle(&[vec![1], vec![2], vec![]]), None);
assert_eq!(find_cycle(&[vec![1, 2], vec![2], vec![]]), None);
assert_eq!(find_cycle(&[vec![0]]), Some(vec![0, 0]));
assert_eq!(
find_cycle(&[vec![1], vec![2], vec![1]]),
Some(vec![1, 2, 1])
);
}
}