From 95742615ef31e7751663b0192a882fb7e5a9d718 Mon Sep 17 00:00:00 2001 From: "Jakob L. Kreuze" Date: Sun, 14 Jun 2020 18:56:22 -0400 Subject: Don't expose image IDs through the CLI interface. --- README.md | 34 ++++++++++++++++------------------ src/birka-cli.rs | 24 ++++++++++++------------ src/database.rs | 14 +++++++++++++- 3 files changed, 41 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 069f8d9..2476df6 100644 --- a/README.md +++ b/README.md @@ -26,18 +26,9 @@ the command-line tool enables her to import images en masse with the full capabilities of the Unix shell at her fingertips. A brief summary of the command-line interface follows. -In бирка-тян's tag database, every image is identified by a signed 64-bit -integer. The command-line tool operates on these internal identifiers, so it is -often helpful to obtain the identifier for an image in the filesystem. - -```sh -$ birka-cli id_for Cat.jpeg -2 -``` - -But, of course, the tag database must be populated with this "Cat.jpeg" before -this command is to work. Images are introduced to the database with the `add` -command, which takes zero or more tags. +Images are introduced to the database with the `add` command, which takes zero +or more tags following the path of an image. The path is automatically +canonicalized, so relative names are perfectly acceptable. ```sh $ birka-cli add Cat.jpeg animal cat cute @@ -46,13 +37,13 @@ $ birka-cli add Cat.jpeg animal cat cute Ah, drat. We should have tagged that image with "photograph", too. ```sh -$ birka-cli add_tags $(birka-cli id_for Cat.jpeg) photograph +$ birka-cli add_tags Cat.jpeg photograph ``` On second thought, that wasn't a particularly cute picture. ```sh -$ birka-cli remove_tags $(birka-cli id_for Cat.jpeg) cute +$ birka-cli remove_tags Cat.jpeg cute ``` Now, let's see all of the photographs in the tag database tagged with @@ -60,12 +51,19 @@ Now, let's see all of the photographs in the tag database tagged with ```sh $ birka-cli query photograph -1,Kww+tPLv/BsbU7M7qqW59ph54v6CSEiUPNpW4XbA0cpoyrrAZsAmT5Ptm30M+hATM71mimoo7PTaS8DEAq57cQ==,/home/jakob/Camera/Cat.jpeg +/home/jakob/Camera/Cat.jpeg ``` -The `query` command outputs CSV; the first field is the internal identifier you -would see from `id_for`, the second is a base64-encoded BLAKE2b hash for the -image, and the third is the absolute path of the image. +The `query` command outputs a line-separated list of paths to any images +satisfying the query string. The semantics are similar to that of Danbooru, +except that the logical disjunction operator ('~') is unsupported. As a +convenience, the 'query' command can be invoked without a query string, to list +all paths indexed by the database. + +```sh +$ birka-cli query +... +``` ### Web-Interface (API) diff --git a/src/birka-cli.rs b/src/birka-cli.rs index 703da7d..1e05712 100644 --- a/src/birka-cli.rs +++ b/src/birka-cli.rs @@ -51,30 +51,30 @@ fn main() { std::process::exit(1); } let path = Path::new(&args[2]).canonicalize().unwrap(); - let id = tb.add_file(&path, &args[3..].to_vec()).unwrap(); + tb.add_file(&path, &args[3..].to_vec()).unwrap(); } "add_tags" => { if args.len() < 3 { - eprintln!("usage: {} add_tags ID [TAGS ...]", args[0]); + eprintln!("usage: {} add_tags PATH [TAGS ...]", args[0]); std::process::exit(1); } - let id = args[2].parse::().unwrap(); - tb.tag_file(id, &args[3..].to_vec()).unwrap(); + let file = tb.file_by_path(&args[2]).unwrap(); + tb.tag_file(file.id, &args[3..].to_vec()).unwrap(); } "remove_tags" => { if args.len() < 3 { - eprintln!("usage: {} remove_tags ID [TAGS ...]", args[0]); + eprintln!("usage: {} remove_tags PATH [TAGS ...]", args[0]); std::process::exit(1); } - let id = args[2].parse::().unwrap(); - tb.untag_file(id, &args[3..].to_vec()).unwrap(); + let file = tb.file_by_path(&args[2]).unwrap(); + tb.untag_file(file.id, &args[3..].to_vec()).unwrap(); } "query" => { - if args.len() < 3 { - eprintln!("usage: {} query QUERY_STRING", args[0]); - std::process::exit(1); - } - let query = database::parse_query(&args[2]).unwrap(); + let query = if args.len() < 3 { + database::parse_query("").unwrap() + } else { + database::parse_query(&args[2]).unwrap() + }; for file in tb.query(&query, None).unwrap() { println!("{}", file.path); } diff --git a/src/database.rs b/src/database.rs index bd5ae65..4e33fa7 100644 --- a/src/database.rs +++ b/src/database.rs @@ -264,6 +264,11 @@ impl TagDatabase { })?) } + /// Return the `File` object for the file at `path`. + pub fn file_by_path>(&self, path: T) -> Result { + self.file_by_id(self.id_for_path(path)?) + } + /// Return the files in the database satisfying `q` and `k`. pub fn query(&self, q: &Query, k: Option) -> Result> { // Return an empty vector if the query contains tags that aren't in the @@ -433,7 +438,7 @@ mod tests { #[test] fn test_add_file_fail_on_already_indexed() { let tb = TagDatabase::new_mem().unwrap(); - let img = tb.add_file("test", &vec!["tag1"]).unwrap(); + tb.add_file("test", &vec!["tag1"]).unwrap(); assert!(tb.add_file("test", &vec!["tag2"]).is_err()); } @@ -569,6 +574,13 @@ mod tests { assert_eq!(tb.file_by_id(img.id).unwrap().path, "test1"); } + #[test] + fn test_path_resolution() { + let tb = TagDatabase::new_mem().unwrap(); + let img = tb.add_file("test1", &vec!["tag1", "tag2"]).unwrap(); + assert_eq!(tb.file_by_path(img.path).unwrap().path, "test1"); + } + #[test] fn top_tags() { let tb = TagDatabase::new_mem().unwrap(); -- cgit v1.3