Current section
Files
Jump to
Current section
Files
src/gluon.gleam
import gleam/erlang/process.{Subject}
import gleam/bit_builder
import tcp_client.{ClientMessage, Close, ReceiveMessage, SendMessage}
import gleam/bit_string.{to_string}
import gleam/string.{drop_left, drop_right}
import gleam/result.{replace_error, try}
import helpers.{attempt, generate_regex, replace_with_regex}
import gleam/int
type Socket =
#(Subject(ClientMessage), Subject(ClientMessage))
pub fn main(host: String, port: Int) {
tcp_client.init(host, port)
}
fn parse_str_resp(resp: String) {
drop_left(resp, 1)
|> drop_right(1)
}
fn parse_bulk_str_resp(resp: String) {
drop_left(resp, 3)
|> drop_right(1)
}
fn receive(receiver: Subject(ClientMessage)) -> Result(String, String) {
use resp <- attempt(
process.receive(receiver, 200),
"Failed to receive response.",
)
case resp {
ReceiveMessage(resp) ->
to_string(resp)
|> replace_error("Failed to parse response.")
_ -> Error("Unknown response")
}
}
pub fn send_command(socket: Socket, command: String) -> Result(String, String) {
let #(sender, receiver) = socket
process.send(sender, SendMessage(bit_builder.from_string(command <> "\r\n")))
use resp <- try(receive(receiver))
case resp {
"$-1" <> _ -> Ok("")
"$" <> _ -> Ok(parse_bulk_str_resp(resp))
"*" <> _ -> Ok(parse_bulk_str_resp(resp))
"+" <> _ -> Ok(parse_str_resp(resp))
":" <> _ -> Ok(parse_str_resp(resp))
"-" <> _ -> Error(parse_str_resp(resp))
_ -> Error("Unknown response: " <> resp)
}
}
pub fn get(socket: Socket, key: String) -> Result(String, String) {
send_command(socket, "GET " <> key)
}
pub fn del(socket: Socket, key: String) -> Result(Int, String) {
use response <- try(send_command(socket, "DEL " <> key))
replace_error(int.parse(response), "Failed to parse response.")
}
pub fn set(socket: Socket, key: String, value: String) -> Result(String, String) {
send_command(socket, "SET " <> key <> " " <> value)
}
pub fn ping(socket: Socket) -> Result(String, String) {
send_command(socket, "PING")
}
pub fn lpush(socket: Socket, key: String, value: String) -> Result(Int, String) {
use response <- try(send_command(socket, "LPUSH " <> key <> " " <> value))
replace_error(int.parse(response), "Failed to parse response.")
}
pub fn llen(socket: Socket, key: String) -> Result(Int, String) {
use response <- try(send_command(socket, "LLEN " <> key))
replace_error(int.parse(response), "Failed to parse response.")
}
pub fn lrange(
socket: Socket,
key: String,
start: Int,
stop: Int,
) -> Result(List(String), String) {
use response <- try(send_command(
socket,
"LRANGE " <> key <> " " <> int.to_string(start) <> " " <> int.to_string(
stop,
),
))
use re <- try(generate_regex("(\\$[0-9]+\r\n)+"))
let response = replace_with_regex(response, re, "")
response
|> string.split("\r\n")
|> Ok
}
pub fn close(socket) {
let #(sender, _) = socket
process.send(sender, Close)
Ok(Nil)
}