lrcat/
folders.rs

1/*
2 This Source Code Form is subject to the terms of the Mozilla Public
3 License, v. 2.0. If a copy of the MPL was not distributed with this
4 file, You can obtain one at http://mozilla.org/MPL/2.0/.
5*/
6
7use rusqlite::{Connection, Row};
8
9use crate::catalog::CatalogVersion;
10use crate::content::Content;
11use crate::fromdb::FromDb;
12use crate::lrobject::{LrId, LrObject};
13
14/// A folder define the container for `LibraryFiles`
15/// They are all attached to a `RootFolder`
16#[derive(Clone)]
17pub struct Folder {
18    id: LrId,
19    uuid: String,
20    /// Path from the `RootFolder`
21    pub path_from_root: String,
22    /// Id of the `RootFolder`
23    pub root_folder: LrId,
24    pub content: Option<Content>,
25}
26
27impl LrObject for Folder {
28    fn id(&self) -> LrId {
29        self.id
30    }
31    fn uuid(&self) -> &str {
32        &self.uuid
33    }
34}
35
36impl FromDb for Folder {
37    fn read_from(_version: CatalogVersion, row: &Row) -> crate::Result<Self> {
38        Ok(Folder {
39            id: row.get(0)?,
40            uuid: row.get(1)?,
41            path_from_root: row.get(2)?,
42            root_folder: row.get(3)?,
43            content: None,
44        })
45    }
46    fn read_db_tables(_version: CatalogVersion) -> &'static str {
47        "AgLibraryFolder"
48    }
49    fn read_db_columns(_version: CatalogVersion) -> &'static str {
50        "id_local,id_global,pathFromRoot,rootFolder"
51    }
52}
53
54impl Folder {
55    pub fn new(id: LrId, uuid: &str) -> Folder {
56        Folder {
57            id,
58            uuid: String::from(uuid),
59            path_from_root: String::from(""),
60            root_folder: 0,
61            content: None,
62        }
63    }
64    pub fn read_content(&self, conn: &Connection) -> Content {
65        Content::from_db(conn, "AgFolderContent", "containingFolder", self.id)
66    }
67}
68
69/// Represent the ancestor of `Folder` and map to
70/// an absolute path
71#[derive(Clone)]
72pub struct RootFolder {
73    id: LrId,
74    uuid: String,
75    /// Absolute path of the `RootFolder`
76    pub absolute_path: String,
77    /// (User readable) name of the `RootFolder`
78    pub name: String,
79    /// Eventually if it is possible the path is relative
80    /// to the catalog file.
81    pub relative_path_from_catalog: Option<String>,
82}
83
84impl LrObject for RootFolder {
85    fn id(&self) -> LrId {
86        self.id
87    }
88    fn uuid(&self) -> &str {
89        &self.uuid
90    }
91}
92
93impl RootFolder {
94    /// Create a new `RootFolder` with an id and uuid
95    pub fn new(id: LrId, uuid: &str) -> RootFolder {
96        RootFolder {
97            id,
98            uuid: String::from(uuid),
99            absolute_path: String::from(""),
100            name: String::from(""),
101            relative_path_from_catalog: None,
102        }
103    }
104}
105
106impl FromDb for RootFolder {
107    fn read_from(_version: CatalogVersion, row: &Row) -> crate::Result<Self> {
108        Ok(RootFolder {
109            id: row.get(0)?,
110            uuid: row.get(1)?,
111            absolute_path: row.get(2)?,
112            name: row.get(3)?,
113            relative_path_from_catalog: row.get(4).ok(),
114        })
115    }
116
117    fn read_db_tables(_version: CatalogVersion) -> &'static str {
118        "AgLibraryRootFolder"
119    }
120
121    fn read_db_columns(_version: CatalogVersion) -> &'static str {
122        "id_local,id_global,absolutePath,name,relativePathFromCatalog"
123    }
124}
125
126/// Represent all the folders
127#[derive(Clone, Default)]
128pub struct Folders {
129    /// The `RootFolder` list
130    pub roots: Vec<RootFolder>,
131    /// The `Folder` list
132    pub folders: Vec<Folder>,
133}
134
135impl Folders {
136    pub fn new() -> Folders {
137        Folders::default()
138    }
139
140    /// Return `true` is it is empty
141    pub fn is_empty(&self) -> bool {
142        self.roots.is_empty() && self.folders.is_empty()
143    }
144
145    /// Add a `Folder`
146    pub fn add_folder(&mut self, folder: Folder) {
147        self.folders.push(folder);
148    }
149
150    /// Add a `RootFolder`
151    pub fn add_root_folder(&mut self, root_folder: RootFolder) {
152        self.roots.push(root_folder);
153    }
154
155    /// Append a vector of `Folder`
156    pub fn append_folders(&mut self, mut folders: Vec<Folder>) {
157        self.folders.append(&mut folders);
158    }
159
160    /// Append a vector of `RootFolder`
161    pub fn append_root_folders(&mut self, mut root_folders: Vec<RootFolder>) {
162        self.roots.append(&mut root_folders);
163    }
164
165    /// Return the eventual `RootFolder` with the id.
166    pub fn find_root_folder(&self, id: LrId) -> Option<&RootFolder> {
167        self.roots.iter().find(|&root| root.id() == id)
168    }
169
170    /// Resolve the folder path by providing an absolute path
171    /// This does not check if the path exist but merely combine
172    /// the `RootFolder` absolute_path and the `Folder` relative path
173    pub fn resolve_folder_path(&self, folder: &Folder) -> Option<String> {
174        let root_folder = self.find_root_folder(folder.root_folder)?;
175        let mut root_path = root_folder.absolute_path.clone();
176        root_path += &folder.path_from_root;
177        Some(root_path)
178    }
179}
180
181#[cfg(test)]
182#[test]
183fn test_resolve_folder_path() {
184    let mut folders = Folders::new();
185
186    let mut rfolder = RootFolder::new(24, "toplevel");
187    rfolder.absolute_path = String::from("/home/hub/Pictures");
188    rfolder.name = String::from("Pictures");
189    folders.add_root_folder(rfolder);
190
191    let mut folder = Folder::new(42, "foobar");
192    folder.root_folder = 24;
193    folder.path_from_root = String::from("/2017/10");
194    folders.add_folder(folder);
195
196    let resolved = folders.resolve_folder_path(&folders.folders[0]);
197    assert!(resolved.is_some());
198    let resolved = resolved.unwrap();
199    assert_eq!(resolved, "/home/hub/Pictures/2017/10");
200}