uplink/bucket.rs
1//! Storj DSC Bucket and related types.
2
3use crate::uplink_c::Ensurer;
4use crate::{Error, Result};
5
6use std::ffi::{CStr, CString};
7use std::time::Duration;
8
9use uplink_sys as ulksys;
10
11/// Contains information about a specific bucket.
12#[derive(Debug)]
13pub struct Bucket {
14 /// Name of the bucket.
15 pub name: String,
16 /// Unix Epoch time when the bucket was created.
17 pub created_at: Duration,
18}
19
20impl Bucket {
21 /// Creates a Bucket instance from the type exposed by the FFI.
22 ///
23 /// It returns an [`Error:Internal`](crate::Error::Internal) if `uc_bucket`'s name invalid
24 /// UTF-8.
25 pub(crate) fn from_ffi_bucket(uc_bucket: *mut ulksys::UplinkBucket) -> Result<Self> {
26 assert!(
27 !uc_bucket.is_null(),
28 "BUG: `uc_bucket` argument cannot be NULL"
29 );
30
31 let uc_bucket_ptr = uc_bucket;
32 // SAFETY: We have checked just above that the pointer isn't NULL.
33 let uc_bucket = unsafe { *uc_bucket_ptr };
34 uc_bucket.ensure();
35
36 let name;
37 let created_at: Duration;
38 // SAFETY: we have check that the `uc_bucket` doesn't have fields with NULL pointers through
39 // the `ensure` method.
40 unsafe {
41 // User create buckets and satellites and/or client libraries or applications don't
42 // likely allow to use invalid UTF-8 characters in their names. Nonetheless, we don't
43 // panic if they contain some and we return an internal error because we see it's a
44 // limitation of Rust and C interoperability and consumers of this crate would have a
45 // chance to deal with them appropriately.
46 let cs = CString::from(CStr::from_ptr(uc_bucket.name));
47 name = cs.into_string().map_err(|err| {
48 ulksys::uplink_free_bucket(uc_bucket_ptr);
49 Error::new_internal(
50 "FFI returned an invalid bucket's name; it contains invalid UTF-8 characters",
51 err.into(),
52 )
53 })?;
54 created_at = Duration::new(uc_bucket.created as u64, 0);
55 ulksys::uplink_free_bucket(uc_bucket_ptr);
56 }
57
58 Ok(Bucket { name, created_at })
59 }
60
61 /// Creates a new instance from the FFI representation for a bucket's result.
62 ///
63 /// It returns the following errors:
64 /// * an [`Error::new_uplink` constructor](crate::Error::new_uplink), if `uc_result` contains a
65 /// non `NULL` pointer in the `error` field.
66 /// * an [`Error::Internal`](crate::Error::Internal) if `uc_result.bucket`'s name contains
67 /// invalid UTF-8 characters.
68 pub(crate) fn from_ffi_bucket_result(uc_result: ulksys::UplinkBucketResult) -> Result<Self> {
69 uc_result.ensure();
70
71 if let Some(err) = Error::new_uplink(uc_result.error) {
72 // SAFETY: we trust the FFI is safe freeing the memory of a valid pointer.
73 unsafe { ulksys::uplink_free_bucket_result(uc_result) };
74 return Err(err);
75 }
76
77 // At this point we don't need to free the `uc_result` because the following function free
78 // the `info` pointer and the `error` pointer is `NULL`, and that's what the free function
79 // for the `uc_result` does (i.e. call a free specific function for each pointer returning
80 // without doing anything if it's `NULL`).
81 Self::from_ffi_bucket(uc_result.bucket)
82 }
83}
84
85/// Iterates over a collection of buckets.
86pub struct Iterator {
87 /// The bucket iterator type of the FFI that an instance of this struct represents and guards
88 /// its lifetime until this instance drops.
89 inner: *mut ulksys::UplinkBucketIterator,
90}
91
92impl Iterator {
93 /// Creates a new instance from the type exposed by the FFI.
94 pub(crate) fn from_ffi_bucket_iterator(uc_iterator: *mut ulksys::UplinkBucketIterator) -> Self {
95 assert!(
96 !uc_iterator.is_null(),
97 "BUG: `uc_iterator` argument cannot be NULL"
98 );
99
100 Iterator { inner: uc_iterator }
101 }
102}
103
104impl std::iter::Iterator for Iterator {
105 type Item = Result<Bucket>;
106
107 /// It returns an:
108 ///
109 /// * [`Error::Uplink`](crate::Error::Uplink) when FFI returns an error when retrieving the
110 /// item.
111 /// * [`Error:Internal`](crate::Error::Internal) if `uc_bucket`'s name invalid UTF-8.
112 fn next(&mut self) -> Option<Self::Item> {
113 // SAFETY: we trust that the FFI functions don't panic when called with an instance returned
114 // by them and they don't return invalid memory references or `null` if next returns `true`.
115 unsafe {
116 if !ulksys::uplink_bucket_iterator_next(self.inner) {
117 let uc_error = ulksys::uplink_bucket_iterator_err(self.inner);
118 return Error::new_uplink(uc_error).map(Err);
119 }
120
121 Some(Bucket::from_ffi_bucket(
122 ulksys::uplink_bucket_iterator_item(self.inner),
123 ))
124 }
125 }
126}
127
128impl Drop for Iterator {
129 fn drop(&mut self) {
130 // SAFETY: we trust that the FFI is safe freeing the memory of a correct
131 // `UplinkBukcetIterator` value.
132 unsafe {
133 ulksys::uplink_free_bucket_iterator(self.inner);
134 }
135 }
136}