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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// Copyright © 2017 Trevor Spiteri

// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! # String concatenation
//!
//! Concatenatation of characters ([`char`][char]), string slices
//! ([`&str`][str]) and owned strings ([`String`][String]).
//!
//! A concatenation is started with the [`Cat`][Cat] element, and any
//! number of characters, string slices or strings can be concatenated
//! using the `+` operator. The concatenation can be converted or
//! appended to a `String`.
//!
//! If the concatenation is converted to a `String`, and it starts
//! with an owned string with enough capacity to store the result, no
//! allocations or reallocations take place. If the concatenation is
//! appended to a `String` with enough capacity, no allocations or
//! reallocations take place. Otherwise, one allocation or
//! reallocation takes place.
//!
//! ## Examples
//!
//! A concatenation can be converted to a `String`.
//!
//! ```rust
//! use sconcat::Cat;
//!
//! let cat1 = Cat + "Hello, " + "world! " + '☺';
//! // One allocation:
//! let s1 = String::from(cat1);
//! assert_eq!(s1, "Hello, world! ☺");
//!
//! let cat2 = Cat + String::from("Hello, ") + "world! " + '☺';
//! // At most one reallocation as the initial `String` is resized:
//! let s2 = String::from(cat2);
//! assert_eq!(s2, "Hello, world! ☺");
//! ```
//!
//! A concatenation can also be appended to a `String`.
//!
//! ```rust
//! use sconcat::Cat;
//!
//! let cat = Cat + "world! " + '☺';
//! let mut s = String::from("Hello, ");
//! // At most one reallocation as the initial `s` is resized:
//! s += cat;
//! assert_eq!(s, "Hello, world! ☺");
//! ```
//!
//! If the concatenation starts with a `String` that has enough
//! reserved space, no reallocations will take place.
//!
//! ```rust
//! use sconcat::Cat;
//!
//! let mut buf = String::from("Hello, ");
//! // 7 bytes for "world! " and 3 bytes for '☺'
//! buf.reserve(10);
//! let ptr = buf.as_ptr();
//! let cat = Cat + buf + "world! " + '☺';
//! let s2 = String::from(cat);
//! assert_eq!(s2, "Hello, world! ☺");
//! assert_eq!(s2.as_ptr(), ptr);
//! ```
//!
//! The concatenation also implements [`Display`][Display] and
//! [`Debug`][Debug]. However, using `to_string()` can result in
//! multiple reallocations, so `String::from(cat)` is preferred over
//! `cat.to_string()` where possible.
//!
//! ```rust
//! use sconcat::Cat;
//!
//! let cat = Cat + "Hello, " + "world! " + '☺';
//! // `s1` can be resized up to three times:
//! let s1 = cat.to_string();
//! assert_eq!(s1, "Hello, world! ☺");
//! // Only one allocation here:
//! let s2 = String::from(cat);
//! assert_eq!(s2, "Hello, world! ☺");
//! // The following would fail as now `cat` has been moved:
//! // let s3 = String::from(cat);
//! ```
//!
//! ## Usage
//!
//! To use `sconcat` in your crate, add `extern crate sconcat;` to the
//! crate root and add `sconcat` as a dependency in `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! sconcat = "0.2"
//! ```
//!
//! ### Optional features
//!
//! The crate supports an optional feature `fast_fmt`, which adds a
//! dependency on the [`fast_fmt`][fastfmt] crate. When the feature is
//! enabled, the concatenation implements `fast_fmt::Fmt`. To enable
//! the feature, the dependency in `Cargo.toml` can be added as:
//!
//! ```toml
//! [dependencies]
//! sconcat = { version = "0.2", features = ["fast_fmt"] }
//! ```
//!
//! [Cat]:     struct.Cat.html
//! [Debug]:   https://doc.rust-lang.org/std/fmt/trait.Debug.html
//! [Display]: https://doc.rust-lang.org/std/fmt/trait.Display.html
//! [fastfmt]: https://crates.io/crates/fast_fmt
//! [String]:  https://doc.rust-lang.org/std/string/struct.String.html
//! [char]:    https://doc.rust-lang.org/std/primitive.char.html
//! [str]:     https://doc.rust-lang.org/std/primitive.str.html

#![warn(missing_docs)]
#![doc(html_root_url = "https://docs.rs/sconcat/0.2.1/",
       test(attr(deny(warnings))))]

#[cfg(feature = "fast_fmt")]
extern crate fast_fmt;

mod cat;
pub use cat::Cat;

#[cfg(test)]
mod tests {
    use Cat;

    #[test]
    fn readme_example_works() {
        let cat1 = Cat + "Hello, " + "world! " + '☺';
        let s1 = String::from(cat1);
        assert_eq!(s1, "Hello, world! ☺");

        let mut s2 = String::from("Hello");
        s2 += Cat + ',' + " world" + String::from("! ") + '☺';
        assert_eq!(s2, "Hello, world! ☺");

        let mut buf = String::from("Hello, ");
        // 7 bytes for "world! " and 3 bytes for '☺'
        buf.reserve(10);
        let ptr = buf.as_ptr();
        // buf is large enough, so no reallocations take place
        let cat3 = Cat + buf + "world! " + '☺';
        let s3 = String::from(cat3);
        assert_eq!(s3, "Hello, world! ☺");
        assert_eq!(s3.as_ptr(), ptr);
    }
}