• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Achiefs / fim / 14275166216

04 Apr 2025 10:13PM UTC coverage: 84.711% (-0.4%) from 85.125%
14275166216

push

github

web-flow
Merge pull request #194 from Achiefs/177-db-hash

Added Hash diff scanner

353 of 433 new or added lines in 12 files covered. (81.52%)

4 existing lines in 1 file now uncovered.

1435 of 1694 relevant lines covered (84.71%)

1.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.65
/src/db.rs
1
// Copyright (C) 2024, Achiefs.
2

3
use crate::dbfile::*;
4
use crate::appconfig::AppConfig;
5

6
use rusqlite::{Connection, Error, params};
7
use rusqlite::Error::QueryReturnedNoRows;
8
use log::*;
9

10
#[derive(Clone)]
11
pub struct DB {
12
    pub path: String
13
}
14

15
#[cfg(test)]
16
mod test;
17

18
// ----------------------------------------------------------------------------
19

20
impl DB {
21
    /// Create a new db object
22
    pub fn new(path: &str) -> DB {
1✔
23
        DB {
24
            path: String::from(path),
1✔
25
        }
26
    }
27

28
    // ------------------------------------------------------------------------
29

30
    /// Open a handle to the db
31
    pub fn open(&self) -> Connection {
1✔
32
        match Connection::open(self.path.clone()) {
1✔
33
            Ok(database) => {
1✔
34
                debug!("Database connection opened ready to read/write");
3✔
35
                database
1✔
36
            }
NEW
37
            Err(e) => {
×
NEW
38
                error!("Database cannot be opened, Err: [{}]", e);
×
NEW
39
                info!("Please, check if {} is locked or in use.", self.path);
×
NEW
40
                panic!();
×
41
            }
42
        }
43
    }
44

45
    // ------------------------------------------------------------------------
46

47
    /// Close the db handle
48
    pub fn close(&self, connection: Connection) {
1✔
49
        match connection.close(){
1✔
50
            Ok(_) => debug!("DB connection closed successfully"),
2✔
NEW
51
            Err(e) => warn!("DB connection could not be closed, error: {:?}", e)
×
52
        };
53
    }
54

55
    // ------------------------------------------------------------------------
56

57
    /// Check if current db is empty
58
    pub fn is_empty(&self) -> bool {
1✔
59
        let connection = self.open();
1✔
60
        let result = connection.query_row("SELECT * FROM files LIMIT 1", [], |_row| Ok(0));
4✔
61
        self.close(connection);
1✔
62
        match result {
1✔
63
            Ok(_v) => false,
1✔
64
            Err(e) => {
1✔
65
                if e == QueryReturnedNoRows {
3✔
NEW
66
                    true
×
67
                } else {
68
                    error!("Could not check if the database is empty, Error: {}", e);
3✔
69
                    true
1✔
70
                }
71
            }
72
        }
73
    }
74

75
    // ------------------------------------------------------------------------
76

77
    /// Create the `files` table where store all files information
78
    /// Defines files table schema
79
    pub fn create_table(&self) {
1✔
80
        let connection = self.open();
1✔
81
        let result = connection.execute(
1✔
82
            "CREATE TABLE IF NOT EXISTS files (
83
                id TEXT PRIMARY KEY,
84
                timestamp TEXT NOT NULL,
85
                hash TEXT NOT NULL,
86
                path TEXT NOT NULL UNIQUE,
87
                size INTEGER,
88
                permissions INTEGER)",
89
            (),
90
        );
91
        match result {
1✔
92
            Ok(_v) => info!("Database successfully created."),
3✔
NEW
93
            Err(e) => error!("Error creating database, Error: '{}'", e)
×
94
        }
95
        self.close(connection);
1✔
96
    }
97

98
    // ------------------------------------------------------------------------
99

100
    /// Insert information of a given DBFile in db
101
    pub fn insert_file(&self, file: DBFile) {
1✔
102
        let connection = self.open();
2✔
103
        let result = connection.execute(
1✔
104
            "INSERT INTO files (id, timestamp, hash, path, size, permissions) VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
105
            (file.id, file.timestamp, file.hash, file.path, file.size, file.permissions)
1✔
106
        );
107
        match result {
1✔
108
            Ok(_) => debug!("Inserted new file in DB"),
3✔
NEW
109
            Err(e) => warn!("Could not insert file in DB (Probably duplicated path), error: {:?}", e)
×
110
        }
111
        self.close(connection);
1✔
112
    }
113

114
    // ------------------------------------------------------------------------
115

116
    /// Retrieve the DBFile object from db, using the path of file
117
    pub fn get_file_by_path(&self, path: String) -> Result<DBFile, DBFileError> {
1✔
118
        let connection = self.open();
2✔
119
        let result = connection.query_row(
1✔
120
            "SELECT * FROM files WHERE path = ?1 LIMIT 1",
121
            [path.clone()],
2✔
122
            |row| Ok(DBFile {
2✔
123
                id: row.get(0).unwrap(),
1✔
124
                timestamp: row.get(1).unwrap(),
2✔
125
                hash: row.get(2).unwrap(),
2✔
126
                path: row.get(3).unwrap(),
2✔
127
                size: row.get(4).unwrap(),
2✔
128
                permissions: row.get(5).unwrap()
1✔
129
            })
130
        );
131

132
        let data = match result {
1✔
133
            Ok(d) => Ok(d),
1✔
134
            Err(e) => {
1✔
135
                match e {
1✔
136
                    Error::QueryReturnedNoRows => Err(DBFileError::not_found_error()),
2✔
137
                    _ => {
NEW
138
                        error!("Could not get file '{}' information in database, Error: {:?}", path, e);
×
NEW
139
                        Err(DBFileError::from(e))
×
140
                    }
141
                }
142
            }
143
        };
144

145
        self.close(connection);
1✔
146
        data
1✔
147
    }
148

149
    // ------------------------------------------------------------------------
150

151
    /// Retrieve a list of files that match the given path
152
    pub fn get_file_list(&self, path: String) -> Vec<DBFile> {
1✔
153
        let connection = self.open();
1✔
154
        let mut list = Vec::new();
1✔
155
        let query = format!("SELECT * FROM files WHERE path LIKE '{}%'", path);
2✔
156
        let mut statement = connection.prepare_cached(&query).unwrap();
2✔
157
        let result = statement.query_map([], |row| {
3✔
158
            Ok(DBFile {
1✔
159
                id: row.get(0).unwrap(),
1✔
160
                timestamp: row.get(1).unwrap(),
2✔
161
                hash: row.get(2).unwrap(),
2✔
162
                path: row.get(3).unwrap(),
2✔
163
                size: row.get(4).unwrap(),
2✔
164
                permissions: row.get(5).unwrap()
1✔
165
            })
166
        });
167
        match result {
1✔
168
            Ok(mapped_rows) => {
1✔
169
                for file in mapped_rows {
4✔
170
                    list.push(file.unwrap())
2✔
171
                }
172
            },
NEW
173
            Err(e) => error!("Could not get database file list, error: {:?}", e)
×
174
        };
175
        list
1✔
176
    }
177

178
    // ------------------------------------------------------------------------
179

180
    /// Update db information of the given DBFile information
181
    pub fn update_file(&self, cfg: AppConfig, dbfile: DBFile) -> Option<DBFile>{
1✔
182
        let connection = self.open();
1✔
183
        let current_dbfile = DBFile::new(cfg, &dbfile.path, Some(dbfile.id));
1✔
184
        let query = "UPDATE files SET timestamp = ?1, hash = ?2, size = ?3, permissions = ?4 WHERE id = ?5";
1✔
185

186
        let mut statement = connection.prepare(query).unwrap();
2✔
187
        let result = statement.execute(params![
1✔
188
            current_dbfile.timestamp,
189
            current_dbfile.hash,
190
            current_dbfile.size,
191
            current_dbfile.permissions,
192
            current_dbfile.id]);
193
        match result {
1✔
194
            Ok(_v) => {
1✔
195
                debug!("File '{}', updated with new information.", dbfile.path);
3✔
196
                Some(current_dbfile)
1✔
197
            },
NEW
198
            Err(e) => {
×
NEW
199
                error!("Cannot update file '{}' information, Error: {:?}", dbfile.path, e);
×
NEW
200
                None
×
201
            }
202
        }
203
    }
204

205
    // ------------------------------------------------------------------------
206

207
    /// Delete information inside db related to the given DBFile
208
    pub fn delete_file(&self, dbfile: DBFile) -> Result<u8, DBFileError>{
1✔
209
        let connection = self.open();
1✔
210
        let mut statement = connection.prepare("DELETE FROM files WHERE id = ?1").unwrap();
2✔
211
        let result = statement.execute(params![dbfile.id]);
1✔
212
        match result {
1✔
213
            Ok(_v) => {
1✔
214
                debug!("File '{}', deleted from database.", dbfile.path);
3✔
215
                Ok(0)
1✔
216
            },
NEW
217
            Err(e) => {
×
NEW
218
                error!("Cannot delete file '{}' information, Error: {:?}", dbfile.path, e);
×
NEW
219
                Err(DBFileError::from(e))
×
220
            }
221
        }
222
    }
223
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc