Skip to content

Commit

Permalink
better errors, clippy, ci (#14)
Browse files Browse the repository at this point in the history
* clippy, better logs

* ci changes

* v1.3.1 cargo bump

* bufreader lines not returning broken pipe
  • Loading branch information
dadleyy authored Aug 31, 2022
1 parent 91017ac commit 80e2639
Show file tree
Hide file tree
Showing 12 changed files with 252 additions and 41 deletions.
17 changes: 16 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,19 @@ jobs:
run: rustup toolchain install stable
- name: use-stable
run: rustup default stable
- name: add-clippy
run: rustup component add clippy
- name: build-sync
run: cargo build
- name: test-sync
run: cargo test
env:
REDIS_HOST: localhost
REDIS_PORT: ${{ job.services.redis.ports[6379] }}
- name: "clippy: acl"
run: cargo clippy --features acl
- name: "clippy: async"
run: cargo clippy --features kramer-async
stable-async:
runs-on: ubuntu-latest
services:
Expand Down Expand Up @@ -95,4 +101,13 @@ jobs:
env:
REDIS_HOST: localhost
REDIS_PORT: ${{ job.services.redis.ports[6379] }}

release:
runs-on: ubuntu-latest
needs: ["stable-sync", "stable-async"]
steps:
- uses: actions/checkout@v2
- name: release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
generate_release_notes: true
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "kramer"
version = "1.3.0"
version = "1.3.1"
authors = ["Danny Hadley <dadleyy@gmail.com>"]
edition = "2018"
license = "MIT"
Expand Down
11 changes: 11 additions & 0 deletions src/acl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,30 @@ pub struct SetUser<S>
where
S:,
{
/// The name of the ACL entry.
pub name: S,

/// An optional password that will be added to the acl command.
pub password: Option<S>,

/// The set of commands the ACL entry should have the ability to execute.
pub commands: Option<S>,

/// The set of keys the ACL entry should have access to.
pub keys: Option<S>,
}

/// Redis acl commands.
#[cfg(feature = "acl")]
#[derive(Debug)]
pub enum AclCommand<S>
where
S: std::fmt::Display,
{
/// Wraps the `SetUser` struct for a type implementing display.
SetUser(SetUser<S>),

/// Wraps the `DelUser` struct for a type implementing display.
DelUser(Arity<S>),
}

Expand Down
42 changes: 27 additions & 15 deletions src/async_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,38 @@ use async_std::prelude::*;

use std::io::{Error, ErrorKind};

/// Attempts to read RESP standard messages (newline delimeters), parsing into our `ResponseValue`
/// enum.
pub async fn read<C>(connection: C) -> Result<Response, Error>
where
C: async_std::io::Read + std::marker::Unpin,
{
let mut lines = async_std::io::BufReader::new(connection).lines();
let mut reader = async_std::io::BufReader::new(connection);
let mut buffer = String::new();

match readline(lines.next().await) {
match reader.read_line(&mut buffer).await.and_then(|_res| readline(buffer)) {
Ok(ResponseLine::Array(size)) => {
println!("array");
let mut store = Vec::with_capacity(size);

if size == 0 {
return Ok(Response::Array(vec![]));
}

while let Ok(kind) = readline(lines.next().await) {
while store.len() < size {
let mut line_buffer = String::new();

let kind = reader
.read_line(&mut line_buffer)
.await
.and_then(|_res| readline(line_buffer))?;

match kind {
ResponseLine::BulkString(size) => match lines.next().await {
Some(Ok(bulky)) if bulky.len() == size => {
store.push(ResponseValue::String(bulky));
}
_ => break,
},
ResponseLine::BulkString(size) => {
let mut real_value = String::with_capacity(size);
reader.read_line(&mut real_value).await?;
store.push(ResponseValue::String(real_value.trim_end().to_string()));
}
_ => break,
}

Expand All @@ -42,25 +52,26 @@ where
Ok(Response::Array(store))
}
Ok(ResponseLine::BulkString(size)) => {
println!("bulky");
if size < 1 {
return Ok(Response::Item(ResponseValue::Empty));
}

let out = lines
.next()
.await
.ok_or_else(|| Error::new(ErrorKind::Other, "no line to work with"))??;
let mut real_value = String::with_capacity(size);
reader.read_line(&mut real_value).await?;

Ok(Response::Item(ResponseValue::String(out)))
Ok(Response::Item(ResponseValue::String(real_value.trim_end().to_string())))
}
Ok(ResponseLine::Null) => Ok(Response::Item(ResponseValue::Empty)),
Ok(ResponseLine::SimpleString(simple)) => Ok(Response::Item(ResponseValue::String(simple))),
Ok(ResponseLine::SimpleString(simple)) => Ok(Response::Item(ResponseValue::String(simple.trim_end().to_string()))),
Ok(ResponseLine::Integer(value)) => Ok(Response::Item(ResponseValue::Integer(value))),
Ok(ResponseLine::Error(e)) => Err(Error::new(ErrorKind::Other, e)),
Err(e) => Err(e),
}
}

/// An async implementation of a complete message exchange. The provided message will be written to
/// our connection, and a response will be read.
pub async fn execute<C, S>(mut connection: C, message: S) -> Result<Response, Error>
where
S: std::fmt::Display,
Expand All @@ -70,6 +81,7 @@ where
read(connection).await
}

/// An async implementation of opening a tcp connection, and sending a single message.
pub async fn send<S>(addr: &str, message: S) -> Result<Response, Error>
where
S: std::fmt::Display,
Expand Down
19 changes: 19 additions & 0 deletions src/hashes.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,38 @@
use crate::modifiers::{format_bulk_string, Arity, Insertion};

/// `HashCommand` represents the possible redis operations of keys that
/// are a hash type.
#[derive(Debug)]
pub enum HashCommand<S, V>
where
S: std::fmt::Display,
V: std::fmt::Display,
{
/// Deletes fields from a given hash.
Del(S, Arity<S>),

/// Sets the value of a hash for a given key.
Set(S, Arity<(S, V)>, Insertion),

/// Returns the value (or many) stored in a hash at a specific field.
Get(S, Option<Arity<S>>),

/// Returns the length of a string stored at a key within a hash.
StrLen(S, S),

/// Returns the amount of keys in the given hash.
Len(S),

/// Increments a key for the hash by a given amount.
Incr(S, S, i64),

/// Returns all keys for the hash stored at a given key.
Keys(S),

/// Returns all values for the hash stored at a given key.
Vals(S),

/// Checks to see if the given field exists in the hash.
Exists(S, S),
}

Expand Down
37 changes: 37 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![warn(clippy::missing_docs_in_private_items)]

//! An implementation of the [redis protocol specification][redis] with an execution helper using
//! the [`TcpStream`][tcp-stream] provided by [async-std].
//!
Expand Down Expand Up @@ -27,46 +29,61 @@
//! [redis]: https://redis.io/topics/protocol
//! [async-std]: https://github.com/async-rs/async-std
//! [tcp-stream]: https://docs.rs/async-std/0.99.11/async_std/net/struct.TcpStream.html

/// The response module contains parsing logic for redis responses.
mod response;
pub use response::{Response, ResponseLine, ResponseValue};

/// Our async_io module uses async-std.
#[cfg(feature = "kramer-async")]
mod async_io;
#[cfg(feature = "kramer-async")]
pub use async_io::{execute, read, send};

/// Our sync_io module uses methods directly from ruststd.
#[cfg(not(feature = "kramer-async"))]
mod sync_io;
#[cfg(not(feature = "kramer-async"))]
pub use sync_io::{execute, read, send};

/// To consolidate the variants of any given command, this module exposes generic and common
/// enumerations that extend the reason of any given enum.
mod modifiers;
use modifiers::format_bulk_string;
pub use modifiers::{humanize_command, Arity, Insertion, Side};

/// List related enums.
mod lists;
pub use lists::ListCommand;

/// ACL related enums.
#[cfg(feature = "acl")]
pub mod acl;
#[cfg(feature = "acl")]
pub use acl::{AclCommand, SetUser};

/// Set related enums.
mod sets;
pub use sets::SetCommand;

/// String related enums.
mod strings;
pub use strings::StringCommand;

/// Hash related enums.
mod hashes;
pub use hashes::HashCommand;

/// Redis authorization supports password and user/password authorization schemes.
#[derive(Debug)]
pub enum AuthCredentials<S>
where
S: std::fmt::Display,
{
/// Builds an AUTH command with only a password.
Password(S),

/// Builds an AUTH command with a password and a user.
User((S, S)),
}

Expand All @@ -87,22 +104,42 @@ where
}
}

/// The main `Command` enum here represents all of the different variants of redis commands
/// that are supported by the library.
#[derive(Debug)]
pub enum Command<S, V>
where
S: std::fmt::Display,
V: std::fmt::Display,
{
/// Returns the kets matching the pattern.
Keys(S),

/// Removes one or more keys.
Del(Arity<S>),

/// Commands for checking the presence of keys.
Exists(Arity<S>),

/// Commands for working with list keys.
List(ListCommand<S, V>),

/// Commands for working with string keys.
Strings(StringCommand<S, V>),

/// Commands for working with hash keys.
Hashes(HashCommand<S, V>),

/// Commands for working with set keys.
Sets(SetCommand<S, V>),

/// The echo command will return the contents of the string sent.
Echo(S),

/// Auth commands
Auth(AuthCredentials<S>),

/// ACL commands; currently unstable.
#[cfg(feature = "acl")]
Acl(AclCommand<S>),
}
Expand Down
22 changes: 20 additions & 2 deletions src/lists.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,37 @@
use crate::modifiers::{format_bulk_string, Arity, Insertion, Side};

/// Lists.
#[derive(Debug)]
pub enum ListCommand<S, V>
where
S: std::fmt::Display,
V: std::fmt::Display,
{
/// List length.
Len(S),

/// Adds an item to the list on the correct side.
Push((Side, Insertion), S, Arity<V>),

/// Pops an item from the side of a list with the option for a timeout.
Pop(Side, S, Option<(Option<Arity<S>>, u64)>),

/// Removes items from a list.
Rem(S, V, u64),

/// Returns the index of an item in a list.
Index(S, i64),

/// Sets the value of an index of a list.
Set(S, u64, V),

/// Inserts a value into a list.
Insert(S, Side, V, V),

/// Truncate a list.
Trim(S, i64, i64),

/// Return the length of a list.
Range(S, i64, i64),
}

Expand Down Expand Up @@ -78,8 +96,8 @@ where
ListCommand::Len(key) => write!(formatter, "*2\r\n$4\r\nLLEN\r\n{}", format_bulk_string(key)),
ListCommand::Pop(side, key, block) => {
let (cmd, ext, kc) = match (side, block) {
(Side::Left, None) => ("LPOP", format!(""), 0),
(Side::Right, None) => ("RPOP", format!(""), 0),
(Side::Left, None) => ("LPOP", "".to_string(), 0),
(Side::Right, None) => ("RPOP", "".to_string(), 0),
(Side::Left, Some((None, timeout))) => ("BLPOP", format_bulk_string(timeout), 1),
(Side::Right, Some((None, timeout))) => ("BRPOP", format_bulk_string(timeout), 1),
(Side::Left, Some((Some(values), timeout))) => {
Expand Down
Loading

0 comments on commit 80e2639

Please sign in to comment.