1#![deny(missing_debug_implementations)]
6#![allow(clippy::type_complexity)]
7#![feature(
8 array_chunks,
9 slice_from_ptr_range,
10 non_null_from_ref,
11 split_array,
12 cell_update,
13 str_split_remainder,
14 try_blocks,
15 extend_one,
16 slice_split_once,
17 iterator_try_collect,
18 thread_local,
19 write_all_vectored,
20 type_alias_impl_trait,
21 concat_bytes,
22 try_trait_v2
23)]
24#![doc = include_str!("../../../README.md")]
25#![doc = include_str!("../README.md")]
26
27pub type LkError = anyhow::Error;
29pub type LkResult<T = ()> = std::result::Result<T, LkError>;
31
32pub mod prelude {
34 pub use super::*;
35 pub use argument_traits::{LkEnv, Stmnts, TryAsSpace};
36 pub use linkspace_core::{
37 ShouldBreak,
38 byte_fmt::{
39 AB, B64,
40 endian_types::{self, U64},
41 },
42 point::{
43 Domain, Error as PointError, GroupID, Link, LkHash, LkIdentity, LkPath, LkPathArray,
44 LkPathError, LkPathPart, LkPathSegm, MutXHeader, NewPoint, PFields, PFieldsExt, Point,
45 PointArc, PointBox, PointExt, PointParts, PointPtr, PointTypeFlags, PubKey,
46 RecvPointPtr, SetXHeader, SigningExt, Stamp, Tag, WithNewXHeader, XFlags, XHeader, ab,
47 abf, as_abtxt_c,
48 link::{link, linkf},
49 lkpath, now,
50 repr::PointFmt,
51 try_ab,
52 },
53 predicate::exprs::{CoreTestOp, TestOp},
54 query::options::KnownOptions,
55 space_expr::Space,
56 };
57 pub use std::ops::ControlFlow;
58}
59
60use linkspace_core::point as lkp;
61use prelude::*;
62
63pub use prelude::LkIdentity;
64
65pub use point::{lk_datapoint, lk_keypoint, lk_linkpoint};
66pub mod point {
68 use std::io;
69
70 use anyhow::Context;
71 use linkspace_core::{ShouldBreak, space_expr::Space};
72 pub use lkp::CheckHash;
73 use lkp::{PartialPoint, reroute::MutXHeaderPoint};
74
75 use super::*;
76 pub fn lk_datapoint(data: &[u8]) -> LkResult<PointBox> {
91 lk_datapoint_ref(data).map(|v| v.as_box())
92 }
93 pub fn lk_datapoint_ref(data: &[u8]) -> LkResult<PointParts<'_>> {
95 Ok(lkp::try_datapoint_ref(data)?)
96 }
97 pub fn lk_linkpoint(space: &dyn TryAsSpace, data: &[u8], links: &[Link]) -> LkResult<PointBox> {
123 let Space {
124 domain,
125 group,
126 path,
127 } = space.try_as_space(&())?;
128 lk_linkpoint_ref(domain, group, &path, data, links, lkp::now()).map(|v| v.as_box())
129 }
130 pub fn lk_linkpoint_ref<'o>(
132 domain: Domain,
133 group: GroupID,
134 path: &'o LkPath,
135 data: &'o [u8],
136 links: &'o [Link],
137 stamp: Stamp,
138 ) -> LkResult<PointParts<'o>> {
139 Ok(lkp::try_linkpoint_ref(
140 group, domain, path, links, data, stamp,
141 )?)
142 }
143 pub fn lk_keypoint(space: &dyn TryAsSpace, data: &[u8], links: &[Link]) -> LkResult<PointBox> {
145 let Space {
146 domain,
147 group,
148 path,
149 } = space.try_as_space(&())?;
150 linkspace_core::thread_local::with_id(|key| {
151 lk_keypoint_ref(key, domain, group, &path, data, links, lkp::now()).map(|v| v.as_box())
152 })
153 .context("No signing key has been set")?
154 }
155 pub fn lk_keypoint_ref<'o>(
157 key: &LkIdentity,
158 domain: Domain,
159 group: GroupID,
160 path: &'o LkPath,
161 data: &'o [u8],
162 links: &'o [Link],
163 stamp: Stamp,
164 ) -> LkResult<PointParts<'o>> {
165 Ok(lkp::try_keypoint_ref(
166 group, domain, path, links, data, stamp, key,
167 )?)
168 }
169 pub fn lk_point_ref<'o>(
171 key: Option<&LkIdentity>,
172 domain: Domain,
173 group: GroupID,
174 path: &'o LkPath,
175 data: &'o [u8],
176 links: &'o [Link],
177 stamp: Stamp,
178 ) -> LkResult<PointParts<'o>> {
179 Ok(lkp::try_point(
180 group, domain, path, links, data, stamp, key,
181 )?)
182 }
183
184 pub fn lk_deserialize_ref<'a>(
185 check: CheckHash,
186 mut buf: &'a [u8],
187 cb: &mut dyn FnMut(&PointPtr) -> ShouldBreak,
188 ) -> Result<&'a [u8], PointError> {
189 while !buf.is_empty() {
190 let (p, rest) = lkp::deserialize::read_point(buf, check)?;
191 buf = rest;
192 if cb(&p) {
193 break;
194 };
195 }
196 Ok(buf)
197 }
198 pub fn lk_deserialize(buf: &[u8], mode: IOMode) -> Result<(PointBox, &[u8]), PointError> {
199 let (mut head, inner, tail) = PartialPoint::take(buf, mode.check())?;
200 mode.set_header(&mut head.xheader);
201 let point = unsafe {
202 PointBox::read_excl2(head, CheckHash::Check, |out| out.copy_from_slice(inner))?
203 };
204 mode.check_private(&point)?;
205 Ok((point, tail))
206 }
207 pub fn lk_deserialize_arc(buf: &[u8], mode: IOMode) -> Result<(PointArc, &[u8]), PointError> {
208 let (mut head, inner, tail) = PartialPoint::take(buf, mode.check())?;
209 mode.set_header(&mut head.xheader);
210 let point = unsafe {
211 PointArc::read_excl(head, CheckHash::Check, |out| out.copy_from_slice(inner))?
212 };
213 mode.check_private(&point)?;
214 Ok((point, tail))
215 }
216
217 pub fn lk_deserialize_len(buf: &[u8]) -> Result<u16, PointError> {
219 lkp::deserialize::read_point_len(buf)
220 }
221
222 #[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
223 #[cfg_attr(feature = "pyo3", pyo3::pyclass(frozen, eq))]
224 #[cfg_attr(feature = "wasm", wasm_bindgen::prelude::wasm_bindgen)]
225 pub enum IOMode {
227 #[default]
228 Transmit,
229 Storage,
232 }
233 impl IOMode {
234 pub fn check_private(self, p: &impl Point) -> Result<(), PointError> {
235 if self != IOMode::Storage {
236 p.check_private()?
237 }
238 Ok(())
239 }
240 pub fn check(self) -> CheckHash {
241 match self {
242 IOMode::Transmit => CheckHash::Check,
243 IOMode::Storage => CheckHash::SkipHash,
244 }
245 }
246 pub fn set_header(self, h: &mut XHeader) {
247 match self {
248 IOMode::Transmit => {
249 h.incr_xhop();
250 h.xflags.remove(XFlags::STORAGE);
251 }
252 IOMode::Storage => {
253 h.xflags.insert(XFlags::STORAGE);
254 }
255 }
256 }
257 }
258
259 pub fn lk_serialize_ref(
263 p: &dyn Point,
264 mode: IOMode,
265 out: &mut dyn FnMut(lkp::ByteSegments) -> LkResult<()>,
266 ) -> LkResult<()> {
267 let mut point = MutXHeaderPoint::new(p);
268 match mode {
269 IOMode::Transmit => {
270 p.check_private().map_err(io::Error::other)?;
271 point.xheader.incr_xhop();
272 point.xheader.xflags.remove(XFlags::STORAGE);
273 }
274 IOMode::Storage => {
275 point.xheader.xflags.insert(XFlags::STORAGE);
276 }
277 }
278 out(point.byte_segments())
279 }
280 pub fn lk_serialize(p: &dyn Point) -> Box<[u8]> {
281 let mut v = vec![].into_boxed_slice();
282 lk_serialize_ref(p, IOMode::Transmit, &mut |o| {
283 v = o.to_bytes();
284 Ok(())
285 })
286 .unwrap();
287 v
288 }
289 pub fn as_netarc(p: &dyn Point) -> PointArc {
290 p.as_arc()
291 }
292 pub fn as_netbox(p: &dyn Point) -> PointBox {
293 p.as_box()
294 }
295}
296
297pub use query::{LkQuery, lkq_add, lkq_insert_mut, lkq_new, lkq_space};
298pub mod query {
300 #[derive(Clone, PartialEq, Default)]
346 #[repr(transparent)]
347 #[cfg_attr(feature = "pyo3", pyo3::pyclass(unsendable, eq))]
348 #[cfg_attr(feature = "wasm", wasm_bindgen::prelude::wasm_bindgen)]
349 pub struct LkQuery(pub(crate) linkspace_core::query::Query);
350
351 pub static LK_Q: LkQuery = LkQuery(linkspace_core::query::Query::DEFAULT);
353
354 impl std::fmt::Display for LkQuery {
355 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
356 self.0.fmt(f, true)
357 }
358 }
359 impl std::fmt::Debug for LkQuery {
360 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
361 self.0.fmt(f, false)
362 }
363 }
364
365 use anyhow::Context;
366 pub use linkspace_core::predicate::predicate_type::PredicateType;
367 pub use linkspace_core::prelude::KnownOptions;
368 use linkspace_core::{prelude::ExtPredicate, query::Query};
369
370 use super::*;
371 pub fn lkq_new(stmnts: &dyn Stmnts, scope: &dyn LkEnv) -> LkResult<LkQuery> {
373 let mut q = LK_Q.clone();
374 lkq_add_mut(&mut q, stmnts, scope)?;
375 Ok(q)
376 }
377
378 pub fn lkq_hash(hash: LkHash, stmnts: &dyn Stmnts) -> LkResult<LkQuery> {
380 let mut q = LkQuery(linkspace_core::query::Query::hash_eq(hash));
381 lkq_add_mut(&mut q, stmnts, &())?;
382 if let Some(wopts) = &mut q.0.watch_options {
383 wopts.id.extend(&now().0);
384 }
385 Ok(q)
386 }
387 pub fn lkq_space(
392 space_expr: &str,
393 stmnts: &dyn Stmnts,
394 scope: &dyn LkEnv,
395 ) -> LkResult<LkQuery> {
396 let qexpr: linkspace_core::space_expr::ShortQueryExpr = space_expr.parse()?;
397 let scope = scope.as_scope();
398 let mut q = LkQuery(qexpr.eval(&scope.as_dyn())?);
399 lkq_add_mut(&mut q, stmnts, &scope)?;
400 Ok(q)
401 }
402
403 pub fn lkq_watch(mut query: LkQuery, watch_prefix: &[u8]) -> LkQuery {
404 let wo = query.0.watch_options.get_or_insert(Default::default());
405 wo.id = [watch_prefix, &wo.id].concat();
406 query
407 }
408 pub fn lkq_path(space: &Space, pubkey: Option<PubKey>) -> LkQuery {
409 let q = Query::subspace(
410 Some(space.domain),
411 Some(space.group),
412 &space.path,
413 true,
414 pubkey,
415 );
416 LkQuery(q)
417 }
418 pub fn lkq_prefix(space: &Space, pubkey: Option<PubKey>) -> LkQuery {
419 let q = Query::subspace(
420 Some(space.domain),
421 Some(space.group),
422 &space.path,
423 false,
424 pubkey,
425 );
426 LkQuery(q)
427 }
428
429 pub fn lkq_insert_mut(query: &mut LkQuery, field: &str, test: &str, val: &[u8]) -> LkResult {
434 if field.is_empty() {
435 query.0.add_option(test, &[val])
436 } else {
437 let epre = ExtPredicate {
438 kind: field
439 .parse()
440 .with_context(|| format!("No such Field '{field}'"))?,
441 op: test
442 .parse()
443 .map_err(|e| anyhow::anyhow!("bad operator `{e}`"))?,
444 val: val.to_vec().into(),
445 };
446 query.0.predicates.add_ext_predicate(epre)
447 }
448 }
449 pub fn lkq_add(query: &LkQuery, stmnts: &dyn Stmnts, scope: &dyn LkEnv) -> LkResult<LkQuery> {
451 let mut query = query.clone();
452 lkq_add_mut(&mut query, stmnts, scope)?;
453 Ok(query)
454 }
455
456 pub fn lkq_add_mut(query: &mut LkQuery, stmnts: &dyn Stmnts, scope: &dyn LkEnv) -> LkResult {
458 let scope = scope.as_scope();
459 let scope = scope.as_dyn();
460 stmnts.per_line(&mut |line| query.0.parse(line, scope))
461 }
462 pub fn lkq_clear_mut(query: &mut LkQuery) {
464 *query = LK_Q.clone();
465 }
466 pub fn lkq_stringify(query: &LkQuery, abe: bool) -> String {
468 query.0.to_str(abe)
469 }
470 pub fn lkq_exact_domain(query: &LkQuery) -> LkResult<Option<Domain>> {
472 let domain_set = query.0.predicates.domain;
473 if domain_set == Default::default() {
474 return Ok(None);
475 }
476 domain_set
477 .as_eq()
478 .map(Into::into)
479 .context("query contains a set of domains")
480 .map(Some)
481 }
482 pub fn lkq_exact_group(query: &LkQuery) -> LkResult<Option<GroupID>> {
484 let group_set = query.0.predicates.group;
485 if group_set == Default::default() {
486 return Ok(None);
487 }
488 group_set
489 .as_eq()
490 .map(Into::into)
491 .context("query contains a set of groups")
492 .map(Some)
493 }
494
495 pub use linkspace_core::query::CompiledQuery;
496 #[allow(clippy::type_complexity)]
498 pub fn lkq_compile(q: LkQuery) -> LkResult<CompiledQuery> {
499 q.0.compile()
500 }
501}
502
503pub mod identity {
505 use super::prelude::*;
506 pub use linkspace_commons_internal::argon2_identity::{
507 self, Costs, DEFAULT_COST, INSECURE_COST, PARANOID_COST,
508 };
509
510 pub fn lki_generate() -> LkIdentity {
512 LkIdentity::generate()
513 }
514 pub fn lki_encrypt(id: &LkIdentity, pass: &[u8]) -> String {
517 argon2_identity::encrypt(
518 id,
519 pass,
520 if pass.is_empty() {
521 INSECURE_COST
522 } else {
523 DEFAULT_COST
524 },
525 )
526 }
527 pub fn lki_encrypt_paranoid(id: &LkIdentity, pass: &[u8]) -> String {
529 argon2_identity::encrypt(id, pass, PARANOID_COST)
530 }
531 pub fn lki_decrypt(enckey: &str, pass: &[u8]) -> LkResult<LkIdentity> {
533 Ok(linkspace_commons_internal::argon2_identity::decrypt(
534 enckey, pass,
535 )?)
536 }
537 pub fn lki_pubkey(encrypted_identity: &str) -> LkResult<PubKey> {
539 Ok(linkspace_commons_internal::argon2_identity::pubkey(
540 encrypted_identity,
541 )?)
542 }
543 pub fn lki_adhoc(space: &dyn TryAsSpace, pass: &[u8]) -> LkResult<LkIdentity> {
545 let space = space.try_as_space(&())?;
546 Ok(linkspace_commons_internal::argon2_identity::adhoc(
547 space.domain,
548 space.group,
549 pass,
550 ))
551 }
552 #[doc(hidden)]
553 pub use linkspace_commons_internal::argon2_identity::test_key;
554}
555
556#[cfg(feature = "lmdb")]
557pub use system::lks_open;
558#[cfg(feature = "inmem")]
559pub use system::lks_open_inmem;
560#[cfg(all(any(feature = "lmdb", feature = "inmem"), not(target_family = "wasm")))]
561pub use system::lks_process_while;
562#[cfg(any(feature = "lmdb", feature = "inmem"))]
563pub use system::{
564 LkSystem,
565 cb::{on_point, on_point_then, try_on_point},
566 lks_process, lks_save, lks_scan, lks_tap,
567};
568
569#[cfg(any(feature = "lmdb", feature = "inmem"))]
570pub mod system {
572 #[derive(Clone, PartialEq)]
588 #[repr(transparent)]
589 #[cfg_attr(feature = "pyo3", pyo3::pyclass(unsendable, eq))]
590 #[cfg_attr(feature = "wasm", wasm_bindgen::prelude::wasm_bindgen)]
591 pub struct LkSystem(pub(crate) System);
592
593 use std::cell::Cell;
594
595 use crate::interop::sys_interop::System;
596 use anyhow::Context;
597 use linkspace_system::*;
598
599 impl std::fmt::Debug for LkSystem {
600 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
601 f.debug_struct("LkSystem")
602 .field("info", &lks_info(Some(self)))
603 .finish()
604 }
605 }
606
607 use crate::query::LkQuery;
608 use cb::PointHandler;
609 use linkspace_core::{prelude::PredicateType, saturating_cast};
610 use linkspace_system::thread_local::*;
611 use tracing::debug_span;
612
613 use super::*;
614 #[cfg(feature = "lmdb")]
619 pub fn lks_open(dir: impl AsRef<std::path::Path>) -> std::io::Result<LkSystem> {
620 let rt = linkspace_system::open_linkspace_dir(dir.as_ref(), true)?;
621 Ok(LkSystem(rt))
622 }
623 #[cfg(feature = "inmem")]
626 pub fn lks_open_inmem(name: &str) -> LkSystem {
627 let rt = linkspace_system::open_inmem(name);
628 LkSystem(rt)
629 }
630 #[cfg(feature = "inmem")]
631 #[doc(hidden)]
632 pub use linkspace_system::storage_engine::inmem::IN_MEMORY_DIRTY_XSTAMP;
633
634 pub fn lks_from_info(lksi: &LksInfo) -> LkResult<LkSystem> {
635 match lksi.kind.as_str() {
636 #[cfg(feature = "lmdb")]
637 "lmdb" => Ok(lks_open(&lksi.name)?),
638 #[cfg(feature = "inmem")]
639 "inmem" => Ok(lks_open_inmem(&lksi.name)),
640 kind => anyhow::bail!("Not compiled with support for {kind} system"),
641 }
642 }
643 pub fn lks_split() -> LkSystem {
645 LkSystem(get_system().unwrap().new_system())
646 }
647
648 pub fn lks_current() -> Option<crate::LkSystem> {
650 linkspace_system::thread_local::opt_get_system().map(crate::LkSystem)
651 }
652
653 pub fn lks_chsys<A>(lks: &LkSystem, fnc: impl FnOnce() -> A) -> A {
654 use_sys(&lks.0, fnc)
655 }
656
657 pub fn lks_last() -> Stamp {
659 get_system().unwrap().last()
660 }
661
662 pub fn lks_save(point: &dyn Point) -> std::io::Result<bool> {
665 save_dyn_one(get_system_io()?.storage_engine(), point).map(|o| o.is_written())
666 }
667 pub fn lks_save_all(ppoint: &[&dyn Point]) -> std::io::Result<usize> {
669 let (start, excl) = lks_save_all_ext(ppoint)?;
670 Ok((excl.get() - start.get()) as usize)
671 }
672 pub fn lks_save_all_ext(ppoint: &[&dyn Point]) -> std::io::Result<(Stamp, Stamp)> {
674 let range =
675 with_system(|lks| save_dyn_iter(lks.storage_engine(), ppoint.iter().copied(), |_| ()))?;
676 Ok((range.start.into(), range.end.into()))
677 }
678 pub fn lks_save_new(
680 ppoint: &[&dyn Point],
681 on_new: &mut dyn FnMut(&dyn Point),
682 ) -> std::io::Result<usize> {
683 let range = with_system(|lks| {
684 save_dyn_iter(lks.storage_engine(), ppoint.iter().copied(), |p| on_new(&p))
685 })?;
686 Ok(range.count())
687 }
688 pub fn lks_scan(
695 query: &LkQuery,
696 cb: &mut dyn for<'o> FnMut(&'o dyn Point) -> ShouldBreak,
697 ) -> LkResult<i64> {
698 let mut match_counter = 0; let options = query.0.get_scan_options();
700 with_system(|lks| {
701 let breaks = lks.scan_query(
702 options,
703 &query.0,
704 &mut match_counter,
705 None,
706 &mut |point: &dyn Point, _wid: Option<&[u8]>, _: &System| {
707 cb::should_break(cb(point))
708 },
709 )?;
710 Ok(linkspace_system::system::return_value(
711 match_counter,
712 breaks,
713 ))
714 })
715 }
716
717 pub fn lks_scan_one_ref<A>(
719 query: &LkQuery,
720 cb: &mut dyn FnMut(&dyn Point) -> A,
721 ) -> LkResult<Option<A>> {
722 let mut result = None;
723 let mut calls = |p: &dyn Point| {
724 result = Some(cb(p));
725 true
726 };
727 with_system(|lks| {
728 let mode = query.0.get_scan_options();
729 let reader = lks.dyn_reader();
730 let _ = dyn_query(&*reader, 1, mode, &query.0.predicates, None, &mut calls);
731 });
732 Ok(result)
733 }
734 pub fn lks_scan_one_hash_ref<A>(
736 hash: LkHash,
737 cb: &mut dyn FnMut(&dyn Point) -> A,
738 ) -> LkResult<Option<A>> {
739 with_system(|lks| {
740 let mut result = None;
741 lks.dyn_reader()
742 .get_points_by_hash(&mut [hash].into_iter(), &mut |point| {
743 if let Ok(point) = point {
744 result = Some(cb(point));
745 }
746 true
747 });
748 Ok(result)
749 })
750 }
751
752 pub fn lks_scan_hashes<H: AsRef<LkHash>>(
754 hashes: &mut dyn Iterator<Item = H>,
755 cb: &mut dyn FnMut(Option<&dyn Point>, H) -> ShouldBreak,
756 ) -> LkResult<i64> {
757 let mut c = 0;
758 with_system(|lks| {
759 let r = lks.dyn_reader();
760 let last_h: Cell<Option<H>> = Cell::new(None);
761 let mut iter = std::iter::from_fn(|| {
762 let item = hashes.next()?;
763 let hash = *item.as_ref();
764 last_h.set(Some(item));
765 Some(hash)
766 });
767 let breaks = r.get_points_by_hash(&mut iter, &mut |result| {
768 if result.is_ok() {
769 c += 1;
770 }
771 cb(result.ok(), last_h.take().unwrap())
772 });
773 Ok(saturating_cast(c, !breaks))
774 })
775 }
776
777 pub fn lks_watch(query: LkQuery, cb: impl PointHandler + 'static) -> LkResult<()> {
779 lks_watch2(query, cb, debug_span!("lks_watch - (untraced)"))
780 }
781
782 pub fn lks_watch2(
785 mut query: LkQuery,
786 cb: impl PointHandler + 'static,
787 span: tracing::Span,
788 ) -> LkResult<()> {
789 with_system(|lks| {
790 query.0.scan_options = None;
791 query.0.watch_options.get_or_insert_default();
792 lks.tap_query(query.0, interop::sys_interop::Handler(cb), span)?;
793 Ok(())
794 })
795 }
796
797 pub fn lks_tap(query: LkQuery, cb: impl PointHandler + 'static) -> LkResult<i64> {
814 lks_tap2(query, cb, debug_span!("lks_tap - (untraced)"))
815 }
816
817 pub fn lks_tap2(
820 query: LkQuery,
821 cb: impl PointHandler + 'static,
822 span: tracing::Span,
823 ) -> LkResult<i64> {
824 with_system(|lks| lks.tap_query(query.0, interop::sys_interop::Handler(cb), span))
825 }
826
827 pub fn lks_multi_tap(
828 queries: Vec<LkQuery>,
829 mut cb: impl PointHandler + 'static,
830 any_close: bool,
831 span: tracing::Span,
832 ) -> LkResult<i64> {
833 with_system(|lks| {
834 if queries.is_empty() {
835 cb.on_close(&query::LK_Q, handlers::StopReason::Finish, 0, 0);
836 return Ok(0);
837 }
838 let mq = handlers::MultiQueryHandler::new(
839 interop::sys_interop::Handler(cb),
840 queries.len(),
841 any_close,
842 );
843 let mut total = 0;
844 let mut has_neg = false;
845 for q in queries {
846 let span = tracing::debug_span!(parent: &span,"multi query", inner=?q);
847 let r = lks.tap_query(q.0, mq.clone(), span)?;
848 has_neg = has_neg || r < 0;
849 total += r.abs();
850 }
851
852 Ok(total * if has_neg { -1 } else { 1 })
853 })
854 }
855
856 pub fn vspan(name: &str) -> tracing::Span {
858 tracing::debug_span!("{}", name)
859 }
860
861 pub fn lks_forget_hashes(
862 hashes: &[LkHash],
863 cb: &mut dyn FnMut(&dyn Point),
864 ) -> anyhow::Result<()> {
865 lks_mutate(MutateOp::Forget, hashes, cb)
866 }
867 pub fn lks_forget(query: &LkQuery, cb: &mut dyn FnMut(&dyn Point)) -> LkResult<()> {
868 let ro = query.0.get_scan_options();
869 with_system(|lks| {
870 let reader = lks.get_reader();
871 let mut hashes = get_query(&reader, ro, &query.0.predicates).map(|p| {
873 tracing::info!(point=%PointFmt(&p), "Removing");
874 p.hash()
875 });
876 storage_engine::methods::remove_by_hash(
877 lks.storage_engine(),
878 MutateOp::Forget,
879 &mut hashes,
880 cb,
881 )?;
882 Ok(())
883 })
884 }
885 pub use linkspace_system::storage_engine::tables_traits::MutateOp;
886 pub fn lks_mutate(
887 op: MutateOp,
888 hashes: &[LkHash],
889 cb: &mut dyn FnMut(&dyn Point),
890 ) -> anyhow::Result<()> {
891 with_system(|lks| {
892 Ok(storage_engine::methods::remove_by_hash(
893 lks.storage_engine(),
894 op,
895 &mut hashes.iter().copied(),
896 cb,
897 )?)
898 })
899 }
900 pub fn lks_stop(id: &[u8], range: bool) {
902 with_system(|lks| {
903 if range {
904 lks.close_prefix(id)
905 } else {
906 lks.close(id)
907 }
908 })
909 }
910 pub fn lks_stop_query(query: &LkQuery, range: bool) -> LkResult<()> {
911 let id = query
912 .0
913 .watch_id()
914 .context("this query doesnt have a :watch ID")?;
915 with_system(|lks| {
916 if range {
917 lks.close_prefix(id)
918 } else {
919 lks.close(id)
920 }
921 });
922 Ok(())
923 }
924 pub fn lks_process() -> Stamp {
926 with_system(|lks| lks.process())
927 }
928 #[cfg(not(target_family = "wasm"))]
938 pub fn lks_process_while(
939 watch: Option<&linkspace_core::query::WatchIDRef>,
940 timeout: Stamp,
941 ) -> LkResult<isize> {
942 let timeout = (timeout != Stamp::ZERO)
943 .then(|| std::time::Instant::now() + std::time::Duration::from_micros(timeout.get()));
944 _lks_process_while(watch, timeout).map(|i| i.as_isize())
945 }
946
947 #[doc(hidden)]
948 pub use linkspace_system::system::ProcessWhileResult;
949 #[doc(hidden)]
950 #[cfg(not(target_family = "wasm"))]
951 pub fn _lks_process_while(
953 watch: Option<&linkspace_core::query::WatchIDRef>,
954 timeout: Option<std::time::Instant>,
955 ) -> LkResult<ProcessWhileResult> {
956 with_system(|lks| lks.run_while(timeout, watch))
957 }
958
959 pub async fn lks_await_write() -> Stamp {
963 let lks = get_system().unwrap();
964 let result = lks.await_write().await;
965 unsafe { set_system(lks) };
966 result
967 }
968 pub async fn lks_process_async() {
972 let lks = get_system().unwrap();
973 loop {
974 lks.process();
975 lks.await_write().await;
976 }
977 }
978 pub fn lks_list_watches(
980 cb: &mut dyn FnMut(&[u8], &LkQuery, Result<(), PredicateType>),
981 point: Option<&dyn Point>,
982 ) {
983 with_system(|lks| {
984 for el in lks.dbg_watches().entries() {
985 let mut is_match = Ok(());
986 if let Some(p) = point {
987 is_match = el.info.tests.test_point(p);
988 }
989 cb(
990 &el.info.query_id,
991 LkQuery::from_impl(&el.info.query),
992 is_match,
993 )
994 }
995 })
996 }
997
998 #[cfg_attr(feature = "pyo3", pyo3::pyclass(frozen, get_all))]
999 #[cfg_attr(
1000 feature = "wasm",
1001 wasm_bindgen::prelude::wasm_bindgen(getter_with_clone, inspectable)
1002 )]
1003 #[derive(Debug, Clone)]
1004 pub struct LksInfo {
1006 pub kind: String,
1008 pub name: String,
1010 pub instance: usize,
1011 }
1012 impl std::fmt::Display for LksInfo {
1013 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1014 std::fmt::Debug::fmt(self, f)
1015 }
1016 }
1017 pub fn lks_info(lks: Option<&LkSystem>) -> LkResult<LksInfo> {
1019 let lks = lks
1020 .map(|o| o.0.clone())
1021 .unwrap_or_else(|| get_system().unwrap());
1022 let instance = lks.instance();
1023 let se = lks.storage_engine();
1024 Ok(LksInfo {
1025 instance,
1026 name: se
1028 .dir()
1029 .to_str()
1030 .context("TODO: non-utf8 name not supported ATM")?
1031 .to_string(),
1032 kind: match se {
1033 #[cfg(feature = "lmdb")]
1034 linkspace_system::storage_engine::impl_enum::StorageEngineImplEnum::Lmdb(_) => {
1035 "lmdb".to_string()
1036 }
1037 #[cfg(feature = "inmem")]
1038 linkspace_system::storage_engine::impl_enum::StorageEngineImplEnum::InMem(_) => {
1039 "inmem".to_string()
1040 }
1041 },
1042 })
1043 }
1044
1045 pub fn _lks_dump() {
1046 let lks = get_system().unwrap();
1047 let se = lks.storage_engine();
1048 storage_engine::impl_enum::debug_se(se);
1049 }
1050
1051 #[cfg(any(feature = "lmdb", feature = "inmem"))]
1052 pub mod cb {
1055 pub use linkspace_system::handlers::StopReason;
1056
1057 use std::ops::{ControlFlow, Try};
1058
1059 use linkspace_core::{ShouldBreak, point::Point, query::WatchIDRef};
1060
1061 use super::query::LkQuery;
1062
1063 pub fn should_break(o: ShouldBreak) -> ControlFlow<()> {
1064 if o {
1065 ControlFlow::Break(())
1066 } else {
1067 ControlFlow::Continue(())
1068 }
1069 }
1070
1071 pub trait PointHandler {
1074 fn on_point(
1076 &mut self,
1077 point: &dyn Point,
1078 watch: Option<&WatchIDRef>,
1079 ) -> ControlFlow<()>;
1080 fn on_close(
1082 &mut self,
1083 _: &LkQuery,
1084 _: StopReason,
1085 _total_calls: u64,
1086 _watch_calls: u64,
1087 ) {
1088 }
1089 }
1090 impl PointHandler for Box<dyn PointHandler> {
1091 fn on_point(
1092 &mut self,
1093 point: &dyn Point,
1094 watch: Option<&WatchIDRef>,
1095 ) -> ControlFlow<()> {
1096 (**self).on_point(point, watch)
1097 }
1098
1099 fn on_close(
1100 &mut self,
1101 query: &LkQuery,
1102 reason: StopReason,
1103 total: u64,
1104 watch_matches: u64,
1105 ) {
1106 (**self).on_close(query, reason, total, watch_matches)
1107 }
1108 }
1109 impl<T: PointHandler> PointHandler for std::rc::Rc<std::cell::RefCell<T>> {
1110 fn on_point(
1111 &mut self,
1112 point: &dyn Point,
1113 watch: Option<&WatchIDRef>,
1114 ) -> ControlFlow<()> {
1115 self.borrow_mut().on_point(point, watch)
1116 }
1117
1118 fn on_close(
1119 &mut self,
1120 q: &LkQuery,
1121 stop_reason: StopReason,
1122 total_calls: u64,
1123 watch_calls: u64,
1124 ) {
1125 self.borrow_mut()
1126 .on_close(q, stop_reason, total_calls, watch_calls);
1127 }
1128 }
1129
1130 #[derive(Copy, Clone, Debug)]
1131 pub struct Handle<
1132 H: FnMut(&dyn Point, Option<&WatchIDRef>) -> ControlFlow<()>,
1133 S: FnMut(&LkQuery, StopReason, u64, u64),
1134 > {
1135 pub on_point: H,
1136 pub on_close: S,
1137 }
1138 impl<H, S> PointHandler for Handle<H, S>
1139 where
1140 H: FnMut(&dyn Point, Option<&WatchIDRef>) -> ControlFlow<()>,
1141 S: FnMut(&LkQuery, StopReason, u64, u64),
1142 {
1143 fn on_point(
1144 &mut self,
1145 point: &dyn Point,
1146 watch: Option<&WatchIDRef>,
1147 ) -> ControlFlow<()> {
1148 let h: &mut H = &mut self.on_point;
1149 h(point, watch)
1150 }
1151
1152 fn on_close(
1153 &mut self,
1154 query: &LkQuery,
1155 reason: StopReason,
1156 total: u64,
1157 watch_matches: u64,
1158 ) {
1159 let s: &mut S = &mut self.on_close;
1160 s(query, reason, total, watch_matches)
1161 }
1162 }
1163 pub fn on_point_then(
1164 mut handle_point: impl FnMut(&dyn Point) -> crate::ShouldBreak,
1165 mut on_close: impl FnMut(StopReason),
1166 ) -> impl PointHandler {
1167 Handle {
1168 on_point: move |point: &dyn Point, _watch: Option<&WatchIDRef>| {
1169 if (handle_point)(point) {
1170 ControlFlow::Break(())
1171 } else {
1172 ControlFlow::Continue(())
1173 }
1174 },
1175 on_close: move |_, r, _, _| (on_close)(r),
1176 }
1177 }
1178
1179 #[derive(Copy, Clone)]
1180 struct OnPoint<A>(A);
1181 pub fn on_point(
1183 mut handle_point: impl FnMut(&dyn Point) -> crate::ShouldBreak,
1184 ) -> impl PointHandler {
1185 OnPoint(move |point: &dyn Point, _watch: Option<&WatchIDRef>| {
1186 if (handle_point)(point) {
1187 ControlFlow::Break(())
1188 } else {
1189 ControlFlow::Continue(())
1190 }
1191 })
1192 }
1193
1194 pub fn try_on_point<F, E>(mut handle_point: F) -> impl PointHandler
1196 where
1197 E: std::fmt::Debug,
1198 F: for<'a> FnMut(&'a dyn Point) -> Result<(), E> + 'static,
1199 {
1200 OnPoint(move |point: &dyn Point, _watch: Option<&WatchIDRef>| {
1201 (handle_point)(point)
1202 .branch()
1203 .map_break(|brk| tracing::error!(?brk, "break"))
1204 })
1205 }
1206
1207 impl<A> PointHandler for OnPoint<A>
1208 where
1209 A: FnMut(&dyn Point, Option<&WatchIDRef>) -> ControlFlow<()>,
1210 {
1211 fn on_point(
1212 &mut self,
1213 point: &dyn Point,
1214 watch: Option<&WatchIDRef>,
1215 ) -> ControlFlow<()> {
1216 (self.0)(point, watch)
1217 }
1218 }
1219
1220 impl<F, R: Try<Output = (), Residual = E>, E: std::fmt::Debug> PointHandler for F
1221 where
1222 F: FnMut(&dyn Point, Option<&WatchIDRef>) -> R + 'static,
1223 {
1224 fn on_point(
1225 &mut self,
1226 point: &dyn Point,
1227 watch: Option<&WatchIDRef>,
1228 ) -> ControlFlow<()> {
1229 match (self)(point, watch).branch() {
1230 ControlFlow::Continue(_) => ControlFlow::Continue(()),
1231 ControlFlow::Break(e) => {
1232 tracing::error!(?e, "break");
1233 ControlFlow::Break(())
1234 }
1235 }
1236 }
1237 }
1238
1239 impl<F, S, R: Try<Output = (), Residual = E>, E> PointHandler for (F, S)
1240 where
1241 F: FnMut(&dyn Point, Option<&WatchIDRef>) -> R + 'static,
1242 S: FnMut(&LkQuery, StopReason, u64, u64) + 'static,
1243 {
1244 fn on_point(
1245 &mut self,
1246 point: &dyn Point,
1247 watch: Option<&WatchIDRef>,
1248 ) -> ControlFlow<()> {
1249 match (self.0)(point, watch).branch() {
1250 ControlFlow::Continue(_) => ControlFlow::Continue(()),
1251 ControlFlow::Break(_e) => ControlFlow::Break(()),
1252 }
1253 }
1254 fn on_close(&mut self, query: &LkQuery, reason: StopReason, total: u64, writen: u64) {
1255 (self.1)(query, reason, total, writen)
1256 }
1257 }
1258 }
1259}
1260
1261pub mod commons;
1263pub mod experimental;
1264
1265mod argument_traits;
1266#[cfg(any(feature = "pyo3", feature = "wasm"))]
1267pub mod ffi;
1268pub use consts::{PRIVATE, PUBLIC};
1269pub mod consts {
1270 pub use linkspace_core::consts::point_consts::*;
1271 pub use linkspace_core::consts::{EXCHANGE_DOMAIN, PRIVATE, PUBLIC, TEST_GROUP, test_key};
1272}
1273
1274pub mod misc {
1276 pub use linkspace_core::point::FieldEnum;
1277 pub use linkspace_core::point::RecvPoint;
1278 pub use linkspace_core::point::point::DEFAULT_ROUTING_BITS;
1279 pub use linkspace_core::point::point::cmp::PointCmp;
1280 pub use linkspace_core::point::reroute::{MutXHeaderPoint, SharePointArc};
1281 pub use linkspace_core::point::space_order::SpaceDBEntry;
1282 pub use linkspace_core::point::{calc_free_space, deserialize::read_point_len};
1283 pub use misc_utils as utils;
1284
1285 pub use linkspace_core::stamp_fmt::START_STAMP;
1286
1287 use linkspace_core::prelude::B64;
1288
1289 pub fn blake3(val: &[u8]) -> B64<[u8; 32]> {
1291 B64(*linkspace_core::crypto::blake3(val).as_bytes())
1292 }
1293
1294 pub fn bytes2uniform(val: &[u8]) -> f64 {
1300 let rand_u64 = u64::from_be_bytes(
1301 val[..8]
1302 .try_into()
1303 .expect("bytes2uniform requires at least 8 bytes"),
1304 );
1305 let f64_exponent_bits: u64 = 1023u64 << 52;
1306 let value1_2 = f64::from_bits((rand_u64 >> (64 - 52)) | f64_exponent_bits);
1308 value1_2 - 1.0
1309 }
1310
1311 pub static MARK_SVG: &str = include_str!("./mark.svg");
1312}
1313
1314pub use abe::lka_eval;
1315pub mod abe {
1321 use super::*;
1322 use linkspace_core::abe::abtxt::as_abtxt;
1323 pub use lkp::repr::DEFAULT_POINT_EXPR;
1324
1325 pub fn lka_eval(expr: &str, scope: &dyn LkEnv) -> LkResult<Vec<u8>> {
1383 _lka_eval(expr, false, scope)
1384 }
1385
1386 pub fn lka_eval2str(expr: &str, scope: &dyn LkEnv) -> LkResult<String> {
1390 Ok(String::from_utf8(lka_eval(expr, scope)?)?)
1391 }
1392
1393 pub fn lka_eval_strict(expr: &str, scope: &dyn LkEnv) -> LkResult<Vec<u8>> {
1408 _lka_eval(expr, true, scope)
1409 }
1410 #[doc(hidden)]
1411 pub fn _lka_eval(expr: &str, strict: bool, scope: &dyn LkEnv) -> LkResult<Vec<u8>> {
1412 Ok(experimental::lka_eval_list(expr, strict, scope)?.concat())
1413 }
1414 pub fn lka_encode(bytes: impl AsRef<[u8]>, options: &str, scope: &dyn LkEnv) -> String {
1450 let bytes = bytes.as_ref();
1451 try_encode_impl(bytes, options, true, scope)
1452 .unwrap_or_else(|_v| as_abtxt(bytes).to_string())
1453 }
1454 pub fn lka_encode_utf8(bytes: &[u8]) -> String {
1456 linkspace_core::abe::abtxt::escape_expressions_loose(bytes).to_string()
1457 }
1458 pub fn lka_try_encode(
1464 bytes: impl AsRef<[u8]>,
1465 options: &str,
1466 ignore_encoder_err: bool,
1467 scope: &dyn LkEnv,
1468 ) -> LkResult<String> {
1469 let bytes = bytes.as_ref();
1470 try_encode_impl(bytes, options, ignore_encoder_err, scope)
1471 }
1472 fn try_encode_impl(
1473 bytes: &[u8],
1474 options: &str,
1475 ignore_encoder_err: bool,
1476 scope: &dyn LkEnv,
1477 ) -> LkResult<String> {
1478 Ok(linkspace_core::abe::eval::encode(
1479 &scope.as_scope().as_dyn(),
1480 bytes,
1481 options,
1482 ignore_encoder_err,
1483 )?)
1484 }
1485 pub fn lka_space_expr(expr: &str, scope: &dyn LkEnv) -> LkResult<Space> {
1486 let qexpr: linkspace_core::space_expr::SpaceExpr = expr.parse()?;
1487 qexpr.eval(scope.as_scope().as_dyn())
1488 }
1489}
1490
1491#[doc(hidden)]
1493pub mod interop;
1494
1495pub mod work_env {
1497
1498 use core::fmt;
1499 use std::sync::Arc;
1500
1501 use either::Either;
1502 use linkspace_commons_internal::{argon2_identity::eval::AdhocKey, rt_scope::RtScopeSet};
1503 use linkspace_core::abe::eval::{AsScope, AsScopeSet, Scope, ScopeSet};
1504 use linkspace_core::abe::scope::ArgV;
1505 use linkspace_core::eval::thread_local_scope::ScopeLocalLkEnv;
1506 use linkspace_core::eval::{CORE_SCOPE, CoreScope};
1507 use linkspace_core::point::{Domain, GroupID, LkIdentity, PointScope, PubKey};
1508 use linkspace_core::prelude::Point;
1509
1510 #[derive(Copy, Clone, Default, Debug)]
1511 #[repr(C)]
1512 pub struct LkEnvData<'o> {
1514 pub point: Option<&'o dyn Point>,
1516 pub argv: Option<&'o [&'o [u8]]>,
1518
1519 pub domain: Option<&'o Domain>,
1521 pub group: Option<&'o GroupID>,
1523 pub pubkey: Option<&'o PubKey>,
1525
1526 pub key: Option<&'o LkIdentity>,
1528
1529 #[cfg(any(feature = "lmdb", feature = "inmem"))]
1530 pub lks: Option<&'o super::LkSystem>,
1531 }
1532
1533 #[derive(Clone, Debug)]
1534 pub struct LkEnvDataSync {
1535 pub point: Option<PointArc>,
1536 pub argv: Option<Arc<Vec<Vec<u8>>>>,
1537 pub domain: Option<Domain>,
1538 pub group: Option<GroupID>,
1539 pub pubkey: Option<PubKey>,
1540 pub key: Option<LkIdentity>,
1541 #[cfg(any(feature = "lmdb", feature = "inmem"))]
1542 pub lksi: Option<LksInfo>,
1543 }
1544
1545 use crate::prelude::*;
1546 use crate::system::{LksInfo, lks_info};
1547 use std::{
1548 cell::{Cell, LazyCell},
1549 ptr::NonNull,
1550 };
1551
1552 use linkspace_core::thread_local as tl;
1553
1554 pub fn lke_domain() -> Domain {
1556 tl::get_domain(LkaScope::default().as_dyn())
1557 }
1558 pub fn lke_set_domain(domain: Domain) {
1559 let _ = tl::set_domain(Some(domain), true);
1560 }
1561
1562 pub fn lke_group() -> GroupID {
1564 tl::get_group(LkaScope::default().as_dyn())
1565 }
1566 pub fn lke_set_group(group: GroupID) {
1567 let _ = tl::set_group(Some(group), true);
1568 }
1569 pub fn lke_pubkey() -> PubKey {
1571 tl::get_pubkey(LkaScope::default().as_dyn())
1572 }
1573 pub fn lke_key() -> Option<LkIdentity> {
1574 tl::get_key()
1575 }
1576 pub fn lke_set_key(id: LkIdentity) {
1577 let _ = tl::set_key(Some(id), true);
1578 }
1579 pub fn lke_keypub() -> Option<PubKey> {
1581 tl::get_keypub()
1582 }
1583
1584 pub fn lke_with<A>(cb: impl FnOnce(crate::work_env::LkEnvData) -> A) -> A {
1586 let point: Option<&dyn Point> = match unsafe { &*crate::work_env::ENV_POINT.as_ptr() } {
1587 None => None,
1588 Some(Either::Left(l)) => Some(l),
1589 Some(Either::Right(l)) => Some(unsafe { l.as_ref() }),
1590 };
1591 let argv: Option<&[&[u8]]> = match unsafe { &*crate::work_env::ENV_ARGV.as_ptr() } {
1592 None => None,
1593 Some((bytes, _)) => Some(bytes),
1594 };
1595 use linkspace_system::thread_local as tls;
1596 let lks = tls::opt_get_system();
1597 cb(crate::work_env::LkEnvData {
1598 point,
1599 argv,
1600 group: unsafe { &*tl::GROUP.as_ptr() }.as_ref(),
1601 pubkey: unsafe { &*tl::PUBKEY.as_ptr() }.as_ref(),
1602 domain: unsafe { &*tl::DOMAIN.as_ptr() }.as_ref(),
1603 lks: lks.as_ref().map(crate::LkSystem::from_impl),
1604 key: unsafe { &*tl::SIGNING_KEY.as_ptr() }.as_ref(),
1605 })
1606 }
1607
1608 pub fn lka_set_keyval(key: &[&[u8]], value: &[u8]) -> crate::LkResult<Vec<u8>> {
1609 linkspace_core::thread_local::set_keyval_map(key, value)
1610 }
1611 #[allow(clippy::type_complexity)]
1612 pub fn lka_set_callback(cb: Box<dyn Fn(&[&[u8]], bool) -> crate::LkResult<Option<Vec<u8>>>>) {
1613 linkspace_core::thread_local::set_shared_cb(cb)
1614 }
1615 #[thread_local]
1616 pub static DUMMY_RT: LazyCell<crate::LkSystem> =
1617 LazyCell::new(|| crate::system::lks_open_inmem("_DUMMY_"));
1618
1619 pub fn lke_set(scope: &dyn LkEnv) {
1620 let mut ud = crate::work_env::LkEnvData::default();
1621 scope.userdata(&mut ud);
1622 if let Some(g) = ud.group {
1623 let _ = tl::set_group(Some(*g), true);
1624 }
1625 if let Some(d) = ud.domain {
1626 let _ = tl::set_domain(Some(*d), true);
1627 }
1628 if let Some(p) = ud.pubkey {
1629 let _ = tl::set_pubkey(Some(*p), true);
1630 }
1631 if let Some(p) = ud.key {
1632 let _ = tl::set_key(Some(p.clone()), true);
1633 }
1634 if let Some(d) = ud.point {
1635 ENV_POINT.set(Some(Either::Left(d.as_arc())));
1636 }
1637 if let Some(rt) = ud.lks {
1638 unsafe { set_system(rt.clone().into()) };
1639 }
1640 if let Some(d) = ud.argv {
1641 let argv_bytes = d.concat().into_boxed_slice();
1642 let mut argv: [&[u8]; 10] = [&[]; 10];
1643 let mut slice: &[u8] = &argv_bytes;
1644 for (bytes, dest) in d.iter().zip(argv.iter_mut()) {
1645 let b: &[u8] = &slice[..bytes.len()];
1646 *dest = unsafe { std::slice::from_ptr_range(b.as_ptr_range()) };
1647 slice = &slice[bytes.len()..]
1648 }
1649 ENV_ARGV.set(Some((argv, Some(argv_bytes))));
1650 }
1651 }
1652 pub fn lke_sync() -> LkEnvDataSync {
1654 lke_with(|e| LkEnvDataSync {
1655 point: e.point.map(|p| p.as_arc()),
1656 argv: e
1657 .argv
1658 .map(|o| Arc::new(o.iter().map(|o| o.to_vec()).collect())),
1659 domain: e.domain.copied(),
1660 group: e.group.copied(),
1661 pubkey: e.pubkey.copied(),
1662 key: e.key.cloned(),
1663 lksi: e.lks.map(|i| lks_info(Some(i)).unwrap()),
1664 })
1665 }
1666
1667 #[thread_local]
1668 pub(crate) static ENV_POINT: Cell<Option<Either<PointArc, NonNull<dyn Point>>>> =
1669 Cell::new(None);
1670
1671 #[thread_local]
1672 pub(crate) static ENV_ARGV: Cell<Option<([&'static [u8]; 10], Option<Box<[u8]>>)>> =
1673 Cell::new(None);
1674
1675 pub fn lke_point_set(point: &dyn Point) {
1676 ENV_POINT.set(Some(Either::Left(point.as_arc())));
1677 }
1678 pub fn lke_point_do<A>(point: &dyn Point, fnc: impl FnOnce() -> A) -> A {
1679 let prev = ENV_POINT
1680 .replace(NonNull::new(point as *const dyn Point as *mut dyn Point).map(Either::Right));
1681 let r = fnc();
1682 ENV_POINT.set(prev);
1683 r
1684 }
1685 pub fn lke_point() -> Option<PointArc> {
1686 match ENV_POINT.take() {
1687 None => None,
1688 Some(Either::Left(l)) => {
1689 ENV_POINT.set(Some(Either::Left(l.clone())));
1690 Some(l)
1691 }
1692 Some(Either::Right(r)) => {
1693 let arc = unsafe { r.as_ref().as_arc() };
1694 ENV_POINT.set(Some(Either::Left(arc.clone())));
1695 Some(arc)
1696 }
1697 }
1698 }
1699
1700 pub fn lke_do<A>(scope: &dyn LkEnv, fnc: impl FnOnce() -> A) -> A {
1704 let mut ud = crate::work_env::LkEnvData::default();
1705 scope.userdata(&mut ud);
1706
1707 let prev_group = if let Some(g) = ud.group {
1708 tl::set_group(Some(*g), true).unwrap()
1709 } else {
1710 None
1711 };
1712 let prev_domain = if let Some(d) = ud.domain {
1713 tl::set_domain(Some(*d), true).unwrap()
1714 } else {
1715 None
1716 };
1717 let prev_pubkey = if let Some(k) = ud.pubkey {
1718 tl::set_pubkey(Some(*k), true).unwrap()
1719 } else {
1720 None
1721 };
1722 let prev_key = if let Some(k) = ud.key {
1723 tl::set_key(Some(k.clone()), true).unwrap()
1724 } else {
1725 None
1726 };
1727 let prev_argv = if let Some(slices) = ud.argv {
1728 let mut argv = [&[] as &[u8]; 10];
1729 for (bytes, dest) in slices.iter().zip(argv.iter_mut()) {
1730 *dest = unsafe { std::slice::from_ptr_range(bytes.as_ptr_range()) };
1731 }
1732 ENV_ARGV.replace(Some((argv, None)))
1733 } else {
1734 None
1735 };
1736
1737 let prev_point = if let Some(point) = ud.point {
1738 ENV_POINT.replace(
1739 NonNull::new(point as *const dyn Point as *mut dyn Point).map(Either::Right),
1740 )
1741 } else {
1742 None
1743 };
1744 let result = if let Some(lks) = ud.lks {
1745 crate::system::lks_chsys(lks, fnc)
1746 } else {
1747 fnc()
1748 };
1749 if prev_group.is_some() {
1750 let _ = tl::set_group(prev_group, true);
1751 }
1752 if prev_domain.is_some() {
1753 let _ = tl::set_domain(prev_domain, true);
1754 }
1755 if prev_pubkey.is_some() {
1756 let _ = tl::set_pubkey(prev_pubkey, true);
1757 }
1758 if prev_key.is_some() {
1759 let _ = tl::set_key(prev_key, true);
1760 }
1761 if prev_point.is_some() {
1762 ENV_POINT.set(prev_point);
1763 }
1764 if prev_argv.is_some() {
1765 ENV_ARGV.set(prev_argv);
1766 }
1767 result
1768 }
1769
1770 impl Default for LkaScope<'_> {
1771 fn default() -> Self {
1772 LkEnvData::default().into()
1773 }
1774 }
1775 impl fmt::Debug for LkaScope<'_> {
1776 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1777 f.debug_tuple("LkScope").field(&"_").finish()
1778 }
1779 }
1780
1781 #[derive(Clone)]
1782 pub struct LkaScope<'o>(pub(crate) InlineScope<'o>);
1784 #[derive(Clone)]
1785 pub(crate) enum InlineScope<'o> {
1786 Std(StdScope<'o>),
1787 Core,
1788 Dyn(&'o dyn Scope),
1789 }
1790 type StdScope<'o> = ScopeSet<LkEnvData<'o>>;
1791
1792 impl<'o> AsScopeSet for LkEnvData<'o> {
1793 type Scope = (
1794 (Option<ScopeSet<PointScope<'o>>>, Option<AsScope<ArgV<'o>>>),
1795 (
1796 Option<AsScope<ScopeLocalLkEnv<'o>>>,
1797 CoreScope,
1798 AsScope<AdhocKey>,
1799 ),
1800 ScopeSet<RtScopeSet<SystemOrThreadLocal<'o>>>,
1801 );
1802 fn as_scope_set(&self) -> Self::Scope {
1803 let mut workenv = None;
1804 if self.domain.is_some()
1805 || self.group.is_some()
1806 || self.pubkey.is_some()
1807 || self.key.is_some()
1808 {
1809 workenv = Some(AsScope(ScopeLocalLkEnv {
1810 domain: self.domain,
1811 group: self.group,
1812 pubkey: self.pubkey,
1813 key: self.key,
1814 }));
1815 }
1816 let point_scope = self
1817 .point
1818 .or_else(|| match unsafe { &*crate::work_env::ENV_POINT.as_ptr() } {
1819 None => None,
1820 Some(Either::Left(l)) => Some(l),
1821 Some(Either::Right(l)) => Some(unsafe { l.as_ref() }),
1822 })
1823 .map(linkspace_core::point::point_scope);
1824 let argv_scope = self
1825 .argv
1826 .or_else(|| match unsafe { &*crate::work_env::ENV_ARGV.as_ptr() } {
1827 None => None,
1828 Some((bytes, _)) => Some(bytes),
1829 })
1830 .map(|bytes| AsScope(ArgV(bytes)));
1831 let lks = ScopeSet(RtScopeSet(SystemOrThreadLocal(self.lks.map(|o| &o.0))));
1832 (
1833 (point_scope, argv_scope),
1834 (workenv, CORE_SCOPE, AsScope(AdhocKey)),
1835 lks,
1836 )
1837 }
1838 }
1839
1840 impl<'o> LkaScope<'o> {
1843 pub fn core() -> LkaScope<'static> {
1844 LkaScope(InlineScope::Core)
1845 }
1846 pub(crate) fn as_dyn(&self) -> &(dyn Scope + 'o) {
1847 match &self.0 {
1848 InlineScope::Std(scope) => scope,
1849 InlineScope::Dyn(scope) => scope,
1850 InlineScope::Core => &CORE_SCOPE,
1851 }
1852 }
1853 #[doc(hidden)]
1854 pub fn from_dyn(sdyn: &'o dyn Scope) -> Self {
1855 LkaScope(InlineScope::Dyn(sdyn))
1856 }
1857 }
1858 use linkspace_system::thread_local::{SystemOrThreadLocal, set_system};
1859}