Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

better errors, clippy, ci #14

Merged
merged 4 commits into from
Aug 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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