Crate mini_exercism

source ·
Expand description

A lightweight crate to interact with the Exercism website’s APIs.

§TOC

§What is Exercism?

Exercism is a free, not-for-profit platform to learn new programming languages. It supports a web editor for solving exercises, mentoring with real humans and a lot more. For more information, see its about page.

§Installing

Add mini_exercism to your dependencies:

[dependencies]
mini_exercism = "3.0.0"

or by running:

cargo add mini_exercism

§API clients

To interact with an Exercism API, you can simply use one of the provided API clients. Each API has its own client:

To create a client, either use its new method to create a default instance, or use its builder method to construct one:

use mini_exercism::api;

fn get_default_client() -> anyhow::Result<api::v2::Client> {
    Ok(api::v2::Client::new()?)
}

fn get_custom_client() -> anyhow::Result<api::v2::Client> {
    let mut builder = api::v2::Client::builder();
    // ... customize API client with builder ...

    Ok(builder.build()?)
}

§Async methods

The client methods used to query the APIs for information are async. As such, in order to call them, you will need to use the await keyword. For more information on async programming in Rust, see Asynchronous Programming in Rust.

Asynchronous programming in Rust requires an async runtime. Runtimes handle the execution of asynchronous code and the callbacks.

One popular async runtime is Tokio. It offers a simple way to write programs that support asynchronous code. You can use the #[tokio::main] attribute to make your main function async, allowing the use of await:

use mini_exercism::api;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let client = api::v2::Client::new()?;
    let tracks = client.get_tracks(None).await?;
    // ...

    Ok(())
}

Tokio offers many customization options; see the docs for more details.

§Example

use mini_exercism::api;

async fn print_language_tracks() -> anyhow::Result<()> {
    let client = api::v2::Client::new()?;

    let tracks = client.get_tracks(None).await?.tracks;
    for track in &tracks {
        println!("Exercism language track: {}", track.title);
    }

    Ok(())
}

async fn print_solutions(track: &str) -> anyhow::Result<()> {
    let client = api::v2::Client::new()?;

    let solutions = client.get_exercises(track, None).await?.solutions;
    for solution in &solutions {
        println!(
            "Solution for exercise {}, public URL: {}",
            solution.exercise.title, solution.public_url,
        );
    }

    Ok(())
}

§Credentials

API clients use Credentials to perform requests as a specific user. Some requests can work both anonymously and authenticated (behaving differently depending on which is used), others require authentication to work.

Credentials use Exercism API tokens to identify a user. This is the token that is used for the Exercism CLI application. It can be fetched from the Exercism’s user settings page.

To pass Credentials to an API client, use its builder:

use mini_exercism::api;
use mini_exercism::core::Credentials;

fn get_api_client() -> anyhow::Result<api::v2::Client> {
    let credentials = Credentials::from_api_token("SOME_API_TOKEN");
    Ok(api::v2::Client::builder()
        .credentials(credentials)
        .build()?)
}

§CLI credentials

This crate provides a helper function to fetch the Credentials used by the currently-installed Exercism CLI application. In order to use this, you need to enable the cli feature:

[dependencies]
mini_exercism = { version = "3.0.0", features = ["cli"] }

Then, you can fetch CLI credentials and use them to perform API requests. Note that it’s possible for the method to fail to find credentials if the CLI is not installed, for instance.

use mini_exercism::api;

fn get_api_client() -> anyhow::Result<api::v2::Client> {
    let mut client_builder = api::v2::Client::builder();

    #[cfg(feature = "cli")]
    if let Ok(credentials) = mini_exercism::cli::get_cli_credentials() {
        client_builder.credentials(credentials);
    } else {
        // Find some other way to fetch credentials, or perform queries anonymously
    }

    Ok(client_builder.build()?)
}

§Custom HTTP client

Internally, mini_exercism uses the reqwest library to perform HTTP calls. Unless overridden, API clients will create a default HTTP client. If you need to customize the behavior of the HTTP client, you can use the API client’s builder to specify a different HTTP client:

use mini_exercism::api;

fn get_api_client() -> anyhow::Result<api::v2::Client> {
    let http_client_builder = reqwest::Client::builder();
    // ... customize HTTP client with `http_client_builder` here ...
    let http_client = http_client_builder.build()?;

    Ok(api::v2::Client::builder()
        .http_client(http_client)
        .build()?)
}

// Another possible way:
fn get_api_client_too() -> anyhow::Result<api::v2::Client> {
    Ok(api::v2::Client::builder()
        .build_http_client(|builder| {
            // ... customize HTTP client with `builder` here ...
            builder
        })
        .build()?)
}

§Crate status

Currently, this crate is a bit minimalistic and does not implement all the Exercism API endpoints. To suggest new endpoints to add, you can enter an issue. Or, even better, don’t hesitate to submit a pull request! 😁

§Minimum Rust version

mini_exercism currently builds on Rust 1.64 or newer.

Re-exports§

  • pub use crate::core::Error;
  • pub use crate::core::Result;

Modules§