telegraph_api_rs/
lib.rs

1#![warn(missing_docs)]
2#![warn(rustdoc::broken_intra_doc_links)]
3//! Rust implementation of [Telegraph API](https://telegra.ph/api)
4//! 
5//! # Quick start
6//! 
7//! ## Create account
8//! ```rust, no_run
9//! use telegraph_api_rs::Telegraph;
10//! 
11//! let telegraph = Telegraph::new();
12//! let account = telegraph.create_account()
13//! .short_name("Short name")
14//! .author_name("Author name")
15//! .send()
16//! .unwrap();
17//! ```
18//! 
19//! ## Edit account
20//! ```rust, no_run
21//! # use telegraph_api_rs::{Telegraph, types::Account};
22//! # let telegraph = Telegraph::new();
23//! # let account = Account::default();
24//! # let token = account.access_token.as_ref().unwrap();
25//! let edited_account = telegraph.edit_account_info()
26//! .access_token(token)
27//! .author_name("Author name 2")
28//! .send()
29//! .unwrap();
30//! ```
31//! 
32//! ## Get account info
33//! ```rust, no_run
34//! # use telegraph_api_rs::{Telegraph, types::{Account, AccountField}};
35//! # let telegraph = Telegraph::new();
36//! # let account = Account::default();
37//! # let token = account.access_token.as_ref().unwrap();
38//! let account_info = telegraph.get_account_info()
39//! .access_token(token)
40//! .fields(vec![AccountField::ShortName, AccountField::AuthorUrl])
41//! .send()
42//! .unwrap();
43//! ```
44//! 
45//! ## Revoke access token
46//! ```rust, no_run
47//! # use telegraph_api_rs::{Telegraph, types::{Account}};
48//! # let telegraph = Telegraph::new();
49//! # let account = Account::default();
50//! # let token = account.access_token.as_ref().unwrap();
51//! let account = telegraph.revoke_access_token()
52//! .access_token(token)
53//! .send()
54//! .unwrap();
55//! ```
56//! 
57//! ## Create page
58//! ```rust, no_run
59//! # use telegraph_api_rs::{Telegraph, build_content, types::{Account}};
60//! # let telegraph = Telegraph::new();
61//! # let account = Account::default();
62//! # let token = account.access_token.as_ref().unwrap();
63//! let content = r#"
64//! [
65//!     {
66//!         "tag": "h3",
67//!         "children": ["Hello world"]
68//!     },
69//!     {
70//!         "tag": "h4",
71//!         "children": ["Title"]
72//!     },
73//!     {
74//!         "tag": "p",
75//!         "children": [
76//!             {
77//!                 "tag": "ul",
78//!                 "children": ["Some text"]
79//!             }
80//!         ]
81//!     }
82//! ]
83//! "#;
84//!
85//! let cont = build_content(content).unwrap();
86//!
87//! let page = telegraph.create_page()
88//! .access_token(token)
89//! .title("Hello world")
90//! .content(cont)
91//! .return_content(true)
92//! .send()
93//! .unwrap();
94//! ```
95//! 
96//! ## Edit page
97//! ```rust, no_run
98//! # use telegraph_api_rs::{Telegraph, build_content, types::{Page, Account}};
99//! # let telegraph = Telegraph::new();
100//! # let account = Account::default();
101//! # let page = Page::default();
102//! # let token = account.access_token.as_ref().unwrap();
103//! let new_content = r#"
104//! [
105//!    {
106//!        "tag": "h3",
107//!        "children": ["Hello world"]
108//!    },
109//!    {
110//!       "tag": "h4",
111//!        "children": ["Title"]
112//!    },
113//!    {
114//!        "tag": "p",
115//!        "children": [
116//!            {
117//!               "tag": "ul",
118//!                "children": ["Some text"]
119//!            },
120//!            {
121//!                "tag": "ul",
122//!                "children": ["Some text 2"]
123//!            }
124//!        ]
125//!    }
126//! ]
127//! "#;
128//!
129//! let new_cont = build_content(new_content).unwrap();
130//!
131//! let edited_page = telegraph.edit_page()
132//! .access_token(token)
133//! .title(&page.title)
134//! .path(&page.path)
135//! .content(new_cont)
136//! .return_content(true)
137//! .send()
138//! .unwrap();
139//! ```
140//! 
141//! ## Get page
142//! ```rust, no_run
143//! # use telegraph_api_rs::{Telegraph, types::{Page, Account}};
144//! # let telegraph = Telegraph::new();
145//! # let account = Account::default();
146//! # let page = Page::default();
147//! let get_page = telegraph.get_page()
148//! .path(&page.path)
149//! .send()
150//! .unwrap();
151//! ```
152//! 
153//! ## Get page list
154//! ```rust, no_run
155//! # use telegraph_api_rs::{Telegraph, types::Account};
156//! # let telegraph = Telegraph::new();
157//! # let account = Account::default();
158//! # let token = account.access_token.as_ref().unwrap();
159//! let page_list = telegraph.get_page_list()
160//! .access_token(token)
161//! .limit(2)
162//! .send()
163//! .unwrap();
164//! ```
165//! 
166//! ## Get views
167//! ```rust, no_run
168//! # use telegraph_api_rs::{Telegraph, types::PageList};
169//! # let telegraph = Telegraph::new();
170//! # let page_list = PageList::default();
171//! let count = telegraph.get_views()
172//! .path(&page_list.pages[0].path)
173//! .year(2022)
174//! .month(10)
175//! .day(15)
176//! .send()
177//! .unwrap();
178//! ```
179//! [`Telegraph`] contains methods Telegraph API  
180//! 
181//! # Errors
182//! 
183//! Possible errors are described in [`TelegraphError`].
184
185pub mod types;
186pub mod requests;
187pub mod error;
188
189use std::rc::Rc;
190use std::path::Path;
191use std::fs::File;
192use std::io::Read;
193
194use reqwest::blocking::{Client, multipart};
195use types::Node;
196#[cfg(feature = "upload")]
197use types::{UploadResult, Media};
198
199use crate::requests::{
200    CreateAccount, EditAccountInfo, GetAccountInfo, 
201    CreatePage, RevokeAccessToken, EditPage, GetPage,
202    GetPageList, GetViews, NoShortName, NoAccessToken,
203    NoTitle, NoContent, NoPath
204};
205pub use crate::error::TelegraphError;
206
207
208struct MethodName {
209    create_account: Rc<String>,
210    edit_account_info: Rc<String>,
211    get_account_info: Rc<String>,
212    revoke_access_token: Rc<String>,
213    create_page: Rc<String>,
214    edit_page: Rc<String>,
215    get_page: Rc<String>,
216    get_page_list: Rc<String>,
217    get_views: Rc<String>
218}
219
220
221impl Default for MethodName{
222    fn default() -> Self {
223        MethodName {
224            create_account: Rc::new("https://api.telegra.ph/createAccount".to_string()),
225            edit_account_info: Rc::new("https://api.telegra.ph/editAccountInfo".to_string()),
226            get_account_info: Rc::new("https://api.telegra.ph/getAccountInfo".to_string()),
227            revoke_access_token: Rc::new("https://api.telegra.ph/revokeAccessToken".to_string()),
228            create_page: Rc::new("https://api.telegra.ph/createPage".to_string()),
229            edit_page: Rc::new("https://api.telegra.ph/editPage".to_string()),
230            get_page: Rc::new("https://api.telegra.ph/getPage".to_string()),
231            get_page_list: Rc::new("https://api.telegra.ph/getPageList".to_string()),
232            get_views: Rc::new("https://api.telegra.ph/getViews".to_string()),
233        }
234    }
235}
236
237
238/// `Telegraph` for calling method builder
239#[derive(Default)]
240pub struct Telegraph {
241    client: Rc<Client>,
242    method_name: MethodName
243}
244
245
246impl Telegraph {
247    /// Constructs a new `Telegraph`
248    pub fn new() -> Self {
249        Telegraph::default()
250    }
251
252    /// Use this method to create a new Telegraph [`Account`][crate::types::Account]. 
253    /// Most users only need one account, but this can be useful 
254    /// for channel administrators who would like to keep individual 
255    /// author names and profile links for each of their channels. 
256    /// On success, returns an [`Account`][crate::types::Account] with the regular 
257    /// fields and an additional `access_token` field.
258    /// 
259    /// # Example
260    /// ```rust, no_run
261    /// use telegraph_api_rs::Telegraph;
262    ///
263    /// let telegraph = Telegraph::new();
264    /// let account = telegraph.create_account()
265    /// .short_name("Short name")
266    /// .author_name("Author name")
267    /// .send()
268    /// .unwrap();
269    /// ```
270    pub fn create_account(&self) -> CreateAccount<NoShortName> {
271        CreateAccount::new(
272            self.client.clone(), 
273            self.method_name.create_account.clone()
274        )
275    }
276
277    /// Use this method to update information about a Telegraph account. 
278    /// Pass only the parameters that you want to edit. 
279    /// On success, returns an [`Account`][crate::types::Account] 
280    /// with the default fields.
281    /// 
282    /// # Example
283    /// ```rust, no_run
284    /// # use telegraph_api_rs::{Telegraph, types::Account};
285    /// # let telegraph = Telegraph::new();
286    /// # let account = Account::default();
287    /// # let token = account.access_token.as_ref().unwrap();
288    /// let edited_account = telegraph.edit_account_info()
289    /// .access_token(token)
290    /// .author_name("Author name 2")
291    /// .send()
292    /// .unwrap();
293    /// ```
294    pub fn edit_account_info(&self) -> EditAccountInfo<NoAccessToken>
295    {
296        EditAccountInfo::new(
297            self.client.clone(), 
298            self.method_name.edit_account_info.clone()
299        )
300    }
301
302    /// Use this method to get information about a Telegraph account. 
303    /// Returns an [`Account`][crate::types::Account] on success.
304    /// 
305    /// # Example
306    /// ```rust, no_run
307    /// # use telegraph_api_rs::{Telegraph, types::{Account, AccountField}};
308    /// # let telegraph = Telegraph::new();
309    /// # let account = Account::default();
310    /// # let token = account.access_token.as_ref().unwrap();
311    /// let account_info = telegraph.get_account_info()
312    /// .access_token(token)
313    /// .fields(vec![AccountField::ShortName, AccountField::AuthorUrl])
314    /// .send()
315    /// .unwrap();
316    /// ```
317    pub fn get_account_info(&self) -> GetAccountInfo<NoAccessToken> {
318        GetAccountInfo::new(
319            self.client.clone(), 
320            self.method_name.get_account_info.clone()
321        )
322    }
323
324    /// Use this method to revoke `access_token` and generate a new one, 
325    /// for example, if the user would like to reset all connected sessions, 
326    /// or you have reasons to believe the token was compromised. 
327    /// On success, returns an [`Account`][crate::types::Account] with 
328    /// new `access_token` and `auth_url` fields.
329    /// 
330    /// # Example
331    /// ```rust, no_run
332    /// # use telegraph_api_rs::{Telegraph, types::{Account}};
333    /// # let telegraph = Telegraph::new();
334    /// # let account = Account::default();
335    /// # let token = account.access_token.as_ref().unwrap();
336    /// let account = telegraph.revoke_access_token()
337    /// .access_token(token)
338    /// .send()
339    /// .unwrap();
340    /// ```
341    pub fn revoke_access_token(&self) -> RevokeAccessToken<NoAccessToken> {
342        RevokeAccessToken::new(
343            self.client.clone(), 
344            self.method_name.revoke_access_token.clone()
345        )
346    }
347
348    /// Use this method to create a new Telegraph page. 
349    /// On success, returns a [`Page`][crate::types::Page].
350    ///
351    /// # Example
352    /// ```rust, no_run
353    /// # use telegraph_api_rs::{Telegraph, build_content, types::{Account}};
354    /// # let telegraph = Telegraph::new();
355    /// # let account = Account::default();
356    /// # let token = account.access_token.as_ref().unwrap();
357    /// let content = r#"
358    /// [
359    ///     {
360    ///         "tag": "h3",
361    ///         "children": ["Hello world"]
362    ///     },
363    ///     {
364    ///         "tag": "h4",
365    ///         "children": ["Title"]
366    ///     },
367    ///     {
368    ///         "tag": "p",
369    ///         "children": [
370    ///             {
371    ///                 "tag": "ul",
372    ///                 "children": ["Some text"]
373    ///             }
374    ///         ]
375    ///     }
376    /// ]
377    /// "#;
378    ///
379    /// let cont = build_content(content).unwrap();
380    ///
381    /// let page = telegraph.create_page()
382    /// .access_token(token)
383    /// .title("Hello world")
384    /// .content(cont)
385    /// .return_content(true)
386    /// .send()
387    /// .unwrap();
388    /// ```
389    pub fn create_page(&self) -> CreatePage<NoAccessToken, NoTitle, NoContent> {
390        CreatePage::new(
391            self.client.clone(), 
392            self.method_name.create_page.clone()
393        )
394    }
395
396    /// Use this method to edit an existing Telegraph page. 
397    /// On success, returns a [`Page`][crate::types::Page].
398    /// 
399    /// # Example
400    /// ```rust, no_run
401    /// # use telegraph_api_rs::{Telegraph, build_content, types::{Page, Account}};
402    /// # let telegraph = Telegraph::new();
403    /// # let account = Account::default();
404    /// # let page = Page::default();
405    /// # let token = account.access_token.as_ref().unwrap();
406    /// let new_content = r#"
407    /// [
408    ///    {
409    ///        "tag": "h3",
410    ///        "children": ["Hello world"]
411    ///    },
412    ///    {
413    ///       "tag": "h4",
414    ///        "children": ["Title"]
415    ///    },
416    ///    {
417    ///        "tag": "p",
418    ///        "children": [
419    ///            {
420    ///               "tag": "ul",
421    ///                "children": ["Some text"]
422    ///            },
423    ///            {
424    ///                "tag": "ul",
425    ///                "children": ["Some text 2"]
426    ///            }
427    ///        ]
428    ///    }
429    /// ]
430    /// "#;
431    ///
432    /// let new_cont = build_content(new_content).unwrap();
433    ///
434    /// let edited_page = telegraph.edit_page()
435    /// .access_token(token)
436    /// .title(&page.title)
437    /// .path(&page.path)
438    /// .content(new_cont)
439    /// .return_content(true)
440    /// .send()
441    /// .unwrap();
442    /// ```
443    pub fn edit_page(&self) -> EditPage<NoAccessToken, NoPath, NoTitle, NoContent> {
444        EditPage::new(
445            self.client.clone(), 
446            self.method_name.edit_page.clone()
447        )
448    }
449
450    /// Use this method to get a Telegraph page. 
451    /// Returns a [`Page`][crate::types::Page] on success.
452    ///
453    /// # Example
454    /// ```rust, no_run
455    /// # use telegraph_api_rs::{Telegraph, types::{Page, Account}};
456    /// # let telegraph = Telegraph::new();
457    /// # let account = Account::default();
458    /// # let page = Page::default();
459    /// let get_page = telegraph.get_page()
460    /// .path(&page.path)
461    /// .send()
462    /// .unwrap();
463    /// ```
464    pub fn get_page(&self) -> GetPage<NoPath> {
465        GetPage::new(
466            self.client.clone(), 
467            self.method_name.get_page.clone()
468        )
469    }
470
471    /// Use this method to get a list of pages belonging to a Telegraph account. 
472    /// Returns a [`PageList`][crate::types::PageList], 
473    /// sorted by most recently created pages first.
474    ///
475    /// # Example
476    /// ```rust, no_run
477    /// # use telegraph_api_rs::{Telegraph, types::Account};
478    /// # let telegraph = Telegraph::new();
479    /// # let account = Account::default();
480    /// let page_list = telegraph.get_page_list()
481    /// .access_token(&account.access_token.unwrap())
482    /// .limit(2)
483    /// .send()
484    /// .unwrap();
485    /// ```
486    pub fn get_page_list(&self) -> GetPageList<NoAccessToken> {
487        GetPageList::new(
488            self.client.clone(), 
489            self.method_name.get_page_list.clone()
490        )
491    }
492
493    /// Use this method to get the number of views for a Telegraph article. 
494    /// Returns a [`PageViews`][crate::types::PageViews] on success. 
495    /// By default, the total number of page views will be returned.
496    ///
497    /// # Example
498    /// ```rust, no_run
499    /// # use telegraph_api_rs::{Telegraph, types::PageList};
500    /// # let telegraph = Telegraph::new();
501    /// # let page_list = PageList::default();
502    /// let count = telegraph.get_views()
503    /// .path(&page_list.pages[0].path)
504    /// .year(2022)
505    /// .month(10)
506    /// .day(15)
507    /// .send()
508    /// .unwrap();
509    /// ```
510    pub fn get_views(&self) -> GetViews<NoPath> {
511        GetViews::new(
512            self.client.clone(), 
513            self.method_name.get_views.clone()
514        )
515    }
516
517    #[cfg(feature = "upload")]
518    fn get_mime<T>(path: T) -> String 
519    where T: AsRef<Path>
520    {
521        let mime = mime_guess::from_path(path).first_or(mime_guess::mime::IMAGE_JPEG);
522        format!("{}/{}", mime.type_(), mime.subtype())
523    }
524
525    #[cfg(feature = "upload")]
526    fn _upload<T>(client: &Client, files: &[T]) -> Result<Vec<Media>, TelegraphError> 
527    where T: AsRef<Path>
528    {
529        let mut form = multipart::Form::new();
530        for (index, file_name) in files.iter().enumerate() {
531            let mut buf = vec![];
532            let mut file = File::open(file_name)?;
533            file.read_to_end(&mut buf)?;
534            let part = multipart::Part::bytes(buf)
535                .file_name(index.to_string())
536                .mime_str(&Self::get_mime(file_name))?;
537            form = form.part(index.to_string(), part);
538        }
539
540        let response = client.post("https://telegra.ph/upload")
541        .multipart(form)
542        .send()?;
543        
544        match response.json::<UploadResult>()? {
545            UploadResult::Error { error } => Err(TelegraphError::ApiError(error)),
546            UploadResult::Ok(vec) => Ok(vec)
547        }
548    }
549
550    #[cfg(feature = "upload")]
551    /// Upload files to telegraph
552    /// 
553    /// # Example
554    /// ``` rust, no_run
555    /// # use telegraph_api_rs::Telegraph;
556    /// let telegraph = Telegraph::new();
557    /// let files = vec!["1.jpg", "2.png"];
558    /// let media = telegraph.upload(&files);
559    /// ```
560    pub fn upload<T>(&self, files: &[T]) -> Result<Vec<Media>, TelegraphError> 
561    where T: AsRef<Path>
562    {
563        Self::_upload(&self.client, files)
564    }
565
566    #[cfg(feature = "upload")]
567    /// Upload files to telegraph with custom client
568    /// 
569    /// # Example
570    /// ``` rust, no_run
571    /// # use telegraph_api_rs::Telegraph;
572    /// use reqwest::blocking::Client;
573    /// 
574    /// let files = vec!["1.jpg", "2.png"];
575    /// let client = Client::new();
576    /// let media = Telegraph::upload_with(&client, &files);
577    /// ```
578    pub fn upload_with<T>(client: &Client, files: &[T]) -> Result<Vec<Media>, TelegraphError> 
579    where T: AsRef<Path>
580    {
581        Self::_upload(client, files)
582    }
583
584}
585
586
587/// Build page content from string
588/// 
589/// # Example
590/// ```rust, no_run
591/// # use telegraph_api_rs::build_content;
592/// let content = r#"
593/// [
594///    {
595///        "tag": "h3",
596///        "children": ["Hello world"]
597///    },
598///    {
599///       "tag": "h4",
600///        "children": ["Title"]
601///    },
602///    {
603///        "tag": "p",
604///        "children": [
605///            {
606///               "tag": "ul",
607///                "children": ["Some text"]
608///            },
609///            {
610///                "tag": "ul",
611///                "children": ["Some text 2"]
612///            }
613///        ]
614///    }
615/// ]
616/// "#;
617///
618/// let cont = build_content(content).unwrap();
619/// ``` 
620pub fn build_content(content: &str) -> Result<Vec<Node>, TelegraphError> {
621    serde_json::from_str(content).map_err(TelegraphError::from)
622}