From 42dea779762b205c386c99a56c3b70394eeb15af3174838b2710e71730212990 Mon Sep 17 00:00:00 2001 From: Alex Wied <543423+centromere@users.noreply.github.com> Date: Tue, 23 Jun 2026 21:55:52 -0400 Subject: [PATCH] . --- Cargo.lock | 307 ++++-------------- Cargo.toml | 2 +- publish/src/app.rs | 58 ++-- publish/src/error.rs | 3 + publish/src/main.rs | 2 + publish/src/rdf/conversion.rs | 47 +++ publish/src/rdf/materialize.rs | 134 ++++++++ publish/src/rdf/mod.rs | 2 + publish/src/rdf/ontologies/ontology.ttl | 349 +------------------- publish/src/rdf/ontology.rs | 414 +++++++----------------- publish/src/rdf/vocab.rs | 3 + publish/src/widget/iri_input.rs | 115 +++++++ publish/src/widget/mod.rs | 1 + search/src/schema.rs | 3 +- 14 files changed, 543 insertions(+), 897 deletions(-) create mode 100644 publish/src/rdf/conversion.rs create mode 100644 publish/src/rdf/materialize.rs create mode 100644 publish/src/widget/iri_input.rs create mode 100644 publish/src/widget/mod.rs diff --git a/Cargo.lock b/Cargo.lock index b4d1625..cad638c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -145,9 +145,9 @@ checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "f02882884d3e1bc524fb12c79f107f6ad0e1cfd498c536ffb494301740995dfe" [[package]] name = "as-raw-xcb-connection" @@ -426,9 +426,9 @@ dependencies = [ [[package]] name = "bon" -version = "3.9.1" +version = "3.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f47dbe92550676ee653353c310dfb9cf6ba17ee70396e1f7cf0a2020ad49b2fe" +checksum = "a602c73c7b0148ec6d12af6fd5cc7a46e2eacc8878271a999abac56eed12f561" dependencies = [ "bon-macros", "rustversion", @@ -436,9 +436,9 @@ dependencies = [ [[package]] name = "bon-macros" -version = "3.9.1" +version = "3.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "519bd3116aeeb42d5372c29d982d16d0170d3d4a5ed85fc7dd91642ffff3c67c" +checksum = "6dee98b0db6a962de883bf5d20362dee4d7ca0d12fe39a7c6c73c844e1cd7c1f" dependencies = [ "darling", "ident_case", @@ -483,9 +483,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" [[package]] name = "calloop" @@ -515,9 +515,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.63" +version = "1.2.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" dependencies = [ "find-msvc-tools", "jobserver", @@ -878,7 +878,6 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ - "powerfmt", "serde_core", ] @@ -1343,15 +1342,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", - "wasip2", - "wasip3", ] [[package]] @@ -1402,9 +1399,9 @@ dependencies = [ [[package]] name = "glam" -version = "0.25.0" +version = "0.33.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" +checksum = "898f5a568a84989b6c0f8caa50a93074b97dbdc58fc6d9543157bb4562758933" [[package]] name = "glob" @@ -1467,16 +1464,6 @@ dependencies = [ "bitflags 2.13.0", ] -[[package]] -name = "guillotiere" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62d5865c036cb1393e23c50693df631d3f5d7bcca4c04fe4cc0fd592e74a782" -dependencies = [ - "euclid", - "svg_fmt", -] - [[package]] name = "half" version = "2.7.1" @@ -1534,12 +1521,6 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - [[package]] name = "hermit-abi" version = "0.5.2" @@ -1649,7 +1630,7 @@ dependencies = [ [[package]] name = "iced" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#38d19f8f5a2be93fd1fd74a2e81df680ce3c1cc2" +source = "git+https://github.com/iced-rs/iced.git?branch=master#e46f9c9e69a8d787823c4a64fdcc49c23f303065" dependencies = [ "iced_core", "iced_debug", @@ -1664,7 +1645,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#38d19f8f5a2be93fd1fd74a2e81df680ce3c1cc2" +source = "git+https://github.com/iced-rs/iced.git?branch=master#e46f9c9e69a8d787823c4a64fdcc49c23f303065" dependencies = [ "bitflags 2.13.0", "bytes", @@ -1682,7 +1663,7 @@ dependencies = [ [[package]] name = "iced_debug" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#38d19f8f5a2be93fd1fd74a2e81df680ce3c1cc2" +source = "git+https://github.com/iced-rs/iced.git?branch=master#e46f9c9e69a8d787823c4a64fdcc49c23f303065" dependencies = [ "iced_core", "iced_futures", @@ -1692,7 +1673,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#38d19f8f5a2be93fd1fd74a2e81df680ce3c1cc2" +source = "git+https://github.com/iced-rs/iced.git?branch=master#e46f9c9e69a8d787823c4a64fdcc49c23f303065" dependencies = [ "futures", "iced_core", @@ -1706,7 +1687,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#38d19f8f5a2be93fd1fd74a2e81df680ce3c1cc2" +source = "git+https://github.com/iced-rs/iced.git?branch=master#e46f9c9e69a8d787823c4a64fdcc49c23f303065" dependencies = [ "bitflags 2.13.0", "bytemuck", @@ -1724,7 +1705,7 @@ dependencies = [ [[package]] name = "iced_program" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#38d19f8f5a2be93fd1fd74a2e81df680ce3c1cc2" +source = "git+https://github.com/iced-rs/iced.git?branch=master#e46f9c9e69a8d787823c4a64fdcc49c23f303065" dependencies = [ "iced_graphics", "iced_runtime", @@ -1733,7 +1714,7 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#38d19f8f5a2be93fd1fd74a2e81df680ce3c1cc2" +source = "git+https://github.com/iced-rs/iced.git?branch=master#e46f9c9e69a8d787823c4a64fdcc49c23f303065" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -1745,7 +1726,7 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#38d19f8f5a2be93fd1fd74a2e81df680ce3c1cc2" +source = "git+https://github.com/iced-rs/iced.git?branch=master#e46f9c9e69a8d787823c4a64fdcc49c23f303065" dependencies = [ "bytes", "iced_core", @@ -1756,7 +1737,7 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#38d19f8f5a2be93fd1fd74a2e81df680ce3c1cc2" +source = "git+https://github.com/iced-rs/iced.git?branch=master#e46f9c9e69a8d787823c4a64fdcc49c23f303065" dependencies = [ "bytemuck", "cosmic-text", @@ -1772,14 +1753,12 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#38d19f8f5a2be93fd1fd74a2e81df680ce3c1cc2" +source = "git+https://github.com/iced-rs/iced.git?branch=master#e46f9c9e69a8d787823c4a64fdcc49c23f303065" dependencies = [ "bitflags 2.13.0", "bytemuck", "cryoglyph", "futures", - "glam", - "guillotiere", "iced_debug", "iced_graphics", "log", @@ -1791,7 +1770,7 @@ dependencies = [ [[package]] name = "iced_widget" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#38d19f8f5a2be93fd1fd74a2e81df680ce3c1cc2" +source = "git+https://github.com/iced-rs/iced.git?branch=master#e46f9c9e69a8d787823c4a64fdcc49c23f303065" dependencies = [ "iced_renderer", "iced_runtime", @@ -1805,7 +1784,7 @@ dependencies = [ [[package]] name = "iced_winit" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#38d19f8f5a2be93fd1fd74a2e81df680ce3c1cc2" +source = "git+https://github.com/iced-rs/iced.git?branch=master#e46f9c9e69a8d787823c4a64fdcc49c23f303065" dependencies = [ "arboard", "iced_debug", @@ -1904,12 +1883,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "ident_case" version = "1.0.1" @@ -1951,8 +1924,6 @@ checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", "hashbrown 0.17.1", - "serde", - "serde_core", ] [[package]] @@ -2128,12 +2099,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "levenshtein_automata" version = "0.2.1" @@ -2224,9 +2189,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.32" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "lru" @@ -2273,15 +2238,15 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.1" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" [[package]] name = "memmap2" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" dependencies = [ "libc", ] @@ -2923,9 +2888,9 @@ checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" [[package]] name = "oxigraph" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ee83ce0422e6dce39a325810a9c66af71577a0a6fb5d887764bf3ae6304667" +checksum = "6d945cbe78640ab719a73697b9bd250054ea82c60470754efea79c21d5fc1cd1" dependencies = [ "dashmap", "getrandom 0.3.4", @@ -3015,9 +2980,9 @@ dependencies = [ [[package]] name = "oxrocksdb-sys" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b7ff9cbbbc3cd628be238ca7f22b4c31ab85c3fc1349ff452fd9fac78615e60" +checksum = "6977c1d1827fcbf14480e768e4dede0b39c2961f6028497d012f9c04bcda91c6" dependencies = [ "bindgen", "cc", @@ -3303,9 +3268,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.45" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" dependencies = [ "proc-macro2", ] @@ -3451,9 +3416,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.3" +version = "1.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" dependencies = [ "aho-corasick", "memchr", @@ -3474,9 +3439,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "renderdoc-sys" @@ -3865,9 +3830,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "smithay-client-toolkit" @@ -4052,9 +4017,9 @@ checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" [[package]] name = "swash" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1804632b66a35ca2b1d277eb0a138e10f46cb365b9a6d297e876b69ef79de43" +checksum = "0811b01ca2c4e8718760713911feaf4675c24f94e50530a015ec646cfb622f7c" dependencies = [ "skrifa 0.42.1", "yazi", @@ -4069,9 +4034,9 @@ checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a" [[package]] name = "syn" -version = "2.0.117" +version = "2.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" dependencies = [ "proc-macro2", "quote", @@ -4273,7 +4238,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.2", + "getrandom 0.4.3", "once_cell", "rustix 1.1.4", "windows-sys 0.61.2", @@ -4339,12 +4304,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.47" +version = "0.3.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "85c17d80feb7334b40c484e45ed1a5273dfd8bfda537c3be2e74a06a6686f327" dependencies = [ "deranged", - "itoa", "num-conv", "powerfmt", "serde_core", @@ -4354,15 +4318,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "dcef1a61bdb119096e153208ec5cbec23944ce8bca13be5c7f60c634f7403935" dependencies = [ "num-conv", "time-core", @@ -4737,12 +4701,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - [[package]] name = "url" version = "2.5.8" @@ -4769,11 +4727,11 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.23.2" +version = "1.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7" +checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" dependencies = [ - "getrandom 0.4.2", + "getrandom 0.4.3", "js-sys", "serde_core", "wasm-bindgen", @@ -4818,20 +4776,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.3+wasi-0.2.9" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen 0.51.0", + "wit-bindgen", ] [[package]] @@ -4893,28 +4842,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - [[package]] name = "wasm-streams" version = "0.5.0" @@ -4928,18 +4855,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags 2.13.0", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - [[package]] name = "wasmtimer" version = "0.4.3" @@ -5004,9 +4919,9 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.12" +version = "0.32.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f" +checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6" dependencies = [ "bitflags 2.13.0", "wayland-backend", @@ -5617,100 +5532,12 @@ dependencies = [ "memchr", ] -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - [[package]] name = "wit-bindgen" version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags 2.13.0", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - [[package]] name = "wl-clipboard-rs" version = "0.9.3" @@ -5906,18 +5733,18 @@ checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" [[package]] name = "zerocopy" -version = "0.8.50" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.50" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 8313632..20f31da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ bytes = "1.11" color-eyre = "0.6" futures = "0.3" http = "1.4" -iced = { git = "https://github.com/iced-rs/iced.git", branch = "master", features = ["tokio"] } +iced = { git = "https://github.com/iced-rs/iced.git", branch = "master", features = ["advanced", "tokio"] } oxigraph = { version = "0.5", features = ["rdf-12"] } oxilangtag = "0.1" parse_link_header = "0.4" diff --git a/publish/src/app.rs b/publish/src/app.rs index ca25a5b..cd30cc8 100644 --- a/publish/src/app.rs +++ b/publish/src/app.rs @@ -1,4 +1,3 @@ -use std::collections::HashSet; use crate::rdf::ontology::{LabeledIri, Ontology}; use crate::rdf::term_helper::{TermHelper, TermHelperMut}; use crate::rdf::vocab::{gl, rda}; @@ -9,7 +8,10 @@ use iced::widget::button::Style; use iced::widget::grid::Sizing; use iced::widget::{button, center, column, combo_box, container, grid, mouse_area, opaque, 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::{Background, Color, Element, Length, Subscription, Task, color, window, keyboard}; +//use iced::advanced::Widget; +use iced::keyboard::{Event, Key}; +use iced::keyboard::key::Named::Control; use iced::widget::text::Wrapping; use ldp::middleware::BasicAuthMiddleware; use ldp::model::{KeyedDataset, QuadKey}; @@ -21,10 +23,12 @@ use oxigraph::io::RdfFormat; use oxigraph::model::vocab::{rdf, rdfs}; use oxigraph::model::{BaseDirection, Dataset, NamedNode, NamedNodeRef, Quad, Term, TermRef}; use tracing::{debug, error, info}; +use crate::widget::iri_input::iri_input; #[derive(Debug, Clone)] pub(crate) enum Message { None, + Foo(QuadKey), Traverse, IndexRdfSource(RdfSource), CommitIndex, @@ -96,7 +100,7 @@ pub(crate) struct Publisher { impl Publisher { pub(crate) fn new() -> (Self, Task) { let ontology = Ontology::builder() - .with_default_ontologies() + .with_path("/home/alex/.local/share/org.graphofliberty.desktop/ontology") .build() .expect("Failed to build ontology"); @@ -107,7 +111,8 @@ impl Publisher { if let Some(id) = ontology.catalog_id(&rdf::PROPERTY.into_owned()) { index.remove_all_of_type(id); } if let Some(id) = ontology.catalog_id(&rdfs::CLASS.into_owned()) { index.remove_all_of_type(id); } - for (iri, info) in ontology.iri_info() { + + /*for (iri, info) in ontology.iri_info() { if let Some(type_) = ontology.catalog_id(&info.type_) { let mut document = doc!( Schema::type_field() => type_, @@ -127,11 +132,11 @@ impl Publisher { index.add(document).expect("Unable to add annotated IRI to search index"); } } - index.commit().expect("Unable to commit ontology to index"); + index.commit().expect("Unable to commit ontology to index");*/ let mut abbreviated_datatypes = ontology .datatypes() - .map(|node| ontology.abbreviate(node)) + .map(|node| ontology.abbreviate(node.as_ref())) .collect::>(); abbreviated_datatypes.sort(); @@ -340,9 +345,11 @@ impl Publisher { .get(key) .and_then(|quad| if quad.predicate == rdf::TYPE { self.ontology.catalog_id(&rdfs::CLASS.into_owned()) - } else { None }) + } else { + self.ontology.catalog_id(&search_state.type_.iri) + }) }, - SearchResultClickAction::URLInput => None, + SearchResultClickAction::URLInput => self.ontology.catalog_id(&search_state.type_.iri), }; search_state.results = self @@ -429,7 +436,7 @@ impl Publisher { Message::SaveGraph(overwrite) => { let client = self.http_client.clone(); let options = SerializationOptions::from_format(RdfFormat::Turtle) - .with_filter(self.ontology.exclude_read_only()); + ;//.with_filter(self.ontology.exclude_read_only()); let request = self .document .to_update(options) @@ -497,7 +504,9 @@ impl Publisher { } pub(crate) fn subscription(&self) -> Subscription { - window::close_events().map(Message::WindowClosed) + Subscription::batch([ + window::close_events().map(Message::WindowClosed), + ]) } fn view_row<'a>( @@ -517,11 +526,11 @@ impl Publisher { container(space()).width(BUTTON_WIDTH) }; - let property_label = self + let property_label = "foo"; /*self .ontology .info(triple.predicate.as_ref()) .and_then(|info| info.label.clone()) - .unwrap_or(self.ontology.abbreviate(triple.predicate.as_ref())); + .unwrap_or(self.ontology.abbreviate(triple.predicate.as_ref()));*/ let property: Element = if state.read_only { text(property_label).into() @@ -536,12 +545,12 @@ impl Publisher { let term = TermHelper::new(&triple.object); - let value_label = term.value_as_named_node().and_then(|node| { + let value_label = "bar"; /*term.value_as_named_node().and_then(|node| { self.ontology .info(node) .and_then(|info| info.label.clone()) .map(|label| container(text(label))) - }); + });*/ let value = term .value_as_named_node() @@ -556,7 +565,8 @@ impl Publisher { }) .unwrap_or(Horizontal::Left); - let value_input_base = text_input("Value", value.as_str()).align_x(value_alignment); + let value_input_base = text_input("Value", value.as_str()) + .align_x(value_alignment); let value_input = if !state.read_only { let is_named_node = term.is_named_node(); @@ -576,7 +586,12 @@ impl Publisher { value_input_base }; - let search_launcher = if term.is_named_node() && !state.read_only { + let foo = iri_input(value_input) + .on_control_click(Message::OpenQueryWindow(SearchResultClickAction::Object( + key, + ))); + + /*let search_launcher = if term.is_named_node() && !state.read_only { Some(container( button(text("\u{1f50e}")) .on_press(Message::OpenQueryWindow(SearchResultClickAction::Object( @@ -586,7 +601,7 @@ impl Publisher { )) } else { None - }; + };*/ let selected_datatype = term.datatype().map(|node| self.ontology.abbreviate(node)); let datatype_selector: Element = if state.read_only { @@ -640,8 +655,9 @@ impl Publisher { button_area, property, value_label, - value_input, - search_launcher, + //value_input, + foo, + //search_launcher, datatype_selector, language_input, direction_slider, @@ -658,11 +674,11 @@ impl Publisher { entities: impl Iterator, ) -> Element<'_, Message> { let buttons = entities.map(|entity| { - let label = self + let label = "baz"; /*self .ontology .info(entity.as_ref()) .and_then(|info| info.label.clone()) - .unwrap_or(entity.as_str().to_string()); + .unwrap_or(entity.as_str().to_string());*/ button(text(label)) .on_press(Message::NewDocument(entity)) .into() diff --git a/publish/src/error.rs b/publish/src/error.rs index 8835ad5..467be57 100644 --- a/publish/src/error.rs +++ b/publish/src/error.rs @@ -6,6 +6,9 @@ pub type Result = std::result::Result; pub(crate) enum Error { #[error(transparent)] Io(#[from] std::io::Error), + + #[error(transparent)] + Storage(#[from] oxigraph::store::StorageError), #[error(transparent)] IriParse(#[from] oxigraph::model::IriParseError), diff --git a/publish/src/main.rs b/publish/src/main.rs index 13d6554..3aebe53 100644 --- a/publish/src/main.rs +++ b/publish/src/main.rs @@ -2,7 +2,9 @@ mod app; mod error; mod rdf; mod windows; +mod widget; +use iced::Program; use crate::app::Publisher; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; diff --git a/publish/src/rdf/conversion.rs b/publish/src/rdf/conversion.rs new file mode 100644 index 0000000..5852d76 --- /dev/null +++ b/publish/src/rdf/conversion.rs @@ -0,0 +1,47 @@ +use oxigraph::model::{NamedNode, Quad, Term}; +use oxigraph::model::vocab::{rdf, xsd}; + +fn quad_to_term(quad: &Quad) -> &Term { + &quad.object +} + +fn term_to_named_node(term: &Term) -> Option<&NamedNode> { + if let Term::NamedNode(node) = term { + Some(node) + } else { + None + } +} + +fn term_to_string(term: &Term) -> Option<&str> { + if let Term::Literal(literal) = term { + match literal.datatype() { + xsd::STRING | xsd::NORMALIZED_STRING | rdf::LANG_STRING | rdf::DIR_LANG_STRING => { + Some(literal.value()) + } + _ => None, + } + } else { + None + } +} + +fn term_to_boolean(term: &Term) -> Option { + if let Term::Literal(literal) = term { + if literal.datatype() == xsd::BOOLEAN { + literal.value().parse().ok() + } else { + None + } + } else { + None + } +} + +fn term_to_u64(term: &Term) -> Option { + if let Term::Literal(literal) = term && + literal.datatype() == xsd::NON_NEGATIVE_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 } +} \ No newline at end of file diff --git a/publish/src/rdf/materialize.rs b/publish/src/rdf/materialize.rs new file mode 100644 index 0000000..b755912 --- /dev/null +++ b/publish/src/rdf/materialize.rs @@ -0,0 +1,134 @@ +use oxigraph::model::{Dataset, GraphName, NamedOrBlankNode, NamedOrBlankNodeRef, Quad, Term}; +use oxigraph::sparql::{QueryResults, SparqlEvaluator}; +use oxigraph::store::Store; +use crate::error; +use crate::rdf::vocab::owl; + +pub fn same_as(store: Store) -> error::Result { + let additional_quads = store + .quads_for_pattern(None, Some(owl::SAME_AS), None, None) + .filter_map(Result::ok) + .fold(Dataset::new(), |mut new_dataset, alias| { + if let NamedOrBlankNode::NamedNode(x) = alias.subject + && let Term::NamedNode(y) = alias.object + { + for mut quad in store.quads_for_pattern( + Some(NamedOrBlankNodeRef::NamedNode(x.as_ref())), + None, + None, + None, + ).filter_map(Result::ok) { + quad.subject = NamedOrBlankNode::NamedNode(y.clone()); + new_dataset.insert(quad.as_ref()); + } + + for mut quad in store.quads_for_pattern( + Some(NamedOrBlankNodeRef::NamedNode(y.as_ref())), + None, + None, + None, + ).filter_map(Result::ok) { + quad.subject = NamedOrBlankNode::NamedNode(x.clone()); + new_dataset.insert(quad.as_ref()); + } + } + new_dataset + }); + + let old_size = store.len()?; + store.extend(&additional_quads)?; + let new_size = store.len()?; + + if new_size > old_size { + same_as(store) + } else { + Ok(store) + } +} + +pub fn super_classes(dataset: &mut Dataset) { + let query = SparqlEvaluator::new() + .parse_query( + r#"PREFIX rdfs: + +CONSTRUCT { + ?item a ?parent +} WHERE { + ?item a ?class . + ?class rdfs:subClassOf ?parent . +}"#, + ) + .expect("Unable to parse superclass query"); + + let mut additional_quads = Dataset::new(); + if let QueryResults::Graph(graph) = query.on_queryable_dataset(&*dataset).execute().unwrap() + { + additional_quads.extend(graph.filter_map(Result::ok).map(|triple| { + Quad::new( + triple.subject, + triple.predicate, + triple.object, + GraphName::DefaultGraph, + ) + })); + } + + let old_size = dataset.len(); + dataset.extend(&additional_quads); + let new_size = dataset.len(); + + if new_size > old_size { + super_classes(dataset) + } +} + +/*fn iri_information(dataset: &Dataset) -> HashMap { + let query = SparqlEvaluator::new() + .parse_query( + r#"PREFIX rdf: +PREFIX rdfs: +PREFIX gl: + +SELECT DISTINCT ?class ?subject ?label ?comment ?read_only { + VALUES ?class { rdf:Property rdfs:Class } + ?subject a ?class . + OPTIONAL { + ?subject rdfs:label ?label + FILTER (langMATCHES(LANG(?label), "en") || !hasLANG(?label)) + } + OPTIONAL { + ?subject rdfs:comment ?comment + FILTER (langMATCHES(LANG(?label), "en") || !hasLANG(?comment)) + } + OPTIONAL { ?subject gl:readOnly ?read_only } +}"#, + ) + .expect("Unable to parse property query"); + + let mut results = HashMap::new(); + if let QueryResults::Solutions(solutions) = + query.on_queryable_dataset(dataset).execute().unwrap() + { + for solution in solutions.filter_map(Result::ok) { + let type_ = solution.get("class").and_then(crate::rdf::ontology::term_to_named_node); + let subject = solution.get("subject").and_then(crate::rdf::ontology::term_to_named_node); + let label = solution.get("label").and_then(crate::rdf::ontology::term_to_string); + let comment = solution.get("comment").and_then(crate::rdf::ontology::term_to_string); + let read_only = solution + .get("read_only") + .and_then(crate::rdf::ontology::term_to_boolean) + .unwrap_or(false); + + if let Some(subject) = subject && let Some(type_) = type_ { + let info = IriInformation { + type_: type_.clone(), + label: label.map(String::from), + comment: comment.map(String::from), + read_only, + }; + results.insert(subject.to_owned(), info); + } + } + } + results +}*/ \ No newline at end of file diff --git a/publish/src/rdf/mod.rs b/publish/src/rdf/mod.rs index db70147..640c592 100644 --- a/publish/src/rdf/mod.rs +++ b/publish/src/rdf/mod.rs @@ -1,3 +1,5 @@ pub(crate) mod ontology; pub(crate) mod term_helper; pub mod vocab; +pub(crate) mod materialize; +pub(crate) mod conversion; diff --git a/publish/src/rdf/ontologies/ontology.ttl b/publish/src/rdf/ontologies/ontology.ttl index c18f556..08eedfc 100644 --- a/publish/src/rdf/ontologies/ontology.ttl +++ b/publish/src/rdf/ontologies/ontology.ttl @@ -83,20 +83,8 @@ xsd:unsignedShort rdf:type rdfs:Datatype ; # Object Properties ################################################################# -### http://rdaregistry.info/Elements/a/P50094 -rdaa:P50094 rdf:type owl:ObjectProperty . - - -### http://rdaregistry.info/Elements/a/identifierForPerson.en -rdaa:identifierForPerson.en rdf:type owl:ObjectProperty . - - -### http://rdaregistry.info/Elements/m/P30154 -rdam:P30154 rdf:type owl:ObjectProperty . - - -### http://rdaregistry.info/Elements/m/uniformResourceLocator.en -rdam:uniformResourceLocator.en rdf:type owl:ObjectProperty . +### http://fedora.info/definitions/v4/repository#hasParent +fedora:hasParent rdf:type owl:ObjectProperty . ### https://graphofliberty.org/2026/04/ont/associatedProperty @@ -106,8 +94,7 @@ rdam:uniformResourceLocator.en rdf:type owl:ObjectProperty . ### https://graphofliberty.org/2026/04/ont/indexedByField :indexedByField rdf:type owl:ObjectProperty ; - rdfs:domain owl:DatatypeProperty ; - rdfs:range :IndexField ; + rdfs:range :IndexDocumentField ; rdfs:comment "The property is associated with the given field in a full-text search database."@en ; rdfs:label "indexed by field"@en . @@ -140,22 +127,6 @@ fedora:lastModified rdf:type owl:DatatypeProperty . fedora:lastModifiedBy rdf:type owl:DatatypeProperty . -### http://rdaregistry.info/Elements/a/P50291 -rdaa:P50291 rdf:type owl:DatatypeProperty . - - -### http://rdaregistry.info/Elements/a/P50292 -rdaa:P50292 rdf:type owl:DatatypeProperty . - - -### http://rdaregistry.info/Elements/a/givenName.en -rdaa:givenName.en rdf:type owl:DatatypeProperty . - - -### http://rdaregistry.info/Elements/a/surname.en -rdaa:surname.en rdf:type owl:DatatypeProperty . - - ### http://www.w3.org/ns/ldp#contains ldp:contains rdf:type owl:DatatypeProperty ; rdfs:subPropertyOf owl:topDataProperty . @@ -163,19 +134,20 @@ ldp:contains rdf:type owl:DatatypeProperty ; ### https://graphofliberty.org/2026/04/ont/catalogId :catalogId rdf:type owl:DatatypeProperty ; - rdfs:domain owl:Class ; rdfs:comment "An integer associated with the class for fast lookup in a database."@en ; rdfs:label "catalog id" . ### https://graphofliberty.org/2026/04/ont/fieldLabel :fieldLabel rdf:type owl:DatatypeProperty ; + rdfs:domain :IndexDocumentField ; rdfs:comment "The label of a field, which shall be displayed to the user."@en ; rdfs:label "field label"@en . ### https://graphofliberty.org/2026/04/ont/fieldName :fieldName rdf:type owl:DatatypeProperty ; + rdfs:domain :IndexDocumentField ; rdfs:comment "The name of the field, as defined in the full-text search document schema."@en ; rdfs:label "field name"@en . @@ -190,122 +162,15 @@ ldp:contains rdf:type owl:DatatypeProperty ; # Classes ################################################################# -### http://fedora.info/definitions/v4/repository#Container -fedora:Container rdf:type owl:Class . - - -### http://fedora.info/definitions/v4/repository#Resource -fedora:Resource rdf:type owl:Class . - - -### http://rdaregistry.info/Elements/c/C10001 -rdac:C10001 rdf:type owl:Class ; - rdfs:subClassOf rdac:C10013 ; - rdfs:label "Work"@en . - - -### http://rdaregistry.info/Elements/c/C10002 -rdac:C10002 rdf:type owl:Class ; - rdfs:subClassOf rdac:C10013 ; - rdfs:label "Agent"@en . - - -### http://rdaregistry.info/Elements/c/C10004 -rdac:C10004 rdf:type owl:Class ; - rdfs:subClassOf rdac:C10002 . - - -### http://rdaregistry.info/Elements/c/C10007 -rdac:C10007 rdf:type owl:Class ; - rdfs:subClassOf rdac:C10013 . - - -### http://rdaregistry.info/Elements/c/C10013 -rdac:C10013 rdf:type owl:Class ; - rdfs:label "RDA Entity"@en . - - -### http://www.w3.org/1999/02/22-rdf-syntax-ns#Property -rdf:Property rdf:type owl:Class . - - -### http://www.w3.org/2000/01/rdf-schema#Class -rdfs:Class rdf:type owl:Class . - - -### http://www.w3.org/2002/07/owl#Class -owl:Class rdf:type owl:Class . - - -### http://www.w3.org/2002/07/owl#DatatypeProperty -owl:DatatypeProperty rdf:type owl:Class . - - -### http://www.w3.org/ns/ldp#BasicContainer -ldp:BasicContainer rdf:type owl:Class . - - -### http://www.w3.org/ns/ldp#Container -ldp:Container rdf:type owl:Class . - - -### http://www.w3.org/ns/ldp#RDFSource -ldp:RDFSource rdf:type owl:Class . - - -### http://www.w3.org/ns/ldp#Resource -ldp:Resource rdf:type owl:Class . - - -### https://graphofliberty.org/2026/04/ont/AudioBook -:AudioBook rdf:type owl:Class ; - rdfs:subClassOf :Entity . - - -### https://graphofliberty.org/2026/04/ont/Book -:Book rdf:type owl:Class ; - rdfs:subClassOf :Entity . - - -### https://graphofliberty.org/2026/04/ont/Document -:Document rdf:type owl:Class ; - rdfs:subClassOf :Entity . - - ### https://graphofliberty.org/2026/04/ont/Entity :Entity rdf:type owl:Class ; rdfs:label "Graph of Liberty Entity"@en . -### https://graphofliberty.org/2026/04/ont/IndexField -:IndexField rdf:type owl:Class ; - rdfs:comment "Describes a single field present within a larger document that is passed to a full-text search database."@en ; - rdfs:label "Index Field"@en . - - -### https://graphofliberty.org/2026/04/ont/Meme -:Meme rdf:type owl:Class ; - rdfs:subClassOf :Entity . - - -### https://graphofliberty.org/2026/04/ont/Movie -:Movie rdf:type owl:Class ; - rdfs:subClassOf :Entity . - - -### https://graphofliberty.org/2026/04/ont/Music -:Music rdf:type owl:Class ; - rdfs:subClassOf :Entity . - - -### https://graphofliberty.org/2026/04/ont/Podcast -:Podcast rdf:type owl:Class ; - rdfs:subClassOf :Entity . - - -### https://graphofliberty.org/2026/04/ont/TVShow -:TVShow rdf:type owl:Class ; - rdfs:subClassOf :Entity . +### https://graphofliberty.org/2026/04/ont/IndexDocumentField +:IndexDocumentField rdf:type owl:Class ; + rdfs:comment "Describes a single field present within a document that is indexed in a full-text search database."@en ; + rdfs:label "Index Document Field"@en . ################################################################# @@ -332,6 +197,11 @@ fedora:createdBy rdf:type owl:NamedIndividual ; :readOnly "true"^^xsd:boolean . +### http://fedora.info/definitions/v4/repository#hasParent +fedora:hasParent rdf:type owl:NamedIndividual ; + :readOnly "true"^^xsd:boolean . + + ### http://fedora.info/definitions/v4/repository#lastModified fedora:lastModified rdf:type owl:NamedIndividual ; :readOnly "true"^^xsd:boolean . @@ -342,89 +212,16 @@ fedora:lastModifiedBy rdf:type owl:NamedIndividual ; :readOnly "true"^^xsd:boolean . -### http://rdaregistry.info/Elements/a/P50094 -rdaa:P50094 rdf:type owl:NamedIndividual ; - owl:sameAs rdaa:identifierForPerson.en . - - -### http://rdaregistry.info/Elements/a/identifierForPerson.en - -### http://rdaregistry.info/Elements/a/P50291 -rdaa:P50291 rdf:type owl:NamedIndividual ; - owl:sameAs rdaa:surname.en ; - :indexedByField :surname . - - -### http://rdaregistry.info/Elements/a/surname.en - -### http://rdaregistry.info/Elements/a/P50292 -rdaa:P50292 rdf:type owl:NamedIndividual ; - owl:sameAs rdaa:givenName.en ; - :indexedByField :givenName . - - -### http://rdaregistry.info/Elements/a/givenName.en - -### http://rdaregistry.info/Elements/a/givenName.en -rdaa:givenName.en rdf:type owl:NamedIndividual . - - -### http://rdaregistry.info/Elements/a/identifierForPerson.en -rdaa:identifierForPerson.en rdf:type owl:NamedIndividual . - - -### http://rdaregistry.info/Elements/a/surname.en -rdaa:surname.en rdf:type owl:NamedIndividual . - - -### http://rdaregistry.info/Elements/c/C10004 -rdac:C10004 rdf:type owl:NamedIndividual ; - :associatedProperty rdaa:P50291 , - rdaa:P50292 ; - :catalogId "8"^^xsd:nonNegativeInteger . - - -### http://rdaregistry.info/Elements/c/C10007 -rdac:C10007 rdf:type owl:NamedIndividual ; - :template [ rdf:type rdac:C10007 - ] . - - -### http://rdaregistry.info/Elements/m/P30154 -rdam:P30154 rdf:type owl:NamedIndividual ; - owl:sameAs rdam:uniformResourceLocator.en . - - -### http://rdaregistry.info/Elements/m/uniformResourceLocator.en - -### http://rdaregistry.info/Elements/m/uniformResourceLocator.en -rdam:uniformResourceLocator.en rdf:type owl:NamedIndividual . - - ### http://www.w3.org/1999/02/22-rdf-syntax-ns#Property rdf:Property rdf:type owl:NamedIndividual ; - :associatedProperty rdfs:comment , - rdfs:label ; :catalogId "0"^^xsd:nonNegativeInteger . ### http://www.w3.org/2000/01/rdf-schema#Class rdfs:Class rdf:type owl:NamedIndividual ; - :associatedProperty rdfs:comment , - rdfs:label ; :catalogId "1"^^xsd:nonNegativeInteger . -### http://www.w3.org/2000/01/rdf-schema#comment -rdfs:comment rdf:type owl:NamedIndividual ; - :indexedByField :comment . - - -### http://www.w3.org/2000/01/rdf-schema#label -rdfs:label rdf:type owl:NamedIndividual ; - :indexedByField :label . - - ### http://www.w3.org/ns/ldp#BasicContainer ldp:BasicContainer rdf:type owl:NamedIndividual ; :readOnly "true"^^xsd:boolean . @@ -450,134 +247,20 @@ ldp:contains rdf:type owl:NamedIndividual ; :readOnly "true"^^xsd:boolean . -### https://graphofliberty.org/2026/04/ont/AudioBook -:AudioBook rdf:type owl:NamedIndividual ; - :catalogId "2"^^xsd:nonNegativeInteger . - - -### https://graphofliberty.org/2026/04/ont/Book -:Book rdf:type owl:NamedIndividual ; - :catalogId "3"^^xsd:nonNegativeInteger . - - -### https://graphofliberty.org/2026/04/ont/Document -:Document rdf:type owl:NamedIndividual ; - :catalogId "4"^^xsd:nonNegativeInteger . - - -### https://graphofliberty.org/2026/04/ont/Meme -:Meme rdf:type owl:NamedIndividual ; - :catalogId "5"^^xsd:nonNegativeInteger . - - -### https://graphofliberty.org/2026/04/ont/Movie -:Movie rdf:type owl:NamedIndividual ; - :catalogId "6"^^xsd:nonNegativeInteger . - - -### https://graphofliberty.org/2026/04/ont/Music -:Music rdf:type owl:NamedIndividual ; - :catalogId "7"^^xsd:nonNegativeInteger . - - -### https://graphofliberty.org/2026/04/ont/Podcast -:Podcast rdf:type owl:NamedIndividual ; - :catalogId "9"^^xsd:nonNegativeInteger . - - -### https://graphofliberty.org/2026/04/ont/TVShow -:TVShow rdf:type owl:NamedIndividual ; - :catalogId "10"^^xsd:nonNegativeInteger . - - ### https://graphofliberty.org/2026/04/ont/comment :comment rdf:type owl:NamedIndividual , - :IndexField ; + :IndexDocumentField ; :fieldLabel "Comment"@en ; :fieldName "comment" ; rdfs:label "Comment Field"@en . -### https://graphofliberty.org/2026/04/ont/givenName -:givenName rdf:type owl:NamedIndividual , - :IndexField ; - :fieldLabel "Given Name"@en ; - :fieldName "givenName" ; - rdfs:label "Given Name Field"@en . - - ### https://graphofliberty.org/2026/04/ont/label :label rdf:type owl:NamedIndividual , - :IndexField ; + :IndexDocumentField ; :fieldLabel "Label"@en ; :fieldName "label" ; rdfs:label "Label Field"@en . -### https://graphofliberty.org/2026/04/ont/surname -:surname rdf:type owl:NamedIndividual , - :IndexField ; - :fieldLabel "Surname"@en ; - :fieldName "surname" ; - rdfs:label "Surname Field"@en . - - -################################################################# -# Annotations -################################################################# - -rdaa:P50094 rdfs:label "has identifier for person"@en . - - -rdaa:P50291 rdfs:label "has surname"@en . - - -rdaa:P50292 rdfs:label "has given name"@en . - - -rdac:C10004 rdfs:label "Person"@en . - - -rdac:C10007 rdfs:label "Manifestation"@en . - - -rdam:P30154 rdfs:label "has uniform resource locator"@en . - - -rdf:Property rdfs:label "Property"@en . - - -rdfs:Class rdfs:label "Class"@en . - - -rdfs:comment rdfs:label "Comment Property"@en . - - -rdfs:label rdfs:label "Label Property"@en . - - -:AudioBook rdfs:label "Audio Book"@en . - - -:Book rdfs:label "Book"@en . - - -:Document rdfs:label "Document"@en . - - -:Meme rdfs:label "Meme"@en . - - -:Movie rdfs:label "Movie"@en . - - -:Music rdfs:label "Music"@en . - - -:Podcast rdfs:label "Podcast"@en . - - -:TVShow rdfs:label "TV Show"@en . - - ### Generated by the OWL API (version 4.5.29.2024-05-13T12:11:03Z) https://github.com/owlcs/owlapi diff --git a/publish/src/rdf/ontology.rs b/publish/src/rdf/ontology.rs index db4c863..5a53060 100644 --- a/publish/src/rdf/ontology.rs +++ b/publish/src/rdf/ontology.rs @@ -2,15 +2,13 @@ use crate::error; use crate::rdf::vocab::{gl, owl, rda}; use oxigraph::io::{RdfFormat, RdfParser}; use oxigraph::model::vocab::{rdf, rdfs, xsd}; -use oxigraph::model::{ - Dataset, GraphName, NamedNode, NamedNodeRef, NamedOrBlankNode, NamedOrBlankNodeRef, Quad, Term, - TermRef, Triple, TripleRef, -}; +use oxigraph::model::{Dataset, GraphName, LiteralRef, NamedNode, NamedNodeRef, NamedOrBlankNode, NamedOrBlankNodeRef, Quad, Term, TermRef, Triple, TripleRef}; use oxigraph::sparql::{QueryResults, SparqlEvaluator}; use std::collections::{HashMap, HashSet}; use std::fmt::Display; -use iced::widget::sensor::Key; -use tracing::{debug, info}; +use std::path::{Path, PathBuf}; +use oxigraph::store::Store; +use crate::rdf::materialize; const PREFIXES: &[(&str, &str)] = &[ ("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"), @@ -29,6 +27,7 @@ const PREFIXES: &[(&str, &str)] = &[ ("dcterms", "http://purl.org/dc/terms/"), ("fedora", "http://fedora.info/definitions/v4/repository#"), ("rdaa", "http://rdaregistry.info/Elements/a/"), + ("rdaad", "http://rdaregistry.info/Elements/a/datatype/"), ("rdac", "http://rdaregistry.info/Elements/c/"), ("rdae", "http://rdaregistry.info/Elements/e/"), ("rdai", "http://rdaregistry.info/Elements/i/"), @@ -43,159 +42,93 @@ const PREFIXES: &[(&str, &str)] = &[ ("gl", "https://graphofliberty.org/2026/04/ont/"), ]; -const RDF_ONT: &[u8] = include_bytes!("ontologies/22-rdf-syntax-ns.ttl"); -const RDFS_ONT: &[u8] = include_bytes!("ontologies/rdf-schema.ttl"); -const OWL_ONT: &[u8] = include_bytes!("ontologies/owl.ttl"); -const LDP_ONT: &[u8] = include_bytes!("ontologies/ldp.ttl"); -const FEDORA_ONT: &[u8] = include_bytes!("ontologies/fedora.xml"); -const GL_ONT: &[u8] = include_bytes!("ontologies/ontology.ttl"); - -pub struct OntologyBuilder<'a> { - ontologies: Vec<(RdfFormat, &'a [u8])>, +pub struct OntologyBuilder { + path: Option, } -impl<'a> OntologyBuilder<'a> { - pub fn with_ontology_bytes(mut self, format: RdfFormat, bytes: &'a [u8]) -> Self { - self.ontologies.push((format, bytes)); +impl OntologyBuilder { + pub fn with_path(mut self, path: impl AsRef) -> Self { + let path = path.as_ref().to_owned(); + self.path = Some(path); self } - pub fn with_default_ontologies(self) -> Self { - self.with_ontology_bytes(RdfFormat::Turtle, RDF_ONT) - .with_ontology_bytes(RdfFormat::Turtle, RDFS_ONT) - .with_ontology_bytes(RdfFormat::Turtle, OWL_ONT) - .with_ontology_bytes(RdfFormat::Turtle, LDP_ONT) - .with_ontology_bytes(RdfFormat::RdfXml, FEDORA_ONT) - .with_ontology_bytes(RdfFormat::Turtle, GL_ONT) - } + pub fn build(self) -> error::Result { + let store = if let Some(path) = self.path { + Store::open(path) + } else { + Store::new() + }?; - fn materialize_same_as(dataset: &mut Dataset) { - let additional_quads = dataset - .quads_for_pattern(None, Some(owl::SAME_AS), None, None) - .fold(Dataset::new(), |mut new_dataset, alias| { - if let NamedOrBlankNodeRef::NamedNode(x) = alias.subject - && let TermRef::NamedNode(y) = alias.object - { - for mut quad in dataset.quads_for_pattern( - Some(NamedOrBlankNodeRef::NamedNode(x)), - None, - None, - None, - ) { - quad.subject = NamedOrBlankNodeRef::NamedNode(y); - new_dataset.insert(quad); - } + let prefixes = PREFIXES + .iter() + .map(|(k, v)| (k.to_string(), v.to_string())) + .collect::>(); - for mut quad in dataset.quads_for_pattern( - Some(NamedOrBlankNodeRef::NamedNode(y)), - None, - None, - None, - ) { - quad.subject = NamedOrBlankNodeRef::NamedNode(x); - new_dataset.insert(quad); + let store = materialize::same_as(store)?; + //materialize::super_classes(&mut dataset); + + // Full-text search index field names + //let fields = Self::fields(&dataset); + + /*let mut entities: HashMap = HashMap::new(); + let entity_iris = Self::subclass_of(&dataset, gl::ENTITY) + .chain(Self::subclass_of(&dataset, rda::ENTITY)) + .chain([ + rdf::PROPERTY.into_owned(), + rdfs::CLASS.into_owned(), + ]); + + for iri in entity_iris { + let subject = iri.as_ref().into(); + + let label = dataset.quads_for_pattern(Some(subject), Some(rdfs::LABEL), None, None) + .filter_map(|quad| term_to_string(&quad.object.into_owned()).map(String::from)) + .next(); + + let catalog_id = dataset.quads_for_pattern(Some(subject), Some(gl::CATALOG_ID), None, None) + .filter_map(|quad| term_to_u64(&quad.object.into_owned())) + .next(); + + if let Some(catalog_id) = catalog_id { + let mut properties = HashSet::new(); + for quad in dataset.quads_for_pattern(Some(subject), Some(gl::ASSOCIATED_PROPERTY), None, None) { + if let NamedOrBlankNodeRef::NamedNode(subject) = quad.subject + && let TermRef::NamedNode(property) = quad.object { + properties.insert(property.into_owned()); } } - new_dataset - }); - - let old_size = dataset.len(); - dataset.extend(&additional_quads); - let new_size = dataset.len(); - - if new_size > old_size { - Self::materialize_same_as(dataset) - } - } - - fn materialize_super_classes(dataset: &mut Dataset) { - let query = SparqlEvaluator::new() - .parse_query( - r#"PREFIX rdfs: - -CONSTRUCT { - ?item a ?parent -} WHERE { - ?item a ?class . - ?class rdfs:subClassOf ?parent . -}"#, - ) - .expect("Unable to parse superclass query"); - - let mut additional_quads = Dataset::new(); - if let QueryResults::Graph(graph) = query.on_queryable_dataset(&*dataset).execute().unwrap() - { - additional_quads.extend(graph.filter_map(Result::ok).map(|triple| { - Quad::new( - triple.subject, - triple.predicate, - triple.object, - GraphName::DefaultGraph, - ) - })); - } - - let old_size = dataset.len(); - dataset.extend(&additional_quads); - let new_size = dataset.len(); - - if new_size > old_size { - Self::materialize_super_classes(dataset) - } - } - - fn iri_information(dataset: &Dataset) -> HashMap { - let query = SparqlEvaluator::new() - .parse_query( - r#"PREFIX rdf: -PREFIX rdfs: -PREFIX gl: - -SELECT DISTINCT ?class ?subject ?label ?comment ?read_only { - VALUES ?class { rdf:Property rdfs:Class } - ?subject a ?class . - OPTIONAL { - ?subject rdfs:label ?label - FILTER (langMATCHES(LANG(?label), "en") || !hasLANG(?label)) - } - OPTIONAL { - ?subject rdfs:comment ?comment - FILTER (langMATCHES(LANG(?label), "en") || !hasLANG(?comment)) - } - OPTIONAL { ?subject gl:readOnly ?read_only } -}"#, - ) - .expect("Unable to parse property query"); - - let mut results = HashMap::new(); - if let QueryResults::Solutions(solutions) = - query.on_queryable_dataset(dataset).execute().unwrap() - { - for solution in solutions.filter_map(Result::ok) { - let type_ = solution.get("class").and_then(term_to_named_node); - let subject = solution.get("subject").and_then(term_to_named_node); - let label = solution.get("label").and_then(term_to_string); - let comment = solution.get("comment").and_then(term_to_string); - let read_only = solution - .get("read_only") - .and_then(term_to_boolean) - .unwrap_or(false); - - if let Some(subject) = subject && let Some(type_) = type_ { - let info = IriInformation { - type_: type_.clone(), - label: label.map(String::from), - comment: comment.map(String::from), - read_only, - }; - results.insert(subject.to_owned(), info); - } + entities.insert(iri, Entity { + label: label.unwrap_or(catalog_id.to_string()), + catalog_id, + properties, + }); } } - results + + let mut indexed_by = HashMap::new(); + for quad in dataset.quads_for_pattern(None, Some(gl::INDEXED_BY_FIELD), None, None) { + if let NamedOrBlankNodeRef::NamedNode(subject) = quad.subject + && let TermRef::NamedNode(field) = quad.object + { + indexed_by.insert(subject.into_owned(), field.into_owned()); + } + }*/ + + Ok(Ontology { + store, + prefixes, + fields: HashMap::new(), + entities: HashMap::from_iter([(rdf::PROPERTY.into_owned(), Entity { + label: "property".to_string(), + catalog_id: 0, + properties: HashSet::new(), + })]), + indexed_by: HashMap::new(), + }) } - fn fields(dataset: &Dataset) -> HashMap { + /*fn fields(dataset: &Dataset) -> HashMap { let query = SparqlEvaluator::new() .parse_query( r#"PREFIX rdfs: @@ -257,82 +190,7 @@ SELECT ?class {{ } else { unreachable!() } - } - - pub fn build(&mut self) -> error::Result { - let prefixes = PREFIXES - .iter() - .map(|(k, v)| (k.to_string(), v.to_string())) - .collect::>(); - - let mut dataset = Dataset::new(); - for (format, bytes) in &self.ontologies { - let quads = RdfParser::from_format(*format) - .for_slice(bytes) - .filter_map(Result::ok); - dataset.extend(quads); - } - Self::materialize_same_as(&mut dataset); - Self::materialize_super_classes(&mut dataset); - - let iri_info = Self::iri_information(&mut dataset); - - // Full-text search index field names - let fields = Self::fields(&dataset); - - let mut entities: HashMap = HashMap::new(); - let entity_iris = Self::subclass_of(&dataset, gl::ENTITY) - .chain(Self::subclass_of(&dataset, rda::ENTITY)) - .chain([ - rdf::PROPERTY.into_owned(), - rdfs::CLASS.into_owned(), - ]); - - for iri in entity_iris { - let subject = iri.as_ref().into(); - - let label = dataset.quads_for_pattern(Some(subject), Some(rdfs::LABEL), None, None) - .filter_map(|quad| term_to_string(&quad.object.into_owned()).map(String::from)) - .next(); - - let catalog_id = dataset.quads_for_pattern(Some(subject), Some(gl::CATALOG_ID), None, None) - .filter_map(|quad| term_to_u64(&quad.object.into_owned())) - .next(); - - if let Some(catalog_id) = catalog_id { - let mut properties = HashSet::new(); - for quad in dataset.quads_for_pattern(Some(subject), Some(gl::ASSOCIATED_PROPERTY), None, None) { - if let NamedOrBlankNodeRef::NamedNode(subject) = quad.subject - && let TermRef::NamedNode(property) = quad.object { - properties.insert(property.into_owned()); - } - } - entities.insert(iri, Entity { - label: label.unwrap_or(catalog_id.to_string()), - catalog_id, - properties, - }); - } - } - - let mut indexed_by = HashMap::new(); - for quad in dataset.quads_for_pattern(None, Some(gl::INDEXED_BY_FIELD), None, None) { - if let NamedOrBlankNodeRef::NamedNode(subject) = quad.subject - && let TermRef::NamedNode(field) = quad.object - { - indexed_by.insert(subject.into_owned(), field.into_owned()); - } - } - - Ok(Ontology { - dataset, - prefixes, - iri_info, - fields, - entities, - indexed_by, - }) - } + }*/ } #[derive(Clone, Debug)] @@ -375,11 +233,11 @@ pub struct IndexField { } pub struct Ontology { - dataset: Dataset, + store: Store, prefixes: HashMap, // Resource (Property or Class) -> Rust Type - iri_info: HashMap, + //iri_info: HashMap, // NamedIndividual of class IndexField -> Rust Type fields: HashMap, @@ -391,58 +249,17 @@ pub struct Ontology { indexed_by: HashMap, } -fn term_to_named_node(term: &Term) -> Option<&NamedNode> { - if let Term::NamedNode(node) = term { - Some(node) - } else { - None - } -} - -fn term_to_string(term: &Term) -> Option<&str> { - if let Term::Literal(literal) = term { - match literal.datatype() { - xsd::STRING | xsd::NORMALIZED_STRING | rdf::LANG_STRING | rdf::DIR_LANG_STRING => { - Some(literal.value()) - } - _ => None, - } - } else { - None - } -} - -fn term_to_boolean(term: &Term) -> Option { - if let Term::Literal(literal) = term { - if literal.datatype() == xsd::BOOLEAN { - literal.value().parse().ok() - } else { - None - } - } else { - None - } -} - -fn term_to_u64(term: &Term) -> Option { - if let Term::Literal(literal) = term && - literal.datatype() == xsd::NON_NEGATIVE_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 } -} - impl Ontology { - pub fn builder<'a>() -> OntologyBuilder<'a> { + pub fn builder() -> OntologyBuilder { OntologyBuilder { - ontologies: Vec::new(), + path: None, } } - pub fn info(&self, node: NamedNodeRef<'_>) -> Option<&IriInformation> { + /*pub fn info(&self, node: NamedNodeRef<'_>) -> Option<&IriInformation> { let node = node.into_owned(); self.iri_info.get(&node) - } + }*/ pub fn abbreviate(&self, node: NamedNodeRef<'_>) -> String { for (prefix_name, prefix_iri) in &self.prefixes { @@ -501,67 +318,62 @@ impl Ontology { }) } - pub fn iri_info(&self) -> &HashMap { - &self.iri_info - } - - pub fn datatypes(&self) -> impl Iterator> { - self.dataset + pub fn datatypes(&self) -> impl Iterator { + self.store .quads_for_pattern( None, Some(rdf::TYPE), Some(TermRef::NamedNode(rdfs::DATATYPE)), None, ) + .filter_map(Result::ok) .filter_map(|quad| match quad.subject { - NamedOrBlankNodeRef::NamedNode(subject) => Some(subject), + NamedOrBlankNode::NamedNode(subject) => Some(subject), _ => None, }) } - fn is_read_only_impl<'a>(iri_info: &HashMap, triple: impl Into>) -> bool { - let triple = triple.into(); - match (triple.predicate, triple.object) { - (rdf::TYPE, TermRef::NamedNode(node)) => iri_info - .get(&node.into_owned()) - .map(|info| info.read_only) - .unwrap_or(false), - (predicate, _) => iri_info - .get(&predicate.into_owned()) - .map(|info| info.read_only) - .unwrap_or(false), - } - } - - pub fn exclude_read_only(&self) -> impl Fn(TripleRef<'_>) -> bool + 'static { - let iri_info = self.iri_info.clone(); - move |triple| { Self::is_read_only_impl(&iri_info, triple) } - } - pub fn is_read_only<'a>(&self, triple: impl Into>) -> bool { - Self::is_read_only_impl(&self.iri_info, triple) + let triple = triple.into(); + let true_term = TermRef::Literal(LiteralRef::new_typed_literal("true", xsd::BOOLEAN)); + + let subject = match (triple.predicate, triple.object) { + (rdf::TYPE, TermRef::NamedNode(class)) => class, + (predicate, _) => predicate, + }; + + self.store + .quads_for_pattern(Some(NamedOrBlankNodeRef::NamedNode(subject)), Some(gl::READ_ONLY), Some(true_term), None) + .filter_map(Result::ok) + .count() >= 1 } + /*pub fn for_each_annotated_(&self) -> impl Iterator> { + self.store + .quads_for_pattern() + }*/ + pub fn template_triples<'a>( &'a self, class: NamedNodeRef<'a>, subject: NamedNodeRef<'_>, ) -> Option> { if let Some(quad) = self - .dataset + .store .quads_for_pattern( Some(NamedOrBlankNodeRef::NamedNode(class)), Some(gl::TEMPLATE), None, None, ) + .filter_map(Result::ok) .next() { - if let TermRef::BlankNode(blank_node) = quad.object { + if let Term::BlankNode(blank_node) = quad.object { let iter = self - .dataset - .quads_for_subject(blank_node) - .map(|quad| quad.into_owned()) + .store + .quads_for_pattern(Some(NamedOrBlankNodeRef::BlankNode(blank_node.as_ref())), None, None, None) + .filter_map(Result::ok) .map(Triple::from) .map(move |mut triple| { triple.subject = NamedOrBlankNode::NamedNode(subject.into_owned()); diff --git a/publish/src/rdf/vocab.rs b/publish/src/rdf/vocab.rs index 275c74e..81e5d34 100644 --- a/publish/src/rdf/vocab.rs +++ b/publish/src/rdf/vocab.rs @@ -16,6 +16,9 @@ pub mod gl { pub const ASSOCIATED_PROPERTY: NamedNodeRef = NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/associatedProperty"); + pub const READ_ONLY: NamedNodeRef = + NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/readOnly"); + /*pub const INDEX_FIELD: NamedNodeRef = NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/IndexField");*/ } diff --git a/publish/src/widget/iri_input.rs b/publish/src/widget/iri_input.rs new file mode 100644 index 0000000..bbe6854 --- /dev/null +++ b/publish/src/widget/iri_input.rs @@ -0,0 +1,115 @@ +use iced::{keyboard, Element, Event, Length, Rectangle, Size}; +use iced::advanced::{mouse, text, Shell}; +use iced::advanced::layout::{Limits, Node}; +use iced::advanced::renderer::Style; +use iced::advanced::{Layout, Widget}; +use iced::advanced::widget::{tree, Tree}; +use iced::mouse::{Cursor, Interaction}; +use iced::widget::text_input::Catalog; +use iced::widget::TextInput; + +pub struct State { + control: bool, +} + +pub struct IriInput<'a, Message, Theme = iced::Theme, Renderer = iced::Renderer> +where +Theme: Catalog, +Renderer: text::Renderer { + text_input: TextInput<'a, Message, Theme, Renderer>, + on_control_click: Option, +} + +impl<'a, Message, Theme, Renderer> IriInput<'a, Message, Theme, Renderer> +where + Theme: Catalog, + Renderer: text::Renderer, +{ + pub fn on_control_click(mut self, on_control_click: Message) -> Self { + self.on_control_click = Some(on_control_click); + self + } +} + +impl<'a, Message, Theme, Renderer> From> +for Element<'a, Message, Theme, Renderer> +where + Message: Clone + 'a, + Theme: Catalog + 'a, + Renderer: text::Renderer + 'a, +{ + fn from(value: IriInput<'a, Message, Theme, Renderer>) -> Self { + Element::new(value) + } +} + +pub fn iri_input(text_input: TextInput) -> IriInput +where + Message: Clone, + Theme: Catalog, + Renderer: text::Renderer, +{ + IriInput { + text_input, + on_control_click: None, + } +} + +impl Widget for IriInput<'_, Message, Theme, Renderer> +where + Message: Clone, + Theme: Catalog, + Renderer: text::Renderer, +{ + fn size(&self) -> Size { + Widget::size(&self.text_input) + } + fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &Limits) -> Node { + Widget::layout(&mut self.text_input, &mut tree.children[0], renderer, limits) + } + + fn draw(&self, tree: &Tree, renderer: &mut Renderer, theme: &Theme, style: &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 { + tree::Tag::of::() + } + + fn state(&self) -> tree::State { + tree::State::new(State { + control: false, + }) + } + + fn diff(&mut self, tree: &mut Tree) { + tree.diff_children(&mut [&mut self.text_input as &mut dyn Widget<_, _, _>]); + } + + 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 { + Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => { + state.control = modifiers.control(); + } + Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => { + if cursor.is_over(layout.bounds()) && state.control && let Some(on_control_click) = &self.on_control_click { + shell.publish(on_control_click.clone()); + } + } + _ => {} + } + + Widget::update(&mut self.text_input, &mut tree.children[0], event, layout, cursor, renderer, shell, viewport) + } + + fn mouse_interaction(&self, tree: &Tree, layout: Layout<'_>, cursor: Cursor, _viewport: &Rectangle, _renderer: &Renderer) -> Interaction { + let state = tree.state.downcast_ref::(); + if cursor.is_over(layout.bounds()) && state.control { + Interaction::Pointer + } else { + Interaction::None + } + } +} \ No newline at end of file diff --git a/publish/src/widget/mod.rs b/publish/src/widget/mod.rs new file mode 100644 index 0000000..2a82a1e --- /dev/null +++ b/publish/src/widget/mod.rs @@ -0,0 +1 @@ +pub mod iri_input; \ No newline at end of file diff --git a/search/src/schema.rs b/search/src/schema.rs index ff9b0de..ee160af 100644 --- a/search/src/schema.rs +++ b/search/src/schema.rs @@ -30,8 +30,9 @@ impl Schema { schema_builder.add_text_field("label", stored_ngram32.clone()); schema_builder.add_text_field("comment", stored_en_stem.clone()); + /*schema_builder.add_text_field("nomen", stored_ngram32.clone()); schema_builder.add_text_field("givenName", stored_ngram32.clone()); - schema_builder.add_text_field("surname", stored_ngram32); + schema_builder.add_text_field("surname", stored_ngram32);*/ /*schema_builder.add_text_field("title", en_stem.clone()); schema_builder.add_text_field("description", en_stem.clone());