1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#[macro_use]
extern crate failure;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;

mod responses;
#[macro_use]
mod utils;

use std::path::Path;

pub use responses::Summary;
use utils::errors::LeanpubResult;
use utils::http::{DefaultHttpClient, HttpClient};

/// The Leanpub API client.
pub struct Client {
    client: Box<HttpClient>,
    api_key: Option<String>,
}

/// Represents the different preview formats.
pub enum PreviewFormat {
    /// PDF format.
    Pdf,
    /// Epub format.
    Epub,
    /// Mobi format.
    Mobi,
}

impl Client {
    /// Creates a new client for a specific slug.
    /// The API key is optional but required when
    /// interacting with API endpoints that requires
    /// an authenticated user.
    pub fn new(api_key: Option<&str>) -> Self {
        return Client {
            client: Box::new(DefaultHttpClient {}),
            api_key: match api_key {
                Some(key) => Option::Some(key.to_string()),
                None => None,
            },
        };
    }

    /// Gets the books summary information.
    ///
    /// # Note
    ///
    /// This does not require authentication. If you do provide an API key,
    /// and you are an author of the book, then we will also give you the
    /// total copies sold and revenue for the book, as well as the URLs for
    /// downloading the current preview and published version of your book
    /// in epub, pdf and mobi formats.
    pub fn get_summary(&self, slug: &str) -> LeanpubResult<Summary> {
        let uri = self.append_api_key(format!("https://leanpub.com/{}.json", slug));
        let response = self.client.get(&uri[..])?;
        return Ok(serde_json::from_str::<Summary>(&response)?);
    }

    /// Downloads the latest preview in the specified format.
    pub fn download_preview(
        &self,
        slug: &str,
        file_path: &Path,
        format: PreviewFormat,
    ) -> LeanpubResult<()> {
        let result = self.get_summary(slug)?;

        let url = match format {
            PreviewFormat::Pdf => result.pdf_preview_url,
            PreviewFormat::Epub => result.epub_preview_url,
            PreviewFormat::Mobi => result.mobi_preview_url,
        };

        match url {
            Some(url) => {
                // Download the file.
                self.client.download(&url.as_str(), file_path)?;
                return Ok(());
            }
            None => return Err(format_err!("No preview available.")),
        }
    }

    fn append_api_key(&self, url: String) -> String {
        return match &self.api_key {
            Some(key) => format!("{}?api_key={}", url, key),
            None => url,
        };
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn should_parse_authenticated_summary_response_correctly() {
        // Given
        let client = create_fake_client!(
            Option::Some("api-key".to_string()),
            include_str!("res/authenticated.json"));

        // When
        let result = client.get_summary("qux").unwrap();

        // Then
        assert_eq!(321, result.id);
        assert_eq!("Qux", result.title);
        assert_eq!("A story about metasyntactic variable names", result.subtitle.as_ref().unwrap());
        assert_eq!("qux", result.slug);
        assert_eq!(None, result.about_the_book);
        assert_eq!(0.0, result.total_revenue);
        assert_eq!(None, result.last_published_at);
        assert_eq!(16645, result.word_count);
        assert_eq!(432, result.page_count);
        assert_eq!(0, result.word_count_published);
        assert_eq!(0, result.page_count_published);
        assert_eq!(0, result.total_copies_sold);
        assert_eq!(None, result.meta_description);
        assert_eq!("John Doe", result.author_string);
        assert_eq!("http://leanpub.com/qux", result.url);
        assert_eq!("https://s3.amazonaws.com/titlepages.leanpub.com/qux/original?123", result.title_page_url);
        assert_eq!(4.99, result.minimum_price);
        assert_eq!(9.99, result.suggested_price);
        assert_eq!("https://s3.amazonaws.com/titlepages.leanpub.com/qux/medium?123", result.image);
        assert_eq!(2, result.possible_reader_count);
        assert_eq!("http://leanpub.com/s/qux.pdf", result.pdf_preview_url.as_ref().unwrap());
        assert_eq!("http://leanpub.com/s/qux.epub", result.epub_preview_url.as_ref().unwrap());
        assert_eq!("http://leanpub.com/s/qux.mobi", result.mobi_preview_url.as_ref().unwrap());
    }
}