Skip to main content

etcd_client/rpc/
lock.rs

1//! Etcd Lock RPC.
2
3use super::pb::v3lockpb;
4use crate::caller::{ClientCaller, ClientCallerBuilder};
5use crate::error::Result;
6use crate::intercept::InterceptedChannel;
7use crate::rpc::ResponseHeader;
8use tonic::{IntoRequest, Request};
9use v3lockpb::lock_client::LockClient as PbLockClient;
10use v3lockpb::{
11    LockRequest as PbLockRequest, LockResponse as PbLockResponse, UnlockRequest as PbUnlockRequest,
12    UnlockResponse as PbUnlockResponse,
13};
14
15type Client = PbLockClient<InterceptedChannel>;
16
17/// Client for Lock operations.
18#[repr(transparent)]
19#[derive(Clone)]
20pub struct LockClient {
21    inner: ClientCaller<Client>,
22}
23
24impl LockClient {
25    /// Creates a lock client.
26    #[inline]
27    pub(crate) fn new(builder: ClientCallerBuilder) -> Self {
28        Self {
29            inner: builder.build(Client::new),
30        }
31    }
32    /// Acquires a distributed shared lock on a given named lock.
33    /// On success, it will return a unique key that exists so long as the
34    /// lock is held by the caller. This key can be used in conjunction with
35    /// transactions to safely ensure updates to etcd only occur while holding
36    /// lock ownership. The lock is held until Unlock is called on the key or the
37    /// lease associate with the owner expires.
38    #[inline]
39    pub async fn lock(
40        &mut self,
41        name: impl Into<Vec<u8>>,
42        options: Option<LockOptions>,
43    ) -> Result<LockResponse> {
44        async fn lock_impl(client: &mut Client, options: LockOptions) -> Result<LockResponse> {
45            let resp = client.lock(options).await?.into_inner();
46            Ok(LockResponse::new(resp))
47        }
48        self.inner
49            .do_call(options.unwrap_or_default().with_name(name), lock_impl)
50            .await
51    }
52
53    /// Takes a key returned by Lock and releases the hold on lock. The
54    /// next Lock caller waiting for the lock will then be woken up and given
55    /// ownership of the lock.
56    #[inline]
57    pub async fn unlock(&mut self, key: impl Into<Vec<u8>>) -> Result<UnlockResponse> {
58        async fn unlock_impl(
59            client: &mut Client,
60            options: UnlockOptions,
61        ) -> Result<UnlockResponse> {
62            let resp = client.unlock(options).await?.into_inner();
63            Ok(UnlockResponse::new(resp))
64        }
65        self.inner
66            .do_call(UnlockOptions::new().with_key(key), unlock_impl)
67            .await
68    }
69}
70
71/// Options for `Lock` operation.
72#[derive(Debug, Default, Clone)]
73#[repr(transparent)]
74pub struct LockOptions(PbLockRequest);
75
76impl LockOptions {
77    /// name is the identifier for the distributed shared lock to be acquired.
78    #[inline]
79    fn with_name(mut self, name: impl Into<Vec<u8>>) -> Self {
80        self.0.name = name.into();
81        self
82    }
83
84    /// Creates a `LockOptions`.
85    #[inline]
86    pub const fn new() -> Self {
87        Self(PbLockRequest {
88            name: Vec::new(),
89            lease: 0,
90        })
91    }
92
93    /// `lease` is the ID of the lease that will be attached to ownership of the
94    /// lock. If the lease expires or is revoked and currently holds the lock,
95    /// the lock is automatically released. Calls to Lock with the same lease will
96    /// be treated as a single acquisition; locking twice with the same lease is a
97    /// no-op.
98    #[inline]
99    pub const fn with_lease(mut self, lease: i64) -> Self {
100        self.0.lease = lease;
101        self
102    }
103}
104
105impl From<LockOptions> for PbLockRequest {
106    #[inline]
107    fn from(options: LockOptions) -> Self {
108        options.0
109    }
110}
111
112impl IntoRequest<PbLockRequest> for LockOptions {
113    #[inline]
114    fn into_request(self) -> Request<PbLockRequest> {
115        Request::new(self.into())
116    }
117}
118
119/// Response for `Lock` operation.
120#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
121#[derive(Debug, Default, Clone)]
122#[repr(transparent)]
123pub struct LockResponse(PbLockResponse);
124
125impl LockResponse {
126    /// Create a new `LockResponse` from pb lock response.
127    #[inline]
128    const fn new(resp: PbLockResponse) -> Self {
129        Self(resp)
130    }
131
132    /// Get response header.
133    #[inline]
134    pub fn header(&self) -> Option<&ResponseHeader> {
135        self.0.header.as_ref().map(From::from)
136    }
137
138    /// Takes the header out of the response, leaving a [`None`] in its place.
139    #[inline]
140    pub fn take_header(&mut self) -> Option<ResponseHeader> {
141        self.0.header.take().map(ResponseHeader::new)
142    }
143
144    /// A key that will exist on etcd for the duration that the Lock caller
145    /// owns the lock. Users should not modify this key or the lock may exhibit
146    /// undefined behavior.
147    #[inline]
148    pub fn key(&self) -> &[u8] {
149        &self.0.key
150    }
151}
152
153/// Options for `Unlock` operation.
154#[derive(Debug, Default, Clone)]
155#[repr(transparent)]
156pub struct UnlockOptions(PbUnlockRequest);
157
158impl UnlockOptions {
159    /// key is the lock ownership key granted by Lock.
160    #[inline]
161    fn with_key(mut self, key: impl Into<Vec<u8>>) -> Self {
162        self.0.key = key.into();
163        self
164    }
165
166    /// Creates a `UnlockOptions`.
167    #[inline]
168    pub const fn new() -> Self {
169        Self(PbUnlockRequest { key: Vec::new() })
170    }
171}
172
173impl From<UnlockOptions> for PbUnlockRequest {
174    #[inline]
175    fn from(options: UnlockOptions) -> Self {
176        options.0
177    }
178}
179
180impl IntoRequest<PbUnlockRequest> for UnlockOptions {
181    #[inline]
182    fn into_request(self) -> Request<PbUnlockRequest> {
183        Request::new(self.into())
184    }
185}
186
187/// Response for `Unlock` operation.
188#[cfg_attr(feature = "pub-response-field", visible::StructFields(pub))]
189#[derive(Debug, Default, Clone)]
190#[repr(transparent)]
191pub struct UnlockResponse(PbUnlockResponse);
192
193impl UnlockResponse {
194    /// Create a new `UnlockResponse` from pb unlock response.
195    #[inline]
196    const fn new(resp: PbUnlockResponse) -> Self {
197        Self(resp)
198    }
199
200    /// Get response header.
201    #[inline]
202    pub fn header(&self) -> Option<&ResponseHeader> {
203        self.0.header.as_ref().map(From::from)
204    }
205
206    /// Takes the header out of the response, leaving a [`None`] in its place.
207    #[inline]
208    pub fn take_header(&mut self) -> Option<ResponseHeader> {
209        self.0.header.take().map(ResponseHeader::new)
210    }
211}