Skip to main content

Rust

Connect to Blink Store from Rust using the standard library plus the base64 crate.


Prerequisites

  • Rust 1.70+ (install from rustup.rs)
  • base64 = "0.22" in Cargo.toml
  • Blink Store server running (Installation)

Interactive client

A complete REPL client. Add base64 = "0.22" to your [dependencies], then save as src/main.rs.

use base64::Engine;
use std::io::{self, BufRead, BufReader, Write};
use std::net::TcpStream;

fn main() -> io::Result<()> {
let addr = std::env::args()
.nth(1)
.unwrap_or_else(|| "127.0.0.1:8765".to_string());

let mut stream = TcpStream::connect(&addr)?;
let mut reader = BufReader::new(stream.try_clone()?);

eprintln!("Connected to {addr}");
eprintln!("Commands: SET <key> <value> | GET <key> | DELETE <key> | USAGE | QUIT\n");

let stdin = io::stdin();
for line in stdin.lock().lines() {
let cmd = line?.trim().to_string();
if cmd.is_empty() {
continue;
}
if cmd.eq_ignore_ascii_case("QUIT") {
break;
}

writeln!(stream, "{cmd}")?;
stream.flush()?;

let mut resp = String::new();
reader.read_line(&mut resp)?;
let resp = resp.trim();

if let Some(b64) = resp.strip_prefix("VALUE ") {
match base64::engine::general_purpose::STANDARD.decode(b64.trim()) {
Ok(bytes) => println!("{}", String::from_utf8_lossy(&bytes)),
Err(_) => println!("{resp}"),
}
} else {
println!("{resp}");
}
}
Ok(())
}

Run:

cargo run
Connected to 127.0.0.1:8765
Commands: SET <key> <value> | GET <key> | DELETE <key> | USAGE | QUIT

SET framework rust
OK
GET framework
rust
QUIT

One-off commands

A reusable helper function:

use base64::Engine;
use std::io::{BufRead, BufReader, Write};
use std::net::TcpStream;

fn blink(command: &str, addr: &str) -> Result<String, Box<dyn std::error::Error>> {
let mut stream = TcpStream::connect(addr)?;
let mut reader = BufReader::new(stream.try_clone()?);

writeln!(stream, "{command}")?;
stream.flush()?;

let mut resp = String::new();
reader.read_line(&mut resp)?;
let resp = resp.trim();

if let Some(b64) = resp.strip_prefix("VALUE ") {
let bytes = base64::engine::general_purpose::STANDARD.decode(b64.trim())?;
Ok(String::from_utf8(bytes)?)
} else {
Ok(resp.to_string())
}
}

fn main() {
let addr = "127.0.0.1:8765";
blink("SET version 1.0", addr).unwrap();
let val = blink("GET version", addr).unwrap();
println!("{val}"); // → 1.0
}

Async client (Tokio)

For async applications, use tokio::net::TcpStream:

use base64::Engine;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::net::TcpStream;

async fn blink(command: &str, addr: &str) -> Result<String, Box<dyn std::error::Error>> {
let stream = TcpStream::connect(addr).await?;
let (reader, mut writer) = stream.into_split();
let mut reader = BufReader::new(reader);

writer.write_all(format!("{command}\n").as_bytes()).await?;
writer.flush().await?;

let mut resp = String::new();
reader.read_line(&mut resp).await?;
let resp = resp.trim();

if let Some(b64) = resp.strip_prefix("VALUE ") {
let bytes = base64::engine::general_purpose::STANDARD.decode(b64.trim())?;
Ok(String::from_utf8(bytes)?)
} else {
Ok(resp.to_string())
}
}

Add to Cargo.toml: tokio = { version = "1", features = ["full"] } and base64 = "0.22".


Key concepts

ConceptRust API
TCP connectionTcpStream::connect(addr)
Line readingBufReader::new(stream).read_line()
Base64 decodebase64::engine::general_purpose::STANDARD.decode()
Error handlingResult<T, E> — no panics