wildland_lfs/
lib.rs

1//
2// Wildland Project
3//
4// Copyright © 2022 Golem Foundation
5//
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License version 3 as published by
8// the Free Software Foundation.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program.  If not, see <https://www.gnu.org/licenses/>.
17
18mod template;
19
20use std::fs::read_dir;
21use std::path::{Path, PathBuf};
22use std::rc::Rc;
23
24use anyhow::anyhow;
25use template::LocalFilesystemStorageTemplate;
26use wildland_dfs::storage_backend::StorageBackend;
27use wildland_dfs::unencrypted::StorageBackendFactory;
28use wildland_dfs::Storage;
29
30#[derive(Debug)]
31pub struct LocalFilesystemStorage {
32    base_dir: PathBuf,
33}
34
35impl StorageBackend for LocalFilesystemStorage {
36    fn readdir(&self, path: &Path) -> Result<Vec<PathBuf>, anyhow::Error> {
37        let relative_path = if path.is_absolute() {
38            path.strip_prefix("/").unwrap()
39        } else {
40            path
41        };
42        read_dir(self.base_dir.join(relative_path))
43            .map_err(|e| anyhow!(e))
44            .and_then(|readdir| {
45                readdir
46                    .into_iter()
47                    .map(|entry_result| {
48                        Ok(Path::new("/").join(entry_result?.path().strip_prefix(&self.base_dir)?))
49                    })
50                    .collect::<Result<Vec<_>, anyhow::Error>>()
51            })
52    }
53}
54
55pub struct LfsBackendFactory {}
56impl StorageBackendFactory for LfsBackendFactory {
57    fn init_backend(&self, storage: Storage) -> Result<Rc<dyn StorageBackend>, anyhow::Error> {
58        let template: LocalFilesystemStorageTemplate =
59            serde_json::from_value(storage.data().clone())?;
60        Ok(Rc::new(LocalFilesystemStorage {
61            base_dir: template.local_dir.join(template.container_prefix),
62        }))
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use std::fs::{create_dir, File};
69    use std::str::FromStr;
70
71    use pretty_assertions::assert_eq;
72    use serde_json::json;
73    use tempdir::TempDir;
74
75    use super::*;
76
77    #[test]
78    fn test_reading_file_in_root_of_lfs_backend() {
79        let tmpdir = TempDir::new("lfs").unwrap(); // storage provider dir
80        let storage = Storage::new(
81            Some("Test LFS".to_owned()),
82            "LFS".to_owned(),
83            json!({
84                "local_dir": tmpdir.path(),
85                "container_prefix": "books"
86            }),
87        );
88        let factory = LfsBackendFactory {};
89        let backend = factory.init_backend(storage).unwrap();
90
91        create_dir(tmpdir.path().join("books")).unwrap(); // container dir
92        let _ = File::create(tmpdir.path().join("books/file1")).unwrap();
93
94        let files = backend.readdir(Path::new("/")).unwrap();
95
96        assert_eq!(files, vec![PathBuf::from_str("/file1").unwrap()]);
97    }
98    #[test]
99    fn test_reading_file_in_subdir_of_lfs_backend() {
100        let tmpdir = TempDir::new("lfs").unwrap(); // storage provider dir
101        let storage = Storage::new(
102            Some("Test LFS".to_owned()),
103            "LFS".to_owned(),
104            json!({
105                "local_dir": tmpdir.path(),
106                "container_prefix": "books"
107            }),
108        );
109        let factory = LfsBackendFactory {};
110        let backend = factory.init_backend(storage).unwrap();
111
112        create_dir(tmpdir.path().join("books")).unwrap(); // container dir
113        create_dir(tmpdir.path().join("books").join("dir")).unwrap(); // container subdir
114        let _ = File::create(tmpdir.path().join("books/dir/file1")).unwrap();
115
116        let files = backend.readdir(Path::new("/dir")).unwrap();
117
118        assert_eq!(files, vec![PathBuf::from_str("/dir/file1").unwrap()]);
119    }
120}