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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// SPDX-License-Identifier: GPL-3.0
//! # Description
//!
//! This crate introduces the Rollback struct, a whole rollback mechanism for file system
//! transactions. Rollback allows to atomically create/modify files and to create directories, so
//! the file system won't be affected by these changes unless the Rollback instance is committed.
//!
//! # Examples
//!
//! A successful execution of a program using the Rollback struct may look like the code below. As
//! it's shown in the example, all the files and directories are successfully committed.
//!
//! ```
//! use fs_rollback::Rollback;
//! use std::fs::File;
//!
//! let tempdir = tempfile::tempdir().unwrap();
//!
//! // Some file that already exists
//! let existing_file = tempdir.path().join("file.txt");
//! File::create(&existing_file).unwrap();
//! std::fs::write(&existing_file, "Hello world!").unwrap();
//! assert_eq!("Hello world!", std::fs::read_to_string(&existing_file).unwrap());
//!
//! // Some dirs that don't exist yet
//! let dir1 = tempdir.path().join("dir1");
//! let dir2 = dir1.join("dir2");
//! let dir3 = tempdir.path().join("dir3");
//! assert!(!dir1.is_dir());
//! assert!(!dir2.is_dir());
//! assert!(!dir3.is_dir());
//!
//! // Some files that don't exist yet, even if they're contained in one of the non-existing dirs above
//! let new_file1 = dir2.join("file1.txt");
//! let new_file2 = tempdir.path().join("file2.txt");
//! assert!(!new_file1.is_file());
//! assert!(!new_file2.is_file());
//!
//! // Rollback instance with capacity for the needed paths
//! let mut rollback = Rollback::with_capacity(1,2,3);
//! rollback.note_file(&existing_file).unwrap();
//! rollback.new_file(&new_file1).unwrap();
//! rollback.new_file(&new_file2).unwrap();
//! rollback.new_dir(&dir1).unwrap();
//! rollback.new_dir(&dir2).unwrap();
//! rollback.new_dir(&dir3).unwrap();
//!
//! // Some operations with the new files and the noted files.
//! std::fs::write(rollback.get_noted_file(&existing_file).unwrap(),"Happy to commit this").unwrap();
//! std::fs::write(rollback.get_new_file(&new_file1).unwrap(),"Happy to commit this").unwrap();
//!
//! // If everything went well, we can commit our changes to the fs
//! rollback.commit().unwrap();
//!
//! // And everything's commited!
//! assert!(dir1.is_dir());
//! assert!(dir2.is_dir());
//! assert!(dir3.is_dir());
//! assert!(new_file1.is_file());
//! assert!(new_file2.is_file());
//! assert_eq!("Happy to commit this", std::fs::read_to_string(&existing_file).unwrap());
//! assert_eq!("Happy to commit this", std::fs::read_to_string(&new_file1).unwrap());
//! ```
//!
//! There's plenty of reasons leading to a failing execution, all of them resulting in an unaltered
//! file system. Visit the Rollback struct docs to learn about all the available methods, as well
//! as their possible errors.
//!
//! This example shows that a failed commit rollbacks everything.
//!
//! ```
//! use fs_rollback::{Rollback, Error};
//! use std::fs::File;
//!
//! let tempdir = tempfile::tempdir().unwrap();
//!
//! // Some file that already exists
//! let existing_file = tempdir.path().join("file.txt");
//! File::create(&existing_file).unwrap();
//! std::fs::write(&existing_file, "Hello world!").unwrap();
//! assert_eq!("Hello world!", std::fs::read_to_string(&existing_file).unwrap());
//!
//! // Some dirs that don't exist yet
//! let dir1 = tempdir.path().join("dir1");
//! let dir2 = dir1.join("dir2");
//! assert!(!dir1.is_dir());
//! assert!(!dir2.is_dir());
//!
//! // A file that doesn't exist and that cannot be committed due to its parent dir doesn't exist
//! // and that won't be noted by the rollback. This file will cause that the rollback commit
//! // fails.
//! let new_file1 = dir2.join("file1.txt");
//! assert!(!new_file1.is_file());
//!
//! // Rollback instance with capacity for the needed paths
//! let mut rollback = Rollback::with_capacity(1,1,2);
//! rollback.note_file(&existing_file).unwrap();
//! rollback.new_file(&new_file1).unwrap();
//! rollback.new_dir(&dir1).unwrap();
//!
//! // Some operations with the new files and the noted files.
//! std::fs::write(rollback.get_noted_file(&existing_file).unwrap(),"Happy to commit this").unwrap();
//! std::fs::write(rollback.get_new_file(&new_file1).unwrap(),"Happy to commit this").unwrap();
//!
//! // If everything went well, we can commit our changes to the fs
//! match rollback.commit(){
//! Err(Error::Commit(item, err)) => {
//! // The error specifies the uncommited file
//! assert_eq!(item, format!("{}",new_file1.display()));
//! // As the error's originated by a not existing directory, the message also explains
//! // that
//! assert!(err.contains("No such file or directory"));
//! },
//! _ => panic!("Unexpected error")
//! }
//!
//! // And everything's rolled back!
//! assert!(!dir1.is_dir());
//! assert!(!dir2.is_dir());
//! assert!(!new_file1.is_file());
//! assert_eq!("Hello world!", std::fs::read_to_string(&existing_file).unwrap());
//! ```
//!
//! While this example shows that uncommitted changes are just discarded if the Rollback instance
//! goes out of scope.
//!
//! ```
//! use fs_rollback::Rollback;
//! use std::path::PathBuf;
//!
//! let tempdir = tempfile::tempdir().unwrap();
//! let new_file = tempdir.path().join("file.txt");
//! let mut tempfile = PathBuf::new();
//! assert!(!new_file.is_file());
//! assert!(!tempfile.is_file());
//!
//! {
//! let mut rollback = Rollback::default();
//! rollback.new_file(&new_file).unwrap();
//! tempfile = rollback.get_new_file(&new_file).unwrap().to_path_buf();
//! assert!(tempfile.is_file());
//! std::fs::write(&tempfile,"Hello world!").unwrap();
//! }
//!
//! assert!(!new_file.is_file());
//! // tempfile contains a path to the temporary file created by the rollback, but as the rollback
//! // went out of scope, that path doesn't point anymore to an existing file. That file was
//! // discarded.
//! assert!(!tempfile.is_file() && tempfile != PathBuf::new());
//! ```
pub use Error;
pub use Rollback;