glowpub 0.5.0

A glowfic to epub converter.
Documentation
use std::{ops::Range, time::Duration};

use rand::{Rng, distributions::Uniform};
use serde_json::Value;

use crate::{Board, Post, utils::http_client};

use super::super::{BoardPosts, Replies};

#[tokio::test]
#[ignore]
async fn gen_boards_file() {
    let urls: Vec<String> = iter_rng(0..4_000).take(100).map(Board::url).collect();

    let responses: Vec<Value> = to_responses(&urls).await;

    save_to_file(&responses, "boards");
}

#[tokio::test]
#[ignore]
async fn gen_posts_file() {
    let urls: Vec<String> = iter_rng(0..4_000).take(100).map(Post::url).collect();

    let responses: Vec<Value> = to_responses(&urls).await;

    save_to_file(&responses, "posts");
}

#[tokio::test]
#[ignore]
async fn gen_replies_file() {
    let urls: Vec<String> = iter_rng(0..4_000)
        .take(50)
        .flat_map(|id| (0..5).map(|page| (id, page)).collect::<Vec<_>>())
        .map(|(id, page)| Replies::page_url(id, page))
        .collect();

    let responses: Vec<Value> = to_responses(&urls).await;

    save_to_file(&responses, "replies");
}

#[tokio::test]
#[ignore]
async fn gen_board_posts_file() {
    let urls: Vec<String> = iter_rng(0..500)
        .take(50)
        .flat_map(|id| (0..5).map(|page| (id, page)).collect::<Vec<_>>())
        .map(|(id, page)| BoardPosts::page_url(id, page))
        .collect();

    let responses: Vec<Value> = to_responses(&urls).await;

    save_to_file(&responses, "board_posts");
}

pub async fn to_responses(urls: &[String]) -> Vec<Value> {
    let mut responses: Vec<Value> = vec![];

    for url in urls {
        responses.push(loop {
            let response = http_client().get(url).send().await.unwrap();
            if !response.status().is_success() {
                println!("{:?} {}", response.status(), response.text().await.unwrap());
                tokio::time::sleep(Duration::from_secs(2 * 60)).await;
                continue;
            }
            break response.json().await.unwrap();
        });
    }

    responses
}

pub fn save_to_file(responses: &[Value], name: &str) {
    std::fs::write(
        format!("./src/api/tests/fixtures/api-{name}.json"),
        serde_json::to_string(responses).unwrap(),
    )
    .unwrap();
    std::fs::write(
        format!("./src/api/tests/fixtures/api-{name}-success.json"),
        serde_json::to_string(&only_successes(responses)).unwrap(),
    )
    .unwrap();
    std::fs::write(
        format!("./src/api/tests/fixtures/api-{name}-error.json"),
        serde_json::to_string(&only_errors(responses)).unwrap(),
    )
    .unwrap();
}

pub fn only_successes(responses: &[Value]) -> Vec<Value> {
    responses
        .iter()
        .filter(|response| !is_error(response))
        .cloned()
        .collect()
}

pub fn only_errors(responses: &[Value]) -> Vec<Value> {
    responses
        .iter()
        .filter(|response| is_error(response))
        .cloned()
        .collect()
}

pub fn is_error(response: &Value) -> bool {
    match response {
        Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => {
            panic!("{response:?}")
        }
        Value::Array(array) => {
            assert!(
                array
                    .iter()
                    .all(|v| !v.as_object().unwrap().contains_key("errors"))
            );

            false
        }
        Value::Object(object) => object.contains_key("errors"),
    }
}

fn iter_rng(range: Range<u64>) -> impl Iterator<Item = u64> {
    rand::thread_rng().sample_iter(Uniform::from(range))
}