1
use std::io::{Read, Seek, SeekFrom, Write};
2
use std::mem::size_of;
3

            
4
use bonsaidb_files::{BonsaiFiles, FileConfig, FilesSchema};
5
use bonsaidb_local::config::{Builder, StorageConfiguration};
6
use bonsaidb_local::Database;
7

            
8
1
#[cfg_attr(test, test)]
9
1
fn main() -> anyhow::Result<()> {
10
    // Create a database for our files. If you would like to use these
11
    // collections in an existing datasbase/schema, `BonsaiFiles` exposes a
12
    // function `define_collections()` which can be called from your
13
    // `Schema::define_collections()` implementation.
14
    //
15
    // Or, if you're using the Schema derive macro, you can add a parameter
16
    // `include = [FilesSchema<BonsaiFiles>]` to use BonsaiFiles within your
17
    // existing schema.
18
1
    let database = Database::open::<FilesSchema<BonsaiFiles>>(StorageConfiguration::new(
19
1
        "basic-files.bonsaidb",
20
1
    ))?;
21

            
22
    // This crate provides a very basic path-based file storage. Documents can
23
    // be up to 4GB in size, but must be loaded completely to access. Files
24
    // stored using `bonsaidb-files` are broken into blocks and can be streamed
25
    // and/or randomly accessed.
26
    //
27
    // The `BonsaiFiles` type implements `FileConfig` and defines a block size
28
    // of 64kb.
29
1
    let mut one_megabyte = Vec::with_capacity(1024 * 1024);
30
262144
    for i in 0..one_megabyte.capacity() / size_of::<u32>() {
31
262144
        // Each u32 in the file will be the current offset in the file.
32
262144
        let offset = u32::try_from(i * size_of::<u32>()).unwrap();
33
262144
        one_megabyte.extend(offset.to_be_bytes());
34
262144
    }
35
1
    let mut file = BonsaiFiles::build("some-file")
36
1
        .contents(&one_megabyte)
37
1
        .create(&database)?;
38

            
39
    // By default, files will be stored at the root level:
40
1
    assert_eq!(file.path(), "/some-file");
41

            
42
    // We can access this file's contents using `std::io::Read` and
43
    // `std::io::Seek`.
44
1
    let mut contents = file.contents()?;
45
1
    assert_eq!(contents.len(), u64::try_from(one_megabyte.len()).unwrap());
46
1
    contents.seek(SeekFrom::Start(1024))?;
47
1
    let mut offset = [0; size_of::<u32>()];
48
1
    contents.read_exact(&mut offset)?;
49
1
    let offset = u32::from_be_bytes(offset);
50
1
    assert_eq!(offset, 1024);
51
1
    drop(contents);
52
1

            
53
1
    // Files can be appended to, but existing contents cannot be modified.
54
1
    // `File::append()` can be used to write data that you have in memory.
55
1
    // Alternatively, a buffered writer can be used to write larger amounts of
56
1
    // data using `std::io::Write`.
57
1
    let mut writer = file.append_buffered();
58
1
    let mut reader = &one_megabyte[..];
59
1
    let bytes_written = std::io::copy(&mut reader, &mut writer)?;
60
1
    assert_eq!(bytes_written, u64::try_from(one_megabyte.len()).unwrap());
61
1
    writer.flush()?;
62
    // The writer will attempt to flush on drop if there are any bytes remaining
63
    // in the buffer. Any errors will be ignored, however, so it is safer to
64
    // flush where you can handle the error.
65
1
    drop(writer);
66

            
67
    // Verify the file has the new contents.
68
1
    let contents = file.contents()?;
69
1
    assert_eq!(
70
1
        contents.len(),
71
1
        u64::try_from(one_megabyte.len()).unwrap() * 2
72
1
    );
73

            
74
    // Clean up the file.
75
1
    file.delete()?;
76

            
77
1
    Ok(())
78
1
}