Auto merge of #34 - matthiasbeyer:doc, r=matthiasbeyer
Add some documentation Still does not produce the documentation I want, though it basically works now.
This commit is contained in:
commit
25045b7345
19 changed files with 417 additions and 125 deletions
166
README.md
166
README.md
|
@ -21,23 +21,11 @@ see [Linking](#Linking))
|
||||||
|
|
||||||
Some modules might not want to store content there, for example you don't want
|
Some modules might not want to store content there, for example you don't want
|
||||||
to put your icalendar files in there. So the calendar module just crawls through
|
to put your icalendar files in there. So the calendar module just crawls through
|
||||||
your ical files and puts the scanned meta-information into the store. Of course,
|
your ical files and puts only a link and some hashsums (for refinding on content
|
||||||
if the content of the ical file changes, the store entry does not. It still
|
move) to the store. Changes are not tracked via this model.
|
||||||
points (via its JSON content for example) to the same file. So changes are not
|
|
||||||
tracked (We can argue here whether we want to copy the contents to the store for
|
|
||||||
ical and vcard files, but we cannot argue on for example music files).
|
|
||||||
|
|
||||||
If a (for example ical-)file gets removed, the store entry gets invalid and has
|
If a (for example ical-)file gets removed, the store entry gets invalid and has
|
||||||
to be garbage-collected.
|
to be garbage-collected.
|
||||||
|
|
||||||
> The current model is not fixed yet. I'm thinking about copying .ical and
|
|
||||||
> .vcard, basically all text files, to the store.
|
|
||||||
> This is not possible for media files like music or movies, though. Also this
|
|
||||||
> is not feasible for documents like .pdf or similar.
|
|
||||||
|
|
||||||
Each of the following modules has a short description including a table what
|
|
||||||
core features are required to get it working.
|
|
||||||
|
|
||||||
### Linking
|
### Linking
|
||||||
|
|
||||||
The UUID/SHA hashes in the file names can be used to connect two store entries.
|
The UUID/SHA hashes in the file names can be used to connect two store entries.
|
||||||
|
@ -89,40 +77,75 @@ should't touch the content at all.
|
||||||
|
|
||||||
Here is a short overview what are the modules like:
|
Here is a short overview what are the modules like:
|
||||||
|
|
||||||
| Module | Indexer | Header | Hdr-Format | Content |
|
| Module | Indexer | Header-Format | Content | Expl | Dep |
|
||||||
| :---------------- | ------- | ------- | ---------- | ------- |
|
| :----------- | ------- | ------------- | ------- | ------- | ----------- |
|
||||||
| Bookmarks | | X | JSON | |
|
| Bibliography | X | JSON | X | .bib | Notes |
|
||||||
| Contacts | X | X | JSON | |
|
| Bookmarks | | JSON | | | |
|
||||||
| Calendar | X | X | JSON | |
|
| Calendar | X | JSON | | | |
|
||||||
| Notes | | X | YAML | X |
|
| Contacts | X | JSON | | | |
|
||||||
| Mail | X | X | JSON | |
|
| Image | X | JSON | | | Notes |
|
||||||
| Wiki | | X | YAML | X |
|
| Mail | X | JSON | | | |
|
||||||
| Todo | | X | YAML | |
|
| Movie | X | JSON | | | Notes |
|
||||||
| Shoppinglist | | X | YAML | |
|
| Music | X | JSON | | | Notes |
|
||||||
| Bibliography | X | X | JSON | |
|
| News | X | JSON | | | |
|
||||||
| News | X | X | JSON | |
|
| Notes | | YAML | X | Content | |
|
||||||
| Image | X | X | JSON | |
|
| Shoppinglist | | YAML | | | Notes, Todo |
|
||||||
| Movie | X | X | JSON | |
|
| Todo | | YAML | | | Notes |
|
||||||
| Music | X | X | JSON | |
|
| Wiki | | YAML | X | Content | |
|
||||||
|
|
||||||
Explanation:
|
Explanation:
|
||||||
|
|
||||||
- An "Indexer" Module does only index some data and store the indexed meta
|
- An "Indexer" Module does only index some data and stores only some information
|
||||||
information in the store
|
on how to content
|
||||||
- "Header" means that the header part of a store entry is used
|
- "Header-Format": Which format is chosen for the header. Basically: YAML if the
|
||||||
- "Hdr-Format" Which format is chosen for the header. Basically: YAML if the
|
|
||||||
user might want to edit the header, otherwise JSON (pretty).
|
user might want to edit the header, otherwise JSON (pretty).
|
||||||
- "Content" means that the content part of a store entry is used
|
- "Content" means that the content part of a store entry is used
|
||||||
|
- "Expl": What the content part of a file is used for. "Content" means simply
|
||||||
|
user content.
|
||||||
|
- "Dep" means that the module uses this other module as a dependency for
|
||||||
|
extending its own functionality
|
||||||
|
|
||||||
|
### External Dependencies
|
||||||
|
|
||||||
|
| Library | Optional | Module |
|
||||||
|
| :------------ | :------: | :------- |
|
||||||
|
| vcard | | Contacts |
|
||||||
|
| icalendar | | Calendar |
|
||||||
|
| XDG | X | Contacts |
|
||||||
|
| | X | Calendar |
|
||||||
|
| | X | Notes |
|
||||||
|
| | X | Mail |
|
||||||
|
| | X | Wiki |
|
||||||
|
| | X | Todo |
|
||||||
|
| | X | Shopping List |
|
||||||
|
| | X | BibMan |
|
||||||
|
| | X | Music |
|
||||||
|
| | X | Movie |
|
||||||
|
| | X | Image |
|
||||||
|
| Markdown | | Notes |
|
||||||
|
| | | Wiki |
|
||||||
|
| Maildir | | Mail |
|
||||||
|
| BibTex parsing | X | BibMan |
|
||||||
|
| git-annex | X | BibMan |
|
||||||
|
| | X | Music |
|
||||||
|
| | X | Movie |
|
||||||
|
| | X | Image |
|
||||||
|
| Exif | X | Image |
|
||||||
|
| id3 | X | Music |
|
||||||
|
| RSS/Atom | | News |
|
||||||
|
|
||||||
|
(Optional means that these things are optional for the main functionality, but
|
||||||
|
should be implemented at some point to offer powerful functionality)
|
||||||
|
|
||||||
### Bookmarks
|
### Bookmarks
|
||||||
|
|
||||||
Bookmarks should be stored in a simple format:
|
Bookmarks should be stored in a simple format:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{ "URL": "https://github.com", "tags": ["ducks", "r", "great"]}
|
{ "URL": "https://github.com", "TAGS": ["ducks", "r", "great"]}
|
||||||
```
|
```
|
||||||
|
|
||||||
| Required core feature | Purpose |
|
| Required util feature | Purpose |
|
||||||
| :------------------------------------ | :------------ |
|
| :------------------------------------ | :------------ |
|
||||||
| XDG-open (Browser) | External program |
|
| XDG-open (Browser) | External program |
|
||||||
|
|
||||||
|
@ -131,29 +154,15 @@ Bookmarks should be stored in a simple format:
|
||||||
Contacts are just read and indexed by `imag`, to create an internal index of
|
Contacts are just read and indexed by `imag`, to create an internal index of
|
||||||
them and to be able to refer to.
|
them and to be able to refer to.
|
||||||
|
|
||||||
| Required core feature | Purpose |
|
|
||||||
| :------------------------------------ | :------------ |
|
|
||||||
| vcard file format parsing | Data access |
|
|
||||||
| XDG-open (Mail program) | External Program |
|
|
||||||
|
|
||||||
### Calendar
|
### Calendar
|
||||||
|
|
||||||
Calendar are just read and indexed by `imag`, to create an internal index of
|
Calendar are just read and indexed by `imag`, to create an internal index of
|
||||||
them and to be able to refer to.
|
them and to be able to refer to.
|
||||||
|
|
||||||
| Required core feature | Purpose |
|
|
||||||
| :------------------------------------ | :------------ |
|
|
||||||
| ical file format parsing | Data access |
|
|
||||||
|
|
||||||
### Notes
|
### Notes
|
||||||
|
|
||||||
Just plain text notes.
|
Just plain text notes.
|
||||||
|
|
||||||
| Required core feature | Purpose |
|
|
||||||
| :------------------------------------ | :------------ |
|
|
||||||
| Linking to other store entries | Data Link |
|
|
||||||
| XDG-open (Editor) | External Program |
|
|
||||||
|
|
||||||
### Mail
|
### Mail
|
||||||
|
|
||||||
`imag` should be able to index all your mail and make them accessible through
|
`imag` should be able to index all your mail and make them accessible through
|
||||||
|
@ -163,26 +172,12 @@ notes, etc etc. to your mail.
|
||||||
Some of these things (like linking contacts, calendar entries) should be
|
Some of these things (like linking contacts, calendar entries) should be
|
||||||
linked automatically.
|
linked automatically.
|
||||||
|
|
||||||
| Required core feature | Purpose |
|
|
||||||
| :------------------------------------ | :------------ |
|
|
||||||
| Parser: Maildir | Data access |
|
|
||||||
| Parser: mbox | Data access |
|
|
||||||
| XDG-open (Mail program) | External Program |
|
|
||||||
| XDG-open (Editor) | External Program |
|
|
||||||
|
|
||||||
### Personal Wiki
|
### Personal Wiki
|
||||||
|
|
||||||
`imag` should contain a complete personal wiki software. It should contain of
|
`imag` should contain a complete personal wiki software. It should contain of
|
||||||
simple markdown files which have a YAML header, nothing too special for the
|
simple markdown files which have a YAML header, nothing too special for the
|
||||||
first step.
|
first step.
|
||||||
|
|
||||||
| Required core feature | Purpose |
|
|
||||||
| :------------------------------------ | :------------ |
|
|
||||||
| Parser: Markdown | Data parsing |
|
|
||||||
| XDG-open (Editor) | External Program |
|
|
||||||
| XDG-open (Mail program) | External Program |
|
|
||||||
| XDG-open (Browser) | External Program |
|
|
||||||
|
|
||||||
Some more ideas:
|
Some more ideas:
|
||||||
|
|
||||||
- Extract URLs and put them into store as Bookmarks
|
- Extract URLs and put them into store as Bookmarks
|
||||||
|
@ -192,21 +187,9 @@ Some more ideas:
|
||||||
`imag` should also contain a full todo-tool. I'm thinking about integrating
|
`imag` should also contain a full todo-tool. I'm thinking about integrating
|
||||||
taskwarrior through a wrapper here.
|
taskwarrior through a wrapper here.
|
||||||
|
|
||||||
| Required core feature | Purpose |
|
|
||||||
| :------------------------------------ | :------------ |
|
|
||||||
| Library: taskwarrior | Implementation |
|
|
||||||
| XDG-open (Editor) | External Program |
|
|
||||||
|
|
||||||
### Shoppinglist
|
### Shoppinglist
|
||||||
|
|
||||||
Simply dot-and-tick lists.
|
Simply dot-and-tick lists, uses Todo and Notes in combination.
|
||||||
|
|
||||||
| Required core feature | Purpose |
|
|
||||||
| :------------------------------------ | :------------ |
|
|
||||||
| Module: Todo-List | Implementation |
|
|
||||||
| XDG-open (Editor) | External Program |
|
|
||||||
|
|
||||||
- Sub-form of the Todo-List module
|
|
||||||
|
|
||||||
### Bibliography management
|
### Bibliography management
|
||||||
|
|
||||||
|
@ -214,53 +197,22 @@ BibTex would be the first step, maybe we should be able to add the actual PDFs
|
||||||
here as well... didn't waste too much thoughts on this by now. If we have the
|
here as well... didn't waste too much thoughts on this by now. If we have the
|
||||||
PDF data, we need git-annex.
|
PDF data, we need git-annex.
|
||||||
|
|
||||||
| Required core feature | Purpose |
|
|
||||||
| :------------------------------------ | :------------ |
|
|
||||||
| BibTex parsing | Data parsing |
|
|
||||||
| Backend: git-annex | Data parsing |
|
|
||||||
| XDG-open (Editor) | External Program |
|
|
||||||
| XDG-open (PDF viewer) | External Program |
|
|
||||||
| XDG-open (Office suite) | External Program |
|
|
||||||
|
|
||||||
### News (RSS)
|
### News (RSS)
|
||||||
|
|
||||||
Just indexing, reading news is not task of `imag` and so isn't syncing.
|
Just indexing, reading news is not task of `imag` and so isn't syncing.
|
||||||
|
|
||||||
| Required core feature | Purpose |
|
|
||||||
| :------------------------------------ | :------------ |
|
|
||||||
| Parser: RSS | Data parsing |
|
|
||||||
| Parser: Atom | Data parsing |
|
|
||||||
|
|
||||||
### Image
|
### Image
|
||||||
|
|
||||||
Just indexing photos.
|
Just indexing photos.
|
||||||
|
|
||||||
| Required core feature | Purpose |
|
|
||||||
| :------------------------------------ | :------------ |
|
|
||||||
| Backend: git-annex | Data parsing |
|
|
||||||
| Image metadata reading | Data parsing |
|
|
||||||
| XDG-open (Image viewer) | External Program |
|
|
||||||
|
|
||||||
### Video
|
### Video
|
||||||
|
|
||||||
Just indexing movies.
|
Just indexing movies.
|
||||||
|
|
||||||
| Required core feature | Purpose |
|
|
||||||
| :------------------------------------ | :------------ |
|
|
||||||
| Backend: git-annex | Data parsing |
|
|
||||||
| Movie metadata reading | Data parsing |
|
|
||||||
| XDG-open (Movie viewer) | External Program |
|
|
||||||
|
|
||||||
### Music
|
### Music
|
||||||
|
|
||||||
Just indexing music.
|
Just indexing music.
|
||||||
|
|
||||||
| Required core feature | Purpose |
|
|
||||||
| :------------------------------------ | :------------ |
|
|
||||||
| Backend: git-annex | Data parsing |
|
|
||||||
| Music metadata reading | Data parsing |
|
|
||||||
| XDG-open (Music player) | External Program |
|
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
This code is released under terms of GNU GPLv2.
|
This code is released under terms of GNU GPLv2.
|
||||||
|
|
14
src/cli.rs
14
src/cli.rs
|
@ -28,18 +28,32 @@ impl<'a> CliConfig<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the CLI says we should run verbose
|
||||||
|
*/
|
||||||
pub fn is_verbose(&self) -> bool {
|
pub fn is_verbose(&self) -> bool {
|
||||||
self.cli_matches.is_present("verbose") || self.is_debugging()
|
self.cli_matches.is_present("verbose") || self.is_debugging()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the CLI says we should run in debugging
|
||||||
|
*/
|
||||||
pub fn is_debugging(&self) -> bool {
|
pub fn is_debugging(&self) -> bool {
|
||||||
self.cli_matches.is_present("debug")
|
self.cli_matches.is_present("debug")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the runtime path the CLI configured
|
||||||
|
*/
|
||||||
pub fn get_rtp(&self) -> Option<String> {
|
pub fn get_rtp(&self) -> Option<String> {
|
||||||
self.cli_matches.value_of("rtp").and_then(|s| Some(String::from(s)))
|
self.cli_matches.value_of("rtp").and_then(|s| Some(String::from(s)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the store path the CLI configured
|
||||||
|
*
|
||||||
|
* TODO: Implement properly. Not working by now.
|
||||||
|
*/
|
||||||
pub fn store_path(&self) -> Option<String> {
|
pub fn store_path(&self) -> Option<String> {
|
||||||
self.get_rtp().and_then(|rtp| {
|
self.get_rtp().and_then(|rtp| {
|
||||||
self.cli_matches
|
self.cli_matches
|
||||||
|
|
|
@ -5,6 +5,15 @@ use config::reader::from_file;
|
||||||
use config::types::Config as Cfg;
|
use config::types::Config as Cfg;
|
||||||
use cli::CliConfig;
|
use cli::CliConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration object which represents the configuration file.
|
||||||
|
*
|
||||||
|
* It gets passed a CliConfig object on ::new(), retreives some data from this one which is then
|
||||||
|
* provided as default value to the callee if there is no value for it in the configuration.
|
||||||
|
*
|
||||||
|
* TODO: Setup is kinda ugly, as we re-use data from the CLI, which is the job of the Runtime
|
||||||
|
* object later.
|
||||||
|
*/
|
||||||
pub struct Configuration {
|
pub struct Configuration {
|
||||||
pub rtp : String,
|
pub rtp : String,
|
||||||
pub store_sub : String,
|
pub store_sub : String,
|
||||||
|
@ -63,18 +72,30 @@ impl Configuration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the configuration says we should run verbose
|
||||||
|
*/
|
||||||
pub fn is_verbose(&self) -> bool {
|
pub fn is_verbose(&self) -> bool {
|
||||||
self.verbose
|
self.verbose
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the configuration says we should run in debugging
|
||||||
|
*/
|
||||||
pub fn is_debugging(&self) -> bool {
|
pub fn is_debugging(&self) -> bool {
|
||||||
self.debugging
|
self.debugging
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the store path the configuration configured
|
||||||
|
*/
|
||||||
pub fn store_path(&self) -> String {
|
pub fn store_path(&self) -> String {
|
||||||
format!("{}{}", self.rtp, self.store_sub)
|
format!("{}{}", self.rtp, self.store_sub)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the runtime path the configuration configured
|
||||||
|
*/
|
||||||
pub fn get_rtp(&self) -> String {
|
pub fn get_rtp(&self) -> String {
|
||||||
self.rtp.clone()
|
self.rtp.clone()
|
||||||
}
|
}
|
||||||
|
@ -89,6 +110,9 @@ impl Configuration {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to get the runtimepath from the CLI
|
||||||
|
*/
|
||||||
fn rtp_path(config: &CliConfig) -> Option<String> {
|
fn rtp_path(config: &CliConfig) -> Option<String> {
|
||||||
config.cli_matches.value_of("rtp")
|
config.cli_matches.value_of("rtp")
|
||||||
.and_then(|s| Some(String::from(s)))
|
.and_then(|s| Some(String::from(s)))
|
||||||
|
@ -98,6 +122,9 @@ fn fetch_config(rtp: Option<String>) -> Option<Cfg> {
|
||||||
rtp.and_then(|r| from_file(Path::new(&(r.clone() + "/config"))).ok())
|
rtp.and_then(|r| from_file(Path::new(&(r.clone() + "/config"))).ok())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default runtime path, if available.
|
||||||
|
*/
|
||||||
fn default_path() -> Option<String> {
|
fn default_path() -> Option<String> {
|
||||||
use std::env::home_dir;
|
use std::env::home_dir;
|
||||||
|
|
||||||
|
|
26
src/main.rs
26
src/main.rs
|
@ -11,21 +11,21 @@ extern crate config;
|
||||||
|
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
use cli::CliConfig;
|
pub use cli::CliConfig;
|
||||||
use configuration::Configuration;
|
pub use configuration::Configuration;
|
||||||
use runtime::{ImagLogger, Runtime};
|
pub use runtime::{ImagLogger, Runtime};
|
||||||
use clap::App;
|
pub use clap::App;
|
||||||
use module::Module;
|
pub use module::Module;
|
||||||
|
|
||||||
mod cli;
|
pub mod cli;
|
||||||
mod configuration;
|
pub mod configuration;
|
||||||
mod runtime;
|
pub mod runtime;
|
||||||
mod module;
|
pub mod module;
|
||||||
mod storage;
|
pub mod storage;
|
||||||
mod ui;
|
pub mod ui;
|
||||||
mod util;
|
pub mod util;
|
||||||
|
|
||||||
use module::bm::BM;
|
pub use module::bm::BM;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let yaml = load_yaml!("../etc/cli.yml");
|
let yaml = load_yaml!("../etc/cli.yml");
|
||||||
|
|
|
@ -40,6 +40,9 @@ impl<'a> BM<'a> {
|
||||||
&self.rt
|
&self.rt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subcommand: add
|
||||||
|
*/
|
||||||
fn command_add(&self, matches: &ArgMatches) -> bool {
|
fn command_add(&self, matches: &ArgMatches) -> bool {
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
use self::header::build_header;
|
use self::header::build_header;
|
||||||
|
@ -100,6 +103,9 @@ impl<'a> BM<'a> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subcommand: list
|
||||||
|
*/
|
||||||
fn command_list(&self, matches: &ArgMatches) -> bool {
|
fn command_list(&self, matches: &ArgMatches) -> bool {
|
||||||
use ui::file::{FilePrinter, TablePrinter};
|
use ui::file::{FilePrinter, TablePrinter};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
@ -124,6 +130,9 @@ impl<'a> BM<'a> {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subcommand: remove
|
||||||
|
*/
|
||||||
fn command_remove(&self, matches: &ArgMatches) -> bool {
|
fn command_remove(&self, matches: &ArgMatches) -> bool {
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
|
@ -152,6 +161,9 @@ impl<'a> BM<'a> {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subcommand: add_tags
|
||||||
|
*/
|
||||||
fn command_add_tags(&self, matches: &ArgMatches) -> bool {
|
fn command_add_tags(&self, matches: &ArgMatches) -> bool {
|
||||||
self.alter_tags_in_files(matches, |old_tags, cli_tags| {
|
self.alter_tags_in_files(matches, |old_tags, cli_tags| {
|
||||||
let mut new_tags = old_tags.clone();
|
let mut new_tags = old_tags.clone();
|
||||||
|
@ -160,6 +172,9 @@ impl<'a> BM<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subcommand: rm_tags
|
||||||
|
*/
|
||||||
fn command_rm_tags(&self, matches: &ArgMatches) -> bool {
|
fn command_rm_tags(&self, matches: &ArgMatches) -> bool {
|
||||||
self.alter_tags_in_files(matches, |old_tags, cli_tags| {
|
self.alter_tags_in_files(matches, |old_tags, cli_tags| {
|
||||||
old_tags.clone()
|
old_tags.clone()
|
||||||
|
@ -169,12 +184,18 @@ impl<'a> BM<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subcommand: set_tags
|
||||||
|
*/
|
||||||
fn command_set_tags(&self, matches: &ArgMatches) -> bool {
|
fn command_set_tags(&self, matches: &ArgMatches) -> bool {
|
||||||
self.alter_tags_in_files(matches, |old_tags, cli_tags| {
|
self.alter_tags_in_files(matches, |old_tags, cli_tags| {
|
||||||
cli_tags.clone()
|
cli_tags.clone()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to alter the tags in a file
|
||||||
|
*/
|
||||||
fn alter_tags_in_files<F>(&self, matches: &ArgMatches, generate_new_tags: F) -> bool
|
fn alter_tags_in_files<F>(&self, matches: &ArgMatches, generate_new_tags: F) -> bool
|
||||||
where F: Fn(Vec<String>, &Vec<String>) -> Vec<String>
|
where F: Fn(Vec<String>, &Vec<String>) -> Vec<String>
|
||||||
{
|
{
|
||||||
|
@ -229,7 +250,10 @@ impl<'a> BM<'a> {
|
||||||
.all(|x| x)
|
.all(|x| x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to get files from the store filtered by the constraints passed via the
|
||||||
|
* CLI
|
||||||
|
*/
|
||||||
fn get_files(&self,
|
fn get_files(&self,
|
||||||
matches: &ArgMatches,
|
matches: &ArgMatches,
|
||||||
id_key: &'static str,
|
id_key: &'static str,
|
||||||
|
@ -257,6 +281,9 @@ impl<'a> BM<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get files from the store, filtere by ID
|
||||||
|
*/
|
||||||
fn get_files_by_id(&self, hash: FileHash) -> Vec<Rc<RefCell<File>>> {
|
fn get_files_by_id(&self, hash: FileHash) -> Vec<Rc<RefCell<File>>> {
|
||||||
let parser = Parser::new(JsonHeaderParser::new(None));
|
let parser = Parser::new(JsonHeaderParser::new(None));
|
||||||
self.rt
|
self.rt
|
||||||
|
@ -266,6 +293,9 @@ impl<'a> BM<'a> {
|
||||||
.unwrap_or(vec![])
|
.unwrap_or(vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get files from the store, filtere by Regex
|
||||||
|
*/
|
||||||
fn get_files_by_match(&self, matcher: String) -> Vec<Rc<RefCell<File>>> {
|
fn get_files_by_match(&self, matcher: String) -> Vec<Rc<RefCell<File>>> {
|
||||||
let parser = Parser::new(JsonHeaderParser::new(None));
|
let parser = Parser::new(JsonHeaderParser::new(None));
|
||||||
let re = Regex::new(&matcher[..]).unwrap_or_else(|e| {
|
let re = Regex::new(&matcher[..]).unwrap_or_else(|e| {
|
||||||
|
@ -292,6 +322,9 @@ impl<'a> BM<'a> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get files from the store, filtere by tags
|
||||||
|
*/
|
||||||
fn get_files_by_tags(&self, tags: Vec<String>) -> Vec<Rc<RefCell<File>>> {
|
fn get_files_by_tags(&self, tags: Vec<String>) -> Vec<Rc<RefCell<File>>> {
|
||||||
let parser = Parser::new(JsonHeaderParser::new(None));
|
let parser = Parser::new(JsonHeaderParser::new(None));
|
||||||
self.rt
|
self.rt
|
||||||
|
@ -309,6 +342,9 @@ impl<'a> BM<'a> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trait implementation for BM module
|
||||||
|
*/
|
||||||
impl<'a> Module<'a> for BM<'a> {
|
impl<'a> Module<'a> for BM<'a> {
|
||||||
|
|
||||||
fn exec(&self, matches: &ArgMatches) -> bool {
|
fn exec(&self, matches: &ArgMatches) -> bool {
|
||||||
|
|
|
@ -1,13 +1,23 @@
|
||||||
/*
|
/*!
|
||||||
* Helpers for headers
|
* Helpers for headers
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub mod tags;
|
pub mod tags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility helpers for header data
|
||||||
|
*/
|
||||||
pub mod data {
|
pub mod data {
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use storage::file::header::data::FileHeaderData as FHD;
|
use storage::file::header::data::FileHeaderData as FHD;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an URL from a header, whereas the header has to have the following format:
|
||||||
|
*
|
||||||
|
* { ..., "URL": "<URL>", ... }
|
||||||
|
*
|
||||||
|
* Does no spec verification.
|
||||||
|
*/
|
||||||
pub fn get_url_from_header(header: &FHD) -> Option<String> {
|
pub fn get_url_from_header(header: &FHD) -> Option<String> {
|
||||||
match header {
|
match header {
|
||||||
&FHD::Map{keys: ref ks} => {
|
&FHD::Map{keys: ref ks} => {
|
||||||
|
|
|
@ -1,30 +1,61 @@
|
||||||
/*
|
/*!
|
||||||
* Helpers for headers - Tags
|
* Helpers for headers - Tags
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spec helpers for header-tags
|
||||||
|
*/
|
||||||
pub mod spec {
|
pub mod spec {
|
||||||
use storage::file::header::spec::FileHeaderSpec as FHS;
|
use storage::file::header::spec::FileHeaderSpec as FHS;
|
||||||
use module::helpers::spec::{named_text, named_text_array};
|
use module::helpers::spec::{named_text, named_text_array};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* helper for a Header spec for
|
||||||
|
*
|
||||||
|
* { "URL": "<Text>" }
|
||||||
|
*/
|
||||||
pub fn url_key() -> FHS {
|
pub fn url_key() -> FHS {
|
||||||
named_text("URL")
|
named_text("URL")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* helper for a Header spec for
|
||||||
|
*
|
||||||
|
* { "TAGS": [ "<Text>", ... ] }
|
||||||
|
*/
|
||||||
pub fn tags_key() -> FHS {
|
pub fn tags_key() -> FHS {
|
||||||
named_text_array("TAGS")
|
named_text_array("TAGS")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data helpers for header-tags
|
||||||
|
*/
|
||||||
pub mod data {
|
pub mod data {
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use storage::file::header::data::FileHeaderData as FHD;
|
use storage::file::header::data::FileHeaderData as FHD;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use a Vec<String> to build a Tag-Array:
|
||||||
|
*
|
||||||
|
* [ "<Text>", ... ]
|
||||||
|
*/
|
||||||
pub fn build_tag_array(tags: Vec<String>) -> FHD {
|
pub fn build_tag_array(tags: Vec<String>) -> FHD {
|
||||||
let texttags = tags.into_iter().map(|t| FHD::Text(t.clone())).collect();
|
let texttags = tags.into_iter().map(|t| FHD::Text(t.clone())).collect();
|
||||||
FHD::Array { values: Box::new(texttags) }
|
FHD::Array { values: Box::new(texttags) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch tags from a header, whereas the header looks like this:
|
||||||
|
*
|
||||||
|
* { ...,
|
||||||
|
* "TAGS": [ "<Text>", ... ],
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* Does no spec verification.
|
||||||
|
*/
|
||||||
pub fn get_tags_from_header(header: &FHD) -> Vec<String> {
|
pub fn get_tags_from_header(header: &FHD) -> Vec<String> {
|
||||||
let mut tags : Vec<String> = vec![];
|
let mut tags : Vec<String> = vec![];
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,39 @@
|
||||||
|
/*!
|
||||||
|
* Utility helpers for modules
|
||||||
|
*/
|
||||||
|
|
||||||
pub mod header;
|
pub mod header;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helpers for header specs
|
||||||
|
*/
|
||||||
pub mod spec {
|
pub mod spec {
|
||||||
use storage::file::header::spec::FileHeaderSpec as FHS;
|
use storage::file::header::spec::FileHeaderSpec as FHS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to get a spec for a Key-Value for a named text:
|
||||||
|
*
|
||||||
|
* { '<name>': "<Text>" }
|
||||||
|
*/
|
||||||
pub fn named_text(name: &str) -> FHS {
|
pub fn named_text(name: &str) -> FHS {
|
||||||
FHS::Key { name: String::from(name), value_type: Box::new(FHS::Text) }
|
FHS::Key { name: String::from(name), value_type: Box::new(FHS::Text) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to get a spec for a Key-Value for a named array:
|
||||||
|
*
|
||||||
|
* { '<name>': [ "<Text>", ...] }
|
||||||
|
*/
|
||||||
pub fn named_text_array(name: &str) -> FHS {
|
pub fn named_text_array(name: &str) -> FHS {
|
||||||
FHS::Key { name: String::from(name), value_type: Box::new(text_array()) }
|
FHS::Key { name: String::from(name), value_type: Box::new(text_array()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to get a spec for Array<Text>:
|
||||||
|
*
|
||||||
|
* [ "<Text>", ...]
|
||||||
|
*/
|
||||||
pub fn text_array() -> FHS {
|
pub fn text_array() -> FHS {
|
||||||
FHS::Array { allowed_types: vec![FHS::Text] }
|
FHS::Array { allowed_types: vec![FHS::Text] }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
|
/**
|
||||||
|
* Utility helpers for CLI
|
||||||
|
*/
|
||||||
pub mod cli {
|
pub mod cli {
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
use runtime::Runtime;
|
use runtime::Runtime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a commandline option "tags" and split the argument by "," to be able to provide a
|
||||||
|
* Vec<String> with the argument as array.
|
||||||
|
*/
|
||||||
pub fn get_tags<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Vec<String> {
|
pub fn get_tags<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Vec<String> {
|
||||||
|
|
||||||
fn reject_if_with_spaces(e: &String) -> bool {
|
fn reject_if_with_spaces(e: &String) -> bool {
|
||||||
|
|
|
@ -11,6 +11,9 @@ use runtime::Runtime;
|
||||||
pub mod bm;
|
pub mod bm;
|
||||||
pub mod helpers;
|
pub mod helpers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module interface, each module has to implement this.
|
||||||
|
*/
|
||||||
pub trait Module<'a> : Debug {
|
pub trait Module<'a> : Debug {
|
||||||
fn exec(&self, matches: &ArgMatches) -> bool;
|
fn exec(&self, matches: &ArgMatches) -> bool;
|
||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
|
|
|
@ -51,6 +51,11 @@ impl log::Log for ImagLogger {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runtime object, represents a single interface to both the CLI configuration and the
|
||||||
|
* configuration file. Also carries the store object around and is basically an object which
|
||||||
|
* contains everything which is required to run a command/module.
|
||||||
|
*/
|
||||||
pub struct Runtime<'a> {
|
pub struct Runtime<'a> {
|
||||||
pub config : CliConfig<'a>,
|
pub config : CliConfig<'a>,
|
||||||
pub configuration : Cfg,
|
pub configuration : Cfg,
|
||||||
|
@ -68,22 +73,37 @@ impl<'a> Runtime<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether we run verbose
|
||||||
|
*/
|
||||||
pub fn is_verbose(&self) -> bool {
|
pub fn is_verbose(&self) -> bool {
|
||||||
self.config.is_verbose() || self.configuration.is_verbose()
|
self.config.is_verbose() || self.configuration.is_verbose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether we run in debugging
|
||||||
|
*/
|
||||||
pub fn is_debugging(&self) -> bool {
|
pub fn is_debugging(&self) -> bool {
|
||||||
self.config.is_debugging() || self.configuration.is_verbose()
|
self.config.is_debugging() || self.configuration.is_verbose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the store path we are currently using
|
||||||
|
*/
|
||||||
pub fn store_path(&self) -> String {
|
pub fn store_path(&self) -> String {
|
||||||
self.config.store_path().unwrap_or(self.configuration.store_path())
|
self.config.store_path().unwrap_or(self.configuration.store_path())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the store object
|
||||||
|
*/
|
||||||
pub fn store(&self) -> &Store {
|
pub fn store(&self) -> &Store {
|
||||||
&self.store
|
&self.store
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the runtime path we are currently using
|
||||||
|
*/
|
||||||
pub fn get_rtp(&self) -> String {
|
pub fn get_rtp(&self) -> String {
|
||||||
if let Some(rtp) = self.config.get_rtp() {
|
if let Some(rtp) = self.config.get_rtp() {
|
||||||
rtp
|
rtp
|
||||||
|
|
|
@ -10,6 +10,11 @@ use uuid::Uuid;
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
#[derive(Eq)]
|
#[derive(Eq)]
|
||||||
#[derive(Hash)]
|
#[derive(Hash)]
|
||||||
|
/**
|
||||||
|
* FileHash type
|
||||||
|
*
|
||||||
|
* Simple abstraction over String by now.
|
||||||
|
*/
|
||||||
pub struct FileHash {
|
pub struct FileHash {
|
||||||
hash: String,
|
hash: String,
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,15 @@ use storage::file::hash::FileHash;
|
||||||
#[derive(Hash)]
|
#[derive(Hash)]
|
||||||
#[derive(Eq)]
|
#[derive(Eq)]
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
|
/**
|
||||||
|
* FileID
|
||||||
|
*
|
||||||
|
* A FileID contains of two parts: The ID type and the Hash. For example the FileID
|
||||||
|
*
|
||||||
|
* UUID-235-1215-1212
|
||||||
|
*
|
||||||
|
* has a type ("UUID") and a Hash ("235-1215-1212").
|
||||||
|
*/
|
||||||
pub struct FileID {
|
pub struct FileID {
|
||||||
id: FileHash,
|
id: FileHash,
|
||||||
id_type: FileIDType,
|
id_type: FileIDType,
|
||||||
|
@ -30,14 +39,23 @@ impl FileID {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the type of the FileID
|
||||||
|
*/
|
||||||
pub fn get_type(&self) -> FileIDType {
|
pub fn get_type(&self) -> FileIDType {
|
||||||
self.id_type.clone()
|
self.id_type.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Hash of the FileID
|
||||||
|
*/
|
||||||
pub fn get_id(&self) -> FileHash {
|
pub fn get_id(&self) -> FileHash {
|
||||||
self.id.clone()
|
self.id.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a String into a FileID, if possible
|
||||||
|
*/
|
||||||
pub fn parse(string: &String) -> Option<FileID> {
|
pub fn parse(string: &String) -> Option<FileID> {
|
||||||
// we assume that it is an path
|
// we assume that it is an path
|
||||||
let regex = Regex::new(r"([:alnum:]*)-([:upper:]*)-([A-Za-z0-9-_]*)\.(.*)").unwrap();
|
let regex = Regex::new(r"([:alnum:]*)-([:upper:]*)-([A-Za-z0-9-_]*)\.(.*)").unwrap();
|
||||||
|
|
|
@ -9,6 +9,11 @@ use std::hash::Hash;
|
||||||
#[derive(Eq)]
|
#[derive(Eq)]
|
||||||
// #[derive(Display)]
|
// #[derive(Display)]
|
||||||
#[derive(Hash)]
|
#[derive(Hash)]
|
||||||
|
/**
|
||||||
|
* File ID type
|
||||||
|
*
|
||||||
|
* Currently only UUID is available. Maybe this will be the only type available at all.
|
||||||
|
*/
|
||||||
pub enum FileIDType {
|
pub enum FileIDType {
|
||||||
UUID,
|
UUID,
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,43 +19,71 @@ use super::parser::{FileHeaderParser, Parser, ParserError};
|
||||||
use self::header::spec::*;
|
use self::header::spec::*;
|
||||||
use self::header::data::*;
|
use self::header::data::*;
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Internal abstract view on a file. Does not exist on the FS and is just kept
|
* Internal abstract view on a file. Does not neccessarily exist on the FS and is just kept
|
||||||
* internally until it is written to disk.
|
* internally until it is written to disk.
|
||||||
*/
|
*/
|
||||||
pub struct File {
|
pub struct File {
|
||||||
|
/// The name of the module which owns this file
|
||||||
pub owning_module_name : &'static str,
|
pub owning_module_name : &'static str,
|
||||||
|
|
||||||
|
/// The header of the file
|
||||||
pub header : FileHeaderData,
|
pub header : FileHeaderData,
|
||||||
|
|
||||||
|
/// The content part of the file
|
||||||
pub data : String,
|
pub data : String,
|
||||||
|
|
||||||
|
/// The ID of the file
|
||||||
pub id : FileID,
|
pub id : FileID,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl File {
|
impl File {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the owner module name of the file
|
||||||
|
*/
|
||||||
pub fn owner_name(&self) -> &'static str {
|
pub fn owner_name(&self) -> &'static str {
|
||||||
self.owning_module_name
|
self.owning_module_name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the header of the file
|
||||||
|
*/
|
||||||
pub fn header(&self) -> &FileHeaderData {
|
pub fn header(&self) -> &FileHeaderData {
|
||||||
&self.header
|
&self.header
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the header of the file
|
||||||
|
*/
|
||||||
pub fn set_header(&mut self, new_header: FileHeaderData) {
|
pub fn set_header(&mut self, new_header: FileHeaderData) {
|
||||||
self.header = new_header;
|
self.header = new_header;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the data of the file
|
||||||
|
*/
|
||||||
pub fn data(&self) -> &String {
|
pub fn data(&self) -> &String {
|
||||||
&self.data
|
&self.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the (header, data) of the file
|
||||||
|
*/
|
||||||
pub fn contents(&self) -> (&FileHeaderData, &String) {
|
pub fn contents(&self) -> (&FileHeaderData, &String) {
|
||||||
(self.header(), self.data())
|
(self.header(), self.data())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the id of the file
|
||||||
|
*/
|
||||||
pub fn id(&self) -> &FileID {
|
pub fn id(&self) -> &FileID {
|
||||||
&self.id
|
&self.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the header or the data of the file match some regex
|
||||||
|
*/
|
||||||
pub fn matches_with(&self, r: &Regex) -> bool {
|
pub fn matches_with(&self, r: &Regex) -> bool {
|
||||||
r.is_match(&self.data[..]) || self.header.matches_with(r)
|
r.is_match(&self.data[..]) || self.header.matches_with(r)
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,12 @@ pub struct Store {
|
||||||
cache : RefCell<Cache>,
|
cache : RefCell<Cache>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store object
|
||||||
|
*
|
||||||
|
* This object is an abstraction layer over FS and an interface to the object store of this
|
||||||
|
* software.
|
||||||
|
*/
|
||||||
impl Store {
|
impl Store {
|
||||||
|
|
||||||
pub fn new(storepath: String) -> Store {
|
pub fn new(storepath: String) -> Store {
|
||||||
|
@ -36,12 +42,20 @@ impl Store {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put a file into the cache
|
||||||
|
*/
|
||||||
fn put_in_cache(&self, f: File) -> FileID {
|
fn put_in_cache(&self, f: File) -> FileID {
|
||||||
let res = f.id().clone();
|
let res = f.id().clone();
|
||||||
self.cache.borrow_mut().insert(f.id().clone(), Rc::new(RefCell::new(f)));
|
self.cache.borrow_mut().insert(f.id().clone(), Rc::new(RefCell::new(f)));
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a file by ID into the cache and return it afterwards
|
||||||
|
*
|
||||||
|
* Returns None if the file could be loaded from the Filesystem
|
||||||
|
*/
|
||||||
pub fn load_in_cache<HP>(&self, m: &Module, parser: &Parser<HP>, id: FileID)
|
pub fn load_in_cache<HP>(&self, m: &Module, parser: &Parser<HP>, id: FileID)
|
||||||
-> Option<Rc<RefCell<File>>>
|
-> Option<Rc<RefCell<File>>>
|
||||||
where HP: FileHeaderParser
|
where HP: FileHeaderParser
|
||||||
|
@ -63,6 +77,11 @@ impl Store {
|
||||||
self.load(&id)
|
self.load(&id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a new file for a module.
|
||||||
|
*
|
||||||
|
* Returns the new FileID object then
|
||||||
|
*/
|
||||||
pub fn new_file(&self, module: &Module)
|
pub fn new_file(&self, module: &Module)
|
||||||
-> FileID
|
-> FileID
|
||||||
{
|
{
|
||||||
|
@ -77,6 +96,11 @@ impl Store {
|
||||||
self.put_in_cache(f)
|
self.put_in_cache(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a new file from a parser result.
|
||||||
|
*
|
||||||
|
* @deprecated This function shouldn't be needed anymore
|
||||||
|
*/
|
||||||
pub fn new_file_from_parser_result(&self,
|
pub fn new_file_from_parser_result(&self,
|
||||||
module: &Module,
|
module: &Module,
|
||||||
id: FileID,
|
id: FileID,
|
||||||
|
@ -94,6 +118,11 @@ impl Store {
|
||||||
self.put_in_cache(f)
|
self.put_in_cache(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a new file for a module, providing some header data
|
||||||
|
*
|
||||||
|
* Returns the new FileID object then
|
||||||
|
*/
|
||||||
pub fn new_file_with_header(&self,
|
pub fn new_file_with_header(&self,
|
||||||
module: &Module,
|
module: &Module,
|
||||||
h: FileHeaderData)
|
h: FileHeaderData)
|
||||||
|
@ -109,6 +138,11 @@ impl Store {
|
||||||
self.put_in_cache(f)
|
self.put_in_cache(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a new file for a module, providing some initial data
|
||||||
|
*
|
||||||
|
* Returns the new FileID object then
|
||||||
|
*/
|
||||||
pub fn new_file_with_data(&self, module: &Module, d: String)
|
pub fn new_file_with_data(&self, module: &Module, d: String)
|
||||||
-> FileID
|
-> FileID
|
||||||
{
|
{
|
||||||
|
@ -122,6 +156,12 @@ impl Store {
|
||||||
self.put_in_cache(f)
|
self.put_in_cache(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a new file for a module, providing some initial data and some header
|
||||||
|
*
|
||||||
|
* Returns the new FileID object then
|
||||||
|
*/
|
||||||
pub fn new_file_with_content(&self,
|
pub fn new_file_with_content(&self,
|
||||||
module: &Module,
|
module: &Module,
|
||||||
h: FileHeaderData,
|
h: FileHeaderData,
|
||||||
|
@ -138,6 +178,11 @@ impl Store {
|
||||||
self.put_in_cache(f)
|
self.put_in_cache(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persist a File on the filesystem
|
||||||
|
*
|
||||||
|
* Returns true if this worked
|
||||||
|
*/
|
||||||
pub fn persist<HP>(&self,
|
pub fn persist<HP>(&self,
|
||||||
p: &Parser<HP>,
|
p: &Parser<HP>,
|
||||||
f: Rc<RefCell<File>>) -> bool
|
f: Rc<RefCell<File>>) -> bool
|
||||||
|
@ -162,8 +207,15 @@ impl Store {
|
||||||
}).map_err(|writeerr| {
|
}).map_err(|writeerr| {
|
||||||
debug!("Could not create file at '{}'", path);
|
debug!("Could not create file at '{}'", path);
|
||||||
}).and(Ok(true)).unwrap()
|
}).and(Ok(true)).unwrap()
|
||||||
|
|
||||||
|
// TODO: Is this unwrap() save?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to generate the store path
|
||||||
|
*
|
||||||
|
* Kills the program if it fails
|
||||||
|
*/
|
||||||
fn ensure_store_path_exists(&self) {
|
fn ensure_store_path_exists(&self) {
|
||||||
use std::fs::create_dir_all;
|
use std::fs::create_dir_all;
|
||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
@ -176,11 +228,20 @@ impl Store {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a file from the cache by FileID
|
||||||
|
*
|
||||||
|
* TODO: Semantics: This function should load from FS if the file is not in the cache yet or
|
||||||
|
* fail if the file is not available.
|
||||||
|
*/
|
||||||
pub fn load(&self, id: &FileID) -> Option<Rc<RefCell<File>>> {
|
pub fn load(&self, id: &FileID) -> Option<Rc<RefCell<File>>> {
|
||||||
debug!("Loading '{:?}'", id);
|
debug!("Loading '{:?}'", id);
|
||||||
self.cache.borrow().get(id).cloned()
|
self.cache.borrow().get(id).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a file from the filesystem/cache by a FileHash
|
||||||
|
*/
|
||||||
pub fn load_by_hash<HP>(&self,
|
pub fn load_by_hash<HP>(&self,
|
||||||
m: &Module,
|
m: &Module,
|
||||||
parser: &Parser<HP>,
|
parser: &Parser<HP>,
|
||||||
|
@ -232,6 +293,11 @@ impl Store {
|
||||||
}).unwrap_or(None)
|
}).unwrap_or(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a file from the filesystem by FileID
|
||||||
|
*
|
||||||
|
* Returns true if this works.
|
||||||
|
*/
|
||||||
pub fn remove(&self, id: FileID) -> bool {
|
pub fn remove(&self, id: FileID) -> bool {
|
||||||
use std::fs::remove_file;
|
use std::fs::remove_file;
|
||||||
|
|
||||||
|
@ -250,6 +316,9 @@ impl Store {
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load all files for a module
|
||||||
|
*/
|
||||||
pub fn load_for_module<HP>(&self, m: &Module, parser: &Parser<HP>)
|
pub fn load_for_module<HP>(&self, m: &Module, parser: &Parser<HP>)
|
||||||
-> Vec<Rc<RefCell<File>>>
|
-> Vec<Rc<RefCell<File>>>
|
||||||
where HP: FileHeaderParser
|
where HP: FileHeaderParser
|
||||||
|
@ -276,6 +345,9 @@ impl Store {
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to generate a new FileID object
|
||||||
|
*/
|
||||||
fn get_new_file_id(&self) -> FileID {
|
fn get_new_file_id(&self) -> FileID {
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
let hash = FileHash::from(Uuid::new_v4().to_hyphenated_string());
|
let hash = FileHash::from(Uuid::new_v4().to_hyphenated_string());
|
||||||
|
|
|
@ -83,11 +83,26 @@ impl Display for ParserError {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trait for a header parser.
|
||||||
|
*
|
||||||
|
* This parser type has to provide two functions:
|
||||||
|
* - read(), which reads an String into a FileHeaderData structure
|
||||||
|
* - write(), which parses a FileHeaderData structure into a String
|
||||||
|
*
|
||||||
|
* TODO: Use Write/Read traits?
|
||||||
|
*/
|
||||||
pub trait FileHeaderParser : Sized + Debug + Display {
|
pub trait FileHeaderParser : Sized + Debug + Display {
|
||||||
fn read(&self, string: Option<String>) -> Result<FileHeaderData, ParserError>;
|
fn read(&self, string: Option<String>) -> Result<FileHeaderData, ParserError>;
|
||||||
fn write(&self, data: &FileHeaderData) -> Result<String, ParserError>;
|
fn write(&self, data: &FileHeaderData) -> Result<String, ParserError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parser
|
||||||
|
*
|
||||||
|
* This Parser object is an abstraction which uses the FileHeaderParser to parse the whole contents
|
||||||
|
* of a file into a header (FileHeaderData) structure and the content (String).
|
||||||
|
*/
|
||||||
pub struct Parser<HP> {
|
pub struct Parser<HP> {
|
||||||
headerp : HP,
|
headerp : HP,
|
||||||
}
|
}
|
||||||
|
@ -100,6 +115,10 @@ impl<HP: FileHeaderParser> Parser<HP> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the String which is the contents of a file into a (FileHeaderData, String) tuple, which
|
||||||
|
* is the header and the content of the file.
|
||||||
|
*/
|
||||||
pub fn read(&self, s: String) -> Result<(FileHeaderData, String), ParserError> {
|
pub fn read(&self, s: String) -> Result<(FileHeaderData, String), ParserError> {
|
||||||
debug!("Reading into internal datastructure: '{}'", s);
|
debug!("Reading into internal datastructure: '{}'", s);
|
||||||
let divided = self.divide_text(&s);
|
let divided = self.divide_text(&s);
|
||||||
|
@ -122,6 +141,10 @@ impl<HP: FileHeaderParser> Parser<HP> {
|
||||||
Ok((h_parseres, data.unwrap_or(String::new())))
|
Ok((h_parseres, data.unwrap_or(String::new())))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the FileHeaderData and String (header and content) of the tuple into a String, which
|
||||||
|
* can then simply be written into the store as a file.
|
||||||
|
*/
|
||||||
pub fn write(&self, tpl : (&FileHeaderData, &String)) -> Result<String, ParserError> {
|
pub fn write(&self, tpl : (&FileHeaderData, &String)) -> Result<String, ParserError> {
|
||||||
debug!("Parsing internal datastructure to String");
|
debug!("Parsing internal datastructure to String");
|
||||||
let (header, data) = tpl;
|
let (header, data) = tpl;
|
||||||
|
@ -132,6 +155,10 @@ impl<HP: FileHeaderParser> Parser<HP> {
|
||||||
Ok(text)
|
Ok(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to parse the full-text of a file into a header part (String) and a content part
|
||||||
|
* (String)
|
||||||
|
*/
|
||||||
fn divide_text(&self, text: &String) -> Result<(Option<String>, Option<String>), ParserError> {
|
fn divide_text(&self, text: &String) -> Result<(Option<String>, Option<String>), ParserError> {
|
||||||
let re = Regex::new(r"(?sm)^---$(.*)^---$(.*)").unwrap();
|
let re = Regex::new(r"(?sm)^---$(.*)^---$(.*)").unwrap();
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,9 @@ use std::ops::Deref;
|
||||||
|
|
||||||
use storage::file::File;
|
use storage::file::File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trait for a printer which can be used to print data from files
|
||||||
|
*/
|
||||||
pub trait FilePrinter {
|
pub trait FilePrinter {
|
||||||
|
|
||||||
fn new(verbose: bool, debug: bool) -> Self;
|
fn new(verbose: bool, debug: bool) -> Self;
|
||||||
|
@ -40,6 +43,9 @@ pub trait FilePrinter {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Printer which prints in debug mode if enabled
|
||||||
|
*/
|
||||||
struct DebugPrinter {
|
struct DebugPrinter {
|
||||||
debug: bool,
|
debug: bool,
|
||||||
}
|
}
|
||||||
|
@ -68,6 +74,9 @@ impl FilePrinter for DebugPrinter {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple printer, which just uses the info!() macro or debug!() macro if in debug mode.
|
||||||
|
*/
|
||||||
struct SimplePrinter {
|
struct SimplePrinter {
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
|
@ -107,6 +116,9 @@ impl FilePrinter for SimplePrinter {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table printer to print file information in a nice ASCII-table
|
||||||
|
*/
|
||||||
pub struct TablePrinter {
|
pub struct TablePrinter {
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Util: Check wether a String can be parsed as an URL
|
||||||
|
*/
|
||||||
pub fn is_url(url: &String) -> bool {
|
pub fn is_url(url: &String) -> bool {
|
||||||
Url::parse(&url[..]).is_ok()
|
Url::parse(&url[..]).is_ok()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue