opendal/raw/accessor.rs
1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use std::fmt::Debug;
19use std::future::ready;
20use std::hash::Hash;
21use std::hash::Hasher;
22use std::mem;
23use std::sync::Arc;
24
25use futures::Future;
26
27use crate::raw::*;
28use crate::*;
29
30/// Underlying trait of all backends for implementers.
31///
32/// The actual data access of storage service happens in Accessor layer.
33/// Every storage supported by OpenDAL must implement [`Access`] but not all
34/// methods of [`Access`] will be implemented according to how the storage service is.
35///
36/// For example, user can not modify the content from one HTTP file server directly.
37/// So [`Http`][crate::services::Http] implements and provides only read related actions.
38///
39/// [`Access`] gives default implementation for all methods which will raise [`ErrorKind::Unsupported`] error.
40/// And what action this [`Access`] supports will be pointed out in [`AccessorInfo`].
41///
42/// # Note
43///
44/// Visit [`internals`][crate::docs::internals] for more tutorials.
45///
46/// # Operations
47///
48/// - Path in args will all be normalized into the same style, services
49/// should handle them based on services' requirement.
50/// - Path that ends with `/` means it's Dir, otherwise, it's File.
51/// - Root dir is `/`
52/// - Path will never be empty.
53/// - Operations without capability requirement like `metadata`, `create` are
54/// basic operations.
55/// - All services must implement them.
56/// - Use `unimplemented!()` if not implemented or can't implement.
57/// - Operations with capability requirement like `presign` are optional operations.
58/// - Services can implement them based on services capabilities.
59/// - The default implementation should return [`ErrorKind::Unsupported`].
60pub trait Access: Send + Sync + Debug + Unpin + 'static {
61 /// Reader is the associated reader returned in `read` operation.
62 type Reader: oio::Read;
63 /// Writer is the associated writer returned in `write` operation.
64 type Writer: oio::Write;
65 /// Lister is the associated lister returned in `list` operation.
66 type Lister: oio::List;
67 /// Deleter is the associated deleter returned in `delete` operation.
68 type Deleter: oio::Delete;
69
70 /// Invoke the `info` operation to get metadata of accessor.
71 ///
72 /// # Notes
73 ///
74 /// This function is required to be implemented.
75 ///
76 /// By returning AccessorInfo, underlying services can declare
77 /// some useful information about itself.
78 ///
79 /// - scheme: declare the scheme of backend.
80 /// - capabilities: declare the capabilities of current backend.
81 fn info(&self) -> Arc<AccessorInfo>;
82
83 /// Invoke the `create` operation on the specified path
84 ///
85 /// Require [`Capability::create_dir`]
86 ///
87 /// # Behavior
88 ///
89 /// - Input path MUST match with EntryMode, DON'T NEED to check mode.
90 /// - Create on existing dir SHOULD succeed.
91 fn create_dir(
92 &self,
93 path: &str,
94 args: OpCreateDir,
95 ) -> impl Future<Output = Result<RpCreateDir>> + MaybeSend {
96 let (_, _) = (path, args);
97
98 ready(Err(Error::new(
99 ErrorKind::Unsupported,
100 "operation is not supported",
101 )))
102 }
103
104 /// Invoke the `stat` operation on the specified path.
105 ///
106 /// Require [`Capability::stat`]
107 ///
108 /// # Behavior
109 ///
110 /// - `stat` empty path means stat backend's root path.
111 /// - `stat` a path endswith "/" means stating a dir.
112 /// - `mode` and `content_length` must be set.
113 fn stat(&self, path: &str, args: OpStat) -> impl Future<Output = Result<RpStat>> + MaybeSend {
114 let (_, _) = (path, args);
115
116 ready(Err(Error::new(
117 ErrorKind::Unsupported,
118 "operation is not supported",
119 )))
120 }
121
122 /// Invoke the `read` operation on the specified path, returns a
123 /// [`Reader`][crate::Reader] if operate successful.
124 ///
125 /// Require [`Capability::read`]
126 ///
127 /// # Behavior
128 ///
129 /// - Input path MUST be file path, DON'T NEED to check mode.
130 /// - The returning content length may be smaller than the range specified.
131 fn read(
132 &self,
133 path: &str,
134 args: OpRead,
135 ) -> impl Future<Output = Result<(RpRead, Self::Reader)>> + MaybeSend {
136 let (_, _) = (path, args);
137
138 ready(Err(Error::new(
139 ErrorKind::Unsupported,
140 "operation is not supported",
141 )))
142 }
143
144 /// Invoke the `write` operation on the specified path, returns a
145 /// written size if operate successful.
146 ///
147 /// Require [`Capability::write`]
148 ///
149 /// # Behavior
150 ///
151 /// - Input path MUST be file path, DON'T NEED to check mode.
152 fn write(
153 &self,
154 path: &str,
155 args: OpWrite,
156 ) -> impl Future<Output = Result<(RpWrite, Self::Writer)>> + MaybeSend {
157 let (_, _) = (path, args);
158
159 ready(Err(Error::new(
160 ErrorKind::Unsupported,
161 "operation is not supported",
162 )))
163 }
164
165 /// Invoke the `delete` operation on the specified path.
166 ///
167 /// Require [`Capability::delete`]
168 ///
169 /// # Behavior
170 ///
171 /// - `delete` is an idempotent operation, it's safe to call `Delete` on the same path multiple times.
172 /// - `delete` SHOULD return `Ok(())` if the path is deleted successfully or not exist.
173 fn delete(&self) -> impl Future<Output = Result<(RpDelete, Self::Deleter)>> + MaybeSend {
174 ready(Err(Error::new(
175 ErrorKind::Unsupported,
176 "operation is not supported",
177 )))
178 }
179
180 /// Invoke the `list` operation on the specified path.
181 ///
182 /// Require [`Capability::list`]
183 ///
184 /// # Behavior
185 ///
186 /// - Input path MUST be dir path, DON'T NEED to check mode.
187 /// - List non-exist dir should return Empty.
188 fn list(
189 &self,
190 path: &str,
191 args: OpList,
192 ) -> impl Future<Output = Result<(RpList, Self::Lister)>> + MaybeSend {
193 let (_, _) = (path, args);
194
195 ready(Err(Error::new(
196 ErrorKind::Unsupported,
197 "operation is not supported",
198 )))
199 }
200
201 /// Invoke the `copy` operation on the specified `from` path and `to` path.
202 ///
203 /// Require [Capability::copy]
204 ///
205 /// # Behaviour
206 ///
207 /// - `from` and `to` MUST be file path, DON'T NEED to check mode.
208 /// - Copy on existing file SHOULD succeed.
209 /// - Copy on existing file SHOULD overwrite and truncate.
210 fn copy(
211 &self,
212 from: &str,
213 to: &str,
214 args: OpCopy,
215 ) -> impl Future<Output = Result<RpCopy>> + MaybeSend {
216 let (_, _, _) = (from, to, args);
217
218 ready(Err(Error::new(
219 ErrorKind::Unsupported,
220 "operation is not supported",
221 )))
222 }
223
224 /// Invoke the `rename` operation on the specified `from` path and `to` path.
225 ///
226 /// Require [Capability::rename]
227 fn rename(
228 &self,
229 from: &str,
230 to: &str,
231 args: OpRename,
232 ) -> impl Future<Output = Result<RpRename>> + MaybeSend {
233 let (_, _, _) = (from, to, args);
234
235 ready(Err(Error::new(
236 ErrorKind::Unsupported,
237 "operation is not supported",
238 )))
239 }
240
241 /// Invoke the `presign` operation on the specified path.
242 ///
243 /// Require [`Capability::presign`]
244 ///
245 /// # Behavior
246 ///
247 /// - This API is optional, return [`std::io::ErrorKind::Unsupported`] if not supported.
248 fn presign(
249 &self,
250 path: &str,
251 args: OpPresign,
252 ) -> impl Future<Output = Result<RpPresign>> + MaybeSend {
253 let (_, _) = (path, args);
254
255 ready(Err(Error::new(
256 ErrorKind::Unsupported,
257 "operation is not supported",
258 )))
259 }
260}
261
262/// `AccessDyn` is the dyn version of [`Access`] make it possible to use as
263/// `Box<dyn AccessDyn>`.
264pub trait AccessDyn: Send + Sync + Debug + Unpin {
265 /// Dyn version of [`Accessor::info`]
266 fn info_dyn(&self) -> Arc<AccessorInfo>;
267 /// Dyn version of [`Accessor::create_dir`]
268 fn create_dir_dyn<'a>(
269 &'a self,
270 path: &'a str,
271 args: OpCreateDir,
272 ) -> BoxedFuture<'a, Result<RpCreateDir>>;
273 /// Dyn version of [`Accessor::stat`]
274 fn stat_dyn<'a>(&'a self, path: &'a str, args: OpStat) -> BoxedFuture<'a, Result<RpStat>>;
275 /// Dyn version of [`Accessor::read`]
276 fn read_dyn<'a>(
277 &'a self,
278 path: &'a str,
279 args: OpRead,
280 ) -> BoxedFuture<'a, Result<(RpRead, oio::Reader)>>;
281 /// Dyn version of [`Accessor::write`]
282 fn write_dyn<'a>(
283 &'a self,
284 path: &'a str,
285 args: OpWrite,
286 ) -> BoxedFuture<'a, Result<(RpWrite, oio::Writer)>>;
287 /// Dyn version of [`Accessor::delete`]
288 fn delete_dyn(&self) -> BoxedFuture<Result<(RpDelete, oio::Deleter)>>;
289 /// Dyn version of [`Accessor::list`]
290 fn list_dyn<'a>(
291 &'a self,
292 path: &'a str,
293 args: OpList,
294 ) -> BoxedFuture<'a, Result<(RpList, oio::Lister)>>;
295 /// Dyn version of [`Accessor::copy`]
296 fn copy_dyn<'a>(
297 &'a self,
298 from: &'a str,
299 to: &'a str,
300 args: OpCopy,
301 ) -> BoxedFuture<'a, Result<RpCopy>>;
302 /// Dyn version of [`Accessor::rename`]
303 fn rename_dyn<'a>(
304 &'a self,
305 from: &'a str,
306 to: &'a str,
307 args: OpRename,
308 ) -> BoxedFuture<'a, Result<RpRename>>;
309 /// Dyn version of [`Accessor::presign`]
310 fn presign_dyn<'a>(
311 &'a self,
312 path: &'a str,
313 args: OpPresign,
314 ) -> BoxedFuture<'a, Result<RpPresign>>;
315}
316
317impl<A: ?Sized> AccessDyn for A
318where
319 A: Access<
320 Reader = oio::Reader,
321 Writer = oio::Writer,
322 Lister = oio::Lister,
323 Deleter = oio::Deleter,
324 >,
325{
326 fn info_dyn(&self) -> Arc<AccessorInfo> {
327 self.info()
328 }
329
330 fn create_dir_dyn<'a>(
331 &'a self,
332 path: &'a str,
333 args: OpCreateDir,
334 ) -> BoxedFuture<'a, Result<RpCreateDir>> {
335 Box::pin(self.create_dir(path, args))
336 }
337
338 fn stat_dyn<'a>(&'a self, path: &'a str, args: OpStat) -> BoxedFuture<'a, Result<RpStat>> {
339 Box::pin(self.stat(path, args))
340 }
341
342 fn read_dyn<'a>(
343 &'a self,
344 path: &'a str,
345 args: OpRead,
346 ) -> BoxedFuture<'a, Result<(RpRead, oio::Reader)>> {
347 Box::pin(self.read(path, args))
348 }
349
350 fn write_dyn<'a>(
351 &'a self,
352 path: &'a str,
353 args: OpWrite,
354 ) -> BoxedFuture<'a, Result<(RpWrite, oio::Writer)>> {
355 Box::pin(self.write(path, args))
356 }
357
358 fn delete_dyn(&self) -> BoxedFuture<Result<(RpDelete, oio::Deleter)>> {
359 Box::pin(self.delete())
360 }
361
362 fn list_dyn<'a>(
363 &'a self,
364 path: &'a str,
365 args: OpList,
366 ) -> BoxedFuture<'a, Result<(RpList, oio::Lister)>> {
367 Box::pin(self.list(path, args))
368 }
369
370 fn copy_dyn<'a>(
371 &'a self,
372 from: &'a str,
373 to: &'a str,
374 args: OpCopy,
375 ) -> BoxedFuture<'a, Result<RpCopy>> {
376 Box::pin(self.copy(from, to, args))
377 }
378
379 fn rename_dyn<'a>(
380 &'a self,
381 from: &'a str,
382 to: &'a str,
383 args: OpRename,
384 ) -> BoxedFuture<'a, Result<RpRename>> {
385 Box::pin(self.rename(from, to, args))
386 }
387
388 fn presign_dyn<'a>(
389 &'a self,
390 path: &'a str,
391 args: OpPresign,
392 ) -> BoxedFuture<'a, Result<RpPresign>> {
393 Box::pin(self.presign(path, args))
394 }
395}
396
397impl Access for dyn AccessDyn {
398 type Reader = oio::Reader;
399 type Writer = oio::Writer;
400 type Deleter = oio::Deleter;
401 type Lister = oio::Lister;
402
403 fn info(&self) -> Arc<AccessorInfo> {
404 self.info_dyn()
405 }
406
407 async fn create_dir(&self, path: &str, args: OpCreateDir) -> Result<RpCreateDir> {
408 self.create_dir_dyn(path, args).await
409 }
410
411 async fn stat(&self, path: &str, args: OpStat) -> Result<RpStat> {
412 self.stat_dyn(path, args).await
413 }
414
415 async fn read(&self, path: &str, args: OpRead) -> Result<(RpRead, Self::Reader)> {
416 self.read_dyn(path, args).await
417 }
418
419 async fn write(&self, path: &str, args: OpWrite) -> Result<(RpWrite, Self::Writer)> {
420 self.write_dyn(path, args).await
421 }
422
423 async fn delete(&self) -> Result<(RpDelete, Self::Deleter)> {
424 self.delete_dyn().await
425 }
426
427 async fn list(&self, path: &str, args: OpList) -> Result<(RpList, Self::Lister)> {
428 self.list_dyn(path, args).await
429 }
430
431 async fn copy(&self, from: &str, to: &str, args: OpCopy) -> Result<RpCopy> {
432 self.copy_dyn(from, to, args).await
433 }
434
435 async fn rename(&self, from: &str, to: &str, args: OpRename) -> Result<RpRename> {
436 self.rename_dyn(from, to, args).await
437 }
438
439 async fn presign(&self, path: &str, args: OpPresign) -> Result<RpPresign> {
440 self.presign_dyn(path, args).await
441 }
442}
443
444/// Dummy implementation of accessor.
445impl Access for () {
446 type Reader = ();
447 type Writer = ();
448 type Lister = ();
449 type Deleter = ();
450
451 fn info(&self) -> Arc<AccessorInfo> {
452 let ai = AccessorInfo::default();
453 ai.set_scheme(Scheme::Custom("dummy"))
454 .set_root("")
455 .set_name("dummy")
456 .set_native_capability(Capability::default());
457 ai.into()
458 }
459}
460
461/// All functions in `Accessor` only requires `&self`, so it's safe to implement
462/// `Accessor` for `Arc<impl Access>`.
463// If we use async fn directly, some weird higher rank trait bound error (`Send`/`Accessor` impl not general enough) will happen.
464// Probably related to https://github.com/rust-lang/rust/issues/96865
465#[allow(clippy::manual_async_fn)]
466impl<T: Access + ?Sized> Access for Arc<T> {
467 type Reader = T::Reader;
468 type Writer = T::Writer;
469 type Lister = T::Lister;
470 type Deleter = T::Deleter;
471
472 fn info(&self) -> Arc<AccessorInfo> {
473 self.as_ref().info()
474 }
475
476 fn create_dir(
477 &self,
478 path: &str,
479 args: OpCreateDir,
480 ) -> impl Future<Output = Result<RpCreateDir>> + MaybeSend {
481 async move { self.as_ref().create_dir(path, args).await }
482 }
483
484 fn stat(&self, path: &str, args: OpStat) -> impl Future<Output = Result<RpStat>> + MaybeSend {
485 async move { self.as_ref().stat(path, args).await }
486 }
487
488 fn read(
489 &self,
490 path: &str,
491 args: OpRead,
492 ) -> impl Future<Output = Result<(RpRead, Self::Reader)>> + MaybeSend {
493 async move { self.as_ref().read(path, args).await }
494 }
495
496 fn write(
497 &self,
498 path: &str,
499 args: OpWrite,
500 ) -> impl Future<Output = Result<(RpWrite, Self::Writer)>> + MaybeSend {
501 async move { self.as_ref().write(path, args).await }
502 }
503
504 fn delete(&self) -> impl Future<Output = Result<(RpDelete, Self::Deleter)>> + MaybeSend {
505 async move { self.as_ref().delete().await }
506 }
507
508 fn list(
509 &self,
510 path: &str,
511 args: OpList,
512 ) -> impl Future<Output = Result<(RpList, Self::Lister)>> + MaybeSend {
513 async move { self.as_ref().list(path, args).await }
514 }
515
516 fn copy(
517 &self,
518 from: &str,
519 to: &str,
520 args: OpCopy,
521 ) -> impl Future<Output = Result<RpCopy>> + MaybeSend {
522 async move { self.as_ref().copy(from, to, args).await }
523 }
524
525 fn rename(
526 &self,
527 from: &str,
528 to: &str,
529 args: OpRename,
530 ) -> impl Future<Output = Result<RpRename>> + MaybeSend {
531 async move { self.as_ref().rename(from, to, args).await }
532 }
533
534 fn presign(
535 &self,
536 path: &str,
537 args: OpPresign,
538 ) -> impl Future<Output = Result<RpPresign>> + MaybeSend {
539 async move { self.as_ref().presign(path, args).await }
540 }
541}
542
543/// Accessor is the type erased accessor with `Arc<dyn Accessor>`.
544pub type Accessor = Arc<dyn AccessDyn>;
545
546#[derive(Debug, Default)]
547struct AccessorInfoInner {
548 scheme: Scheme,
549 root: Arc<str>,
550 name: Arc<str>,
551
552 native_capability: Capability,
553 full_capability: Capability,
554
555 http_client: HttpClient,
556 executor: Executor,
557}
558
559/// Info for the accessor. Users can use this struct to retrieve information about the underlying backend.
560///
561/// This struct is intentionally not implemented with `Clone` to ensure that all accesses
562/// within the same operator, access layers, and services use the same instance of `AccessorInfo`.
563/// This is especially important for `HttpClient` and `Executor`.
564///
565/// ## Panic Safety
566///
567/// All methods provided by `AccessorInfo` will safely handle lock poisoning scenarios.
568/// If the inner `RwLock` is poisoned (which happens when another thread panicked while holding
569/// the write lock), this method will gracefully continue execution.
570///
571/// - For read operations, the method will return the current state.
572/// - For write operations, the method will do nothing.
573///
574/// ## Maintain Notes
575///
576/// We are using `std::sync::RwLock` to provide thread-safe access to the inner data.
577///
578/// I have performed [the bench across different arc-swap alike crates](https://github.com/krdln/arc-swap-benches):
579///
580/// ```txt
581/// test arcswap ... bench: 14.85 ns/iter (+/- 0.33)
582/// test arcswap_full ... bench: 128.27 ns/iter (+/- 4.30)
583/// test baseline ... bench: 11.33 ns/iter (+/- 0.76)
584/// test mutex_4 ... bench: 296.73 ns/iter (+/- 49.96)
585/// test mutex_unconteded ... bench: 13.26 ns/iter (+/- 0.56)
586/// test rwlock_fast_4 ... bench: 201.60 ns/iter (+/- 7.47)
587/// test rwlock_fast_uncontended ... bench: 12.77 ns/iter (+/- 0.37)
588/// test rwlock_parking_4 ... bench: 232.02 ns/iter (+/- 11.14)
589/// test rwlock_parking_uncontended ... bench: 13.18 ns/iter (+/- 0.39)
590/// test rwlock_std_4 ... bench: 219.56 ns/iter (+/- 5.56)
591/// test rwlock_std_uncontended ... bench: 13.55 ns/iter (+/- 0.33)
592/// ```
593///
594/// The results show that as long as there aren't too many uncontended accesses, `RwLock` is the
595/// best choice, allowing for fast access and the ability to modify partial data without cloning
596/// everything.
597///
598/// And it's true: we only update and modify the internal data in a few instances, such as when
599/// building an operator or applying new layers.
600#[derive(Debug, Default)]
601pub struct AccessorInfo {
602 inner: std::sync::RwLock<AccessorInfoInner>,
603}
604
605impl PartialEq for AccessorInfo {
606 fn eq(&self, other: &Self) -> bool {
607 self.scheme() == other.scheme()
608 && self.root() == other.root()
609 && self.name() == other.name()
610 }
611}
612
613impl Eq for AccessorInfo {}
614
615impl Hash for AccessorInfo {
616 fn hash<H: Hasher>(&self, state: &mut H) {
617 self.scheme().hash(state);
618 self.root().hash(state);
619 self.name().hash(state);
620 }
621}
622
623impl AccessorInfo {
624 /// [`Scheme`] of backend.
625 ///
626 /// # Panic Safety
627 ///
628 /// This method safely handles lock poisoning scenarios. If the inner `RwLock` is poisoned,
629 /// this method will gracefully continue execution by simply returning the current scheme.
630 pub fn scheme(&self) -> Scheme {
631 match self.inner.read() {
632 Ok(v) => v.scheme,
633 Err(err) => err.get_ref().scheme,
634 }
635 }
636
637 /// Set [`Scheme`] for backend.
638 ///
639 /// # Panic Safety
640 ///
641 /// This method safely handles lock poisoning scenarios. If the inner `RwLock` is poisoned,
642 /// this method will gracefully continue execution by simply skipping the update operation
643 /// rather than propagating the panic.
644 pub fn set_scheme(&self, scheme: Scheme) -> &Self {
645 if let Ok(mut v) = self.inner.write() {
646 v.scheme = scheme;
647 }
648
649 self
650 }
651
652 /// Root of backend, will be in format like `/path/to/dir/`
653 ///
654 /// # Panic Safety
655 ///
656 /// This method safely handles lock poisoning scenarios. If the inner `RwLock` is poisoned,
657 /// this method will gracefully continue execution by simply returning the current root.
658 pub fn root(&self) -> Arc<str> {
659 match self.inner.read() {
660 Ok(v) => v.root.clone(),
661 Err(err) => err.get_ref().root.clone(),
662 }
663 }
664
665 /// Set root for backend.
666 ///
667 /// Note: input root must be normalized.
668 ///
669 /// # Panic Safety
670 ///
671 /// This method safely handles lock poisoning scenarios. If the inner `RwLock` is poisoned,
672 /// this method will gracefully continue execution by simply skipping the update operation
673 /// rather than propagating the panic.
674 pub fn set_root(&self, root: &str) -> &Self {
675 if let Ok(mut v) = self.inner.write() {
676 v.root = Arc::from(root);
677 }
678
679 self
680 }
681
682 /// Name of backend, could be empty if underlying backend doesn't have namespace concept.
683 ///
684 /// For example:
685 ///
686 /// - `s3` => bucket name
687 /// - `azblob` => container name
688 /// - `azdfs` => filesystem name
689 /// - `azfile` => share name
690 ///
691 /// # Panic Safety
692 ///
693 /// This method safely handles lock poisoning scenarios. If the inner `RwLock` is poisoned,
694 /// this method will gracefully continue execution by simply returning the current scheme.
695 pub fn name(&self) -> Arc<str> {
696 match self.inner.read() {
697 Ok(v) => v.name.clone(),
698 Err(err) => err.get_ref().name.clone(),
699 }
700 }
701
702 /// Set name of this backend.
703 ///
704 /// # Panic Safety
705 ///
706 /// This method safely handles lock poisoning scenarios. If the inner `RwLock` is poisoned,
707 /// this method will gracefully continue execution by simply skipping the update operation
708 /// rather than propagating the panic.
709 pub fn set_name(&self, name: &str) -> &Self {
710 if let Ok(mut v) = self.inner.write() {
711 v.name = Arc::from(name)
712 }
713
714 self
715 }
716
717 /// Get backend's native capabilities.
718 ///
719 /// # Panic Safety
720 ///
721 /// This method safely handles lock poisoning scenarios. If the inner `RwLock` is poisoned,
722 /// this method will gracefully continue execution by simply returning the current native capability.
723 pub fn native_capability(&self) -> Capability {
724 match self.inner.read() {
725 Ok(v) => v.native_capability,
726 Err(err) => err.get_ref().native_capability,
727 }
728 }
729
730 /// Set native capabilities for service.
731 ///
732 /// # NOTES
733 ///
734 /// Set native capability will also flush the full capability. The only way to change
735 /// full_capability is via `update_full_capability`.
736 ///
737 /// # Panic Safety
738 ///
739 /// This method safely handles lock poisoning scenarios. If the inner `RwLock` is poisoned,
740 /// this method will gracefully continue execution by simply skipping the update operation
741 /// rather than propagating the panic.
742 pub fn set_native_capability(&self, capability: Capability) -> &Self {
743 if let Ok(mut v) = self.inner.write() {
744 v.native_capability = capability;
745 v.full_capability = capability;
746 }
747
748 self
749 }
750
751 /// Get service's full capabilities.
752 ///
753 /// # Panic Safety
754 ///
755 /// This method safely handles lock poisoning scenarios. If the inner `RwLock` is poisoned,
756 /// this method will gracefully continue execution by simply returning the current native capability.
757 pub fn full_capability(&self) -> Capability {
758 match self.inner.read() {
759 Ok(v) => v.full_capability,
760 Err(err) => err.get_ref().full_capability,
761 }
762 }
763
764 /// Update service's full capabilities.
765 ///
766 /// # Panic Safety
767 ///
768 /// This method safely handles lock poisoning scenarios. If the inner `RwLock` is poisoned,
769 /// this method will gracefully continue execution by simply skipping the update operation
770 /// rather than propagating the panic.
771 pub fn update_full_capability(&self, f: impl FnOnce(Capability) -> Capability) -> &Self {
772 if let Ok(mut v) = self.inner.write() {
773 v.full_capability = f(v.full_capability);
774 }
775
776 self
777 }
778
779 /// Get http client from the context.
780 ///
781 /// # Panic Safety
782 ///
783 /// This method safely handles lock poisoning scenarios. If the inner `RwLock` is poisoned,
784 /// this method will gracefully continue execution by simply returning the current http client.
785 pub fn http_client(&self) -> HttpClient {
786 match self.inner.read() {
787 Ok(v) => v.http_client.clone(),
788 Err(err) => err.get_ref().http_client.clone(),
789 }
790 }
791
792 /// Update http client for the context.
793 ///
794 /// # Note
795 ///
796 /// Requests must be forwarded to the old HTTP client after the update. Otherwise, features such as retry, tracing, and metrics may not function properly.
797 ///
798 /// # Panic Safety
799 ///
800 /// This method safely handles lock poisoning scenarios. If the inner `RwLock` is poisoned,
801 /// this method will gracefully continue execution by simply skipping the update operation.
802 pub fn update_http_client(&self, f: impl FnOnce(HttpClient) -> HttpClient) -> &Self {
803 if let Ok(mut v) = self.inner.write() {
804 let client = mem::take(&mut v.http_client);
805 v.http_client = f(client);
806 }
807
808 self
809 }
810
811 /// Get executor from the context.
812 ///
813 /// # Panic Safety
814 ///
815 /// This method safely handles lock poisoning scenarios. If the inner `RwLock` is poisoned,
816 /// this method will gracefully continue execution by simply returning the current executor.
817 pub fn executor(&self) -> Executor {
818 match self.inner.read() {
819 Ok(v) => v.executor.clone(),
820 Err(err) => err.get_ref().executor.clone(),
821 }
822 }
823
824 /// Update executor for the context.
825 ///
826 /// # Note
827 ///
828 /// Tasks must be forwarded to the old executor after the update. Otherwise, features such as retry, timeout, and metrics may not function properly.
829 ///
830 /// # Panic Safety
831 ///
832 /// This method safely handles lock poisoning scenarios. If the inner `RwLock` is poisoned,
833 /// this method will gracefully continue execution by simply skipping the update operation.
834 pub fn update_executor(&self, f: impl FnOnce(Executor) -> Executor) -> &Self {
835 if let Ok(mut v) = self.inner.write() {
836 let executor = mem::take(&mut v.executor);
837 v.executor = f(executor);
838 }
839
840 self
841 }
842}