Get Started

The first step to use structsy is just to add it as dependency to your Cargo.toml

structsy="0.3"
structsy-derive="0.3"

done this you can use structsy directly in your code, so let's start with a simple main with a simple struct, to persist:

// WE import the basic sturctsy API
use structsy::{Structsy, StructsyError, StructsyTx};
// This allow to have derive(Persistent)
use structsy_derive::Persistent;

// We write our own struct with our own fields, and we add the Derive(Persistent)
#[derive(Persistent)]
struct MyData {
    name: String,
}

fn main() -> Result<(), StructsyError> {
    // This will open the file and create it if it does not exists
    let db = Structsy::open("get_started.db")?;
    // We declare the struct on the existing file.
    db.define::<MyData>()?;

    let my_data = MyData {
        name: "Example".to_string(),
    };
    let mut tx = db.begin()?;
    // We insert some data.
    tx.insert(&my_data)?;
    tx.commit()?;
    Ok(())
}

At this level there are already a lot of things to clarify:

Strutcsy::open("path") will open the file or create it, but not only it will also lock the file so if multiple processes try to access the same file only the first will succeed.
db.define::<MyData>) this serve two purposes, in the first run will store the meta informations of the struct in the file, any successive run will check that the current structure match the structure definition stored in the file, and in case fail to open the file, check the migrate method in structsy to handle migration of data in case of structures changes.
The transaction guarantee that all the data included in the transaction is committed correctly, if the software terminate before the transaction is complete at the next open the transaction will be rollback and the content will be be at the state prior the transaction changes.

Now that we inserted some data, the next step is search some just inserted data, here is the same example of before just adding the logic for search:

use structsy::{Structsy, StructsyError, StructsyTx};
// Here we add  the import of queries, to let structsy generate the logic for queries
use structsy_derive::{Persistent, queries};

#[derive(Persistent)]
struct MyData {
    name: String,
}
// We define a trait with our own logic and we ask structsy to generate the query on the MyData struct
#[queries(MyData)]
trait MyDataQuery {
    // here is our condition method, to notice that the name of the parameter has to be exactly the same of the struct field.
    fn search(self, name:&str) -> Self;
}

fn main() -> Result<(), StructsyError> {
    let db = Structsy::open("get_started.db")?;
    db.define::<MyData>()?;

    let my_data = MyData {
        name: "Example".to_string(),
    };
    let mut tx = db.begin()?;
    tx.insert(&my_data)?;
    tx.commit()?;
    
    // We just start a query on my data, that allow to have all the condition for the trait just defined before
    let query = db.query::<MyData>().search("Example");
    // We execute the query.
    for (id,my_data) in query.into_iter() {
       // access id and my_data
    }

    Ok(())
}

Also here there is a lot to add, first thing the name of the query trait, or the name of it's own methods do not actually matter, the important thing is that the function parameters have the same name of the struct fields and the same type as well, for some specific types like String is possible to use some compatible types, like &str.
The signature of the methods of the query traits have to receive self as parameter and return Self, this is checked at compile times, like also is checked the parameter-> filed matching.
When created a new query is possible to call any trait method on it, even multiple time, all the conditions used will be add in AND to the query, if a different grouping operator is needed check the Operators trait on structsy docs.
The query is not executed until the into_iter() method is called, this method will try to reorder all the conditions to optimize the query and execute it returning an iterator.

That's all for start, please check the online docs and in source code examples for more complex use cases.