Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/askonomm/siena
A data provider agnostic ORM.
https://github.com/askonomm/siena
database frontmatter orm yaml
Last synced: about 1 month ago
JSON representation
A data provider agnostic ORM.
- Host: GitHub
- URL: https://github.com/askonomm/siena
- Owner: askonomm
- License: mit
- Created: 2022-12-04T00:24:17.000Z (about 2 years ago)
- Default Branch: master
- Last Pushed: 2024-05-15T13:49:34.000Z (7 months ago)
- Last Synced: 2024-10-31T11:48:47.630Z (about 2 months ago)
- Topics: database, frontmatter, orm, yaml
- Language: Rust
- Homepage:
- Size: 67.4 KB
- Stars: 7
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.txt
Awesome Lists containing this project
README
# Siena
Siena is data provider agnostic ORM for Rust, enabling you to easily use custom data stores for your application with all the niceties of a quering engine.
Siena comes built-in with a flat-file data provider, `LocalProvider`, supporting YAML and FrontMatter files, but you can easily create your own data provider by implementing the `StoreProvider` trait.
## Install
Add the following to your Cargo.toml file:
```TOML
siena = "3.2.1"
```## Changelog
To see what's changed, check the [changelog](https://github.com/askonomm/siena/blob/master/CHANGELOG.md).
## Usage
### Create store
The first thing you need to do when using Siena is creating the store. A store is an instance of Siena with the data
provider set. A data provider is anything that implements the `StoreProvider` trait (so you can create your own!).
Siena comes with the `LocalProvider` provider, which works on the local file system.**Example:**
```rust
use siena::providers::local::LocalProvider;
use siena::siena::siena;fn main() {
let provider = LocalProvider { directory: "./path".to_string() };
let store = siena(provider);
}
```### Fetching Records
Records are placed in collections. A collection is a directory in your store. So let's say
that you have a collection called "blog-posts", you could fetch them like this:```rust
let posts = store.collection("blog-posts").get_all();
```You can also just get the first record via `get_first()` or the last one via
`.get_last()`.### Filtering Records
You can filter records using numerous `when_*` methods. And yes, you can chain them
as much as you want.#### `when_is`
To filter records by a record key that equals a given value, you can use the `when_is` method, like so:
```rust
let posts = store
.collection("blog-posts")
.when_is("status", "published")
.get_all();
```#### `when_is_not`
Similarly, to filter records the opposite way, by a record key that does _not_ equal a given value, you can use the
`when_isnt` method:```rust
let posts = store
.collection("blog-posts")
.when_is_not("status", "published")
.get_all();
```#### `when_has`
To filter records by the presence of a record key, you can use the `when_has` method, like so:
```rust
let posts = store
.collection("blog-posts")
.when_has("status")
.get_all();
```#### `when_has_not`
Similarly, to filter records the opposite way, by the _lack_ of a presence of a record key, you can use the `when_hasnt` method:
```rust
let posts = store
.collection("blog-posts")
.when_has_not("status")
.get_all();
```#### `when_matches`
To filter records by a record key that matches a value according to a Regex pattern, you can use the `when_matches` method, like so:
```rust
let posts = store
.collection("blog-posts")
.when_matches("date", r"2022\-09")
.get_all();
```There is no opposite method for `when_matches`, because regex gives you the ability to do that yourself.
### Sorting Records
You can sort records with the `sort` method, like so:
```rust
use siena::siena::{RecordSortOrder};let posts = store
.collection("blog-posts")
.sort("date", RecordSortOrder::Desc)
.get_all();
```The available ways to sort are:
- `RecordSortOrder::Desc`
- `RecordSortOrder::Asc`### Limiting Records
To limit the result, use the `limit` method:
```rust
let posts = store
.collection("blog-posts")
.limit(10)
.get_all();
```### Offsetting Records
To offset the result, use the `offset` method:
```rust
let posts = store
.collection("blog-posts")
.offset(10)
.get_all();
```### Pagination
With the combination of `limit` and `offset` method, you can create easy pagination, for example:
```rust
let page = 2;
let posts_per_page = 10;let posts = store
.collection("blog-posts")
.offset((page - 1) * posts_per_page)
.limit(posts_per_page)
.get_all();
```Or, simply use the `paginate` method which does this work for you, like this:
```rust
let posts = store
.collection("blog-posts")
.paginate(2, 10)
.get_all();
```### Updating Records
You can update the result of your query via the `set` method. It doesn't matter if you have one record or multiple records, it will update anything that you have matching your query.
For example:
```rust
let posts = store
.collection("blog-posts")
.set(Vec::from([("status", "private")]));
```This will update all the records in the `blog-post` collection by updating the `status` to `private`.
Whereas this example:
```rust
let posts = store
.collection("blog-posts")
.when_is("status", "public")
.set(Vec::from([("status", "private")]));
```Will only update all the records that have `status` as `public` _to_ `private`.
### Creating Records
The `create` method is what you use for creating a new record. Note however that the
record is not persisted until you use the `set` method to add some data. The `set` method is the only method
which writes data. The `create` method only creates the record in-memory so that the `set` method would know
where to write data.An example:
```rust
store
.create("blog-posts", "hello-world")
.set(Vec::from([("title", "Hello, World.")]));
```The `create` method takes two arguments, the collection name, and the ID of the record, which has to be unique to that collection or it will overwrite an existing record.
### Deleting Records
The `delete` method is what you use for deleting all the records matching a query, so for example if you want to
delete all records matching the status "draft", you'd run this:```rust
store
.collection("blog-posts")
.when_is("status", "draft")
.delete();
```## Providers
### `LocalProvider`
The `LocalProvider` is a provider that works on the local file system. It supports YAML and Markdown (FrontMatter) files. In the case of Markdown files, the `Record`'s returned will have `content` and `content_raw` String entries, one for the rendered HTML and one for the raw Markdown, respectively.
Supported data types are:
- `String`
- `usize`
- `bool`
- `HashMap`
- `Vec`### Custom Providers
You can create your own provider by implementing the `StoreProvider` trait. The trait has three methods that you need to implement:
```rust
pub trait StoreProvider {
fn retrieve(&self, name: &str) -> Vec;
fn set(&self, records: Vec, data: Vec<(&str, &RecordData)>) -> Vec;
fn delete(&self, records: Vec);
}
```#### The `retrieve` function
This function should take in a `name` of a data collection, e.g `posts` and return all `Record`'s for that.
#### The `set` function
This function should take in a `Vec` and a `Vec<(&str, &RecordData)>` and return a `Vec`. The `Vec` is the records that you want to update, and the `Vec<(&str, &RecordData)>` is the data that you want to update them with. The `&str` is the key of the data, and the `&RecordData` is the value.
#### The `delete` function
This function should take in a `Vec` and delete them.