rustorm

Latest Version

A Work in Progress rust ORM library for Postgresql

A fork of my previous, very simple ORM which was written in java

Dependency

Features

To see it in action:

Put the sample database in your local postgresql installation

```bash psql -U postgres -h localhost -W -d -f ./scripts/bazaarv6all.sql

```

Alternatively, you cam

```bash sh setup.sh

`` Look at the code./examples/generatemodelcode.rs` This will be the code to generate the model code based on the table schema and relationships to other tables.

```rust ///generatemodelcode.rs

extern crate rustorm;

use rustorm::db::postgres::Postgres; use rustorm::codegen; use rustorm::codegen::Config;

fn main(){ let pg:Result = Postgres::new("postgres://postgres:p0stgr3s@localhost/bazaarv6"); match pg{ Ok(pg) => { let config = Config{ basemodule:Some("gen".tostring()), includetablereferences:true, usecondensedname:true, generatetablemeta:true, basedir:"./examples".tostring(), }; codegen::generateall(&pg, &config); } Err(error) =>{ println!("{}",error); } } }

```

bash cargo run --release --example generate_model_code cat ./examples/gen/bazaar/product.rs

Product table as an example

This is an example table product in schema bazaar (./scripts/bazaar_v6_dump_schema.sql)

```sql

CREATE TABLE bazaar.product ( -- Inherited from table system.record: organizationid uuid, -- @Value(users.userid) , which means the value will be set with the users.userid value... -- Inherited from table system.record: clientid uuid, -- @Value(users.clientid) The clientid of the user creating this records -- Inherited from table system.record: created timestamp with time zone NOT NULL DEFAULT now(), -- Inherited from table system.record: createdby uuid, -- @Value(users.userid) -- Inherited from table system.record: updated timestamp with time zone NOT NULL DEFAULT now(), -- Inherited from table system.record: updatedby uuid, -- @Value(users.userid) -- Inherited from table system.record: priority numeric, -- Inherited from table system.record: name character varying, -- This is @Required it has @DisplayLength(50) - 50 character in display length a @MinLength(1) and @MaxLength(100) - Do not go over 100 characters or else the system will throw a ValueTooLong exception... -- Inherited from table system.record: description character varying, -- @DisplayLength(100) When building a UI for this field... -- Inherited from table system.record: help text, -- Inherited from table system.record: active boolean NOT NULL DEFAULT true, -- @Active productid uuid NOT NULL DEFAULT uuidgeneratev4(), parentproductid uuid, isservice boolean DEFAULT false, price numeric, useparentprice boolean DEFAULT false, unit character varying, tags json, info json, -- {color:"red",... seqno integer, -- @Sequence can be used to do alternate ordering of the values, when alphetical or time can not be used upfrontfee numeric DEFAULT 0.00, -- Applicable to services, usually services has an upfront fee barcode character varying, -- barcode if scanning the product, conflict can happen, expect to return matching list of products using the barcode ownerid uuid, -- Whom this product belongs, since createdby can be someone else create the product list in behalf of the owner of the product currencyid uuid, CONSTRAINT productpkey PRIMARY KEY (productid), CONSTRAINT productcurrencyidfkey FOREIGN KEY (currencyid) REFERENCES payment.currency (currencyid) MATCH SIMPLE ON UPDATE CASCADE ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED, CONSTRAINT productuseridfkey FOREIGN KEY (ownerid) REFERENCES bazaar.users (user_id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE RESTRICT DEFERRABLE INITIALLY DEFERRED ) INHERITS (system.record)

```

Look at the file ./examples/gen/bazaar/product.rs The generated rust code should look like

```rust /// /// This will be exposed as an @Api, including @Table(users, category, product_availability, photo) ///

[derive(RustcDecodable, RustcEncodable)]

[derive(Debug)]

pub struct Product { /// primary /// default: uuidgeneratev4() /// not nullable /// db data type: uuid pub productid:Uuid, /// barcode if scanning the product, conflict can happen, expect to return matching list of products using the barcode /// db data type: character varying pub barcode:Option, /// db data type: uuid pub currencyid:Option, /// {color:"red", /// dimension:"10x20x30", /// dimensionUnit:"mm", /// weight:"4", /// weightUnit:"kg" /// } /// db data type: json pub info:Option, /// default: false /// db data type: boolean pub isservice:Option, /// Whom this product belongs, since createdby can be someone else create the product list in behalf of the owner of the product /// db data type: uuid pub ownerid:Option, /// db data type: uuid pub parentproductid:Option, /// db data type: numeric pub price:Option, /// @Sequence can be used to do alternate ordering of the values, when alphetical or time can not be used /// db data type: integer pub seqno:Option, /// db data type: json pub tags:Option, /// db data type: character varying pub unit:Option, /// Applicable to services, usually services has an upfront fee /// default: 0.00 /// db data type: numeric pub upfrontfee:Option, /// default: false /// db data type: boolean pub useparentprice:Option, /// @Active /// default: true /// not nullable /// --inherited-- /// db data type: boolean pub active:bool, /// @Value(users.clientid) The clientid of the user creating this records /// --inherited-- /// db data type: uuid pub clientid:Option, /// default: now() /// not nullable /// --inherited-- /// db data type: timestamp with time zone pub created:DateTime, /// @Value(users.userid) /// --inherited-- /// db data type: uuid pub createdby:Option, /// @DisplayLength(100) When building a UI for this field /// @MaxLength(200) Do not go over 200 character on this one /// --inherited-- /// db data type: character varying pub description:Option, /// --inherited-- /// db data type: text pub help:Option, /// This is @Required it has @DisplayLength(50) - 50 character in display length a @MinLength(1) and @MaxLength(100) - Do not go over 100 characters or else the system will throw a ValueTooLong exception /// can also be express with @Length(1-100) /// --inherited-- /// db data type: character varying pub name:Option, /// @Value(users.userid) , which means the value will be set with the users.userid value /// /// @Where(users.active=true) /// --inherited-- /// db data type: uuid pub organizationid:Option, /// --inherited-- /// db data type: numeric pub priority:Option, /// default: now() /// not nullable /// --inherited-- /// db data type: timestamp with time zone pub updated:DateTime, /// @Value(users.userid) /// --inherited-- /// db data type: uuid pub updatedby:Option, /// has one pub owner:Option, /// has one pub currency:Option, /// has one, extension table pub productavailability:Option>, /// has many, indirect referring table, derived from linker table: productcategory pub category:Option>, /// has many, indirect referring table, derived from linker table: productphoto pub photo:Option>, /// has many, indirect referring table, derived from linker table: productreview pub review:Option>, } ```

Take notice of these last members of the struct here

```rust

  /// has one, extension table
pub product_availability:Option<Box<ProductAvailability>>,
/// has many, indirect referring table, derived from linker table: product_category
pub category:Option<Vec<Category>>,
/// has many, indirect referring table, derived from linker table: product_photo
pub photo:Option<Vec<Photo>>,
/// has many, indirect referring table, derived from linker table: product_review
pub review:Option<Vec<Review>>,

```

I have the generated model code, Now what?

```rust

use gen::bazaar::Product;

// this is the generated module, generated by generatemodelcode example mod gen;

fn main(){ let pg:Result = Postgres::new("postgres://postgres:p0stgr3s@localhost/bazaarv6"); match pg{ Ok(pg) => { let sql = "select * from bazaar.product".tostring(); let stmt = pg.conn.prepare(&sql).unwrap(); for row in stmt.query(&[]).unwrap() { let product = Product{ productid: row.get("productid"), barcode: row.getopt("barcode").ok(), currencyid: row.getopt("currencyid").ok(), info: row.getopt("info").ok(), isservice: row.getopt("isservice").ok(), ownerid: row.getopt("ownerid").ok(), parentproductid: row.getopt("parentproductid").ok(), price: row.getopt("price").ok(), seqno: row.getopt("seqno").ok(), tags: row.getopt("tags").ok(), unit: row.getopt("unit").ok(), upfrontfee: row.getopt("upfrontfee").ok(), useparentprice: row.getopt("useparentprice").ok(), active: row.get("active"), clientid: row.getopt("clientid").ok(), created: row.get("created"), createdby: row.getopt("createdby").ok(), description: row.getopt("description").ok(), help: row.getopt("help").ok(), name: row.getopt("name").ok(), organizationid: row.getopt("organizationid").ok(), priority:row.getopt("priority").ok(), updated:row.get("updated"), updatedby: row.getopt("updatedby").ok(), owner:None, currency:None, productavailability:None, category:None, photo:None, review:None }; println!("product:{}({})", product.name.asref().unwrap(), product.productid); println!("{},", json::aspretty_json(&product)); } } Err(error) =>{ println!("{}",error); } } }

```

```sh

cargo run --release --example test_product

sh product:iphone4s(f7521093734d488a9f60fc9f11f7e750) ...

```

Documentation

docs

Roadmap

For Updates

Follow me on twitter: @ivanceras