GithubClient

Struct GithubClient 

Source
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

Source

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");
Source

pub fn get(&self, url: &str) -> RequestBuilder

Returns a GET request builder with the Authorization header set if a token is present.

Source

pub fn post(&self, url: &str) -> RequestBuilder

Returns a POST request builder with the Authorization header set if a token is present.

Source

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.

Source

fn with_auth(&self, builder: RequestBuilder) -> RequestBuilder

Source

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);
Source

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),
}
Source

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
§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§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> Downcast for T
where T: Any,

§

fn into_any(self: Box<T>) -> Box<dyn Any>

Converts 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>

Converts 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)

Converts &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)

Converts &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
where T: Any + Send,

§

fn into_any_send(self: Box<T>) -> Box<dyn Any + Send>

Converts Box<Trait> (where Trait: DowncastSend) to Box<dyn Any + Send>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

§

fn into_any_sync(self: Box<T>) -> Box<dyn Any + Sync + Send>

Converts Box<Trait> (where Trait: DowncastSync) to Box<dyn Any + Send + Sync>, which can then be downcast into Box<ConcreteType> where ConcreteType implements Trait.
§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Converts Arc<Trait> (where Trait: DowncastSync) to Arc<Any>, which can then be downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
§

impl<T> PolicyExt for T
where T: ?Sized,

§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] only if self and other return Action::Follow. Read more
§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns [Action::Follow] if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more