wikijs 0.2.1

API bindings, CLI client and FUSE filesystem for Wiki.js written in Rust.
Documentation
use graphql_client::reqwest::post_graphql_blocking as post_graphql;
use reqwest::blocking::Client;
use serde::{Deserialize, Serialize};
use thiserror::Error;

use crate::common::{
    classify_response_error, classify_response_status_error, Boolean,
    KeyValuePair, KeyValuePairInput, KnownErrorCodes, ResponseStatus,
    UnknownError,
};

#[derive(Clone, Debug, Error, PartialEq)]
pub enum SearchError {
    #[error("An unexpected error occurred during search operation.")]
    SearchGenericError,
    #[error("Search Engine activation failed.")]
    SearchActivationFailed,
    #[error("Unknown response error code: {code}: {message}")]
    UnknownErrorCode { code: i64, message: String },
    #[error("Unknown response error: {message}")]
    UnknownErrorMessage { message: String },
    #[error("Unknown response error.")]
    UnknownError,
}

impl From<i64> for SearchError {
    fn from(code: i64) -> Self {
        match code {
            4001 => SearchError::SearchGenericError,
            4002 => SearchError::SearchActivationFailed,
            _ => SearchError::UnknownErrorCode {
                code,
                message: "Unknown error".to_string(),
            },
        }
    }
}

impl UnknownError for SearchError {
    fn unknown_error_code(code: i64, message: String) -> Self {
        SearchError::UnknownErrorCode { code, message }
    }
    fn unknown_error_message(message: String) -> Self {
        SearchError::UnknownErrorMessage { message }
    }
    fn unknown_error() -> Self {
        SearchError::UnknownError
    }
}

impl KnownErrorCodes for SearchError {
    fn known_error_codes() -> Vec<i64> {
        vec![4001, 4002]
    }

    fn is_known_error_code(code: i64) -> bool {
        (4001..=4002).contains(&code)
    }
}

#[derive(Clone, Deserialize)]
pub struct SearchEngine {
    #[serde(rename = "isEnabled")]
    pub is_enabled: Boolean,
    pub key: String,
    pub title: String,
    pub description: Option<String>,
    pub logo: Option<String>,
    pub website: Option<String>,
    #[serde(rename = "isAvailable")]
    pub is_available: Option<Boolean>,
    pub config: Option<Vec<Option<KeyValuePair>>>,
}

#[derive(Clone, Serialize)]
pub struct SearchEngineInput {
    #[serde(rename = "isEnabled")]
    pub is_enabled: Boolean,
    pub key: String,
    pub config: Option<Vec<Option<KeyValuePairInput>>>,
}

pub mod search_engine_list {
    use super::*;

    pub struct SearchEngineList;

    pub const OPERATION_NAME: &str = "SearchEngineList";
    pub const QUERY : & str = "query SearchEngineList($filter: String, $orderBy: String) {\n  search {\n    searchEngines(filter: $filter, orderBy: $orderBy) {\n      isEnabled\n      key\n      title\n      description\n      logo\n      website\n      isAvailable\n      config {\n        key\n        value\n      }\n    }\n  }\n}\n" ;

    #[derive(Serialize)]
    pub struct Variables {
        pub filter: Option<String>,
        #[serde(rename = "orderBy")]
        pub order_by: Option<String>,
    }

    impl Variables {}

    #[derive(Deserialize)]
    pub struct ResponseData {
        pub search: Option<SearchEngineListSearch>,
    }

    #[derive(Deserialize)]
    pub struct SearchEngineListSearch {
        #[serde(rename = "searchEngines")]
        pub search_engines: Option<Vec<Option<SearchEngine>>>,
    }

    impl graphql_client::GraphQLQuery for SearchEngineList {
        type Variables = Variables;
        type ResponseData = ResponseData;
        fn build_query(
            variables: Self::Variables,
        ) -> ::graphql_client::QueryBody<Self::Variables> {
            graphql_client::QueryBody {
                variables,
                query: QUERY,
                operation_name: OPERATION_NAME,
            }
        }
    }
}

pub fn search_engine_list(
    client: &Client,
    url: &str,
    filter: Option<String>,
    order_by: Option<String>,
) -> Result<Vec<SearchEngine>, SearchError> {
    let variables = search_engine_list::Variables { filter, order_by };
    let response = post_graphql::<search_engine_list::SearchEngineList, _>(
        client, url, variables,
    );
    if response.is_err() {
        return Err(SearchError::UnknownErrorMessage {
            message: response.err().unwrap().to_string(),
        });
    }
    let response_body = response.unwrap();
    if let Some(data) = response_body.data {
        if let Some(search) = data.search {
            if let Some(search_engines) = search.search_engines {
                return Ok(search_engines.into_iter().flatten().collect());
            }
        }
    }
    Err(classify_response_error::<SearchError>(response_body.errors))
}

pub mod search_engine_index_rebuild {
    use super::*;

    pub struct SearchEngineIndexRebuild;

    pub const OPERATION_NAME: &str = "SearchEngineIndexRebuild";
    pub const QUERY : & str = "mutation SearchEngineIndexRebuild {\n  search {\n    rebuildIndex {\n      responseResult {\n        succeeded\n        errorCode\n        slug\n        message\n      }\n    }\n  }\n}\n" ;

    #[derive(Serialize)]
    pub struct Variables;

    #[derive(Deserialize)]
    pub struct ResponseData {
        pub search: Option<Search>,
    }

    #[derive(Deserialize)]
    pub struct Search {
        #[serde(rename = "rebuildIndex")]
        pub rebuild_index: Option<RebuildIndex>,
    }

    #[derive(Deserialize)]
    pub struct RebuildIndex {
        #[serde(rename = "responseResult")]
        pub response_result: Option<ResponseStatus>,
    }

    impl graphql_client::GraphQLQuery for SearchEngineIndexRebuild {
        type Variables = Variables;
        type ResponseData = ResponseData;
        fn build_query(
            variables: Self::Variables,
        ) -> ::graphql_client::QueryBody<Self::Variables> {
            graphql_client::QueryBody {
                variables,
                query: QUERY,
                operation_name: OPERATION_NAME,
            }
        }
    }
}

pub fn search_engine_index_rebuild(
    client: &Client,
    url: &str,
) -> Result<(), SearchError> {
    let variables = search_engine_index_rebuild::Variables;
    let response = post_graphql::<
        search_engine_index_rebuild::SearchEngineIndexRebuild,
        _,
    >(client, url, variables);
    if response.is_err() {
        return Err(SearchError::UnknownErrorMessage {
            message: response.err().unwrap().to_string(),
        });
    }
    let response_body = response.unwrap();
    if let Some(data) = response_body.data {
        if let Some(search) = data.search {
            if let Some(rebuild_index) = search.rebuild_index {
                if let Some(response_result) = rebuild_index.response_result {
                    if response_result.succeeded {
                        return Ok(());
                    } else {
                        return Err(classify_response_status_error::<
                            SearchError,
                        >(response_result));
                    }
                }
            }
        }
    }
    Err(classify_response_error::<SearchError>(response_body.errors))
}

pub mod search_engine_update {
    use super::*;

    pub struct SearchEngineUpdate;

    pub const OPERATION_NAME: &str = "SearchEngineUpdate";
    pub const QUERY : & str = "mutation SearchEngineUpdate($engines: [SearchEngineInput]) {\n  search {\n    updateSearchEngines(engines: $engines) {\n      responseResult {\n        succeeded\n        errorCode\n        slug\n        message\n      }\n    }\n  }\n}\n" ;

    #[derive(Serialize)]
    pub struct Variables {
        pub engines: Option<Vec<Option<SearchEngineInput>>>,
    }

    impl Variables {}

    #[derive(Deserialize)]
    pub struct ResponseData {
        pub search: Option<Search>,
    }

    #[derive(Deserialize)]
    pub struct Search {
        #[serde(rename = "updateSearchEngines")]
        pub update_search_engines: Option<UpdateSearchEngines>,
    }

    #[derive(Deserialize)]
    pub struct UpdateSearchEngines {
        #[serde(rename = "responseResult")]
        pub response_result: Option<ResponseStatus>,
    }

    impl graphql_client::GraphQLQuery for SearchEngineUpdate {
        type Variables = Variables;
        type ResponseData = ResponseData;
        fn build_query(
            variables: Self::Variables,
        ) -> ::graphql_client::QueryBody<Self::Variables> {
            graphql_client::QueryBody {
                variables,
                query: QUERY,
                operation_name: OPERATION_NAME,
            }
        }
    }
}

pub fn search_engine_update(
    client: &Client,
    url: &str,
    engines: Vec<SearchEngineInput>,
) -> Result<(), SearchError> {
    let variables = search_engine_update::Variables {
        engines: Some(engines.into_iter().map(Some).collect()),
    };
    let response = post_graphql::<search_engine_update::SearchEngineUpdate, _>(
        client, url, variables,
    );
    if response.is_err() {
        return Err(SearchError::UnknownErrorMessage {
            message: response.err().unwrap().to_string(),
        });
    }
    let response_body = response.unwrap();
    if let Some(data) = response_body.data {
        if let Some(search) = data.search {
            if let Some(update_search_engines) = search.update_search_engines {
                if let Some(response_result) =
                    update_search_engines.response_result
                {
                    if response_result.succeeded {
                        return Ok(());
                    } else {
                        return Err(classify_response_status_error::<
                            SearchError,
                        >(response_result));
                    }
                }
            }
        }
    }
    Err(classify_response_error::<SearchError>(response_body.errors))
}