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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#![deny( //These lint options are copied from https://pascalhertleif.de/artikel/good-practices-for-writing-rust-libraries/
    missing_docs,
    missing_debug_implementations,
    missing_copy_implementations,
    trivial_casts,
    trivial_numeric_casts,
    unsafe_code,
    unstable_features,
    unused_import_braces,
    unused_qualifications
)]

//! # Datamuse Api Wrapper
//! This library provides an unofficial wrapper around the Datamuse api for use in rust projects.
//! The Datamuse api allows users to retrieve lists of words from different vocabulary lists
//! based on a large number of different parameters. Additionally, it can be used to retrieve
//! words based on a short string of letters, allowing for autocomplete functionality.
//! Currently, the api does not require an api key and can be used to make up to 100,000
//! requests per day after which the requests may be rate-limited (for higher usage see website).
//! If you use this api you should make reference to the Datamuse api in your project.
//! For more information see the official documentation at [https://www.datamuse.com/api/](https://www.datamuse.com/api/)
//!
//! ## Examples
//! ### Words Endpoint
//! ```rust
//! extern crate tokio;
//! extern crate datamuse_api_wrapper;
//! use datamuse_api_wrapper::{ DatamuseClient, Vocabulary, EndPoint, RelatedType };
//!
//! #[tokio::main]
//! async fn main() -> datamuse_api_wrapper::Result<()> {
//!     let client = DatamuseClient::new();
//!     let request = client.new_query(Vocabulary::English, EndPoint::Words)
//!         .means_like("breakfast") // The words we're looking for are related to "breakfast"
//!         .related(RelatedType::Rhyme, "grape"); // and rhyme with "grape"
//!     let word_list = request.list().await?; // Get the list of words from the api
//!
//!     assert_eq!("crepe", word_list[0].word); // "crepe" is the only result as of writing this
//!
//!     Ok(())
//! }
//! ```
//!
//! ### Suggest EndPoint
//! ```rust
//! extern crate tokio;
//! extern crate datamuse_api_wrapper;
//! use datamuse_api_wrapper::{ DatamuseClient, Vocabulary, EndPoint };
//!
//! #[tokio::main]
//! async fn main() -> datamuse_api_wrapper::Result<()> {
//!     let client = DatamuseClient::new();
//!     let request = client.new_query(Vocabulary::English, EndPoint::Suggest)
//!         .hint_string("hello wor") // The user has alread typed in "hello wor"
//!         .max_results(2); // We only want the first 2 results to be returned
//!
//!     let request = request.build()?; // Build the request
//!     let response = request.send().await?; // Send the request
//!     let word_list = response.list()?; // Parse the response into a word_list
//!
//!     assert_eq!("hello world", word_list[0].word); // "hello world" is the first result as of writing this
//!
//!     Ok(())
//! }
//! ```

extern crate reqwest;
extern crate serde;
extern crate serde_json;

use std::error;
use std::fmt::{self, Display, Formatter};
use std::result;

mod request;
mod response;

pub use request::*;
pub use response::*;

/// This struct represents the client which can be used to make requests
/// to the Datamuse api. Requests can be created using the new_query() method
#[derive(Debug)]
pub struct DatamuseClient {
    client: reqwest::Client,
}

impl DatamuseClient {
    /// Returns a new DatamuseClient struct
    pub fn new() -> Self {
        DatamuseClient {
            client: reqwest::Client::new(),
        }
    }

    /// Returns a new [RequestBuilder](request::RequestBuilder) struct with which requests can be created
    /// and later sent. As parameters the vocabulary set and endpoint of the request are required. See
    /// their individual documentations for more information
    pub fn new_query<'a>(
        &'a self,
        vocabulary: Vocabulary,
        endpoint: EndPoint,
    ) -> RequestBuilder<'a> {
        RequestBuilder::new(self, vocabulary, endpoint)
    }
}

/// A type alias for Results with the library Error type
pub type Result<T> = result::Result<T, Error>;

/// An enum representing the different kind of Errors that can be returned within the library
#[derive(Debug)]
pub enum Error {
    /// An error resulting from an underlying call to reqwest
    ReqwestError(reqwest::Error),
    /// An error resulting from an underlying call to serde
    SerdeError(serde_json::Error),
    /// An error resulting from the use of a parameter not availible for a specific vocabulary list
    VocabularyError((String, String)),
    /// An error resulting from the use of a parameter not intended for the specified endpoint
    EndPointError((String, String)),
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Self::ReqwestError(err) => write!(f, "{}", err),
            Self::SerdeError(err) => write!(f, "{}", err),
            Self::VocabularyError((lang, param)) => write!(
                f,
                "Error: The parameter {} is not yet supported for {}",
                param, lang
            ),
            Self::EndPointError((endpoint, param)) => write!(
                f,
                "Error: The parameter {} is not supported for {}",
                param, endpoint
            ),
        }
    }
}

impl error::Error for Error {}

impl From<reqwest::Error> for Error {
    fn from(error: reqwest::Error) -> Self {
        Error::ReqwestError(error)
    }
}

impl From<serde_json::Error> for Error {
    fn from(error: serde_json::Error) -> Self {
        Error::SerdeError(error)
    }
}