#everything #sdk #wrapper #bindings

everything-sdk

An ergonomic Everything(voidtools) SDK wrapper in Rust. (Supports async and raw sdk functions)

5 releases

0.0.6 Sep 2, 2024
0.0.5 Dec 22, 2023
0.0.4 Dec 12, 2023
0.0.3 Dec 12, 2023
0.0.2 Dec 11, 2023

#618 in Filesystem

21 downloads per month
Used in ery

GPL-3.0-or-later

150KB
2K SLoC

Rust 1.5K SLoC // 0.1% comments C 622 SLoC // 0.0% comments

Everything SDK in Rust

Everything Version crates.io docs.rs MSRV

Use Everything SDK in Rust way. Types and Lifetime prevent you from accidentally calling the IPC query functions by mistake.

No document proofing yet, but you could be able to try it out.

Usage

[dependencies]
everything-sdk = "0.0.6"

The Sample all you should know: readme.rs .

use everything_sdk::*;

fn main() {
    // At first, we should clearly understand that Everything-SDK IPC code is
    // based on **global mutable static variables** (the internal state is
    // stored in them), at least that's the case for now.

    // Even if you use multiple processes to query by IPC at the same time, they
    // will only be processed serially by the Everything.exe (ver 1.4.1) process.

    // So we need and can only do the query serially via global states.
    let mut everything = global().try_lock().unwrap();

    // Check whether the Everything.exe in the background is running.
    match everything.is_db_loaded() {
        Ok(false) => panic!("The Everything database has not been fully loaded now."),
        Err(EverythingError::Ipc) => panic!("Everything is required to run in the background."),
        _ => {
            // Now _Everything_ is OK!

            // We got the searcher, which can be reused for multiple times queries and cleans up
            // memory when it has been dropped.
            let mut searcher = everything.searcher();

            // Set the query parameters, chaining call is optional.
            searcher.set_search("jpg");
            searcher
                .set_request_flags(
                    RequestFlags::EVERYTHING_REQUEST_FILE_NAME
                        | RequestFlags::EVERYTHING_REQUEST_PATH
                        | RequestFlags::EVERYTHING_REQUEST_SIZE
                        // | RequestFlags::EVERYTHING_REQUEST_ATTRIBUTES // no attr-data request
                        | RequestFlags::EVERYTHING_REQUEST_RUN_COUNT,
                )
                .set_max(5)
                .set_sort(SortType::EVERYTHING_SORT_DATE_RECENTLY_CHANGED_ASCENDING);

            // They have default value, check them in docs.
            assert_eq!(searcher.get_match_case(), false);

            // Send IPC query now, _block_ and wait for the result to return.
            // Some heavy query (like search single 'a') may take a lot of time in IPC data transfer, so
            // if you need unblocking, do them in a new thread or enable the `async` feature in crate.
            let results = searcher.query();

            // We set the max-limit(5) for query, so we can check these 5 or less results.
            let visible_num_results = dbg!(results.num());
            assert!(visible_num_results <= 5);
            // But we also know the total number of results if max not set. (just know, no IPC data copy)
            let total_num_results = dbg!(results.total());
            assert!(total_num_results >= visible_num_results);

            // Make sure you set the corresponding `RequestFlags` for getting result props.
            let is_attr_flag_set =
                dbg!(results.request_flags()).contains(RequestFlags::EVERYTHING_REQUEST_ATTRIBUTES);
            // So we have no corresponding data to call item.attributes() in for-loop as below.
            assert!(!is_attr_flag_set);

            // Walking the 5 query results from Everything IPC by iterator.
            for item in results.iter() {
                let full_path = item.filepath().unwrap();
                println!(
                    "Item[{}]: {} ({} bytes)",
                    item.index(),
                    full_path.display(),
                    // We have set the `RequestFlags::EVERYTHING_REQUEST_SIZE` for it before.
                    item.size().unwrap(),
                );
            }

            // Or you are only interested in the run count of the 3rd result in Everything Run History.
            let run_count = results
                .at(2)
                .expect("I'm pretty sure there are at least 3 results.")
                .run_count()
                .unwrap();
            println!("Run Count for Item[2]: `{}`", run_count);

            // Remember, because of global variables, there can only be one `everything`, `searcher`
            // and `results` at any time during the entire program lifetime.

            drop(results);
            // When the `results` lifetime over, we can do the next query by `searcher`.
            searcher.set_search("cargo");
            let _results = searcher.query();

            // So the opposite, we can not call this by `everything` for the lifetime limit.
            // let _ = everything.version().unwrap();

            // But the `searcher` will be dropped here as out of scope.
        }
    }

    // So we can use `everything` again for now, to check the Everything.exe version.
    let (major, minor, patch, build, taget) = everything.version().unwrap();
    println!("Everything.exe version is {major}.{minor}.{patch}.{build} ({taget})");

    // Remember the LIFETIME again!
    global().try_lock().expect_err("Prev lock is still held.");
    drop(everything);
    let _is_in_appdata = global()
        .try_lock()
        .expect("We could take the lock now, use it, and return it immediately.")
        .is_appdata()
        .unwrap();
}

The async feature

[dependencies]
everything-sdk = { version = "0.0.6", features = ["async"] }

There are only two differences in async code compared to synchronous code.

let mut everything = global().lock().await; // get the global instance
let results = searcher.query().await; // the key point, unblocking query

The complete Sample in async mode with the same logic: readme_async.rs .

The raw feature

[dependencies]
everything-sdk = { version = "0.0.6", features = ["raw"] }
use everything_sdk::raw::*;

fn main() {
    let (major, minor, patch, build, taget) = (
        Everything_GetMajorVersion().unwrap(),
        Everything_GetMinorVersion().unwrap(),
        Everything_GetRevision().unwrap(),
        Everything_GetBuildNumber().unwrap(),
        Everything_GetTargetMachine().unwrap(),
    );
    println!("Everything.exe version is {major}.{minor}.{patch}.{build} ({taget})");
}

The complete Sample in raw mode with the same logic: readme_raw.rs .

So why do we need these tedious steps?

It looks no different from the ergonomic wrapper, why can't we just write the code like this?

Think about that:

For any Everything_* function as below, we insert another Everything_* function between them, which will cause the modifications of the mutable global shared states (the underhood we know they are just the global static variables in C code), because they all have access to them. Finally it will cause everything to become messy, uncontrollable and unreliable.

All we can do is to line them up, in some certain order, and let them move forward one by one to prevent chaos.

License

This project use the GPLv3 License.

Dependencies

~0.7–36MB
~552K SLoC