tokio_fs_ext/fs/wasm/
open_options.rs

1use std::{io, path::Path};
2
3use bitflags::bitflags;
4use futures::io::AsyncSeekExt;
5
6use super::{
7    File,
8    opfs::{CreateFileMode, SyncAccessMode, open_file},
9};
10
11bitflags! {
12    #[derive(Clone , Debug, Copy)]
13    struct Flags: u8 {
14        const READ = 1 << 0;
15        const WRITE= 1 << 1;
16        const APPEND = 1 << 2;
17        const CREATE = 1 << 3;
18        const TRUNCATE = 1 << 4;
19        const CREATE_NEW = 1 << 5;
20    }
21}
22
23impl Default for Flags {
24    fn default() -> Self {
25        Flags::READ
26    }
27}
28
29#[derive(Clone, Debug, Copy)]
30pub struct OpenOptions(Flags);
31
32impl OpenOptions {
33    pub fn new() -> OpenOptions {
34        OpenOptions(Flags::READ)
35    }
36
37    pub fn read(&mut self, read: bool) -> &mut OpenOptions {
38        if read {
39            self.0 |= Flags::READ;
40        } else {
41            self.0.remove(Flags::READ);
42        }
43        self
44    }
45
46    pub fn write(&mut self, write: bool) -> &mut OpenOptions {
47        if write {
48            self.0 |= Flags::WRITE;
49        } else {
50            self.0.remove(Flags::WRITE)
51        }
52        self
53    }
54
55    pub fn append(&mut self, append: bool) -> &mut OpenOptions {
56        if append {
57            self.0 |= Flags::APPEND;
58        } else {
59            self.0.remove(Flags::APPEND)
60        }
61        self
62    }
63
64    pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions {
65        if truncate {
66            self.0 |= Flags::TRUNCATE;
67        } else {
68            self.0.remove(Flags::TRUNCATE);
69        }
70        self
71    }
72
73    pub fn create(&mut self, create: bool) -> &mut OpenOptions {
74        if create {
75            self.0 |= Flags::CREATE;
76        } else {
77            self.0.remove(Flags::CREATE);
78        }
79        self
80    }
81
82    pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions {
83        if create_new {
84            self.0 |= Flags::CREATE_NEW;
85        } else {
86            self.0.remove(Flags::CREATE_NEW);
87        }
88        self
89    }
90
91    pub async fn open(&self, path: impl AsRef<Path>) -> io::Result<File> {
92        if self.is_invalid() {
93            return Err(io::Error::from(io::ErrorKind::InvalidInput));
94        }
95
96        let mut file = open_file(path, self.into(), self.into(), self.is_truncate()).await?;
97
98        if self.0.contains(Flags::APPEND) {
99            file.seek(io::SeekFrom::End(0)).await?;
100        }
101
102        Ok(file)
103    }
104}
105
106impl OpenOptions {
107    fn is_invalid(&self) -> bool {
108        self.0
109            .contains(Flags::CREATE | Flags::CREATE_NEW | Flags::TRUNCATE | Flags::APPEND)
110            && !self.0.contains(Flags::WRITE)
111    }
112
113    fn is_truncate(&self) -> bool {
114        self.0.contains(Flags::TRUNCATE | Flags::CREATE)
115    }
116}
117
118impl Default for OpenOptions {
119    fn default() -> Self {
120        Self::new()
121    }
122}
123
124impl From<&OpenOptions> for CreateFileMode {
125    fn from(options: &OpenOptions) -> Self {
126        if options.0.contains(Flags::CREATE) {
127            CreateFileMode::Create
128        } else if options.0.contains(Flags::CREATE_NEW) {
129            CreateFileMode::CreateNew
130        } else {
131            CreateFileMode::NotCreate
132        }
133    }
134}
135
136impl From<&OpenOptions> for SyncAccessMode {
137    fn from(options: &OpenOptions) -> Self {
138        if options.0.contains(Flags::WRITE) {
139            SyncAccessMode::ReadwriteUnsafe
140        } else {
141            SyncAccessMode::Readonly
142        }
143    }
144}