pub struct Index {
pub files: BTreeMap<PathBuf, Zettel>,
pub timestamp: SystemTime,
}Expand description
The heart of a Zettelkasten is the data representing the interrelations
between the zettels. All that information is bundled in the
Index. See its Fields
for details.
A YAML-representation of an example Index might look like this:
---
files:
file1.md:
title: File 1
followups: [file2.md]
keywords: [example, first]
links: [subdir/file3.md]
file2.md:
title: File 2
followups: [subdir/file3.md]
keywords: [example, second]
links: []
subdir/file3.md:
title: File 3
followups: []
keywords: [example, third]
links: [file1.md]
timestamp:
secs_since_epoch: 1543078763
nanos_since_epoch: 449322318The Index provides the functionality necessary to query the Zettelkasten. For that, it offers a
- Basic API, as well as an
- Extended API
Please refer to the API Introduction for info on which to use.
§Basic API
§Handling the Index itself
§Working with Zettels
§Inspect Sequences
§Inspect Links
§Search
§Listing
§Extended API
All functions of the Basic API plus the following:
§Handling the Index itself
§Working with Zettels
Fields§
§files: BTreeMap<PathBuf, Zettel>A key-value-store containing indexed data about the zettels.
- The keys are paths to the respective zettel file (relative to the root directory of the Zettelkasten).
- The values are
Zettels
timestamp: SystemTimeA timestamp that shows when the index was last updated.
Implementations§
Source§impl Index
impl Index
Sourcepub fn new(cfg: &Config) -> Result<Index, Error>
pub fn new(cfg: &Config) -> Result<Index, Error>
Creates a new Index with data contained in
Config. The actual fields used are:
rootdir: path to the root directory containing the zettel files.indexingmethod: theIndexingMethodused.ignorefile: path to the gitignore-compatible file telling the program which files in the root directory to ignore.
Note: This really creates a new Index. All files in the root
directory are parsed. If you just want to load (and maybe update) an
existing index, use Index::load and update respectively.
§Example
let cfg = Config::from_file(Path::new("examples/zettels-examples.cfg.yaml"))?;
let index = Index::new(&cfg)?;Note:
- If one of the files is non-text (e.g. an image) it is not added
to the index. An
infoabout this is issued to logging. - If one of the files does not contain YAML-metadata, it is added to the
index with the
title“untitled” and emptyfollowupsandkeywords. The markdown links (if any) are still parsed andlinksis populated as usual.
§Errors
Error::BadLinkif one of the files in the root directory
links to a target that doesn’t exist (both viafollowupsand via markdown link).Error::IgnoreFilefor problems applying an existing ignore file. (A missing ignore file is no problem).Error::Iowrapping several kinds ofstd::io:Error, e.g. problems executing grep or ripgrep, problems with the files etc.Error::NormalizePathif one of the files or a link infollowupsorlinkscan not be expressed relative to the root directoryError::Yamlwhen deserializing a Zettel from YAML failed for one of the files (but only if it contains any YAML, at all).
Sourcepub fn load(cfg: &Config) -> Result<Index, Error>
pub fn load(cfg: &Config) -> Result<Index, Error>
Loads an existing Index from the file
specified in the indexfile field of Config.
Note: This only loads the information contained in the index file.
Changes in the zettel files since last update of the index are not
represented in the index (i.e. since
Index::new() or
index.update() were called).
§Example
let cfg = Config::from_file(Path::new("examples/zettels-examples.cfg.yaml"))?;
let index = Index::load(&cfg)?;§Errors
Error::Iofor problems reading the index file specified inConfig’s fieldindexfile.Error::Yamlwhen deserializing from YAML failed.
Sourcepub fn update(&mut self, cfg: &Config) -> Result<(), Error>
pub fn update(&mut self, cfg: &Config) -> Result<(), Error>
Updates an existing index using the info contained in the
Config passed as an argument. Only files
modified after the index’ timestamp are inspected.
The actual Config fields used are:
cfg.rootdircfg.ignorefilecfg.indexingmethod
§Example
let cfg = Config::from_file(Path::new("examples/zettels-examples.cfg.yaml"))?;
let mut index = Index::load(&cfg)?;
index.update(&cfg)?;Note:
- If one of the files is non-text (e.g. an image) it is not added
to the index. An
infoabout this is issued to logging. - If one of the files does not contain YAML-metadata, it is added to the
index with the
title“untitled” and emptyfollowupsandkeywords. The markdown links (if any) are still parsed andlinksis populated as usual.
§Errors
Error::BadLinkif one of the files links to a target that doesn’t exist (both viafollowupsand via markdown link).Error::IgnoreFilefor problems applying an existing ignore file. (A missing ignore file is no problem).Error::Iowrapping several kinds ofstd::io:Error, e.g. problems executing grep or ripgrep, problems with the files etc.Error::NormalizePathif one of the files or a link infollowupsorlinkscan not be expressed relative to the root directoryError::Yamlwhen deserializing a Zettel from YAML failed for one of the files (but only if it contains any YAML, at all).
Sourcepub fn save(&self, cfg: &Config) -> Result<(), Error>
pub fn save(&self, cfg: &Config) -> Result<(), Error>
Write a YAML-representation to the file specified in the Config field
indexfile.
§Example
let cfg = Config::from_file(Path::new("examples/zettels-examples.cfg.yaml"))?;
let index = Index::load(&cfg)?;
index.save(&cfg)
.expect("Failed to save index to file.");§Errors
Error::Iofor problems with the specified indexfile.Error::Yamlfor problems serializing to YAML.
Sourcepub fn get_zettel<P: AsRef<Path>>(&self, key: P) -> Option<&Zettel>
pub fn get_zettel<P: AsRef<Path>>(&self, key: P) -> Option<&Zettel>
Returns a reference to the Zettel found at key.
Returns None if said key is not present.
The key is a path to the file relative to root directory.
Internally, this function calls the method
get
of std::collections::BTreeMap.
See documentation there for details.
§Example
let cfg = Config::from_file(Path::new("examples/zettels-examples.cfg.yaml"))?;
let index = Index::load(&cfg)?;
let z1 = index.get_zettel(Path::new("file1.md"));
assert!(z1.is_some());
let z1 = z1.unwrap();
assert_eq!(z1.title, "File 1");Sourcepub fn sequences(
&self,
scope: &HashSet<PathBuf>,
cfg_sequence_start: &SequenceStart,
) -> HashSet<PathBuf>
pub fn sequences( &self, scope: &HashSet<PathBuf>, cfg_sequence_start: &SequenceStart, ) -> HashSet<PathBuf>
Returns a list of all the sequences the Zettels specified by scope is
a part of.
§Example
let cfg = Config::from_file(Path::new("examples/zettels-examples.cfg.yaml"))?;
let index = Index::load(&cfg)?;
let mut scope = HashSet::new();
scope.insert(PathBuf::from("file1.md"));
let sequences = index.sequences(&scope, &cfg.sequencestart);Sourcepub fn zettels_of_sequence<P: AsRef<Path>>(&self, key: P) -> HashSet<PathBuf>
pub fn zettels_of_sequence<P: AsRef<Path>>(&self, key: P) -> HashSet<PathBuf>
Using the Zettel specified by key as an identifier for a sequence, this
function returns a list of all the zettels that belong to that sequence.
§Example
let cfg = Config::from_file(Path::new("examples/zettels-examples.cfg.yaml"))?;
let index = Index::load(&cfg)?;
let seq_zettels = index.zettels_of_sequence(PathBuf::from("file1.md"));Sourcepub fn sequence_tree(&self, scope: &HashSet<PathBuf>) -> HashSet<PathBuf>
pub fn sequence_tree(&self, scope: &HashSet<PathBuf>) -> HashSet<PathBuf>
Returns a list of zettels that are part of all sequences of which the
zettels specified by scope are also a part of.
§Example
let cfg = Config::from_file(Path::new("examples/zettels-examples.cfg.yaml"))?;
let index = Index::load(&cfg)?;
let mut scope = HashSet::new();
scope.insert(PathBuf::from("file1.md"));
let sequence_tree = index.sequence_tree(&scope);
assert!(sequence_tree.contains(&PathBuf::from("file1.md")));
assert!(sequence_tree.contains(&PathBuf::from("file2.md")));
assert!(sequence_tree.contains(&PathBuf::from("file3.md")));
assert!(sequence_tree.contains(&PathBuf::from("subdir/file4.md")));Sourcepub fn sequence_tree_whole(&self, scope: &HashSet<PathBuf>) -> HashSet<PathBuf>
pub fn sequence_tree_whole(&self, scope: &HashSet<PathBuf>) -> HashSet<PathBuf>
Returns a list of zettels that are part of all sequences of which the
zettels specified by scope are also a part of, as well as related
sequences.
§Example
let cfg = Config::from_file(Path::new("examples/zettels-examples.cfg.yaml"))?;
let index = Index::load(&cfg)?;
let mut scope = HashSet::new();
scope.insert(PathBuf::from("file1.md"));
let sequence_tree = index.sequence_tree_whole(&scope);
assert!(sequence_tree.contains(&PathBuf::from("file1.md")));
assert!(sequence_tree.contains(&PathBuf::from("file2.md")));
assert!(sequence_tree.contains(&PathBuf::from("file3.md")));
assert!(sequence_tree.contains(&PathBuf::from("subdir/file4.md")));
assert!(sequence_tree.contains(&PathBuf::from("subdir/file5.md")));Sourcepub fn parents_of_zettel<P: AsRef<Path>>(&self, key: P) -> HashSet<PathBuf>
pub fn parents_of_zettel<P: AsRef<Path>>(&self, key: P) -> HashSet<PathBuf>
Returns the paths to all zettels in the index that list the zettel
specified by key as their followups.
§Note
In Luhmann’s Zettelkasten system, a Zettel can not be a followup of more than one other Zettel. A user might approach this differently, however, so this returns a list (a HashSet, to be exact).
§Example
let cfg = Config::from_file(Path::new("examples/zettels-examples.cfg.yaml"))?;
let index = Index::load(&cfg)?;
let parents = index.parents_of_zettel("file2.md");
for parent in parents {
println!("{:?}", parent);
}Sourcepub fn inspect_links(&self, scope: &HashSet<PathBuf>) -> HashSet<PathBuf>
pub fn inspect_links(&self, scope: &HashSet<PathBuf>) -> HashSet<PathBuf>
Collects the outgoing links of a list of zettels specified by
linkers.
Returns a HashSet.
§Example
let cfg = Config::from_file(Path::new("examples/zettels-examples.cfg.yaml"))?;
let index = Index::load(&cfg)?;
// We want the links of one Zettel: file1.md
let mut linkers = HashSet::new();
linkers.insert(PathBuf::from("file1.md"));
let links = index.inspect_links(&linkers);Sourcepub fn inspect_incoming_links(
&self,
scope: &HashSet<PathBuf>,
all: bool,
) -> HashSet<PathBuf>
pub fn inspect_incoming_links( &self, scope: &HashSet<PathBuf>, all: bool, ) -> HashSet<PathBuf>
Returns a list of zettels that link to the zettels specified by
zettels. If all is set to true, only zettels are returned that
link to all specified zettels.
§Example
let cfg = Config::from_file(Path::new("examples/zettels-examples.cfg.yaml"))?;
let index = Index::load(&cfg)?;
// We want zettels linking to two Zettels: file2.md and file3.md
let mut targets = HashSet::new();
targets.insert(PathBuf::from("file2.md"));
targets.insert(PathBuf::from("file3.md"));
// What links to either of them?
let incoming = index.inspect_incoming_links(&targets, false);
assert_eq!(incoming.len(), 3);
assert!(incoming.contains(&PathBuf::from("file1.md")));
assert!(incoming.contains(&PathBuf::from("file2.md")));
assert!(incoming.contains(&PathBuf::from("onlies/markdown-only.md")));
// What links to both of them?
let incoming = index.inspect_incoming_links(&targets, true);
assert_eq!(incoming.len(), 1);
assert!(incoming.contains(&PathBuf::from("file1.md")));Sourcepub fn search_keywords(
&self,
searched_keywords: Vec<String>,
all: bool,
) -> HashSet<PathBuf>
pub fn search_keywords( &self, searched_keywords: Vec<String>, all: bool, ) -> HashSet<PathBuf>
Takes a vector of Strings, containing keywords to be searched for.
Returns a list of zettels that have one or – if all is true – all
of these keywords.
§Example
let cfg = Config::from_file(Path::new("examples/zettels-examples.cfg.yaml"))?;
let index = Index::load(&cfg)?;
let searched_keywords = vec!["first".to_string()];
let matching_zettels = index.search_keywords(searched_keywords,
false);
assert_eq!(matching_zettels.len(), 1);
assert!(matching_zettels.contains(&PathBuf::from("file1.md")));Sourcepub fn search_title<T: AsRef<str>>(
&self,
search_term: T,
exact: bool,
) -> HashSet<PathBuf>
pub fn search_title<T: AsRef<str>>( &self, search_term: T, exact: bool, ) -> HashSet<PathBuf>
Searches the index for zettels whose title matches search_term
(exactly if exact is true).
Returns a list of matching zettels.
§Example
let cfg = Config::from_file(Path::new("examples/zettels-examples.cfg.yaml"))?;
let index = Index::load(&cfg)?;
let matching_zettels = index.search_title("File 1", false);
assert_eq!(matching_zettels.len(), 1);
assert!(matching_zettels.contains(&PathBuf::from("file1.md")));Sourcepub fn combi_search<T: AsRef<str>>(
&self,
searched_keywords: Vec<String>,
all: bool,
search_term: T,
exact: bool,
) -> HashSet<PathBuf>
pub fn combi_search<T: AsRef<str>>( &self, searched_keywords: Vec<String>, all: bool, search_term: T, exact: bool, ) -> HashSet<PathBuf>
Combines searches for keywords and title.
If exact is true, the title must match search_term exactly.
If all is true, only zettels are returned that match all search
criteria.
§Example
let cfg = Config::from_file(Path::new("examples/zettels-examples.cfg.yaml"))?;
let index = Index::load(&cfg)?;
let searched_keywords = vec!["first".to_string()];
let matching_zettels = index.combi_search(searched_keywords,
true,
"File",
false);
assert_eq!(matching_zettels.len(), 1);
assert!(matching_zettels.contains(&PathBuf::from("file1.md")));Sourcepub fn get_keywords(&self) -> HashSet<String>
pub fn get_keywords(&self) -> HashSet<String>
Lists all keywords present in the zettels.
§Example
let cfg = Config::from_file(Path::new("examples/zettels-examples.cfg.yaml"))?;
let index = Index::load(&cfg)?;
let all_keywords = index.get_keywords();
assert!(all_keywords.contains("test"));
assert!(all_keywords.contains("example"));Sourcepub fn count_keywords(&self) -> BTreeMap<String, u32>
pub fn count_keywords(&self) -> BTreeMap<String, u32>
Lists all keywords present in the zettels along with how often they occur in the whole zettelkasen.
§Example
let cfg = Config::from_file(Path::new("examples/zettels-examples.cfg.yaml"))?;
let index = Index::load(&cfg)?;
let keyword_count = index.count_keywords();
assert_eq!(Some(&5), keyword_count.get("example"));
assert_eq!(Some(&1), keyword_count.get("test"));Sourcepub fn from_yaml<T: AsRef<str>>(yaml: T) -> Result<Index, Error>
pub fn from_yaml<T: AsRef<str>>(yaml: T) -> Result<Index, Error>
§Extended API
Creates a new Index by deserializing it from a YAML-string. Returns an error if something went wrong with deserializing it.
§Example
let yaml = "---
files:
file1.md:
title: File 1
followups: [subdir/file2.md]
keywords: [example]
links: []
subdir/file2.md:
title: File 2
followups: []
keywords: [example, second]
links: [file1.md]
timestamp:
secs_since_epoch: 1543078763
nanos_since_epoch: 449322318";
let index = Index::from_yaml(yaml)?;§Errors
Error::Yamlwhen deserializing from YAML failed.
Sourcepub fn from_file<P: AsRef<Path>>(indexfile: P) -> Result<Index, Error>
pub fn from_file<P: AsRef<Path>>(indexfile: P) -> Result<Index, Error>
§Extended API
Loads an existing Index by deserializing it from a YAML-file. Returns an error if something went wrong with reading the file or deserializing it.
let file = Path::new("examples/index.yaml");
let index = Index::from_file(file)?;§Errors
Error::Iofor problems reading the file.Error::Yamlwhen deserializing from YAML failed.
Sourcepub fn update_timestamp(&mut self)
pub fn update_timestamp(&mut self)
Sourcepub fn to_file<P: AsRef<Path>>(&self, indexfile: P) -> Result<(), Error>
pub fn to_file<P: AsRef<Path>>(&self, indexfile: P) -> Result<(), Error>
§Extended API
Write a YAML-representation of the index to file.
§Example
let cfg = Config::from_file(Path::new("examples/zettels-examples.cfg.yaml"))?;
let index = Index::load(&cfg)?;
index.to_file(Path::new("examples/index.yaml"))?;§Errors
Error::Iofor problems with the specified file.Error::Yamlfor problems serializing to YAML.
Sourcepub fn add_zettel<P: AsRef<Path>>(&mut self, key: P, zettel: Zettel)
pub fn add_zettel<P: AsRef<Path>>(&mut self, key: P, zettel: Zettel)
§Extended API
Adds a Zettel to the index with the path to the file relative to root
directory as key. Internally,
insert
of std::collections::BTreeMap
is called. See documentation there for details. That method’s return
value is disregarded, however.
let cfg = Config::from_file(Path::new("examples/zettels-examples.cfg.yaml"))?;
let mut index = Index::new(&cfg)?;
let file = Path::new("test-zettel.md"); //relative to the root directory
let zettel = Zettel::new("Empty Zettel for Testing");
index.add_zettel(file, zettel);Sourcepub fn remove_zettel<P: AsRef<Path>>(&mut self, key: P) -> Option<Zettel>
pub fn remove_zettel<P: AsRef<Path>>(&mut self, key: P) -> Option<Zettel>
§Extended API
Removes a Zettel from the index. The key is the path to the
corresponding file, relative to the root directory.
Returns the old Zettel found at the key or None if it was not present
in the first place. Please note that the return value doesn’t need to
be checked for success. The entry for key is guaranteed to be gone,
regardless of the return value.
Internally,
remove
of std::collections::BTreeMap
is called. See documentation there for details.
§Example 1
In most cases, you’ll just disregard the return value.
let cfg = Config::from_file(Path::new("examples/zettels-examples.cfg.yaml"))?;
let mut index = Index::new(&cfg)?;
let file = Path::new("test-zettel.md"); //relative to root directory
let zettel = Zettel::new("Empty Zettel for Testing");
index.add_zettel(file, zettel);
index.remove_zettel(file);
// or, if you want to be very tidy:
let _ = index.remove_zettel(file);§Example 2
If you want to do something with the old Zettel:
// snip
let zettel = Zettel::new("Empty Zettel for Testing");
// We clone here so we have something to compare.
index.add_zettel(file, zettel.clone());
let removed = index.remove_zettel(file);
assert_eq!(removed, Some(zettel));Sourcepub fn get_mut_zettel<P: AsRef<Path>>(&mut self, key: P) -> Option<&mut Zettel>
pub fn get_mut_zettel<P: AsRef<Path>>(&mut self, key: P) -> Option<&mut Zettel>
§Extended API
Returns a mutable reference to the Zettel found
at key.
Returns None of said key is not present.
The key is the path to the corresponding file relative to root
directory.
Note: Editing this only changes the entry in the index, not the file itself. Changes might be overwritten when the index us updated next. This method is intended for developers of frontends who want to present the metadata and the document body to their users, separately. If you use this, take care to write changes back to the file.
Internally, this function calls the method
get_mut
of std::collections::BTreeMap.
See documentation there for details.
§Example
let cfg = Config::from_file(Path::new("examples/zettels-examples.cfg.yaml"))?;
let mut index = Index::load(&cfg)?;
// Use an inner scope so z goes out of scope automatically.
{
let z = index.get_mut_zettel(Path::new("file1.md"));
assert!(z.is_some());
let z = z.unwrap();
z.title = String::from("Changed Title 1");
}
// Let's get it again from index to see if the changes are there.
let z = index.get_zettel(Path::new("file1.md")).unwrap();
assert_eq!(z.title, "Changed Title 1");