Commit 0391e93a authored by Leonardo Schwarz's avatar Leonardo Schwarz

Package documentation.

parent e86cbca5
{"request":{"url":"https://musicbrainz.org/ws/2/area/2db42837-c832-3c27-b4a3-08198f75693c","method":"GET","body":null,"headers":{"User-Agent":"MusicBrainz-Rust/Testing"}},"response":{"url":"https://musicbrainz.org/ws/2/area/2db42837-c832-3c27-b4a3-08198f75693c","status":200,"headers":{"Access-Control-Allow-Origin":"*","Connection":"keep-alive","Content-Type":"application/xml; charset=utf-8","Date":"Sun, 23 Jul 2017 22:51:55 GMT","ETag":"W/\"e272cfbb60b971b1a9674929765699d1\"","Keep-Alive":"timeout=15","Server":"Plack::Handler::Starlet","Transfer-Encoding":"chunked","Vary":"Accept-Encoding","X-RateLimit-Limit":"1200","X-RateLimit-Remaining":"976","X-RateLimit-Reset":"1500850316"},"body":"PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48bWV0YWRhdGEgeG1sbnM9Imh0dHA6Ly9tdXNpY2JyYWluei5vcmcvbnMvbW1kLTIuMCMiPjxhcmVhIHR5cGU9IkNvdW50cnkiIHR5cGUtaWQ9IjA2ZGQwYWU0LThjNzQtMzBiYi1iNDNkLTk1ZGNlZGY5NjFkZSIgaWQ9IjJkYjQyODM3LWM4MzItM2MyNy1iNGEzLTA4MTk4Zjc1NjkzYyI+PG5hbWU+SmFwYW48L25hbWU+PHNvcnQtbmFtZT5KYXBhbjwvc29ydC1uYW1lPjxpc28tMzE2Ni0xLWNvZGUtbGlzdD48aXNvLTMxNjYtMS1jb2RlPkpQPC9pc28tMzE2Ni0xLWNvZGU+PC9pc28tMzE2Ni0xLWNvZGUtbGlzdD48L2FyZWE+PC9tZXRhZGF0YT4="},"format_version":3}
\ No newline at end of file
{"request":{"url":"https://musicbrainz.org/ws/2/area/a1411661-be21-4290-8dc1-50f3d8e3ea67","method":"GET","body":null,"headers":{"User-Agent":"MusicBrainz-Rust/Testing"}},"response":{"url":"https://musicbrainz.org/ws/2/area/a1411661-be21-4290-8dc1-50f3d8e3ea67","status":200,"headers":{"Access-Control-Allow-Origin":"*","Connection":"keep-alive","Content-Type":"application/xml; charset=utf-8","Date":"Sun, 23 Jul 2017 22:51:55 GMT","ETag":"W/\"f86ed43adb8182283a405ca5f79caa78\"","Keep-Alive":"timeout=15","Server":"Plack::Handler::Starlet","Transfer-Encoding":"chunked","Vary":"Accept-Encoding","X-RateLimit-Limit":"1200","X-RateLimit-Remaining":"968","X-RateLimit-Reset":"1500850316"},"body":"PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48bWV0YWRhdGEgeG1sbnM9Imh0dHA6Ly9tdXNpY2JyYWluei5vcmcvbnMvbW1kLTIuMCMiPjxhcmVhIGlkPSJhMTQxMTY2MS1iZTIxLTQyOTAtOGRjMS01MGYzZDhlM2VhNjciIHR5cGUtaWQ9IjZmZDhmMjlhLTNkMGEtMzJmYy05ODBkLWVhNjk3YjY5ZGE3OCIgdHlwZT0iQ2l0eSI+PG5hbWU+SG9ub2x1bHU8L25hbWU+PHNvcnQtbmFtZT5Ib25vbHVsdTwvc29ydC1uYW1lPjwvYXJlYT48L21ldGFkYXRhPg=="},"format_version":3}
\ No newline at end of file
{"request":{"url":"https://musicbrainz.org/ws/2/area/2db42837-c832-3c27-b4a3-08198f75693c?inc=","method":"GET","body":null,"headers":{"User-Agent":"MusicBrainz-Rust/Testing"}},"response":{"url":"https://musicbrainz.org/ws/2/area/2db42837-c832-3c27-b4a3-08198f75693c?inc=","status":200,"headers":{"Access-Control-Allow-Origin":"*","Connection":"keep-alive","Content-Type":"application/xml; charset=utf-8","Date":"Fri, 25 Aug 2017 12:26:25 GMT","ETag":"W/\"e272cfbb60b971b1a9674929765699d1\"","Keep-Alive":"timeout=15","Server":"Plack::Handler::Starlet","Transfer-Encoding":"chunked","Vary":"Accept-Encoding","X-RateLimit-Limit":"1200","X-RateLimit-Remaining":"1010","X-RateLimit-Reset":"1503663986"},"body":"PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48bWV0YWRhdGEgeG1sbnM9Imh0dHA6Ly9tdXNpY2JyYWluei5vcmcvbnMvbW1kLTIuMCMiPjxhcmVhIHR5cGU9IkNvdW50cnkiIHR5cGUtaWQ9IjA2ZGQwYWU0LThjNzQtMzBiYi1iNDNkLTk1ZGNlZGY5NjFkZSIgaWQ9IjJkYjQyODM3LWM4MzItM2MyNy1iNGEzLTA4MTk4Zjc1NjkzYyI+PG5hbWU+SmFwYW48L25hbWU+PHNvcnQtbmFtZT5KYXBhbjwvc29ydC1uYW1lPjxpc28tMzE2Ni0xLWNvZGUtbGlzdD48aXNvLTMxNjYtMS1jb2RlPkpQPC9pc28tMzE2Ni0xLWNvZGU+PC9pc28tMzE2Ni0xLWNvZGUtbGlzdD48L2FyZWE+PC9tZXRhZGF0YT4="},"format_version":3}
\ No newline at end of file
{"request":{"url":"https://musicbrainz.org/ws/2/area/a1411661-be21-4290-8dc1-50f3d8e3ea67?inc=","method":"GET","body":null,"headers":{"User-Agent":"MusicBrainz-Rust/Testing"}},"response":{"url":"https://musicbrainz.org/ws/2/area/a1411661-be21-4290-8dc1-50f3d8e3ea67?inc=","status":200,"headers":{"Access-Control-Allow-Origin":"*","Connection":"keep-alive","Content-Type":"application/xml; charset=utf-8","Date":"Fri, 25 Aug 2017 12:26:25 GMT","ETag":"W/\"2654605fab446f54a13bd7de2ac47252\"","Keep-Alive":"timeout=15","Server":"Plack::Handler::Starlet","Transfer-Encoding":"chunked","Vary":"Accept-Encoding","X-RateLimit-Limit":"1200","X-RateLimit-Remaining":"1007","X-RateLimit-Reset":"1503663986"},"body":"PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48bWV0YWRhdGEgeG1sbnM9Imh0dHA6Ly9tdXNpY2JyYWluei5vcmcvbnMvbW1kLTIuMCMiPjxhcmVhIHR5cGU9IkNpdHkiIGlkPSJhMTQxMTY2MS1iZTIxLTQyOTAtOGRjMS01MGYzZDhlM2VhNjciIHR5cGUtaWQ9IjZmZDhmMjlhLTNkMGEtMzJmYy05ODBkLWVhNjk3YjY5ZGE3OCI+PG5hbWU+SG9ub2x1bHU8L25hbWU+PHNvcnQtbmFtZT5Ib25vbHVsdTwvc29ydC1uYW1lPjwvYXJlYT48L21ldGFkYXRhPg=="},"format_version":3}
\ No newline at end of file
......@@ -12,17 +12,15 @@ use xpath_reader::reader::{FromXmlContained, XpathStrReader};
use std::time::{Duration, Instant};
use std::thread::sleep;
use search::{AreaSearchBuilder, ArtistSearchBuilder, ReleaseGroupSearchBuilder};
// TODO reconsider reexport
pub use search::SearchBuilder;
use search::{AreaSearchBuilder, ArtistSearchBuilder, ReleaseGroupSearchBuilder, SearchBuilder};
mod error;
pub(crate) use self::error::check_response_error;
/// Helper extracting the number of milliseconds from a `Duration`.
fn as_millis(duration: &Duration) -> f64
fn as_millis(duration: &Duration) -> u64
{
(duration.as_secs() as f64) + (duration.subsec_nanos() as f64) * 1e6
((duration.as_secs() as f64) + (duration.subsec_nanos() as f64) * 1e6) as u64
}
/// Returns an `Instant` at least 1000 seconds ago.
......@@ -51,6 +49,34 @@ pub struct ClientConfig {
/// How many times to retry requests where MusicBrainz returned 503 because
/// too many requests were being made.
pub max_retries: u8,
/// Specifies amounts of time to wait between certain actions.
pub waits: ClientWaits,
}
/// Specification of the wait time between requests.
///
/// Times are specified in miliseconds.
#[derive(Clone, Debug)]
pub struct ClientWaits {
/// Initial wait time after a ServiceUnavailable to use for the exponential
/// backoff strategy.
pub backoff_init: u64,
// TODO: Make this configurable if and only if a custom server instance is used,
// to make abuse of the main servers harder.
/// Minimal time between requests
requests: u64,
}
impl Default for ClientWaits {
fn default() -> Self
{
ClientWaits {
backoff_init: 400,
requests: 1000,
}
}
}
/// The main struct to be used to communicate with the MusicBrainz API.
......@@ -83,9 +109,8 @@ impl Client {
/// Create a new `Client` instance with the specified `HttpClient`.
///
/// 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.
/// `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
{
Client {
......@@ -97,13 +122,14 @@ impl Client {
}
impl Client {
/// Waits until we are allowed to make the next request to the MusicBrainz
/// API.
fn wait_if_needed(&mut self)
{
let now = Instant::now();
let elapsed = now.duration_since(self.last_request);
if as_millis(&elapsed) < 1000. {
// We have to wait a bit.
sleep(Duration::new(1, 0) - elapsed);
if as_millis(&elapsed) < self.config.waits.requests {
sleep(Duration::from_millis(self.config.waits.requests) - elapsed);
}
self.last_request = now;
}
......@@ -128,10 +154,7 @@ impl Client {
self.wait_if_needed();
let mut attempts = 0;
// TODO : make initial value configurable once we allow users to run against
// their own
// mirrors of the musicbrainz api.
let mut backoff = 400;
let mut backoff = self.config.waits.backoff_init;
while attempts < self.config.max_retries {
let response = self.http_client
......@@ -183,6 +206,7 @@ mod tests {
ClientConfig {
user_agent: "MusicBrainz-Rust/Testing".to_string(),
max_retries: 5,
waits: ClientWaits::default(),
},
HttpClient::replay_file(format!("replay/test_client/search/{}.json", testname)),
)
......
......@@ -80,17 +80,12 @@ impl FromXml for Area {
impl Resource for Area {
fn get_name() -> &'static str
{
"Area"
"area"
}
fn get_url(mbid: &Mbid) -> String
fn get_incs() -> &'static str
{
format!("https://musicbrainz.org/ws/2/area/{}", mbid)
}
fn base_url() -> &'static str
{
"https://musicbrainz.org/ws/2/area/"
""
}
}
......
......@@ -117,17 +117,12 @@ impl FromXml for Artist {
impl Resource for Artist {
fn get_name() -> &'static str
{
"Artist"
"artist"
}
fn get_url(mbid: &Mbid) -> String
fn get_incs() -> &'static str
{
format!("https://musicbrainz.org/ws/2/artist/{}?inc=aliases", mbid)
}
fn base_url() -> &'static str
{
"https://musicbrainz.org/ws/2/artist/"
"aliases"
}
}
......
......@@ -56,20 +56,12 @@ pub struct Event {
impl Resource for Event {
fn get_name() -> &'static str
{
"Event"
"event"
}
fn get_url(mbid: &Mbid) -> String
fn get_incs() -> &'static str
{
format!(
"https://musicbrainz.org/ws/2/event/{}?inc=aliases+annotation",
mbid
)
}
fn base_url() -> &'static str
{
"https://musicbrainz.org/ws/2/event/"
"aliases+annotation"
}
}
......
......@@ -60,17 +60,12 @@ pub struct Label {
impl Resource for Label {
fn get_name() -> &'static str
{
"Label"
"label"
}
fn get_url(mbid: &Mbid) -> String
fn get_incs() -> &'static str
{
format!("https://musicbrainz.org/ws/2/label/{}?inc=aliases", mbid)
}
fn base_url() -> &'static str
{
"https://musicbrainz.org/ws/2/label/"
"aliases"
}
}
......
......@@ -51,17 +51,30 @@ pub use self::mbid::Mbid;
/// efficiently, users of the `musicbrainz` crate shouldn't need to use this
/// type directly.
pub trait Resource {
/// Returns the name of the Resource, e. g. `"Artist"`.
/// Returns the name of the Resource, e. g. `"artist"`.
fn get_name() -> &'static str;
fn get_incs() -> &'static str;
/// Returns the url where one can get a resource in the valid format for
/// parsing from.
fn get_url(mbid: &Mbid) -> String;
fn get_url(mbid: &Mbid) -> String
{
format!(
"https://musicbrainz.org/ws/2/{}/{}?inc={}",
Self::get_name(),
mbid,
Self::get_incs()
)
}
/// Base url of the entity, e. g. `"https://musicbrainz.org/ws/2/artist/"`.
///
/// These are used for building search requests.
fn base_url() -> &'static str;
fn base_url() -> String
{
format!("https://musicbrainz.org/ws/2/{}/", Self::get_name())
}
}
// TODO pub struct Work {}
......
......@@ -105,20 +105,12 @@ impl FromXml for Place {
impl Resource for Place {
fn get_name() -> &'static str
{
"Place"
"place"
}
fn get_url(mbid: &Mbid) -> String
fn get_incs() -> &'static str
{
format!(
"https://musicbrainz.org/ws/2/place/{}?inc=annotation+aliases",
mbid
)
}
fn base_url() -> &'static str
{
"https://musicbrainz.org/ws/2/place/"
"annotation+aliases"
}
}
......
......@@ -57,20 +57,12 @@ impl FromXml for Recording {
impl Resource for Recording {
fn get_name() -> &'static str
{
"Recording"
"recording"
}
fn get_url(mbid: &Mbid) -> String
fn get_incs() -> &'static str
{
format!(
"https://musicbrainz.org/ws/2/recording/{}?inc=artists+annotation+isrcs",
mbid
)
}
fn base_url() -> &'static str
{
"https://musicbrainz.org/ws/2/recording/"
"artists+annotation+isrcs"
}
}
......
......@@ -224,20 +224,12 @@ impl FromXml for Release {
impl Resource for Release {
fn get_name() -> &'static str
{
"Release"
"release"
}
fn get_url(mbid: &Mbid) -> String
fn get_incs() -> &'static str
{
format!(
"https://musicbrainz.org/ws/2/release/{}?inc=aliases+artists+labels+recordings",
mbid
)
}
fn base_url() -> &'static str
{
"https://musicbrainz.org/ws/2/release/"
"aliases+artists+labels+recordings"
}
}
......
......@@ -86,20 +86,12 @@ pub struct ReleaseGroup {
impl Resource for ReleaseGroup {
fn get_name() -> &'static str
{
"ReleaseGroup"
"release-group"
}
fn get_url(mbid: &Mbid) -> String
fn get_incs() -> &'static str
{
format!(
"https://musicbrainz.org/ws/2/release-group/{}?inc=annotation+artists+releases",
mbid
)
}
fn base_url() -> &'static str
{
"https://musicbrainz.org/ws/2/release-group/"
"annotation+artists+releases"
}
}
......
......@@ -53,20 +53,12 @@ impl FromXml for Series {
impl Resource for Series {
fn get_name() -> &'static str
{
"Series"
"series"
}
fn get_url(mbid: &Mbid) -> String
fn get_incs() -> &'static str
{
format!(
"https://musicbrainz.org/ws/2/series/{}?inc=annotation+aliases",
mbid
)
}
fn base_url() -> &'static str
{
"https://musicbrainz.org/ws/2/series/"
"annotation+aliases"
}
}
......
......@@ -13,7 +13,7 @@ use super::full_entities::{Mbid, PartialDate};
// use super::entities;
pub trait SearchField {
type Value: ToString;
type Value;
fn to_string(&self) -> String;
}
......
//! Provides an ergonomic way to query entries from the MusicBrainz database.
//!
//! First a bit of terminology: You can search entities using different fields,
//! which are all in the module [::search::fields](fields/index.html). Either
//! use them directly from that module or use on of the submodules, which
//! organize the fields by entity. It's impossible to use invalid search
//! fields on the wrong entity.
//!
//! When performing a search you will get an instance of
//! [SearchResult](struct.SearchResult.html) wrapping instances of
//! [SearchEntity](search_entities/trait.SearchEntity.html) corresponding to
//! the full entity you want to query from the database. You can fetch the
//! full entity from a search entity, using the `fetch_full()` method on the
//! search entity.
use entities as full_entities;
use entities::Resource;
use errors::ClientError;
......
//! Query builder code.
//!
//! In general you won't need to use any of these types directly, but instead
//! through the
//! facilities provided by `Client`.
//! through the facilities provided by `Client`.
use super::*;
use regex::Regex;
......
......@@ -9,7 +9,7 @@ pub fn musicbrainz_context<'d>() -> Context<'d>
#[cfg(test)]
pub mod test_utils {
use client::{Client, ClientConfig};
use client::{Client, ClientConfig, ClientWaits};
use entities::{Mbid, Resource};
use errors::ClientError;
use reqwest_mock::GenericClient as HttpClient;
......@@ -21,6 +21,7 @@ pub mod test_utils {
ClientConfig {
user_agent: "MusicBrainz-Rust/Testing".to_string(),
max_retries: 5,
waits: ClientWaits::default(),
},
HttpClient::replay_file(format!(
"replay/test_entities/{}/{}.json",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment