From 79894f654d4710a4d481f89b462ed910564ed0429d6e13b7944042c650183339 Mon Sep 17 00:00:00 2001 From: Alex Wied <2+alex@noreply.code.graphofliberty.org> Date: Sat, 8 Aug 2026 23:50:04 -0400 Subject: [PATCH] . --- graph/src/curie.rs | 97 +++++----- graph/src/error.rs | 8 +- graph/src/inference.rs | 46 +++-- graph/src/lib.rs | 6 +- graph/src/vocab.rs | 2 +- publish/src/app.rs | 245 ++++++++++++++++---------- publish/src/args.rs | 4 +- publish/src/error.rs | 8 +- publish/src/main.rs | 79 ++++++--- publish/src/navigator.rs | 2 +- publish/src/rdf/conversion.rs | 18 +- publish/src/rdf/mod.rs | 2 +- publish/src/rdf/ontology.rs | 230 +++++++++++++++--------- publish/src/theme/mod.rs | 2 +- publish/src/theme/styles.rs | 4 +- publish/src/widget/iri_input.rs | 121 ++++++++++--- publish/src/widget/mod.rs | 2 +- publish/src/widget/navigation_area.rs | 45 ++++- publish/src/windows/mod.rs | 1 + search/src/error.rs | 2 +- search/src/index.rs | 22 +-- search/src/language.rs | 25 ++- search/src/lib.rs | 8 +- search/src/rdf.rs | 135 ++++++++++---- search/src/schema.rs | 24 +-- search/src/subtitles.rs | 21 ++- 26 files changed, 748 insertions(+), 411 deletions(-) diff --git a/graph/src/curie.rs b/graph/src/curie.rs index 0bf0209..18e0dd8 100644 --- a/graph/src/curie.rs +++ b/graph/src/curie.rs @@ -1,49 +1,52 @@ +use oxigraph::model::{GraphNameRef, NamedNodeRef}; use std::collections::BTreeMap; use std::sync::LazyLock; -use oxigraph::model::{GraphNameRef, NamedNodeRef}; const ONTOLOGY_PREFIX: &str = "https://graphofliberty.org/2026/04/ont/"; pub static PREFIXES: LazyLock> = LazyLock::new(|| { - BTreeMap::from_iter([ - ("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"), - ("rdfs", "http://www.w3.org/2000/01/rdf-schema#"), - ("owl", "http://www.w3.org/2002/07/owl#"), - ("xsd", "http://www.w3.org/2001/XMLSchema#"), - ("ldp", "http://www.w3.org/ns/ldp#"), - ("dc", "http://purl.org/dc/elements/1.1/"), - ("posix", "http://www.w3.org/ns/posix/stat#"), - ( - "ebucore", - "http://www.ebu.ch/metadata/ontologies/ebucore/ebucore#", - ), - ("prov", "http://www.w3.org/ns/prov#"), - ("locid", "http://id.loc.gov/vocabulary/identifiers/"), - ("loclang", "http://id.loc.gov/vocabulary/languages/"), - ("premis", "http://www.loc.gov/premis/rdf/v1#"), - ("premis3", "http://www.loc.gov/premis/rdf/v3/"), - ("dcterms", "http://purl.org/dc/terms/"), - ("fedora", "http://fedora.info/definitions/v4/repository#"), - ("rdaa", "http://rdaregistry.info/Elements/a/"), - ("rdac", "http://rdaregistry.info/Elements/c/"), - ("rdae", "http://rdaregistry.info/Elements/e/"), - ("rdai", "http://rdaregistry.info/Elements/i/"), - ("rdam", "http://rdaregistry.info/Elements/m/"), - ("rdan", "http://rdaregistry.info/Elements/n/"), - ("rdap", "http://rdaregistry.info/Elements/p/"), - ("rdaf", "http://rdaregistry.info/Elements/rof/"), - ("rdat", "http://rdaregistry.info/Elements/t/"), - ("rdau", "http://rdaregistry.info/Elements/u/"), - ("rdaw", "http://rdaregistry.info/Elements/w/"), - ("rdax", "http://rdaregistry.info/Elements/x/"), - ("rdaco", "http://rdaregistry.info/termList/RDAContentType/"), - ("rdact", "http://rdaregistry.info/termList/RDACarrierType/"), - ("rdamt", "http://rdaregistry.info/termList/RDAMediaType/"), - ("rdaft", "http://rdaregistry.info/termList/fileType/"), - ("schema", "https://schema.org/"), - ("gl", "http://fedora.quill.lan/rest/"), - ("glo", ONTOLOGY_PREFIX), - ].map(|(k, v)| (k.to_string(), v.to_string()))) + BTreeMap::from_iter( + [ + ("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"), + ("rdfs", "http://www.w3.org/2000/01/rdf-schema#"), + ("owl", "http://www.w3.org/2002/07/owl#"), + ("xsd", "http://www.w3.org/2001/XMLSchema#"), + ("ldp", "http://www.w3.org/ns/ldp#"), + ("dc", "http://purl.org/dc/elements/1.1/"), + ("posix", "http://www.w3.org/ns/posix/stat#"), + ( + "ebucore", + "http://www.ebu.ch/metadata/ontologies/ebucore/ebucore#", + ), + ("prov", "http://www.w3.org/ns/prov#"), + ("locid", "http://id.loc.gov/vocabulary/identifiers/"), + ("loclang", "http://id.loc.gov/vocabulary/languages/"), + ("premis", "http://www.loc.gov/premis/rdf/v1#"), + ("premis3", "http://www.loc.gov/premis/rdf/v3/"), + ("dcterms", "http://purl.org/dc/terms/"), + ("fedora", "http://fedora.info/definitions/v4/repository#"), + ("rdaa", "http://rdaregistry.info/Elements/a/"), + ("rdac", "http://rdaregistry.info/Elements/c/"), + ("rdae", "http://rdaregistry.info/Elements/e/"), + ("rdai", "http://rdaregistry.info/Elements/i/"), + ("rdam", "http://rdaregistry.info/Elements/m/"), + ("rdan", "http://rdaregistry.info/Elements/n/"), + ("rdap", "http://rdaregistry.info/Elements/p/"), + ("rdaf", "http://rdaregistry.info/Elements/rof/"), + ("rdat", "http://rdaregistry.info/Elements/t/"), + ("rdau", "http://rdaregistry.info/Elements/u/"), + ("rdaw", "http://rdaregistry.info/Elements/w/"), + ("rdax", "http://rdaregistry.info/Elements/x/"), + ("rdaco", "http://rdaregistry.info/termList/RDAContentType/"), + ("rdact", "http://rdaregistry.info/termList/RDACarrierType/"), + ("rdamt", "http://rdaregistry.info/termList/RDAMediaType/"), + ("rdaft", "http://rdaregistry.info/termList/fileType/"), + ("schema", "https://schema.org/"), + ("gl", "http://fedora.quill.lan/rest/"), + ("glo", ONTOLOGY_PREFIX), + ] + .map(|(k, v)| (k.to_string(), v.to_string())), + ) }); #[derive(Clone)] @@ -53,13 +56,13 @@ pub struct CurieHelper { impl CurieHelper { pub fn new(prefixes: BTreeMap) -> Self { - Self { - prefixes, - } + Self { prefixes } } pub fn abbreviate(&self, base: Option<&str>, iri: &str) -> Option { - if let Some(base) = base && let Some(local_name) = iri.strip_prefix(base) { + if let Some(base) = base + && let Some(local_name) = iri.strip_prefix(base) + { return Some(format!(":{local_name}")); } @@ -74,7 +77,9 @@ impl CurieHelper { pub fn expand(&self, base: Option<&str>, abbreviated_iri: &str) -> Option { let (prefix, name) = abbreviated_iri.split_once(':')?; - if prefix == "" && let Some(base) = base { + if prefix == "" + && let Some(base) = base + { Some(format!("{base}{name}")) } else { self.prefixes @@ -82,4 +87,4 @@ impl CurieHelper { .map(|base| format!("{base}{name}")) } } -} \ No newline at end of file +} diff --git a/graph/src/error.rs b/graph/src/error.rs index e66821f..ef0a2a9 100644 --- a/graph/src/error.rs +++ b/graph/src/error.rs @@ -9,13 +9,13 @@ pub enum Error { #[error(transparent)] Storage(#[from] oxigraph::store::StorageError), - + #[error(transparent)] SparqlSyntax(#[from] oxigraph::sparql::SparqlSyntaxError), #[error(transparent)] QueryEvaluation(#[from] oxigraph::sparql::QueryEvaluationError), - + #[error(transparent)] - UpdateEvaluation(#[from] oxigraph::sparql::UpdateEvaluationError) -} \ No newline at end of file + UpdateEvaluation(#[from] oxigraph::sparql::UpdateEvaluationError), +} diff --git a/graph/src/inference.rs b/graph/src/inference.rs index 9dfa5a1..b57b899 100644 --- a/graph/src/inference.rs +++ b/graph/src/inference.rs @@ -1,13 +1,14 @@ -use std::sync::Arc; use oxigraph::model::{Dataset, GraphNameRef, NamedNodeRef, Quad, QuadRef}; use oxigraph::sparql::{QueryResults, SparqlEvaluator}; use oxigraph::store::Store; -use tracing::{debug, debug_span, field}; +use tracing::{debug_span, field}; const RDF_SCHEMA_PREFIX: &str = "http://www.w3.org/2000/01/rdf-schema#"; const GL_PREFIX: &str = "https://graphofliberty.org/"; -const INFERENCE_GRAPH: GraphNameRef = GraphNameRef::NamedNode(NamedNodeRef::new_unchecked("https://graphofliberty.org/inference")); +const INFERENCE_GRAPH: GraphNameRef = GraphNameRef::NamedNode(NamedNodeRef::new_unchecked( + "https://graphofliberty.org/inference", +)); /// `prp-spo1` const SUB_PROPERTY_OF_QUERY: &str = r#"CONSTRUCT { @@ -49,14 +50,22 @@ fn run_query(name: &str, query: &str, dataset: &Dataset) -> crate::Result crate::Result { let span = debug_span!("Inference", inferences = field::Empty).entered(); - let input = dataset.iter().map(|triple| QuadRef::new(triple.subject, triple.predicate, triple.object, INFERENCE_GRAPH)); + let input = dataset.iter().map(|triple| { + QuadRef::new( + triple.subject, + triple.predicate, + triple.object, + INFERENCE_GRAPH, + ) + }); let mut ontology = self.ontology.clone(); ontology.extend(input); @@ -105,4 +119,4 @@ impl InferenceEngine { span.record("inferences", result.len()); Ok(result) } -} \ No newline at end of file +} diff --git a/graph/src/lib.rs b/graph/src/lib.rs index 1ebe92a..977694c 100644 --- a/graph/src/lib.rs +++ b/graph/src/lib.rs @@ -2,8 +2,8 @@ mod curie; //mod materialize; mod error; -pub mod vocab; pub mod inference; +pub mod vocab; -pub use curie::{PREFIXES, CurieHelper}; -pub use error::{Error, Result}; \ No newline at end of file +pub use curie::{CurieHelper, PREFIXES}; +pub use error::{Error, Result}; diff --git a/graph/src/vocab.rs b/graph/src/vocab.rs index 743ee48..38ef433 100644 --- a/graph/src/vocab.rs +++ b/graph/src/vocab.rs @@ -70,4 +70,4 @@ pub mod rdaad { pub const NAME_OF_CORPORATE_BODY: NamedNodeRef = NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/a/datatype/P50032"); -} \ No newline at end of file +} diff --git a/publish/src/app.rs b/publish/src/app.rs index 1995db2..a1fd0de 100644 --- a/publish/src/app.rs +++ b/publish/src/app.rs @@ -3,6 +3,8 @@ use crate::rdf::ontology::{LabeledIri, Ontology}; use crate::rdf::term_helper::{TermHelper, TermHelperMut}; use crate::widget::iri_input::iri_input; use crate::widget::navigation_area::navigation_area; +use gl_graph::CurieHelper; +use gl_graph::inference::InferenceEngine; use gl_search::language; use gl_search::{Schema, SearchDocument, SearchIndex, Value}; use http::StatusCode; @@ -11,7 +13,10 @@ use iced::keyboard::{Event, key}; use iced::widget::button::Style; use iced::widget::grid::Sizing; use iced::widget::text::Wrapping; -use iced::widget::{button, center, column, combo_box, container, grid, mouse_area, opaque, operation, pick_list, row, scrollable, space, stack, table, text, text_input, toggler}; +use iced::widget::{ + button, center, column, combo_box, container, grid, mouse_area, opaque, operation, pick_list, + row, scrollable, space, stack, table, text, text_input, toggler, +}; use iced::window::Settings; use iced::{Background, Color, Element, Length, Subscription, Task, color, keyboard, window}; use ldp::middleware::BasicAuthMiddleware; @@ -21,11 +26,12 @@ use ldp::reqwest_middleware::{ClientBuilder, ClientWithMiddleware}; use ldp::{RdfSource, RdfSourceUpdateResponse, ResourceRequestBuilder, SerializationOptions}; use oxigraph::io::RdfFormat; use oxigraph::model::vocab::{rdf, rdfs}; -use oxigraph::model::{BaseDirection, Dataset, Graph, NamedNode, NamedOrBlankNode, NamedOrBlankNodeRef, Quad, Term, TripleRef}; +use oxigraph::model::{ + BaseDirection, Dataset, Graph, NamedNode, NamedOrBlankNode, NamedOrBlankNodeRef, Quad, Term, + TripleRef, +}; use oxigraph::store::Store; use tracing::{debug_span, error, trace}; -use gl_graph::CurieHelper; -use gl_graph::inference::InferenceEngine; #[derive(Clone, Debug)] pub(crate) enum Message { @@ -139,9 +145,12 @@ impl Publisher { let mut abbreviated_datatypes = ontology .datatypes() .into_iter() - .map(|node| curie_helper.abbreviate(None, node.as_str()) - .unwrap_or(node.as_str().to_string()) - ).collect::>(); + .map(|node| { + curie_helper + .abbreviate(None, node.as_str()) + .unwrap_or(node.as_str().to_string()) + }) + .collect::>(); abbreviated_datatypes.sort(); let (id, task) = window::open(Settings::default()); @@ -246,21 +255,15 @@ impl Publisher { }); } Message::LoadDocument(document) => { - let messages = document - .dataset() - .quads - .iter() - .map(|(key, quad)| { - let datatype_state = combo_box::State::new( - self.abbreviated_datatypes.clone(), - ); + let messages = document.dataset().quads.iter().map(|(key, quad)| { + let datatype_state = combo_box::State::new(self.abbreviated_datatypes.clone()); - let state = RowState { - read_only: self.ontology.is_read_only(quad.as_ref()), - datatype_state, - }; - Message::AddRow(Some((key, state))) - }); + let state = RowState { + read_only: self.ontology.is_read_only(quad.as_ref()), + datatype_state, + }; + Message::AddRow(Some((key, state))) + }); task = Task::batch(messages.map(Task::done)) .chain(Task::done(Message::RunInference)) .chain(Task::done(Message::ResetState)); @@ -277,9 +280,7 @@ impl Publisher { quad.object = Term::NamedNode(empty_node); let key = self.document.dataset_mut().quads.insert(quad); - let datatype_state = combo_box::State::new( - self.abbreviated_datatypes.clone(), - ); + let datatype_state = combo_box::State::new(self.abbreviated_datatypes.clone()); let state = RowState { read_only: false, datatype_state, @@ -306,14 +307,14 @@ impl Publisher { let selected_entity_class = match action { SearchResultClickAction::Predicate(_) => Some(rdf::PROPERTY), SearchResultClickAction::Object(key) => { - self.document - .dataset() - .quads - .get(key) - .and_then(|quad| if quad.predicate == rdf::TYPE { + self.document.dataset().quads.get(key).and_then(|quad| { + if quad.predicate == rdf::TYPE { Some(rdfs::CLASS) - } else { None }) - }, + } else { + None + } + }) + } SearchResultClickAction::URLInput => None, }; @@ -332,7 +333,10 @@ impl Publisher { } if let Some(selected_entity_class) = selected_entity_class { - let selection = self.ontology.info(&selected_entity_class.into_owned(), &*language::ENGLISH_OR_UNTAGGED); + let selection = self.ontology.info( + &selected_entity_class.into_owned(), + &*language::ENGLISH_OR_UNTAGGED, + ); task = task.chain(Task::done(Message::QueryTypeUpdated(selection))); } } @@ -344,13 +348,16 @@ impl Publisher { } Message::QueryUpdated(new_query) => { if let Some(search_state) = &mut self.search_state { - let category_id = search_state.entity_selection + let category_id = search_state + .entity_selection .clone() .and_then(|info| self.ontology.category_id(&info.iri)); let query = new_query.clone(); search_state.query = new_query; - let results = self.index.query(category_id, query.as_str(), Schema::all_fields(), 25) + let results = self + .index + .query(category_id, query.as_str(), Schema::all_fields(), 25) .expect("Unable to complete search"); task = Task::done(Message::SetSearchResults(results)); }; @@ -548,7 +555,8 @@ impl Publisher { self.show_inferred_properties = value; } Message::RunInference => { - let dataset = self.document + let dataset = self + .document .dataset() .quads .iter() @@ -556,18 +564,19 @@ impl Publisher { .collect(); let engine = self.inference_engine.clone(); - task = Task::perform(tokio::task::spawn_blocking(move || { - engine.run(&dataset).unwrap() - }), |dataset| Message::SetInferredTriples(dataset.unwrap())); + task = Task::perform( + tokio::task::spawn_blocking(move || engine.run(&dataset).unwrap()), + |dataset| Message::SetInferredTriples(dataset.unwrap()), + ); } Message::SetInferredTriples(dataset) => { self.inferred_triples = Graph::from_iter(&dataset); } Message::Event(Event::KeyPressed { - key: keyboard::Key::Named(key::Named::Tab), - modifiers, - .. - }) => { + key: keyboard::Key::Named(key::Named::Tab), + modifiers, + .. + }) => { if modifiers.shift() { task = operation::focus_previous(); } else { @@ -621,22 +630,34 @@ impl Publisher { subject_input_base.on_input(move |value| Message::SubjectUpdated(key, value)) }; - let predicate_input_base = iri_input(&self.curie_helper, "Predicate", Some(&base), triple.predicate.as_str()); + let predicate_input_base = iri_input( + &self.curie_helper, + "Predicate", + Some(&base), + triple.predicate.as_str(), + ); let predicate_input = if self.ontology.is_read_only(triple.as_ref()) { predicate_input_base } else { - predicate_input_base.on_input(move |value| Message::PredicateUpdated(key, value)) - .on_control_click(Message::OpenQueryWindow(SearchResultClickAction::Predicate(key))) + predicate_input_base + .on_input(move |value| Message::PredicateUpdated(key, value)) + .on_control_click(Message::OpenQueryWindow( + SearchResultClickAction::Predicate(key), + )) .on_shift_click(Message::NavigateToPredicate(key)) }; - let predicate_info = self.ontology.info(&triple.predicate, &*language::ENGLISH_OR_UNTAGGED); + let predicate_info = self + .ontology + .info(&triple.predicate, &*language::ENGLISH_OR_UNTAGGED); let predicate_label = container(text(predicate_info.label)); let term = TermHelper::new(&triple.object); let value_label = term.value_as_named_node().and_then(|node| { - let info = self.ontology.info(&node.into_owned(), &*language::ENGLISH_OR_UNTAGGED); + let info = self + .ontology + .info(&node.into_owned(), &*language::ENGLISH_OR_UNTAGGED); Some(container(text(info.label))) }); @@ -660,7 +681,8 @@ impl Publisher { if self.ontology.is_read_only(triple.as_ref()) { base.into() } else { - base.on_input(move |value| Message::ObjectUpdated(key, value)).into() + base.on_input(move |value| Message::ObjectUpdated(key, value)) + .into() } } else { let base = iri_input(&self.curie_helper, "Object", Some(&base), value.as_str()) @@ -670,12 +692,15 @@ impl Publisher { } else { base.align_x(value_alignment) .on_input(move |value| Message::ObjectUpdated(key, value)) - .on_control_click(Message::OpenQueryWindow(SearchResultClickAction::Object(key))) + .on_control_click(Message::OpenQueryWindow(SearchResultClickAction::Object( + key, + ))) .into() } }; - let selected_datatype = term.datatype() + let selected_datatype = term + .datatype() .and_then(|node| self.curie_helper.abbreviate(None, node.as_str())); let datatype_selector: Element = if state.read_only { @@ -690,7 +715,8 @@ impl Publisher { .on_input(move |input| { let value = if input.is_empty() { None } else { Some(input) }; Message::DatatypeUpdated(key, value) - }).into() + }) + .into() }; let language = term.language().unwrap_or("en").to_owned(); @@ -748,7 +774,9 @@ impl Publisher { entities: impl IntoIterator, ) -> Element<'_, Message> { let buttons = entities.into_iter().map(|entity| { - let abbreviation = self.curie_helper.abbreviate(None, entity.iri.as_str()) + let abbreviation = self + .curie_helper + .abbreviate(None, entity.iri.as_str()) .unwrap_or_else(|| entity.iri.as_str().to_string()); let label = format!("{} ({})", entity.label, abbreviation); @@ -766,35 +794,41 @@ impl Publisher { if let Some(search_state) = &self.search_state && search_state.window_id == window { - let search_input = - text_input("Query", &search_state.query).on_input(Message::QueryUpdated) - .id("query"); + let search_input = text_input("Query", &search_state.query) + .on_input(Message::QueryUpdated) + .id("query"); let entities = Vec::new(); /*self.ontology - .searchable_classes(&*language::ENGLISH_OR_UNTAGGED) - .into_iter() - .collect::>();*/ + .searchable_classes(&*language::ENGLISH_OR_UNTAGGED) + .into_iter() + .collect::>();*/ - let type_selector = pick_list(search_state.entity_selection.as_ref(), entities, ToString::to_string) - .on_select(|selection| Message::QueryTypeUpdated(selection)); + let type_selector = pick_list( + search_state.entity_selection.as_ref(), + entities, + ToString::to_string, + ) + .on_select(|selection| Message::QueryTypeUpdated(selection)); - let mut columns = vec![ - table::column(text("CURIE"), |document: &SearchDocument| { - let iri = document.get_first(Schema::iri_field()) - .and_then(|value| value.as_str()) - .unwrap_or_default(); + let mut columns = vec![table::column(text("CURIE"), |document: &SearchDocument| { + let iri = document + .get_first(Schema::iri_field()) + .and_then(|value| value.as_str()) + .unwrap_or_default(); - let abbreviated_iri = self.curie_helper.abbreviate(None, iri) - .unwrap_or_else(|| iri.to_string()); + let abbreviated_iri = self + .curie_helper + .abbreviate(None, iri) + .unwrap_or_else(|| iri.to_string()); - button(text(abbreviated_iri).wrapping(Wrapping::Word)) - .on_press(Message::SearchResultClicked(NamedNode::new_unchecked(iri))) - .style(button::text) - }) - ]; + button(text(abbreviated_iri).wrapping(Wrapping::Word)) + .on_press(Message::SearchResultClicked(NamedNode::new_unchecked(iri))) + .style(button::text) + })]; let fields = if let Some(selection) = search_state.entity_selection.as_ref() { - self.ontology.fields_for_class(&selection.iri, &*language::ENGLISH_OR_UNTAGGED) + self.ontology + .fields_for_class(&selection.iri, &*language::ENGLISH_OR_UNTAGGED) .expect("Unable to load fields for class") } else { vec![] @@ -804,18 +838,21 @@ impl Publisher { let field_name = field.name.clone(); columns.push( table::column(text(field.label), move |document: &SearchDocument| { - let iri = document.get_first(Schema::iri_field()) + let iri = document + .get_first(Schema::iri_field()) .and_then(|value| value.as_str()) .unwrap_or_default(); - let value = document.get_first(Schema::field(&field_name, Some(language::ENGLISH_PRIMARY))) + let value = document + .get_first(Schema::field(&field_name, Some(language::ENGLISH_PRIMARY))) .and_then(|value| value.as_str()) .unwrap_or_default(); button(text(value).wrapping(Wrapping::Word)) .on_press(Message::SearchResultClicked(NamedNode::new_unchecked(iri))) .style(button::text) - }).width(Length::Fixed(256.0)) + }) + .width(Length::Fixed(256.0)), ); } @@ -825,10 +862,7 @@ impl Publisher { Some(scrollable(table(columns, &search_state.results))) }; - return column![ - row![search_input, type_selector], - results_table - ].into(); + return column![row![search_input, type_selector], results_table].into(); } let back_button = button("\u{1f870}").on_press(Message::NavigateBack); @@ -841,7 +875,9 @@ impl Publisher { .on_input(Message::URLInputChanged) .on_submit(Message::URLInputSubmitted) .on_control_click(Message::OpenQueryWindow(SearchResultClickAction::URLInput)) - .style(crate::theme::styles::text_input::required(!self.url_input_valid)); + .style(crate::theme::styles::text_input::required( + !self.url_input_valid, + )); let mut rows: Vec> = vec![]; rows = self @@ -854,7 +890,9 @@ impl Publisher { let body: Element = if self.show_new_document_buttons { let subclasses = Vec::new(); // TODO self.ontology.subclasses_of(vocab::rda::ENTITY.into_owned()); - let labeled_subclasses = subclasses.iter().map(|iri| self.ontology.info(iri, &*language::ENGLISH_OR_UNTAGGED)); + let labeled_subclasses = subclasses + .iter() + .map(|iri| self.ontology.info(iri, &*language::ENGLISH_OR_UNTAGGED)); column![self.view_new_entity_buttons(labeled_subclasses)].into() } else { column(rows).into() @@ -864,24 +902,38 @@ impl Publisher { let subject_column = table::column("Subject", |triple: TripleRef| { let curie = if let NamedOrBlankNodeRef::NamedNode(subject) = triple.subject { self.curie_helper.abbreviate(None, subject.as_str()) - } else { None }; + } else { + None + }; curie.map(text) }); let predicate_column = table::column("Predicate", |triple: TripleRef| { - let predicate_info = self.ontology.info(&triple.predicate.into_owned(), &*language::ENGLISH_OR_UNTAGGED); + let predicate_info = self.ontology.info( + &triple.predicate.into_owned(), + &*language::ENGLISH_OR_UNTAGGED, + ); let label = if predicate_info.label.is_empty() { self.curie_helper .abbreviate(None, triple.predicate.as_str()) .unwrap_or(triple.predicate.as_str().to_string()) - } else { predicate_info.label }; + } else { + predicate_info.label + }; text(label) }); - let object_column = table::column("Object", |triple: TripleRef| text(triple.object.to_string())); - Some(table([subject_column, predicate_column, object_column], self.inferred_triples.iter())) - } else { None }; + let object_column = table::column("Object", |triple: TripleRef| { + text(triple.object.to_string()) + }); + Some(table( + [subject_column, predicate_column, object_column], + self.inferred_triples.iter(), + )) + } else { + None + }; let save_button_base = button(text("\u{1f4be}")); let save_button = if self.modified { @@ -890,14 +942,13 @@ impl Publisher { save_button_base }; - let inferred_type_toggle = toggler(self.show_inferred_types) - .on_toggle(Message::SetInferredTypes); + let inferred_type_toggle = + toggler(self.show_inferred_types).on_toggle(Message::SetInferredTypes); - let inferred_property_toggle = toggler(self.show_inferred_properties) - .on_toggle(Message::SetInferredProperties); + let inferred_property_toggle = + toggler(self.show_inferred_properties).on_toggle(Message::SetInferredProperties); - let read_only_toggle = toggler(self.show_read_only) - .on_toggle(Message::SetReadOnly); + let read_only_toggle = toggler(self.show_read_only).on_toggle(Message::SetReadOnly); let footer = row![ space::horizontal(), @@ -923,8 +974,8 @@ impl Publisher { scrollable(body), scrollable(inference_table), ]) - .on_back_click(Message::NavigateBack) - .on_forward_click(Message::NavigateForward); + .on_back_click(Message::NavigateBack) + .on_forward_click(Message::NavigateForward); if self.show_overwrite_confirmation { let confirmation_modal = container(column![ @@ -973,4 +1024,4 @@ where ) ] .into() -} \ No newline at end of file +} diff --git a/publish/src/args.rs b/publish/src/args.rs index 510762c..b8131a9 100644 --- a/publish/src/args.rs +++ b/publish/src/args.rs @@ -1,5 +1,5 @@ -use std::path::PathBuf; use clap::{Args, Parser, Subcommand}; +use std::path::PathBuf; #[derive(Args)] pub(crate) struct QueryArgs { @@ -31,4 +31,4 @@ pub(crate) enum Command { pub(crate) struct AppArgs { #[command(subcommand)] pub(crate) command: Option, -} \ No newline at end of file +} diff --git a/publish/src/error.rs b/publish/src/error.rs index f2ab04a..062310d 100644 --- a/publish/src/error.rs +++ b/publish/src/error.rs @@ -6,7 +6,7 @@ pub type Result = std::result::Result; pub(crate) enum Error { #[error(transparent)] Io(#[from] std::io::Error), - + #[error(transparent)] Storage(#[from] oxigraph::store::StorageError), @@ -21,13 +21,13 @@ pub(crate) enum Error { #[error(transparent)] Search(#[from] gl_search::SearchError), - + #[error(transparent)] QueryEvaluation(#[from] oxigraph::sparql::QueryEvaluationError), - + #[error(transparent)] UpdateEvaluation(#[from] oxigraph::sparql::UpdateEvaluationError), - + #[error(transparent)] SparqlSyntax(#[from] oxigraph::sparql::SparqlSyntaxError), diff --git a/publish/src/main.rs b/publish/src/main.rs index 1dfd098..89e03a7 100644 --- a/publish/src/main.rs +++ b/publish/src/main.rs @@ -1,14 +1,17 @@ mod app; -mod error; -mod rdf; -mod windows; -mod widget; -mod navigator; -mod theme; mod args; +mod error; +mod navigator; +mod rdf; +mod theme; +mod widget; +mod windows; -use std::fs::File; +use crate::app::Publisher; +use crate::args::{AppArgs, Command}; use clap::Parser; +use gl_graph::inference::InferenceEngine; +use gl_search::SearchIndex; use iced::futures::StreamExt; use ldp::middleware::BasicAuthMiddleware; use ldp::reqwest::{Client, Url}; @@ -16,23 +19,24 @@ use ldp::reqwest_middleware::ClientBuilder; use ldp::traverse::Traverse; use oxigraph::io::{RdfFormat, RdfParser, RdfSerializer}; use oxigraph::model::{Dataset, GraphName, NamedNode, Quad, Triple}; -use oxigraph::sparql::{QueryResults, SparqlEvaluator}; use oxigraph::sparql::results::{QueryResultsFormat, QueryResultsSerializer}; +use oxigraph::sparql::{QueryResults, SparqlEvaluator}; use oxigraph::store::Store; +use std::fs::File; use tracing::{debug_span, error, field}; -use crate::app::Publisher; +use tracing_subscriber::fmt::format::FmtSpan; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; use tracing_subscriber::{EnvFilter, fmt}; -use tracing_subscriber::fmt::format::FmtSpan; -use gl_graph::inference::InferenceEngine; -use gl_search::SearchIndex; -use crate::args::{AppArgs, Command}; fn main() -> color_eyre::Result<()> { let appender = tracing_appender::rolling::never("/tmp", "publisher-log"); tracing_subscriber::registry() - .with(fmt::layer().with_span_events(FmtSpan::CLOSE).with_writer(appender)) + .with( + fmt::layer() + .with_span_events(FmtSpan::CLOSE) + .with_writer(appender), + ) .with(EnvFilter::from_default_env()) .init(); color_eyre::install()?; @@ -48,7 +52,10 @@ fn main() -> color_eyre::Result<()> { let store = Store::open("/home/alex/.local/share/org.graphofliberty.desktop/ontology")?; let inference_engine = InferenceEngine::new(store.clone()); - let graph_name = GraphName::NamedNode(NamedNode::new_unchecked(format!("file://{}", args.dataset_path.to_string_lossy()))); + let graph_name = GraphName::NamedNode(NamedNode::new_unchecked(format!( + "file://{}", + args.dataset_path.to_string_lossy() + ))); let dataset = RdfParser::from_format(RdfFormat::Turtle) .with_default_graph(graph_name) .for_reader(File::open(&args.dataset_path)?) @@ -57,11 +64,13 @@ fn main() -> color_eyre::Result<()> { let dataset_with_inferences = inference_engine.run(&dataset)?; let raw_query = String::from_utf8(std::fs::read(&args.query_path)?)?; - let mut query = SparqlEvaluator::new() - .parse_query(&raw_query)?; + let mut query = SparqlEvaluator::new().parse_query(&raw_query)?; query.dataset_mut().set_default_graph_as_union(); - match query.on_queryable_dataset(&dataset_with_inferences).execute()? { + match query + .on_queryable_dataset(&dataset_with_inferences) + .execute()? + { QueryResults::Graph(graph) => { let mut serializer = RdfSerializer::from_format(RdfFormat::Turtle) .with_prefix("rdfs", "http://www.w3.org/2000/01/rdf-schema#")? @@ -78,15 +87,20 @@ fn main() -> color_eyre::Result<()> { serializer.finish()?; } QueryResults::Solutions(solutions) => { - let json_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Json); - let mut writer = json_serializer.serialize_solutions_to_writer(std::io::stdout(), Vec::from_iter(solutions.variables().iter().cloned()))?; + let json_serializer = + QueryResultsSerializer::from_format(QueryResultsFormat::Json); + let mut writer = json_serializer.serialize_solutions_to_writer( + std::io::stdout(), + Vec::from_iter(solutions.variables().iter().cloned()), + )?; for solution in solutions.filter_map(Result::ok) { writer.serialize(&solution)?; } writer.finish()?; } QueryResults::Boolean(result) => { - let json_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Json); + let json_serializer = + QueryResultsSerializer::from_format(QueryResultsFormat::Json); json_serializer.serialize_boolean_to_writer(std::io::stdout(), result)?; } } @@ -108,7 +122,11 @@ fn main() -> color_eyre::Result<()> { { let span = debug_span!("Index Schema", documents = field::Empty).entered(); - let count = gl_search::rdf::index_schema(store, &*gl_search::language::ENGLISH_OR_UNTAGGED, &writer)?; + let count = gl_search::rdf::index_schema( + store, + &*gl_search::language::ENGLISH_OR_UNTAGGED, + &writer, + )?; span.record("documents", count); } @@ -140,8 +158,17 @@ fn main() -> color_eyre::Result<()> { let inferences = inference_engine.run(&dataset).unwrap(); dataset.extend(&inferences); - let span = debug_span!("Index Repository", rdf_sources = field::Empty, documents = field::Empty).entered(); - let document_count = gl_search::rdf::index_entity(&dataset, &*gl_search::language::ENGLISH_OR_UNTAGGED, &writer)?; + let span = debug_span!( + "Index Repository", + rdf_sources = field::Empty, + documents = field::Empty + ) + .entered(); + let document_count = gl_search::rdf::index_entity( + &dataset, + &*gl_search::language::ENGLISH_OR_UNTAGGED, + &writer, + )?; span.record("rdf_sources", rdf_source_count); span.record("documents", document_count); @@ -149,9 +176,7 @@ fn main() -> color_eyre::Result<()> { }); let mut writer = runtime.block_on(traversal_task)??; - debug_span!("Commit").in_scope(|| { - writer.commit() - })?; + debug_span!("Commit").in_scope(|| writer.commit())?; } None => { iced::daemon(Publisher::new, Publisher::update, Publisher::view) diff --git a/publish/src/navigator.rs b/publish/src/navigator.rs index b6af0a7..bfc94cd 100644 --- a/publish/src/navigator.rs +++ b/publish/src/navigator.rs @@ -33,4 +33,4 @@ impl Navigator { } self.current() } -} \ No newline at end of file +} diff --git a/publish/src/rdf/conversion.rs b/publish/src/rdf/conversion.rs index 94058c8..a755566 100644 --- a/publish/src/rdf/conversion.rs +++ b/publish/src/rdf/conversion.rs @@ -1,5 +1,5 @@ -use oxigraph::model::{NamedNode, Quad, Term}; use oxigraph::model::vocab::{rdf, xsd}; +use oxigraph::model::{NamedNode, Quad, Term}; pub fn quad_into_term(quad: Quad) -> Term { quad.object @@ -51,13 +51,19 @@ pub fn term_to_boolean(term: &Term) -> Option { } pub fn term_to_u64(term: &Term) -> Option { - if let Term::Literal(literal) = term && - (literal.datatype() == xsd::NON_NEGATIVE_INTEGER || literal.datatype() == xsd::INTEGER) { - let value: u64 = literal.value().parse().expect("Failed to parse u64 from ontology. It ought to be a non-negative integer."); + if let Term::Literal(literal) = term + && (literal.datatype() == xsd::NON_NEGATIVE_INTEGER || literal.datatype() == xsd::INTEGER) + { + let value: u64 = literal + .value() + .parse() + .expect("Failed to parse u64 from ontology. It ought to be a non-negative integer."); Some(value) - } else { None } + } else { + None + } } pub fn term_into_u64(term: Term) -> Option { term_to_u64(&term) -} \ No newline at end of file +} diff --git a/publish/src/rdf/mod.rs b/publish/src/rdf/mod.rs index 866727d..0ff046c 100644 --- a/publish/src/rdf/mod.rs +++ b/publish/src/rdf/mod.rs @@ -1,3 +1,3 @@ +pub(crate) mod conversion; pub(crate) mod ontology; pub(crate) mod term_helper; -pub(crate) mod conversion; \ No newline at end of file diff --git a/publish/src/rdf/ontology.rs b/publish/src/rdf/ontology.rs index df3f287..c9934c5 100644 --- a/publish/src/rdf/ontology.rs +++ b/publish/src/rdf/ontology.rs @@ -4,7 +4,10 @@ use gl_graph::vocab::gl; use gl_search::language::LanguageCondition; use iced::futures::TryFutureExt; use oxigraph::model::vocab::{rdf, rdfs, xsd}; -use oxigraph::model::{Dataset, Graph, GraphNameRef, LiteralRef, NamedNode, NamedNodeRef, NamedOrBlankNode, NamedOrBlankNodeRef, Term, TermRef, Triple, TripleRef}; +use oxigraph::model::{ + Dataset, Graph, GraphNameRef, LiteralRef, NamedNode, NamedNodeRef, NamedOrBlankNode, + NamedOrBlankNodeRef, Term, TermRef, Triple, TripleRef, +}; use oxigraph::sparql::{QueryResults, SparqlEvaluator}; use oxigraph::store::Store; use std::collections::{BTreeMap, BTreeSet, HashMap}; @@ -14,48 +17,53 @@ use std::sync::LazyLock; use tracing::{debug_span, field}; const ONTOLOGY_PREFIX: &str = "https://graphofliberty.org/2026/04/ont/"; -const ONTOLOGY_GRAPH_NAME: GraphNameRef = GraphNameRef::NamedNode(NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont")); +const ONTOLOGY_GRAPH_NAME: GraphNameRef = GraphNameRef::NamedNode(NamedNodeRef::new_unchecked( + "https://graphofliberty.org/2026/04/ont", +)); static PREFIXES: LazyLock> = LazyLock::new(|| { - BTreeMap::from_iter([ - ("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"), - ("rdfs", "http://www.w3.org/2000/01/rdf-schema#"), - ("owl", "http://www.w3.org/2002/07/owl#"), - ("xsd", "http://www.w3.org/2001/XMLSchema#"), - ("ldp", "http://www.w3.org/ns/ldp#"), - ("dc", "http://purl.org/dc/elements/1.1/"), - ("posix", "http://www.w3.org/ns/posix/stat#"), - ( - "ebucore", - "http://www.ebu.ch/metadata/ontologies/ebucore/ebucore#", - ), - ("prov", "http://www.w3.org/ns/prov#"), - ("locid", "http://id.loc.gov/vocabulary/identifiers/"), - ("loclang", "http://id.loc.gov/vocabulary/languages/"), - ("premis", "http://www.loc.gov/premis/rdf/v1#"), - ("premis3", "http://www.loc.gov/premis/rdf/v3/"), - ("dcterms", "http://purl.org/dc/terms/"), - ("fedora", "http://fedora.info/definitions/v4/repository#"), - ("rdaa", "http://rdaregistry.info/Elements/a/"), - ("rdac", "http://rdaregistry.info/Elements/c/"), - ("rdae", "http://rdaregistry.info/Elements/e/"), - ("rdai", "http://rdaregistry.info/Elements/i/"), - ("rdam", "http://rdaregistry.info/Elements/m/"), - ("rdan", "http://rdaregistry.info/Elements/n/"), - ("rdap", "http://rdaregistry.info/Elements/p/"), - ("rdaf", "http://rdaregistry.info/Elements/rof/"), - ("rdat", "http://rdaregistry.info/Elements/t/"), - ("rdau", "http://rdaregistry.info/Elements/u/"), - ("rdaw", "http://rdaregistry.info/Elements/w/"), - ("rdax", "http://rdaregistry.info/Elements/x/"), - ("rdaco", "http://rdaregistry.info/termList/RDAContentType/"), - ("rdact", "http://rdaregistry.info/termList/RDACarrierType/"), - ("rdamt", "http://rdaregistry.info/termList/RDAMediaType/"), - ("rdaft", "http://rdaregistry.info/termList/fileType/"), - ("schema", "https://schema.org/"), - ("gl", "http://fedora.quill.lan/rest/"), - ("glo", ONTOLOGY_PREFIX), - ].map(|(k, v)| (k.to_string(), v.to_string()))) + BTreeMap::from_iter( + [ + ("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"), + ("rdfs", "http://www.w3.org/2000/01/rdf-schema#"), + ("owl", "http://www.w3.org/2002/07/owl#"), + ("xsd", "http://www.w3.org/2001/XMLSchema#"), + ("ldp", "http://www.w3.org/ns/ldp#"), + ("dc", "http://purl.org/dc/elements/1.1/"), + ("posix", "http://www.w3.org/ns/posix/stat#"), + ( + "ebucore", + "http://www.ebu.ch/metadata/ontologies/ebucore/ebucore#", + ), + ("prov", "http://www.w3.org/ns/prov#"), + ("locid", "http://id.loc.gov/vocabulary/identifiers/"), + ("loclang", "http://id.loc.gov/vocabulary/languages/"), + ("premis", "http://www.loc.gov/premis/rdf/v1#"), + ("premis3", "http://www.loc.gov/premis/rdf/v3/"), + ("dcterms", "http://purl.org/dc/terms/"), + ("fedora", "http://fedora.info/definitions/v4/repository#"), + ("rdaa", "http://rdaregistry.info/Elements/a/"), + ("rdac", "http://rdaregistry.info/Elements/c/"), + ("rdae", "http://rdaregistry.info/Elements/e/"), + ("rdai", "http://rdaregistry.info/Elements/i/"), + ("rdam", "http://rdaregistry.info/Elements/m/"), + ("rdan", "http://rdaregistry.info/Elements/n/"), + ("rdap", "http://rdaregistry.info/Elements/p/"), + ("rdaf", "http://rdaregistry.info/Elements/rof/"), + ("rdat", "http://rdaregistry.info/Elements/t/"), + ("rdau", "http://rdaregistry.info/Elements/u/"), + ("rdaw", "http://rdaregistry.info/Elements/w/"), + ("rdax", "http://rdaregistry.info/Elements/x/"), + ("rdaco", "http://rdaregistry.info/termList/RDAContentType/"), + ("rdact", "http://rdaregistry.info/termList/RDACarrierType/"), + ("rdamt", "http://rdaregistry.info/termList/RDAMediaType/"), + ("rdaft", "http://rdaregistry.info/termList/fileType/"), + ("schema", "https://schema.org/"), + ("gl", "http://fedora.quill.lan/rest/"), + ("glo", ONTOLOGY_PREFIX), + ] + .map(|(k, v)| (k.to_string(), v.to_string())), + ) }); pub struct OntologyBuilder { @@ -82,18 +90,18 @@ impl OntologyBuilder { .collect::>(); let mut indexed_by = HashMap::new(); - for quad in store.quads_for_pattern(None, Some(gl::INDEXED_BY_FIELD), None, None) - .filter_map(Result::ok) { + for quad in store + .quads_for_pattern(None, Some(gl::INDEXED_BY_FIELD), None, None) + .filter_map(Result::ok) + { if let NamedOrBlankNode::NamedNode(subject) = quad.subject - && let Term::NamedNode(field) = quad.object { + && let Term::NamedNode(field) = quad.object + { indexed_by.insert(subject, field); } } - Ok(Ontology { - store, - prefixes, - }) + Ok(Ontology { store, prefixes }) } } @@ -146,9 +154,7 @@ pub struct Ontology { impl Ontology { pub fn builder() -> OntologyBuilder { - OntologyBuilder { - path: None, - } + OntologyBuilder { path: None } } pub fn prefixes() -> &'static BTreeMap { @@ -166,9 +172,14 @@ impl Ontology { self.store.clone() } - pub fn query_for_indexable_triples(&self, language: &LanguageCondition, source: Option) -> impl Future>> + 'static { + pub fn query_for_indexable_triples( + &self, + language: &LanguageCondition, + source: Option, + ) -> impl Future>> + 'static { let language_filter = language.to_filter_expression("fieldValue"); - let query = format!(r#"SELECT ?individual ?categoryId ?fieldName ?fieldValue + let query = format!( + r#"SELECT ?individual ?categoryId ?fieldName ?fieldValue WHERE {{ ?class a gl:SearchableClass ; gl:categoryId ?categoryId ; @@ -180,9 +191,11 @@ WHERE {{ ?property ?fieldValue . {language_filter} -}}"#); +}}"# + ); let mut sparql = SparqlEvaluator::new() - .with_prefix("gl", ONTOLOGY_PREFIX).unwrap() + .with_prefix("gl", ONTOLOGY_PREFIX) + .unwrap() .parse_query(&query) .expect("Unable to parse query"); sparql.dataset_mut().set_default_graph_as_union(); @@ -194,24 +207,40 @@ WHERE {{ sparql.on_queryable_dataset(source).execute() } else { sparql.on_store(&store).execute() - }.expect("Unable to execute indexing query"); + } + .expect("Unable to execute indexing query"); let mut results = HashMap::new(); if let QueryResults::Solutions(solutions) = query_results { let mut counter = 0usize; for solution in solutions.filter_map(Result::ok) { - let individual = solution.get("individual").and_then(conversion::term_to_named_node); + let individual = solution + .get("individual") + .and_then(conversion::term_to_named_node); let catalog_id = solution.get("categoryId").and_then(conversion::term_to_u64); let field_name = solution.get("fieldName").and_then(conversion::term_as_str); let field_value = solution.get("fieldValue").and_then(conversion::term_as_str); - if let (Some(individual), Some(catalog_id), Some(field_name), Some(field_value)) = (individual, catalog_id, field_name, field_value) { - results.entry(individual.to_owned()) + if let ( + Some(individual), + Some(catalog_id), + Some(field_name), + Some(field_value), + ) = (individual, catalog_id, field_name, field_value) + { + results + .entry(individual.to_owned()) .and_modify(|entry: &mut IndexEntry| { - entry.fields.insert(field_name.to_owned(), field_value.to_owned()); - }).or_insert(IndexEntry { - category_id: catalog_id, - fields: HashMap::from_iter([(field_name.to_owned(), field_value.to_owned())]), - }); + entry + .fields + .insert(field_name.to_owned(), field_value.to_owned()); + }) + .or_insert(IndexEntry { + category_id: catalog_id, + fields: HashMap::from_iter([( + field_name.to_owned(), + field_value.to_owned(), + )]), + }); } counter += 1; } @@ -220,27 +249,35 @@ WHERE {{ unreachable!() } results - }).map_err(error::Error::from) + }) + .map_err(error::Error::from) } pub fn category_id(&self, class: &NamedNode) -> Option { - self.store.quads_for_pattern(Some(class.into()), Some(gl::CATEGORY_ID), None, None) + self.store + .quads_for_pattern(Some(class.into()), Some(gl::CATEGORY_ID), None, None) .filter_map(Result::ok) .map(conversion::quad_into_term) .filter_map(conversion::term_into_u64) .next() } - pub fn fields_for_class(&self, class: &NamedNode, language: &LanguageCondition) -> error::Result> { + pub fn fields_for_class( + &self, + class: &NamedNode, + language: &LanguageCondition, + ) -> error::Result> { let language_filter = language.to_filter_expression("label"); - let query = format!(r#"SELECT DISTINCT ?name ?label {{ + let query = format!( + r#"SELECT DISTINCT ?name ?label {{ {class} gl:associatedProperty/gl:indexedByField ?field . ?field gl:fieldName ?name ; gl:fieldLabel ?label . {language_filter} -}}"#); +}}"# + ); let mut sparql = SparqlEvaluator::new() .with_prefix("rdfs", "http://www.w3.org/2000/01/rdf-schema#")? .with_prefix("gl", ONTOLOGY_PREFIX)? @@ -253,7 +290,9 @@ WHERE {{ let name = solution.get("name").and_then(conversion::term_as_str); let label = solution.get("label").and_then(conversion::term_as_str); - if let Some(name) = name && let Some(label) = label { + if let Some(name) = name + && let Some(label) = label + { results.push(IndexField { name: name.to_string(), label: label.to_string(), @@ -265,7 +304,9 @@ WHERE {{ } pub fn info(&self, iri: &NamedNode, language: &LanguageCondition) -> LabeledIri { - let label = self.store.quads_for_pattern(Some(iri.as_ref().into()), Some(rdfs::LABEL), None, None) + let label = self + .store + .quads_for_pattern(Some(iri.as_ref().into()), Some(rdfs::LABEL), None, None) .filter_map(Result::ok) .map(conversion::quad_into_term) .filter(|term| language.primary_matches_term(term)) @@ -280,11 +321,16 @@ WHERE {{ } pub fn subclasses_of(&self, class: &NamedNode) -> BTreeSet { - self.store.quads_for_pattern(None, Some(rdfs::SUB_CLASS_OF), Some(class.into()), None) + self.store + .quads_for_pattern(None, Some(rdfs::SUB_CLASS_OF), Some(class.into()), None) .filter_map(Result::ok) - .filter_map(|quad| if let NamedOrBlankNode::NamedNode(subject) = quad.subject { - Some(subject) - } else { None }) + .filter_map(|quad| { + if let NamedOrBlankNode::NamedNode(subject) = quad.subject { + Some(subject) + } else { + None + } + }) .collect() } @@ -300,7 +346,8 @@ WHERE {{ .filter_map(|quad| match quad.subject { NamedOrBlankNode::NamedNode(subject) => Some(subject), _ => None, - }).collect() + }) + .collect() } pub fn is_read_only<'a>(&self, triple: impl Into>) -> bool { @@ -313,23 +360,35 @@ WHERE {{ }; self.store - .quads_for_pattern(Some(NamedOrBlankNodeRef::NamedNode(subject)), Some(gl::READ_ONLY), Some(true_term), None) + .quads_for_pattern( + Some(NamedOrBlankNodeRef::NamedNode(subject)), + Some(gl::READ_ONLY), + Some(true_term), + None, + ) .filter_map(Result::ok) - .count() >= 1 + .count() + >= 1 } pub fn exclude_read_only(&self) -> impl Fn(TripleRef<'_>) -> bool + 'static { let true_term = TermRef::Literal(LiteralRef::new_typed_literal("true", xsd::BOOLEAN)); - let read_only_graph = self.store + let read_only_graph = self + .store .quads_for_pattern(None, Some(gl::READ_ONLY), Some(true_term), None) .filter_map(Result::ok) .map(Triple::from) .collect::(); move |triple| { - if triple.predicate == rdf::TYPE && let TermRef::NamedNode(class) = triple.object { + if triple.predicate == rdf::TYPE + && let TermRef::NamedNode(class) = triple.object + { read_only_graph.triples_for_subject(class).count() == 0 } else { - read_only_graph.triples_for_subject(triple.predicate).count() == 0 + read_only_graph + .triples_for_subject(triple.predicate) + .count() + == 0 } } } @@ -353,7 +412,12 @@ WHERE {{ if let Term::BlankNode(blank_node) = quad.object { let iter = self .store - .quads_for_pattern(Some(NamedOrBlankNodeRef::BlankNode(blank_node.as_ref())), None, None, None) + .quads_for_pattern( + Some(NamedOrBlankNodeRef::BlankNode(blank_node.as_ref())), + None, + None, + None, + ) .filter_map(Result::ok) .map(Triple::from) .map(move |mut triple| { @@ -368,4 +432,4 @@ WHERE {{ None } } -} \ No newline at end of file +} diff --git a/publish/src/theme/mod.rs b/publish/src/theme/mod.rs index 00d9633..95938dc 100644 --- a/publish/src/theme/mod.rs +++ b/publish/src/theme/mod.rs @@ -1 +1 @@ -pub mod styles; \ No newline at end of file +pub mod styles; diff --git a/publish/src/theme/styles.rs b/publish/src/theme/styles.rs index b38062b..93b1442 100644 --- a/publish/src/theme/styles.rs +++ b/publish/src/theme/styles.rs @@ -1,7 +1,7 @@ pub mod text_input { - use iced::{color, Theme}; use iced::widget::text_input; use iced::widget::text_input::{Status, Style}; + use iced::{Theme, color}; pub fn required(invalid: bool) -> impl Fn(&Theme, Status) -> Style { move |theme: &Theme, status| { @@ -16,4 +16,4 @@ pub mod text_input { Style { border, ..default } } } -} \ No newline at end of file +} diff --git a/publish/src/widget/iri_input.rs b/publish/src/widget/iri_input.rs index beb2d6d..12f2552 100644 --- a/publish/src/widget/iri_input.rs +++ b/publish/src/widget/iri_input.rs @@ -1,12 +1,12 @@ -use iced::{keyboard, widget, Element, Event, Length, Rectangle, Size}; -use iced::advanced::{mouse, text, Shell, renderer}; +use gl_graph::CurieHelper; use iced::advanced::layout::{Limits, Node}; +use iced::advanced::widget::{Operation, Tree, tree}; use iced::advanced::{Layout, Widget}; -use iced::advanced::widget::{tree, Tree, Operation}; +use iced::advanced::{Shell, mouse, renderer, text}; use iced::clipboard::Content; use iced::mouse::{Cursor, Interaction}; use iced::widget::text_input::{Catalog, Status, Style, StyleFn}; -use gl_graph::CurieHelper; +use iced::{Element, Event, Length, Rectangle, Size, keyboard, widget}; pub struct State { control: bool, @@ -31,8 +31,15 @@ where Theme: Catalog, Renderer: text::Renderer, { - pub fn new(curie_helper: &'a CurieHelper, placeholder: &'a str, base: Option<&str>, iri: &str) -> Self { - let display_value = curie_helper.abbreviate(base, iri).unwrap_or_else(|| iri.to_string()); + pub fn new( + curie_helper: &'a CurieHelper, + placeholder: &'a str, + base: Option<&str>, + iri: &str, + ) -> Self { + let display_value = curie_helper + .abbreviate(base, iri) + .unwrap_or_else(|| iri.to_string()); let text_input = widget::TextInput::new(placeholder, display_value); Self { curie_helper, @@ -96,7 +103,7 @@ where } impl<'a, Message, Theme, Renderer> From> -for Element<'a, Message, Theme, Renderer> + for Element<'a, Message, Theme, Renderer> where Message: Clone + 'a, Theme: Catalog + 'a, @@ -107,7 +114,12 @@ where } } -pub fn iri_input<'a, Message, Theme, Renderer>(curie_helper: &'a CurieHelper, placeholder: &'a str, base: Option<&str>, iri: &str) -> IriInput<'a, Message, Theme, Renderer> +pub fn iri_input<'a, Message, Theme, Renderer>( + curie_helper: &'a CurieHelper, + placeholder: &'a str, + base: Option<&str>, + iri: &str, +) -> IriInput<'a, Message, Theme, Renderer> where Message: Clone, Theme: Catalog, @@ -116,7 +128,8 @@ where IriInput::new(curie_helper, placeholder, base, iri) } -impl Widget for IriInput<'_, Message, Theme, Renderer> +impl Widget + for IriInput<'_, Message, Theme, Renderer> where Message: Clone, Theme: Catalog, @@ -127,11 +140,34 @@ where } fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &Limits) -> Node { - Widget::layout(&mut self.text_input, &mut tree.children[0], renderer, limits) + Widget::layout( + &mut self.text_input, + &mut tree.children[0], + renderer, + limits, + ) } - fn draw(&self, tree: &Tree, renderer: &mut Renderer, theme: &Theme, style: &renderer::Style, layout: Layout<'_>, cursor: Cursor, viewport: &Rectangle) { - Widget::draw(&self.text_input, &tree.children[0], renderer, theme, style, layout, cursor, viewport) + fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: Cursor, + viewport: &Rectangle, + ) { + Widget::draw( + &self.text_input, + &tree.children[0], + renderer, + theme, + style, + layout, + cursor, + viewport, + ) } fn tag(&self) -> tree::Tag { @@ -156,10 +192,20 @@ where renderer: &Renderer, operation: &mut dyn Operation, ) { - self.text_input.operate(&mut tree.children[0], layout, renderer, operation); + self.text_input + .operate(&mut tree.children[0], layout, renderer, operation); } - fn update(&mut self, tree: &mut Tree, event: &Event, layout: Layout<'_>, cursor: Cursor, renderer: &Renderer, shell: &mut Shell<'_, Message>, viewport: &Rectangle) { + fn update( + &mut self, + tree: &mut Tree, + event: &Event, + layout: Layout<'_>, + cursor: Cursor, + renderer: &Renderer, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + ) { let state = tree.state.downcast_mut::(); match event { @@ -169,12 +215,16 @@ where } Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => { if cursor.is_over(layout.bounds()) { - if state.control && let Some(on_control_click) = &self.on_control_click { + if state.control + && let Some(on_control_click) = &self.on_control_click + { shell.publish(on_control_click.clone()); shell.capture_event(); } - if state.shift && let Some(on_shift_click) = &self.on_shift_click { + if state.shift + && let Some(on_shift_click) = &self.on_shift_click + { shell.publish(on_shift_click.clone()); shell.capture_event(); } @@ -183,7 +233,16 @@ where _ => {} } - Widget::update(&mut self.text_input, &mut tree.children[0], event, layout, cursor, renderer, shell, viewport); + Widget::update( + &mut self.text_input, + &mut tree.children[0], + event, + layout, + cursor, + renderer, + shell, + viewport, + ); let clipboard = shell.clipboard_mut(); if let Some(Content::Text(content)) = &clipboard.write { @@ -193,15 +252,29 @@ where } } - fn mouse_interaction(&self, tree: &Tree, layout: Layout<'_>, cursor: Cursor, viewport: &Rectangle, renderer: &Renderer) -> Interaction { + fn mouse_interaction( + &self, + tree: &Tree, + layout: Layout<'_>, + cursor: Cursor, + viewport: &Rectangle, + renderer: &Renderer, + ) -> Interaction { let state = tree.state.downcast_ref::(); let interaction = if cursor.is_over(layout.bounds()) { - if (state.control && self.on_control_click.is_some()) || - (state.shift && self.on_shift_click.is_some()) { + if (state.control && self.on_control_click.is_some()) + || (state.shift && self.on_shift_click.is_some()) + { Some(Interaction::Pointer) - } else { None } - } else { None }; + } else { + None + } + } else { + None + }; - interaction.unwrap_or_else(|| Widget::mouse_interaction(&self.text_input, tree, layout, cursor, viewport, renderer)) + interaction.unwrap_or_else(|| { + Widget::mouse_interaction(&self.text_input, tree, layout, cursor, viewport, renderer) + }) } -} \ No newline at end of file +} diff --git a/publish/src/widget/mod.rs b/publish/src/widget/mod.rs index 55056c9..f5b1423 100644 --- a/publish/src/widget/mod.rs +++ b/publish/src/widget/mod.rs @@ -1,2 +1,2 @@ pub mod iri_input; -pub mod navigation_area; \ No newline at end of file +pub mod navigation_area; diff --git a/publish/src/widget/navigation_area.rs b/publish/src/widget/navigation_area.rs index d162963..ed4fc38 100644 --- a/publish/src/widget/navigation_area.rs +++ b/publish/src/widget/navigation_area.rs @@ -1,10 +1,10 @@ -use iced::advanced::{mouse, text, Layout, Shell, Widget, overlay}; -use iced::{Element, Event, Length, Rectangle, Size, Vector}; use iced::advanced::layout::{Limits, Node}; use iced::advanced::renderer::Style; use iced::advanced::widget::{Operation, Tree}; +use iced::advanced::{Layout, Shell, Widget, mouse, overlay, text}; use iced::mouse::Cursor; use iced::widget::text_input::Catalog; +use iced::{Element, Event, Length, Rectangle, Size, Vector}; pub struct NavigationArea<'a, Message, Theme, Renderer> where @@ -45,7 +45,7 @@ where } impl<'a, Message, Theme, Renderer> From> -for Element<'a, Message, Theme, Renderer> + for Element<'a, Message, Theme, Renderer> where Message: Clone + 'a, Theme: Catalog + 'a, @@ -56,7 +56,9 @@ where } } -pub fn navigation_area<'a, Message, Theme, Renderer>(content: impl Into>) -> NavigationArea<'a, Message, Theme, Renderer> +pub fn navigation_area<'a, Message, Theme, Renderer>( + content: impl Into>, +) -> NavigationArea<'a, Message, Theme, Renderer> where Message: Clone, Theme: Catalog, @@ -65,7 +67,8 @@ where NavigationArea::new(content) } -impl Widget for NavigationArea<'_, Message, Theme, Renderer> +impl Widget + for NavigationArea<'_, Message, Theme, Renderer> where Message: Clone, Theme: Catalog, @@ -81,7 +84,16 @@ where .layout(&mut tree.children[0], renderer, limits) } - fn draw(&self, tree: &Tree, renderer: &mut Renderer, theme: &Theme, style: &Style, layout: Layout<'_>, cursor: Cursor, viewport: &Rectangle) { + fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &Style, + layout: Layout<'_>, + cursor: Cursor, + viewport: &Rectangle, + ) { self.content.as_widget().draw( &tree.children[0], renderer, @@ -97,13 +109,28 @@ where tree.diff_children(std::slice::from_mut(&mut self.content)); } - fn operate(&mut self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn Operation) { + fn operate( + &mut self, + tree: &mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn Operation, + ) { self.content .as_widget_mut() .operate(&mut tree.children[0], layout, renderer, operation); } - fn update(&mut self, tree: &mut Tree, event: &Event, layout: Layout<'_>, cursor: Cursor, renderer: &Renderer, shell: &mut Shell<'_, Message>, viewport: &Rectangle) { + fn update( + &mut self, + tree: &mut Tree, + event: &Event, + layout: Layout<'_>, + cursor: Cursor, + renderer: &Renderer, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + ) { self.content.as_widget_mut().update( &mut tree.children[0], event, @@ -168,4 +195,4 @@ where translation, ) } -} \ No newline at end of file +} diff --git a/publish/src/windows/mod.rs b/publish/src/windows/mod.rs index 473a0f4..4c0d52d 100644 --- a/publish/src/windows/mod.rs +++ b/publish/src/windows/mod.rs @@ -0,0 +1 @@ + diff --git a/search/src/error.rs b/search/src/error.rs index 783fff0..4096e11 100644 --- a/search/src/error.rs +++ b/search/src/error.rs @@ -11,7 +11,7 @@ pub enum SearchError { #[error(transparent)] OpenDirectoryError(#[from] tantivy::directory::error::OpenDirectoryError), - + #[error(transparent)] Io(#[from] std::io::Error), diff --git a/search/src/index.rs b/search/src/index.rs index 2750bc9..9b6e50c 100644 --- a/search/src/index.rs +++ b/search/src/index.rs @@ -1,7 +1,7 @@ -use std::collections::HashMap; -use std::fs; use crate::error; use crate::schema::Schema; +use std::collections::HashMap; +use std::fs; use std::path::PathBuf; use tantivy::collector::TopDocs; use tantivy::directory::{ManagedDirectory, MmapDirectory}; @@ -24,9 +24,7 @@ impl SearchIndexBuilder { pub fn build(self) -> error::Result { let ngram_32 = NgramTokenizer::new(1, 32, false)?; - let ngram_32_lowercase = TextAnalyzer::builder(ngram_32) - .filter(LowerCaser) - .build(); + let ngram_32_lowercase = TextAnalyzer::builder(ngram_32).filter(LowerCaser).build(); let tokenizer_manager = TokenizerManager::default(); tokenizer_manager.register("ngram_32", ngram_32_lowercase); @@ -49,10 +47,7 @@ impl SearchIndexBuilder { .reload_policy(ReloadPolicy::OnCommitWithDelay) .try_into()?; - Ok(SearchIndex { - index, - reader, - }) + Ok(SearchIndex { index, reader }) } } @@ -82,9 +77,7 @@ impl SearchIndex { let parser = QueryParser::for_index(&self.index, default_fields); let (user_query, _) = parser.parse_query_lenient(user_query); - let mut subqueries = vec![ - (Occur::Must, user_query) - ]; + let mut subqueries = vec![(Occur::Must, user_query)]; if let Some(type_) = type_ { let doc_type_term = Term::from_field_u64(Schema::discriminant_field(), type_); @@ -94,11 +87,12 @@ impl SearchIndex { let query = BooleanQuery::new(subqueries); let searcher = self.reader.searcher(); - let results: Vec = searcher.search(&query, &TopDocs::with_limit(limit).order_by_score())? + let results: Vec = searcher + .search(&query, &TopDocs::with_limit(limit).order_by_score())? .iter() .map(|(_, address)| searcher.doc(*address)) .filter_map(Result::ok) .collect(); Ok(results) } -} \ No newline at end of file +} diff --git a/search/src/language.rs b/search/src/language.rs index f50c942..ab72f9f 100644 --- a/search/src/language.rs +++ b/search/src/language.rs @@ -1,11 +1,13 @@ -use std::sync::LazyLock; use oxigraph::model::{Term, TermRef}; use oxilangtag::LanguageTag; +use std::sync::LazyLock; pub const ENGLISH_PRIMARY: &str = "en"; pub static ENGLISH_OR_UNTAGGED: LazyLock = LazyLock::new(|| { - LanguageCondition::ExactMatchOrUntagged(LanguageTag::parse(ENGLISH_PRIMARY.to_string()).unwrap()) + LanguageCondition::ExactMatchOrUntagged( + LanguageTag::parse(ENGLISH_PRIMARY.to_string()).unwrap(), + ) }); pub enum LanguageCondition { @@ -18,13 +20,16 @@ pub enum LanguageCondition { impl LanguageCondition { pub fn primary_matches_term<'a>(&self, term: impl Into>) -> bool { if let TermRef::Literal(literal) = term.into() { - let tag = literal.language() + let tag = literal + .language() .map(LanguageTag::parse_and_normalize) .and_then(Result::ok); match (tag, self) { - (Some(language), LanguageCondition::ExactMatchOnly(expectation)) | - (Some(language), LanguageCondition::ExactMatchOrUntagged(expectation)) => language.primary_language() == expectation.primary_language(), + (Some(language), LanguageCondition::ExactMatchOnly(expectation)) + | (Some(language), LanguageCondition::ExactMatchOrUntagged(expectation)) => { + language.primary_language() == expectation.primary_language() + } (None, LanguageCondition::ExactMatchOrUntagged(_)) => true, (None, LanguageCondition::UntaggedOnly) => true, (_, LanguageCondition::AnyOrNone) => true, @@ -45,10 +50,14 @@ impl LanguageCondition { pub fn to_filter_expression(&self, var: &str) -> String { match self { - LanguageCondition::ExactMatchOnly(language) => format!(r#"FILTER(langMATCHES(LANG(?{var}), "{language}"))"#), - LanguageCondition::ExactMatchOrUntagged(language) => format!(r#"FILTER(langMATCHES(LANG(?{var}), "{language}") || !hasLANG(?{var}))"#), + LanguageCondition::ExactMatchOnly(language) => { + format!(r#"FILTER(langMATCHES(LANG(?{var}), "{language}"))"#) + } + LanguageCondition::ExactMatchOrUntagged(language) => { + format!(r#"FILTER(langMATCHES(LANG(?{var}), "{language}") || !hasLANG(?{var}))"#) + } LanguageCondition::UntaggedOnly => format!("FILTER(!hasLANG(?{var}))"), LanguageCondition::AnyOrNone => "".to_string(), } } -} \ No newline at end of file +} diff --git a/search/src/lib.rs b/search/src/lib.rs index c3cee3e..ad9a563 100644 --- a/search/src/lib.rs +++ b/search/src/lib.rs @@ -1,15 +1,15 @@ mod error; mod index; +pub mod language; +pub mod rdf; mod schema; pub mod subtitles; -pub mod rdf; -pub mod language; use oxigraph::model::NamedNodeRef; pub use tantivy::TantivyDocument as SearchDocument; pub use tantivy::doc; -pub use tantivy::schema::document::{Document, Value}; pub use tantivy::indexer::IndexWriter; +pub use tantivy::schema::document::{Document, Value}; pub use error::{Result, SearchError}; use gl_graph::vocab; @@ -55,4 +55,4 @@ impl DocType { _ => None, } } -} \ No newline at end of file +} diff --git a/search/src/rdf.rs b/search/src/rdf.rs index 985b699..e20f250 100644 --- a/search/src/rdf.rs +++ b/search/src/rdf.rs @@ -1,14 +1,14 @@ -use std::collections::HashMap; -use oxigraph::model::{Dataset, NamedNode, NamedNodeRef, NamedOrBlankNodeRef, Term, TermRef}; -use oxigraph::model::vocab::{rdf, xsd}; -use oxigraph::sparql::{QueryResults, SparqlEvaluator}; -use oxigraph::store::Store; -use tantivy::IndexWriter; -use tantivy::schema::{Field, OwnedValue}; -use gl_graph::{vocab, CurieHelper}; use crate::DocType; use crate::language::LanguageCondition; use crate::schema::Schema; +use gl_graph::{CurieHelper, vocab}; +use oxigraph::model::vocab::{rdf, xsd}; +use oxigraph::model::{Dataset, NamedNode, NamedNodeRef, NamedOrBlankNodeRef, Term, TermRef}; +use oxigraph::sparql::{QueryResults, SparqlEvaluator}; +use oxigraph::store::Store; +use std::collections::HashMap; +use tantivy::IndexWriter; +use tantivy::schema::{Field, OwnedValue}; fn term_to_named_node(term: &Term) -> Option<&NamedNode> { if let Term::NamedNode(node) = term { @@ -52,13 +52,18 @@ fn term_ref_as_str(term: TermRef<'_>) -> Option<&str> { } } -pub fn index_entity(dataset: &Dataset, language: &LanguageCondition, writer: &IndexWriter>) -> crate::Result { +pub fn index_entity( + dataset: &Dataset, + language: &LanguageCondition, + writer: &IndexWriter>, +) -> crate::Result { let curie_helper = CurieHelper::new((&*gl_graph::PREFIXES).clone()); let mut subject_type_map = HashMap::new(); for quad in dataset.quads_for_pattern(None, Some(rdf::TYPE), None, None) { if let NamedOrBlankNodeRef::NamedNode(subject) = quad.subject { - subject_type_map.entry(subject) + subject_type_map + .entry(subject) .and_modify(|types: &mut Vec<_>| types.push(quad.object)) .or_insert_with(|| vec![quad.object]); } @@ -67,19 +72,28 @@ pub fn index_entity(dataset: &Dataset, language: &LanguageCondition, writer: &In let mut counter = 0usize; for (subject, types) in subject_type_map { for type_ in types { - if let Some(doc_type) = term_ref_as_named_node(type_).and_then(DocType::try_from_named_node) { + if let Some(doc_type) = + term_ref_as_named_node(type_).and_then(DocType::try_from_named_node) + { let mut doc = match doc_type { DocType::Person => index_person(dataset, subject, language), DocType::CorporateBody => index_corporate_body(dataset, subject, language), - _ => continue + _ => continue, }; let subject_str = subject.as_str(); - doc.insert(Schema::discriminant_field(), OwnedValue::U64(doc_type as u64)); - doc.insert(Schema::iri_field(), OwnedValue::Str(subject_str.to_string())); + doc.insert( + Schema::discriminant_field(), + OwnedValue::U64(doc_type as u64), + ); + doc.insert( + Schema::iri_field(), + OwnedValue::Str(subject_str.to_string()), + ); - let curie = curie_helper.abbreviate(None, subject_str) + let curie = curie_helper + .abbreviate(None, subject_str) .map(OwnedValue::Str) .unwrap_or(OwnedValue::Null); doc.insert(Schema::curie_field(), curie); @@ -93,8 +107,18 @@ pub fn index_entity(dataset: &Dataset, language: &LanguageCondition, writer: &In Ok(counter) } -fn index_person(dataset: &Dataset, subject: NamedNodeRef<'_>, language: &LanguageCondition) -> HashMap { - let given_name = dataset.quads_for_pattern(Some(subject.into()), Some(vocab::rdaad::GIVEN_NAME), None, None) +fn index_person( + dataset: &Dataset, + subject: NamedNodeRef<'_>, + language: &LanguageCondition, +) -> HashMap { + let given_name = dataset + .quads_for_pattern( + Some(subject.into()), + Some(vocab::rdaad::GIVEN_NAME), + None, + None, + ) .map(|q| q.object) .filter(|term| language.primary_matches_term(*term)) .filter_map(term_ref_as_str) @@ -103,7 +127,13 @@ fn index_person(dataset: &Dataset, subject: NamedNodeRef<'_>, language: &Languag .next() .unwrap_or_else(|| OwnedValue::Null); - let surname = dataset.quads_for_pattern(Some(subject.into()), Some(vocab::rdaad::SURNAME), None, None) + let surname = dataset + .quads_for_pattern( + Some(subject.into()), + Some(vocab::rdaad::SURNAME), + None, + None, + ) .map(|q| q.object) .filter(|term| language.primary_matches_term(*term)) .filter_map(term_ref_as_str) @@ -113,13 +143,29 @@ fn index_person(dataset: &Dataset, subject: NamedNodeRef<'_>, language: &Languag .unwrap_or_else(|| OwnedValue::Null); HashMap::from_iter([ - (Schema::field("given_name", language.primary_language()), given_name), - (Schema::field("surname", language.primary_language()), surname), + ( + Schema::field("given_name", language.primary_language()), + given_name, + ), + ( + Schema::field("surname", language.primary_language()), + surname, + ), ]) } -fn index_corporate_body(dataset: &Dataset, subject: NamedNodeRef<'_>, language: &LanguageCondition) -> HashMap { - let name = dataset.quads_for_pattern(Some(subject.into()), Some(vocab::rdaad::NAME_OF_CORPORATE_BODY), None, None) +fn index_corporate_body( + dataset: &Dataset, + subject: NamedNodeRef<'_>, + language: &LanguageCondition, +) -> HashMap { + let name = dataset + .quads_for_pattern( + Some(subject.into()), + Some(vocab::rdaad::NAME_OF_CORPORATE_BODY), + None, + None, + ) .map(|q| q.object) .filter(|term| language.primary_matches_term(*term)) .filter_map(term_ref_as_str) @@ -128,18 +174,24 @@ fn index_corporate_body(dataset: &Dataset, subject: NamedNodeRef<'_>, language: .next() .unwrap_or_else(|| OwnedValue::Null); - HashMap::from_iter([ - (Schema::field("corporate_name", language.primary_language()), name), - ]) + HashMap::from_iter([( + Schema::field("corporate_name", language.primary_language()), + name, + )]) } -pub fn index_schema(store: Store, language: &LanguageCondition, writer: &IndexWriter>) -> crate::Result { +pub fn index_schema( + store: Store, + language: &LanguageCondition, + writer: &IndexWriter>, +) -> crate::Result { let curie_helper = CurieHelper::new((&*gl_graph::PREFIXES).clone()); let label_filter = language.to_filter_expression("label"); let description_filter = language.to_filter_expression("description"); - let query = format!(r#"SELECT ?class ?subject ?label ?description WHERE {{ + let query = format!( + r#"SELECT ?class ?subject ?label ?description WHERE {{ VALUES ?class {{ rdf:Property rdfs:Class skos:Concept }} ?subject a ?class ; rdfs:label ?label . @@ -149,11 +201,15 @@ pub fn index_schema(store: Store, language: &LanguageCondition, writer: &IndexWr OPTIONAL {{ ?subject skos:definition ?definition }} BIND(COALESCE(?definition, ?comment) AS ?description) {description_filter} -}}"#); +}}"# + ); let mut sparql = SparqlEvaluator::new() - .with_prefix("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#").unwrap() - .with_prefix("rdfs", "http://www.w3.org/2000/01/rdf-schema#").unwrap() - .with_prefix("skos", "http://www.w3.org/2004/02/skos/core#").unwrap() + .with_prefix("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#") + .unwrap() + .with_prefix("rdfs", "http://www.w3.org/2000/01/rdf-schema#") + .unwrap() + .with_prefix("skos", "http://www.w3.org/2004/02/skos/core#") + .unwrap() .parse_query(&query)?; sparql.dataset_mut().set_default_graph_as_union(); @@ -167,7 +223,8 @@ pub fn index_schema(store: Store, language: &LanguageCondition, writer: &IndexWr for solution in solutions.filter_map(Result::ok) { let mut document = HashMap::with_capacity(4); - let discriminant = solution.get("class") + let discriminant = solution + .get("class") .and_then(term_to_named_node) .and_then(DocType::try_from_named_node) .map(|doc_type| doc_type as u64) @@ -175,7 +232,8 @@ pub fn index_schema(store: Store, language: &LanguageCondition, writer: &IndexWr .unwrap_or(OwnedValue::Null); document.insert(Schema::discriminant_field(), discriminant); - let subject_str = solution.get("subject") + let subject_str = solution + .get("subject") .and_then(term_to_named_node) .map(NamedNode::as_str); @@ -185,17 +243,20 @@ pub fn index_schema(store: Store, language: &LanguageCondition, writer: &IndexWr .unwrap_or(OwnedValue::Null); document.insert(Schema::curie_field(), curie); - let subject = subject_str.map(OwnedValue::from) + let subject = subject_str + .map(OwnedValue::from) .unwrap_or(OwnedValue::Null); document.insert(Schema::iri_field(), subject); - let label = solution.get("label") + let label = solution + .get("label") .and_then(term_as_str) .map(OwnedValue::from) .unwrap_or(OwnedValue::Null); document.insert(label_field, label); - let definition = solution.get("description") + let definition = solution + .get("description") .and_then(term_as_str) .map(OwnedValue::from) .unwrap_or(OwnedValue::Null); @@ -208,4 +269,4 @@ pub fn index_schema(store: Store, language: &LanguageCondition, writer: &IndexWr } else { unreachable!() } -} \ No newline at end of file +} diff --git a/search/src/schema.rs b/search/src/schema.rs index a78c544..9cf5f1d 100644 --- a/search/src/schema.rs +++ b/search/src/schema.rs @@ -11,17 +11,21 @@ pub struct Schema; impl Schema { pub fn schema() -> &'static TantivySchema { SCHEMA.get_or_init(|| { - let stored_ngram32 = TextOptions::default().set_indexing_options( - TextFieldIndexing::default() - .set_index_option(IndexRecordOption::WithFreqsAndPositions) - .set_tokenizer("ngram_32"), - ).set_stored(); + let stored_ngram32 = TextOptions::default() + .set_indexing_options( + TextFieldIndexing::default() + .set_index_option(IndexRecordOption::WithFreqsAndPositions) + .set_tokenizer("ngram_32"), + ) + .set_stored(); - let stored_en_stem = TextOptions::default().set_indexing_options( - TextFieldIndexing::default() - .set_index_option(IndexRecordOption::WithFreqsAndPositions) - .set_tokenizer("en_stem"), - ).set_stored(); + let stored_en_stem = TextOptions::default() + .set_indexing_options( + TextFieldIndexing::default() + .set_index_option(IndexRecordOption::WithFreqsAndPositions) + .set_tokenizer("en_stem"), + ) + .set_stored(); let mut schema_builder = TantivySchema::builder(); schema_builder.add_u64_field("discriminant", schema::FAST | schema::INDEXED); diff --git a/search/src/subtitles.rs b/search/src/subtitles.rs index a0d9848..a318bec 100644 --- a/search/src/subtitles.rs +++ b/search/src/subtitles.rs @@ -1,7 +1,7 @@ +use crate::{Schema, SearchDocument}; use subtitler; use subtitler::SubtitleFormat; use tantivy::doc; -use crate::{Schema, SearchDocument}; pub fn bytes_to_documents(data: &[u8]) -> crate::Result> { let subtitle_start = Schema::field("subtitle_start", None); @@ -10,12 +10,15 @@ pub fn bytes_to_documents(data: &[u8]) -> crate::Result> { let subtitle_file = subtitler::parse_bytes(data)?; let subtitles = subtitle_file.subtitles(); - let documents = subtitles.iter().map(|subtitle| { - doc!( - subtitle_start => subtitle.start, - subtitle_end => subtitle.end, - subtitle_content => subtitle.text, - ) - }).collect::>(); + let documents = subtitles + .iter() + .map(|subtitle| { + doc!( + subtitle_start => subtitle.start, + subtitle_end => subtitle.end, + subtitle_content => subtitle.text, + ) + }) + .collect::>(); Ok(documents) -} \ No newline at end of file +}