This is an implementation of indent from Python's textwrap module in Rust.

Example

let text = "\
Test
  Test

Test";

println!("{}", text);
// Output:
// Test
//   Test
//
// Test

println!("{}", indent(text, ">>>>"));
// Output:
// >>>>Test
// >>>>  Test
//
// >>>>Test

println!("{}", indent_with_predicate(text, ">>>>", |line| true));
// Output:
// >>>>Test
// >>>>  Test
// >>>>
// >>>>Test

Snippet

fn indent_with_predicate(text: &str, prefix: &str, predicate: fn(&str) -> bool) -> String {
    text.lines()
        .map(|line| if predicate(line) {
            [prefix, line].concat()
        } else {
            line.to_string()
        })
        .collect::<Vec<String>>()
        .join("\n")
}

fn indent(text: &str, prefix: &str) -> String {
    indent_with_predicate(text, prefix, |line| {
        line.chars().any(|c| !c.is_whitespace())
    })
}

AsRef Version

Using AsRef allows mixing &str and String and other types implementing AsRef<str>.

Making it possible to do:

let text = "A\bB";
let prefix = String::new(">");

indent(text, prefix)

Instead of needing an explicit .as_ref() for String:

let text = "A\bB";
let prefix = String::new(">");

indent(text, prefix.as_ref())

Here's the updated snippet:

fn indent_with_predicate<S, T>(text: S, prefix: T, predicate: fn(&str) -> bool) -> String
where
    S: AsRef<str>,
    T: AsRef<str>,
{
    let prefix = prefix.as_ref();

    text.as_ref()
        .lines()
        .map(|line| if predicate(line) {
            [prefix, line].concat()
        } else {
            line.to_string()
        })
        .collect::<Vec<String>>()
        .join("\n")
}

fn indent<S, T>(text: S, prefix: T) -> String
where
    S: AsRef<str>,
    T: AsRef<str>,
{
    indent_with_predicate(text, prefix, |line| {
        line.chars().any(|c| !c.is_whitespace())
    })
}