Skip to content

Commit

Permalink
feat(lookup): Add find reusing module logic
Browse files Browse the repository at this point in the history
Resolves #22.
Signed-off-by: Alexander Gil <[email protected]>
  • Loading branch information
pando85 committed Aug 15, 2024
1 parent ceeca01 commit 55e0787
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 35 deletions.
8 changes: 4 additions & 4 deletions mdbook_rash/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ fn replace_matches(captures: Vec<(Match, Option<String>, String)>, ch: &mut Chap
if capture.2 == "include_module_index" {
let mut indexes_vec = MODULES
.iter()
.map(|(name, _)| format!("- [{name}](./{name}.html)"))
.map(|(name, _)| format!("- [{name}](./module_{name}.html)"))
.collect::<Vec<String>>();
indexes_vec.sort();
let indexes_body = indexes_vec.join("\n");
Expand Down Expand Up @@ -224,7 +224,7 @@ indent: true
let mut new_ch = Chapter::new(
name,
content_header,
format!("{}.md", &name),
format!("module_{}.md", &name),
vec![ch.name.clone()],
);
new_ch.number = Some(new_section_number);
Expand All @@ -235,7 +235,7 @@ indent: true
} else if capture.2 == "include_lookup_index" {
let mut indexes_vec = LOOKUPS
.iter()
.map(|name| format!("- [{name}](./{name}.html)"))
.map(|name| format!("- [{name}](./lookup_{name}.html)"))
.collect::<Vec<String>>();
indexes_vec.sort();
let indexes_body = indexes_vec.join("\n");
Expand Down Expand Up @@ -268,7 +268,7 @@ indent: true
let mut new_ch = Chapter::new(
lookup_name,
content_header,
format!("{}.md", &lookup_name),
format!("lookup_{}.md", &lookup_name),
vec![ch.name.clone()],
);
new_ch.number = Some(new_section_number);
Expand Down
35 changes: 35 additions & 0 deletions rash_core/src/jinja/lookup/find.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/// ANCHOR: lookup
/// # find
///
/// Use [find module](./module_find.html) as a lookup. Returns the extra field of the module result.
///
/// ANCHOR_END: lookup
/// ANCHOR: examples
/// ## Example
///
/// ```yaml
/// - debug:
/// msg: "{{ find(paths='/') }}"
/// ```
/// ANCHOR_END: examples
use crate::jinja::lookup::utils::to_minijinja_error;
use crate::modules::find::find;
use crate::modules::parse_params;

use std::result::Result as StdResult;

use minijinja::{Error as MinijinjaError, Value};

pub fn function(config: Value) -> StdResult<Value, MinijinjaError> {
parse_params(serde_yaml::to_value(config).map_err(to_minijinja_error)?)
.map_err(to_minijinja_error)
.and_then(|params| {
Ok(find(params)
.map_err(to_minijinja_error)
.map(|x| serde_yaml::to_value(x.get_extra()))
.map_err(to_minijinja_error)?
.map(Value::from_serialize))
})
.map_err(to_minijinja_error)?
.map_err(to_minijinja_error)
}
5 changes: 4 additions & 1 deletion rash_core/src/jinja/lookup/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
mod find;
#[cfg(feature = "passwordstore")]
mod passwordstore;

mod utils;

use rash_derive::generate_lookup_functions;

generate_lookup_functions!(passwordstore);
generate_lookup_functions!((find, false), (passwordstore, true),);
14 changes: 6 additions & 8 deletions rash_core/src/jinja/lookup/passwordstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
/// msg: "{{ passwordstore('foo/boo') }}"
/// ```
/// ANCHOR_END: examples
use crate::jinja::lookup::utils::to_minijinja_error;

use std::env;
use std::result::Result as StdResult;

Expand All @@ -34,15 +36,11 @@ pub fn function(path: String) -> StdResult<Value, MinijinjaError> {

let config = Config::from(Proto::Gpg);
let plaintext = crypto::context(&config)
.map_err(|e| MinijinjaError::new(MinijinjaErrorKind::InvalidOperation, e.to_string()))?
.map_err(to_minijinja_error)?
.decrypt_file(&secret.path)
.map_err(|e| MinijinjaError::new(MinijinjaErrorKind::InvalidOperation, e.to_string()))?;
let first_line = plaintext
.first_line()
.map_err(|e| MinijinjaError::new(MinijinjaErrorKind::InvalidOperation, e.to_string()))?;
.map_err(to_minijinja_error)?;
let first_line = plaintext.first_line().map_err(to_minijinja_error)?;

let password = first_line
.unsecure_to_str()
.map_err(|e| MinijinjaError::new(MinijinjaErrorKind::InvalidOperation, e.to_string()))?;
let password = first_line.unsecure_to_str().map_err(to_minijinja_error)?;
Ok(Value::from(password))
}
5 changes: 5 additions & 0 deletions rash_core/src/jinja/lookup/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use minijinja::{Error as MinijinjaError, ErrorKind as MinijinjaErrorKind};

pub fn to_minijinja_error<E: std::fmt::Display>(err: E) -> MinijinjaError {
MinijinjaError::new(MinijinjaErrorKind::InvalidOperation, err.to_string())
}
2 changes: 1 addition & 1 deletion rash_core/src/modules/find.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ fn get_regex_set(v: Option<Vec<String>>) -> Result<Option<RegexSet>> {
}
}

fn find(params: Params) -> Result<ModuleResult> {
pub fn find(params: Params) -> Result<ModuleResult> {
let paths = parse_if_json(params.paths);
if paths.iter().map(Path::new).any(|x| x.is_relative()) {
return Err(Error::new(
Expand Down
2 changes: 1 addition & 1 deletion rash_core/src/modules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ mod command;
mod copy;
mod debug;
mod file;
mod find;
pub mod find;
mod pacman;
mod set_vars;
mod template;
Expand Down
73 changes: 53 additions & 20 deletions rash_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ extern crate syn;

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, punctuated::Punctuated, Ident, Token};
use syn::{parse_macro_input, punctuated::Punctuated, Expr, ExprLit, ExprTuple, Lit, Token};

/// Implementation of the `#[derive(FieldNames)]` derive macro.
///
Expand Down Expand Up @@ -76,22 +76,25 @@ pub fn derive_doc_json_schema(input: TokenStream) -> TokenStream {
/// Macro to generate a function that adds lookup functions to a `minijinja::Environment`.
///
/// This macro generates an `add_lookup_functions` function that registers multiple lookup
/// functions into a `minijinja::Environment`, with each function being conditionally compiled
/// based on the presence of a corresponding feature flag.
/// functions into a `minijinja::Environment`. Each function can be conditionally compiled
/// based on the presence of a corresponding feature flag if specified in the tuple.
///
/// Additionally, when the `docs` feature is enabled, it will generate a `LOOKUPS` constant that
/// lists all the lookup function names.
///
/// # Example
///
/// Assuming you have three modules `lookup1`, `lookup2`, and `lookup3`, each with a `function`
/// that you want to add to the environment, you would use the macro like this:
///
/// ```
/// ```rust
/// mod lookup1;
/// mod lookup2;
/// mod lookup3;
///
/// use rash_derive::generate_lookup_functions;
/// use my_macro::generate_lookup_functions;
///
/// generate_lookup_functions!(lookup1, lookup2, lookup3);
/// generate_lookup_functions!((lookup1, true), (lookup2, false), (lookup3, true));
/// ```
///
/// This will generate the following function:
Expand All @@ -101,6 +104,9 @@ pub fn derive_doc_json_schema(input: TokenStream) -> TokenStream {
/// #[cfg(feature = "lookup1")]
/// env.add_function("lookup1", lookup1::function);
///
///
/// #[cfg(feature = "lookup2")]
///
/// #[cfg(feature = "lookup2")]
/// env.add_function("lookup2", lookup2::function);
///
Expand All @@ -109,39 +115,66 @@ pub fn derive_doc_json_schema(input: TokenStream) -> TokenStream {
/// }
/// ```
///
/// You can then control which functions are included by specifying the corresponding features
/// When the `docs` feature is enabled, it will also generate the following constant:
///
/// ```rust
/// #[cfg(feature = "docs")]
/// const LOOKUPS: &[&str] = &[
/// "lookup1",
/// "lookup2",
/// "lookup3",
/// ];
/// ```
///
/// You can control which functions are included by specifying the corresponding features
/// in your `Cargo.toml`:
///
/// ```toml
/// [features]
/// lookup1 = []
/// lookup2 = []
/// lookup3 = []
/// docs = []
/// ```
///
/// When building your crate, you can enable the desired features:
/// When building your crate with the `docs` feature, the `LOOKUPS` constant will be included:
///
/// ```sh
/// cargo build --features "lookup1 lookup2"
/// cargo build --features "docs"
/// ```
#[proc_macro]
pub fn generate_lookup_functions(input: TokenStream) -> TokenStream {
// Parse the input tokens into a punctuated list of identifiers separated by commas
let function_names =
parse_macro_input!(input with Punctuated::<Ident, Token![,]>::parse_terminated);
// Parse the input tokens into a punctuated list of tuples separated by commas
let tuples =
parse_macro_input!(input with Punctuated::<ExprTuple, Token![,]>::parse_terminated);

let mut add_functions = Vec::new();
let mut lookup_names = Vec::new();

// Iterate through each identifier and generate the corresponding function call with #[cfg]
for func in function_names.iter() {
let func_name_str = func.to_string(); // Convert the identifier to a string
add_functions.push(quote! {
#[cfg(feature = #func_name_str)]
env.add_function(#func_name_str, #func::function);
});
// Iterate through each tuple and generate the corresponding function call with optional #[cfg]
for tuple in tuples.iter() {
if let (
Some(Expr::Path(path)),
Some(Expr::Lit(ExprLit {
lit: Lit::Bool(lit_bool),
..
})),
) = (tuple.elems.first(), tuple.elems.last())
{
let func_name = path.path.segments.first().unwrap().ident.to_string(); // Extract function name
lookup_names.push(func_name.clone());

lookup_names.push(func_name_str);
if lit_bool.value {
add_functions.push(quote! {
#[cfg(feature = #func_name)]
env.add_function(#func_name, #path::function);
});
} else {
add_functions.push(quote! {
env.add_function(#func_name, #path::function);
});
}
}
}

// Generate the output function code
Expand Down

0 comments on commit 55e0787

Please sign in to comment.