ezlime_rs/
lib.rs

1//! # ezlime-rs
2//!
3//! A Rust client library for the [ezli.me](https://ezli.me) URL shortener API.
4//!
5//! This crate provides a simple interface to create shortened URLs using the ezli.me service.
6//! To use this API, you'll need an API key from ezli.me.
7//!
8//! ## Getting an API Key
9//!
10//! If you're interested in using ezli.me for your own project via the API, please join the
11//! [Discord server](https://discord.gg/MHzmYHnnsE) to request an API key.
12//!
13//! ## Example
14//!
15//! ```rust,no_run
16//! # async fn example() -> Result<(), ezlime_rs::EzlimeApiError> {
17//! use ezlime_rs::EzlimeApi;
18//!
19//! let api = EzlimeApi::new("your-api-key-here".to_string());
20//! let original_url = "https://example.com/very/long/url";
21//!
22//! let shortened = api.create_short_url(original_url).await?;
23//! println!("Shortened URL: {}", shortened);
24//! # Ok(())
25//! # }
26//! ```
27//!
28
29use reqwest::Url;
30use serde::{Deserialize, Serialize};
31use thiserror::Error;
32
33/// Request payload for creating a shortened URL.
34#[derive(Debug, Serialize, Deserialize)]
35pub struct CreateLinkRequest {
36    /// The original URL to be shortened.
37    pub url: String,
38}
39
40/// Response from the ezli.me API after creating a shortened URL.
41#[derive(Debug, Serialize, Deserialize)]
42pub struct CreatedLinkResponse {
43    /// The unique identifier for the shortened link.
44    pub id: String,
45    /// The complete shortened URL.
46    pub shortened_url: String,
47    /// The original URL that was shortened.
48    pub original_url: String,
49}
50
51impl CreatedLinkResponse {
52    /// Creates a new `CreatedLinkResponse` with the given parameters.
53    ///
54    /// # Arguments
55    ///
56    /// * `id` - The unique identifier for the shortened link
57    /// * `prefix` - The URL prefix (e.g., `https://ezli.me`)
58    /// * `original_url` - The original URL that was shortened
59    pub fn new(id: String, prefix: &str, original_url: String) -> Self {
60        let shortened_url = format!("{}/{}", prefix, id);
61        Self {
62            id,
63            shortened_url,
64            original_url,
65        }
66    }
67}
68
69/// A client for interacting with the ezli.me API.
70///
71/// This struct provides a convenient interface for creating shortened URLs
72/// using the ezli.me service. It manages the API endpoint, authentication,
73/// and HTTP client internally.
74///
75/// # Example
76///
77/// ```rust,no_run
78/// # async fn example() -> Result<(), ezlime_rs::EzlimeApiError> {
79/// use ezlime_rs::EzlimeApi;
80///
81/// let api = EzlimeApi::new("your-api-key".to_string());
82/// let shortened = api.create_short_url("https://example.com").await?;
83/// println!("Shortened URL: {}", shortened);
84/// # Ok(())
85/// # }
86/// ```
87#[derive(Clone)]
88pub struct EzlimeApi {
89    url: String,
90    key: String,
91    client: reqwest::Client,
92}
93
94/// Errors that can occur when interacting with the ezli.me API.
95#[derive(Debug, Error)]
96pub enum EzlimeApiError {
97    /// An error occurred during API configuration (e.g., invalid URL parsing).
98    #[error("Configuration error: {0}")]
99    ConfigurationError(String),
100    /// An error occurred while sending the HTTP request or receiving the response.
101    #[error("Request error: {0}")]
102    RequestError(String),
103    /// An error occurred while deserializing the API response.
104    #[error("Deserialization error: {0}")]
105    DeserializationError(String),
106}
107
108impl EzlimeApi {
109    /// Creates a new `EzlimeApi` client with the default ezli.me endpoint.
110    ///
111    /// # Arguments
112    ///
113    /// * `key` - Your ezli.me API key for authentication
114    ///
115    /// # Example
116    ///
117    /// ```rust
118    /// use ezlime_rs::EzlimeApi;
119    ///
120    /// let api = EzlimeApi::new("your-api-key".to_string());
121    /// ```
122    pub fn new(key: String) -> Self {
123        Self {
124            url: String::from("https://ezli.me"),
125            key,
126            client: reqwest::Client::new(),
127        }
128    }
129
130    /// Sets a custom API endpoint URL.
131    ///
132    /// By default, the client uses `https://ezli.me`. Use this method to
133    /// configure a different endpoint, such as for testing or self-hosted instances.
134    ///
135    /// # Arguments
136    ///
137    /// * `url` - The base URL of the ezli.me API endpoint
138    ///
139    /// # Example
140    ///
141    /// ```rust
142    /// use ezlime_rs::EzlimeApi;
143    ///
144    /// let api = EzlimeApi::new("your-api-key".to_string())
145    ///     .with_url("https://custom.ezli.me");
146    /// ```
147    pub fn with_url(mut self, url: &str) -> Self {
148        self.url = url.into();
149        self
150    }
151
152    /// Creates a shortened URL using the ezli.me API.
153    ///
154    /// This method sends a request to the ezli.me API to create a shortened version
155    /// of the provided URL.
156    ///
157    /// # Arguments
158    ///
159    /// * `original_link` - The URL to be shortened
160    ///
161    /// # Returns
162    ///
163    /// Returns `Ok(String)` containing the shortened URL on success, or an
164    /// `EzlimeApiError` if the request fails.
165    ///
166    /// # Errors
167    ///
168    /// This function will return an error if:
169    /// - The API endpoint URL is invalid (`ConfigurationError`)
170    /// - The HTTP request fails (`RequestError`)
171    /// - The response cannot be deserialized (`DeserializationError`)
172    ///
173    /// # Example
174    ///
175    /// ```rust,no_run
176    /// # async fn example() -> Result<(), ezlime_rs::EzlimeApiError> {
177    /// use ezlime_rs::EzlimeApi;
178    ///
179    /// let api = EzlimeApi::new("your-api-key".to_string());
180    /// let shortened = api.create_short_url("https://example.com/long/url").await?;
181    /// println!("Shortened URL: {}", shortened);
182    /// # Ok(())
183    /// # }
184    /// ```
185    pub async fn create_short_url(&self, original_link: &str) -> Result<String, EzlimeApiError> {
186        let url: Url = Url::parse(&format!("{}/link/create", self.url))
187            .map_err(|e| EzlimeApiError::ConfigurationError(e.to_string()))?;
188
189        let resp = self
190            .client
191            .post(url)
192            .header("Authorization", self.key.clone())
193            .json(&CreateLinkRequest {
194                url: original_link.to_string(),
195            })
196            .send()
197            .await
198            .map_err(|e| EzlimeApiError::RequestError(e.to_string()))?
199            .json::<CreatedLinkResponse>()
200            .await
201            .map_err(|e| EzlimeApiError::DeserializationError(e.to_string()))?;
202
203        Ok(resp.shortened_url)
204    }
205}