diff --git a/Cargo.lock b/Cargo.lock index 25f6dde..abb348b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,88 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + [[package]] name = "rustysearch" version = "0.1.0" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "serde" +version = "1.0.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/Cargo.toml b/Cargo.toml index 4d74f4f..09895e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,5 @@ license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +serde = { version = "1.0.189", features = ["derive"] } +serde_json = "1.0.107" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..d761767 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,2 @@ +pub mod types; +pub mod search; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 2418909..2e1b391 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,62 +1,7 @@ -use std::{fs, path::Path}; - -pub struct Rustysearch { - base_directory: String, - index_path: String, - docs_path: String, - stats_path: String, -} - -impl Rustysearch { - /// **Sets up the object & the data directory** - /// - /// Requires a ``base_directory`` parameter, which specifies the parent - /// directory the index/document/stats data will be kept in. - /// - pub fn new(path: &str) -> Self { - Self { - base_directory: path.to_string(), - index_path: format!("{}/index", path), - docs_path: format!("{}/docs", path), - stats_path: format!("{}/stats.json", path), - } - } - - /// **Handles the creation of the various data directories** - /// - /// If the paths do not exist, it will create them. As a side effect, you - /// must have read/write access to the location you're trying to create - /// the data at. - /// - fn setup(&self) { - // Create the base directory - if !Path::new(&self.base_directory).exists() { - fs::create_dir(&self.base_directory).expect("Unable to create base directory"); - } - // Create the index directory - if !Path::new(&self.index_path).exists() { - fs::create_dir(&self.index_path).expect("Unable to create index directory"); - } - // Create the docs directory - if !Path::new(&self.docs_path).exists() { - fs::create_dir(&self.docs_path).expect("Unable to create docs directory"); - } - } - - /// **Reads the index-wide stats** - /// - /// If the stats do not exist, it makes returns data with the current - /// version of ``rustysearch`` & zero docs (used in scoring). - /// - pub fn read_stats(&self) -> String { - if !Path::new(&self.stats_path).exists() { - return String::from("{\"version\": \"0.1.0\", \"docs\": 0}"); - } - - return String::from(""); - } -} +use rustysearch::search::Rustysearch; fn main() { - println!("Hello, world!") -} + println!("Hello, world!"); + let search = Rustysearch::new("/tmp/rustysearch"); + search.setup(); +} \ No newline at end of file diff --git a/src/search.rs b/src/search.rs new file mode 100644 index 0000000..afd35d2 --- /dev/null +++ b/src/search.rs @@ -0,0 +1,101 @@ +use std::{fs, path::Path}; + +use crate::types::Stats; + +pub struct Rustysearch { + base_directory: String, + index_path: String, + docs_path: String, + stats_path: String, +} + +impl Rustysearch { + /// **Sets up the object & the data directory** + /// + /// Requires a ``base_directory`` parameter, which specifies the parent + /// directory the index/document/stats data will be kept in. + /// + pub fn new(path: &str) -> Self { + Self { + base_directory: path.to_string(), + index_path: format!("{}/index", path), + docs_path: format!("{}/docs", path), + stats_path: format!("{}/stats.json", path), + } + } + + /// **Handles the creation of the various data directories** + /// + /// If the paths do not exist, it will create them. As a side effect, you + /// must have read/write access to the location you're trying to create + /// the data at. + /// + pub fn setup(&self) { + // Create the base directory + if !Path::new(&self.base_directory).exists() { + fs::create_dir(&self.base_directory).expect("Unable to create base directory"); + } + // Create the index directory + if !Path::new(&self.index_path).exists() { + fs::create_dir(&self.index_path).expect("Unable to create index directory"); + } + // Create the docs directory + if !Path::new(&self.docs_path).exists() { + fs::create_dir(&self.docs_path).expect("Unable to create docs directory"); + } + } + + /// **Reads the index-wide stats** + /// + /// If the stats do not exist, it makes returns data with the current + /// version of ``rustysearch`` & zero docs (used in scoring). + /// + pub fn read_stats(&self) -> Stats { + if !Path::new(&self.stats_path).exists() { + let stats = Stats { + version: String::from("0.1.0"), + total_docs: 0, + }; + return stats; + } + + // Read the stats file + let stats_json = fs::read_to_string(&self.stats_path).expect("Unable to read stats"); + let stats: Stats = serde_json::from_str(&stats_json).unwrap(); + return stats; + } + + /// **Writes the index-wide stats** + /// + /// Takes a ``new_stats`` parameter, which should be a dictionary of + /// stat data. Example stat data:: + /// + /// { + /// 'version': '1.0.0', + /// 'total_docs': 25, + /// } + /// + pub fn write_stats(&self, new_stats: Stats) { + // Write new_stats as json to stats_path + let new_stats_json = serde_json::to_string(&new_stats).unwrap(); + fs::write(&self.stats_path, new_stats_json).expect("Unable to write stats"); + } + + /// **Increments the total number of documents the index is aware of** + /// + /// This is important for scoring reasons & is typically called as part + /// of the indexing process. + /// + pub fn increment_total_docs(&self) { + let mut current_stats = self.read_stats(); + current_stats.total_docs += 1; + self.write_stats(current_stats); + } + + /// **Returns the total number of documents the index is aware of** + /// + pub fn get_total_docs(&self) -> i32 { + let stats = self.read_stats(); + return stats.total_docs; + } +} diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..610ac5d --- /dev/null +++ b/src/types.rs @@ -0,0 +1,7 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct Stats{ + pub version: String, + pub total_docs: i32, +} \ No newline at end of file diff --git a/tests/rustysearch.rs b/tests/rustysearch.rs new file mode 100644 index 0000000..3b7596a --- /dev/null +++ b/tests/rustysearch.rs @@ -0,0 +1,76 @@ +#[cfg(test)] +mod tests { + use rustysearch::{types::Stats, search::Rustysearch}; + + #[test] + fn test_write_new_stats(){ + let stats = Stats{ + version: String::from("0.1.0"), + total_docs: 0, + }; + + assert_eq!(stats.version, "0.1.0"); + assert_eq!(stats.total_docs, 0); + + let search = Rustysearch::new("/tmp/rustysearch"); + search.setup(); + + search.write_stats(stats); + } + + #[test] + fn test_read_stats(){ + let search = Rustysearch::new("/tmp/rustysearch"); + search.setup(); + + clean_stats(); + + let stats = search.read_stats(); + assert_eq!(stats.version, "0.1.0"); + assert_eq!(stats.total_docs, 0); + } + + #[test] + fn test_increment_total_docs(){ + let search = Rustysearch::new("/tmp/rustysearch"); + search.setup(); + + clean_stats(); + + let stats = search.read_stats(); + assert_eq!(stats.total_docs, 0); + + search.increment_total_docs(); + let stats = search.read_stats(); + assert_eq!(stats.total_docs, 1); + } + + #[test] + fn test_get_total_docs(){ + let search = Rustysearch::new("/tmp/rustysearch"); + search.setup(); + + clean_stats(); + + let stats = search.read_stats(); + assert_eq!(stats.total_docs, 0); + + search.increment_total_docs(); + let stats = search.read_stats(); + assert_eq!(stats.total_docs, 1); + + let total_docs = search.get_total_docs(); + assert_eq!(total_docs, 1); + } + + fn clean_stats(){ + let search = Rustysearch::new("/tmp/rustysearch"); + search.setup(); + + let new_stats = Stats{ + version: String::from("0.1.0"), + total_docs: 0, + }; + search.write_stats(new_stats); + } +} \ No newline at end of file