Merge branch 'unnamed-annotations' into master-ff
This merge adds several patches to improve libimagannotation and imag-annotate.
This commit is contained in:
commit
03e0864268
8 changed files with 102 additions and 143 deletions
|
@ -32,6 +32,7 @@ libimagstore = { version = "0.10.0", path = "../../../lib/core/libimag
|
||||||
libimagrt = { version = "0.10.0", path = "../../../lib/core/libimagrt" }
|
libimagrt = { version = "0.10.0", path = "../../../lib/core/libimagrt" }
|
||||||
libimagerror = { version = "0.10.0", path = "../../../lib/core/libimagerror" }
|
libimagerror = { version = "0.10.0", path = "../../../lib/core/libimagerror" }
|
||||||
libimagentryannotation = { version = "0.10.0", path = "../../../lib/entry/libimagentryannotation" }
|
libimagentryannotation = { version = "0.10.0", path = "../../../lib/entry/libimagentryannotation" }
|
||||||
|
libimagentrylink = { version = "0.10.0", path = "../../../lib/entry/libimagentrylink" }
|
||||||
libimagentryedit = { version = "0.10.0", path = "../../../lib/entry/libimagentryedit" }
|
libimagentryedit = { version = "0.10.0", path = "../../../lib/entry/libimagentryedit" }
|
||||||
libimagutil = { version = "0.10.0", path = "../../../lib/etc/libimagutil" }
|
libimagutil = { version = "0.10.0", path = "../../../lib/etc/libimagutil" }
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,9 @@
|
||||||
extern crate clap;
|
extern crate clap;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
#[macro_use]
|
||||||
extern crate failure;
|
extern crate failure;
|
||||||
|
extern crate toml_query;
|
||||||
|
|
||||||
extern crate libimagentryannotation;
|
extern crate libimagentryannotation;
|
||||||
extern crate libimagentryedit;
|
extern crate libimagentryedit;
|
||||||
|
@ -45,10 +47,12 @@ extern crate libimagerror;
|
||||||
#[macro_use] extern crate libimagrt;
|
#[macro_use] extern crate libimagrt;
|
||||||
extern crate libimagstore;
|
extern crate libimagstore;
|
||||||
extern crate libimagutil;
|
extern crate libimagutil;
|
||||||
|
extern crate libimagentrylink;
|
||||||
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
|
use toml_query::read::TomlValueReadTypeExt;
|
||||||
|
|
||||||
use libimagentryannotation::annotateable::*;
|
use libimagentryannotation::annotateable::*;
|
||||||
use libimagentryannotation::annotation_fetcher::*;
|
use libimagentryannotation::annotation_fetcher::*;
|
||||||
|
@ -57,9 +61,12 @@ use libimagerror::trace::MapErrTrace;
|
||||||
use libimagerror::exit::ExitUnwrap;
|
use libimagerror::exit::ExitUnwrap;
|
||||||
use libimagerror::io::ToExitCode;
|
use libimagerror::io::ToExitCode;
|
||||||
use libimagerror::errors::ErrorMsg as EM;
|
use libimagerror::errors::ErrorMsg as EM;
|
||||||
|
use libimagerror::iter::TraceIterator;
|
||||||
use libimagrt::runtime::Runtime;
|
use libimagrt::runtime::Runtime;
|
||||||
use libimagrt::setup::generate_runtime_setup;
|
use libimagrt::setup::generate_runtime_setup;
|
||||||
use libimagstore::store::FileLockEntry;
|
use libimagstore::store::FileLockEntry;
|
||||||
|
use libimagstore::iter::get::StoreIdGetIteratorExtension;
|
||||||
|
use libimagentrylink::internal::InternalLinker;
|
||||||
|
|
||||||
mod ui;
|
mod ui;
|
||||||
|
|
||||||
|
@ -89,23 +96,48 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add(rt: &Runtime) {
|
fn add(rt: &Runtime) {
|
||||||
let scmd = rt.cli().subcommand_matches("add").unwrap(); // safed by main()
|
let scmd = rt.cli().subcommand_matches("add").unwrap(); // safed by main()
|
||||||
let annotation_name = scmd.value_of("annotation_name").unwrap(); // safed by clap
|
let mut ids = rt.ids::<::ui::PathProvider>().map_err_trace_exit_unwrap(1).into_iter();
|
||||||
let ids = rt.ids::<::ui::PathProvider>().map_err_trace_exit_unwrap(1);
|
|
||||||
|
|
||||||
ids.into_iter().for_each(|id| {
|
if let Some(first) = ids.next() {
|
||||||
let _ = rt.store()
|
let mut annotation = rt.store()
|
||||||
.get(id.clone())
|
.get(first.clone())
|
||||||
.map_err_trace_exit_unwrap(1)
|
.map_err_trace_exit_unwrap(1)
|
||||||
.ok_or_else(|| EM::EntryNotFound(id.local_display_string()))
|
.ok_or_else(|| EM::EntryNotFound(first.local_display_string()))
|
||||||
.map_err(Error::from)
|
.map_err(Error::from)
|
||||||
.map_err_trace_exit_unwrap(1)
|
.map_err_trace_exit_unwrap(1)
|
||||||
.annotate(rt.store(), annotation_name)
|
.annotate(rt.store())
|
||||||
.map_err_trace_exit_unwrap(1)
|
|
||||||
.edit_content(&rt)
|
|
||||||
.map_err_trace_exit_unwrap(1);
|
.map_err_trace_exit_unwrap(1);
|
||||||
})
|
|
||||||
|
|
||||||
|
let _ = annotation.edit_content(&rt).map_err_trace_exit_unwrap(1);
|
||||||
|
|
||||||
|
for id in ids {
|
||||||
|
let mut entry = rt.store().get(id.clone())
|
||||||
|
.map_err_trace_exit_unwrap(1)
|
||||||
|
.ok_or_else(|| format_err!("Not found: {}", id.local_display_string()))
|
||||||
|
.map_err_trace_exit_unwrap(1);
|
||||||
|
|
||||||
|
let _ = entry.add_internal_link(&mut annotation).map_err_trace_exit_unwrap(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !scmd.is_present("dont-print-name") {
|
||||||
|
if let Some(annotation_id) = annotation
|
||||||
|
.get_header()
|
||||||
|
.read_string("annotation.name")
|
||||||
|
.map_err(Error::from)
|
||||||
|
.map_err_trace_exit_unwrap(1)
|
||||||
|
{
|
||||||
|
let _ = writeln!(rt.stdout(), "Name of the annotation: {}", annotation_id)
|
||||||
|
.to_exit_code()
|
||||||
|
.unwrap_or_exit();
|
||||||
|
} else {
|
||||||
|
error!("Unnamed annotation: {:?}", annotation.get_location());
|
||||||
|
error!("This is most likely a BUG, please report!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debug!("No entries to annotate");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove(rt: &Runtime) {
|
fn remove(rt: &Runtime) {
|
||||||
|
@ -162,25 +194,26 @@ fn list(rt: &Runtime) {
|
||||||
.ok_or_else(|| EM::EntryNotFound(id.local_display_string()))
|
.ok_or_else(|| EM::EntryNotFound(id.local_display_string()))
|
||||||
.map_err(Error::from)
|
.map_err(Error::from)
|
||||||
.map_err_trace_exit_unwrap(1)
|
.map_err_trace_exit_unwrap(1)
|
||||||
.annotations(rt.store())
|
.annotations()
|
||||||
.map_err_trace_exit_unwrap(1)
|
.map_err_trace_exit_unwrap(1)
|
||||||
|
.into_get_iter(rt.store())
|
||||||
|
.trace_unwrap_exit(1)
|
||||||
|
.map(|opt| opt.ok_or_else(|| format_err!("Cannot find entry")))
|
||||||
|
.trace_unwrap_exit(1)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, a)| {
|
.for_each(|(i, entry)| list_annotation(&rt, i, entry, with_text));
|
||||||
list_annotation(&rt, i, a.map_err_trace_exit_unwrap(1), with_text)
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
});
|
});
|
||||||
} else { // ids.len() == 0
|
} else { // ids.len() == 0
|
||||||
// show them all
|
// show them all
|
||||||
let _ = rt
|
rt.store()
|
||||||
.store()
|
|
||||||
.all_annotations()
|
.all_annotations()
|
||||||
.map_err_trace_exit_unwrap(1)
|
.map_err_trace_exit_unwrap(1)
|
||||||
|
.into_get_iter(rt.store())
|
||||||
|
.trace_unwrap_exit(1)
|
||||||
|
.map(|opt| opt.ok_or_else(|| format_err!("Cannot find entry")))
|
||||||
|
.trace_unwrap_exit(1)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, a)| {
|
.for_each(|(i, entry)| list_annotation(&rt, i, entry, with_text));
|
||||||
list_annotation(&rt, i, a.map_err_trace_exit_unwrap(1), with_text)
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,15 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
|
||||||
.subcommand(SubCommand::with_name("add")
|
.subcommand(SubCommand::with_name("add")
|
||||||
.about("Add annotation to an entry")
|
.about("Add annotation to an entry")
|
||||||
.version("0.1")
|
.version("0.1")
|
||||||
|
|
||||||
|
.arg(Arg::with_name("dont-print-name")
|
||||||
|
.short("N")
|
||||||
|
.long("no-name")
|
||||||
|
.takes_value(false)
|
||||||
|
.required(false)
|
||||||
|
.multiple(false)
|
||||||
|
.help("Do not print the name of the annotation after annotating.")
|
||||||
|
)
|
||||||
.arg(Arg::with_name("entry")
|
.arg(Arg::with_name("entry")
|
||||||
.index(1)
|
.index(1)
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
|
@ -38,13 +47,6 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
|
||||||
.multiple(false)
|
.multiple(false)
|
||||||
.help("The entry to add the latitude/longitude to")
|
.help("The entry to add the latitude/longitude to")
|
||||||
.value_name("ENTRY"))
|
.value_name("ENTRY"))
|
||||||
.arg(Arg::with_name("annotation_name")
|
|
||||||
.index(2)
|
|
||||||
.takes_value(true)
|
|
||||||
.required(true)
|
|
||||||
.multiple(false)
|
|
||||||
.help("Name of the new annotation")
|
|
||||||
.value_name("NAME"))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
.subcommand(SubCommand::with_name("remove")
|
.subcommand(SubCommand::with_name("remove")
|
||||||
|
|
|
@ -25,6 +25,9 @@ toml = "0.4"
|
||||||
toml-query = "0.8"
|
toml-query = "0.8"
|
||||||
failure = "0.1"
|
failure = "0.1"
|
||||||
failure_derive = "0.1"
|
failure_derive = "0.1"
|
||||||
|
uuid = { version = "0.7", features = ["v4"] }
|
||||||
|
log = "0.4.0"
|
||||||
|
|
||||||
|
|
||||||
libimagstore = { version = "0.10.0", path = "../../../lib/core/libimagstore" }
|
libimagstore = { version = "0.10.0", path = "../../../lib/core/libimagstore" }
|
||||||
libimagerror = { version = "0.10.0", path = "../../../lib/core/libimagerror" }
|
libimagerror = { version = "0.10.0", path = "../../../lib/core/libimagerror" }
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
use toml::Value;
|
use toml::Value;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
use libimagstore::store::Entry;
|
use libimagstore::store::Entry;
|
||||||
use libimagstore::store::FileLockEntry;
|
use libimagstore::store::FileLockEntry;
|
||||||
|
@ -28,7 +29,6 @@ use libimagentrylink::internal::InternalLinker;
|
||||||
use libimagentryutil::isa::Is;
|
use libimagentryutil::isa::Is;
|
||||||
use libimagentryutil::isa::IsKindHeaderPathProvider;
|
use libimagentryutil::isa::IsKindHeaderPathProvider;
|
||||||
|
|
||||||
use toml_query::read::TomlValueReadTypeExt;
|
|
||||||
use toml_query::insert::TomlValueInsertExt;
|
use toml_query::insert::TomlValueInsertExt;
|
||||||
|
|
||||||
use failure::Fallible as Result;
|
use failure::Fallible as Result;
|
||||||
|
@ -36,12 +36,12 @@ use failure::ResultExt;
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use failure::err_msg;
|
use failure::err_msg;
|
||||||
|
|
||||||
use iter::*;
|
use module_path::ModuleEntryPath;
|
||||||
|
|
||||||
pub trait Annotateable {
|
pub trait Annotateable {
|
||||||
fn annotate<'a>(&mut self, store: &'a Store, ann_name: &str) -> Result<FileLockEntry<'a>>;
|
fn annotate<'a>(&mut self, store: &'a Store) -> Result<FileLockEntry<'a>>;
|
||||||
fn denotate<'a>(&mut self, store: &'a Store, ann_name: &str) -> Result<Option<FileLockEntry<'a>>>;
|
fn denotate<'a>(&mut self, store: &'a Store, ann_name: &str) -> Result<Option<FileLockEntry<'a>>>;
|
||||||
fn annotations<'a>(&self, store: &'a Store) -> Result<AnnotationIter<'a>>;
|
fn annotations(&self) -> Result<StoreIdIterator>;
|
||||||
fn is_annotation(&self) -> Result<bool>;
|
fn is_annotation(&self) -> Result<bool>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,9 +50,11 @@ provide_kindflag_path!(IsAnnotation, "annotation.is_annotation");
|
||||||
impl Annotateable for Entry {
|
impl Annotateable for Entry {
|
||||||
|
|
||||||
/// Annotate an entry, returns the new entry which is used to annotate
|
/// Annotate an entry, returns the new entry which is used to annotate
|
||||||
fn annotate<'a>(&mut self, store: &'a Store, ann_name: &str) -> Result<FileLockEntry<'a>> {
|
fn annotate<'a>(&mut self, store: &'a Store) -> Result<FileLockEntry<'a>> {
|
||||||
use module_path::ModuleEntryPath;
|
let ann_name = Uuid::new_v4().to_hyphenated().to_string();
|
||||||
store.retrieve(ModuleEntryPath::new(ann_name).into_storeid()?)
|
debug!("Creating annotation with name = {}", ann_name);
|
||||||
|
|
||||||
|
store.retrieve(ModuleEntryPath::new(ann_name.clone()).into_storeid()?)
|
||||||
.and_then(|mut anno| {
|
.and_then(|mut anno| {
|
||||||
{
|
{
|
||||||
let _ = anno.set_isflag::<IsAnnotation>()?;
|
let _ = anno.set_isflag::<IsAnnotation>()?;
|
||||||
|
@ -70,30 +72,28 @@ impl Annotateable for Entry {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks the current entry for all annotations and removes the one where the name is
|
// Removes the annotation `ann_name` from the current entry.
|
||||||
/// `ann_name`, which is then returned
|
// Fails if there's no such annotation entry or if the link to that annotation entry does not
|
||||||
|
// exist.
|
||||||
fn denotate<'a>(&mut self, store: &'a Store, ann_name: &str) -> Result<Option<FileLockEntry<'a>>> {
|
fn denotate<'a>(&mut self, store: &'a Store, ann_name: &str) -> Result<Option<FileLockEntry<'a>>> {
|
||||||
for annotation in self.annotations(store)? {
|
if let Some(mut annotation) = store.get(ModuleEntryPath::new(ann_name).into_storeid()?)? {
|
||||||
let mut anno = annotation?;
|
let _ = self.remove_internal_link(&mut annotation)?;
|
||||||
let name = match anno.get_header().read_string("annotation.name")? {
|
Ok(Some(annotation))
|
||||||
Some(ref name) => name.clone(),
|
} else {
|
||||||
None => continue,
|
// error: annotation does not exist
|
||||||
};
|
Err(format_err!("Annotation '{}' does not exist", ann_name)).map_err(Error::from)
|
||||||
|
|
||||||
if name == ann_name {
|
|
||||||
let _ = self.remove_internal_link(&mut anno)?;
|
|
||||||
return Ok(Some(anno));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get all annotations of an entry
|
/// Get all annotations of an entry
|
||||||
fn annotations<'a>(&self, store: &'a Store) -> Result<AnnotationIter<'a>> {
|
fn annotations(&self) -> Result<StoreIdIterator> {
|
||||||
self.get_internal_links()
|
self.get_internal_links()
|
||||||
.map(|iter| StoreIdIterator::new(Box::new(iter.map(|e| e.get_store_id().clone()).map(Ok))))
|
.map(|it| {
|
||||||
.map(|i| AnnotationIter::new(i, store))
|
it.filter(|link| link.get_store_id().is_in_collection(&["annotation"]))
|
||||||
|
.map(|link| Ok(link.get_store_id().clone()))
|
||||||
|
})
|
||||||
|
.map(Box::new)
|
||||||
|
.map(|inner| StoreIdIterator::new(inner))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_annotation(&self) -> Result<bool> {
|
fn is_annotation(&self) -> Result<bool> {
|
||||||
|
|
|
@ -18,21 +18,17 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
use libimagstore::store::Store;
|
use libimagstore::store::Store;
|
||||||
|
use libimagstore::storeid::StoreIdIterator;
|
||||||
|
|
||||||
use failure::Fallible as Result;
|
use failure::Fallible as Result;
|
||||||
use iter::*;
|
|
||||||
|
|
||||||
pub trait AnnotationFetcher<'a> {
|
|
||||||
|
|
||||||
fn all_annotations(&'a self) -> Result<AnnotationIter<'a>>;
|
|
||||||
|
|
||||||
|
pub trait AnnotationFetcher {
|
||||||
|
fn all_annotations(&self) -> Result<StoreIdIterator>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> AnnotationFetcher<'a> for Store {
|
impl<'a> AnnotationFetcher for Store {
|
||||||
|
fn all_annotations(&self) -> Result<StoreIdIterator> {
|
||||||
fn all_annotations(&'a self) -> Result<AnnotationIter<'a>> {
|
self.entries().map(|iter| iter.in_collection("annotation").without_store())
|
||||||
Ok(AnnotationIter::new(self.entries()?.without_store(), self))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
//
|
|
||||||
// imag - the personal information management suite for the commandline
|
|
||||||
// Copyright (C) 2015-2019 Matthias Beyer <mail@beyermatthias.de> and contributors
|
|
||||||
//
|
|
||||||
// This library is free software; you can redistribute it and/or
|
|
||||||
// modify it under the terms of the GNU Lesser General Public
|
|
||||||
// License as published by the Free Software Foundation; version
|
|
||||||
// 2.1 of the License.
|
|
||||||
//
|
|
||||||
// This library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
// Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public
|
|
||||||
// License along with this library; if not, write to the Free Software
|
|
||||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
//
|
|
||||||
|
|
||||||
use toml_query::read::TomlValueReadTypeExt;
|
|
||||||
|
|
||||||
use libimagstore::store::Store;
|
|
||||||
use libimagstore::store::FileLockEntry;
|
|
||||||
use libimagstore::storeid::StoreIdIterator;
|
|
||||||
use libimagerror::errors::ErrorMsg as EM;
|
|
||||||
|
|
||||||
use failure::Fallible as Result;
|
|
||||||
use failure::ResultExt;
|
|
||||||
use failure::Error;
|
|
||||||
use failure::err_msg;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct AnnotationIter<'a>(StoreIdIterator, &'a Store);
|
|
||||||
|
|
||||||
impl<'a> AnnotationIter<'a> {
|
|
||||||
|
|
||||||
pub fn new(iter: StoreIdIterator, store: &'a Store) -> AnnotationIter<'a> {
|
|
||||||
AnnotationIter(iter, store)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iterator for AnnotationIter<'a> {
|
|
||||||
type Item = Result<FileLockEntry<'a>>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
loop {
|
|
||||||
match self.0.next() {
|
|
||||||
None => return None, // iterator consumed
|
|
||||||
Some(Err(e)) => return Some(Err(e).map_err(Error::from)),
|
|
||||||
Some(Ok(id)) => match self.1.get(id) {
|
|
||||||
Err(e) => {
|
|
||||||
return Some(Err(e)
|
|
||||||
.context(err_msg("Store read error"))
|
|
||||||
.map_err(Error::from))
|
|
||||||
},
|
|
||||||
Ok(Some(entry)) => {
|
|
||||||
match entry
|
|
||||||
.get_header()
|
|
||||||
.read_bool("annotation.is_annotation")
|
|
||||||
.context(EM::EntryHeaderReadError)
|
|
||||||
.map_err(Error::from)
|
|
||||||
{
|
|
||||||
Ok(None) => continue, // not an annotation
|
|
||||||
Ok(Some(false)) => continue,
|
|
||||||
Ok(Some(true)) => return Some(Ok(entry)),
|
|
||||||
Err(e) => return Some(Err(e)),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Ok(None) => continue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -39,7 +39,9 @@
|
||||||
|
|
||||||
extern crate toml;
|
extern crate toml;
|
||||||
extern crate toml_query;
|
extern crate toml_query;
|
||||||
extern crate failure;
|
#[macro_use] extern crate failure;
|
||||||
|
#[macro_use] extern crate log;
|
||||||
|
extern crate uuid;
|
||||||
|
|
||||||
#[macro_use] extern crate libimagstore;
|
#[macro_use] extern crate libimagstore;
|
||||||
extern crate libimagerror;
|
extern crate libimagerror;
|
||||||
|
@ -50,5 +52,4 @@ module_entry_path_mod!("annotations");
|
||||||
|
|
||||||
pub mod annotateable;
|
pub mod annotateable;
|
||||||
pub mod annotation_fetcher;
|
pub mod annotation_fetcher;
|
||||||
pub mod iter;
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue