Current section
Files
Jump to
Current section
Files
src/radish/sorted_set.gleam
import gleam/int
import gleam/list
import gleam/float
import gleam/result
import radish/resp
import radish/error
import radish/utils.{execute}
import radish/command/sorted_set as command
pub type Score {
Infinity
Double(Float)
NegativeInfinity
}
/// see [here](https://redis.io/commands/zadd)!
pub fn add_new(
client,
key: String,
members: List(#(String, Score)),
timeout: Int,
) {
command.add_new(key, list.map(members, encode_member))
|> execute(client, _, timeout)
|> result.map(fn(value) {
case value {
[resp.Integer(n)] -> Ok(n)
_ -> Error(error.RESPError)
}
})
|> result.flatten
}
/// see [here](https://redis.io/commands/zadd)!
pub fn upsert(
client,
key: String,
members: List(#(String, Score)),
timeout: Int,
) {
command.upsert(key, list.map(members, encode_member))
|> execute(client, _, timeout)
|> result.map(fn(value) {
case value {
[resp.Integer(n)] -> Ok(n)
_ -> Error(error.RESPError)
}
})
|> result.flatten
}
/// see [here](https://redis.io/commands/zadd)!
pub fn upsert_only_lower_scores(
client,
key: String,
members: List(#(String, Score)),
timeout: Int,
) {
command.upsert_only_lower_scores(key, list.map(members, encode_member))
|> execute(client, _, timeout)
|> result.map(fn(value) {
case value {
[resp.Integer(n)] -> Ok(n)
_ -> Error(error.RESPError)
}
})
|> result.flatten
}
/// see [here](https://redis.io/commands/zadd)!
pub fn upsert_only_higher_scores(
client,
key: String,
members: List(#(String, Score)),
timeout: Int,
) {
command.upsert_only_higher_scores(key, list.map(members, encode_member))
|> execute(client, _, timeout)
|> result.map(fn(value) {
case value {
[resp.Integer(n)] -> Ok(n)
_ -> Error(error.RESPError)
}
})
|> result.flatten
}
/// see [here](https://redis.io/commands/zadd)!
pub fn update(
client,
key: String,
members: List(#(String, Score)),
timeout: Int,
) {
command.update(key, list.map(members, encode_member))
|> execute(client, _, timeout)
|> result.map(fn(value) {
case value {
[resp.Integer(n)] -> Ok(n)
_ -> Error(error.RESPError)
}
})
|> result.flatten
}
/// see [here](https://redis.io/commands/zadd)!
pub fn update_only_lower_scores(
client,
key: String,
members: List(#(String, Score)),
timeout: Int,
) {
command.update_only_lower_scores(key, list.map(members, encode_member))
|> execute(client, _, timeout)
|> result.map(fn(value) {
case value {
[resp.Integer(n)] -> Ok(n)
_ -> Error(error.RESPError)
}
})
|> result.flatten
}
/// see [here](https://redis.io/commands/zadd)!
pub fn update_only_higher_scores(
client,
key: String,
members: List(#(String, Score)),
timeout: Int,
) {
command.update_only_higher_scores(key, list.map(members, encode_member))
|> execute(client, _, timeout)
|> result.map(fn(value) {
case value {
[resp.Integer(n)] -> Ok(n)
_ -> Error(error.RESPError)
}
})
|> result.flatten
}
/// see [here](https://redis.io/commands/zincrby)!
pub fn incr_by(
client,
key: String,
member: String,
change_in_score: Score,
timeout: Int,
) {
command.incr_by(key, member, encode_score(change_in_score))
|> execute(client, _, timeout)
|> result.map(map_score)
|> result.flatten
}
/// see [here](https://redis.io/commands/zcard)!
pub fn card(client, key: String, timeout: Int) {
command.card(key)
|> execute(client, _, timeout)
|> result.map(fn(value) {
case value {
[resp.Integer(n)] -> Ok(n)
_ -> Error(error.RESPError)
}
})
|> result.flatten
}
/// see [here](https://redis.io/commands/zcount)!
pub fn count(client, key: String, min: Score, max: Score, timeout: Int) {
command.count(key, encode_score(min), encode_score(max))
|> execute(client, _, timeout)
|> result.map(fn(value) {
case value {
[resp.Integer(n)] -> Ok(n)
_ -> Error(error.RESPError)
}
})
|> result.flatten
}
/// see [here](https://redis.io/commands/zscore)!
pub fn score(client, key: String, member: String, timeout: Int) {
command.score(key, member)
|> execute(client, _, timeout)
|> result.map(fn(value) {
case value {
[resp.Infinity] -> Ok(Infinity)
[resp.Double(score)] -> Ok(Double(score))
[resp.NegativeInfinity] -> Ok(NegativeInfinity)
[resp.Null] -> Error(error.NotFound)
_ -> Error(error.RESPError)
}
})
|> result.flatten
}
/// see [here](https://redis.io/commands/zscan)!
pub fn scan(client, key: String, cursor: Int, count: Int, timeout: Int) {
command.scan(key, cursor, count)
|> execute(client, _, timeout)
|> result.map(fn(value) {
case value {
[resp.Array([resp.BulkString(new_cursor_str), resp.Array(members)])] ->
case int.parse(new_cursor_str) {
Ok(new_cursor) -> {
use array <- result.then(
members
|> list.sized_chunk(2)
|> list.try_map(fn(item) {
case item {
[resp.BulkString(member), resp.BulkString(score)] ->
case decode_score(score) {
Ok(score) -> Ok(#(member, score))
_ -> Error(error.RESPError)
}
_ -> Error(error.RESPError)
}
}),
)
Ok(#(array, new_cursor))
}
Error(Nil) -> Error(error.RESPError)
}
_ -> Error(error.RESPError)
}
})
|> result.flatten
}
/// see [here](https://redis.io/commands/zscan)!
pub fn scan_pattern(
client,
key: String,
cursor: Int,
pattern: String,
count: Int,
timeout: Int,
) {
command.scan_pattern(key, cursor, pattern, count)
|> execute(client, _, timeout)
|> result.map(fn(value) {
case value {
[resp.Array([resp.BulkString(new_cursor_str), resp.Array(members)])] ->
case int.parse(new_cursor_str) {
Ok(new_cursor) -> {
use array <- result.then(
members
|> list.sized_chunk(2)
|> list.try_map(fn(item) {
case item {
[resp.BulkString(member), resp.BulkString(score)] ->
case decode_score(score) {
Ok(score) -> Ok(#(member, score))
_ -> Error(error.RESPError)
}
_ -> Error(error.RESPError)
}
}),
)
Ok(#(array, new_cursor))
}
Error(Nil) -> Error(error.RESPError)
}
_ -> Error(error.RESPError)
}
})
|> result.flatten
}
/// see [here](https://redis.io/commands/zrem)!
pub fn rem(client, key: String, members: List(String), timeout: Int) {
command.rem(key, members)
|> execute(client, _, timeout)
|> result.map(fn(value) {
case value {
[resp.Integer(n)] -> Ok(n)
_ -> Error(error.RESPError)
}
})
|> result.flatten
}
/// see [here](https://redis.io/commands/zrandmember)!
pub fn random_members(client, key: String, count: Int, timeout: Int) {
command.random_members(key, count)
|> execute(client, _, timeout)
|> result.map(fn(value) {
case value {
[resp.Array(members)] -> {
use array <- result.then(
members
|> list.try_map(fn(item) {
case item {
resp.Array([resp.BulkString(member), score]) ->
case map_score([score]) {
Ok(score) -> Ok(#(member, score))
_ -> Error(error.RESPError)
}
_ -> Error(error.RESPError)
}
}),
)
Ok(array)
}
_ -> Error(error.RESPError)
}
})
|> result.flatten
}
/// see [here](https://redis.io/commands/zrank)!
pub fn rank(client, key: String, member: String, timeout: Int) {
command.rank(key, member)
|> execute(client, _, timeout)
|> result.map(fn(value) {
case value {
[resp.Array([resp.Integer(rank), score])] ->
[score]
|> map_score
|> result.map(fn(score) { #(rank, score) })
[resp.Null] -> Error(error.NotFound)
_ -> Error(error.RESPError)
}
})
|> result.flatten
}
/// see [here](https://redis.io/commands/zrevrank)!
pub fn reverse_rank(client, key: String, member: String, timeout: Int) {
command.reverse_rank(key, member)
|> execute(client, _, timeout)
|> result.map(fn(value) {
case value {
[resp.Array([resp.Integer(rank), score])] ->
[score]
|> map_score
|> result.map(fn(score) { #(rank, score) })
[resp.Null] -> Error(error.NotFound)
_ -> Error(error.RESPError)
}
})
|> result.flatten
}
/// see [here](https://redis.io/commands/zpopmin)!
pub fn pop_min(client, key: String, count: Int, timeout: Int) {
command.pop_min(key, count)
|> execute(client, _, timeout)
|> result.map(fn(value) {
case value {
[resp.Array(members)] -> {
use array <- result.then(
members
|> list.try_map(fn(item) {
case item {
resp.Array([resp.BulkString(member), score]) ->
case map_score([score]) {
Ok(score) -> Ok(#(member, score))
_ -> Error(error.RESPError)
}
_ -> Error(error.RESPError)
}
}),
)
Ok(array)
}
_ -> Error(error.RESPError)
}
})
|> result.flatten
}
/// see [here](https://redis.io/commands/zpopmax)!
pub fn pop_max(client, key: String, count: Int, timeout: Int) {
command.pop_max(key, count)
|> execute(client, _, timeout)
|> result.map(fn(value) {
case value {
[resp.Array(members)] -> {
use array <- result.then(
members
|> list.try_map(fn(item) {
case item {
resp.Array([resp.BulkString(member), score]) ->
case map_score([score]) {
Ok(score) -> Ok(#(member, score))
_ -> Error(error.RESPError)
}
_ -> Error(error.RESPError)
}
}),
)
Ok(array)
}
_ -> Error(error.RESPError)
}
})
|> result.flatten
}
fn encode_member(member: #(String, Score)) {
#(member.0, encode_score(member.1))
}
fn encode_score(score: Score) {
case score {
Infinity -> "+inf"
NegativeInfinity -> "-inf"
Double(score) -> float.to_string(score)
}
}
fn map_score(score: List(resp.Value)) {
case score {
[resp.Infinity] -> Ok(Infinity)
[resp.Double(score)] -> Ok(Double(score))
[resp.NegativeInfinity] -> Ok(NegativeInfinity)
_ -> Error(error.RESPError)
}
}
fn decode_score(score: String) {
case score {
"+inf" | "inf" -> Ok(Infinity)
"-inf" -> Ok(NegativeInfinity)
_ ->
case int.parse(score) {
Ok(score) ->
score
|> int.to_float
|> Double
|> Ok
Error(Nil) ->
score
|> float.parse
|> result.map(Double)
}
}
}