//! Macros for the repo_path crate.

use proc_macro::TokenStream;
use syn::ExprLit;

use crate::implementation::custom_base_path::CustomPath;

mod implementation;
mod path;


/// Easily construct a [std::path::PathBuf] for static paths in your repository.
///
/// ```
///# use repo_path_macro::repo_path;
///
/// let readme = repo_path!("README.md"); //top-level README.md file
/// let asset = repo_path!("assets/file.txt"); //specify directories with slashes
/// ```
///
/// The macro checks the paths during compilation and will throw an error,
/// if it's not pointing to anything, for example when the file got
/// renamed or moved:
///
/// ```compile_fail
///# use repo_path_macro::repo_path;
/// let nonexistent = repo_path!("non/existent/path.rs");
/// ```
///
/// If no path is passed, the root of the repository is returned:
/// ```
///# use repo_path_macro::repo_path;
/// let repo_root = repo_path!();
/// ```
#[proc_macro]
pub fn repo_path(input: TokenStream) -> TokenStream {
    let relative =
        if input.is_empty() {
            None
        } else {
            Some(syn::parse_macro_input!(input as ExprLit))
        };

    let expanded = implementation::repo_path::repo_path(relative)
        .unwrap_or_else(|cause| cause.to_compile_error());

    TokenStream::from(expanded)
}


/// Include the contents of a file in your repository as a string into the source code.
///
/// This behaves like [std::include_str], except the path you specify is resolved
/// relative to the repository root, rather than the current source code file.
///
/// ```
///# use repo_path_macro::repo_include_str;
/// let result = repo_include_str!("assets/file.txt");
/// assert_eq!(result, "Sample file referenced in documentation tests.\n");
/// assert_eq!(result, include_str!("../../assets/file.txt"));
/// ```
#[proc_macro]
pub fn repo_include_str(input: TokenStream) -> TokenStream {
    let input = syn::parse_macro_input!(input as ExprLit);

    let expanded = implementation::repo_include_str::repo_include_str(input)
        .unwrap_or_else(|cause| cause.to_compile_error());

    TokenStream::from(expanded)
}


/// Construct a [std::path::PathBuf] for static paths in your repository with a custom base path.
/// This macro is intended for use in `custom_base_path_macro!()`.
///
/// ```
/// use repo_path_macro::custom_base_path;
///
/// let readme = custom_base_path!("assets/", "file.txt");
/// ```
///
/// The macro checks the paths during compilation and will throw an error,
/// if it's not pointing to anything, for example when the file got
/// renamed or moved:
///
/// ```compile_fail
///# use repo_path_macro::custom_base_path;
/// let nonexistent = custom_base_path!("assets/", "non/existent/path.rs");
/// ```
///
/// If no path is passed, the base path is returned:
/// ```
///# use repo_path_macro::custom_base_path;
/// let tests_dir = custom_base_path!("assets/");
/// ```
#[proc_macro]
pub fn custom_base_path(input: TokenStream) -> TokenStream {
    let input = syn::parse_macro_input!(input as CustomPath);

    let expanded = implementation::custom_base_path::custom_base_path(input)
        .unwrap_or_else(|cause| cause.to_compile_error());

    TokenStream::from(expanded)
}
