Skip to content

Commit 1fcf03b

Browse files
committed
Add KV example.
1 parent c521ded commit 1fcf03b

File tree

6 files changed

+190
-9
lines changed

6 files changed

+190
-9
lines changed

README.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,32 @@ It provides asynchronous client backed by [tokio](https://github.com/tokio-rs/to
2121
- [ ] Election
2222
- [ ] Lock
2323

24+
## Usage
25+
26+
```Rust
27+
use ectdv3::*;
28+
29+
let mut client = Client::connect(["localhost:2379"]).await?;
30+
// put kv
31+
client.put("foo", "bar", None).await?;
32+
// get kv
33+
let resp = client.get("foo", None).await?;
34+
if let Some(kv) = resp.kvs().first() {
35+
println!("Get kv: {{{}: {}}}", kv.key_str()?, kv.value_str()?);
36+
}
37+
```
38+
39+
## Examples
40+
41+
Examples can be found in [`examples`](./examples).
42+
2443
## Test
2544

2645
We test this library using etcd 3.4.
2746

2847
Notes that we use a fixed `etcd` server URI (localhost:2379) to connect to etcd server.
2948

30-
## Rust Version
49+
## Rust version requirements
3150

3251
`etcdv3` works on rust `1.39` and above as it requires support for the `async_await`
3352
feature.

examples/kv.rs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
//! KV example.
2+
3+
use etcdv3::*;
4+
5+
#[derive(Debug)]
6+
struct KV {
7+
key: String,
8+
value: String,
9+
}
10+
11+
impl KV {
12+
#[inline]
13+
pub fn new(key: impl Into<String>, value: impl Into<String>) -> Self {
14+
KV {
15+
key: key.into(),
16+
value: value.into(),
17+
}
18+
}
19+
20+
#[inline]
21+
pub fn key(&self) -> &str {
22+
&self.key
23+
}
24+
25+
#[inline]
26+
pub fn value(&self) -> &str {
27+
&self.value
28+
}
29+
}
30+
31+
#[tokio::main]
32+
async fn main() -> Result<(), Error> {
33+
let mut client = Client::connect(["localhost:2379"]).await?;
34+
35+
let alice = KV::new("Alice", "15");
36+
let bob = KV::new("Bob", "20");
37+
let chris = KV::new("Chris", "16");
38+
39+
// put kv
40+
let resp = client.put(alice.key(), alice.value(), None).await?;
41+
println!("put kv: {:?}", alice);
42+
let revison = resp.header().unwrap().revision();
43+
client.put(bob.key(), bob.value(), None).await?;
44+
println!("put kv: {:?}", bob);
45+
client.put(chris.key(), chris.value(), None).await?;
46+
println!("put kv: {:?}", chris);
47+
println!();
48+
49+
// get bob
50+
let resp = client.get(bob.key(), None).await?;
51+
if let Some(kv) = resp.kvs().first() {
52+
println!("Get kv: {{{}: {}}}", kv.key_str()?, kv.value_str()?);
53+
println!();
54+
}
55+
56+
// get all key-value pairs
57+
println!("Get all users:");
58+
let resp = client
59+
.get("", Some(GetOptions::new().with_from_key()))
60+
.await?;
61+
for kv in resp.kvs() {
62+
println!("\t{{{}: {}}}", kv.key_str()?, kv.value_str()?);
63+
}
64+
println!();
65+
66+
// delete chris
67+
let resp = client
68+
.delete(chris.key(), Some(DeleteOptions::new().with_prev_key()))
69+
.await?;
70+
if let Some(kv) = resp.prev_kvs().first() {
71+
println!("Delete kv: {{{}: {}}}", kv.key_str()?, kv.value_str()?);
72+
println!();
73+
}
74+
75+
// transaction
76+
let resp = client.get(alice.key(), None).await?;
77+
if let Some(kv) = resp.kvs().first() {
78+
println!(
79+
"Before transaction: {{{}: {}}}",
80+
kv.key_str()?,
81+
kv.value_str()?
82+
);
83+
}
84+
let txn = Txn::new()
85+
.when(vec![Compare::value(
86+
alice.key(),
87+
CompareOp::Equal,
88+
alice.value(),
89+
)])
90+
.and_then(vec![TxnOp::put(
91+
alice.key(),
92+
"18",
93+
Some(PutOptions::new().with_ignore_lease()),
94+
)]);
95+
println!("transaction: {:?}", txn);
96+
let resp = client.txn(txn).await?;
97+
for op_resp in resp.op_responses() {
98+
println!("transaction resp: {:?}", op_resp);
99+
}
100+
let resp = client.get(alice.key(), None).await?;
101+
if let Some(kv) = resp.kvs().first() {
102+
println!(
103+
"After transaction: {{{}: {}}}",
104+
kv.key_str()?,
105+
kv.value_str()?
106+
);
107+
}
108+
println!();
109+
110+
// compact
111+
client
112+
.compact(revison, Some(CompactionOptions::new().with_physical()))
113+
.await?;
114+
println!("Compact to revision {}.", revison);
115+
116+
Ok(())
117+
}

src/client.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,12 @@ impl Client {
117117
#[cfg(test)]
118118
mod tests {
119119
use super::*;
120-
use crate::{get_client, Compare, CompareOp, EventType, TxnOp, TxnOpResponse};
120+
use crate::{Compare, CompareOp, EventType, TxnOp, TxnOpResponse};
121+
122+
/// Get client for testing.
123+
async fn get_client() -> Result<Client> {
124+
Client::connect(["localhost:2379"]).await
125+
}
121126

122127
#[tokio::test]
123128
async fn test_put() -> Result<()> {

src/error.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Etcd Client Error handling.
22
33
use std::fmt::{Display, Formatter};
4+
use std::str::Utf8Error;
45

56
pub type Result<T> = std::result::Result<T, Error>;
67

@@ -24,6 +25,9 @@ pub enum Error {
2425

2526
/// Watch error
2627
WatchError(String),
28+
29+
/// Utf8Error
30+
Utf8Error(Utf8Error),
2731
}
2832

2933
impl Display for Error {
@@ -36,6 +40,7 @@ impl Display for Error {
3640
Error::TransportError(e) => write!(f, "transport error: {}", e),
3741
Error::GRPCStatus(e) => write!(f, "grep request error: {}", e),
3842
Error::WatchError(e) => write!(f, "watch error: {}", e),
43+
Error::Utf8Error(e) => write!(f, "utf8 error: {}", e),
3944
}
4045
}
4146
}
@@ -69,3 +74,10 @@ impl From<tonic::Status> for Error {
6974
Error::GRPCStatus(e)
7075
}
7176
}
77+
78+
impl From<Utf8Error> for Error {
79+
#[inline]
80+
fn from(e: Utf8Error) -> Self {
81+
Error::Utf8Error(e)
82+
}
83+
}

src/lib.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,3 @@ pub use crate::rpc::watch::{
1515
Event, EventType, WatchFilterType, WatchOptions, WatchResponse, WatchStream, Watcher,
1616
};
1717
pub use crate::rpc::{KeyValue, ResponseHeader};
18-
19-
/// Get client for testing.
20-
#[doc(hidden)]
21-
#[cfg(test)]
22-
pub async fn get_client() -> crate::error::Result<Client> {
23-
Client::connect(["localhost:2379"]).await
24-
}

src/rpc/mod.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub mod lock;
1111
pub mod maintenance;
1212
pub mod watch;
1313

14+
use crate::error::Result;
1415
use pb::etcdserverpb::ResponseHeader as PbResponseHeader;
1516
use pb::mvccpb::KeyValue as PbKeyValue;
1617

@@ -79,12 +80,46 @@ impl KeyValue {
7980
&self.0.key
8081
}
8182

83+
/// The key in string. An empty key is not allowed.
84+
#[inline]
85+
pub fn key_str(&self) -> Result<&str> {
86+
std::str::from_utf8(self.key()).map_err(From::from)
87+
}
88+
89+
/// The key in string. An empty key is not allowed.
90+
///
91+
/// # Safety
92+
/// This function is unsafe because it does not check that the bytes of the key are valid UTF-8.
93+
/// If this constraint is violated, undefined behavior results,
94+
/// as the rest of Rust assumes that [`&str`]s are valid UTF-8.
95+
#[inline]
96+
pub unsafe fn key_str_unchecked(&self) -> &str {
97+
std::str::from_utf8_unchecked(self.key())
98+
}
99+
82100
/// The value held by the key, in bytes.
83101
#[inline]
84102
pub fn value(&self) -> &[u8] {
85103
&self.0.value
86104
}
87105

106+
/// The value held by the key, in string.
107+
#[inline]
108+
pub fn value_str(&self) -> Result<&str> {
109+
std::str::from_utf8(self.value()).map_err(From::from)
110+
}
111+
112+
/// The value held by the key, in bytes.
113+
///
114+
/// # Safety
115+
/// This function is unsafe because it does not check that the bytes of the value are valid UTF-8.
116+
/// If this constraint is violated, undefined behavior results,
117+
/// as the rest of Rust assumes that [`&str`]s are valid UTF-8.
118+
#[inline]
119+
pub unsafe fn value_str_unchecked(&self) -> &str {
120+
std::str::from_utf8_unchecked(self.value())
121+
}
122+
88123
/// The revision of last creation on this key.
89124
#[inline]
90125
pub const fn create_revision(&self) -> i64 {

0 commit comments

Comments
 (0)