https://crates.io/crates/transistor

Overview

Transistor

A Rust Crux Client crate/lib. For now, this crate intends to support 2 ways to interact with Crux:

  • Via Docker with a crux-standalone version docker-hub. Current Docker image juxt/crux-standalone:20.09-1.11.0.
  • Via HTTP using the HTTP API.
  • Async support.
  • Clojure.api. (To be evaluated.)
  • FFI. (To be evaluated.)

Other solutions may be added after the first release.

Bitemporal Crux

Crux is optimised for efficient and globally consistent point-in-time queries using a pair of transaction-time and valid-time timestamps.

Ad-hoc systems for bitemporal recordkeeping typically rely on explicitly tracking either valid-from and valid-to timestamps or range types directly within relations. The bitemporal document model that Crux provides is very simple to reason about and it is universal across the entire database, therefore it does not require you to consider which historical information is worth storing in special "bitemporal tables" upfront.

One or more documents may be inserted into Crux via a put transaction at a specific valid-time, defaulting to the transaction time (i.e. now), and each document remains valid until explicitly updated with a new version via put or deleted via delete.

Why?

Time Purpose
transaction-time Used for audit purposes, technical requirements such as event sourcing.
valid-time Used for querying data across time, historical analysis.

transaction-time represents the point at which data arrives into the database. This gives us an audit trail and we can see what the state of the database was at a particular point in time. You cannot write a new transaction with a transaction-time that is in the past.

valid-time is an arbitrary time that can originate from an upstream system, or by default is set to transaction-time. Valid time is what users will typically use for query purposes.

Reference crux bitemporality and value of bitemporality

Usage

To add this crate to your project you should add one of the following line to your dependencies field in Cargo.toml:

[dependencies]
transistor = "2.1.2"

Creating a Crux Client

All operations with Transistor start in the module client with Crux::new("localhost", "3000"). The struct Crux is responsabile for defining request HeadersMap and the request URL. The URL definition is required and it is done by the static function new, which receives as argument a host and a port and returns a Crux instance. To change HeadersMap info so that you can add AUTHORIZATION you can use the function with_authorization that receives as argument the authorization token and mutates the Crux instance.

  • HeaderMap already contains the header Content-Type: application/edn.

Finally, to create a Crux Client the function <type>_client should be called, for example http_client. This function returns a struct that contains all possible implementarions to query Crux Docker and Standalone HTTP Server.

use transistor::client::Crux;

// HttpClient with AUTHORIZATION
let auth_client = Crux::new("127.0.0.1","3000").with_authorization("my-auth-token").http_client();

// HttpClient without AUTHORIZATION
let client = Crux::new("127.0.0.1","3000").http_client();

Http Client

Once you have called http_client you will have an instance of the HttpClient struct which has a bunch of functions to query Crux on Docker and Standalone HTTP Server:

  • state queries endpoint / with a GET. No args. Returns various details about the state of the database.
let body = client.state().unwrap();

// StateResponse { 
//     index___index_version: 5, 
//     doc_log___consumer_state: None, 
//     tx_log___consumer_state: None, 
//     kv___kv_store: "crux.kv.rocksdb.RocksKv", 
//     kv___estimate_num_keys: 56, 
//     kv___size: 2271042 
// }
  • tx_log requests endpoint /tx-log via POST. Actions is expected as argument. The "write" endpoint, to post transactions.
use transistor::http::{Actions};
use transistor::client::Crux;
use transistor::types::{CruxId};

let person1 = Person {
    crux__db___id: CruxId::new("jorge-3"), 
    ..
};

let person2 = Person {
    crux__db___id: CruxId::new("manuel-1"), 
    ..
};

let actions = Actions::new()
    .append_put(person1)
    .append_put(person2);

let body = client.tx_log(actions).unwrap();
// {:crux.tx/tx-id 7, :crux.tx/tx-time #inst \"2020-07-16T21:50:39.309-00:00\"}
  • tx_logs requests endpoint /tx-log via GET. No args. Returns a list of all transactions.
use transistor::client::Crux;

let body = client.tx_logs().unwrap();

// TxLogsResponse {
//     tx_events: [
//         TxLogResponse {
//             tx___tx_id: 0,
//             tx___tx_time: 2020-07-09T23:38:06.465-00:00,
//             tx__event___tx_events: Some(
//                 [
//                     [
//                         ":crux.tx/put",
//                         "a15f8b81a160b4eebe5c84e9e3b65c87b9b2f18e",
//                         "125d29eb3bed1bf51d64194601ad4ff93defe0e2",
//                     ],
//                 ],
//             ),
//         },
//         TxLogResponse {
//             tx___tx_id: 1,
//             tx___tx_time: 2020-07-09T23:39:33.815-00:00,
//             tx__event___tx_events: Some(
//                 [
//                     [
//                         ":crux.tx/put",
//                         "a15f8b81a160b4eebe5c84e9e3b65c87b9b2f18e",
//                         "1b42e0d5137e3833423f7bb958622bee29f91eee",
//                     ],
//                 ],
//             ),
//         },
//         ...
//     ]
// } 
  • entity requests endpoint /entity via POST. A serialized CruxId, serialized Edn::Key or a String containing a keyword must be passed as argument. Returns an entity for a given ID and optional valid-time/transaction-time co-ordinates.
let person = Person {
    crux__db___id: CruxId::new("hello-entity"), 
    ...
};

let client = Crux::new("localhost", "3000").http_client();

// entity expects a CruxId
let edn_body = client.entity(person.crux__db___id).unwrap();
// Map(
//     Map(
//         {
//             ":crux.db/id": Key(
//                 ":hello-entity",
//             ),
//             ":first-name": Str(
//                 "Hello",
//             ),
//             ":last-name": Str(
//                 "World",
//             ),
//         },
//     ),
// )
  • entity_timed is similar to entity as it requests the same endpoint, the difference is that it can send transaction-time and valid-time as query-params. This is done by the extra arguments transaction_time: Option<DateTime<FixedOffset>> and valid_time: Option<DateTime<FixedOffset>>.

  • entity_tx requests endpoint /entity-tx via POST. A serialized CruxId, serialized Edn::Key or a String containing a keyword must be passed as argument. Returns the transaction that most recently set a key.

use transistor::http::{Action};
use transistor::client::Crux;
use transistor::types::{CruxId};

let person = Person {
    crux__db___id: CruxId::new("hello-entity"), 
    ...
};

let client = Crux::new("localhost", "3000").http_client();

let tx_body = client.entity_tx(edn_rs::to_string(person.crux__db___id)).unwrap();
// EntityTxResponse {
//     db___id: "d72ccae848ce3a371bd313865cedc3d20b1478ca",
//     db___content_hash: "1828ebf4466f98ea3f5252a58734208cd0414376",
//     db___valid_time: 2020-07-20T20:38:27.515-00:00,
//     tx___tx_id: 31,
//     tx___tx_time: 2020-07-20T20:38:27.515-00:00,
// }
  • entity_tx_timed is similar to entity_tx as it requests the same endpoint, the difference is that it can send transaction-time and valid-time as query-params. This is done by the extra arguments transaction_time: Option<DateTime<FixedOffset>> and valid_time: Option<DateTime<FixedOffset>>.

  • entity_history requests endpoint /entity-history via GET. Arguments are the crux.db/id as a String, an ordering argument defined by the enum http::Order (Asc or Desc) and a boolean for the with-docs? flag. The response is a Vector containing EntityHistoryElement. If with-docs? is true, thank the field db__doc, :crux.db/doc, witll return an Option<Edn> containing the inserted struct.

use transistor::client::Crux;
use transistor::http::Order;
use transistor::types::CruxId;

let person = Person {
    crux__db___id: CruxId::new("hello-history"),
    ...

let client = Crux::new("localhost", "3000").http_client();

let tx_body = client.entity_tx(person.crux__db___id).unwrap();

let entity_history = client.entity_history(tx_body.db___id.clone(), Order::Asc, true);
// EntityHistoryResponse { history: [
//     EntityHistoryElement { 
//         db___valid_time: 2020-08-05T03:00:06.476-00:00, 
//         tx___tx_id: 37, tx___tx_time: 2020-08-05T03:00:06.476-00:00, 
//         db___content_hash: "2da097a2dffbb9828cd4377f1461a59e8454674b", 
//         db__doc: Some(Map(Map(
//                 {":crux.db/id": Key(":hello-history"), 
//                 ":first-name": Str("Hello"), 
//                 ":last-name": Str("World")}
//                ))) 
//     }
// ]}

let entity_history_without_docs = client.entity_history(tx_body.db___id, Order::Asc, false);
// EntityHistoryResponse { 
//     history: [
//         EntityHistoryElement {
//              db___valid_time: 2020-08-05T03:00:06.476-00:00, 
//              tx___tx_id: 37, 
//              tx___tx_time: 2020-08-05T03:00:06.476-00:00, 
//              db___content_hash: "2da097a2dffbb9828cd4377f1461a59e8454674b", 
//              db__doc: None 
//             }
//         }
//     ]}
  • entity_history_timed is similar to entity_histoty as it requests the same endpoint, the difference is that it can send start-transaction-time, end-transaction-time, start-valid-time and end-valid-time as query-params. This is done by adding a Vec<TimeHistory> containing one TimeHistory::TransactionTime and/or one TimeHistory::ValidTime, both of them receive two Option<DateTime<Utc>>. The first DateTime is the start-<type>-time and the second is the end-<type>-time.

  • query requests endpoint /query via POST. Argument is a query of the type Query. Retrives a Set containing a vector of the values defined by the function Query::find. Available functions are find, find_by_aggregates, where_clause, args, order_by, limit, offset, examples complex_query and limit_offset_query have examples on how to use them.

Simple find

use transistor::client::Crux;
use transistor::types::{query::Query};

let client = Crux::new("localhost", "3000").http_client();

let query_is_sql = Query::find(vec!["?p1", "?n"])
    .where_clause(vec!["?p1 :name ?n", "?p1 :is-sql true"])
    .build();
// Query:
// {:query
//     {:find [?p1 ?n]
//      :where [[?p1 :name ?n]
//              [?p1 :is-sql true]]}}

let is_sql = client.query(query_is_sql.unwrap()).unwrap();
// {[":mysql", "MySQL"], [":postgres", "Postgres"]} BTreeSet

Find by aggregates

  • Available aggregates at Aggregate
use transistor::client::Crux;
use transistor::types::{query::Query};

let client = Crux::new("localhost", "3000").http_client();

let q = Query::find_by_aggregates(vec![
    Aggregate::Min("?e".to_string()), Aggregate::Max("?e".to_string()), Aggregate::Count("?e".to_string()),
    Aggregate::MinN(5, "?e".to_string()), Aggregate::CountDistinct("?e".to_string())
])?
.where_clause(vec!["?e :type :burger"])?
.build()?;
// Query:
// {:query
//   {:find [(min ?e) (max ?e) (count ?e) (min 5 ?e) (count-distinct ?e)]
//    :where [[?e :type :burger]]
// }}

let _ = client.query(q)?;

Transisitor's Structs and Enums

Actions is a builder struct to help you create a Vec<Action> for tx_log. Available functions are:

  • new static method to instantiate struct Actions.
  • append_put<T: Serialize>(action: T) appends a Put to Actions with no valid-time. Put writes a document.
  • append_put_timed<T: Serialize>(action: T, date: DateTime<FixedOffset>) appends a Put to Actions with valid-time.
  • append_delete(id: CruxId) appends a Delete to Actions with no valid-time. Deletes the specific document at last valid-time.
  • append_delete_timed(id: CruxId, date: DateTime<FixedOffset>) appends a Delete to Actions with valid-time. Deletes the specific document at the given valid-time.
  • append_evict(id: CruxId) appends an Evict to Actions. Evicts a document entirely, including all historical versions (receives only the ID to evict).
  • append_match_doc<T: Serialize>(id: CruxId, action: T) appends a Match to Actions with no valid-time. Matches the current state of an entity, if the state doesn't match the provided document, the transaction will not continue.
  • append_match_doc_timed<T: Serialize>(id: CruxId, action: T, date: DateTime<FixedOffset>) appends a Match to Actions with valid-time.
  • build generates the Vec<Action> from Actions
use transistor::client::Crux;
use transistor::types::Actions;

fn main() -> Result<(), CruxError> {
    let crux = Database {
        // ...
    };

    let psql = Database {
        // ...
    };

    let mysql = Database {
        // ...
    };

    let cassandra = Database {
        // ...
    };

    let sqlserver = Database {
        // ...
    };

    let client = Crux::new("localhost", "3000").http_client();
    let timed = "2014-11-28T21:00:09-09:00"
        .parse::<DateTime<FixedOffset>>()
        .unwrap();

    let actions: Vec<Action> = Actions::new()
        .append_put(crux)
        .append_put(psql)
        .append_put(mysql)
        .append_put_timed(cassandra, timed)
        .append_put(sqlserver)
        .build();

    let _ = client.tx_log(actions)?;
}

Query is a struct responsible for creating the fields and serializing them into the correct query format. It has a function for each field and a build function to help check if it is correctyly formatted.

  • find is a static builder function to define the elements inside the :find clause.
  • where_clause is a builder function that defines the vector os elements inside the :where [] array.
  • order_by is a builder function to define the elements inside the :order-by clause.
  • args is a builder function to define the elements inside the :args clause.
  • limit is a builder function to define the elements inside the :limit clause.
  • offset is a builder function to define the elements inside the :offset clause.
  • with_full_results is a builder function to define the flag full-results? as true. This allows your query response to return the whole document instead of only the searched keys. The result of the Query {:query {:find [?user ?a] :where [[?user :first-name ?a]] :full-results? true}} will be a BTreeSet<Vec<String>> like ([{:crux.db/id :fafilda, :first-name "Jorge", :last-name "Klaus"} "Jorge"]), so the document will need further EDN parsing to become the document's struct.

Errors are defined in the CruxError enum.

  • EdnError is a wrapper over edn_rs::EdnError.
  • RequestError is originated by reqwest crate. Failed to make HTTP request.
  • QueryFormatError is originated when the provided Query struct did not match schema.
  • QueryError is responsible for encapsulation the Stacktrace error from Crux response:
use transistor::client::Crux;
use transistor::types::{query::Query};

let _client = Crux::new("localhost", "3000").http_client();

// field `n` doesn't exist
let _query_error_response = Query::find(vec!["?p1", "?n"])
    .where_clause(vec!["?p1 :name ?g", "?p1 :is-sql true"])
    .build();

let error = client.query(query_error_response?)?;
println!("Stacktrace \n{:?}", error);

// Stacktrace
// QueryError("{:via
//      [{:type java.lang.IllegalArgumentException,
//        :message \"Find refers to unknown variable: n\",
//    :at [crux.query$q invokeStatic \"query.clj\" 1152]}],
//  :trace
//  [[crux.query$q invokeStatic \"query.clj\" 1152]
//   [crux.query$q invoke \"query.clj\" 1099]
//   [crux.query$q$fn__10850 invoke \"query.clj\" 1107]
//   [clojure.core$binding_conveyor_fn$fn__5754 invoke \"core.clj\" 2030]
//   [clojure.lang.AFn call \"AFn.java\" 18]
//   [java.util.concurrent.FutureTask run \"FutureTask.java\" 264]
//   [java.util.concurrent.ThreadPoolExecutor
//    runWorker
//    \"ThreadPoolExecutor.java\"
//    1128]
//   [java.util.concurrent.ThreadPoolExecutor$Worker
//    run
//    \"ThreadPoolExecutor.java\"
//    628]
//   [java.lang.Thread run \"Thread.java\" 834]],
//  :cause \"Find refers to unknown variable: n\"}
// ")

Testing the Crux Client

For testing purpose there is a feature called mock that enables the http_mock function that is a replacement for the http_client function. To use it run your commands with the the flag --features "mock" as in cargo test --test lib --no-fail-fast --features "mock". The mocking feature uses the crate mockito = "0.26" as a Cargo dependency. An example usage with this feature enabled:

use transistor::client::Crux;
use transistor::http::Action;
use edn_derive::Serialize;
use transistor::types::{CruxId};
use mockito::mock;

#[test]
#[cfg(feature = "mock")]
fn mock_client() {
    let _m = mock("POST", "/tx-log")
        .with_status(200)
        .match_body("[[:crux.tx/put { :crux.db/id :jorge-3, :first-name \"Michael\", :last-name \"Jorge\", }], [:crux.tx/put { :crux.db/id :manuel-1, :first-name \"Diego\", :last-name \"Manuel\", }]]")
        .with_header("content-type", "text/plain")
        .with_body("{:crux.tx/tx-id 8, :crux.tx/tx-time #inst \"2020-07-16T21:53:14.628-00:00\"}")
        .create();

    let person1 = Person {
        // ...
    };

    let person2 = Person {
        /// ...
    };

    let actions = vec![Action::put(person1), Action::put(person2)];
    
    let body = Crux::new("localhost", "3000")
        .http_mock()
        .tx_log(actions)
        .unwrap();

    assert_eq!(
        format!("{:?}", body),
        String::from("TxLogResponse { tx___tx_id: 8, tx___tx_time: 2020-07-16T21:53:14.628-00:00, tx__event___tx_events: None }")
    );
}

#[derive(Debug, Clone, Serialize)]
#[allow(non_snake_case)]
pub struct Person {
    crux__db___id: CruxId,
    // ...
}

Also, struct Actions can be tested with feature mock by using enum ActionMock due to the implementation of impl PartialEq<Vec<ActionMock>> for Actions. A demo example can be:

use transistor::types::http::{Actions, ActionMock};

fn test_actions_eq_actions_mock() {
    let actions = test_actions();
    let mock = test_action_mock();

    assert_eq!(actions, mock);
}

fn test_action_mock() -> Vec<ActionMock> {
    let person1 = Person {
        crux__db___id: CruxId::new("jorge-3"),
        first_name: "Michael".to_string(),
        last_name: "Jorge".to_string(),
    };

    let person2 = Person {
        crux__db___id: CruxId::new("manuel-1"),
        first_name: "Diego".to_string(),
        last_name: "Manuel".to_string(),
    };

    vec![
        ActionMock::Put(edn_rs::to_string(person1.clone()), None),
        ActionMock::Put(edn_rs::to_string(person2), None),
        ActionMock::Delete(edn_rs::to_string(person1.crux__db___id), None),
    ]
}

fn test_actions() -> Actions {
    let person1 = Person {
        crux__db___id: CruxId::new("jorge-3"),
        first_name: "Michael".to_string(),
        last_name: "Jorge".to_string(),
    };

    let person2 = Person {
        crux__db___id: CruxId::new("manuel-1"),
        first_name: "Diego".to_string(),
        last_name: "Manuel".to_string(),
    };
    Actions::new().append_put(person1.clone()).append_put(person2).append_delete(person1.crux__db___id)
}

Async support

Async feature is still in BETA as it depends heavily on unwraps.

It is possible to use async/await http client, for that it is necessary to enable feature async in transistor, transistor = { version = "2.1.2", features = ["async"] }. With this feature enabled the HttpClient will use reqwest::Client instead of reqwest::blocking::Client. The default async runtime for reqwest::Client is tokio, so it is good to have tokio with feature macros, as well as futures, in your Cargo.toml:

futures = {version = "0.3.5" }
tokio = {version = "0.2.22", features = ["macros"] }

An async query example can be found below:

use tokio::prelude::*;
use transistor::client::Crux;
use edn_derive::Serialize;
use transistor::types::http::Action;
use transistor::types::{
    error::CruxError,
    {query::Query, CruxId},
};

#[tokio::main]
async fn main() {
    let crux = Database {
        crux__db___id: CruxId::new("crux"),
        name: "Crux Datalog".to_string(),
        is_sql: false,
    };

    let psql = Database {
        crux__db___id: CruxId::new("postgres"),
        name: "Postgres".to_string(),
        is_sql: true,
    };

    let mysql = Database {
        crux__db___id: CruxId::new("mysql"),
        name: "MySQL".to_string(),
        is_sql: true,
    };

    let client = Crux::new("localhost", "3000").http_client();
    let action1 = Action::put(crux, None);
    let action2 = Action::put(psql, None);
    let action3 = Action::put(mysql, None);

    let _ = client.tx_log(vec![action1, action2, action3]).await;

    let query_is_sql = Query::find(vec!["?p1", "?n"])
        .unwrap()
        .where_clause(vec!["?p1 :name ?n", "?p1 :is-sql true"])
        .unwrap()
        .build();

    let is_sql = client.query(query_is_sql.unwrap()).await;

    let query_is_no_sql = Query::find(vec!["?p1", "?n", "?s"])
        .unwrap()
        .where_clause(vec!["?p1 :name ?n", "?p1 :is-sql ?s", "?p1 :is-sql false"])
        .unwrap()
        .with_full_results()
        .build();

    let is_no_sql = client.query(query_is_no_sql.unwrap()).await;
}

#[derive(Debug, Clone, Serialize)]
#[allow(non_snake_case)]
pub struct Database {
    crux__db___id: CruxId,
    name: String,
    is_sql: bool
}

Note use tokio::prelude::*; and #[tokio::main] \n async fn main().

Enabling feature time_as_str

It is possible to use receive the responses (TxLogResponse, EntityTxResponse, EntityHistoryElement) time dates as Strings, to do so you have to enable feature time_as_str:

transistor = { version = "2.1.2", features = ["time_as_str"] }

Possible Features

mock = ["mockito"] -> http_mock()
time_as_str = [] -> DataTime types become Strings
async = ["tokio", "futures"] -> async/await

Dependencies

A strong dependency of this crate is the edn-rs crate, as many of the return types are in the Edn format, also the edn-derive. The sync http client is reqwest with blocking feature enabled. Chrono for time values that can be DateTime<Utc>, for inserts, and DateTime<FixedOffset>, for reads, and mockito for feature mock.

Licensing

This project is licensed under LGPP-3.0 (GNU Lesser General Public License v3.0).

Comments
  • Create CircleCI config

    Create CircleCI config

    Hi! So this CircleCI config should be working, but for whatever reason the entity_history test fails on the latest commit. Was this already the case or is there a bug in my config? If the latter, let me know and I will fix it. :>

    Resolves #53

    opened by PurpleMyst 6
  • TXLogsResponse has the wrong format for field tx__event___tx_events

    TXLogsResponse has the wrong format for field tx__event___tx_events

    Field tx__event___tx_events is deserialized by tx__event___tx_events: edn.get(":crux.tx.event/tx-events").map(|edn| edn.to_vec().unwrap()) which has an expected format of Option<Vec<String>>, like Some(vec![":hello", "1", "3/4"]). However the current format is:

    Some([
      "[Key(":hello"), Int("1"), Rational("3/4")]"
    ])
    
    • Checked with edn-rs and there it seems to work fine.
    bug help wanted good first issue Rust 
    opened by naomijub 5
  • Implement tx-log MATCH

    Implement tx-log MATCH

    https://opencrux.com/docs#transactions-match

    There should be a trait TxLogMatch that when implemented reads the id field containing a CruxId and receives an edn containing the map of properties and values to be checked:

    • The PUT: [:crux.tx/put { :crux.db/id :jorge-3, :first-name "Michael", :last-name "Jorge", }].

    The MATCH:

    [:crux.tx/match
      :jorge-3
     {:first-name "Michael"}]
    
    help wanted derive macros Rust 
    opened by naomijub 5
  • `entity_tx` panics if the id provided is non-existent

    `entity_tx` panics if the id provided is non-existent

    Currently if I do something like:

    let client = Crux::new("crux", "3000").http_client();
    let tx_body = client.entity_tx(CruxId::new("unknown-id-here"))
    

    It panics with 'called Result::unwrap() on an Err value: ParseError(Invalid)', /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823/transistor-2.0.0/src/types/response.rs:135:74'.

    A request to http://crux:3000/entity-tx with {:eid :unknown-id-here} will respond with a status of 404 and a body of nil, which should probably be handled gracefully with an error rather than panicking.

    opened by jarhodes314 2
  • Examples fail when running in the CI due to Docker requirements

    Examples fail when running in the CI due to Docker requirements

    Describe the bug Cargo run --example usually fails when running in the CI and they need to be run individually which requires a lot of extra compile time. Converting them to cargo test --examples should solve this problem and can be done by using mock feature.

    To Reproduce Steps to reproduce the behavior:

    1. Go to https://travis-ci.com/github/naomijub/transistor/builds
    2. Scroll down
    3. See the many failed build that occurred in examples run

    Expected behavior Not fail because of Docker requirements

    bug enhancement good first issue CI test 
    opened by naomijub 2
  • Change CI to CircleCI

    Change CI to CircleCI

    Is your feature request related to a problem or issue? Please describe or annex. Travis CI is too buggy

    Purpose the solution you'd like Change to CircleCI A clear and concise description of what you want to happen.

    Describe alternatives you've considered A clear and concise description of any alternative solutions or features you've considered.

    Additional context Our PRs always fail due to test problems in Travis CI Add any other context or screenshots about the feature request here.

    bug good first issue CI 
    opened by naomijub 2
  • Crux links are broken in docs

    Crux links are broken in docs

    Describe the bug Crux docs links are broken due to current Crux docs update (https://opencrux.com/reference/get-started.html)

    Expected behavior links changed from https://opencrux.com/docs to https://opencrux.com/reference

    opened by naomijub 2
  • Error treatment

    Error treatment

    Currently Errors are not unified and different error strategies are being employed. We should unify Errors to return a generic Error containing all cases:

    • Edn parse Errors.
    • Reqwest Errors.
    • Response Parse Error / Stacktrace
    enhancement errors 
    opened by naomijub 2
  • find_by_aggregates should support non-aggregative elements

    find_by_aggregates should support non-aggregative elements

    Describe the bug find_by_aggregates should support non-aggregative elements. Include element Aggregate::NonAggregative that receives a simple query element, like ?p.

    Expected behavior :find [(min ?p) ?n ?p]

    opened by naomijub 1
  • Reqwest StatusCode != 2xx

    Reqwest StatusCode != 2xx

    Handle cases when http function returns a code different than 2XX Created from https://github.com/naomijub/transistor/issues/64 and PR https://github.com/naomijub/transistor/pull/65

    opened by naomijub 1
  • Issue 64

    Issue 64

    • [x] Fixed error. handling when entity functions do not return 2XX.
    • [x] Upgrade edn-rs
    • [x] Removes unnecessary check if id starts with ":"
    • [ ] Fix error handling for status code != 2xx in other functions

    REMOVED:

    if !crux_id.starts_with(":") {
         return Ok(edn!({:status ":bad-request", :message "ID required", :code 400}));
    }
    
    opened by naomijub 1
  • Update to XTDB

    Update to XTDB

    Docker Container: https://hub.docker.com/r/juxt/xtdb-in-memory/tags?page=1&ordering=last_updated HTTP reference: https://xtdb.com/reference/1.19.0-beta1/http.html

    opened by naomijub 1
  • Query Rules not supported

    Query Rules not supported

    Is your feature request related to a problem or issue? Please describe or annex. https://www.opencrux.com/reference/queries.html#rules

    {:find [?e2]
      :where [(follow ?e1 ?e2)]
      :args [{?e1 :1}]
      :rules [[(follow ?e1 ?e2)
               [?e1 :follow ?e2]]
              [(follow ?e1 ?e2)
               [?e1 :follow ?t]
               (follow ?t ?e2)]]}
    

    ** Purpose the solution you'd like** Create Query function rules

    opened by naomijub 0
Owner
Julia Naomi
Rust, Games, Kotlin, Elixir
Julia Naomi
An API Wrapper for https://paste.myst.rs written in rust

PasteMyst.RS pastemyst-rs is an api wrapper for pastemyst written in Rust. ⚠ This package is under development ⚠ Sample usage To get a paste from past

ANF Studios 14 Nov 28, 2021
Cassandra DB native client written in Rust language. Find 1.x versions on https://github.com/AlexPikalov/cdrs/tree/v.1.x Looking for an async version? - Check WIP https://github.com/AlexPikalov/cdrs-async

CDRS CDRS is looking for maintainers CDRS is Apache Cassandra driver written in pure Rust. ?? Looking for an async version? async-std https://github.c

Alex Pikalov 338 Jan 1, 2023
A web service that generates images of dependency graphs for crates hosted on crates.io

crate-deps A web service that generates images of dependency graphs for crates hosted on crates.io This project is built entirely in Rust using these

Corey Farwell 20 Nov 9, 2020
crates is an extension aims to help people to manage their dependencies for rust (crates.io & TOML).

crates Hello Rust & VSCode lovers, This is crates, an extension for crates.io dependencies. Aims helping developers to manage dependencies while using

Seray Uzgur 164 Jan 4, 2023
"Crates for Cheese" is a Rust collection library of those crates I consider a useful "extended standard".

cfc The purpose of this library is to provide a minimal list of currated crates which enhance the std library. In addition, most or all crates in this

null 0 Dec 23, 2021
Security advisory database for Rust crates published through crates.io

RustSec Advisory Database The RustSec Advisory Database is a repository of security advisories filed against Rust crates published via https://crates.

RustSec 682 Jan 1, 2023
Crates - A collection of open source Rust crates from iqlusion

iqlusion crates ?? This repository contains a set of Apache 2.0-licensed packages (a.k.a. "crates") for the Rust programming language, contributed to

iqlusion 335 Dec 26, 2022
Download .crate files of all versions of all crates from crates.io

get-all-crates Download .crate files of all versions of all crates from crates.io. Useful for things like noisy-clippy which need to analyze the sourc

David Tolnay 18 Jan 3, 2023
A gui tool written in Dioxus to make it easy to release a workspace of crates to crates.io

Easy-Release: a visual tool for releasing workspaces of libraries A work-in-progress GUI for releasing a large workspace of crates manually, but easil

Jon Kelley 13 Jan 18, 2023
Crates Registry is a tool for serving and publishing crates and serving rustup installation in offline networks.

Crates Registry Description Crates Registry is a tool for serving and publishing crates and serving rustup installation in offline networks. (like Ver

TalYRoni 5 Jul 6, 2023
Mirror of https://gitlab.redox-os.org/redox-os/redox

Redox is an operating system written in Rust, a language with focus on safety and high performance. Redox, following the microkernel design, aims to b

Redox OS 14.3k Jan 3, 2023
This project now lives on in a rewrite at https://gitlab.redox-os.org/redox-os/parallel

MIT/Rust Parallel: A Command-line CPU Load Balancer Written in Rust This is an attempt at recreating the functionality of GNU Parallel, a work-stealer

Michael Murphy 1.2k Nov 20, 2022
Mirror of https://gitlab.com/mmstick/tv-renamer

Build Status: Features Written safely in the Rust programming language Features both a command-line and GTK3 interface Support for Templates to define

Michael Murphy 143 Sep 24, 2022
Mirror of https://gitlab.redox-os.org/redox-os/ion

Introduction Ion is a modern system shell that features a simple, yet powerful, syntax. It is written entirely in Rust, which greatly increases the ov

Redox OS 1.3k Jan 4, 2023
Federated blogging application, thanks to ActivityPub (now on https://git.joinplu.me/ — this is just a mirror)

Plume Website — Documentation — Contribute — Instances list Plume is a federated blogging engine, based on ActivityPub. It is written in Rust, with th

Plume 1.9k Jan 8, 2023
Mirror of https://gitlab.redox-os.org/redox-os/termion

Documentation Examples Changelog Tutorial Termion is a pure Rust, bindless library for low-level handling, manipulating and reading information about

Redox OS 1.9k Dec 31, 2022
Mirror of https://gitlab.redox-os.org/redox-os/rusttype

RustType RustType is a pure Rust alternative to libraries like FreeType. The current capabilities of RustType: Reading OpenType formatted fonts and fo

Redox OS 567 Dec 5, 2022
Natural language detection library for Rust. Try demo online: https://www.greyblake.com/whatlang/

Whatlang Natural language detection for Rust with focus on simplicity and performance. Content Features Get started Documentation Supported languages

Sergey Potapov 805 Dec 28, 2022
A fast static site generator in a single binary with everything built-in. https://www.getzola.org

zola (né Gutenberg) A fast static site generator in a single binary with everything built-in. Documentation is available on its site or in the docs/co

Zola 10.1k Jan 5, 2023
https://getrust.tech

learn-rust 这是一个分享Rust学习资料的在线学习网站 https://getrust.tech ?? 。 微信群二维码过期你可以添加我微信: AA996DD 目的是什么 ❓ 通过连载文章的形式帮助有一定其他语言编程基础的人快速学习和入门 Rust 内容包括 Rust 基础、内置库、web

Jarvib Ding 313 Dec 18, 2022