1use littlefs2_core::DynFilesystem;
2
3#[derive(Debug)]
4pub struct Migrator {
5 pub migrate: fn(&dyn DynFilesystem, &dyn DynFilesystem) -> Result<(), littlefs2_core::Error>,
9
10 pub version: u32,
12}
13
14#[cfg(feature = "migration-tests")]
15pub mod test_utils {
16
17 use littlefs2::{
21 fs::Filesystem, io::Error, object_safe::DynFilesystem, path, path::Path, ram_storage,
22 };
23
24 pub enum FsValues {
26 Dir(&'static [(&'static Path, FsValues)]),
27 File(usize),
28 }
29
30 type Result<T, E = Error> = core::result::Result<T, E>;
31
32 pub fn prepare_fs(fs: &dyn DynFilesystem, value: &FsValues, path: &Path) {
34 match value {
35 FsValues::File(f_data_len) => {
36 fs.create_file_and_then(path, &mut |f| {
37 f.set_len(*f_data_len).unwrap();
38 Ok(())
39 })
40 .unwrap();
41 }
42 FsValues::Dir(d) => {
43 if path != path!("/") {
44 fs.create_dir(path).unwrap();
45 }
46 for (p, v) in *d {
47 prepare_fs(fs, v, &path.join(p));
48 }
49 }
50 }
51 }
52
53 pub fn test_fs_equality(fs: &dyn DynFilesystem, value: &FsValues, path: &Path) {
55 match value {
56 FsValues::Dir(d) => {
57 let mut expected_iter = d.iter();
58 fs.read_dir_and_then(path, &mut |dir| {
59 dir.next().unwrap().unwrap();
61 dir.next().unwrap().unwrap();
62 for (expected_path, expected_values) in expected_iter.by_ref() {
63 let entry = dir.next().unwrap().unwrap();
64 assert_eq!(entry.file_name(), *expected_path);
65 test_fs_equality(fs, expected_values, &path.join(expected_path));
66 }
67 assert!(dir.next().is_none());
68 Ok(())
69 })
70 .unwrap();
71 }
72 FsValues::File(f_data_len) => {
73 fs.open_file_and_then(path, &mut |f| {
74 let mut buf = [0; 512];
75 let data = f.read(&mut buf).unwrap();
76 assert_eq!(data, *f_data_len);
77 Ok(())
78 })
79 .unwrap();
80 }
81 }
82 }
83
84 ram_storage!(
85 name = NoBackendStorage,
86 backend = RamDirect,
87 erase_value = 0xff,
88 read_size = 16,
89 write_size = 16,
90 cache_size_ty = littlefs2::consts::U512,
91 block_size = 512,
92 block_count = 128,
93 lookahead_size_ty = littlefs2::consts::U8,
94 );
95
96 pub fn test_migration_one(
97 before: &FsValues,
98 after: &FsValues,
99 migrate: impl Fn(&dyn DynFilesystem) -> Result<(), Error>,
100 ) {
101 test_migration(
102 before,
103 after,
104 &FsValues::Dir(&[]),
105 &FsValues::Dir(&[]),
106 |ifs, _efs| migrate(ifs),
107 );
108 }
109
110 pub fn test_migration(
111 before_ifs: &FsValues,
112 after_ifs: &FsValues,
113 before_efs: &FsValues,
114 after_efs: &FsValues,
115 migrate: impl Fn(&dyn DynFilesystem, &dyn DynFilesystem) -> Result<(), Error>,
116 ) {
117 let mut storage_ifs = RamDirect::default();
118 let mut storage_efs = RamDirect::default();
119
120 let backend_efs = &mut NoBackendStorage::new(&mut storage_efs);
121 let backend_ifs = &mut NoBackendStorage::new(&mut storage_ifs);
122
123 Filesystem::format(backend_ifs).unwrap();
124 Filesystem::format(backend_efs).unwrap();
125
126 Filesystem::mount_and_then(backend_ifs, |ifs| {
127 Filesystem::mount_and_then(backend_efs, |efs| {
128 prepare_fs(ifs, before_ifs, path!("/"));
129 prepare_fs(efs, before_efs, path!("/"));
130
131 test_fs_equality(ifs, before_ifs, path!("/"));
132 test_fs_equality(efs, before_efs, path!("/"));
133
134 migrate(ifs, efs).unwrap();
135 test_fs_equality(efs, after_efs, path!("/"));
136 test_fs_equality(ifs, after_ifs, path!("/"));
137 Ok(())
138 })
139 .unwrap();
140 Ok(())
141 })
142 .unwrap();
143 }
144}