linkedin_api/
types.rs

1use serde::{Deserialize, Deserializer, Serialize, Serializer};
2use serde_json::Value;
3use time::Month;
4use url::Url;
5
6pub struct Identity {
7    pub authentication_token: String,
8    pub session_cookie: String,
9}
10
11/// Parameters for people search.
12#[derive(Debug, Clone, Default)]
13pub struct SearchPeopleParams {
14    pub keywords: Option<String>,
15    pub connection_of: Option<String>,
16    pub network_depth: Option<String>,
17    pub current_company: Option<Vec<String>>,
18    pub past_companies: Option<Vec<String>>,
19    pub nonprofit_interests: Option<Vec<String>>,
20    pub profile_languages: Option<Vec<String>>,
21    pub regions: Option<Vec<String>>,
22    pub industries: Option<Vec<String>>,
23    pub schools: Option<Vec<String>>,
24    pub include_private_profiles: bool,
25    pub limit: Option<usize>,
26}
27
28/// Strongly-typed struct for profiles.
29#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
30#[serde(rename_all = "camelCase")]
31pub struct Profile {
32    #[serde(rename = "entityUrn")]
33    pub entity_urn: Option<String>,
34
35    pub first_name: Option<String>,
36    pub last_name: Option<String>,
37    pub headline: Option<String>,
38    pub summary: Option<String>,
39    pub industry_name: Option<String>,
40    #[serde(rename = "industryUrn")]
41    pub industry_urn: Option<String>,
42
43    pub geo_country_name: Option<String>,
44    #[serde(rename = "geoCountryUrn")]
45    pub geo_country_urn: Option<String>,
46    #[serde(rename = "geoLocationName")]
47    pub geo_location_name: Option<String>,
48    #[serde(rename = "geoLocationBackfilled")]
49    pub geo_location_backfilled: Option<bool>,
50
51    pub address: Option<String>,
52    pub birth_date: Option<BirthDate>,
53
54    pub default_locale: Option<Locale>,
55    pub supported_locales: Option<Vec<Locale>>,
56
57    pub location: Option<Location>,
58    #[serde(rename = "locationName")]
59    pub location_name: Option<String>,
60
61    #[serde(rename = "miniProfile")]
62    pub mini_profile: Option<MiniProfile>,
63
64    pub profile_picture: Option<ProfilePicture>,
65    #[serde(rename = "profilePictureOriginalImage")]
66    pub profile_picture_original_image: Option<VectorImageContainer>,
67
68    #[serde(rename = "showEducationOnProfileTopCard")]
69    pub show_education_on_profile_top_card: Option<bool>,
70    pub student: Option<bool>,
71
72    #[serde(rename = "versionTag")]
73    pub version_tag: Option<String>,
74
75    #[serde(skip)]
76    pub profile_id: String, // extracted from entityUrn with get_id_from_urn
77
78    #[serde(skip)]
79    pub experience: Vec<Experience>,
80
81    #[serde(skip)]
82    pub education: Vec<Education>,
83
84    #[serde(skip)]
85    pub skills: Vec<Skill>,
86
87    #[serde(skip)]
88    pub contact: ContactInfo,
89}
90
91#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
92#[serde(rename_all = "camelCase")]
93pub struct BirthDate {
94    pub day: Option<u32>,
95    pub month: Option<u32>,
96    pub year: Option<u32>,
97}
98
99#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
100#[serde(rename_all = "camelCase")]
101pub struct Locale {
102    pub country: Option<String>,
103    pub language: Option<String>,
104}
105
106#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]
107#[serde(rename_all = "camelCase")]
108pub struct Location {
109    pub basic_location: Option<BasicLocation>,
110}
111
112#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]
113#[serde(rename_all = "camelCase")]
114pub struct BasicLocation {
115    pub country_code: Option<String>,
116    pub postal_code: Option<String>,
117}
118
119#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]
120#[serde(rename_all = "camelCase")]
121pub struct MiniProfile {
122    #[serde(rename = "dashEntityUrn")]
123    pub dash_entity_urn: Option<String>,
124    #[serde(rename = "entityUrn")]
125    pub entity_urn: Option<String>,
126    #[serde(rename = "objectUrn")]
127    pub object_urn: Option<String>,
128    pub public_identifier: Option<String>,
129    pub first_name: Option<String>,
130    pub last_name: Option<String>,
131    pub occupation: Option<String>,
132    pub tracking_id: Option<String>,
133    pub picture: Option<VectorImageContainer>,
134}
135
136#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
137#[serde(rename_all = "camelCase")]
138pub struct ProfilePicture {
139    pub display_image: Option<String>,
140    pub original_image: Option<String>,
141    pub photo_filter_edit_info: Option<PhotoFilterEditInfo>,
142}
143
144#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]
145#[serde(rename_all = "camelCase")]
146pub struct PhotoFilterEditInfo {
147    pub top_left: Option<Point>,
148    pub top_right: Option<Point>,
149    pub bottom_left: Option<Point>,
150    pub bottom_right: Option<Point>,
151    pub brightness: Option<f32>,
152    pub contrast: Option<f32>,
153    pub saturation: Option<f32>,
154    pub vignette: Option<f32>,
155    pub photo_filter_type: Option<String>,
156}
157
158#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]
159#[serde(rename_all = "camelCase")]
160pub struct Point {
161    pub x: f32,
162    pub y: f32,
163}
164
165#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]
166#[serde(rename_all = "camelCase")]
167pub struct VectorImageContainer {
168    #[serde(rename = "com.linkedin.common.VectorImage")]
169    pub vector_image: Option<VectorImage>,
170}
171
172#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]
173#[serde(rename_all = "camelCase")]
174pub struct VectorImage {
175    pub root_url: Option<String>,
176    pub artifacts: Vec<ImageArtifact>,
177}
178
179#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]
180#[serde(rename_all = "camelCase")]
181pub struct ImageArtifact {
182    pub height: u32,
183    pub width: u32,
184    pub expires_at: Option<u64>,
185    pub file_identifying_url_path_segment: Option<String>,
186}
187
188/// An experience entry
189#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
190#[serde(rename_all = "camelCase")]
191pub struct Experience {
192    pub title: Option<String>,
193    #[serde(rename = "companyName")]
194    pub company_name: Option<String>,
195    #[serde(rename = "companyLogoUrl")]
196    pub company_logo_url: Option<String>,
197    pub role: Option<String>,
198    #[serde(rename = "timePeriod")]
199    pub time_period: Option<TimePeriod>,
200    pub description: Option<String>,
201}
202
203#[derive(Debug, PartialEq, Serialize, Clone, Deserialize)]
204pub struct TimePeriod {
205    #[serde(rename = "startDate")]
206    pub start_date: YearMonth,
207    #[serde(rename = "endDate")]
208    pub end_date: YearMonth,
209}
210
211#[derive(Debug, PartialEq, Clone)]
212pub struct YearMonth {
213    pub year: i32,
214    pub month: Month,
215}
216
217impl Serialize for YearMonth {
218    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
219    where
220        S: Serializer,
221    {
222        use serde::ser::SerializeStruct;
223
224        let mut s = serializer.serialize_struct("YearMonth", 2)?;
225        s.serialize_field("year", &self.year)?;
226        s.serialize_field("month", &(self.month as u8))?;
227        s.end()
228    }
229}
230
231impl<'de> Deserialize<'de> for YearMonth {
232    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
233    where
234        D: Deserializer<'de>,
235    {
236        #[derive(Deserialize)]
237        struct Raw {
238            year: i32,
239            month: u8,
240        }
241
242        let raw = Raw::deserialize(deserializer)?;
243        let month = Month::try_from(raw.month)
244            .map_err(|_| serde::de::Error::custom("invalid month value"))?;
245
246        Ok(YearMonth {
247            year: raw.year,
248            month,
249        })
250    }
251}
252
253/// An education entry
254#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]
255#[serde(rename_all = "camelCase")]
256pub struct Education {
257    pub school_name: Option<String>,
258    pub degree: Option<String>,
259    pub field_of_study: Option<String>,
260}
261
262/// A skill entry
263#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
264#[serde(rename_all = "camelCase")]
265pub struct Skill {
266    pub name: String,
267}
268
269#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize)]
270pub struct ContactInfo {
271    #[serde(rename = "emailAddress")]
272    pub email_address: Option<String>,
273    pub websites: Vec<Website>,
274    pub twitter: Vec<String>,
275    #[serde(rename = "phoneNumbers")]
276    pub phone_numbers: Vec<String>,
277    pub birthdate: Option<String>,
278    pub ims: Option<Vec<Value>>,
279}
280
281#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
282pub struct Website {
283    pub url: String,
284    pub label: Option<String>,
285}
286
287#[derive(Debug, Clone, Serialize, Deserialize)]
288pub struct Connection {
289    pub urn_id: String,
290    pub public_id: String,
291    pub distance: String,
292}
293
294#[derive(Debug, Clone, Serialize, Deserialize)]
295pub struct Conversation {
296    pub id: String,
297}
298
299#[derive(Debug, Clone, Serialize, Deserialize)]
300pub struct ConversationDetails {
301    pub id: String,
302}
303
304#[derive(Debug, Clone, Serialize, Deserialize)]
305pub struct MemberBadges {
306    pub premium: bool,
307    pub open_link: bool,
308    pub influencer: bool,
309    pub job_seeker: bool,
310}
311
312#[derive(Debug, Clone, Serialize, Deserialize)]
313pub struct NetworkInfo {
314    pub followers_count: u64,
315}
316
317#[derive(Debug, Clone, Serialize, Deserialize)]
318pub struct School {
319    pub name: String,
320}
321
322#[derive(Debug, Clone, Serialize, Deserialize)]
323pub struct Company {
324    pub name: String,
325}
326
327#[derive(Debug, Clone, Serialize, Deserialize)]
328pub struct PersonSearchResult {
329    pub urn_id: String,
330    pub public_id: String,
331    pub distance: String,
332}
333
334#[derive(Debug, Clone, Serialize, Deserialize)]
335pub struct Invitation {
336    #[serde(rename = "entityUrn")]
337    pub entity_urn: String,
338    #[serde(rename = "sharedSecret")]
339    pub shared_secret: String,
340}
341
342pub struct UniformResourceName {
343    pub namespace: String, // the context of the id
344    pub id: String,
345}
346impl Profile {
347    /// Helper method to get full name
348    pub fn get_full_name(&self) -> Option<String> {
349        match (&self.first_name, &self.last_name) {
350            (Some(first), Some(last)) => Some(format!("{} {}", first, last)),
351            (Some(first), None) => Some(first.clone()),
352            (None, Some(last)) => Some(last.clone()),
353            (None, None) => None,
354        }
355    }
356
357    /// Helper method to get profile image URL
358    pub fn get_profile_image(&self) -> Option<Url> {
359        self.profile_picture_original_image
360            .as_ref()
361            .and_then(|container| container.vector_image.as_ref())
362            .and_then(|vector_image| {
363                if let (Some(root_url), Some(artifact)) =
364                    (&vector_image.root_url, vector_image.artifacts.first())
365                {
366                    let full_url = format!(
367                        "{}{}",
368                        root_url,
369                        artifact
370                            .file_identifying_url_path_segment
371                            .as_deref()
372                            .unwrap_or("")
373                    );
374                    Url::parse(&full_url).ok()
375                } else {
376                    None
377                }
378            })
379    }
380}