everything_sdk/
ergo.rs

1use std::ffi::OsStr;
2use std::ffi::OsString;
3use std::marker::PhantomData;
4use std::path::Path;
5use std::path::PathBuf;
6use std::sync::OnceLock;
7
8use crate::raw;
9
10pub use raw::FileInfoType;
11pub use raw::RequestFlags;
12pub use raw::SortType;
13pub use raw::TargetMachine;
14
15pub mod error {
16    use super::RequestFlags;
17    use thiserror::Error as ThisError;
18
19    pub type Result<T> = std::result::Result<T, EverythingError>;
20
21    #[non_exhaustive]
22    #[derive(ThisError, Debug)]
23    pub enum EverythingError {
24        #[error("Failed to allocate memory for the search query.")]
25        Memory,
26        #[error("IPC is not available.")]
27        Ipc,
28        #[error("Failed to register the search query window class.")]
29        RegisterClassEx,
30        #[error("Failed to create the search query window.")]
31        CreateWindow,
32        #[error("Failed to create the search query thread.")]
33        CreateThread,
34        #[error("Invalid index. The index must be greater or equal to 0 and less than the number of visible results.")]
35        InvalidIndex,
36        #[error("Invalid call.")]
37        InvalidCall,
38        #[error("invalid request data, request data first.")]
39        InvalidRequest(#[from] InvalidRequestError),
40        #[error("bad parameter.")]
41        InvalidParameter,
42        #[error("not supported when using set_request_flags or set_sort to non-default value. (that is in query verison 2)")]
43        UnsupportedInQueryVersion2,
44    }
45
46    #[non_exhaustive]
47    #[derive(ThisError, Debug)]
48    pub enum InvalidRequestError {
49        #[error("should set the request flag {0:?}")]
50        RequestFlagsNotSet(RequestFlags),
51    }
52}
53
54pub use error::{EverythingError, InvalidRequestError, Result};
55
56use tracing::debug;
57use widestring::U16CStr;
58
59mod helper {
60    use super::*;
61
62    pub fn is_default_request_flags(request_flags: RequestFlags) -> bool {
63        request_flags == RequestFlags::default()
64    }
65
66    pub fn is_default_sort_type(sort_type: SortType) -> bool {
67        sort_type == SortType::default()
68    }
69
70    // when send IPC query, try version 2 first (if we specified some non-version 1 request flags or sort)
71    pub fn should_use_query_version_2(request_flags: RequestFlags, sort_type: SortType) -> bool {
72        !is_default_request_flags(request_flags) || !is_default_sort_type(sort_type)
73    }
74}
75
76#[cfg(not(feature = "async"))]
77pub fn global() -> &'static std::sync::Mutex<EverythingGlobal> {
78    static EVERYTHING_CELL: OnceLock<std::sync::Mutex<EverythingGlobal>> = OnceLock::new();
79    EVERYTHING_CELL.get_or_init(|| std::sync::Mutex::new(EverythingGlobal {}))
80}
81
82#[cfg(feature = "async")]
83pub fn global() -> &'static futures::lock::Mutex<EverythingGlobal> {
84    static EVERYTHING_CELL: OnceLock<futures::lock::Mutex<EverythingGlobal>> = OnceLock::new();
85    EVERYTHING_CELL.get_or_init(|| futures::lock::Mutex::new(EverythingGlobal {}))
86}
87
88#[non_exhaustive]
89#[derive(Debug)]
90pub struct EverythingGlobal {}
91
92impl Drop for EverythingGlobal {
93    /// NEVER call this, as the static variable would not be dropped.
94    fn drop(&mut self) {
95        // So this will not be called too.
96        // We don't need this, `raw::Everything_Reset` in `EverythingSearcher` will
97        // free the allocated memory.
98        raw::Everything_CleanUp();
99        unreachable!()
100    }
101}
102
103impl EverythingGlobal {
104    /// New the only one searcher.
105    ///
106    /// There is **at most one** searcher can exist globally at the same time.
107    pub fn searcher<'a>(&'a mut self) -> EverythingSearcher<'a> {
108        EverythingSearcher {
109            _phantom: PhantomData::<&'a ()>,
110        }
111    }
112
113    // --- General ---
114
115    /// Everything uses the version format: `<major>.<minor>.<revision>.<build>`.
116    /// The build part is incremental and unique for all Everything versions.
117    pub fn version(&self) -> Result<(u32, u32, u32, u32, TargetMachine)> {
118        Ok((
119            self.get_major_version()?,
120            self.get_minor_version()?,
121            self.get_revision()?,
122            self.get_build_number()?,
123            self.get_target_machine()?,
124        ))
125    }
126
127    pub fn get_major_version(&self) -> Result<u32> {
128        raw::Everything_GetMajorVersion().ok_or(EverythingError::Ipc)
129    }
130
131    pub fn get_minor_version(&self) -> Result<u32> {
132        raw::Everything_GetMinorVersion().ok_or(EverythingError::Ipc)
133    }
134
135    pub fn get_revision(&self) -> Result<u32> {
136        raw::Everything_GetRevision().ok_or(EverythingError::Ipc)
137    }
138
139    pub fn get_build_number(&self) -> Result<u32> {
140        raw::Everything_GetBuildNumber().ok_or(EverythingError::Ipc)
141    }
142
143    pub fn get_target_machine(&self) -> Result<TargetMachine> {
144        raw::Everything_GetTargetMachine().ok_or(EverythingError::Ipc)
145    }
146
147    /// Request Everything to save settings and data to disk and exit.
148    pub fn save_and_exit(&mut self) -> Result<bool> {
149        raw::Everything_Exit().ok_or(EverythingError::Ipc)
150    }
151
152    /// Check if Everything's database is loaded.
153    ///
154    /// When Everything is loading, any queries will appear to return no results.
155    /// Use this to determine if the database has been loaded before performing a query.
156    pub fn is_db_loaded(&self) -> Result<bool> {
157        raw::Everything_IsDBLoaded().ok_or(EverythingError::Ipc)
158    }
159
160    /// Check if Everything is running as administrator or as a standard user.
161    pub fn is_admin(&self) -> Result<bool> {
162        raw::Everything_IsAdmin().ok_or(EverythingError::Ipc)
163    }
164
165    /// Check if Everything is saving settings and data to `%APPDATA%\Everything` or to the same location
166    /// as the `Everything.exe`.
167    pub fn is_appdata(&self) -> Result<bool> {
168        raw::Everything_IsAppData().ok_or(EverythingError::Ipc)
169    }
170
171    /// Request Everything to forcefully rebuild the Everything index.
172    ///
173    /// Requesting a rebuild will mark all indexes as dirty and start the rebuild process.
174    /// Use `self.is_db_loaded()` to determine if the database has been rebuilt before
175    /// performing a query.
176    pub fn rebuild_db(&mut self) -> Result<bool> {
177        // rebuild the database.
178        raw::Everything_RebuildDB().ok_or(EverythingError::Ipc)
179    }
180
181    /// Request Everything to rescan all folder indexes.
182    ///
183    /// Everything will begin updating all folder indexes in the background.
184    pub fn update_all_folder_indexes(&mut self) -> Result<bool> {
185        // Request all folder indexes be rescanned.
186        raw::Everything_UpdateAllFolderIndexes().ok_or(EverythingError::Ipc)
187    }
188
189    /// Request Everything to save the index to disk.
190    ///
191    /// The index is only saved to disk when you exit Everything.
192    /// Call this to write the index to the file: `Everything.db`.
193    pub fn save_db(&mut self) -> Result<bool> {
194        // flush index to disk
195        raw::Everything_SaveDB().ok_or(EverythingError::Ipc)
196    }
197
198    // --- Run History ---
199
200    /// Request Everything to save the run history to disk.
201    ///
202    /// The run history is only saved to disk when you close an Everything search window or
203    /// exit Everything.
204    /// Call this to write the run history to the file: `Run History.csv`.
205    pub fn save_run_history(&mut self) -> Result<bool> {
206        // flush run history to disk
207        raw::Everything_SaveRunHistory().ok_or(EverythingError::Ipc)
208    }
209
210    /// Delete all run history.
211    ///
212    /// Calling this function will clear all run history from memory and disk.
213    pub fn delete_run_history(&mut self) -> Result<bool> {
214        // clear run history
215        raw::Everything_DeleteRunHistory().ok_or(EverythingError::Ipc)
216    }
217
218    /// Gets the run count from a specified file in the Everything index by file name.
219    pub fn get_run_count(&self, filename: impl AsRef<Path>) -> Result<u32> {
220        raw::Everything_GetRunCountFromFileName(filename.as_ref()).ok_or(EverythingError::Ipc)
221    }
222
223    /// Sets the run count for a specified file in the Everything index by file name.
224    pub fn set_run_count(&mut self, filename: impl AsRef<Path>, run_count: u32) -> Result<()> {
225        if raw::Everything_SetRunCountFromFileName(filename.as_ref(), run_count) {
226            Ok(())
227        } else {
228            Err(EverythingError::Ipc)
229        }
230    }
231
232    /// Increments the run count by one for a specified file in the Everything by file name.
233    pub fn inc_run_count(&mut self, filename: impl AsRef<Path>) -> Result<u32> {
234        raw::Everything_IncRunCountFromFileName(filename.as_ref())
235            .map(|n| n.get())
236            .ok_or(EverythingError::Ipc)
237    }
238
239    // --- Others ---
240
241    /// Check if the specified file information is indexed and has fast sort enabled.
242    pub fn is_fast_sort(&self, sort_type: SortType) -> Result<bool> {
243        raw::Everything_IsFastSort(sort_type).ok_or(EverythingError::Ipc)
244    }
245
246    /// Check if the specified file information is indexed.
247    pub fn is_file_info_indexed(&self, file_info_type: FileInfoType) -> Result<bool> {
248        raw::Everything_IsFileInfoIndexed(file_info_type).ok_or(EverythingError::Ipc)
249    }
250}
251
252#[non_exhaustive]
253pub struct EverythingSearcher<'a> {
254    _phantom: PhantomData<&'a ()>,
255}
256
257impl Drop for EverythingSearcher<'_> {
258    fn drop(&mut self) {
259        raw::Everything_Reset(); // CAUTION!
260        debug!("[Drop] EverythingSearcher is dropped! (did Reset)");
261    }
262}
263
264impl<'a> EverythingSearcher<'a> {
265    // --- Manipulating the search state ---
266    /// empty string "" by default.
267    pub fn set_search(&mut self, text: impl AsRef<OsStr>) -> &'_ mut EverythingSearcher<'a> {
268        raw::Everything_SetSearch(text);
269        self
270    }
271
272    /// disable (false) by default.
273    pub fn set_match_path(&mut self, enable: bool) -> &'_ mut EverythingSearcher<'a> {
274        raw::Everything_SetMatchPath(enable);
275        self
276    }
277
278    /// disable (false) by default.
279    pub fn set_match_case(&mut self, enable: bool) -> &'_ mut EverythingSearcher<'a> {
280        raw::Everything_SetMatchCase(enable);
281        self
282    }
283
284    /// disable (false) by default.
285    pub fn set_match_whole_word(&mut self, enable: bool) -> &'_ mut EverythingSearcher<'a> {
286        raw::Everything_SetMatchWholeWord(enable);
287        self
288    }
289
290    /// disable (false) by default.
291    pub fn set_regex(&mut self, enable: bool) -> &'_ mut EverythingSearcher<'a> {
292        raw::Everything_SetRegex(enable);
293        self
294    }
295
296    /// `u32::MAX` (0xffffffff) by default, which means all results.
297    pub fn set_max(&mut self, max_results: u32) -> &'_ mut EverythingSearcher<'a> {
298        raw::Everything_SetMax(max_results);
299        self
300    }
301
302    /// zero (0) by default.
303    pub fn set_offset(&mut self, offset: u32) -> &'_ mut EverythingSearcher<'a> {
304        raw::Everything_SetOffset(offset);
305        self
306    }
307
308    /// The default sort is EVERYTHING_SORT_NAME_ASCENDING (1). This sort is free.
309    pub fn set_sort(&mut self, sort_type: SortType) -> &'_ mut EverythingSearcher<'a> {
310        raw::Everything_SetSort(sort_type);
311        self
312    }
313
314    /// The default request flags are EVERYTHING_REQUEST_FILE_NAME | EVERYTHING_REQUEST_PATH (0x00000003).
315    pub fn set_request_flags(&mut self, flags: RequestFlags) -> &'_ mut EverythingSearcher<'a> {
316        raw::Everything_SetRequestFlags(flags);
317        self
318    }
319
320    // --- Reading the search state ---
321    pub fn get_search(&self) -> OsString {
322        raw::Everything_GetSearch()
323    }
324
325    pub fn get_match_path(&self) -> bool {
326        raw::Everything_GetMatchPath()
327    }
328
329    pub fn get_match_case(&self) -> bool {
330        raw::Everything_GetMatchCase()
331    }
332
333    pub fn get_match_whole_word(&self) -> bool {
334        raw::Everything_GetMatchWholeWord()
335    }
336
337    pub fn get_regex(&self) -> bool {
338        raw::Everything_GetRegex()
339    }
340
341    pub fn get_max(&self) -> u32 {
342        raw::Everything_GetMax()
343    }
344
345    pub fn get_offset(&self) -> u32 {
346        raw::Everything_GetOffset()
347    }
348
349    pub fn get_sort(&self) -> SortType {
350        raw::Everything_GetSort()
351    }
352
353    pub fn get_request_flags(&self) -> RequestFlags {
354        raw::Everything_GetRequestFlags()
355    }
356}
357
358impl<'a> EverythingSearcher<'a> {
359    #[cfg(not(feature = "async"))]
360    /// Execute an Everything IPC query with the current search state.
361    ///
362    /// It may take some time if you query a lot of items. Therefore, blocking needs to be
363    /// considered in specific situations. (run it in new thread or use the `async` feature)
364    pub fn query<'b>(&'b mut self) -> EverythingResults<'b> {
365        raw::Everything_Query(true);
366        EverythingResults {
367            _phantom: PhantomData::<&'b ()>,
368        }
369    }
370
371    #[cfg(feature = "async")]
372    pub async fn query<'b>(&'b mut self) -> EverythingResults<'b> {
373        non_blocking::QueryFuture::<'b>::new().await
374    }
375
376    /// Query and sort the results by path then file name in place.
377    ///
378    /// **NOT RECOMMENDED!** Use searcher.set_sort(_) instead.
379    pub fn _query_and_sort_by_path<'b>(&'b mut self) -> EverythingResults<'b> {
380        raw::Everything_Query(true);
381        // SortResultsByPath is CPU Intensive. Sorting by path can take several seconds.
382        // For improved performance, use [`raw::Everything_SetSort`]
383        raw::Everything_SortResultsByPath();
384        EverythingResults {
385            _phantom: PhantomData::<&'b ()>,
386        }
387    }
388}
389
390#[cfg(feature = "async")]
391mod non_blocking {
392    use std::{
393        marker::PhantomData,
394        pin::Pin,
395        sync::{Arc, Mutex},
396        task::{Context, Poll, Waker},
397        thread,
398    };
399
400    use windows::{
401        core::w,
402        Win32::{
403            Foundation::{FALSE, HINSTANCE, HWND, LPARAM, LRESULT, WPARAM},
404            System::LibraryLoader::GetModuleHandleW,
405            UI::WindowsAndMessaging::{
406                CreateWindowExW, DefWindowProcW, DestroyWindow, GetClassInfoExW, PeekMessageW,
407                PostMessageW, RegisterClassExW, WaitMessage, HWND_MESSAGE, MSG, PM_NOREMOVE,
408                WINDOW_EX_STYLE, WM_COPYDATA, WM_USER, WNDCLASSEXW, WS_OVERLAPPED,
409            },
410        },
411    };
412
413    use tracing::debug;
414
415    use super::EverythingResults;
416    use crate::raw;
417
418    #[non_exhaustive]
419    pub struct QueryFuture<'a> {
420        // query_expected: ExpectedParams,
421        shared_state: Arc<Mutex<SharedState>>,
422        _phantom: PhantomData<&'a ()>,
423    }
424
425    /// Shared state between the future and the waiting thread
426    struct SharedState {
427        /// Whether or not the sleep time has elapsed
428        completed: bool,
429
430        /// The waker for the task that `TimerFuture` is running on.
431        /// The thread can use this after setting `completed = true` to tell
432        /// `TimerFuture`'s task to wake up, see that `completed = true`, and
433        /// move forward.
434        waker: Option<Waker>,
435    }
436
437    impl<'a> std::future::Future for QueryFuture<'a> {
438        type Output = EverythingResults<'a>;
439        fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
440            debug!("poll() called");
441            let mut shared_state = self.shared_state.lock().unwrap();
442            if shared_state.completed {
443                let results = EverythingResults {
444                    _phantom: PhantomData::<&'a ()>,
445                };
446                debug!("Poll::Ready(_)!");
447                Poll::Ready(results)
448            } else {
449                shared_state.waker = Some(cx.waker().clone());
450                debug!("Poll::Pending");
451                Poll::Pending
452            }
453        }
454    }
455
456    impl<'a> QueryFuture<'a> {
457        pub fn new() -> Self {
458            debug!("QueryFuture::new() start");
459
460            let shared_state = Arc::new(Mutex::new(SharedState {
461                completed: false,
462                waker: None,
463            }));
464
465            // Spawn the new thread
466            let thread_shared_state = shared_state.clone();
467            thread::spawn(move || {
468                debug!("thread::spawn");
469                unsafe {
470                    debug!("first time for init");
471                    raw::Everything_SetReplyID(CUSTOM_REPLY_ID);
472                    debug_assert_eq!(raw::Everything_GetReplyID(), CUSTOM_REPLY_ID);
473                    let hwnd = create_window().unwrap();
474                    raw::Everything_SetReplyWindow(hwnd);
475                    debug_assert_eq!(raw::Everything_GetReplyWindow(), hwnd);
476
477                    debug!("Execute Query with _FALSE_");
478                    assert!(raw::Everything_Query(false));
479
480                    let mut msg: MSG = MSG::default();
481                    debug!("WaitMessage()...");
482                    WaitMessage().unwrap(); // will blocking
483                    debug!("WaitMessage() Done, One msg at least, then PeekMessageW()...");
484                    if PeekMessageW(&mut msg, hwnd, 0, 0, PM_NOREMOVE) == FALSE {
485                        panic!("There must be a message in the queue after WaitMessage().");
486                    }
487                    debug!("Gooooooot it! WM_{:#06x} ({})", msg.message, msg.message);
488                    if msg.message != WM_USER_IS_QUERY_REPLY_DONE {
489                        panic!("Must be only one type message set by us.");
490                    }
491                    debug!("Yes, we did it. (now we have results)");
492                    DestroyWindow(hwnd).unwrap();
493                    debug!("DestroyWindow() Done");
494
495                    let mut shared_state = thread_shared_state.lock().unwrap();
496                    // Signal that the Query has completed and wake up the last
497                    // task on which the future was polled, if one exists.
498                    shared_state.completed = true;
499                    debug!("set .completed to true");
500                    if let Some(waker) = shared_state.waker.take() {
501                        debug!("waker.wake()");
502                        waker.wake()
503                    }
504                }
505            });
506
507            debug!("QueryFuture::new() end");
508            Self {
509                shared_state,
510                _phantom: PhantomData::<&'a ()>,
511            }
512        }
513    }
514
515    const WM_USER_IS_QUERY_REPLY_DONE: u32 = WM_USER + 42;
516    const CUSTOM_REPLY_ID: u32 = 9527;
517
518    extern "system" fn wndproc(
519        hwnd: HWND,
520        message: u32,
521        wparam: WPARAM,
522        lparam: LPARAM,
523    ) -> LRESULT {
524        unsafe {
525            match message {
526                WM_COPYDATA => {
527                    if raw::Everything_IsQueryReply(message, wparam, lparam, CUSTOM_REPLY_ID) {
528                        debug!("[wndproc] Everything_IsQueryReply() -> YEEEESSSSSS!! (So copy done and PostMessage(WM_USER_IS_QUERY_REPLY_DONE))");
529                        PostMessageW(hwnd, WM_USER_IS_QUERY_REPLY_DONE, WPARAM(0), LPARAM(0))
530                            .unwrap();
531                        LRESULT(1)
532                    } else {
533                        // DefWindowProcW(hwnd, message, wparam, lparam)
534                        panic!("!!!! Everything_IsQueryReply() -> NOOOO!!");
535                    }
536                }
537                _ => {
538                    debug!(
539                        "[wndproc] DefWindowProcW( msg => WM_{:#06x} ({}) )",
540                        message, message
541                    );
542                    DefWindowProcW(hwnd, message, wparam, lparam)
543                }
544            }
545        }
546    }
547
548    fn create_window() -> windows::core::Result<HWND> {
549        unsafe {
550            let instance: HINSTANCE = GetModuleHandleW(None)?.into();
551            assert!(!instance.is_invalid());
552
553            let window_class_name = w!("EVERYTHING_SDK_RUST");
554
555            let mut wc = WNDCLASSEXW {
556                cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
557                hInstance: instance,
558                lpszClassName: window_class_name,
559                lpfnWndProc: Some(wndproc),
560                ..Default::default()
561            };
562
563            if GetClassInfoExW(instance, window_class_name, &mut wc).is_err() {
564                let atom = RegisterClassExW(&wc);
565                assert!(atom != 0);
566            }
567
568            let hwnd = CreateWindowExW(
569                WINDOW_EX_STYLE::default(),
570                window_class_name,
571                w!("The window for async query in everything-sdk-rs crate"),
572                WS_OVERLAPPED,
573                0,
574                0,
575                0,
576                0,
577                // Ref: https://devblogs.microsoft.com/oldnewthing/20171218-00/?p=97595
578                HWND_MESSAGE,
579                None,
580                instance,
581                None,
582            );
583
584            assert_ne!(hwnd, HWND(0));
585
586            Ok(hwnd)
587        }
588    }
589}
590
591#[non_exhaustive]
592pub struct EverythingResults<'a> {
593    _phantom: PhantomData<&'a ()>,
594}
595
596impl<'a> Drop for EverythingResults<'a> {
597    fn drop(&mut self) {
598        // I want to free memory for the results, but no api just for it.
599        // and should not call [`raw::Everything_Reset`], for long live reuse EverythingSearcher.
600        debug!("[Drop] EverythingResults is dropped!");
601    }
602}
603
604impl<'a> EverythingResults<'a> {
605    /// the results logic length, for available index in iterator.
606    pub fn len(&self) -> u32 {
607        self.num()
608    }
609
610    pub fn at(&self, index: u32) -> Option<EverythingItem<'a>> {
611        self.iter().nth(index as usize)
612    }
613
614    pub fn iter(&self) -> Iter<'a> {
615        Iter {
616            next_index: 0,
617            length: self.len(),
618            request_flags: self.request_flags(),
619            _phantom: PhantomData::<&'a ()>,
620        }
621    }
622
623    pub fn request_flags(&self) -> RequestFlags {
624        raw::Everything_GetResultListRequestFlags()
625    }
626
627    pub fn sort_type(&self) -> SortType {
628        raw::Everything_GetResultListSort()
629    }
630
631    fn is_query_version_2(&self) -> bool {
632        helper::should_use_query_version_2(self.request_flags(), self.sort_type())
633    }
634
635    pub fn num_files(&self) -> Result<u32> {
636        if self.is_query_version_2() {
637            Err(EverythingError::UnsupportedInQueryVersion2)
638        } else {
639            let num = raw::Everything_GetNumFileResults();
640            Ok(num) // would not be error (EVERYTHING_ERROR_INVALIDCALL), zero is valid.
641        }
642    }
643
644    pub fn num_folders(&self) -> Result<u32> {
645        if self.is_query_version_2() {
646            Err(EverythingError::UnsupportedInQueryVersion2)
647        } else {
648            let num = raw::Everything_GetNumFolderResults();
649            Ok(num) // would not be error (EVERYTHING_ERROR_INVALIDCALL), zero is valid.
650        }
651    }
652
653    /// the number of visible file and folder results.
654    pub fn num(&self) -> u32 {
655        let num = raw::Everything_GetNumResults();
656        num // would not be error (EVERYTHING_ERROR_INVALIDCALL), zero is valid.
657    }
658
659    pub fn total_files(&self) -> Result<u32> {
660        if self.is_query_version_2() {
661            Err(EverythingError::UnsupportedInQueryVersion2)
662        } else {
663            let num = raw::Everything_GetTotFileResults();
664            Ok(num) // would not be error (EVERYTHING_ERROR_INVALIDCALL), zero is valid.
665        }
666    }
667
668    pub fn total_folders(&self) -> Result<u32> {
669        if self.is_query_version_2() {
670            Err(EverythingError::UnsupportedInQueryVersion2)
671        } else {
672            let num = raw::Everything_GetTotFolderResults();
673            Ok(num) // would not be error (EVERYTHING_ERROR_INVALIDCALL), zero is valid.
674        }
675    }
676
677    pub fn total(&self) -> u32 {
678        let total = raw::Everything_GetTotResults();
679        total // would not be error (EVERYTHING_ERROR_INVALIDCALL), zero is valid.
680    }
681}
682
683#[non_exhaustive]
684pub struct EverythingItem<'a> {
685    index: u32,
686    request_flags: RequestFlags,
687    _phantom: PhantomData<&'a ()>,
688}
689
690#[non_exhaustive]
691pub struct Iter<'a> {
692    next_index: u32,
693    length: u32,
694    request_flags: RequestFlags,
695    _phantom: PhantomData<&'a ()>,
696}
697
698impl<'a> Iterator for Iter<'a> {
699    type Item = EverythingItem<'a>;
700    fn next(&mut self) -> Option<Self::Item> {
701        if self.next_index < self.length {
702            let index = self.next_index;
703            self.next_index += 1;
704            Some(EverythingItem {
705                index,
706                request_flags: self.request_flags,
707                _phantom: PhantomData::<&'a ()>,
708            })
709        } else {
710            None
711        }
712    }
713
714    fn size_hint(&self) -> (usize, Option<usize>) {
715        let rest = usize::try_from(self.length - self.next_index).unwrap();
716        (rest, Some(rest))
717    }
718
719    fn nth(&mut self, n: usize) -> Option<Self::Item> {
720        let index = self.next_index + u32::try_from(n).unwrap();
721        if index < self.length {
722            self.next_index = index + 1;
723            Some(EverythingItem {
724                index,
725                request_flags: self.request_flags,
726                _phantom: PhantomData::<&'a ()>,
727            })
728        } else {
729            self.next_index = self.length;
730            None
731        }
732    }
733}
734
735impl<'a> ExactSizeIterator for Iter<'a> {}
736
737impl<'a> IntoIterator for EverythingResults<'a> {
738    type Item = EverythingItem<'a>;
739    type IntoIter = Iter<'a>;
740    fn into_iter(self) -> Self::IntoIter {
741        Iter {
742            next_index: 0,
743            length: self.len(),
744            request_flags: self.request_flags(),
745            _phantom: PhantomData::<&'a ()>,
746        }
747    }
748}
749
750impl<'a> EverythingItem<'a> {
751    pub fn index(&self) -> u32 {
752        self.index
753    }
754
755    pub fn is_volume(&self) -> bool {
756        raw::Everything_IsVolumeResult(self.index)
757    }
758
759    pub fn is_folder(&self) -> bool {
760        raw::Everything_IsFolderResult(self.index)
761    }
762
763    pub fn is_file(&self) -> bool {
764        raw::Everything_IsFileResult(self.index)
765    }
766
767    pub fn filename(&self) -> Result<OsString> {
768        self.need_flags_set(RequestFlags::EVERYTHING_REQUEST_FILE_NAME)?;
769        Ok(raw::Everything_GetResultFileName(self.index).unwrap())
770    }
771
772    pub fn path(&self) -> Result<PathBuf> {
773        self.need_flags_set(RequestFlags::EVERYTHING_REQUEST_PATH)?;
774        Ok(raw::Everything_GetResultPath(self.index).unwrap().into())
775    }
776
777    /// A convenient function to get the full path by Everything_GetResultFullPathName.
778    ///
779    /// Different from the [`full_path_name`], this is an unofficial function provided for
780    /// the special case. (We can use [`raw::Everything_GetResultFullPathName`] with the
781    /// two default flags EVERYTHING_REQUEST_PATH and EVERYTHING_REQUEST_FILE_NAME)
782    pub fn filepath(&self) -> Result<PathBuf> {
783        // A bit weird but this is a special case in the official documentation.
784        self.need_flags_set(
785            RequestFlags::EVERYTHING_REQUEST_PATH | RequestFlags::EVERYTHING_REQUEST_FILE_NAME,
786        )?;
787        let buf_len = u32::from(raw::Everything_GetResultFullPathNameSizeHint(self.index).unwrap());
788        let mut buf = vec![0; buf_len as usize];
789        let n_wchar =
790            u32::from(raw::Everything_GetResultFullPathName(self.index, &mut buf).unwrap());
791        assert_eq!(buf_len, n_wchar + 1);
792        Ok(U16CStr::from_slice(&buf).unwrap().to_os_string().into())
793    }
794
795    /// Get the full path name, can be with len limit if you need.
796    ///
797    /// Similar to x.path().join(x.filename()) if parent path is NOT drive root (like C:).
798    /// (Ref: <https://github.com/nodejs/node/issues/14405>)
799    ///
800    /// Buf if the pathname is too long, you can choose to cut off the tail, reduce the
801    /// memory consumption, or limit the max size of buffer memory allocation.
802    pub fn full_path_name(&self, max_len: Option<u32>) -> Result<PathBuf> {
803        self.need_flags_set(RequestFlags::EVERYTHING_REQUEST_FULL_PATH_AND_FILE_NAME)?;
804        let size_hint =
805            u32::from(raw::Everything_GetResultFullPathNameSizeHint(self.index).unwrap());
806        let buf_len = std::cmp::min(size_hint, max_len.unwrap_or(u32::MAX)) as usize;
807        let mut buf = vec![0; buf_len];
808        let n_wchar =
809            u32::from(raw::Everything_GetResultFullPathName(self.index, &mut buf).unwrap());
810        assert_eq!(size_hint, n_wchar + 1);
811        Ok(U16CStr::from_slice(&buf).unwrap().to_os_string().into())
812    }
813
814    // Check if the corresponding flags are set. (usually just check a single flag)
815    fn need_flags_set(&self, flags: RequestFlags) -> Result<()> {
816        if self.request_flags.contains(flags) {
817            Ok(())
818        } else {
819            Err(EverythingError::InvalidRequest(
820                InvalidRequestError::RequestFlagsNotSet(flags),
821            ))
822        }
823    }
824
825    pub fn extension(&self) -> Result<OsString> {
826        self.need_flags_set(RequestFlags::EVERYTHING_REQUEST_EXTENSION)?;
827        Ok(raw::Everything_GetResultExtension(self.index).unwrap())
828    }
829
830    pub fn size(&self) -> Result<u64> {
831        self.need_flags_set(RequestFlags::EVERYTHING_REQUEST_SIZE)?;
832        let file_size = raw::Everything_GetResultSize(self.index).unwrap();
833        // If request flag `RequestFlags::EVERYTHING_REQUEST_ATTRIBUTES` is not set, the GetResultSize function
834        // will success, but the file_size for folder will be Some(-1). If the ATTRIBUTES flag is set. the
835        // GetResultSize will success too, but the file_size for folder will be Some(0).
836        //
837        // There is no relevant explanation in the documentation about that. (so wired, maybe we do not know
838        // whether this index points to a file or a directory unless we have ATTRIBUTES.)
839        //
840        // So for consistency, we will get Ok(0) for folder index regardless of whether the request flag
841        // `RequestFlags::EVERYTHING_REQUEST_ATTRIBUTES` had been set.
842        u64::try_from(file_size).or_else(|_e| {
843            if raw::Everything_IsFolderResult(self.index) {
844                debug_assert_eq!(file_size, -1); // file_size will most likely be -1
845                Ok(0)
846            } else {
847                panic!(
848                    "file size should not be a negative integer => {}",
849                    file_size
850                )
851            }
852        })
853    }
854
855    pub fn date_created(&self) -> Result<u64> {
856        self.need_flags_set(RequestFlags::EVERYTHING_REQUEST_DATE_CREATED)?;
857        Ok(raw::Everything_GetResultDateCreated(self.index).unwrap())
858    }
859
860    pub fn date_modified(&self) -> Result<u64> {
861        self.need_flags_set(RequestFlags::EVERYTHING_REQUEST_DATE_MODIFIED)?;
862        Ok(raw::Everything_GetResultDateModified(self.index).unwrap())
863    }
864
865    pub fn date_accessed(&self) -> Result<u64> {
866        self.need_flags_set(RequestFlags::EVERYTHING_REQUEST_DATE_ACCESSED)?;
867        Ok(raw::Everything_GetResultDateAccessed(self.index).unwrap())
868    }
869
870    pub fn attributes(&self) -> Result<u32> {
871        self.need_flags_set(RequestFlags::EVERYTHING_REQUEST_ATTRIBUTES)?;
872        Ok(raw::Everything_GetResultAttributes(self.index).unwrap())
873    }
874
875    pub fn file_list_filename(&self) -> Result<OsString> {
876        self.need_flags_set(RequestFlags::EVERYTHING_REQUEST_FILE_LIST_FILE_NAME)?;
877        Ok(raw::Everything_GetResultFileListFileName(self.index).unwrap())
878    }
879
880    pub fn run_count(&self) -> Result<u32> {
881        self.need_flags_set(RequestFlags::EVERYTHING_REQUEST_RUN_COUNT)?;
882        Ok(raw::Everything_GetResultRunCount(self.index))
883    }
884
885    pub fn date_run(&self) -> Result<u64> {
886        self.need_flags_set(RequestFlags::EVERYTHING_REQUEST_DATE_RUN)?;
887        Ok(raw::Everything_GetResultDateRun(self.index).unwrap())
888    }
889
890    pub fn date_recently_changed(&self) -> Result<u64> {
891        self.need_flags_set(RequestFlags::EVERYTHING_REQUEST_DATE_RECENTLY_CHANGED)?;
892        Ok(raw::Everything_GetResultDateRecentlyChanged(self.index).unwrap())
893    }
894
895    pub fn highlighted_filename(&self) -> Result<OsString> {
896        self.need_flags_set(RequestFlags::EVERYTHING_REQUEST_HIGHLIGHTED_FILE_NAME)?;
897        Ok(raw::Everything_GetResultHighlightedFileName(self.index).unwrap())
898    }
899
900    pub fn highlighted_path(&self) -> Result<OsString> {
901        self.need_flags_set(RequestFlags::EVERYTHING_REQUEST_HIGHLIGHTED_PATH)?;
902        Ok(raw::Everything_GetResultHighlightedPath(self.index).unwrap())
903    }
904
905    pub fn highlighted_full_path_and_filename(&self) -> Result<OsString> {
906        self.need_flags_set(RequestFlags::EVERYTHING_REQUEST_HIGHLIGHTED_FULL_PATH_AND_FILE_NAME)?;
907        Ok(raw::Everything_GetResultHighlightedFullPathAndFileName(self.index).unwrap())
908    }
909}