feat(indexer): get multiple claims value and various others
This commit is contained in:
parent
6c6e096287
commit
8f1b67cfd9
12 changed files with 132 additions and 32 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
|
@ -578,6 +578,12 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "2.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8"
|
||||
|
||||
[[package]]
|
||||
name = "inotify"
|
||||
version = "0.9.6"
|
||||
|
|
@ -910,6 +916,7 @@ dependencies = [
|
|||
"chrono-tz",
|
||||
"clap",
|
||||
"fully_pub",
|
||||
"indoc",
|
||||
"notify",
|
||||
"reqwest",
|
||||
"serde",
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ chrono = { version = "0.4.26", features = ["serde"] }
|
|||
notify = "6.0.1"
|
||||
anyhow = "1.0.75"
|
||||
chrono-tz = "0.8.4"
|
||||
indoc = "2"
|
||||
|
||||
[dependencies.uuid]
|
||||
version = "1.4.1"
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
// impl From<io::Error> for CliError {
|
||||
// fn from(error: io::Error) -> Self {
|
||||
// CliError::IoError(error)
|
||||
// }
|
||||
// }
|
||||
|
|
@ -5,6 +5,7 @@ pub mod parse_extractor;
|
|||
pub mod models;
|
||||
pub mod labels;
|
||||
pub mod read;
|
||||
pub mod filter;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_parse_extractor;
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ struct EntryContainer {
|
|||
parsed_entry: PEntry,
|
||||
|
||||
/// the final interpreted Entry, more pratical to work with, if you have only data query
|
||||
pure_entry: Option<Entry>,
|
||||
pure_entry: Entry,
|
||||
|
||||
/// a reference to the file that originally contained this entry
|
||||
source_file_id: Uuid
|
||||
|
|
@ -127,6 +127,8 @@ struct LanguageLabels {
|
|||
|
||||
|
||||
/// functions to work with pure entry
|
||||
|
||||
/// get claims matching a property search string from a pure_entry
|
||||
pub fn get_entry_claims<'a>(pure_entry: &'a Entry, property_str: &str) -> Vec<&'a EntryClaim> {
|
||||
pure_entry.claims
|
||||
.iter()
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@ use anyhow::{Result, anyhow};
|
|||
use crate::pdel_parser::{PEntry, PEntryValue, ParseOutput, PEntryClaim, PFunction, PFunctionArgument, UnresolvedReference};
|
||||
use crate::database::models::{Entry, EntryValue, EntryClaim, Property};
|
||||
|
||||
pub fn get_claim_value<'a>(entry: &'a PEntry, target_property: &str) -> Option<&'a PEntryValue> {
|
||||
/// to work with parsed entry
|
||||
pub fn get_claim_values<'a>(entry: &'a PEntry, target_property: &str) -> Vec<&'a PEntryValue> {
|
||||
entry.claims.p.iter()
|
||||
.find(|claim| claim.p.property.p == target_property)
|
||||
.filter(|claim| claim.p.property.p == target_property)
|
||||
.map(|claim| &claim.p.value_container.p.value.p)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_pure_property(property_locator: &String) -> Property {
|
||||
|
|
@ -124,10 +126,17 @@ pub fn get_pure_value(parsed_value: &PEntryValue) -> Result<EntryValue> {
|
|||
let l = naive_dt.and_local_timezone(Paris).unwrap().with_timezone(&Utc);
|
||||
EntryValue::Time(l)
|
||||
},
|
||||
_ => unimplemented!()
|
||||
"VaultEntry" => {
|
||||
let entry_path: String = func.arguments.iter().nth(0).map(|x| &x.p)
|
||||
.map(get_argument_value)
|
||||
.ok_or(anyhow!("Cannot get argument"))?
|
||||
.try_into().map_err(|_| anyhow!("Entry path argument is not String"))?;
|
||||
EntryValue::String(format!("vault_entry://{}", entry_path))
|
||||
}
|
||||
_ => return Err(anyhow!("Encountered unsupported function {:?}", func.name.p))
|
||||
}
|
||||
},
|
||||
_ => unimplemented!()
|
||||
_ => return Err(anyhow!("Entry value type not supported by extractor {:?}", parsed_value))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
use std::io::BufReader;
|
||||
use anyhow::Result;
|
||||
use std::fs::File;
|
||||
use crate::unwrap_or_return;
|
||||
|
||||
use super::models::Notebook;
|
||||
use super::NotebookContext;
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ReadErr {
|
||||
CannotReadDB
|
||||
}
|
||||
|
||||
|
||||
pub fn read_db(context: &NotebookContext) -> Result<Notebook, ReadErr> {
|
||||
let db_file = unwrap_or_return!(
|
||||
File::open(&context.database_path()),
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ fn test_transform_into_pure_value_with_globe_coordinate() {
|
|||
|
||||
/// util function
|
||||
/// TODO: move it to appropriate place in the code tree
|
||||
fn format_date(time: DateTime<Utc>, format: &str) -> String {
|
||||
time.naive_local().date().format(format).to_string()
|
||||
fn format_datum(time: DateTime<Utc>, format: &str) -> String {
|
||||
time.naive_local().format(format).to_string()
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -48,9 +48,9 @@ fn test_transform_into_pure_value_with_datum_date() {
|
|||
unreachable!()
|
||||
};
|
||||
|
||||
assert_eq!(format_date(time, "%Y"), "2023");
|
||||
assert_eq!(format_date(time, "%m"), "11");
|
||||
assert_eq!(format_date(time, "%d"), "19"); // 19 instead of 20 because we are in UTC+1
|
||||
assert_eq!(format_datum(time, "%Y"), "2023");
|
||||
assert_eq!(format_datum(time, "%m"), "11");
|
||||
assert_eq!(format_datum(time, "%d"), "19"); // 19 instead of 20 because we are in UTC+1
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -67,10 +67,11 @@ fn test_transform_into_pure_value_with_datum_datetime() {
|
|||
let EntryValue::Time(time) = pure_value else {
|
||||
unreachable!()
|
||||
};
|
||||
dbg!("wow {:?}", pure_value);
|
||||
|
||||
assert_eq!(format_date(time, "%d"), "19");
|
||||
assert_eq!(format_date(time, "%H"), "10"); // UTC+1
|
||||
assert_eq!(format_date(time, "%M"), "55");
|
||||
assert_eq!(format_datum(time, "%d"), "19");
|
||||
assert_eq!(format_datum(time, "%H"), "10"); // UTC+1
|
||||
assert_eq!(format_datum(time, "%M"), "55");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -1,17 +1,20 @@
|
|||
use crate::database::NotebookContext;
|
||||
use crate::database::models::{Notebook, EntryContainer, SourceFile};
|
||||
use crate::database::parse_extractor::{get_pure_entry, get_claim_value};
|
||||
use std::path::Path;
|
||||
use std::fs;
|
||||
use crate::pdel_parser::parse_wrapper;
|
||||
use crate::pdel_parser::markdown::parse_markdown;
|
||||
use crate::pdel_parser::{ParseOutput, PEntry};
|
||||
use bincode::serialize;
|
||||
use std::fs::File;
|
||||
// import the write trait (not directly used)
|
||||
use std::io::Write;
|
||||
use crate::unwrap_or_return;
|
||||
use uuid::Uuid;
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher};
|
||||
use std::io::Read;
|
||||
|
||||
|
||||
mod reference_resolver;
|
||||
|
|
@ -19,12 +22,16 @@ mod reference_resolver;
|
|||
#[cfg(test)]
|
||||
mod test_reference_resolver;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_indexer;
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum IndexingErr {
|
||||
CannotOpen,
|
||||
IoErr,
|
||||
ParseErr
|
||||
ParseErr,
|
||||
ExtractErr(anyhow::Error)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -37,28 +44,41 @@ struct IndexingResult {
|
|||
/// # Arguments
|
||||
///
|
||||
/// * `file_id` - the id that you known
|
||||
///
|
||||
/// - parse entry
|
||||
/// - check for existing id
|
||||
/// - if id missing add the code
|
||||
/// - reparse
|
||||
/// - extract the pure entry
|
||||
fn index_file(file_path: &Path, file_id: Uuid) -> Result<IndexingResult, IndexingErr> {
|
||||
// keep an existing id or get a new one
|
||||
// or use the git id?
|
||||
let mut entries_containers: Vec<EntryContainer> = vec![];
|
||||
let contents = fs::read_to_string(file_path)
|
||||
.expect("Should be able to read the file, there is something wrong with it");
|
||||
|
||||
let source_file = SourceFile {
|
||||
id: file_id,
|
||||
path: file_path.to_path_buf(),
|
||||
};
|
||||
|
||||
let res = parse_wrapper(parse_markdown, &contents);
|
||||
match res {
|
||||
Ok(pout) => {
|
||||
// if add_id_on_entry(&source_file, &pout) {
|
||||
// return index_file(file_path, file_id);
|
||||
// }
|
||||
|
||||
for entry in pout.p {
|
||||
let pure_entry = get_pure_entry(&entry).map_err(|e| IndexingErr::ExtractErr(e))?;
|
||||
entries_containers.push(EntryContainer {
|
||||
source_file_id: file_id,
|
||||
pure_entry,
|
||||
parsed_entry: entry,
|
||||
pure_entry: None
|
||||
})
|
||||
}
|
||||
Ok(IndexingResult {
|
||||
files: vec![SourceFile {
|
||||
id: file_id,
|
||||
path: file_path.to_path_buf(),
|
||||
}],
|
||||
files: vec![],
|
||||
entries: entries_containers
|
||||
})
|
||||
},
|
||||
|
|
@ -105,7 +125,6 @@ fn index_dir(dir_path: &Path) -> Result<IndexingResult, IndexingErr> {
|
|||
// we preparsed the entites, now going to link them
|
||||
// TODO: index into a graph db? IndraDB or sqlite?
|
||||
//
|
||||
|
||||
// for each entity, index the names into the table,
|
||||
// then check if the names that is referenced in the entry are valid
|
||||
// if valid, replace them with actual id (or pointer) of the referenced entry
|
||||
|
|
@ -118,6 +137,43 @@ fn index_dir(dir_path: &Path) -> Result<IndexingResult, IndexingErr> {
|
|||
})
|
||||
}
|
||||
|
||||
use crate::database::models::get_entry_claims;
|
||||
|
||||
/// Will add ids on entries that don't have an id yet
|
||||
/// we need to manipulate the file and write it back
|
||||
/// we act directly on the file system and write the file
|
||||
/// return Result True if id were missing False if not
|
||||
/// we have here a function to only work on one entry at the time to allow for more flexibility in
|
||||
/// the testing
|
||||
fn add_id_on_entry(input_entry_code: String, pout: ParseOutput<PEntry>) -> Result<(Uuid, String)> {
|
||||
// let mut f = File::open(&source_file.path).map_err(|e| anyhow!("Failed to open file: {e:?}"))?;
|
||||
// if get_claim_value(&pout.p, "id").len() >= 1 {
|
||||
// return Result<
|
||||
// }
|
||||
// detected missing id
|
||||
|
||||
// generate new id
|
||||
let id = Uuid::new_v4();
|
||||
// add the id as String
|
||||
let id_claim_pdel_code = format!(" id: {:?},\n", id.to_string());
|
||||
// let first_claim = pout.p.claims.p.iter().nth(0).ok_or(anyhow!("Expected at least one claim"))?;
|
||||
// // open file
|
||||
// let mut code = String::new();
|
||||
let mut code = input_entry_code;
|
||||
|
||||
// println!("{:?}", &code.as_str()[(pout.p.claims.start_loc)..(pout.p.claims.start_loc+10)]);
|
||||
// TODO: have good whitespace aligment
|
||||
// detect { and then
|
||||
// detect whitespace alignment
|
||||
// right now, +3 is arbitrary (to handle 4 spaces identation)
|
||||
code.insert_str(pout.p.claims.start_loc+3, &id_claim_pdel_code);
|
||||
|
||||
// get the cursor position at the begining of the entries claims
|
||||
// append the code
|
||||
// for now we won't have the issue of macro expansion because we will append before !labels
|
||||
Ok((id, code))
|
||||
}
|
||||
|
||||
fn index_notebook(path: &Path) -> Result<Notebook, IndexingErr>
|
||||
{
|
||||
let index_res = match index_dir(path) {
|
||||
|
|
|
|||
29
src/lib/indexer/test_indexer.rs
Normal file
29
src/lib/indexer/test_indexer.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
use crate::indexer::add_id_on_entry;
|
||||
use crate::pdel_parser::parse_entry;
|
||||
use indoc::{indoc, formatdoc};
|
||||
|
||||
#[test]
|
||||
fn test_add_id_on_entry() {
|
||||
let entry_code = indoc! {r#"
|
||||
@Entry {
|
||||
foo: "bar",
|
||||
foo2: "bar",
|
||||
}
|
||||
"#}.to_string();
|
||||
|
||||
let res = parse_entry(&entry_code, 0);
|
||||
let pout = res.unwrap();
|
||||
let res = add_id_on_entry(entry_code, pout);
|
||||
assert!(res.is_ok());
|
||||
let (uuid, out_code) = res.unwrap();
|
||||
|
||||
let expected_output_code = formatdoc! {r#"
|
||||
@Entry {{
|
||||
id: "{uuid}",
|
||||
foo: "bar",
|
||||
foo2: "bar",
|
||||
}}
|
||||
"#}.to_string();
|
||||
|
||||
assert_eq!(out_code, expected_output_code);
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ use super::*;
|
|||
use crate::pdel_parser::PEntryValue;
|
||||
use crate::pdel_parser::values::container::parse_entry_value_container;
|
||||
use crate::pdel_parser::parse_wrapper;
|
||||
use crate::database::parse_extractor::get_claim_value;
|
||||
use crate::database::parse_extractor::get_claim_values;
|
||||
|
||||
#[test]
|
||||
fn test_parse_simple_entry() {
|
||||
|
|
@ -98,9 +98,9 @@ fn test_parse_with_labels() {
|
|||
assert!(res.is_ok(), "{:?}", res);
|
||||
let pout = res.unwrap();
|
||||
let claims = &pout.p.claims.p;
|
||||
assert!(get_claim_value(&pout.p, &"name").is_some());
|
||||
assert!(get_claim_value(&pout.p, &"description").is_some());
|
||||
assert!(get_claim_value(&pout.p, &"alias").is_some());
|
||||
assert!(get_claim_values(&pout.p, &"name").len() == 2);
|
||||
assert!(get_claim_values(&pout.p, &"description").len() == 2);
|
||||
assert!(get_claim_values(&pout.p, &"alias").len() == 1);
|
||||
assert_eq!(claims[2].p.property.p, "name");
|
||||
assert_eq!(claims[2].p.value_container.p.value.p, PEntryValue::String("Douglas Adams".to_string()));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue