duat_core/lib.rs
1//! The core of Duat, for use by Duat's built-in plugins.
2//!
3//! This crate isn't really meant for public use, since it is used
4//! only by a select few plugins. Configuration crates and plugins
5//! should make use of the [duat] crate.
6//!
7//! One thing to note about this "builti-in plugins" thing though, is
8//! that the api of `duat` is a superset of `duat-core`'s api, the
9//! only reason why this distinction exists is so I can include some
10//! other plugins in `duat`'s api, like `duat-base`,
11//! `duat-treesitter`, and `duat-lsp`.
12//!
13//! [duat]: https://crates.io/duat
14#![warn(rustdoc::unescaped_backticks)]
15#![allow(clippy::single_range_in_vec_init)]
16
17// This is because of the weird Strs pointer trickery that I'm doing,
18// usize _must_ be u64
19#[cfg(target_pointer_width = "16")]
20compile_error!("This crate does not support 16-bit systems.");
21#[cfg(target_pointer_width = "32")]
22compile_error!("This crate does not support 32-bit systems.");
23
24#[allow(unused_imports)]
25use dirs_next::cache_dir;
26
27pub use self::{namespace::Ns, ranges::Ranges};
28
29pub mod buffer;
30pub mod cmd;
31pub mod context;
32pub mod data;
33pub mod form;
34pub mod hook;
35pub mod mode;
36pub mod opts;
37mod ranges;
38#[doc(hidden)]
39pub mod session;
40pub mod text;
41pub mod ui;
42pub mod utils;
43
44mod namespace {
45 //! A namespace for Duat operations.
46
47 use std::{
48 ops::Range,
49 sync::{
50 LazyLock,
51 atomic::{AtomicU32, Ordering::Relaxed},
52 },
53 };
54
55 static NAMESPACE_COUNT: AtomicU32 = AtomicU32::new(3);
56
57 /// A namespace for Duat operations.
58 ///
59 /// This is a unique identifier which makes sure you're only
60 /// affecting the parts of Duat that you want to affect. It is
61 /// used in various places within duat:
62 ///
63 /// - For adding [`Tag`]s to [`Text`].
64 /// - For [adding] and [removing] [hooks].
65 /// - For dealing with the [`Gutter`]
66 ///
67 /// Here's an example of a namespace being used to add `Tag`s to
68 /// `Text`:
69 ///
70 /// ```rust
71 /// # duat_core::doc_duat!(duat);
72 /// # use duat::prelude::*;
73 /// let mut text = txt!("This is text with no tags in it");
74 /// // This key will be used to modify text.
75 /// let ns1 = Ns::new();
76 ///
77 /// let id = form::id_of!("invisible");
78 ///
79 /// // You can create an `impl Tag` directly from a `FormId`
80 /// text.insert_tag(ns1, 18..20, id.to_tag(0));
81 ///
82 /// assert_eq!(text, txt!("This is text with [invisible]no[] tags in it"));
83 ///
84 /// // ns2 != ns1, so it shouldn't be able to change what was done with ns1.
85 /// let ns2 = Ns::new();
86 /// text.remove_tags(ns2, 18);
87 ///
88 /// assert_eq!(text, txt!("This is text with [invisible]no[] tags in it"));
89 /// ```
90 ///
91 /// [`Tag`]: crate::text::Tag
92 /// [`Text`]: crate::text::Text
93 /// [adding]: crate::hook::add
94 /// [removing]: crate::hook::remove
95 /// [hooks]: crate::hook
96 /// [`Gutter`]: ../duat/widgets/struct.Gutter.html
97 #[derive(
98 Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, bincode::Decode, bincode::Encode,
99 )]
100 pub struct Ns(u32);
101
102 impl Ns {
103 /// Returns a new, unique namespace.
104 pub fn new() -> Self {
105 Self(NAMESPACE_COUNT.fetch_add(1, Relaxed))
106 }
107
108 /// Returns a new [`LazyLock<Ns>`].
109 ///
110 /// You can use this in order to create `static` namespaces by
111 /// calling something like:
112 ///
113 /// ```rust
114 /// # duat_core::doc_duat!(duat);
115 /// use std::sync::LazyLock;
116 ///
117 /// use duat::prelude::*;
118 ///
119 /// static NS: LazyLock<Ns> = Ns::new_lazy();
120 /// ```
121 pub const fn new_lazy() -> LazyLock<Self> {
122 LazyLock::new(Self::new)
123 }
124
125 /// Returns a number of new, unique namespaces
126 ///
127 /// You may want to do this if you expect to be placing and
128 /// removing a lot of tags, and you want the finest possible
129 /// control over what gets added and deleted from the
130 /// [`Text`].
131 ///
132 /// [`Text`]: crate::text::Text
133 pub fn new_many(amount: u32) -> Range<Self> {
134 let start = NAMESPACE_COUNT.fetch_add(amount + 1, Relaxed);
135 Self(start)..Self(start + amount)
136 }
137
138 /// A simple key with no uniqueness guarantee
139 ///
140 /// You should use this if you're editing widgets that are not
141 /// the [`Buffer`] widget, since you're probably the
142 /// only one that is going to be modifying said widget
143 /// anyway.
144 ///
145 /// The advantage of this function is speed. Since it is a
146 /// `const` function, it's value is just substituted in with
147 /// the code, so there is no need to store it in
148 /// structs or statics.
149 ///
150 /// [`Buffer`]: crate::buffer::Buffer
151 pub const fn basic() -> Self {
152 Self(0)
153 }
154
155 /// A [`Tagger`] specifically for remaps
156 pub(crate) const fn for_alias() -> Self {
157 Self(1)
158 }
159
160 pub(crate) const fn for_toggle() -> Self {
161 Self(2)
162 }
163 }
164
165 impl Default for Ns {
166 /// Returns a _new_ namespace.
167 ///
168 /// Not to be confused with [`Ns::basic`].
169 fn default() -> Self {
170 Self::new()
171 }
172 }
173
174 impl std::fmt::Debug for Ns {
175 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176 write!(f, "Ns({})", self.0)
177 }
178 }
179}
180
181pub mod clipboard {
182 //! Clipboard interaction for Duat.
183 //!
184 //! Just a regular clipboard, no image functionality.
185 use std::sync::Mutex;
186
187 use crate::session::{self, ipc::MsgFromChild};
188
189 static CLIPBOARD: Mutex<Option<String>> = Mutex::new(None);
190
191 /// Gets a [`String`] from the clipboard.
192 ///
193 /// This can fail if the clipboard does not contain UTF-8 encoded
194 /// text.
195 pub fn get() -> Option<String> {
196 let content = if cfg!(target_os = "android") {
197 None
198 } else {
199 session::ipc::send(MsgFromChild::RequestClipboard);
200 session::ipc::recv_clipboard()
201 };
202
203 let mut clipboard = CLIPBOARD.lock().unwrap();
204
205 if let Some(content) = content {
206 *clipboard = Some(content);
207 }
208
209 clipboard.clone()
210 }
211
212 /// Sets the content of the clipboard.
213 pub fn set(content: impl std::fmt::Display) {
214 let content = content.to_string();
215 *CLIPBOARD.lock().unwrap() = Some(content.clone());
216
217 #[cfg(not(target_os = "android"))]
218 {
219 session::ipc::send(MsgFromChild::UpdateClipboard(content));
220 }
221 }
222
223 /// Sets the content of the clipboard without changing the system
224 /// clipboard.
225 pub fn set_local(content: impl std::fmt::Display) {
226 let content = content.to_string();
227 *CLIPBOARD.lock().unwrap() = Some(content.clone());
228 }
229}
230
231pub mod notify {
232 //! File watching utility for Duat.
233 //!
234 //! Provides a simplified interface over the [`notify`] crate.
235 //!
236 //! [`notify`]: https://crates.io/crates/notify
237 use std::{
238 collections::HashMap,
239 path::{Path, PathBuf},
240 sync::{LazyLock, Mutex},
241 time::{Duration, Instant},
242 };
243
244 pub use notify::event;
245 use notify::{
246 Config, RecommendedWatcher, RecursiveMode, Watcher as NWatcher,
247 event::{AccessKind, AccessMode, Event, EventKind},
248 };
249
250 static DUAT_WRITES: LazyLock<Mutex<HashMap<PathBuf, DuatWrite>>> =
251 LazyLock::new(Mutex::default);
252
253 /// A record of write events that came from Duat.
254 #[derive(Default)]
255 struct DuatWrite {
256 count: usize,
257 last: Option<Instant>,
258 }
259
260 /// Wether an event came from Duat or not.
261 ///
262 /// This is only ever [`FromDuat::Yes`] if the event is a write
263 /// event resulting from [`Handle::<Buffer>::save`].
264 ///
265 /// This can be useful if you want to sort events based on
266 /// external or internal factors. For example, duat makes use of
267 /// this in order to calculate file diffs only if the file was
268 /// modified from outside of duat.
269 ///
270 /// [`Handle::<Buffer>::save`]: crate::context::Handle::save
271 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
272 pub enum FromDuat {
273 /// The event came from Duat, more specifically, it cam from a
274 /// function like [`Handle::<Buffer>::save`].
275 ///
276 /// Another thing to note is that this will only be this value
277 /// if _all_ the paths were written by Duat. If this is not
278 /// the case, then it will be [`FromDuat::No`]. This usually
279 /// isn't an issue, since the vast majority of events emmit
280 /// only one path, but it is something to keep in mind.
281 ///
282 /// [`Handle::<Buffer>::save`]: crate::context::Handle::save
283 Yes,
284 /// The event didn't come from Duat.
285 ///
286 /// Note that, even if the event actually came from Duat,
287 /// unless it is a write event, it will always be set to this.
288 No,
289 }
290
291 /// A [`Path`] watcher.
292 ///
293 /// If this struct is [`drop`]ped, the `Path`s it was watching
294 /// will no longer be watched by it.
295 pub struct Watcher(Mutex<RecommendedWatcher>);
296
297 impl Watcher {
298 /// Spawn a new `Watcher`, with a callback function
299 ///
300 /// You can add paths to watch through [`Watcher::watch`] and
301 /// [`Watcher::watch_recursive`].
302 pub fn new(
303 mut callback: impl FnMut(notify::Result<Event>, FromDuat) + Send + 'static,
304 ) -> notify::Result<Self> {
305 Ok(Self(Mutex::new(RecommendedWatcher::new(
306 move |event| {
307 use FromDuat::*;
308 let from_duat = if let Ok(Event {
309 kind: EventKind::Access(AccessKind::Close(AccessMode::Write)),
310 paths,
311 ..
312 }) = &event
313 && !paths.is_empty()
314 {
315 let now = Instant::now();
316 let mut duat_writes = DUAT_WRITES.lock().unwrap();
317 let mut all_are_from_duat = true;
318
319 for path in paths {
320 if let Some(dw) = duat_writes.get_mut(path)
321 && (dw.count > 0
322 || dw.last.is_some_and(|instant| {
323 now.duration_since(instant) < Duration::from_millis(2)
324 }))
325 {
326 dw.count = dw.count.saturating_sub(1);
327 dw.last = Some(now);
328 } else {
329 all_are_from_duat = false;
330 }
331 }
332
333 if all_are_from_duat { Yes } else { No }
334 } else {
335 No
336 };
337
338 callback(event, from_duat);
339 },
340 Config::default(),
341 )?)))
342 }
343
344 /// Watch a [`Path`] non-recursively.
345 pub fn watch(&self, path: &Path) -> notify::Result<()> {
346 self.0
347 .lock()
348 .unwrap()
349 .watch(path, RecursiveMode::NonRecursive)
350 }
351
352 /// Watch a [`Path`] recursively.
353 pub fn watch_recursive(&self, path: &Path) -> notify::Result<()> {
354 self.0.lock().unwrap().watch(path, RecursiveMode::Recursive)
355 }
356
357 /// Stop watching a [`Path`].
358 pub fn unwatch(&self, path: &Path) -> notify::Result<()> {
359 self.0.lock().unwrap().unwatch(path)
360 }
361 }
362
363 impl Drop for Watcher {
364 fn drop(&mut self) {}
365 }
366
367 /// A callback for Watcher events.
368 ///
369 /// **FOR USE BY THE DUAT EXECUTABLE ONLY**
370 #[doc(hidden)]
371 pub struct WatcherCallback {
372 callback: Box<dyn FnMut(std::io::Result<Event>) + Send + 'static>,
373 // This is required, so the WATCHER_COUNT is from the loaded config, not the duat
374 // executable.
375 drop: fn(),
376 }
377
378 impl WatcherCallback {
379 /// Calls the callback.
380 pub fn call(&mut self, event: std::io::Result<Event>) {
381 (self.callback)(event)
382 }
383 }
384
385 impl Drop for WatcherCallback {
386 fn drop(&mut self) {
387 (self.drop)();
388 }
389 }
390
391 /// Declares that the next write event actually came from Duat,
392 /// for a given path.
393 pub(crate) fn set_next_write_as_from_duat(path: PathBuf) {
394 DUAT_WRITES.lock().unwrap().entry(path).or_default().count += 1;
395 }
396
397 /// Declares that the next write event didn't come from Duat,
398 /// for a given path.
399 pub(crate) fn unset_next_write_as_from_duat(path: PathBuf) {
400 let mut duat_writes = DUAT_WRITES.lock().unwrap();
401 let dw = duat_writes.entry(path).or_default();
402 dw.count = dw.count.saturating_sub(1);
403 }
404}
405
406pub mod process {
407 //! Utilities for spawning processes that should outlive the
408 //! config.
409 use std::{
410 ffi::{OsStr, OsString},
411 io::{BufRead, BufWriter, Read, Write},
412 process::{Child, Command, Stdio},
413 sync::{
414 Mutex,
415 atomic::{AtomicUsize, Ordering},
416 mpsc,
417 },
418 time::Duration,
419 };
420
421 use bincode::{
422 Decode, Encode, config,
423 error::{DecodeError, EncodeError},
424 };
425 use interprocess::local_socket::prelude::*;
426 pub use interrupt_read::InterruptReader;
427
428 use crate::session::{
429 self,
430 ipc::{MsgFromChild, ProcessReader},
431 };
432
433 /// Spawn a new `PersistentChild`, which can be reused in
434 /// future config reloads.
435 ///
436 /// The command will forcibly make use of [`Stdio::piped`] for all
437 /// stdio. This is because stdin, stdout and stderr are
438 /// reserved for use by the [`Ui`] implementation, so something
439 /// like [`Stdio::inherit`] would interfere with that.
440 ///
441 /// [`Ui`]: crate::ui::Ui
442 pub fn spawn_persistent(command: &mut Command) -> std::io::Result<PersistentChild> {
443 let encode = |value: &OsStr| value.as_encoded_bytes().to_vec();
444
445 let args = command.get_args().map(encode).collect();
446 let envs = command
447 .get_envs()
448 .map(|(k, v)| (encode(k), v.map(encode)))
449 .collect();
450
451 session::ipc::send(MsgFromChild::SpawnProcess(PersistentSpawnRequest {
452 program: encode(command.get_program()),
453 args,
454 envs,
455 }));
456
457 match session::ipc::recv_spawn() {
458 Ok(id) => Ok(PersistentChild::new(id, Vec::new(), Vec::new())),
459 Err(err) => Err(std::io::Error::from_raw_os_error(err)),
460 }
461 }
462 /// A child process that will persist over multiple reload cycles.
463 ///
464 /// This child makes use of [`interprocess`]'s [local sockets] for
465 /// communication. This is because the process will be spawned by
466 /// the duat executor, and communication between it and the config
467 /// child process won't be possible by regular stdio.
468 ///
469 /// Unless you call [`PersistentChild::kill`], duat will assume
470 /// that you want it to be kept alive for future reloads.
471 ///
472 /// # Later retrieval
473 ///
474 /// If you want to retrieve this `PersistentChild` on a future
475 /// reload cycle. You will need to store it by calling
476 /// [`storage::store`], from duat's [`storage`] module. You can
477 /// only do this once, trying it again will (logically) cause a
478 /// panic.
479 ///
480 /// Since this struct implements [`Decode`] and
481 /// [`Encode`], it can be stored and retrieved, even if
482 /// it is part of another struct.
483 ///
484 /// If you don't call `storage::store`, it is assumed that you no
485 /// longer need the process, and it will be killed.
486 ///
487 /// # Unread bytes
488 ///
489 /// When reloading Duat, the stdin, stdout and stderr processes
490 /// are guaranteed to not lose any bytes.
491 ///
492 /// This is only the case, however, if you don't have some sort of
493 /// buffering and/or aren't doing a deserialization attempt with
494 /// said data.
495 ///
496 /// If you want to do deserialization (via [`Decode`]),
497 /// you will want to use [`PersistentReader::decode_bytes_as`].
498 /// This method will fail to decode if a reload is requested
499 /// midway through reading, but the bytes will be saved for
500 /// the next reload cycle, where you can start decoding again.
501 ///
502 /// [local sockets]: interprocess::local_socket::Stream
503 /// [`storage::store`]: crate::storage::store
504 /// [`storage`]: crate::storage
505 pub struct PersistentChild {
506 /// The standard input of the [`Child`].
507 ///
508 /// This struct will send the bytes to an ipc enabled
509 /// [`LocalSocketStream`], which will in turn be sent to the
510 /// process indirectly, since said process is owned by the
511 /// parent executor, not the child.
512 pub stdin: Option<PersistentWriter>,
513 stdout: Mutex<Option<PersistentReader>>,
514 stderr: Mutex<Option<PersistentReader>>,
515 stdout_rx: mpsc::Receiver<ReaderPair>,
516 stderr_rx: mpsc::Receiver<ReaderPair>,
517 id: usize,
518 }
519
520 impl PersistentChild {
521 fn new(id: usize, stdout_bytes: Vec<u8>, stderr_bytes: Vec<u8>) -> Self {
522 let (stdin, [stdout, stderr]) =
523 session::ipc::connect_process_channel(id, stdout_bytes, stderr_bytes).unwrap();
524 let (stdout_tx, stdout_rx) = mpsc::channel();
525 let (stderr_tx, stderr_rx) = mpsc::channel();
526
527 Self {
528 stdin: Some(PersistentWriter(RawPersistentWriter(BufWriter::new(stdin)))),
529 stdout: Mutex::new(Some(PersistentReader {
530 pair: Some(ReaderPair { decode_bytes: Vec::new(), conn: stdout }),
531 pair_tx: stdout_tx,
532 })),
533 stderr: Mutex::new(Some(PersistentReader {
534 pair: Some(ReaderPair { decode_bytes: Vec::new(), conn: stderr }),
535 pair_tx: stderr_tx,
536 })),
537 stdout_rx,
538 stderr_rx,
539 id,
540 }
541 }
542
543 /// The standard output of the [`Child`].
544 ///
545 /// This reader is already buffered, so you don't need to wrap
546 /// it in a [`BufReader`] to use it efficiently.
547 ///
548 /// In order to use this correctly, you must follow one of
549 /// three scenarios:
550 ///
551 /// - A reading loop that never stops. This is the most common
552 /// usecase.
553 /// - If you are going to stop, make sure that the reader is
554 /// dropped or that you have called
555 /// [`PersistentReader::give_back`]. This is to ensure that
556 /// any unread bytes are stored correctly when reloading
557 /// Duat.
558 /// - If the child has been [killed], then you don't need to
559 /// do anything in particular.
560 ///
561 /// For decoding [`bincode::Decode`] types, you should make
562 /// use of the [`PersistentReader::decode_bytes_as`] function,
563 /// since that one is not prone to losses if it is interrupted
564 /// by a reload.
565 ///
566 /// [killed]: PersistentChild::kill
567 /// [`BufReader`]: std::io::BufReader
568 pub fn get_stdout(&self) -> Option<PersistentReader> {
569 self.stdout.lock().unwrap().take()
570 }
571
572 /// The standard error of the [`Child`].
573 ///
574 /// This reader is already buffered, so you don't need to wrap
575 /// it in a [`BufReader`] to use it efficiently.
576 ///
577 /// In order to use this correctly, you must follow one of
578 /// three scenarios:
579 ///
580 /// - A reading loop that never stops. This is the most common
581 /// usecase.
582 /// - If you are going to stop, make sure that the reader is
583 /// dropped or that you have called
584 /// [`PersistentReader::give_back`]. This is to ensure that
585 /// any unread bytes are stored correctly when reloading
586 /// Duat.
587 /// - If the child has been [killed], then you don't need to
588 /// do anything in particular.
589 ///
590 /// [killed]: PersistentChild::kill
591 /// [`BufReader`]: std::io::BufReader
592 pub fn get_stderr(&self) -> Option<PersistentReader> {
593 self.stderr.lock().unwrap().take()
594 }
595
596 /// Kill the [`Child`] process.
597 pub fn kill(self) -> std::io::Result<()> {
598 session::ipc::send(MsgFromChild::KillProcess(self.id));
599 session::ipc::recv_kill().map_err(std::io::Error::from_raw_os_error)
600 }
601 }
602
603 impl<Context> Decode<Context> for PersistentChild {
604 fn decode<D: bincode::de::Decoder<Context = Context>>(
605 decoder: &mut D,
606 ) -> Result<Self, DecodeError> {
607 let stored = StoredPersistentChild::decode(decoder)?;
608 Ok(Self::new(
609 stored.id,
610 stored.stdout_bytes,
611 stored.stderr_bytes,
612 ))
613 }
614 }
615
616 impl Encode for PersistentChild {
617 /// Encodes the `PersistentChild`
618 ///
619 /// This can only be done once, trying to do it again will
620 /// result in a panic.
621 #[track_caller]
622 fn encode<E: bincode::enc::Encoder>(
623 &self,
624 encoder: &mut E,
625 ) -> Result<(), bincode::error::EncodeError> {
626 session::ipc::send(MsgFromChild::InterruptWrites(self.id));
627
628 let (stdout, stderr) = (
629 self.stdout.lock().unwrap().take(),
630 self.stderr.lock().unwrap().take(),
631 );
632
633 let (stdout_bytes, stderr_bytes) = match (stdout, stderr) {
634 (None, None) => {
635 let stdout_bytes = self.stdout_rx.recv().unwrap().consume();
636 let stderr_bytes = self.stderr_rx.recv().unwrap().consume();
637 (stdout_bytes, stderr_bytes)
638 }
639 (None, Some(mut stderr)) => {
640 let stdout_bytes = self.stdout_rx.recv().unwrap().consume();
641 let stderr_bytes = stderr.pair.take().unwrap().consume();
642 (stdout_bytes, stderr_bytes)
643 }
644 (Some(mut stdout), None) => {
645 let stdout_bytes = stdout.pair.take().unwrap().consume();
646 let stderr_bytes = self.stderr_rx.recv().unwrap().consume();
647 (stderr_bytes, stdout_bytes)
648 }
649 (Some(mut stdout), Some(mut stderr)) => {
650 let stdout_bytes = stdout.pair.take().unwrap().consume();
651 let stderr_bytes = stderr.pair.take().unwrap().consume();
652 (stderr_bytes, stdout_bytes)
653 }
654 };
655
656 StoredPersistentChild { id: self.id, stdout_bytes, stderr_bytes }.encode(encoder)
657 }
658 }
659
660 impl Drop for PersistentChild {
661 fn drop(&mut self) {}
662 }
663
664 /// A pair used for reading and decoding.
665 struct ReaderPair {
666 decode_bytes: Vec<u8>,
667 conn: ProcessReader,
668 }
669
670 impl ReaderPair {
671 /// Consumes the reader, returning all unread bytes.
672 fn consume(mut self) -> Vec<u8> {
673 _ = self.conn.read_to_end(&mut self.decode_bytes);
674 self.decode_bytes
675 }
676 }
677
678 /// A [`Read`]er which is meant to be used across multiple reload
679 /// cycles.
680 ///
681 /// This reader is _already buffered_, don't wrap it in a
682 /// [`BufReader`], or else you _will lose bytes on reloads_.
683 ///
684 /// [`BufReader`]: std::io::BufReader
685 pub struct PersistentReader {
686 pair: Option<ReaderPair>,
687 pair_tx: mpsc::Sender<ReaderPair>,
688 }
689
690 impl PersistentReader {
691 /// Attempts to decode the bytes as a type.
692 pub fn decode_bytes_as<D: Decode<()>>(&mut self) -> Result<D, DecodeError> {
693 struct RepeatReader<'p>(&'p mut PersistentReader);
694
695 impl<'p> Read for RepeatReader<'p> {
696 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
697 let reader = &mut self.0;
698 let pair = reader.pair.as_mut().unwrap();
699
700 match pair.conn.read(buf) {
701 Ok(0) => {
702 _ = reader.pair_tx.send(reader.pair.take().unwrap());
703 // We loop forever, to abstract away the reloading of Duat in reading
704 // loops.
705 loop {
706 std::thread::park();
707 }
708 }
709 Ok(num_bytes) => {
710 pair.decode_bytes.extend_from_slice(&buf[..num_bytes]);
711 Ok(num_bytes)
712 }
713 Err(err) => Err(err),
714 }
715 }
716 }
717
718 let value = bincode::decode_from_std_read(&mut RepeatReader(self), config::standard())?;
719 self.pair.as_mut().unwrap().decode_bytes.clear();
720
721 Ok(value)
722 }
723
724 /// Returns this [`Read`]er (and its bytes) to be retrieved
725 /// later on.
726 ///
727 /// Note that if the [`PersistentChild`] was already [killed],
728 /// this won't do anything.
729 ///
730 /// [killed]: PersistentChild::kill
731 pub fn give_back(self) {
732 drop(self)
733 }
734 }
735
736 impl Read for PersistentReader {
737 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
738 let pair = self.pair.as_mut().unwrap();
739 match pair.conn.read(buf) {
740 // This means that the equivalent Stream in the parent process was
741 // dropped, which means we're about to reload.
742 Ok(0) => {
743 _ = self.pair_tx.send(self.pair.take().unwrap());
744 // We loop forever, to abstract away the reloading of Duat in reading
745 // loops.
746 loop {
747 std::thread::park();
748 }
749 }
750 Ok(bytes) => Ok(bytes),
751 Err(err) => Err(err),
752 }
753 }
754 }
755
756 impl BufRead for PersistentReader {
757 fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
758 let pair = self.pair.as_mut().unwrap();
759 match pair.conn.fill_buf() {
760 Ok([]) => {
761 _ = self.pair_tx.send(self.pair.take().unwrap());
762 // We loop forever, to abstract away the reloading of Duat in reading
763 // loops.
764 loop {
765 std::thread::park();
766 }
767 }
768 Ok(_) => {
769 let pair = self.pair.as_ref().unwrap();
770 Ok(pair.conn.buffer())
771 }
772 Err(err) => Err(err),
773 }
774 }
775
776 fn consume(&mut self, amount: usize) {
777 let pair = self.pair.as_mut().unwrap();
778 pair.conn.consume(amount);
779 }
780 }
781
782 impl Drop for PersistentReader {
783 fn drop(&mut self) {
784 if let Some(pair) = self.pair.take() {
785 // The entry may have already been removed.
786 _ = self.pair_tx.send(pair);
787 }
788 }
789 }
790
791 /// A [`Write`]r "lock" for a [`PersistentChild`].
792 ///
793 /// This struct will send bytes to the `stdin` of a
794 /// `PersistentChild`. This is done indirectly through a
795 /// [`LocalSocketStream`], since the child process doesn't have
796 /// direct access to the stdin of the child, as it belongs to the
797 /// parent process.
798 ///
799 /// This writer is more of a "writer lock" over the actual inner
800 /// writer. This is because of reloads.
801 ///
802 /// When duat reloads, the child process is finished. This could
803 /// prematurely end `write` calls of separate threads, leading to
804 /// the loss, duplication, or corruption of data.
805 ///
806 /// That's why this struct has the [`PersistentWriter::on_writer`]
807 /// method. This method will give you mutable access to the writer
808 /// while preventing duat from reloading.
809 ///
810 /// You should make use of it in order to "confirm" that a value
811 /// has actually been written. Any confirmation outside of this
812 /// method can't be trusted.
813 pub struct PersistentWriter(RawPersistentWriter);
814
815 impl PersistentWriter {
816 /// Calls a function on the inner [`Write`]r.
817 ///
818 /// This will also prevent Duat from reloading, allowing for
819 /// lossless and duplicationless data sending.
820 #[track_caller]
821 pub fn on_writer<R>(
822 &mut self,
823 f: impl FnOnce(&mut RawPersistentWriter) -> std::io::Result<R>,
824 ) -> std::io::Result<R> {
825 use std::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
826
827 WRITERS_WRITING.fetch_add(1, Ordering::Acquire);
828 let result = catch_unwind(AssertUnwindSafe(move || f(&mut self.0)));
829 WRITERS_WRITING.fetch_sub(1, Ordering::Release);
830
831 match result {
832 Ok(result) => result,
833 Err(panic) => resume_unwind(panic),
834 }
835 }
836 }
837
838 /// The writer from [`PersistentWriter::on_writer`].
839 ///
840 /// This struct is basically just a wrapper over a
841 /// [`BufWriter<LocalSocketStream>`], but it also comes with the
842 /// [`encode_as_bytes`] method, which lets you encode a value as
843 /// bytes in a more convenient, less prone to error way, than
844 /// using [`bincode`] by itself.
845 ///
846 /// [`encode_as_bytes`]: RawPersistentWriter::encode_as_bytes
847 pub struct RawPersistentWriter(BufWriter<LocalSocketStream>);
848
849 impl RawPersistentWriter {
850 /// Encode the value as bytes, in a duat compatible way.
851 ///
852 /// Note that you have to call [`Write::flush`] in order to
853 /// actually send the data.
854 pub fn encode_as_bytes(&mut self, value: impl Encode) -> Result<usize, EncodeError> {
855 bincode::encode_into_std_write(value, &mut self.0, config::standard())
856 }
857 }
858
859 impl Write for RawPersistentWriter {
860 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
861 self.0.write(buf)
862 }
863
864 fn flush(&mut self) -> std::io::Result<()> {
865 self.0.flush()
866 }
867 }
868
869 /// A request to spawn a new [`PersistentChild`] process.
870 #[doc(hidden)]
871 #[derive(Decode, Encode)]
872 pub struct PersistentSpawnRequest {
873 program: Vec<u8>,
874 args: Vec<Vec<u8>>,
875 envs: Vec<(Vec<u8>, Option<Vec<u8>>)>,
876 }
877
878 impl PersistentSpawnRequest {
879 /// Spawn the [`Command`].
880 ///
881 /// Returns the id of this command, as well as the
882 /// [`Child`] that was spawned.
883 ///
884 /// This should only be done in the Duat executable.
885 // This will become `std::io::RawOsError` once that is stable.
886 pub fn spawn(self) -> Result<(String, Child), i32> {
887 let decode = |value: Vec<u8>| unsafe { OsString::from_encoded_bytes_unchecked(value) };
888
889 let caller = decode(self.program.clone()).to_string_lossy().to_string();
890
891 let child = Command::new(decode(self.program))
892 .args(self.args.into_iter().map(decode))
893 .envs(
894 self.envs
895 .into_iter()
896 .filter_map(|(k, v)| Some((decode(k), decode(v?)))),
897 )
898 .stdin(Stdio::piped())
899 .stdout(Stdio::piped())
900 .stderr(Stdio::piped())
901 // TODO: Deal with pre_exec closures.
902 .spawn()
903 .map_err(|err| err.raw_os_error().unwrap())?;
904
905 Ok((caller, child))
906 }
907 }
908
909 impl std::fmt::Debug for PersistentSpawnRequest {
910 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
911 let decode = |value: Vec<u8>| unsafe { OsString::from_encoded_bytes_unchecked(value) };
912
913 f.debug_struct("PersistentSpawnRequest")
914 .field("program", &decode(self.program.clone()))
915 .field(
916 "args",
917 &self.args.iter().cloned().map(decode).collect::<Vec<_>>(),
918 )
919 .field(
920 "envs",
921 &self
922 .envs
923 .iter()
924 .cloned()
925 .map(|(k, v)| (decode(k), v.map(decode)))
926 .collect::<Vec<_>>(),
927 )
928 .finish()
929 }
930 }
931
932 /// A stored [`PersistentChild`].
933 #[derive(Decode, Encode)]
934 struct StoredPersistentChild {
935 id: usize,
936 stdout_bytes: Vec<u8>,
937 stderr_bytes: Vec<u8>,
938 }
939
940 /// Wait for a [`PersistentChild`] writers to be done writing.
941 pub(crate) fn wait_for_writers() {
942 while WRITERS_WRITING.load(Ordering::Relaxed) > 0 {
943 std::thread::sleep(Duration::from_millis(1));
944 }
945 }
946
947 static WRITERS_WRITING: AtomicUsize = AtomicUsize::new(0);
948}
949
950pub mod storage {
951 //! Utilities for storing items inbetween reloads.
952 use std::{
953 collections::{HashMap, hash_map::Entry},
954 io::Cursor,
955 sync::Mutex,
956 };
957
958 pub use bincode;
959 use bincode::{BorrowDecode, Decode, Encode, config, error::EncodeError};
960
961 use crate::data::Pass;
962
963 static STORED: Mutex<Option<HashMap<String, MaybeTypedValues>>> = Mutex::new(None);
964
965 /// Store a value across reload cycles.
966 ///
967 /// You can use this function if you want to store a value through
968 /// reload cycles, retrieving it after Duat reloads.
969 ///
970 /// The [`Pass`] argument is used here to ensure that you're doing
971 /// this from the main thread, since storing from other threads
972 /// could result in the object not _actually_ being stored, if
973 /// this function is called in the very small time interval
974 /// inbetween duat taking the stored objects out and unloading the
975 /// config.
976 pub fn store<E>(_: &Pass, value: E) -> Result<(), EncodeError>
977 where
978 E: Encode + Send + 'static,
979 {
980 let key = std::any::type_name_of_val(&value).to_string();
981
982 let mut stored_guard = STORED.lock().unwrap_or_else(|err| err.into_inner());
983 let stored = stored_guard.as_mut().unwrap();
984
985 match stored.entry(key.clone()) {
986 Entry::Occupied(mut occupied_entry) => match occupied_entry.get_mut() {
987 MaybeTypedValues::NotTyped(list_of_bytes) => {
988 let mut encoded = std::mem::take(list_of_bytes);
989 bincode::encode_into_std_write(value, &mut encoded, config::standard())?;
990 occupied_entry.insert(MaybeTypedValues::typed(PartiallyDecodedValues::<E> {
991 encoded: Mutex::new(Cursor::new(encoded)),
992 decoded: Vec::new(),
993 }));
994 }
995 MaybeTypedValues::Typed(typed, _) => {
996 let values: &mut PartiallyDecodedValues<E> = typed.downcast_mut().unwrap();
997 bincode::encode_into_std_write(
998 value,
999 encoded(&values.encoded).get_mut(),
1000 config::standard(),
1001 )?;
1002 }
1003 },
1004 Entry::Vacant(vacant_entry) => {
1005 let mut encoded = Vec::new();
1006 bincode::encode_into_std_write(value, &mut encoded, config::standard())?;
1007 vacant_entry.insert(MaybeTypedValues::typed(PartiallyDecodedValues::<E> {
1008 encoded: Mutex::new(Cursor::new(encoded)),
1009 decoded: Vec::new(),
1010 }));
1011 }
1012 };
1013
1014 Ok(())
1015 }
1016
1017 /// Retrieve a value that might have been stored on a previous
1018 /// reload cycle.
1019 ///
1020 /// This will call the predicate on each stored value of type `D`
1021 /// (not including those stored as fields in structs), and will
1022 /// extract the value if the predicate returns `true`.
1023 ///
1024 /// If the layout of the type has changed inbetween reloads, all
1025 /// values of said type will be discarded.
1026 ///
1027 /// # Note
1028 ///
1029 /// This function relies on the name of the stored type. This is
1030 /// because more reliable indicators (like [`TypeId`]) can't be
1031 /// relied to be the same inbetween reloads, while a `&str` can.
1032 ///
1033 /// This does mean however, that changing the path of a type will
1034 /// immediately invalidade the stored values, so you should avoid
1035 /// doing that.
1036 ///
1037 /// [`TypeId`]: std::any::TypeId
1038 pub fn get_if<D>(mut pred: impl FnMut(&D) -> bool) -> Option<D>
1039 where
1040 D: Decode<()> + Encode + Send + 'static,
1041 {
1042 let key = std::any::type_name::<D>();
1043
1044 let mut stored_guard = STORED.lock().unwrap_or_else(|err| err.into_inner());
1045 let stored = stored_guard.as_mut().unwrap();
1046
1047 let entry = stored.get_mut(key)?;
1048
1049 let values: &mut PartiallyDecodedValues<D> = match entry {
1050 MaybeTypedValues::NotTyped(list_of_bytes) => {
1051 *entry = MaybeTypedValues::typed(PartiallyDecodedValues::<D> {
1052 encoded: Mutex::new(Cursor::new(std::mem::take(list_of_bytes))),
1053 decoded: Vec::new(),
1054 });
1055
1056 let MaybeTypedValues::Typed(values, _) = entry else {
1057 unreachable!();
1058 };
1059
1060 values.downcast_mut().unwrap()
1061 }
1062 MaybeTypedValues::Typed(values, _) => values.downcast_mut().unwrap(),
1063 };
1064
1065 if let Some(value) = values.decoded.extract_if(.., |value| pred(value)).next() {
1066 Some(value)
1067 } else {
1068 let mut encoded = encoded(&values.encoded);
1069 let iter = std::iter::from_fn(|| {
1070 while encoded.get_ref().len() - encoded.position() as usize > 0 {
1071 if let Ok(value) =
1072 bincode::decode_from_std_read(&mut *encoded, config::standard())
1073 {
1074 return Some(value);
1075 }
1076 }
1077
1078 None
1079 });
1080
1081 for value in iter {
1082 if pred(&value) {
1083 return Some(value);
1084 } else {
1085 values.decoded.push(value);
1086 }
1087 }
1088
1089 None
1090 }
1091 }
1092
1093 /// Take the stored structs for retrieval on a future reload.
1094 pub(crate) fn get_structs() -> HashMap<String, MaybeTypedValues> {
1095 let mut stored_guard = STORED.lock().unwrap_or_else(|err| err.into_inner());
1096 let mut stored = stored_guard.take().unwrap();
1097 stored.retain(|_, maybe_typed| {
1098 if let MaybeTypedValues::Typed(values, to_bytes) = maybe_typed {
1099 let values = to_bytes(values.as_mut());
1100 if values.is_empty() {
1101 false
1102 } else {
1103 *maybe_typed = MaybeTypedValues::NotTyped(values);
1104 true
1105 }
1106 } else {
1107 true
1108 }
1109 });
1110
1111 stored
1112 }
1113
1114 /// A possibly typed list of stored values.
1115 #[doc(hidden)]
1116 pub enum MaybeTypedValues {
1117 NotTyped(Vec<u8>),
1118 Typed(
1119 Box<dyn std::any::Any + Send>,
1120 fn(&(dyn std::any::Any + Send)) -> Vec<u8>,
1121 ),
1122 }
1123
1124 impl MaybeTypedValues {
1125 fn typed<E: Encode + Send + 'static>(values: PartiallyDecodedValues<E>) -> Self {
1126 Self::Typed(Box::new(values), |values| {
1127 let values: &PartiallyDecodedValues<E> = values.downcast_ref().unwrap();
1128 let mut encoded = encoded(&values.encoded);
1129
1130 for value in values.decoded.iter() {
1131 _ = bincode::encode_into_std_write(
1132 value,
1133 encoded.get_mut(),
1134 config::standard(),
1135 );
1136 }
1137
1138 let position = encoded.position();
1139 encoded.get_ref()[position as usize..].to_vec()
1140 })
1141 }
1142 }
1143
1144 impl std::fmt::Debug for MaybeTypedValues {
1145 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1146 match self {
1147 Self::NotTyped(arg0) => f.debug_tuple("NotTyped").field(arg0).finish(),
1148 Self::Typed(..) => f.debug_tuple("Typed").finish_non_exhaustive(),
1149 }
1150 }
1151 }
1152
1153 impl<Context> Decode<Context> for MaybeTypedValues {
1154 fn decode<D: bincode::de::Decoder<Context = Context>>(
1155 decoder: &mut D,
1156 ) -> Result<Self, bincode::error::DecodeError> {
1157 Ok(Self::NotTyped(Decode::decode(decoder)?))
1158 }
1159 }
1160
1161 impl<'de, Context> BorrowDecode<'de, Context> for MaybeTypedValues {
1162 fn borrow_decode<D: bincode::de::BorrowDecoder<'de, Context = Context>>(
1163 decoder: &mut D,
1164 ) -> Result<Self, bincode::error::DecodeError> {
1165 Ok(Self::NotTyped(Decode::decode(decoder)?))
1166 }
1167 }
1168
1169 impl Encode for MaybeTypedValues {
1170 fn encode<E: bincode::enc::Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
1171 match self {
1172 MaybeTypedValues::NotTyped(list_of_bytes) => {
1173 Encode::encode(&list_of_bytes, encoder)
1174 }
1175 MaybeTypedValues::Typed(any, get_list_of_bytes) => {
1176 Encode::encode(&get_list_of_bytes(any.as_ref()), encoder)
1177 }
1178 }
1179 }
1180 }
1181
1182 struct PartiallyDecodedValues<T> {
1183 encoded: Mutex<Cursor<Vec<u8>>>,
1184 decoded: Vec<T>,
1185 }
1186
1187 /// Set the initial list of stored structs.
1188 pub(crate) fn set_structs(structs: HashMap<String, MaybeTypedValues>) {
1189 *STORED.lock().unwrap() = Some(structs);
1190 }
1191
1192 /// Get the encoded [`Cursor`] bytes.
1193 fn encoded(encoded: &Mutex<Cursor<Vec<u8>>>) -> std::sync::MutexGuard<'_, Cursor<Vec<u8>>> {
1194 encoded.lock().unwrap_or_else(|err| err.into_inner())
1195 }
1196}
1197
1198////////// Text Builder macros (for pub/private bending)
1199#[doc(hidden)]
1200pub mod private_exports {
1201 pub use format_like::format_like;
1202}
1203
1204/// Converts a string to a valid priority
1205#[doc(hidden)]
1206pub const fn priority(priority: &str) -> u8 {
1207 let mut bytes = priority.as_bytes();
1208 let mut val = 0;
1209
1210 while let [byte, rest @ ..] = bytes {
1211 assert!(b'0' <= *byte && *byte <= b'9', "invalid digit");
1212 val = val * 10 + (*byte - b'0') as usize;
1213 bytes = rest;
1214 }
1215
1216 assert!(val <= 240, "priority cannot exceed 240");
1217
1218 val as u8
1219}
1220
1221/// Tries to evaluate a block that returns [`Result<T, Text>`]
1222///
1223/// If the block returns [`Ok`], this macro will resolve to `T`. If it
1224/// returns [`Err`], it will log the error with [`context::error!`],
1225/// then it will return from the function. As an example, this:
1226///
1227/// ```rust
1228/// # duat_core::doc_duat!(duat);
1229/// # fn test() {
1230/// use duat::prelude::*;
1231///
1232/// let ret = try_or_log_err! {
1233/// let value = result_fn()?;
1234/// value
1235/// };
1236///
1237/// fn result_fn() -> Result<usize, Text> {
1238/// Err(txt!(":("))
1239/// }
1240/// # }
1241/// ```
1242///
1243/// Will expand into:
1244///
1245/// ```rust
1246/// # duat_core::doc_duat!(duat);
1247/// # fn test() {
1248/// use duat::prelude::*;
1249///
1250/// let ret = match (|| -> Result<_, Text> { Ok(result_fn()?) })() {
1251/// Ok(ret) => ret,
1252/// Err(err) => {
1253/// context::error!("{err}");
1254/// return;
1255/// }
1256/// };
1257///
1258/// fn result_fn() -> Result<usize, Text> {
1259/// Err(txt!(":("))
1260/// }
1261/// # }
1262/// ```
1263///
1264/// Note the [`Ok`] wrapping the tokens, so it works like the `try`
1265/// keyword in that regard.
1266#[macro_export]
1267macro_rules! try_or_log_err {
1268 { $($tokens:tt)* } => {
1269 match (|| -> Result<_, $crate::text::Text> { Ok({ $($tokens)* }) })() {
1270 Ok(ret) => ret,
1271 Err(err) => {
1272 $crate::context::error!("{err}");
1273 return;
1274 }
1275 }
1276 }
1277}