diff options
author | Lars Wirzenius <liw@liw.fi> | 2020-11-10 07:30:32 +0200 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2020-11-10 10:46:07 +0200 |
commit | 6a5a9854eb90b767b668403928e2c64091929b51 (patch) | |
tree | e17847eea9be6df26da86669008185b2ac2ec967 /src/generation.rs | |
parent | 6707f65cc6152bc1a5f54331e921d4fac5f597fc (diff) | |
download | obnam2-6a5a9854eb90b767b668403928e2c64091929b51.tar.gz |
feat: restore a generation
Diffstat (limited to 'src/generation.rs')
-rw-r--r-- | src/generation.rs | 62 |
1 files changed, 57 insertions, 5 deletions
diff --git a/src/generation.rs b/src/generation.rs index ca1f8d5..b9edb74 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -1,9 +1,11 @@ -use crate::fsentry::FilesystemEntry; +use crate::fsentry::{FilesystemEntry, FilesystemKind}; +use std::ffi::OsStr; +use std::os::unix::ffi::OsStrExt; //use crate::fsiter::FsIterator; use crate::chunkid::ChunkId; -use rusqlite::{params, Connection, OpenFlags, Transaction}; +use rusqlite::{params, Connection, OpenFlags, Row, Transaction}; use std::os::unix::ffi::OsStringExt; -use std::path::Path; +use std::path::{Path, PathBuf}; /// A backup generation. pub struct Generation { @@ -12,7 +14,7 @@ pub struct Generation { } impl Generation { - pub fn new<P>(filename: P) -> anyhow::Result<Self> + pub fn create<P>(filename: P) -> anyhow::Result<Self> where P: AsRef<Path>, { @@ -30,6 +32,16 @@ impl Generation { Ok(Self { conn, fileno: 0 }) } + pub fn open<P>(filename: P) -> anyhow::Result<Self> + where + P: AsRef<Path>, + { + let flags = OpenFlags::SQLITE_OPEN_READ_WRITE; + let conn = Connection::open_with_flags(filename, flags)?; + conn.pragma_update(None, "journal_mode", &"WAL")?; + Ok(Self { conn, fileno: 0 }) + } + pub fn insert(&mut self, e: FilesystemEntry, ids: &[ChunkId]) -> anyhow::Result<()> { let t = self.conn.transaction()?; insert_one(&t, e, self.fileno, ids)?; @@ -51,6 +63,46 @@ impl Generation { t.commit()?; Ok(()) } + + pub fn files(&self) -> anyhow::Result<Vec<(u64, FilesystemEntry)>> { + let mut stmt = self.conn.prepare("SELECT * FROM files")?; + let iter = stmt.query_map(params![], |row| row_to_entry(row))?; + let mut files: Vec<(u64, FilesystemEntry)> = vec![]; + for x in iter { + let (fileid, entry) = x?; + files.push((fileid, entry)); + } + Ok(files) + } + + pub fn chunkids(&self, fileid: u64) -> anyhow::Result<Vec<ChunkId>> { + let fileid = fileid as i64; + let mut stmt = self + .conn + .prepare("SELECT chunkid FROM chunks WHERE fileid = ?1")?; + let iter = stmt.query_map(params![fileid], |row| Ok(row.get(0)?))?; + let mut ids: Vec<ChunkId> = vec![]; + for x in iter { + let fileid: String = x?; + ids.push(ChunkId::from(&fileid)); + } + Ok(ids) + } +} + +fn row_to_entry(row: &Row) -> rusqlite::Result<(u64, FilesystemEntry)> { + let fileid: i64 = row.get(row.column_index("fileid")?)?; + let fileid = fileid as u64; + let path: Vec<u8> = row.get(row.column_index("path")?)?; + let path: &OsStr = OsStrExt::from_bytes(&path); + let path: PathBuf = PathBuf::from(path); + let kind = row.get(row.column_index("kind")?)?; + let kind = FilesystemKind::from_code(kind).unwrap(); + let entry = match kind { + FilesystemKind::Regular => FilesystemEntry::regular(path, 0), + FilesystemKind::Directory => FilesystemEntry::directory(path), + }; + Ok((fileid, entry)) } fn insert_one( @@ -85,7 +137,7 @@ mod test { fn empty() { let filename = NamedTempFile::new().unwrap().path().to_path_buf(); { - let mut _gen = Generation::new(&filename).unwrap(); + let mut _gen = Generation::create(&filename).unwrap(); // _gen is dropped here; the connection is close; the file // should not be removed. } |