Skip to main content

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}