Lines
0 %
Functions
Branches
100 %
use self::index_path::IndexPath;
use crate::server_error::MyError;
use sophia::{
api::{
graph::MutableGraph,
ns::{rdf::type_, Namespace},
parser::TripleParser,
serializer::{Stringifier, TripleSerializer},
source::TripleSource,
triple::Triple,
},
inmem::graph::LightGraph,
iri::Iri,
turtle::{parser::turtle::TurtleParser, serializer::turtle::TurtleSerializer},
};
use std::{error::Error, path::Path};
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub struct IndexEntry {
pub name: String,
pub base_uri: Option<String>,
}
pub(crate) mod index_path {
use chrono::{DateTime, Utc};
use std::{
ffi::OsStr,
fs::DirEntry,
path::{Path, PathBuf},
pub(crate) struct IndexPath(PathBuf);
impl IndexPath {
pub(crate) fn to_path_buf(&self) -> PathBuf {
self.0.clone()
fn entries(&self) -> std::io::Result<impl Iterator<Item = DirEntry>> {
std::fs::read_dir(self.0.parent().unwrap()).map(|iter| {
iter.filter_map(|e| e.ok()).filter(|e| {
if e.file_name() == INDEX_NAME {
return false;
// skip entries that are not files
!e.file_type().map(|ft| ft.is_file()).unwrap_or_default()
})
pub(crate) fn file_paths(&self) -> std::io::Result<impl Iterator<Item = PathBuf>> {
self.entries().map(|f| f.map(|e| e.path()))
pub(crate) fn files(&self) -> std::io::Result<impl Iterator<Item = String>> {
self.entries()
.map(|f| f.filter_map(|path| path.file_name().to_str().map(|s| s.to_string())))
pub(crate) fn dir_name(&self) -> &OsStr {
self.0.parent().unwrap().file_name().unwrap()
pub(crate) fn date(&self) -> DateTime<Utc> {
DateTime::parse_from_rfc3339(self.dir_name().to_str().unwrap())
.unwrap()
.into()
#[derive(Debug, thiserror::Error)]
#[error("{0}")]
pub(crate) struct InvalidIndexPath(PathBuf);
const INDEX_NAME: &str = "index.ttl";
impl TryFrom<PathBuf> for IndexPath {
type Error = InvalidIndexPath;
fn try_from(dir: PathBuf) -> Result<Self, Self::Error> {
let file_name = if let Some(file_name) = dir.file_name() {
file_name
} else {
return Err(InvalidIndexPath(dir));
let file_name = if let Some(file_name) = file_name.to_str() {
if DateTime::parse_from_rfc3339(file_name).is_err() {
let path = dir.join(INDEX_NAME);
if !path.exists() {
Ok(Self(path))
impl TryFrom<&Path> for IndexPath {
fn try_from(value: &Path) -> Result<Self, Self::Error> {
IndexPath::try_from(value.to_path_buf())
impl AsRef<Path> for IndexPath {
fn as_ref(&self) -> &Path {
&self.0
pub(crate) fn read_index(index_ttl: &index_path::IndexPath) -> Result<Vec<IndexEntry>, MyError> {
println!("{}", index_ttl.as_ref().display());
let rdf = std::fs::read_to_string(index_ttl)
.map_err(|e| MyError::io_error(index_ttl.to_path_buf(), e))?;
let base: Iri<String> = Iri::new("http://example.com/".to_string())?;
let parser = TurtleParser { base: Some(base) };
let mut source = parser.parse_str(&rdf);
let mut entries = Vec::new();
let ldp = Namespace::new("http://www.w3.org/ns/ldp#")?;
let ldp_contains = ldp.get("contains")?;
source
.for_each_triple(|t| {
let p = t.p();
if ldp_contains.eq(&p) {
entries.push(IndexEntry {
name: t.s().to_string(),
base_uri: None,
});
.map_err(MyError::format_error)?;
Ok(entries)
pub fn write_index(index_path: &Path, entries: &[IndexEntry]) -> Result<(), Box<dyn Error>> {
let mut graph = LightGraph::new();
let local = Namespace::new("")?;
let ldp_container = ldp.get("Container")?;
let ldpc = local.get("")?;
graph.insert(ldpc, type_, ldp_container)?;
for entry in entries {
graph.insert(ldpc, ldp_contains, local.get(&entry.name)?)?;
let mut stringifier = TurtleSerializer::new_stringifier();
let rdf = stringifier.serialize_graph(&graph)?.as_str();
std::fs::write(index_path, rdf)?;
println!("wrote {}", index_path.display());
Ok(())
pub fn read_entries(path: &Path) -> Result<Vec<IndexEntry>, Box<dyn Error>> {
let index_path = IndexPath::try_from(path)?;
let mut names = Vec::new();
for name in index_path.files()? {
names.push(IndexEntry {
name,
Ok(names)
pub fn check_index(path: &Path) -> Result<(), Box<dyn Error>> {
read_index(&index_path)?;