Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added basic support for getting user profiles #616

Merged
merged 1 commit into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/api/users.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
//! The users API.

mod follow;
mod user_repos;

use self::follow::{ListUserFollowerBuilder, ListUserFollowingBuilder};
pub use self::user_repos::ListUserReposBuilder;
use crate::Octocrab;

Expand All @@ -15,6 +17,24 @@ impl<'octo> UserHandler<'octo> {
Self { crab, user }
}

/// Get this users profile info
pub async fn profile(&self) -> crate::Result<crate::models::UserProfile> {
// build the route to get info on this user
let route = format!("/users/{}", self.user);
// get info on the specified user
self.crab.get(route, None::<&()>).await
}

/// List this users that follow this user
pub fn followers(&self) -> ListUserFollowerBuilder {
ListUserFollowerBuilder::new(self)
}

/// List this user is following
pub fn following(&self) -> ListUserFollowingBuilder {
ListUserFollowingBuilder::new(self)
}

pub fn repos(&self) -> ListUserReposBuilder<'_, '_> {
ListUserReposBuilder::new(self)
}
Expand Down
86 changes: 86 additions & 0 deletions src/api/users/follow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use crate::api::users::UserHandler;
use crate::Page;

/// A builder pattern struct for listing a users followers
///
/// created by [`UserHandler::followers`]
#[derive(serde::Serialize)]
pub struct ListUserFollowerBuilder<'octo, 'r> {
#[serde(skip)]
handler: &'r UserHandler<'octo>,
#[serde(skip_serializing_if = "Option::is_none")]
per_page: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
page: Option<u32>,
}

impl<'octo, 'b> ListUserFollowerBuilder<'octo, 'b> {
pub(crate) fn new(handler: &'b UserHandler<'octo>) -> Self {
Self {
handler,
per_page: None,
page: None,
}
}

/// Results per page (max 100).
pub fn per_page(mut self, per_page: impl Into<u8>) -> Self {
self.per_page = Some(per_page.into());
self
}

/// Page number of the results to fetch.
pub fn page(mut self, page: impl Into<u32>) -> Self {
self.page = Some(page.into());
self
}

/// Sends the actual request.
pub async fn send(self) -> crate::Result<Page<crate::models::Follower>> {
// build the route to get this users followers
let route = format!("/users/{}/followers", self.handler.user);
self.handler.crab.get(route, Some(&self)).await
}
}

/// A builder pattern struct for listing who a user is following
///
/// created by [`UserHandler::following`]
#[derive(serde::Serialize)]
pub struct ListUserFollowingBuilder<'octo, 'r> {
#[serde(skip)]
handler: &'r UserHandler<'octo>,
#[serde(skip_serializing_if = "Option::is_none")]
per_page: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
page: Option<u32>,
}

impl<'octo, 'b> ListUserFollowingBuilder<'octo, 'b> {
pub(crate) fn new(handler: &'b UserHandler<'octo>) -> Self {
Self {
handler,
per_page: None,
page: None,
}
}

/// Results per page (max 100).
pub fn per_page(mut self, per_page: impl Into<u8>) -> Self {
self.per_page = Some(per_page.into());
self
}

/// Page number of the results to fetch.
pub fn page(mut self, page: impl Into<u32>) -> Self {
self.page = Some(page.into());
self
}

/// Sends the actual request.
pub async fn send(self) -> crate::Result<Page<crate::models::Followee>> {
// build the route to get this users followers
let route = format!("/users/{}/following", self.handler.user);
self.handler.crab.get(route, Some(&self)).await
}
}
105 changes: 105 additions & 0 deletions src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,111 @@ pub struct Author {
pub email: Option<String>,
}

/// If a string is empty then deserialize it as none
fn empty_string_is_none<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
D: Deserializer<'de>,
{
// try to deserialize our input string
let cast = String::deserialize(deserializer)?;
// if this string is empty then return None
if cast.is_empty() {
Ok(None)
} else {
Ok(Some(cast))
}
}

/// The full profile for a user
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct UserProfile {
pub login: String,
pub id: UserId,
pub node_id: String,
pub avatar_url: Url,
pub gravatar_id: String,
pub url: Url,
pub html_url: Url,
pub followers_url: Url,
pub following_url: Url,
pub gists_url: Url,
pub starred_url: Url,
pub subscriptions_url: Url,
pub organizations_url: Url,
pub repos_url: Url,
pub events_url: Url,
pub received_events_url: Url,
pub r#type: String,
pub site_admin: bool,
pub name: String,
pub company: Option<String>,
#[serde(deserialize_with = "empty_string_is_none")]
pub blog: Option<String>,
pub location: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
pub hireable: Option<bool>,
pub bio: Option<String>,
pub twitter_username: Option<String>,
pub public_repos: u64,
pub public_gists: u64,
pub followers: u64,
pub following: u64,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}

/// A user that is following another user
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Follower {
pub login: String,
pub id: UserId,
pub node_id: String,
pub avatar_url: Url,
pub gravatar_id: String,
pub url: Url,
pub html_url: Url,
pub followers_url: Url,
pub following_url: Url,
pub gists_url: Url,
pub starred_url: Url,
pub subscriptions_url: Url,
pub organizations_url: Url,
pub repos_url: Url,
pub events_url: Url,
pub received_events_url: Url,
pub r#type: String,
pub site_admin: bool,
pub patch_url: Option<String>,
}

/// A user that is being followed by another user
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Followee {
pub login: String,
pub id: UserId,
pub node_id: String,
pub avatar_url: Url,
pub gravatar_id: String,
pub url: Url,
pub html_url: Url,
pub followers_url: Url,
pub following_url: Url,
pub gists_url: Url,
pub starred_url: Url,
pub subscriptions_url: Url,
pub organizations_url: Url,
pub repos_url: Url,
pub events_url: Url,
pub received_events_url: Url,
pub r#type: String,
pub site_admin: bool,
pub patch_url: Option<String>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[non_exhaustive]
Expand Down
Loading