Commit 15606361 authored by Leonardo Schwarz's avatar Leonardo Schwarz

Update dependencies (notably xpath_reader 0.5.2)

parent 2c2095bb
......@@ -2,6 +2,6 @@ target
/replay/*
!/replay/test_entities
Cargo.lock
/.idea
# rustfmt backup files
*.rs.bk
......@@ -10,14 +10,14 @@ default = []
[dependencies]
error-chain = "0.11"
isolang = "0.2"
regex = "0.2.1"
isolang = "1"
regex = "1"
reqwest_mock = "0.5"
rusqlite = { version = "0.12.0", optional = true }
url = "1.4.0"
uuid = { version = "0.6", features = ["use_std"] }
xpath_reader = "0.4"
uuid = { version = "0.7" }
xpath_reader = "0.5"
[dev-dependencies]
pretty_env_logger = "0.2"
pretty_env_logger = "0.3"
log = "0.4"
use super::*;
use xpath_reader::XpathReader;
use xpath_reader::Reader;
/// Checks if there is an error in the document provided by the reader and
/// returns Ok if there
/// wasn't and Err parsing the MusicBrainz error if the API actually returned
/// an error.
pub fn check_response_error<'d, R>(reader: &'d R) -> Result<(), ClientError>
where
R: XpathReader<'d>,
{
match reader.read_vec::<String>("//error/text") {
pub fn check_response_error<'d>(reader: &'d Reader<'d>) -> Result<(), ClientError> {
let r: Result<Vec<String>, _> = reader.read("//error/text");
match r {
Ok(errs) => {
if errs.len() > 0 {
let text = errs.join("\n");
......@@ -26,7 +25,7 @@ where
mod tests {
use super::*;
use error_chain::ChainedError;
use xpath_reader::XpathStrReader;
use xpath_reader::Reader;
const XML_ERR: &'static str = r#"<?xml version="1.0" encoding="UTF-8"?>
<error>
......@@ -37,10 +36,9 @@ mod tests {
const XML_OK: &'static str = r#"<?xml version="1.0" encoding="UTF-8"?><metadata xmlns="http://musicbrainz.org/ns/mmd-2.0#"><release-group type="Album" type-id="f529b476-6e62-324f-b0aa-1f3e33d313fc" id="23c74936-ad4f-45bb-8b6b-527d4aeaaad6"><title>A.I Complex</title><first-release-date>2014-07-26</first-release-date><primary-type id="f529b476-6e62-324f-b0aa-1f3e33d313fc">Album</primary-type></release-group></metadata>"#;
#[test]
fn error()
{
fn error() {
let context = ::util::musicbrainz_context();
let reader = XpathStrReader::new(XML_ERR, &context).unwrap();
let reader = Reader::from_str(XML_ERR, Some(&context)).unwrap();
let res = check_response_error(&reader);
let err = res.err().unwrap();
......@@ -51,10 +49,9 @@ mod tests {
}
#[test]
fn ok()
{
fn ok() {
let context = ::util::musicbrainz_context();
let reader = XpathStrReader::new(XML_OK, &context).unwrap();
let reader = Reader::from_str(XML_OK, Some(&context)).unwrap();
// should not raise error
check_response_error(&reader).unwrap();
......
......@@ -7,7 +7,7 @@ use reqwest_mock::Client as MockClient;
use reqwest_mock::GenericClient as HttpClient;
use reqwest_mock::{StatusCode, Url};
use reqwest_mock::header::UserAgent;
use xpath_reader::reader::{FromXmlContained, XpathStrReader};
use xpath_reader::reader::{FromXml, Reader};
use std::time::{Duration, Instant};
use std::thread::sleep;
......@@ -18,14 +18,12 @@ mod error;
pub(crate) use self::error::check_response_error;
/// Helper extracting the number of milliseconds from a `Duration`.
fn as_millis(duration: &Duration) -> u64
{
fn as_millis(duration: &Duration) -> u64 {
((duration.as_secs() as f64) + (duration.subsec_nanos() as f64) * 1e6) as u64
}
/// Returns an `Instant` at least 1000 seconds ago.
fn past_instant() -> Instant
{
fn past_instant() -> Instant {
Instant::now() - Duration::new(1000, 0)
}
......@@ -70,8 +68,7 @@ pub struct ClientWaits {
}
impl Default for ClientWaits {
fn default() -> Self
{
fn default() -> Self {
ClientWaits {
backoff_init: 400,
requests: 1000,
......@@ -97,8 +94,7 @@ pub struct Client {
impl Client {
/// Create a new `Client` instance.
pub fn new(config: ClientConfig) -> Self
{
pub fn new(config: ClientConfig) -> Self {
Client {
config: config,
http_client: HttpClient::direct(),
......@@ -111,8 +107,7 @@ impl Client {
/// This is useful for testing purposes where you can inject a different
/// `HttpClient`, i. e. one replaying requests to save API calls or one
/// providing explicit stubbing.
pub fn with_http_client(config: ClientConfig, client: HttpClient) -> Self
{
pub fn with_http_client(config: ClientConfig, client: HttpClient) -> Self {
Client {
config: config,
http_client: client,
......@@ -124,8 +119,7 @@ impl Client {
impl Client {
/// Waits until we are allowed to make the next request to the MusicBrainz
/// API.
fn wait_if_needed(&mut self)
{
fn wait_if_needed(&mut self) {
let now = Instant::now();
let elapsed = now.duration_since(self.last_request);
if as_millis(&elapsed) < self.config.waits.requests {
......@@ -137,27 +131,27 @@ impl Client {
/// Fetch the specified ressource from the server and parse it.
pub fn get_by_mbid<Res>(&mut self, mbid: &Mbid) -> Result<Res, ClientError>
where
Res: Resource + FromXmlContained,
Res: Resource + FromXml,
{
let url = Res::get_url(mbid);
let response_body = self.get_body(url.parse()?)?;
// Parse the response.
let context = ::util::musicbrainz_context();
let reader = XpathStrReader::new(&response_body[..], &context)?;
let reader = Reader::from_str(&response_body[..], Some(&context))?;
check_response_error(&reader)?;
Ok(Res::from_xml(&reader)?)
}
pub(crate) fn get_body(&mut self, url: Url) -> Result<String, ClientError>
{
pub(crate) fn get_body(&mut self, url: Url) -> Result<String, ClientError> {
self.wait_if_needed();
let mut attempts = 0;
let mut backoff = self.config.waits.backoff_init;
while attempts < self.config.max_retries {
let response = self.http_client
let response = self
.http_client
.get(url.clone())
.header(UserAgent::new(self.config.user_agent.clone()))
.send()?;
......@@ -172,26 +166,21 @@ impl Client {
return Ok(response_body);
}
}
Err(
"MusicBrainz returned 503 (ServiceUnavailable) too many times.".into(),
)
Err("MusicBrainz returned 503 (ServiceUnavailable) too many times.".into())
}
/// Returns a search builder to search for an area.
pub fn search_area<'cl>(&'cl mut self) -> AreaSearchBuilder<'cl>
{
pub fn search_area<'cl>(&'cl mut self) -> AreaSearchBuilder<'cl> {
AreaSearchBuilder::new(self)
}
/// Returns a search biulder to search for an artist.
pub fn search_artist<'cl>(&'cl mut self) -> ArtistSearchBuilder<'cl>
{
pub fn search_artist<'cl>(&'cl mut self) -> ArtistSearchBuilder<'cl> {
ArtistSearchBuilder::new(self)
}
/// Returns a search builder to search for a release group.
pub fn search_release_group<'cl>(&'cl mut self) -> ReleaseGroupSearchBuilder<'cl>
{
pub fn search_release_group<'cl>(&'cl mut self) -> ReleaseGroupSearchBuilder<'cl> {
ReleaseGroupSearchBuilder::new(self)
}
}
......@@ -200,8 +189,7 @@ impl Client {
mod tests {
use super::*;
fn get_client(testname: &str) -> Client
{
fn get_client(testname: &str) -> Client {
Client::with_http_client(
ClientConfig {
user_agent: "MusicBrainz-Rust/Testing".to_string(),
......@@ -213,8 +201,7 @@ mod tests {
}
#[test]
fn search_release_group()
{
fn search_release_group() {
let mut client = get_client("release_group_01");
let results = client
.search_release_group()
......
use xpath_reader::{FromXml, FromXmlError, XpathReader};
use xpath_reader::reader::{FromXmlContained, FromXmlElement};
use xpath_reader::{FromXml, Error, Reader};
use entities::{Mbid, Resource};
enum_mb_xml!{
enum_mb_xml! {
/// Specifies what a specific `Area` instance actually is.
pub enum AreaType {
/// Areas included (or previously included) in ISO 3166-1.
......@@ -59,20 +58,15 @@ pub struct Area {
pub iso_3166: Option<String>,
}
impl FromXmlContained for Area {}
impl FromXml for Area {
fn from_xml<'d, R>(reader: &'d R) -> Result<Area, FromXmlError>
where
R: XpathReader<'d>,
{
fn from_xml<'d>(reader: &'d Reader<'d>) -> Result<Area, Error> {
Ok(Area {
mbid: reader.read(".//mb:area/@id")?,
name: reader.read(".//mb:area/mb:name/text()")?,
sort_name: reader.read(".//mb:area/mb:sort-name/text()")?,
area_type: reader.read(".//mb:area/@type")?,
iso_3166: reader.read_option(
".//mb:area/mb:iso-3166-1-code-list/mb:iso-3166-1-code/text()",
)?,
iso_3166: reader
.read(".//mb:area/mb:iso-3166-1-code-list/mb:iso-3166-1-code/text()")?,
})
}
}
......@@ -88,8 +82,7 @@ mod tests {
use std::str::FromStr;
#[test]
fn area_read_xml1()
{
fn area_read_xml1() {
let mbid = Mbid::from_str("a1411661-be21-4290-8dc1-50f3d8e3ea67").unwrap();
let area: Area = ::util::test_utils::fetch_entity(&mbid).unwrap();
......@@ -101,8 +94,7 @@ mod tests {
}
#[test]
fn area_read_xml2()
{
fn area_read_xml2() {
let mbid = Mbid::from_str("2db42837-c832-3c27-b4a3-08198f75693c").unwrap();
let area: Area = ::util::test_utils::fetch_entity(&mbid).unwrap();
......
use xpath_reader::{FromXml, FromXmlError, XpathReader};
use xpath_reader::reader::{FromXmlContained, FromXmlElement};
use xpath_reader::{FromXml, FromXmlOptional, Error, Reader};
use entities::{Mbid, Resource};
use entities::date::PartialDate;
use entities::refs::AreaRef;
enum_mb_xml! {
enum_mb_xml_optional! {
/// Specification of the gender of an artist.
pub enum Gender {
var Female = "Female",
......@@ -14,7 +13,7 @@ enum_mb_xml! {
}
}
enum_mb_xml! {
enum_mb_xml_optional! {
/// Specifies what an `Artist` instance actually is.
pub enum ArtistType {
var Person = "Person",
......@@ -90,23 +89,19 @@ pub struct Artist {
pub isni_code: Option<String>,
}
impl FromXmlContained for Artist {}
impl FromXml for Artist {
fn from_xml<'d, R>(reader: &'d R) -> Result<Self, FromXmlError>
where
R: XpathReader<'d>,
{
fn from_xml<'d>(reader: &'d Reader<'d>) -> Result<Self, Error> {
Ok(Artist {
aliases: reader.read_vec(".//mb:artist/mb:alias-list/mb:alias/text()")?,
annotation: reader.read_option(".//mb:artist/mb:annotation/text()")?,
area: reader.read_option(".//mb:artist/mb:area")?,
artist_type: reader.read_option(".//mb:artist/@type")?,
begin_date: reader.read_option(".//mb:artist/mb:life-span/mb:begin/text()")?,
disambiguation: reader.read_option(".//mb:artist/mb:disambiguation/text()")?,
end_date: reader.read_option(".//mb:artist/mb:life-span/mb:end/text()")?,
gender: reader.read_option(".//mb:artist/mb:gender/text()")?,
ipi_code: reader.read_option(".//mb:artist/mb:ipi/text()")?,
isni_code: reader.read_option(".//mb:artist/mb:isni-list/mb:isni/text()")?,
aliases: reader.read(".//mb:artist/mb:alias-list/mb:alias/text()")?,
annotation: reader.read(".//mb:artist/mb:annotation/text()")?,
area: reader.read(".//mb:artist/mb:area")?,
artist_type: reader.read(".//mb:artist/@type")?,
begin_date: reader.read(".//mb:artist/mb:life-span/mb:begin/text()")?,
disambiguation: reader.read(".//mb:artist/mb:disambiguation/text()")?,
end_date: reader.read(".//mb:artist/mb:life-span/mb:end/text()")?,
gender: reader.read(".//mb:artist/mb:gender/text()")?,
ipi_code: reader.read(".//mb:artist/mb:ipi/text()")?,
isni_code: reader.read(".//mb:artist/mb:isni-list/mb:isni/text()")?,
mbid: reader.read(".//mb:artist/@id")?,
name: reader.read(".//mb:artist/mb:name/text()")?,
sort_name: reader.read(".//mb:artist/mb:sort-name/text()")?,
......@@ -125,9 +120,7 @@ mod tests {
use std::str::FromStr;
#[test]
fn artist_read_xml1()
{
fn artist_read_xml1() {
let mbid = Mbid::from_str("90e7c2f9-273b-4d6c-a662-ab2d73ea4b8e").unwrap();
let artist: Artist = ::util::test_utils::fetch_entity(&mbid).unwrap();
......@@ -158,8 +151,7 @@ mod tests {
}
#[test]
fn artist_read_xml2()
{
fn artist_read_xml2() {
let mbid = Mbid::from_str("650e7db6-b795-4eb5-a702-5ea2fc46c848").unwrap();
let artist: Artist = ::util::test_utils::fetch_entity(&mbid).unwrap();
......
......@@ -6,7 +6,7 @@ use std::str::FromStr;
use std::num::ParseIntError;
use std::error::Error;
use std::fmt::Display;
use xpath_reader::{FromXml, FromXmlError, XpathReader};
use xpath_reader::{FromXml, FromXmlOptional, Reader};
/// Represents a partial date as it is used across MusicBrainz.
///
......@@ -27,8 +27,7 @@ pub struct FullDate {
}
impl PartialDate {
pub fn new(year: Option<u16>, month: Option<u8>, day: Option<u8>) -> PartialDate
{
pub fn new(year: Option<u16>, month: Option<u8>, day: Option<u8>) -> PartialDate {
PartialDate {
year: year,
month: month,
......@@ -36,18 +35,15 @@ impl PartialDate {
}
}
pub fn year(&self) -> Option<u16>
{
pub fn year(&self) -> Option<u16> {
self.year
}
pub fn month(&self) -> Option<u8>
{
pub fn month(&self) -> Option<u8> {
self.month
}
pub fn day(&self) -> Option<u8>
{
pub fn day(&self) -> Option<u8> {
self.day
}
......@@ -68,8 +64,7 @@ impl PartialDate {
/// Some(FullDate::new(2017, 2, 2))
/// );
/// ```
pub fn full_date(&self) -> Option<FullDate>
{
pub fn full_date(&self) -> Option<FullDate> {
if self.year.is_some() && self.month.is_some() && self.day.is_some() {
Some(FullDate {
year: self.year.unwrap(),
......@@ -83,8 +78,7 @@ impl PartialDate {
}
impl FullDate {
pub fn new(year: u16, month: u8, day: u8) -> FullDate
{
pub fn new(year: u16, month: u8, day: u8) -> FullDate {
FullDate {
year: year,
month: month,
......@@ -92,18 +86,15 @@ impl FullDate {
}
}
pub fn year(&self) -> u16
{
pub fn year(&self) -> u16 {
self.year
}
pub fn month(&self) -> u8
{
pub fn month(&self) -> u8 {
self.month
}
pub fn day(&self) -> u8
{
pub fn day(&self) -> u8 {
self.day
}
}
......@@ -111,14 +102,16 @@ impl FullDate {
impl FromStr for PartialDate {
type Err = ParseDateError;
fn from_str(s: &str) -> Result<Self, Self::Err>
{
fn from_str(s: &str) -> Result<Self, Self::Err> {
// Get the pieces of the date.
let ps: Result<Vec<Option<u16>>, ParseIntError> = s.split("-")
.map(|x| if x == "??" || x == "????" {
Ok(None)
} else {
x.parse().map(|y| Some(y))
let ps: Result<Vec<Option<u16>>, ParseIntError> = s
.split("-")
.map(|x| {
if x == "??" || x == "????" {
Ok(None)
} else {
x.parse().map(|y| Some(y))
}
})
.collect();
......@@ -149,25 +142,36 @@ impl FromStr for PartialDate {
}
impl Display for PartialDate {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error>
{
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
// TODO optimize later (no allocations)
let year = self.year.map(|n| format!("{:04}", n)).unwrap_or_else(|| "????".to_string());
let month = self.month.map(|n| format!("{:02}", n)).unwrap_or_else(|| "??".to_string());
let day = self.day.map(|n| format!("{:02}", n)).unwrap_or_else(|| "??".to_string());
let year = self
.year
.map(|n| format!("{:04}", n))
.unwrap_or_else(|| "????".to_string());
let month = self
.month
.map(|n| format!("{:02}", n))
.unwrap_or_else(|| "??".to_string());
let day = self
.day
.map(|n| format!("{:02}", n))
.unwrap_or_else(|| "??".to_string());
write!(f, "{}-{}-{}", year, month, day)
}
}
impl FromXml for PartialDate {
fn from_xml<'d, R>(reader: &'d R) -> Result<Self, FromXmlError>
where
R: XpathReader<'d>,
{
use xpath_reader::errors::ChainXpathErr;
String::from_xml(reader)?.parse().chain_err(|| "Parse Date error").map_err(
|e| FromXmlError::from(e),
)
impl FromXmlOptional for PartialDate {
fn from_xml_optional<'d>(
reader: &'d Reader<'d>,
) -> Result<Option<Self>, ::xpath_reader::Error> {
let s = Option::<String>::from_xml(reader)?;
if let Some(s) = s {
s.parse()
.map(|d| Some(d))
.map_err(::xpath_reader::Error::custom_err)
} else {
Ok(None)
}
}
}
......@@ -181,8 +185,7 @@ pub enum ParseDateError {
}
impl Error for ParseDateError {
fn description(&self) -> &str
{
fn description(&self) -> &str {
use self::ParseDateError::*;
match *self {
WrongNumberOfComponents(_) => "wrong number of components",
......@@ -194,8 +197,7 @@ impl Error for ParseDateError {
// TODO: Evaluate if this is what we want and if we can use this like this in
// requests.
impl Display for ParseDateError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error>
{
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
use self::ParseDateError::*;
match *self {
WrongNumberOfComponents(n) => {
......@@ -207,8 +209,7 @@ impl Display for ParseDateError {
}
impl From<ParseIntError> for ParseDateError {
fn from(e: ParseIntError) -> Self
{
fn from(e: ParseIntError) -> Self {
ParseDateError::ComponentInvalid(e)
}
}
......
use xpath_reader::{FromXml, FromXmlError, XpathReader};
use xpath_reader::reader::{FromXmlContained, FromXmlElement};
use xpath_reader::{FromXml, FromXmlOptional, Error, Reader};
use entities::{Mbid, Resource};
use entities::date::PartialDate;
enum_mb_xml! {
enum_mb_xml_optional! {
pub enum EventType {
var Concert = "Concert",
var Festival = "Festival",
......@@ -58,22 +57,18 @@ impl Resource for Event {
const INCL: &'static str = "aliases+annotation";
}
impl FromXmlContained for Event {}
impl FromXml for Event {
fn from_xml<'d, R>(reader: &'d R) -> Result<Self, FromXmlError>
where
R: XpathReader<'d>,
{
fn from_xml<'d>(reader: &'d Reader<'d>) -> Result<Self, Error> {
Ok(Event {
mbid: reader.read(".//mb:event/@id")?,
name: reader.read(".//mb:event/mb:name")?,
aliases: reader.read_vec(".//mb:event/mb:alias-list/mb:alias/text()")?,
event_type: reader.read_option(".//mb:event/@type")?,
setlist: reader.read_option(".//mb:event/mb:setlist")?,
aliases: reader.read(".//mb:event/mb:alias-list/mb:alias/text()")?,
event_type: reader.read(".//mb:event/@type")?,
setlist: reader.read(".//mb:event/mb:setlist")?,
begin_date: reader.read(".//mb:event/mb:life-span/mb:begin")?,
end_date: reader.read_option(".//mb:event/mb:life-span/mb:end")?,
disambiguation: reader.read_option(".//mb:event/mb:disambiguation")?,
annotation: reader.read_option(".//mb:event/mb:annotation/mb:text/text()")?,
end_date: reader.read(".//mb:event/mb:life-span/mb:end")?,
disambiguation: reader.read(".//mb:event/mb:disambiguation")?,
annotation: reader.read(".//mb:event/mb:annotation/mb:text/text()")?,
})
}
}
......@@ -84,8 +79,7 @@ mod tests {
use std::str::FromStr;
#[test]
fn read_1()
{
fn read_1() {
let mbid = Mbid::from_str("6e2ab7d5-f340-4c41-99a3-c901733402b4").unwrap();
let event: Event = ::util::test_utils::fetch_entity(&mbid).unwrap();
......@@ -101,8 +95,7 @@ mod tests {
}
#[test]
fn read_2()
{
fn read_2() {
let mbid = Mbid::from_str("9754f4dd-6fad-49b7-8f30-940c9af6b776").unwrap();
let event: Event = ::util::test_utils::fetch_entity(&mbid).unwrap();
......
use std::time::Duration;
use xpath_reader::{FromXmlError, XpathReader};
use xpath_reader::{Reader, FromXml, FromXmlOptional};
/// Note that the requirement of the `var` (variant) token is rather ugly but
/// required,
......@@ -31,18 +31,18 @@ macro_rules! enum_mb_xml
)+
}
impl FromXmlElement for $enum {}
impl FromXml for $enum {
fn from_xml<'d, R>(reader: &'d R) -> Result<Self, FromXmlError>
where
R: XpathReader<'d>,
fn from_xml<'d>(reader: &'d Reader<'d>) -> Result<Self, ::xpath_reader::Error>
{
match String::from_xml(reader)?.as_str() {
$(
$str => Ok($enum::$variant),
)+
"" => Err(FromXmlError::Absent),
s => Err(format!("Unknown `{}` value: '{}'", stringify!($enum), s).into()),
s => Err(
::xpath_reader::Error::custom_msg(
format!("Unknown `{}` value: '{}'", stringify!($enum), s)
)
)
}
}
}
......@@ -61,12 +61,71 @@ macro_rules! enum_mb_xml
}
}
pub fn read_mb_duration<'d, R>(reader: &'d R, path: &str) -> Result<Option<Duration>, FromXmlError>
where
R: XpathReader<'d>,
macro_rules! enum_mb_xml_optional
{
match reader.read_option::<String>(path)? {
Some(millis) => Ok(Some(Duration::from_millis(millis.parse()?))),
(
$(#[$attr:meta])* pub enum $enum:ident {
$(
$(#[$attr2:meta])*
var $variant:ident = $str:expr
),+
,
}
)
=>
{
$(#[$attr])*
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum $enum {
$(
$(#[$attr2])* $variant ,
)+
}
impl FromXmlOptional for $enum {
fn from_xml_optional<'d>(reader: &'d Reader<'d>) -> Result<Option<Self>, ::xpath_reader::Error>
{
let s = Option::<String>::from_xml(reader)?;
if let Some(s) = s {
match s.as_ref() {
$(
$str => Ok(Some($enum::$variant)),
)+
s => Err(
::xpath_reader::Error::custom_msg(
format!("Unknown `{}` value: '{}'", stringify!($enum), s)
)
)
}
} else {
Ok(None)
}
}
}
impl ::std::fmt::Display for $enum {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result
{
let s = match *self {
$(
$enum::$variant => $str,
)+
};
write!(f, "{}", s)
}
}
}
}
pub fn read_mb_duration<'d>(
reader: &'d Reader<'d>,
path: &str,
) -> Result<Option<Duration>, ::xpath_reader::Error> {
let s: Option<String> = reader.read(path)?;
match s {
Some(millis) => Ok(Some(Duration::from_millis(
millis.parse().map_err(::xpath_reader::Error::custom_err)?,
))),
None => Ok(None),
}
}
use xpath_reader::{FromXml, FromXmlError, XpathReader};
use xpath_reader::reader::{FromXmlContained, FromXmlElement};
use xpath_reader::{FromXml, FromXmlOptional, Error, Reader};
use entities::{Mbid, Resource};
use entities::date::PartialDate;
......@@ -62,30 +61,26 @@ impl Resource for Label {
const INCL: &'static str = "aliases";
}
impl FromXmlContained for Label {}