persy/
open_options.rs

1use crate::{
2    config::Config,
3    error::{OpenError, OpenMemoryError, PE},
4    persy::PersyImpl,
5    Persy, Recover,
6};
7use std::{fs, fs::File, path::Path, sync::Arc};
8
9/// Options, flags, configs which can be used
10/// to configure how a persy database is opened.
11///
12/// ```
13/// use persy::{OpenOptions, Persy, PersyId, ValueMode};
14///
15/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
16/// // This function will only be called on database creation
17/// fn init(persy: &Persy) -> Result<(), Box<dyn std::error::Error>> {
18///     let mut tx = persy.begin()?;
19///
20///     tx.create_segment("data")?;
21///     tx.create_index::<u64, PersyId>("index", ValueMode::Replace)?;
22///
23///     let prepared = tx.prepare()?;
24///     prepared.commit()?;
25///
26///     println!("Segment and Index successfully created");
27///     Ok(())
28/// }
29///
30/// let persy = OpenOptions::new().create(true).prepare_with(init).open("target/persy.db")?;
31/// # std::fs::remove_file("target/persy.db")?;
32/// # Ok(())
33/// # }
34/// ```
35pub struct OpenOptions {
36    truncate: bool,
37    create: bool,
38    create_new: bool,
39    config: Config,
40    prepare: Option<Box<dyn Fn(&Persy) -> Result<(), Box<dyn std::error::Error>>>>,
41    recover: Option<Box<dyn Fn(&Vec<u8>) -> bool>>,
42}
43
44impl OpenOptions {
45    pub fn new() -> OpenOptions {
46        OpenOptions {
47            truncate: false,
48            create: false,
49            create_new: false,
50            config: Config::new(),
51            prepare: None,
52            recover: None,
53        }
54    }
55    /// Truncate the file on open removing all the persistent data
56    pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions {
57        self.truncate = truncate;
58        self
59    }
60
61    /// Create a new file if not exists
62    pub fn create(&mut self, create: bool) -> &mut OpenOptions {
63        self.create = create;
64        self
65    }
66
67    /// Create a new file if exists fail
68    pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions {
69        self.create_new = create_new;
70        self
71    }
72
73    /// Provide a function for initialize the storage in case did not existed
74    ///
75    pub fn prepare_with<F>(&mut self, prepare: F) -> &mut OpenOptions
76    where
77        F: Fn(&Persy) -> Result<(), Box<dyn std::error::Error>> + 'static,
78    {
79        self.prepare = Some(Box::new(prepare));
80        self
81    }
82
83    /// Provide a function for check if a transaction must be committed or rollback in case of
84    /// recover from crash
85    pub fn recover_with<F>(&mut self, recover: F) -> &mut OpenOptions
86    where
87        F: Fn(&Vec<u8>) -> bool + 'static,
88    {
89        self.recover = Some(Box::new(recover));
90        self
91    }
92
93    /// Provide general storage configurations
94    pub fn config(&mut self, config: Config) -> &mut OpenOptions {
95        self.config = config;
96        self
97    }
98
99    /// Open a file to a recover structure to list pending transactions and select witch commit and
100    /// rollback
101    pub fn recover<P>(&mut self, path: P) -> Result<Recover, PE<OpenError>>
102    where
103        P: AsRef<Path>,
104    {
105        let path = path.as_ref();
106        let exists = path.exists();
107
108        let file = fs::OpenOptions::new()
109            .read(true)
110            .write(true)
111            .create(self.create)
112            .create_new(self.create_new)
113            .open(path)?;
114        self.int_recover_file(file, exists)
115    }
116
117    /// Open a file to a recover structure to list pending transactions and select witch commit and
118    /// rollback
119    pub fn recover_file(&mut self, file: File) -> Result<Recover, PE<OpenError>> {
120        self.int_recover_file(file, true)
121    }
122    /// Open a file to a recover structure to list pending transactions and select witch commit and
123    /// rollback
124    fn int_recover_file(&mut self, file: File, exists: bool) -> Result<Recover, PE<OpenError>> {
125        let must_prepare = !exists || self.truncate;
126
127        let config = self.config.clone();
128
129        if must_prepare {
130            let (persy_impl, recov) = if self.truncate {
131                PersyImpl::truncate_and_open(file, config)?
132            } else {
133                PersyImpl::create_and_open(file, config)?
134            };
135            let p = Arc::new(persy_impl);
136            if let Some(prepare) = &mut self.prepare {
137                let persy = Persy { persy_impl: p.clone() };
138                (prepare)(&persy).map_err(|e| OpenError::InitError(format!("{}", e)))?;
139            }
140            Ok(Recover::new(recov, p))
141        } else if let Some(recover) = &self.recover {
142            let (persy_impl, mut recov) = PersyImpl::open_recover(file, config)?;
143            recov.apply(recover)?;
144            Ok(Recover::new(recov, Arc::new(persy_impl)))
145        } else {
146            let (persy_impl, recov) = PersyImpl::open_recover(file, config)?;
147            Ok(Recover::new(recov, Arc::new(persy_impl)))
148        }
149    }
150
151    /// Open a file from the given path with the current options
152    pub fn open<P>(&mut self, path: P) -> Result<Persy, PE<OpenError>>
153    where
154        P: AsRef<Path>,
155    {
156        let recover = self.recover(path)?;
157        recover.finalize().map_err(|e| PE::PE(OpenError::from(e.error())))
158    }
159
160    /// Create a persy instance backed by a `Vec<u8>` with no persistence
161    ///
162    ///
163    /// # Example
164    /// ```
165    /// use persy::{OpenOptions, Persy, PersyId, ValueMode};
166    ///
167    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
168    ///
169    /// let persy = OpenOptions::new().memory()?;
170    /// # Ok(())
171    /// # }
172    /// ```
173    pub fn memory(&mut self) -> Result<Persy, PE<OpenMemoryError>> {
174        let config = self.config.clone();
175        let persy_impl = PersyImpl::memory(config)?;
176        let persy = Persy {
177            persy_impl: Arc::new(persy_impl),
178        };
179        if let Some(prepare) = &mut self.prepare {
180            (prepare)(&persy).map_err(|e| OpenMemoryError::InitError(format!("{}", e)))?;
181        }
182        Ok(persy)
183    }
184}
185
186impl Default for OpenOptions {
187    fn default() -> OpenOptions {
188        OpenOptions::new()
189    }
190}