diff --git a/Cargo.lock b/Cargo.lock index ab3a33c..4e8bc9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -333,6 +333,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonfilter" +version = "0.1.0" +dependencies = [ + "regex", + "serde", + "serde_json", +] + [[package]] name = "libc" version = "0.2.150" @@ -370,12 +379,12 @@ dependencies = [ "comfy-table", "csv", "env_logger", + "jsonfilter", "log", "regex", "serde", "serde_json", "serde_yaml", - "txd", "walkdir", ] @@ -425,18 +434,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.70" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -452,9 +461,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.2" +version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", @@ -464,9 +473,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick", "memchr", @@ -521,18 +530,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.193" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", @@ -541,9 +550,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" dependencies = [ "itoa", "ryu", @@ -596,9 +605,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.39" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", @@ -614,16 +623,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "txd" -version = "0.1.0" -source = "git+https://git.hydrar.de/jmarya/txd#657b5dc2a76342d1021452ca226da9b3e19f7b82" -dependencies = [ - "chrono", - "serde", - "serde_json", -] - [[package]] name = "unicode-ident" version = "1.0.12" diff --git a/Cargo.toml b/Cargo.toml index 9fb32be..c190f89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,10 +15,10 @@ regex = "1.10.2" serde = "1.0.189" serde_yaml = "0.9.25" walkdir = "2.4.0" -txd = { git = "https://git.hydrar.de/jmarya/txd" } serde_json = "1.0.107" comfy-table = "7.1.0" env_logger = "0.10.0" log = "0.4.20" chrono = "0.4.31" csv = "1.3.0" +jsonfilter = { path = "../../Gitea/jsonfilter" } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 87e0ab6..514260b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ -use std::collections::{HashMap, HashSet}; - -use txd::DataType; +use std::{ + cmp::Ordering, + collections::{HashMap, HashSet}, +}; /// get frontmatter from markdown document #[must_use] @@ -14,6 +15,28 @@ pub fn get_frontmatter(markdown: &str) -> Option { }) } +trait ToYaml { + fn to_yaml(&self) -> serde_yaml::Value; +} + +impl ToYaml for serde_json::Value { + fn to_yaml(&self) -> serde_yaml::Value { + let str = serde_yaml::to_string(self).unwrap(); + return serde_yaml::from_str(&str).unwrap(); + } +} + +trait ToJson { + fn to_json(&self) -> serde_json::Value; +} + +impl ToJson for serde_yaml::Value { + fn to_json(&self) -> serde_json::Value { + let str = serde_json::to_string(self).unwrap(); + return serde_json::from_str(&str).unwrap(); + } +} + /// get inline #tags from markdown file #[must_use] pub fn get_inline_tags(markdown: &str) -> Vec { @@ -118,20 +141,10 @@ impl Index { if let Some(sort) = sort { scope.sort_by(|a, b| { - let a_str = a.get_key(&sort); - let b_str = b.get_key(&sort); - let mut a = txd::parse(&a_str); - let mut b = txd::parse(&b_str); + let a_str: serde_json::Value = a.get_key(&sort); + let b_str: serde_json::Value = b.get_key(&sort); - log::debug!("Trying to order {a:?} and {b:?}",); - - if !a.same_as(&b) { - log::debug!("trying to cast a to string because of different types"); - a = txd::DataType::String(a_str); - b = txd::DataType::String(b_str); - } - - a.order_with(&b).unwrap() + jsonfilter::order(&a_str, &b_str) }); } @@ -155,7 +168,10 @@ impl Index { let mut grouped_items: HashMap> = HashMap::new(); for doc in self.documents.clone() { - grouped_items.entry(doc.get_key(key)).or_default().push(doc); + grouped_items + .entry(stringify(&doc.get_key(key).to_yaml())) + .or_default() + .push(doc); } grouped_items @@ -171,7 +187,7 @@ impl Index { for doc in &self.documents { let mut rcol = vec![]; for c in col { - rcol.push(doc.get_key(c)); + rcol.push(stringify(&doc.get_key(c).to_yaml())); } rows.push(rcol); } @@ -181,42 +197,21 @@ impl Index { /// Apply filters to the documents of the index returning a new filtered index #[must_use] - pub fn filter_documents(&self, filters: &[txd::filter::Filter]) -> Self { - // TODO : Implement option for chaining filters with AND OR + pub fn filter_documents(&self, filters: &serde_json::Value) -> Self { let docs: Vec<_> = self .documents .iter() .filter(|x| { - let mut is_included = true; - - for f in filters { - let a_str = x.get_key(&f.0); - let mut a = txd::parse(&a_str); - let b = txd::parse(&f.2); - - log::debug!( - "Trying to compare '{}' = {a:?} and {b:?} with {:?}", - f.0, - f.1 - ); - - if a_str.is_empty() { - // TODO : Maybe add explicit null instead of empty string - is_included = false; - break; - } - - if !a.same_as(&b) && !matches!(a, DataType::List(_)) { - log::debug!("trying to cast a to string because of different types"); - a = txd::DataType::String(a_str); - } - - if !a.compare(f.1, &b).unwrap() { - is_included = false; - } + let res = jsonfilter::try_matches(filters, &x.get_full_frontmatter()); + match res { + Ok(valid) => Ok(valid), + Err(e) => match e { + jsonfilter::FilterError::InvalidFilter => Err(e), + jsonfilter::FilterError::UnknownOperator => Err(e), + jsonfilter::FilterError::KeyNotFound => Ok(false), + }, } - - is_included + .unwrap() }) .cloned() .collect(); @@ -228,63 +223,80 @@ impl Index { impl Document { /// Get a key from document. /// This will return internal properties first, then it will search the document frontmatter for the key and return it. If nothing was found an empty string is returned. - fn get_key(&self, key: &str) -> String { + fn get_key(&self, key: &str) -> serde_json::Value { match key { "file.title" => { let path = std::path::Path::new(&self.path); - return path.file_stem().unwrap().to_str().unwrap().to_string(); + return serde_json::Value::String( + path.file_stem().unwrap().to_str().unwrap().to_string(), + ); } "file.name" => { let path = std::path::Path::new(&self.path); - return path.file_name().unwrap().to_str().unwrap().to_string(); + return serde_json::Value::String( + path.file_name().unwrap().to_str().unwrap().to_string(), + ); } "file.parent" => { let path = std::path::Path::new(&self.path); - return path - .parent() - .unwrap() - .file_name() - .unwrap() - .to_str() - .unwrap() - .to_string(); + return serde_json::Value::String( + path.parent() + .unwrap() + .file_name() + .unwrap() + .to_str() + .unwrap() + .to_string(), + ); } "file.folder" => { let path = std::path::Path::new(&self.path); - return path.parent().unwrap().to_str().unwrap().to_string(); + return serde_json::Value::String( + path.parent().unwrap().to_str().unwrap().to_string(), + ); } "file.ext" => { let path = std::path::Path::new(&self.path); - return path.extension().unwrap().to_str().unwrap().to_string(); + return serde_json::Value::String( + path.extension().unwrap().to_str().unwrap().to_string(), + ); } "file.size" => { let path = std::path::Path::new(&self.path); - return path.metadata().unwrap().len().to_string(); + return serde_json::Value::String(path.metadata().unwrap().len().to_string()); } "file.ctime" => { let path = std::path::Path::new(&self.path); - return system_time_to_date_time(path.metadata().unwrap().created().unwrap()) - .to_rfc3339(); + return serde_json::Value::String( + system_time_to_date_time(path.metadata().unwrap().created().unwrap()) + .to_rfc3339(), + ); } "file.cday" => { let path = std::path::Path::new(&self.path); - return system_time_to_date_time(path.metadata().unwrap().created().unwrap()) - .format("%Y-%m-%d") - .to_string(); + return serde_json::Value::String( + system_time_to_date_time(path.metadata().unwrap().created().unwrap()) + .format("%Y-%m-%d") + .to_string(), + ); } "file.mtime" => { let path = std::path::Path::new(&self.path); - return system_time_to_date_time(path.metadata().unwrap().modified().unwrap()) - .to_rfc3339(); + return serde_json::Value::String( + system_time_to_date_time(path.metadata().unwrap().modified().unwrap()) + .to_rfc3339(), + ); } "file.mday" => { let path = std::path::Path::new(&self.path); - return system_time_to_date_time(path.metadata().unwrap().modified().unwrap()) - .format("%Y-%m-%d") - .to_string(); + return serde_json::Value::String( + system_time_to_date_time(path.metadata().unwrap().modified().unwrap()) + .format("%Y-%m-%d") + .to_string(), + ); } "file.path" => { - return self.path.clone(); + return serde_json::Value::String(self.path.clone()); } _ => {} } @@ -298,27 +310,44 @@ impl Document { .unwrap() .get(split_path.first().unwrap()); if data.is_none() { - return String::new(); + return serde_json::Value::Null; } let mut data = data.unwrap(); for path in &split_path[1..] { let data_opt = data.as_mapping().unwrap().get(path); if data_opt.is_none() { - return String::new(); + return serde_json::Value::Null; } data = data_opt.unwrap(); } - stringify(data) + data.to_json() } else { self.frontmatter .as_mapping() .unwrap() .get(key) - .map_or_else(String::new, stringify) + .map_or_else(|| serde_json::Value::Null, |x| x.to_json()) } } + + pub fn get_full_frontmatter(&self) -> serde_json::Value { + let mut frontmatter = self.frontmatter.to_json(); + let frontmatter_obj = frontmatter.as_object_mut().unwrap(); + frontmatter_obj.insert("file.title".into(), self.get_key("file.title")); + frontmatter_obj.insert("file.name".into(), self.get_key("file.name")); + frontmatter_obj.insert("file.parent".into(), self.get_key("file.parent")); + frontmatter_obj.insert("file.folder".into(), self.get_key("file.folder")); + frontmatter_obj.insert("file.ext".into(), self.get_key("file.ext")); + frontmatter_obj.insert("file.size".into(), self.get_key("file.size")); + frontmatter_obj.insert("file.ctime".into(), self.get_key("file.ctime")); + frontmatter_obj.insert("file.cday".into(), self.get_key("file.cday")); + frontmatter_obj.insert("file.mtime".into(), self.get_key("file.mtime")); + frontmatter_obj.insert("file.mday".into(), self.get_key("file.mday")); + frontmatter_obj.insert("file.path".into(), self.get_key("file.path")); + frontmatter + } } fn stringify(val: &serde_yaml::Value) -> String { diff --git a/src/main.rs b/src/main.rs index 8036072..f520ba4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -55,14 +55,23 @@ fn main() { .map_or_else(std::vec::Vec::new, std::iter::Iterator::collect); log::debug!("raw filters: {filters:?}"); - let filters: Vec<_> = filters - .into_iter() - .map(|x| txd::filter::parse_condition(x).expect("failed to parse filter")) - .collect(); + + let filters = if filters.len() == 1 { + serde_json::from_str(filters.first().unwrap()).unwrap() + } else { + let filters: Vec<_> = filters + .iter() + .map(|x| serde_json::from_str::(x).unwrap()) + .collect(); + serde_json::json!({ + "$and": filters + }) + }; + log::debug!("parsed filters: {filters:?}"); let mut i = Index::new(root_dir, ignoretags); - if !filters.is_empty() { + if !filters.is_null() { i = i.filter_documents(&filters); } @@ -95,18 +104,10 @@ fn main() { if std::io::stdout().is_terminal() { let mut grouped_keys = grouped.iter().map(|(key, _)| key).collect::>(); grouped_keys.sort_by(|a_str, b_str| { - let mut a = txd::parse(a_str); - let mut b = txd::parse(b_str); + let a: serde_json::Value = serde_json::from_str(a_str).unwrap(); + let b: serde_json::Value = serde_json::from_str(b_str).unwrap(); - log::debug!("Trying to order {a:?} and {b:?}",); - - if !a.same_as(&b) { - log::debug!("trying to cast a to string because of different types"); - a = txd::DataType::String((*a_str).to_string()); - b = txd::DataType::String((*b_str).to_string()); - } - - a.order_with(&b).unwrap() + jsonfilter::order(&a, &b) }); for group in grouped_keys { println!("# {group}");