pub struct GithubClient {
http: Client,
token: Option<SecretString>,
}Expand description
Thin HTTP wrapper around the GitHub REST API.
Handles TLS setup, authentication headers, and base URL construction.
Consumers (e.g. OtaClient) call the specific
API methods they need.
§Examples
use cadmus_core::github::GithubClient;
// Unauthenticated client for public endpoints
let client = GithubClient::new(None).expect("failed to build client");use cadmus_core::github::GithubClient;
use secrecy::SecretString;
// Authenticated client for private/token-gated endpoints
let token = SecretString::from("ghp_…".to_owned());
let client = GithubClient::new(Some(token)).expect("failed to build client");Fields§
§http: Client§token: Option<SecretString>Implementations§
Source§impl GithubClient
impl GithubClient
Sourcepub fn new(token: Option<SecretString>) -> Result<Self, GithubError>
pub fn new(token: Option<SecretString>) -> Result<Self, GithubError>
Creates a new client with optional GitHub token authentication.
Uses webpki-roots certificates for TLS — no system cert store
required, which matters on Kobo devices that ship without a CA bundle.
§Errors
Returns an error if the underlying HTTP client fails to build.
§Examples
use cadmus_core::github::GithubClient;
let client = GithubClient::new(None).expect("failed to build client");Sourcepub fn get(&self, url: &str) -> RequestBuilder
pub fn get(&self, url: &str) -> RequestBuilder
Returns a GET request builder with the Authorization header set if a
token is present.
Sourcepub fn post(&self, url: &str) -> RequestBuilder
pub fn post(&self, url: &str) -> RequestBuilder
Returns a POST request builder with the Authorization header set if a
token is present.
Sourcepub fn get_unauthenticated(&self, url: &str) -> RequestBuilder
pub fn get_unauthenticated(&self, url: &str) -> RequestBuilder
Returns a GET request builder without any Authorization header.
Used for public URLs (e.g. release asset downloads) where sending a token would cause GitHub to reject the request with a 401.
fn with_auth(&self, builder: RequestBuilder) -> RequestBuilder
Sourcepub fn initiate_device_flow(&self) -> Result<DeviceCodeResponse, String>
pub fn initiate_device_flow(&self) -> Result<DeviceCodeResponse, String>
Initiates GitHub device flow authentication.
POSTs to /login/device/code to obtain a short user code and the
verification URL. The caller must display these to the user and then
call poll_device_token repeatedly until
authorization completes or the code expires.
The required OAuth scopes are derived from REQUIRED_SCOPES so this
method and verify_token_scopes always
stay in sync.
§Errors
Returns an error if the network request fails or GitHub returns a non-2xx status.
§Examples
use cadmus_core::github::GithubClient;
let client = GithubClient::new(None).expect("failed to build client");
let response = client.initiate_device_flow().expect("device flow failed");
println!("Go to {} and enter {}", response.verification_uri, response.user_code);Sourcepub fn verify_token_scopes(&self) -> Result<(), VerifyScopesError>
pub fn verify_token_scopes(&self) -> Result<(), VerifyScopesError>
Verifies that the current token has all scopes listed in
REQUIRED_SCOPES.
Makes a lightweight GET /user request and reads the
X-OAuth-Scopes response header, which GitHub includes on every
authenticated API call. Returns Ok(()) if all required scopes are
present, or Err(missing) listing the absent scope names.
Call this once before starting a download to catch stale tokens early, rather than failing mid-download with confusing 403.
§Errors
Returns Err if the network request fails, GitHub returns a non-2xx
status, or one or more required scopes are absent.
§Examples
use cadmus_core::github::GithubClient;
use secrecy::SecretString;
let token = SecretString::from("ghp_…".to_owned());
let client = GithubClient::new(Some(token)).expect("failed to build client");
match client.verify_token_scopes() {
Ok(()) => println!("Token has all required scopes"),
Err(e) => println!("Error: {}", e),
}Sourcepub fn poll_device_token(
&self,
device_code: &str,
) -> Result<TokenPollResult, String>
pub fn poll_device_token( &self, device_code: &str, ) -> Result<TokenPollResult, String>
Polls GitHub once to check if the user has authorized the device.
Must be called at least interval seconds apart (from the
DeviceCodeResponse). GitHub returns slow_down if polled too
frequently; the caller must add 5 seconds to the interval before the
next attempt.
§Arguments
device_code- Thedevice_codefrominitiate_device_flow
§Errors
Returns an error if the network request fails.
§Examples
use cadmus_core::github::GithubClient;
use cadmus_core::github::TokenPollResult;
use std::time::Duration;
let client = GithubClient::new(None).expect("failed to build client");
let flow = client.initiate_device_flow().expect("device flow failed");
loop {
std::thread::sleep(Duration::from_secs(flow.interval));
match client.poll_device_token(&flow.device_code).expect("poll failed") {
TokenPollResult::Complete(token) => break,
TokenPollResult::Pending => continue,
_ => break,
}
}Auto Trait Implementations§
impl Freeze for GithubClient
impl !RefUnwindSafe for GithubClient
impl Send for GithubClient
impl Sync for GithubClient
impl Unpin for GithubClient
impl !UnwindSafe for GithubClient
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.§impl<T> DowncastSend for T
impl<T> DowncastSend for T
§impl<T> DowncastSync for T
impl<T> DowncastSync for T
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more