.
This commit is contained in:
@@ -8,6 +8,7 @@ edition = "2024"
|
||||
gl-search.workspace = true
|
||||
ldp.workspace = true
|
||||
|
||||
clap.workspace = true
|
||||
color-eyre.workspace = true
|
||||
csv = "1.4"
|
||||
http.workspace = true
|
||||
|
||||
+31
-111
@@ -1,40 +1,34 @@
|
||||
use std::collections::HashMap;
|
||||
use crate::rdf::ontology::{IndexEntry, LabeledIri, Ontology};
|
||||
use crate::navigator::Navigator;
|
||||
use crate::rdf::curie::CurieHelper;
|
||||
use crate::rdf::ontology::{LabeledIri, Ontology};
|
||||
use crate::rdf::term_helper::{TermHelper, TermHelperMut};
|
||||
use gl_search::{Schema, SearchDocument, SearchIndex, doc, Value};
|
||||
use crate::rdf::vocab::rda;
|
||||
use crate::widget::iri_input::iri_input;
|
||||
use crate::widget::navigation_area::navigation_area;
|
||||
use gl_search::language;
|
||||
use gl_search::{IndexWriter, Schema, SearchDocument, SearchIndex, Value};
|
||||
use http::StatusCode;
|
||||
use iced::alignment::Horizontal;
|
||||
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::window::Settings;
|
||||
use iced::{Background, Color, Element, Length, Subscription, Task, color, window};
|
||||
use iced::widget::text::Wrapping;
|
||||
use iced::{Background, Color, Element, Length, Subscription, Task, color, keyboard, window};
|
||||
use ldp::middleware::BasicAuthMiddleware;
|
||||
use ldp::model::{KeyedDataset, QuadKey};
|
||||
use ldp::reqwest::{Client, Url};
|
||||
use ldp::reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
|
||||
use ldp::traverse::Traverse;
|
||||
use ldp::{RdfSource, RdfSourceUpdateResponse, ResourceRequestBuilder, SerializationOptions};
|
||||
use oxigraph::io::RdfFormat;
|
||||
use oxigraph::model::vocab::{rdf, rdfs};
|
||||
use oxigraph::model::{BaseDirection, Dataset, NamedNode, NamedOrBlankNode, Quad, Term};
|
||||
use tracing::{debug_span, error, field, trace};
|
||||
use crate::navigator::Navigator;
|
||||
use crate::rdf::curie::CurieHelper;
|
||||
use crate::rdf::language;
|
||||
use crate::rdf::vocab::rda;
|
||||
use crate::widget::iri_input::iri_input;
|
||||
use crate::widget::navigation_area::navigation_area;
|
||||
use tracing::{debug_span, error, trace};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum Message {
|
||||
None,
|
||||
RebuildIndex,
|
||||
Traverse,
|
||||
AddRdfSource(RdfSource<Dataset>),
|
||||
ConcludeTraversal,
|
||||
IndexQueryResults(HashMap<NamedNode, IndexEntry>),
|
||||
WindowClosed(window::Id),
|
||||
URLInputChanged(String),
|
||||
URLInputSubmitted,
|
||||
@@ -71,6 +65,7 @@ pub(crate) enum Message {
|
||||
NavigateForward,
|
||||
ResetState,
|
||||
SetReadOnly(bool),
|
||||
Event(Event),
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
@@ -181,90 +176,6 @@ impl Publisher {
|
||||
trace!(?message);
|
||||
|
||||
match message {
|
||||
Message::RebuildIndex => {
|
||||
let mut writer = self.index.writer().expect("Unable to obtain index writer");
|
||||
let clear_index_task = Task::perform(tokio::task::spawn_blocking(move || {
|
||||
let _span = debug_span!("Clear Index").entered();
|
||||
writer.delete_all_documents().expect("Unable to clear index");
|
||||
writer.commit().expect("Unable to commit index operation");
|
||||
}), |_| Message::None);
|
||||
|
||||
let index_future = self.ontology
|
||||
.query_for_indexable_triples(&*language::ENGLISH_OR_UNTAGGED, None);
|
||||
let index_task = Task::perform(index_future, |task_result| {
|
||||
match task_result {
|
||||
Ok(results) => Message::IndexQueryResults(results),
|
||||
Err(err) => Message::ShowError(err.to_string()),
|
||||
}
|
||||
});
|
||||
|
||||
task = clear_index_task.chain(Task::batch([
|
||||
index_task,
|
||||
Task::done(Message::Traverse),
|
||||
]));
|
||||
}
|
||||
Message::Traverse => {
|
||||
let client = self.http_client.clone();
|
||||
let root = Url::parse(&self.url_input).expect("Invalid URL");
|
||||
let stream = Traverse::new(client, root, None);
|
||||
|
||||
task = Task::run(stream, |result| match result {
|
||||
Ok(rdf_source) => Message::AddRdfSource(rdf_source),
|
||||
Err(err) => Message::ShowError(format!("Unable to fetch RDF Source: {err}")),
|
||||
}).chain(Task::done(Message::ConcludeTraversal));
|
||||
|
||||
self.traversal = Some(self.ontology.to_dataset());
|
||||
}
|
||||
Message::AddRdfSource(rdf_source) => {
|
||||
if let Some(traversal) = &mut self.traversal {
|
||||
traversal.extend(rdf_source.dataset());
|
||||
}
|
||||
}
|
||||
Message::ConcludeTraversal => {
|
||||
if let Some(traversal) = self.traversal.take() {
|
||||
let index_task = self.ontology
|
||||
.query_for_indexable_triples(&*language::ENGLISH_OR_UNTAGGED, Some(traversal));
|
||||
task = Task::perform(index_task, |task_result| {
|
||||
match task_result {
|
||||
Ok(results) => Message::IndexQueryResults(results),
|
||||
Err(err) => Message::ShowError(err.to_string()),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Message::IndexQueryResults(results) => {
|
||||
let mut writer = self.index.writer().expect("Unable to obtain index writer");
|
||||
let curie_helper = self.curie_helper.clone();
|
||||
task = Task::perform(tokio::task::spawn_blocking(move || {
|
||||
let span = debug_span!("Index Query Results", documents = field::Empty).entered();
|
||||
let mut counter = 0usize;
|
||||
for (individual, entry) in results {
|
||||
let mut document = doc!(
|
||||
Schema::type_field() => entry.category_id,
|
||||
Schema::iri_field() => individual.as_str(),
|
||||
);
|
||||
|
||||
if let Some(curie) = curie_helper.abbreviate(None, individual.as_str()) {
|
||||
document.add_text(Schema::curie_field(), curie);
|
||||
}
|
||||
|
||||
for (key, value) in entry.fields {
|
||||
document.add_text(Schema::field(&key, language::ENGLISH_PRIMARY), value.as_str());
|
||||
}
|
||||
writer.add_document(document).expect("Failed to add document to search index");
|
||||
counter += 1;
|
||||
}
|
||||
|
||||
span.record("documents", counter);
|
||||
writer.commit()
|
||||
}), |result| {
|
||||
if let Err(err) = result {
|
||||
Message::ShowError(err.to_string())
|
||||
} else {
|
||||
Message::None
|
||||
}
|
||||
});
|
||||
}
|
||||
Message::WindowClosed(id) => {
|
||||
if self.window_id == id {
|
||||
task = iced::exit();
|
||||
@@ -355,9 +266,8 @@ impl Publisher {
|
||||
.map(|datatype| self.curie_helper.abbreviate(None, datatype.as_str())
|
||||
.unwrap_or(datatype.as_str().to_string()));
|
||||
|
||||
let datatype_state = combo_box::State::with_selection(
|
||||
let datatype_state = combo_box::State::new(
|
||||
self.abbreviated_datatypes.clone(),
|
||||
datatype.as_ref(),
|
||||
);
|
||||
let state = RowState {
|
||||
read_only,
|
||||
@@ -425,7 +335,7 @@ impl Publisher {
|
||||
|
||||
let query = new_query.clone();
|
||||
search_state.query = new_query;
|
||||
let results = self.index.query(category_id, query.as_str(), Schema::all_fields(), 10)
|
||||
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));
|
||||
};
|
||||
@@ -616,6 +526,17 @@ impl Publisher {
|
||||
Message::SetReadOnly(value) => {
|
||||
self.show_read_only = value;
|
||||
}
|
||||
Message::Event(Event::KeyPressed {
|
||||
key: keyboard::Key::Named(key::Named::Tab),
|
||||
modifiers,
|
||||
..
|
||||
}) => {
|
||||
if modifiers.shift() {
|
||||
task = operation::focus_previous();
|
||||
} else {
|
||||
task = operation::focus_next();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
task
|
||||
@@ -628,6 +549,7 @@ impl Publisher {
|
||||
pub(crate) fn subscription(&self) -> Subscription<Message> {
|
||||
Subscription::batch([
|
||||
window::close_events().map(Message::WindowClosed),
|
||||
keyboard::listen().map(Message::Event),
|
||||
])
|
||||
}
|
||||
|
||||
@@ -697,7 +619,7 @@ impl Publisher {
|
||||
// If the datatype is something, then it's not a named node, and we should use a plain
|
||||
// text_input as opposed to an iri_input.
|
||||
let object_input: Element<Message> = if term.datatype().is_some() {
|
||||
let base = text_input("Object", value.as_str());
|
||||
let base = text_input("Object", value.clone());
|
||||
if self.ontology.is_read_only(triple.as_ref()) {
|
||||
base.into()
|
||||
} else {
|
||||
@@ -734,9 +656,10 @@ impl Publisher {
|
||||
}).into()
|
||||
};
|
||||
|
||||
let language = term.language().unwrap_or("en").to_owned();
|
||||
let language_input = match term.datatype() {
|
||||
Some(rdf::LANG_STRING) | Some(rdf::DIR_LANG_STRING) => Some(container(
|
||||
text_input("Language", term.language().unwrap_or("en")).on_input(move |input| {
|
||||
text_input("Language", language).on_input(move |input| {
|
||||
let value = if input.is_empty() { None } else { Some(input) };
|
||||
Message::LanguageUpdated(key, value)
|
||||
}),
|
||||
@@ -848,7 +771,7 @@ impl Publisher {
|
||||
.and_then(|value| value.as_str())
|
||||
.unwrap_or_default();
|
||||
|
||||
let value = document.get_first(Schema::field(&field_name, 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();
|
||||
|
||||
@@ -877,8 +800,6 @@ impl Publisher {
|
||||
|
||||
let add_row_button = button("Add row").on_press(Message::AddRow(None));
|
||||
|
||||
let reindex_ontology_button = button("Rebuild index").on_press(Message::RebuildIndex);
|
||||
|
||||
let address_input = iri_input(&self.curie_helper, "URL", None, &self.url_input)
|
||||
.on_input(Message::URLInputChanged)
|
||||
.on_submit(Message::URLInputSubmitted)
|
||||
@@ -920,7 +841,6 @@ impl Publisher {
|
||||
|
||||
let content = navigation_area(column![
|
||||
row![
|
||||
reindex_ontology_button,
|
||||
add_row_button,
|
||||
back_button,
|
||||
forward_button,
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
|
||||
#[derive(Args)]
|
||||
pub(crate) struct SearchArgs {
|
||||
#[arg(short, long, value_name = "DOC TYPE")]
|
||||
pub(crate) discriminant: Option<u64>,
|
||||
|
||||
#[arg(value_name = "QUERY")]
|
||||
pub(crate) query: String,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum Command {
|
||||
Search(SearchArgs),
|
||||
Reindex,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
#[clap(version, long_about = None)]
|
||||
pub(crate) struct AppArgs {
|
||||
#[command(subcommand)]
|
||||
pub(crate) command: Option<Command>,
|
||||
}
|
||||
+122
-5
@@ -5,12 +5,29 @@ mod windows;
|
||||
mod widget;
|
||||
mod navigator;
|
||||
mod theme;
|
||||
mod args;
|
||||
|
||||
use crate::app::Publisher;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::thread;
|
||||
use clap::Parser;
|
||||
use color_eyre::eyre::eyre;
|
||||
use iced::futures::StreamExt;
|
||||
use iced::Task;
|
||||
use ldp::middleware::BasicAuthMiddleware;
|
||||
use ldp::reqwest::{Client, Url};
|
||||
use ldp::reqwest_middleware::ClientBuilder;
|
||||
use ldp::traverse::Traverse;
|
||||
use oxigraph::model::Dataset;
|
||||
use tracing::{debug, debug_span, error, field};
|
||||
use crate::app::{Message, Publisher};
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
use tracing_subscriber::{EnvFilter, fmt};
|
||||
use tracing_subscriber::fmt::format::FmtSpan;
|
||||
use gl_search::{DocType, Document, Schema, SearchIndex};
|
||||
use crate::args::{AppArgs, Command};
|
||||
use crate::rdf::curie::CurieHelper;
|
||||
use crate::rdf::ontology::Ontology;
|
||||
|
||||
fn main() -> color_eyre::Result<()> {
|
||||
let appender = tracing_appender::rolling::never("/tmp", "publisher-log");
|
||||
@@ -20,10 +37,110 @@ fn main() -> color_eyre::Result<()> {
|
||||
.init();
|
||||
color_eyre::install()?;
|
||||
|
||||
iced::daemon(Publisher::new, Publisher::update, Publisher::view)
|
||||
.title(Publisher::title)
|
||||
.subscription(Publisher::subscription)
|
||||
.run()?;
|
||||
let ontology = Ontology::builder()
|
||||
.with_path("/home/alex/.local/share/org.graphofliberty.desktop/ontology")
|
||||
.build()
|
||||
.expect("Failed to build ontology");
|
||||
|
||||
let mut index = SearchIndex::builder()
|
||||
.with_path("/home/alex/.local/share/org.graphofliberty.desktop/index")
|
||||
.build()
|
||||
.expect("Failed to build search index");
|
||||
|
||||
let args = AppArgs::parse();
|
||||
match args.command {
|
||||
Some(Command::Search(args)) => {
|
||||
for doc in index.query(args.discriminant, &args.query, Schema::all_fields(), 5)? {
|
||||
println!("{}", doc.to_json(Schema::schema()));
|
||||
}
|
||||
}
|
||||
Some(Command::Reindex) => {
|
||||
let curie_helper = CurieHelper::new(Ontology::prefixes().clone());
|
||||
|
||||
let writer = Arc::new(RwLock::new(index.writer()?));
|
||||
debug_span!("Clear Index").in_scope(|| {
|
||||
let mut writer = writer.write()?;
|
||||
writer.delete_all_documents()?;
|
||||
writer.commit()
|
||||
})?;
|
||||
|
||||
let store = ontology.store();
|
||||
let writer_clone = writer.clone();
|
||||
let index_classes_thread = thread::spawn(move || {
|
||||
let span = debug_span!("Index Classes", documents = field::Empty).entered();
|
||||
let writer = writer_clone.read().unwrap();
|
||||
let count = gl_search::rdf::index_triples(DocType::RdfsClass, store, &*gl_search::language::ENGLISH_OR_UNTAGGED, &*writer)?;
|
||||
span.record("documents", count);
|
||||
Ok::<(), gl_search::SearchError>(())
|
||||
});
|
||||
|
||||
let store = ontology.store();
|
||||
let writer_clone = writer.clone();
|
||||
let index_properties_thread = thread::spawn(move || {
|
||||
let span = debug_span!("Index Properties", documents = field::Empty).entered();
|
||||
let writer = writer_clone.read().unwrap();
|
||||
let count = gl_search::rdf::index_triples(DocType::RdfProperty, store, &*gl_search::language::ENGLISH_OR_UNTAGGED, &writer)?;
|
||||
span.record("documents", count);
|
||||
Ok::<(), gl_search::SearchError>(())
|
||||
});
|
||||
|
||||
let store = ontology.store();
|
||||
let writer_clone = writer.clone();
|
||||
let index_concepts_thread = thread::spawn(move || {
|
||||
let span = debug_span!("Index Concepts", documents = field::Empty).entered();
|
||||
let writer = writer_clone.read().unwrap();
|
||||
let count = gl_search::rdf::index_triples(DocType::SkosConcept, store, &*gl_search::language::ENGLISH_OR_UNTAGGED, &writer)?;
|
||||
span.record("documents", count);
|
||||
Ok::<(), gl_search::SearchError>(())
|
||||
});
|
||||
|
||||
let starting_url = Url::parse("http://fedora.quill.lan/rest/")?;
|
||||
let writer_clone = writer.clone();
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.name("repository-traversal")
|
||||
.build()?;
|
||||
|
||||
let traversal_task = runtime.spawn(async move {
|
||||
let http_client = ClientBuilder::new(Client::new())
|
||||
.with(BasicAuthMiddleware::new(
|
||||
"fedoraAdmin".to_string(),
|
||||
Some("fedoraAdmin".to_string()),
|
||||
))
|
||||
.build();
|
||||
|
||||
let mut documents = 0usize;
|
||||
let mut traversal = Traverse::new(http_client, starting_url, None);
|
||||
while let Some(result) = traversal.next().await {
|
||||
match result {
|
||||
Ok(rdf_source) => {
|
||||
let writer = writer_clone.read().unwrap();
|
||||
documents += gl_search::rdf::index_entity(rdf_source.dataset(), &*gl_search::language::ENGLISH_OR_UNTAGGED, &writer)?;
|
||||
}
|
||||
Err(err) => error!(?err),
|
||||
}
|
||||
}
|
||||
debug!(documents, "Repository Traversal");
|
||||
Ok::<(), gl_search::SearchError>(())
|
||||
});
|
||||
|
||||
index_classes_thread.join().unwrap()?;
|
||||
index_properties_thread.join().unwrap()?;
|
||||
index_concepts_thread.join().unwrap()?;
|
||||
runtime.block_on(traversal_task)??;
|
||||
|
||||
debug_span!("Commit").in_scope(|| {
|
||||
let mut writer = writer.write()?;
|
||||
writer.commit()
|
||||
})?;
|
||||
}
|
||||
None => {
|
||||
iced::daemon(Publisher::new, Publisher::update, Publisher::view)
|
||||
.title(Publisher::title)
|
||||
.subscription(Publisher::subscription)
|
||||
.run()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
use std::sync::LazyLock;
|
||||
use oxigraph::model::Term;
|
||||
use oxilangtag::LanguageTag;
|
||||
|
||||
pub const ENGLISH_PRIMARY: &str = "en";
|
||||
|
||||
pub static ENGLISH_OR_UNTAGGED: LazyLock<LanguageCondition> = LazyLock::new(|| {
|
||||
LanguageCondition::ExactMatchOrUntagged(LanguageTag::parse(ENGLISH_PRIMARY.to_string()).unwrap())
|
||||
});
|
||||
|
||||
pub enum LanguageCondition {
|
||||
ExactMatchOnly(LanguageTag<String>),
|
||||
ExactMatchOrUntagged(LanguageTag<String>),
|
||||
UntaggedOnly,
|
||||
AnyOrNone,
|
||||
}
|
||||
|
||||
impl LanguageCondition {
|
||||
pub fn primary_matches_term(&self, term: &Term) -> bool {
|
||||
if let Term::Literal(literal) = term {
|
||||
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(),
|
||||
(None, LanguageCondition::ExactMatchOrUntagged(_)) => true,
|
||||
(None, LanguageCondition::UntaggedOnly) => true,
|
||||
(_, LanguageCondition::AnyOrNone) => true,
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
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::UntaggedOnly => format!("FILTER(!hasLANG(?{var}))"),
|
||||
LanguageCondition::AnyOrNone => "".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,5 +3,4 @@ pub(crate) mod term_helper;
|
||||
pub mod vocab;
|
||||
pub(crate) mod materialize;
|
||||
pub(crate) mod conversion;
|
||||
pub(crate) mod language;
|
||||
pub(crate) mod curie;
|
||||
@@ -1,17 +1,17 @@
|
||||
use crate::error;
|
||||
use crate::rdf::vocab::gl;
|
||||
use crate::rdf::{conversion, materialize};
|
||||
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::sparql::{QueryResults, SparqlEvaluator};
|
||||
use oxigraph::store::Store;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||
use std::fmt::Display;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::LazyLock;
|
||||
use iced::futures::TryFutureExt;
|
||||
use oxigraph::store::Store;
|
||||
use tracing::{debug_span, field};
|
||||
use crate::rdf::{conversion, materialize};
|
||||
use crate::rdf::language::LanguageCondition;
|
||||
|
||||
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"));
|
||||
@@ -50,6 +50,7 @@ static PREFIXES: LazyLock<BTreeMap<String, String>> = LazyLock::new(|| {
|
||||
("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/"),
|
||||
@@ -179,6 +180,10 @@ impl Ontology {
|
||||
.collect::<Dataset>()
|
||||
}
|
||||
|
||||
pub fn store(&self) -> Store {
|
||||
self.store.clone()
|
||||
}
|
||||
|
||||
pub fn query_for_indexable_triples(&self, language: &LanguageCondition, source: Option<Dataset>) -> impl Future<Output = error::Result<HashMap<NamedNode, IndexEntry>>> + 'static {
|
||||
let language_filter = language.to_filter_expression("fieldValue");
|
||||
let query = format!(r#"SELECT ?individual ?categoryId ?fieldName ?fieldValue
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use iced::{alignment, keyboard, widget, Element, Event, Length, Rectangle, Size, Theme, Background, Border};
|
||||
use iced::{alignment, keyboard, widget, Element, Event, Length, Rectangle, Size};
|
||||
use iced::advanced::{mouse, text, Shell, renderer};
|
||||
use iced::advanced::layout::{Limits, Node};
|
||||
use iced::advanced::{Layout, Widget};
|
||||
use iced::advanced::widget::{tree, Tree};
|
||||
use iced::advanced::widget::{tree, Tree, Operation};
|
||||
use iced::clipboard::Content;
|
||||
use iced::mouse::{Cursor, Interaction};
|
||||
use iced::widget::text_input::{Catalog, Status, Style, StyleFn};
|
||||
@@ -31,9 +31,9 @@ where
|
||||
Theme: Catalog,
|
||||
Renderer: text::Renderer,
|
||||
{
|
||||
pub fn new(curie_helper: &'a CurieHelper, placeholder: &str, base: Option<&str>, iri: &str) -> Self {
|
||||
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);
|
||||
let text_input = widget::TextInput::new(placeholder, display_value);
|
||||
Self {
|
||||
curie_helper,
|
||||
base: base.map(String::from),
|
||||
@@ -44,7 +44,7 @@ where
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn align_x(mut self, alignment: impl Into<alignment::Horizontal>) -> Self {
|
||||
pub fn align_x(mut self, alignment: impl Into<text::Alignment>) -> Self {
|
||||
self.text_input = self.text_input.align_x(alignment);
|
||||
self
|
||||
}
|
||||
@@ -87,6 +87,12 @@ where
|
||||
self.text_input = self.text_input.style(style);
|
||||
self
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn id(mut self, id: impl Into<widget::Id>) -> Self {
|
||||
self.text_input = self.text_input.id(id);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Theme, Renderer> From<IriInput<'a, Message, Theme, Renderer>>
|
||||
@@ -94,14 +100,14 @@ for Element<'a, Message, Theme, Renderer>
|
||||
where
|
||||
Message: Clone + 'a,
|
||||
Theme: Catalog + 'a,
|
||||
Renderer: text::Renderer + 'a,
|
||||
Renderer: text::Renderer + 'static,
|
||||
{
|
||||
fn from(value: IriInput<'a, Message, Theme, Renderer>) -> Self {
|
||||
Element::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iri_input<'a, Message, Theme, Renderer>(curie_helper: &'a CurieHelper, placeholder: &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,
|
||||
@@ -114,7 +120,7 @@ impl <Message, Theme, Renderer> Widget<Message, Theme, Renderer> for IriInput<'_
|
||||
where
|
||||
Message: Clone,
|
||||
Theme: Catalog,
|
||||
Renderer: text::Renderer,
|
||||
Renderer: text::Renderer + 'static,
|
||||
{
|
||||
fn size(&self) -> Size<Length> {
|
||||
Widget::size(&self.text_input)
|
||||
@@ -143,6 +149,16 @@ where
|
||||
tree.diff_children(&mut [&mut self.text_input as &mut dyn Widget<_, _, _>]);
|
||||
}
|
||||
|
||||
fn operate(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
operation: &mut dyn 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) {
|
||||
let state = tree.state.downcast_mut::<State>();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user