Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions examples/greeting.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Hello $USER!

It's nice to see you! Here is some information about the current environment:

\$CARGO: $CARGO
\$CARGO_PKG_NAME: $CARGO_PKG_NAME
\$CARGO_PKG_VERSION: $CARGO_PKG_VERSION
\$CARGO_PKG_DESCRIPTION: $CARGO_PKG_DESCRIPTION
\$CARGO_PKG_AUTHORS: $CARGO_PKG_AUTHORS
\$CARGO_PKG_HOMEPAGE: $CARGO_PKG_HOMEPAGE
\$CARGO_CRATE_NAME: $CARGO_CRATE_NAME
\$PATH: $PATH
49 changes: 49 additions & 0 deletions examples/merge-environments.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//! Merge the runtime and compile-time environment.
//!
//! See [Issue #20](https://github.com/fizyr/subst/issues/20) for inspiration.
use std::borrow::Cow;

use subst::map::{fallback, map_value};

static STATIC_ENV: &[(&str, &str)] = {
&[
("CARGO", env!("CARGO")),
("CARGO_PKG_VERSION", env!("CARGO_PKG_VERSION")),
("CARGO_PKG_NAME", env!("CARGO_PKG_NAME")),
("CARGO_PKG_DESCRIPTION", env!("CARGO_PKG_DESCRIPTION")),
("CARGO_PKG_AUTHORS", env!("CARGO_PKG_AUTHORS")),
("CARGO_PKG_HOMEPAGE", env!("CARGO_PKG_HOMEPAGE")),
("CARGO_CRATE_NAME", env!("CARGO_CRATE_NAME")),
]
};

pub fn main() {
let template = subst::Template::from_str(include_str!("greeting.txt")).unwrap();

println!("Substitution using Env:");
println!(
"{}",
template
.expand(&subst::Env)
.expect_err("Env doesn't know anything about compile-time variables")
);
println!();

println!("Substitution using STATIC_ENV:");
println!(
"{}",
template
.expand(STATIC_ENV)
.expect_err("STATIC_ENV doesn't know anything about runtime variables.")
);
println!();

println!("Substitution using Env, falling back to STATIC_ENV:");

// `Env` returns `String`s, but `STATIC_ENV` returns `&str` references.
let merged = fallback(
map_value(subst::Env, Cow::<str>::Owned),
map_value(STATIC_ENV, |&value| Cow::<str>::Borrowed(value)),
);
println!("{}", template.expand(&merged).unwrap());
}
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@
pub mod error;
pub use error::Error;

mod map;
pub use map::*;
pub mod map;
pub use map::{Env, EnvBytes, NoSubstitution, VariableMap};

mod template;
pub use template::*;
Expand Down
103 changes: 0 additions & 103 deletions src/map.rs

This file was deleted.

39 changes: 39 additions & 0 deletions src/map/fallback.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use super::VariableMap;

/// [`VariableMap`] produced by [`fallback()`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Fallback<BaseMap, FallbackMap> {
base: BaseMap,
fallback: FallbackMap,
}

impl<'a, BaseMap, FallbackMap> VariableMap<'a> for Fallback<BaseMap, FallbackMap>
where
BaseMap: VariableMap<'a>,
FallbackMap: VariableMap<'a, Value = BaseMap::Value>,
{
type Value = BaseMap::Value;

fn get(&'a self, key: &str) -> Option<Self::Value> {
self.base.get(key).or_else(|| self.fallback.get(key))
}
}

/// Creates a [`VariableMap`] that will first try to find values in `base`, and then attempt to
/// find values in `fallback`.
///
///
/// # Example
/// ```rust
/// # use subst::map::{fallback, VariableMap};
///
/// let contact_info = [("first_name", "John"), ("last_name", "Doe")];
/// let with_fallback = fallback(contact_info, [("middle_name", "<unknown>")]);
///
/// assert_eq!(with_fallback.get("first_name"), Some(&"John"));
/// assert_eq!(with_fallback.get("last_name"), Some(&"Doe"));
/// assert_eq!(with_fallback.get("middle_name"), Some(&"<unknown>"));
/// ```
pub const fn fallback<Base, Fallback>(base: Base, fallback: Fallback) -> self::Fallback<Base, Fallback> {
self::Fallback { base, fallback }
}
42 changes: 42 additions & 0 deletions src/map/fn_map.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use super::VariableMap;

/// [`VariableMap`] produced by [`from_fn()`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct FnMap<F> {
func: F,
}

impl<'a, F, V> VariableMap<'a> for FnMap<F>
where
F: 'a + Fn(&str) -> Option<V>,
{
type Value = V;

#[inline(always)]
fn get(&'a self, key: &str) -> Option<Self::Value> {
(self.func)(key)
}
}

/// Creates a [`VariableMap`] that delegates to the given function.
///
/// # Example
/// ```rust
/// # use subst::map::{from_fn, VariableMap};
///
/// let contact_info = from_fn(|key| match key {
/// "first_name" => Some("John"),
/// "last_name" => Some("Doe"),
/// _ => None,
/// });
///
/// assert_eq!(contact_info.get("first_name"), Some("John"));
/// assert_eq!(contact_info.get("last_name"), Some("Doe"));
/// assert_eq!(contact_info.get("middle_name"), None);
/// ```
pub const fn from_fn<F, V>(func: F) -> FnMap<F>
where
F: Fn(&str) -> Option<V>,
{
FnMap { func }
}
44 changes: 44 additions & 0 deletions src/map/map_value.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use super::VariableMap;

/// [`VariableMap`] produced by [`map_value()`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct MapValue<M, F> {
map: M,
func: F,
}

impl<'a, M, F, V> VariableMap<'a> for MapValue<M, F>
where
M: VariableMap<'a>,
F: Fn(M::Value) -> V,
{
type Value = V;

fn get(&'a self, key: &str) -> Option<Self::Value> {
let value = self.map.get(key)?;
Some((self.func)(value))
}
}

/// Creates a [`VariableMap`] that will apply a function to the values of another map.
///
///
/// # Example
/// ```rust
/// # use subst::map::{map_value, VariableMap};
///
/// let contact_info = [("first_name", "John"), ("last_name", "Doe")];
///
/// let contact_info_capitalized = map_value(contact_info, |value| value.to_uppercase());
///
/// assert_eq!(contact_info_capitalized.get("first_name"), Some("JOHN".to_string()));
/// assert_eq!(contact_info_capitalized.get("last_name"), Some("DOE".to_string()));
/// assert_eq!(contact_info_capitalized.get("middle_name"), None);
/// ```
pub const fn map_value<'a, M, F, V>(map: M, func: F) -> MapValue<M, F>
where
M: VariableMap<'a>,
F: Fn(M::Value) -> V,
{
MapValue { map, func }
}
Loading
Loading