#data-access #macro #persistent-storage #framework #serialize-deserialize #debugging

light-magic

A lightweight, fast and easy-to-use implementation of a optionally encrypted/persistent in-memory database

14 releases (6 breaking)

0.7.2 Oct 6, 2024
0.6.2 Oct 3, 2024
0.6.0 Jul 23, 2024

#366 in Database interfaces

Download history 17/week @ 2024-09-11 15/week @ 2024-09-18 10/week @ 2024-09-25 537/week @ 2024-10-02 75/week @ 2024-10-09 19/week @ 2024-10-16

1,006 downloads per month

MIT license

34KB
629 lines

light-magic

crates.io crates.io docs.rs

A lightweight, fast and easy-to-use implementation of a optionally encrypted/persistent in-memory database.

Features

Please note that this database is highly optimized for read operations. Writing to the database is relatively slow when using open because each write operation involves writing data to the disk. These writes are done atomically, ensuring no data loss on a system-wide crash.

  • Persistent Data Storage: Data can be saved automatically and persistently to a formatted JSON file via open, or it can be operated in-memory using open_in_memory.
  • Encrypted Persistent Data Storage: Data can be also saved encrypted via the encrypted module using the same open method.
  • Easy Table Markup: Utilizes Rusts beautiful type system, structs and traits.
  • Powerful Data Access Functions: Utilize functions like search / search_ordered and the join! macro for efficient data searching and joining.
  • Efficient Storage: The database employs a custom Table data type, which uses the BTreeMap type from std::collections under the hood, for efficient storage and easy access of its tables.
  • Parallel Access Support: Access the database in parallel using Arc<AtomicDatabase<_>>.

...and more. Look into Todos for more planned features!

Installation

Add this to your Cargo.toml:

[dependencies]
light_magic = "0.7.2"

Examples

Using it in an axum Server? Look here: maud-magic-rs. Otherwise, look at this general example:

use light_magic::{
    atomic::DataStore,
    join,
    serde::{Deserialize, Serialize},
    table::{PrimaryKey, Table},
};

#[derive(Default, Debug, Serialize, Deserialize)]
struct Database {
    users: Table<User>,
    permissions: Table<Permission>,
    criminals: Table<Criminal>,
    settings: Settings,
}

impl light_magic::atomic::DataStore for Database {}
// or
// impl light_magic::encrypted::EncryptedDataStore for Database {}

#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
struct User {
    id: usize,
    name: String,
    kind: String,
}

impl PrimaryKey for User {
    type PrimaryKeyType = usize;

    fn primary_key(&self) -> &Self::PrimaryKeyType {
        &self.id
    }
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
struct Permission {
    user_name: String,
    level: Level,
}

impl PrimaryKey for Permission {
    type PrimaryKeyType = String;

    fn primary_key(&self) -> &Self::PrimaryKeyType {
        &self.user_name
    }
}

#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
enum Level {
    #[default]
    Admin,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
struct Criminal {
    user_name: String,
    entry: String,
}

impl PrimaryKey for Criminal {
    type PrimaryKeyType = String;

    fn primary_key(&self) -> &Self::PrimaryKeyType {
        &self.user_name
    }
}

#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
struct Settings {
    time: usize,
    password: String,
}

fn main() {
    let db = Database::open("./tests/test.json");
    // or
    // let db = Database::open("./tests/test.json", "somePassword");

     db.write().users.add(User {
        id: 0,
        name: String::from("Nils"),
        kind: String::from("Young"),
    });
    println!("{:?}", db.read().users.get(&0));
    println!("{:?}", db.read().users.search(|user| { user.name.contains("Nils") }));

    db.write().permissions.add(Permission {
        user_name: String::from("Nils"),
        level: Level::Admin,
    });
    println!("{:?}", db.read().permissions.get(&String::from("Nils")));
    println!("{:?}", db.read().permissions.search(|permission| { permission.level == Level::Admin }));

    db.write().criminals.add(Criminal {
        user_name: String::from("Nils"),
        entry: String::from("No records until this day! Keep ur eyes pealed!"),
    });
    println!("{:?}", db.read().criminals.get(&String::from("Nils")));
    println!("{:?}", db.read().criminals.search(|criminal| { criminal.entry.contains("No records") }));

    db.write().settings = Settings {
        time: 1718744090,
        password: String::from("password"),
    };
    println!("{:?}", db.read().settings);

    let joined = join!(db.read(), "Nils", users => name, permissions => user_name, criminals => user_name);
    println!("{:?}", joined);
}

Todos

None currently

Dependencies

~3–4MB
~80K SLoC