repo_path_macro 1.2.0

Proc_macros for repo_path.
Documentation
use std::path::PathBuf;

use proc_macro::TokenStream;
use syn::ExprLit;
use token::CustomPath;

mod path;
mod token;


/// Easily construct a [std::path::PathBuf] for static paths in your repository.
///
/// ```
/// use repo_path::repo_path;
///
/// let readme = repo_path!("README.md"); //top-level README.md file
/// let ui_tests = repo_path!("tests/ui/"); //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::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::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 repo_dir = repo_path_lib::repo_dir();
    let result = parse_relative_path(repo_dir, relative.as_ref());

    token::output_result_as_tokenstream(result)
}


/// 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::custom_base_path;
///
/// let readme = custom_base_path!("tests/", "ui/");
/// ```
///
/// 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::custom_base_path;
/// let nonexistent = custom_base_path!("tests/", "non/existent/path.rs");
/// ```
///
/// If no path is passed, the base path is returned:
/// ```
///# use repo_path::custom_base_path;
/// let tests_dir = custom_base_path!("tests/");
/// ```
#[proc_macro]
pub fn custom_base_path(input: TokenStream) -> TokenStream {
    let CustomPath { base_path, relative_path } = syn::parse_macro_input!(input as CustomPath);

    let result = token::parse_path_from_expr_lit(&base_path)
        .and_then(|(base_path, _)| parse_relative_path(base_path, relative_path.as_ref()));

    token::output_result_as_tokenstream(result)
}


fn parse_relative_path(base_path: PathBuf, relative_path: Option<&ExprLit>) -> Result<PathBuf, syn::Error> {

    let relative_path = relative_path
        .map(token::parse_path_from_expr_lit)
        .transpose()?;

    let repo_dir = repo_path_lib::repo_dir();
    let path = path::construct_and_validate_path(&base_path, &repo_dir, relative_path)?;

    Ok(path)
}