1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//! Provides efficient concatenation of strings and vectors
//!
//! The goal of these macros are to reduce the amount of allocations that are required
//! when concatenating string buffers and vectors; with a macro that makes it simple to
//! achieve in practice.
//!
//! # Implementation Notes
//!
//! - The caller must provide a buffer for appending the slices to
//! - The buffer is resized to accomodate the total length of all slices given

/// Appends any number of string slices onto a string buffer
///
/// ```rust
/// use concat_in_place::strcat;
///
/// let domain = "domain.com";
/// let endpoint = "inventory/parts";
/// let id = "10512";
///
/// let mut url = String::new();
/// let slice = strcat!(&mut url, domain "/" endpoint "/" id);
/// assert_eq!(slice, "domain.com/inventory/parts/10512");
/// ```
///
/// # Implementation Notes
///
/// Technically works with any string type that has the following methods:
///
/// - `capacity`
/// - `len`
/// - `push_str`
#[macro_export]
macro_rules! strcat {
    ($input:expr, $($element:expr)*) => {{
        let out = $input;
        let mut required = 0;

        $(
            required += $element.len();
        )*

        let free = out.capacity() - out.len();
        if (free < required) {
            out.reserve(required - free);
        }

        $(
            out.push_str($element);
        )*

        &*out
    }}
}

/// Appends any number of slices onto a vector
///
/// ```rust
/// use concat_in_place::veccat;
///
/// let domain = b"domain.com";
/// let endpoint = b"inventory/parts";
/// let id = b"10512";
///
/// let mut url = Vec::new();
/// let slice = veccat!(&mut url, domain b"/" endpoint b"/" id);
/// assert_eq!(slice, b"domain.com/inventory/parts/10512");
/// ```
///
/// # Implementation Notes
///
/// Technically works with any type that has the following methods:
///
/// - `capacity`
/// - `len`
/// - `reserve`
/// - `extend_from_slice`
#[macro_export]
macro_rules! veccat {
    ($input:expr, $($element:expr)*) => {{
        let out = $input;
        let mut required = 0;

        $(
            required += $element.len();
        )*

        let free = out.capacity() - out.len();
        if (free < required) {
            out.reserve(required - free);
        }

        $(
            out.extend_from_slice($element);
        )*

        &*out
    }}
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn bytes() {
        let mut out = Vec::new();

        let expected = b"domain.com/endpoint/id";
        let actual: &[u8] = veccat!(&mut out, b"domain.com" b"/" b"endpoint" b"/" b"id");
        assert_eq!(actual, expected, "{}", String::from_utf8_lossy(actual));
    }

    #[test]
    fn strings() {
        let mut out = String::new();

        let expected = "domain.com/endpoint/id";
        let actual = strcat!(&mut out, "domain.com" "/" "endpoint" "/" "id");
        assert_eq!(actual, expected);
    }
}