linkedin_api/
lib.rs

1//! Rust Wrapper for the LinkedIn API
2//!
3//! This crate provides an asynchronous interface to the LinkedIn Voyager API.
4//!
5//! # Example
6//!
7//! ```no_run
8//! use linkedin_api_rs::Linkedin;
9//! use std::env;
10//!
11//! #[tokio::main]
12//! async fn main() -> Result<(), linkedin_api_rs::LinkedinError> {
13//!     let username = env::var("LINKEDIN_USERNAME").unwrap();
14//!     let password = env::var("LINKEDIN_PASSWORD").unwrap();
15//!
16//!     let api = Linkedin::new(&username, &password, false).await?;
17//!
18//!     let profile = api.get_profile("billy-g").await?;
19//!
20//!     Ok(())
21//! }
22//! ```
23
24use serde_json::Value;
25use std::collections::HashMap;
26
27pub use crate::error::LinkedinError;
28use crate::types::SearchPeopleParams;
29use crate::{
30    linkedin::LinkedinInner,
31    types::{
32        Company, Connection, ContactInfo, Conversation, ConversationDetails, Identity, Invitation,
33        MemberBadges, NetworkInfo, PersonSearchResult, Profile, School, Skill, UniformResourceName,
34    },
35};
36
37pub mod client;
38pub mod error;
39pub mod linkedin;
40pub mod types;
41pub mod utils;
42
43/// Main struct for interacting with the LinkedIn API asynchronously.
44#[derive(Clone)]
45pub struct Linkedin {
46    inner: LinkedinInner,
47}
48
49impl UniformResourceName {
50    pub fn parse(urn: &str) -> Result<Self, LinkedinError> {
51        let parts: Vec<&str> = urn.split(':').collect();
52        if parts.len() < 4 {
53            return Err(LinkedinError::InvalidInput(format!(
54                "Not enough components in URN: {urn}"
55            )));
56        }
57
58        // Skip the first part (just qualifier that this is indeed an urn)
59
60        let namespace = parts[2].to_string();
61        let id = parts[3].to_string();
62
63        Ok(Self { namespace, id })
64    }
65
66    /// Return the "id" part only, e.g. the ACoAAA… string.
67    pub fn id_str(&self) -> &str {
68        &self.id
69    }
70
71    /// Return the full URN as a string ("urn:li:ns:id").
72    pub fn as_str(&self) -> String {
73        format!("urn:li:{}:{}", self.namespace, self.id)
74    }
75}
76
77impl AsRef<str> for UniformResourceName {
78    fn as_ref(&self) -> &str {
79        &self.id
80    }
81}
82
83impl Linkedin {
84    /// Create a new LinkedIn client and authenticate.
85    pub async fn new(identity: &Identity, refresh_cookies: bool) -> Result<Self, LinkedinError> {
86        let inner = LinkedinInner::new(identity, refresh_cookies).await?;
87        Ok(Self { inner })
88    }
89
90    /// Returns a LinkedIn profile.
91    pub async fn get_profile(&self, public_id: &str) -> Result<Profile, LinkedinError> {
92        self.inner.get_profile(Some(public_id), None).await
93    }
94
95    /// Returns a LinkedIn profile by URN ID.
96    pub async fn get_profile_by_urn(
97        &self,
98        urn: &UniformResourceName,
99    ) -> Result<Profile, LinkedinError> {
100        self.inner.get_profile(None, Some(urn)).await
101    }
102
103    /// Returns a LinkedIn profile's first degree connections.
104    pub async fn get_profile_connections(
105        &self,
106        urn_id: &str,
107    ) -> Result<Vec<Connection>, LinkedinError> {
108        self.inner.get_profile_connections(urn_id).await
109    }
110
111    /// Returns a LinkedIn profile's contact information.
112    pub async fn get_profile_contact_info(
113        &self,
114        public_id: &str,
115    ) -> Result<ContactInfo, LinkedinError> {
116        self.inner
117            .get_profile_contact_info(Some(public_id), None)
118            .await
119    }
120
121    /// Returns a LinkedIn profile's contact information by URN ID.
122    pub async fn get_profile_contact_info_by_urn(
123        &self,
124        urn_id: &UniformResourceName,
125    ) -> Result<ContactInfo, LinkedinError> {
126        self.inner
127            .get_profile_contact_info(None, Some(urn_id))
128            .await
129    }
130
131    /// Returns a LinkedIn profile's skills.
132    pub async fn get_profile_skills(&self, public_id: &str) -> Result<Vec<Skill>, LinkedinError> {
133        self.inner.get_profile_skills(Some(public_id), None).await
134    }
135
136    /// Returns a LinkedIn profile's skills by URN ID.
137    pub async fn get_profile_skills_by_urn(
138        &self,
139        urn: UniformResourceName,
140    ) -> Result<Vec<Skill>, LinkedinError> {
141        self.inner.get_profile_skills(None, Some(&urn)).await
142    }
143
144    /// Returns a LinkedIn profile's privacy settings.
145    pub async fn get_profile_privacy_settings(
146        &self,
147        public_id: &str,
148    ) -> Result<HashMap<String, Value>, LinkedinError> {
149        self.inner.get_profile_privacy_settings(public_id).await
150    }
151
152    /// Returns a LinkedIn profile's member badges.
153    pub async fn get_profile_member_badges(
154        &self,
155        public_id: &str,
156    ) -> Result<MemberBadges, LinkedinError> {
157        self.inner.get_profile_member_badges(public_id).await
158    }
159
160    /// Returns high-level network info for a profile.
161    pub async fn get_profile_network_info(
162        &self,
163        public_id: &str,
164    ) -> Result<NetworkInfo, LinkedinError> {
165        self.inner.get_profile_network_info(public_id).await
166    }
167
168    /// Removes a connection.
169    pub async fn remove_connection(&self, public_id: &str) -> Result<bool, LinkedinError> {
170        self.inner.remove_connection(public_id).await
171    }
172
173    /// Return list of metadata of the user's conversations.
174    pub async fn get_conversations(&self) -> Result<Vec<Conversation>, LinkedinError> {
175        self.inner.get_conversations().await
176    }
177
178    /// Return conversation details for a profile URN ID.
179    pub async fn get_conversation_details(
180        &self,
181        profile_urn_id: &str,
182    ) -> Result<ConversationDetails, LinkedinError> {
183        self.inner.get_conversation_details(profile_urn_id).await
184    }
185
186    /// Return a conversation.
187    pub async fn get_conversation(
188        &self,
189        conversation_urn_id: &str,
190    ) -> Result<Conversation, LinkedinError> {
191        self.inner.get_conversation(conversation_urn_id).await
192    }
193
194    /// Sends a message to a conversation or recipients.
195    pub async fn send_message(
196        &self,
197        conversation_urn_id: Option<&str>,
198        recipients: Option<Vec<String>>,
199        message_body: &str,
200    ) -> Result<bool, LinkedinError> {
201        self.inner
202            .send_message(conversation_urn_id, recipients, message_body)
203            .await
204    }
205
206    /// Mark a conversation as seen.
207    pub async fn mark_conversation_as_seen(
208        &self,
209        conversation_urn_id: &str,
210    ) -> Result<bool, LinkedinError> {
211        self.inner
212            .mark_conversation_as_seen(conversation_urn_id)
213            .await
214    }
215
216    /// Get view statistics for the current profile.
217    pub async fn get_current_profile_views(&self) -> Result<u64, LinkedinError> {
218        self.inner.get_current_profile_views().await
219    }
220
221    /// Returns a school's LinkedIn profile.
222    pub async fn get_school(&self, public_id: &str) -> Result<School, LinkedinError> {
223        self.inner.get_school(public_id).await
224    }
225
226    /// Returns a company's LinkedIn profile.
227    pub async fn get_company(&self, public_id: &str) -> Result<Company, LinkedinError> {
228        self.inner.get_company(public_id).await
229    }
230
231    /// Perform a LinkedIn search.
232    pub async fn search(
233        &self,
234        params: HashMap<String, String>,
235        limit: Option<usize>,
236    ) -> Result<Vec<Value>, LinkedinError> {
237        self.inner.search(params, limit).await
238    }
239
240    /// Perform a people search.
241    pub async fn search_people(
242        &self,
243        params: SearchPeopleParams,
244    ) -> Result<Vec<PersonSearchResult>, LinkedinError> {
245        self.inner.search_people(params).await
246    }
247
248    /// Get company updates.
249    pub async fn get_company_updates(
250        &self,
251        public_id: Option<&str>,
252        urn_id: Option<&str>,
253        max_results: Option<usize>,
254    ) -> Result<Vec<Value>, LinkedinError> {
255        self.inner
256            .get_company_updates(public_id, urn_id, max_results)
257            .await
258    }
259
260    /// Get profile updates.
261    pub async fn get_profile_updates(
262        &self,
263        public_id: Option<&str>,
264        urn_id: Option<&str>,
265        max_results: Option<usize>,
266    ) -> Result<Vec<Value>, LinkedinError> {
267        self.inner
268            .get_profile_updates(public_id, urn_id, max_results)
269            .await
270    }
271
272    /// Get all invitations for the current profile.
273    pub async fn get_invitations(
274        &self,
275        start: usize,
276        limit: usize,
277    ) -> Result<Vec<Invitation>, LinkedinError> {
278        self.inner.get_invitations(start, limit).await
279    }
280
281    /// Reply to an invitation.
282    pub async fn reply_invitation(
283        &self,
284        invitation_entity_urn: &str,
285        invitation_shared_secret: &str,
286        action: &str,
287    ) -> Result<bool, LinkedinError> {
288        self.inner
289            .reply_invitation(invitation_entity_urn, invitation_shared_secret, action)
290            .await
291    }
292
293    /// Get current user profile.
294    pub async fn get_user_profile(&self) -> Result<Value, LinkedinError> {
295        self.inner.get_user_profile().await
296    }
297
298    /// Stub people search with query.
299    pub async fn stub_people_search(
300        &self,
301        query: &str,
302        count: usize,
303        start: usize,
304    ) -> Result<Value, LinkedinError> {
305        self.inner.stub_people_search(query, count, start).await
306    }
307}