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