0
# Types and Error Handling
1
2
OpenDAL provides a comprehensive type system for storage-agnostic data handling, metadata management, and robust error handling. All types are designed for zero-copy operations and efficient memory usage.
3
4
```rust
5
use std::ops::RangeBounds;
6
use bytes::Bytes;
7
use chrono::{DateTime, Utc};
8
use flagset::{FlagSet};
9
```
10
11
## Capabilities
12
13
### Core Data Types
14
15
Primary data structures for file operations and metadata.
16
17
```rust { .api }
18
/// Efficient bytes container supporting both contiguous and non-contiguous data
19
pub struct Buffer;
20
21
impl Buffer {
22
/// Create buffer from bytes
23
pub fn from(data: impl Into<Bytes>) -> Self;
24
25
/// Create buffer from byte vector
26
pub fn from_vec(data: Vec<u8>) -> Self;
27
28
/// Create buffer from static byte slice
29
pub fn from_static(data: &'static [u8]) -> Self;
30
31
/// Get buffer length
32
pub fn len(&self) -> usize;
33
34
/// Check if buffer is empty
35
pub fn is_empty(&self) -> bool;
36
37
/// Convert to bytes
38
pub fn to_bytes(&self) -> Bytes;
39
40
/// Convert to byte vector (may require copying)
41
pub fn to_vec(&self) -> Vec<u8>;
42
43
/// Create a slice of the buffer within given range
44
pub fn slice(&self, range: impl RangeBounds<usize>) -> Self;
45
46
/// Truncate buffer to specified length
47
pub fn truncate(&mut self, len: usize);
48
49
/// Get current chunk as Bytes
50
pub fn current(&self) -> Bytes;
51
52
/// Get number of remaining chunks
53
pub fn count(&self) -> usize;
54
}
55
56
// Buffer implements AsRef<[u8]> trait for slice access
57
impl AsRef<[u8]> for Buffer;
58
59
impl From<&str> for Buffer;
60
impl From<String> for Buffer;
61
impl From<Vec<u8>> for Buffer;
62
impl From<Bytes> for Buffer;
63
impl From<&[u8]> for Buffer;
64
```
65
66
**Usage Examples:**
67
68
```rust
69
use opendal::Buffer;
70
71
// Create buffer from various sources
72
let buf1 = Buffer::from("Hello, World!");
73
let buf2 = Buffer::from(vec![1, 2, 3, 4, 5]);
74
let buf3 = Buffer::from_static(b"static data");
75
76
// Write buffer to storage
77
op.write("text.txt", buf1).await?;
78
op.write("binary.dat", buf2).await?;
79
80
// Buffer operations
81
let data = op.read("file.txt").await?;
82
println!("Size: {} bytes", data.len());
83
let text = String::from_utf8_lossy(data.as_ref());
84
```
85
86
### Directory Entry Types
87
88
Types for directory listing and file system navigation.
89
90
```rust { .api }
91
/// Directory entry containing path and metadata
92
pub struct Entry {
93
path: String,
94
metadata: Metadata,
95
}
96
97
impl Entry {
98
/// Get entry path
99
pub fn path(&self) -> &str;
100
101
/// Get entry metadata
102
pub fn metadata(&self) -> &Metadata;
103
104
/// Get entry mode (file/directory)
105
pub fn mode(&self) -> EntryMode;
106
107
/// Get entry name (basename)
108
pub fn name(&self) -> &str;
109
110
/// Consume entry and return its parts
111
pub fn into_parts(self) -> (String, Metadata);
112
}
113
114
/// File system entry type
115
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116
pub enum EntryMode {
117
/// Regular file
118
FILE,
119
/// Directory
120
DIR,
121
/// Unknown type
122
Unknown,
123
}
124
125
impl EntryMode {
126
/// Check if entry is a file
127
pub fn is_file(&self) -> bool;
128
129
/// Check if entry is a directory
130
pub fn is_dir(&self) -> bool;
131
}
132
```
133
134
**Usage Examples:**
135
136
```rust
137
use opendal::{EntryMode, Entry};
138
139
// List directory entries
140
let entries = op.list("/documents/").await?;
141
142
for entry in entries {
143
match entry.mode() {
144
EntryMode::FILE => {
145
println!("File: {} ({} bytes)",
146
entry.path(),
147
entry.metadata().content_length()
148
);
149
},
150
EntryMode::DIR => {
151
println!("Directory: {}", entry.path());
152
},
153
EntryMode::Unknown => {
154
println!("Unknown: {}", entry.path());
155
}
156
}
157
}
158
```
159
160
### Metadata Types
161
162
Comprehensive metadata system for file attributes and properties.
163
164
```rust { .api }
165
/// File metadata containing size, timestamps, and other attributes
166
pub struct Metadata {
167
bit: BitSet<Metakey>,
168
mode: EntryMode,
169
cache_control: Option<String>,
170
content_disposition: Option<String>,
171
content_length: Option<u64>,
172
content_md5: Option<String>,
173
content_range: Option<BytesContentRange>,
174
content_type: Option<String>,
175
etag: Option<String>,
176
last_modified: Option<OffsetDateTime>,
177
version: Option<String>,
178
}
179
180
impl Metadata {
181
/// Get entry mode (file/directory/unknown)
182
pub fn mode(&self) -> EntryMode;
183
184
/// Get content length in bytes
185
pub fn content_length(&self) -> u64;
186
187
/// Get content MIME type
188
pub fn content_type(&self) -> Option<&str>;
189
190
/// Get content MD5 hash
191
pub fn content_md5(&self) -> Option<&str>;
192
193
/// Get content range information
194
pub fn content_range(&self) -> Option<BytesContentRange>;
195
196
/// Get ETag for caching/versioning
197
pub fn etag(&self) -> Option<&str>;
198
199
/// Get last modified timestamp
200
pub fn last_modified(&self) -> Option<OffsetDateTime>;
201
202
/// Get object version
203
pub fn version(&self) -> Option<&str>;
204
205
/// Get cache control header
206
pub fn cache_control(&self) -> Option<&str>;
207
208
/// Get content disposition header
209
pub fn content_disposition(&self) -> Option<&str>;
210
211
/// Check if specific metadata is available
212
pub fn contains_metakey(&self, key: Metakey) -> bool;
213
}
214
215
/// Metadata keys for selective fetching
216
#[derive(Debug, Clone, Copy)]
217
pub enum Metakey {
218
/// Complete metadata (all fields)
219
Complete,
220
/// Entry mode only
221
Mode,
222
/// Cache control header
223
CacheControl,
224
/// Content length
225
ContentLength,
226
/// Content MD5 hash
227
ContentMd5,
228
/// Content MIME type
229
ContentType,
230
/// Content range
231
ContentRange,
232
/// ETag for versioning
233
Etag,
234
/// Last modified timestamp
235
LastModified,
236
/// Object version
237
Version,
238
/// Content disposition
239
ContentDisposition,
240
}
241
242
impl Metakey {
243
/// Convert from string representation
244
pub fn from_str(s: &str) -> Option<Self>;
245
246
/// Get string representation
247
pub fn as_str(&self) -> &'static str;
248
}
249
```
250
251
**Usage Examples:**
252
253
```rust
254
use opendal::{Metadata, Metakey};
255
use time::OffsetDateTime;
256
257
// Get complete metadata
258
let metadata = op.stat("document.pdf").await?;
259
260
// Access metadata fields
261
println!("Size: {} bytes", metadata.content_length());
262
println!("Type: {:?}", metadata.content_type());
263
println!("ETag: {:?}", metadata.etag());
264
265
if let Some(modified) = metadata.last_modified() {
266
println!("Last modified: {}", modified);
267
}
268
269
// Selective metadata fetching
270
let partial_meta = op.stat_with("large-file.zip")
271
.metakey(Metakey::ContentLength | Metakey::LastModified)
272
.await?;
273
274
// Check what metadata is available
275
if partial_meta.contains_metakey(Metakey::ContentLength) {
276
println!("Size is available: {}", partial_meta.content_length());
277
}
278
```
279
280
### Service and Capability Types
281
282
Types for service identification and capability detection.
283
284
```rust { .api }
285
/// Storage service scheme identifier
286
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
287
pub enum Scheme {
288
// Major cloud providers
289
S3, Gcs, Azblob, Oss, Obs, Cos,
290
// File systems
291
Fs, Hdfs, HdfsNative, Webdav, Ftp, Sftp,
292
// Databases
293
Redis, Postgresql, Mysql, Sqlite, Mongodb, Rocksdb,
294
// Memory/Cache
295
Memory, Dashmap, Memcached, Moka, MiniMoka, Cacache,
296
// Platform services
297
Github, Dropbox, Onedrive, Gdrive, Supabase,
298
// Specialized
299
Http, Ipfs, Alluxio,
300
// ... and 30+ more services
301
}
302
303
impl Scheme {
304
/// Parse scheme from string
305
pub fn from_str(s: &str) -> Result<Self>;
306
307
/// Get string representation
308
pub fn into_static(self) -> &'static str;
309
}
310
311
/// Service capability description
312
pub struct Capability {
313
stat: bool,
314
stat_with_if_match: bool,
315
stat_with_if_none_match: bool,
316
read: bool,
317
read_with_range: bool,
318
read_with_if_match: bool,
319
read_with_if_none_match: bool,
320
write: bool,
321
write_can_multi: bool,
322
write_can_empty: bool,
323
write_can_append: bool,
324
write_with_cache_control: bool,
325
write_with_content_type: bool,
326
write_with_content_disposition: bool,
327
create_dir: bool,
328
delete: bool,
329
copy: bool,
330
rename: bool,
331
list: bool,
332
list_with_limit: bool,
333
list_with_start_after: bool,
334
list_with_recursive: bool,
335
presign: bool,
336
presign_read: bool,
337
presign_stat: bool,
338
presign_write: bool,
339
batch: bool,
340
batch_delete: bool,
341
batch_max_operations: Option<usize>,
342
blocking: bool,
343
}
344
345
impl Capability {
346
/// Check if operation is supported
347
pub fn stat(&self) -> bool;
348
pub fn read(&self) -> bool;
349
pub fn write(&self) -> bool;
350
pub fn delete(&self) -> bool;
351
pub fn list(&self) -> bool;
352
pub fn copy(&self) -> bool;
353
pub fn rename(&self) -> bool;
354
pub fn create_dir(&self) -> bool;
355
pub fn presign(&self) -> bool;
356
pub fn batch(&self) -> bool;
357
pub fn blocking(&self) -> bool;
358
359
/// Check option support
360
pub fn read_with_range(&self) -> bool;
361
pub fn write_with_content_type(&self) -> bool;
362
pub fn list_with_recursive(&self) -> bool;
363
364
/// Get batch operation limits
365
pub fn batch_max_operations(&self) -> Option<usize>;
366
}
367
368
/// Operator information
369
pub struct OperatorInfo {
370
scheme: Scheme,
371
root: String,
372
name: String,
373
capability: Capability,
374
}
375
376
impl OperatorInfo {
377
/// Get service scheme
378
pub fn scheme(&self) -> Scheme;
379
380
/// Get root path
381
pub fn root(&self) -> &str;
382
383
/// Get service name
384
pub fn name(&self) -> &str;
385
386
/// Get service capabilities
387
pub fn full_capability(&self) -> &Capability;
388
}
389
```
390
391
**Usage Examples:**
392
393
```rust
394
use opendal::{Scheme, Capability};
395
396
// Get operator information
397
let info = op.info();
398
println!("Service: {:?}", info.scheme());
399
println!("Root: {}", info.root());
400
401
// Check capabilities
402
let cap = info.full_capability();
403
if cap.presign() {
404
let presigned = op.presign_read("file.txt", Duration::from_secs(3600)).await?;
405
println!("Presigned URL: {}", presigned.uri());
406
} else {
407
println!("Presigned URLs not supported");
408
}
409
410
// Conditional operations based on capabilities
411
if cap.batch() {
412
// Use batch operations
413
op.remove(vec!["file1.txt".to_string(), "file2.txt".to_string()]).await?;
414
} else {
415
// Fall back to individual operations
416
op.delete("file1.txt").await?;
417
op.delete("file2.txt").await?;
418
}
419
```
420
421
### Error Handling Types
422
423
Comprehensive error system with detailed context and error classification.
424
425
```rust { .api }
426
/// Main error type with rich context
427
pub struct Error {
428
kind: ErrorKind,
429
message: Cow<'static, str>,
430
source: Option<anyhow::Error>,
431
operation: Operation,
432
path: String,
433
context: HashMap<&'static str, String>,
434
}
435
436
impl Error {
437
/// Get error kind
438
pub fn kind(&self) -> ErrorKind;
439
440
/// Get error message
441
pub fn to_string(&self) -> String;
442
443
/// Get operation that failed
444
pub fn operation(&self) -> Operation;
445
446
/// Get path involved in error
447
pub fn path(&self) -> &str;
448
449
/// Get additional context
450
pub fn context(&self) -> &HashMap<&'static str, String>;
451
}
452
453
/// Error classification
454
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
455
pub enum ErrorKind {
456
/// Configuration is invalid
457
ConfigInvalid,
458
/// Path or resource not found
459
NotFound,
460
/// Permission denied
461
PermissionDenied,
462
/// Resource is a directory when file expected
463
IsADirectory,
464
/// Resource is not a directory when directory expected
465
NotADirectory,
466
/// Resource already exists
467
AlreadyExists,
468
/// Rate limited by service
469
RateLimited,
470
/// Service is busy, try again later
471
IsBusy,
472
/// Request does not contain valid range
473
RangeNotSatisfied,
474
/// Condition failed (e.g., if-match)
475
ConditionNotMatch,
476
/// Content is incomplete
477
ContentIncomplete,
478
/// Content length mismatch
479
ContentTruncated,
480
/// Operation not supported by service
481
Unsupported,
482
/// Unexpected error (catch-all)
483
Unexpected,
484
}
485
486
impl ErrorKind {
487
/// Check if error is retryable
488
pub fn is_retryable(&self) -> bool;
489
490
/// Check if error is temporary
491
pub fn is_temporary(&self) -> bool;
492
}
493
494
/// Operation type for error context
495
#[derive(Debug, Clone, Copy)]
496
pub enum Operation {
497
Read,
498
Write,
499
Stat,
500
Delete,
501
List,
502
Copy,
503
Rename,
504
CreateDir,
505
Presign,
506
Batch,
507
}
508
509
/// Standard Result type
510
pub type Result<T> = std::result::Result<T, Error>;
511
```
512
513
**Usage Examples:**
514
515
```rust
516
use opendal::{Error, ErrorKind, Result};
517
518
// Pattern matching on error kinds
519
match op.read("missing-file.txt").await {
520
Ok(data) => println!("Success: {} bytes", data.len()),
521
Err(e) => {
522
match e.kind() {
523
ErrorKind::NotFound => {
524
println!("File not found: {}", e.path());
525
},
526
ErrorKind::PermissionDenied => {
527
println!("Access denied: {}", e.path());
528
},
529
ErrorKind::RateLimited => {
530
println!("Rate limited, retrying later...");
531
// Implement backoff logic
532
},
533
kind if kind.is_retryable() => {
534
println!("Retryable error: {}", e);
535
// Retry logic
536
},
537
_ => {
538
eprintln!("Unexpected error: {}", e);
539
return Err(e);
540
}
541
}
542
}
543
}
544
545
// Error context inspection
546
if let Err(e) = op.write("readonly/file.txt", "data").await {
547
println!("Operation: {:?}", e.operation());
548
println!("Path: {}", e.path());
549
println!("Kind: {:?}", e.kind());
550
551
// Access additional context
552
for (key, value) in e.context() {
553
println!("Context {}: {}", key, value);
554
}
555
}
556
557
// Converting to std::io::Error for compatibility
558
let io_error: std::io::Error = opendal_error.into();
559
```
560
561
### Range and Utility Types
562
563
Supporting types for operations and configurations.
564
565
```rust { .api }
566
use std::ops::{Range, RangeFrom, RangeTo, RangeInclusive, RangeFull, RangeBounds};
567
568
// OpenDAL uses standard Rust range types for byte range operations.
569
// Any type implementing RangeBounds<u64> can be used for range operations:
570
571
// Examples of supported range types:
572
// - Range<u64>: 0..1024 (bytes 0 to 1023)
573
// - RangeFrom<u64>: 1024.. (from byte 1024 to end)
574
// - RangeTo<u64>: ..1024 (from start to byte 1023)
575
// - RangeInclusive<u64>: 0..=1023 (bytes 0 to 1023 inclusive)
576
// - RangeFull: .. (full range, all bytes)
577
578
/// Presigned request details
579
pub struct PresignedRequest {
580
method: http::Method,
581
uri: http::Uri,
582
headers: http::HeaderMap,
583
}
584
585
impl PresignedRequest {
586
/// Get HTTP method
587
pub fn method(&self) -> &http::Method;
588
589
/// Get request URI
590
pub fn uri(&self) -> &http::Uri;
591
592
/// Get HTTP headers
593
pub fn headers(&self) -> &http::HeaderMap;
594
}
595
```
596
597
**Usage Examples:**
598
599
```rust
600
use std::ops::{Range, RangeFrom, RangeTo};
601
602
// OpenDAL accepts standard Rust range types directly
603
let partial_data = op.read_with("large-file.dat")
604
.range(0..8192) // Read first 8KB (Range<u64>)
605
.await?;
606
607
// Different range types supported
608
let from_middle = op.read_with("data.bin")
609
.range(1024..) // From byte 1024 to end (RangeFrom<u64>)
610
.await?;
611
612
let first_kb = op.read_with("file.txt")
613
.range(..1024) // First 1024 bytes (RangeTo<u64>)
614
.await?;
615
616
let inclusive_range = op.read_with("doc.pdf")
617
.range(0..=1023) // First 1024 bytes inclusive (RangeInclusive<u64>)
618
.await?;
619
620
// Content range information is available in metadata for partial responses
621
let metadata = op.stat("file.txt").await?;
622
if let Some(_content_range) = metadata.content_range() {
623
// Content range details are available but typically not needed by users
624
println!("Partial content response received");
625
}
626
```