mod app;
mod args;
mod argument_schema;
mod cache;
mod configuration;
mod custom_command_definition_finder;
mod diff;
mod formatter;
mod keyword_preprocessor;
mod node;
mod parser;
mod runner;
mod sanity_checker;
mod two_words_keyword_isolator;
mod utils;
mod warning_sink;

use pyo3::pymodule;

#[pymodule]
mod gersemi_rust_backend {
    use crate::argument_schema::CommandSchemas;
    use crate::parser::{Error, Parser};
    use crate::sanity_checker::check_equivalence;
    use pyo3::pyfunction;
    use std::path::PathBuf;

    #[pyfunction]
    #[allow(clippy::needless_pass_by_value)]
    fn validate(text: String) -> Result<(), Error> {
        let schemas = CommandSchemas::default();
        let parser = Parser::new(&text, &schemas);
        parser.start().and(Ok(()))
    }

    #[pyfunction]
    #[allow(clippy::needless_pass_by_value)]
    fn check_code_equivalence(before: String, after: String) -> Result<bool, Error> {
        let schemas = CommandSchemas::default();
        let before_parser = Parser::new(&before, &schemas);
        let after_parser = Parser::new(&after, &schemas);

        Ok(check_equivalence(
            before_parser.start()?,
            after_parser.start()?,
        ))
    }

    #[pymodule_export]
    use crate::formatter::Formatter;

    #[pymodule_export]
    use crate::app::App;

    #[pyfunction]
    pub fn warn(s: String) {
        crate::warning_sink::warn(s);
    }

    #[pyfunction]
    fn version() -> &'static str {
        static RESULT: &str = env!("CARGO_VERSION");
        RESULT
    }

    #[pyfunction]
    pub fn max_number_of_workers() -> usize {
        match std::thread::available_parallelism() {
            Err(_) => 1,
            Ok(n) => n.get(),
        }
    }

    #[pyfunction]
    #[allow(clippy::needless_pass_by_value)]
    pub fn user_cache_dir(appname: String, version: String) -> Option<PathBuf> {
        dirs::cache_dir().and_then(|p| {
            let p = p.join(appname).join(version);
            if std::fs::create_dir_all(&p).is_ok() {
                Some(p)
            } else {
                None
            }
        })
    }
}
