Merge pull request #1110 from matthiasbeyer/libimagcontact/init
Libimagcontact/init
This commit is contained in:
commit
6efd0a9450
10 changed files with 470 additions and 0 deletions
|
@ -21,6 +21,7 @@ members = [
|
||||||
"lib/core/libimagrt",
|
"lib/core/libimagrt",
|
||||||
"lib/core/libimagstore",
|
"lib/core/libimagstore",
|
||||||
"lib/domain/libimagbookmark",
|
"lib/domain/libimagbookmark",
|
||||||
|
"lib/domain/libimagcontact",
|
||||||
"lib/domain/libimagdiary",
|
"lib/domain/libimagdiary",
|
||||||
"lib/domain/libimagmail",
|
"lib/domain/libimagmail",
|
||||||
"lib/domain/libimagnotes",
|
"lib/domain/libimagnotes",
|
||||||
|
|
|
@ -1,2 +1,49 @@
|
||||||
## libimagcontacts
|
## libimagcontacts
|
||||||
|
|
||||||
|
The contact library basically only creates references to the actual icalendar
|
||||||
|
and vcard files, though it also can parse (via the `vobject` crate) the
|
||||||
|
information and return it from an entry directly.
|
||||||
|
|
||||||
|
The architecture of indirections is as follows:
|
||||||
|
|
||||||
|
```{.numberLines}
|
||||||
|
|
||||||
|
+--------------------------------+
|
||||||
|
| |
|
||||||
|
| Store, as ContactStore |
|
||||||
|
| |
|
||||||
|
+----------------+---------------+
|
||||||
|
|
|
||||||
|
| Provides access to
|
||||||
|
|
|
||||||
|
+----------------v---------------+
|
||||||
|
| |
|
||||||
|
| (FileLock)Entry as Contact |
|
||||||
|
| |
|
||||||
|
| which is actually a: |
|
||||||
|
| |
|
||||||
|
| (FileLock)Entry as Ref |
|
||||||
|
| |
|
||||||
|
+----------------+---------------+
|
||||||
|
|
|
||||||
|
| refers to
|
||||||
|
|
|
||||||
|
+----------------v---------------+
|
||||||
|
| |
|
||||||
|
| vcard file (outside store) |
|
||||||
|
| |
|
||||||
|
+----------------+---------------+
|
||||||
|
|
|
||||||
|
| contains
|
||||||
|
|
|
||||||
|
+----------------v---------------+
|
||||||
|
| |
|
||||||
|
| vcard data |
|
||||||
|
| |
|
||||||
|
+--------------------------------+
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
As the library is build upon `libimagentryref`, it does not create a new
|
||||||
|
subcollection in the store `/contacts`, but uses the infrastructure of
|
||||||
|
`libimagentryref` which automatically puts all references in `/ref`.
|
||||||
|
|
26
lib/domain/libimagcontact/Cargo.toml
Normal file
26
lib/domain/libimagcontact/Cargo.toml
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
[package]
|
||||||
|
name = "libimagcontact"
|
||||||
|
version = "0.5.0"
|
||||||
|
authors = ["Matthias Beyer <mail@beyermatthias.de>"]
|
||||||
|
|
||||||
|
description = "Library for the imag core distribution"
|
||||||
|
|
||||||
|
keywords = ["imag", "PIM", "personal", "information", "management"]
|
||||||
|
readme = "../../../README.md"
|
||||||
|
license = "LGPL-2.1"
|
||||||
|
|
||||||
|
documentation = "https://matthiasbeyer.github.io/imag/imag_documentation/index.html"
|
||||||
|
repository = "https://github.com/matthiasbeyer/imag"
|
||||||
|
homepage = "http://imag-pim.org"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
error-chain = "0.11"
|
||||||
|
log = "0.3"
|
||||||
|
toml = "0.4"
|
||||||
|
toml-query = "0.4"
|
||||||
|
vobject = { git = 'https://github.com/matthiasbeyer/rust-vobject', branch = "next" }
|
||||||
|
|
||||||
|
libimagstore = { version = "0.5.0", path = "../../../lib/core/libimagstore" }
|
||||||
|
libimagerror = { version = "0.5.0", path = "../../../lib/core/libimagerror" }
|
||||||
|
libimagentryref = { version = "0.5.0", path = "../../../lib/entry/libimagentryref/" }
|
||||||
|
|
1
lib/domain/libimagcontact/README.md
Symbolic link
1
lib/domain/libimagcontact/README.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../../doc/src/05100-lib-contacts.md
|
90
lib/domain/libimagcontact/src/contact.rs
Normal file
90
lib/domain/libimagcontact/src/contact.rs
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 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 std::ops::Deref;
|
||||||
|
|
||||||
|
use vobject::Component;
|
||||||
|
use toml::Value;
|
||||||
|
use toml_query::read::TomlValueReadExt;
|
||||||
|
|
||||||
|
use libimagstore::store::Entry;
|
||||||
|
use libimagentryref::reference::Ref;
|
||||||
|
|
||||||
|
use error::ContactError as CE;
|
||||||
|
use error::ContactErrorKind as CEK;
|
||||||
|
use error::Result;
|
||||||
|
use util;
|
||||||
|
|
||||||
|
/// Trait to be implemented on ::libimagstore::store::Entry
|
||||||
|
///
|
||||||
|
/// Based on the functionality from libimagentryref, for fetching the Ical data from disk
|
||||||
|
pub trait Contact : Ref {
|
||||||
|
|
||||||
|
fn is_contact(&self) -> Result<bool>;
|
||||||
|
|
||||||
|
// getting data
|
||||||
|
|
||||||
|
fn get_contact_data(&self) -> Result<ContactData>;
|
||||||
|
|
||||||
|
// More convenience functionality may follow
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Contact for Entry {
|
||||||
|
|
||||||
|
fn is_contact(&self) -> Result<bool> {
|
||||||
|
let location = "contact.marker";
|
||||||
|
match self.get_header().read(location)? {
|
||||||
|
Some(&Value::Boolean(b)) => Ok(b),
|
||||||
|
Some(_) => Err(CE::from_kind(CEK::HeaderTypeError("boolean", location))),
|
||||||
|
None => Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_contact_data(&self) -> Result<ContactData> {
|
||||||
|
let component = self
|
||||||
|
.fs_file()
|
||||||
|
.map_err(From::from)
|
||||||
|
.and_then(util::read_to_string)
|
||||||
|
.and_then(util::parse)?;
|
||||||
|
|
||||||
|
Ok(ContactData(component))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ContactData(Component);
|
||||||
|
|
||||||
|
impl ContactData {
|
||||||
|
|
||||||
|
pub fn into_inner(self) -> Component {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for ContactData {
|
||||||
|
type Target = Component;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
52
lib/domain/libimagcontact/src/error.rs
Normal file
52
lib/domain/libimagcontact/src/error.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 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 libimagstore::storeid::StoreId;
|
||||||
|
|
||||||
|
error_chain! {
|
||||||
|
types {
|
||||||
|
ContactError, ContactErrorKind, ResultExt, Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
links {
|
||||||
|
StoreError(::libimagstore::error::StoreError, ::libimagstore::error::StoreErrorKind);
|
||||||
|
RefError(::libimagentryref::error::RefError, ::libimagentryref::error::RefErrorKind);
|
||||||
|
VObjectError(::vobject::error::VObjectError, ::vobject::error::VObjectErrorKind);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreign_links {
|
||||||
|
Io(::std::io::Error);
|
||||||
|
TomlQueryError(::toml_query::error::Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
errors {
|
||||||
|
|
||||||
|
HeaderTypeError(ty: &'static str, loc: &'static str) {
|
||||||
|
description("Type error in header")
|
||||||
|
display("Type error in header, expected {} at '{}', found other type", ty, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
EntryNotFound(sid: StoreId) {
|
||||||
|
description("Entry not found with StoreId")
|
||||||
|
display("Entry {:?} not found", sid)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
69
lib/domain/libimagcontact/src/iter.rs
Normal file
69
lib/domain/libimagcontact/src/iter.rs
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 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 libimagstore::storeid::StoreIdIterator;
|
||||||
|
use libimagstore::store::Store;
|
||||||
|
use libimagstore::store::FileLockEntry;
|
||||||
|
|
||||||
|
use contact::Contact;
|
||||||
|
use error::ContactError as CE;
|
||||||
|
use error::ContactErrorKind as CEK;
|
||||||
|
use error::Result;
|
||||||
|
|
||||||
|
pub struct ContactIter<'a>(StoreIdIterator, &'a Store);
|
||||||
|
|
||||||
|
/// Iterator over contacts
|
||||||
|
///
|
||||||
|
/// As the libimagcontact works with libimagentryref in the backend, we must hold a reference to the
|
||||||
|
/// Store here as well, so we can check whether a fetched StoreId actually points to a contact
|
||||||
|
/// reference or not.
|
||||||
|
///
|
||||||
|
/// So, the Iterator `Store::get()`s the object pointed to by the StoreId and returns it if
|
||||||
|
/// everything worked.
|
||||||
|
impl<'a> ContactIter<'a> {
|
||||||
|
|
||||||
|
pub fn new(sii: StoreIdIterator, store: &'a Store) -> ContactIter<'a> {
|
||||||
|
ContactIter(sii, store)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for ContactIter<'a> {
|
||||||
|
type Item = Result<FileLockEntry<'a>>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
loop {
|
||||||
|
match self.0.next() {
|
||||||
|
None => return None,
|
||||||
|
Some(sid) => match self.1.get(sid.clone()).map_err(From::from) {
|
||||||
|
Err(e) => return Some(Err(e)),
|
||||||
|
Ok(None) => return Some(Err(CE::from_kind(CEK::EntryNotFound(sid)))),
|
||||||
|
Ok(Some(entry)) => match entry.is_contact().map_err(From::from) {
|
||||||
|
Ok(true) => return Some(Ok(entry)),
|
||||||
|
Ok(false) => continue,
|
||||||
|
Err(e) => return Some(Err(e)),
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
53
lib/domain/libimagcontact/src/lib.rs
Normal file
53
lib/domain/libimagcontact/src/lib.rs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#![deny(
|
||||||
|
dead_code,
|
||||||
|
non_camel_case_types,
|
||||||
|
non_snake_case,
|
||||||
|
path_statements,
|
||||||
|
trivial_numeric_casts,
|
||||||
|
unstable_features,
|
||||||
|
unused_allocation,
|
||||||
|
unused_import_braces,
|
||||||
|
unused_imports,
|
||||||
|
unused_must_use,
|
||||||
|
unused_mut,
|
||||||
|
unused_qualifications,
|
||||||
|
while_true,
|
||||||
|
)]
|
||||||
|
|
||||||
|
#[macro_use] extern crate log;
|
||||||
|
#[macro_use] extern crate error_chain;
|
||||||
|
extern crate vobject;
|
||||||
|
extern crate toml;
|
||||||
|
extern crate toml_query;
|
||||||
|
|
||||||
|
#[macro_use] extern crate libimagstore;
|
||||||
|
extern crate libimagerror;
|
||||||
|
extern crate libimagentryref;
|
||||||
|
|
||||||
|
module_entry_path_mod!("contact");
|
||||||
|
|
||||||
|
pub mod contact;
|
||||||
|
pub mod error;
|
||||||
|
pub mod iter;
|
||||||
|
pub mod store;
|
||||||
|
mod util;
|
||||||
|
|
86
lib/domain/libimagcontact/src/store.rs
Normal file
86
lib/domain/libimagcontact/src/store.rs
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 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 std::path::PathBuf;
|
||||||
|
|
||||||
|
use vobject::parse_component;
|
||||||
|
use toml::Value;
|
||||||
|
use toml_query::insert::TomlValueInsertExt;
|
||||||
|
|
||||||
|
use libimagstore::store::Store;
|
||||||
|
use libimagstore::store::FileLockEntry;
|
||||||
|
use libimagstore::storeid::StoreIdIterator;
|
||||||
|
use libimagentryref::refstore::RefStore;
|
||||||
|
use libimagentryref::flags::RefFlags;
|
||||||
|
|
||||||
|
use error::Result;
|
||||||
|
use util;
|
||||||
|
|
||||||
|
pub trait ContactStore<'a> : RefStore {
|
||||||
|
|
||||||
|
// creating
|
||||||
|
|
||||||
|
fn create_from_path(&'a self, p: &PathBuf) -> Result<FileLockEntry<'a>>;
|
||||||
|
|
||||||
|
/// Create contact ref from buffer
|
||||||
|
///
|
||||||
|
/// Needs the `p` argument as we're finally creating a reference by path, the buffer is only for
|
||||||
|
/// collecting metadata.
|
||||||
|
fn create_from_buf(&'a self, p: &PathBuf, buf: &String) -> Result<FileLockEntry<'a>>;
|
||||||
|
|
||||||
|
// getting
|
||||||
|
|
||||||
|
fn all_contacts(&'a self) -> Result<StoreIdIterator>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The extension for the Store to work with contacts
|
||||||
|
///
|
||||||
|
/// The contact functionality is implemented by using the `libimagentryref` library, so basically
|
||||||
|
/// we only reference vcard files from outside the store.
|
||||||
|
///
|
||||||
|
/// Because of this, we do not have an own store collection `/contacts` or something like that, but
|
||||||
|
/// must stress the `libimagentryref` API for everything.
|
||||||
|
impl<'a> ContactStore<'a> for Store {
|
||||||
|
|
||||||
|
fn create_from_path(&'a self, p: &PathBuf) -> Result<FileLockEntry<'a>> {
|
||||||
|
util::read_to_string(p).and_then(|buf| self.create_from_buf(p, &buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create contact ref from buffer
|
||||||
|
fn create_from_buf(&'a self, p: &PathBuf, buf: &String) -> Result<FileLockEntry<'a>> {
|
||||||
|
let component = parse_component(&buf)?;
|
||||||
|
debug!("Parsed: {:?}", component);
|
||||||
|
|
||||||
|
let flags = RefFlags::default().with_content_hashing(true).with_permission_tracking(false);
|
||||||
|
RefStore::create(self, p.clone(), flags)
|
||||||
|
.map_err(From::from)
|
||||||
|
.and_then(|mut entry| {
|
||||||
|
entry.get_header_mut()
|
||||||
|
.insert("contact.marker", Value::Boolean(true))
|
||||||
|
.map_err(From::from)
|
||||||
|
.map(|_| entry)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn all_contacts(&'a self) -> Result<StoreIdIterator> {
|
||||||
|
self.all_references().map_err(From::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
45
lib/domain/libimagcontact/src/util.rs
Normal file
45
lib/domain/libimagcontact/src/util.rs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
//
|
||||||
|
// imag - the personal information management suite for the commandline
|
||||||
|
// Copyright (C) 2015, 2016 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 std::path::Path;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
use error::Result;
|
||||||
|
|
||||||
|
use vobject::Component;
|
||||||
|
|
||||||
|
pub fn read_to_string<A: AsRef<Path> + Debug>(pb: A) -> Result<String> {
|
||||||
|
let mut cont = String::new();
|
||||||
|
|
||||||
|
let mut file = File::open(pb.as_ref())?;
|
||||||
|
let bytes = file.read_to_string(&mut cont)?;
|
||||||
|
|
||||||
|
debug!("Read {} bytes from {:?}", bytes, pb);
|
||||||
|
|
||||||
|
Ok(cont)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper for chaining results nicely
|
||||||
|
pub fn parse(buf: String) -> Result<Component> {
|
||||||
|
use vobject::parse_component;
|
||||||
|
parse_component(&buf).map_err(From::from)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue