ascii-0.9.2/.gitignore010064400017500001731000000000241255623531300130230ustar0000000000000000/target /Cargo.lock ascii-0.9.2/.travis.yml010064400017500001731000000015721326106152200131460ustar0000000000000000sudo: false language: rust rust: - nightly - beta - stable - 1.9.0 before_script: - | pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH script: - | travis-cargo build && travis-cargo test && travis-cargo build -- --no-default-features && travis-cargo test -- --no-default-features after_success: - | if [ "$TRAVIS_RUST_VERSION" == "stable" ]; then travis-cargo doc -- --no-default-features && mv target/doc target/doc_core && travis-cargo doc && mv target/doc_core target/doc/core && travis-cargo doc-upload fi env: global: - TRAVIS_CARGO_NIGHTLY_FEATURE="" # no unstable feature - secure: ddcWXicVcCooC+Dy8guGruZY2bAU3oyGjrxdC3YNfBYdatEKzW1toAiQyN8SRyZyfoHsbb7lh4YeBfv1rpmTPM6nvHMz9CHMlvED8Y+/QuYoKN2qrNiQ7eQ9xSVhOVlha/GMPSZXxmEIuJVj0Dn1D/S4RWyNMKCJdj2YvybPzOU= ascii-0.9.2/Cargo.toml.orig010064400017500001731000000012031351034106400137120ustar0000000000000000[package] authors = ["Thomas Bahn ", "Torbjørn Birch Moltu ", "Simon Sapin "] description = "ASCII-only equivalents to `char`, `str` and `String`." documentation = "https://docs.rs/ascii" license = "Apache-2.0 / MIT" name = "ascii" readme = "README.md" repository = "https://github.com/tomprogrammer/rust-ascii" version = "0.9.2" [dependencies] quickcheck = { version = "0.6", optional = true } serde = { version = "1.0.25", optional = true } serde_test = { version = "1.0", optional = true } [features] default = ["std"] std = [] [[test]] name = "tests" path = "tests.rs" ascii-0.9.2/Cargo.toml0000644000000022320000000000000101700ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "ascii" version = "0.9.2" authors = ["Thomas Bahn ", "Torbjørn Birch Moltu ", "Simon Sapin "] description = "ASCII-only equivalents to `char`, `str` and `String`." documentation = "https://docs.rs/ascii" readme = "README.md" license = "Apache-2.0 / MIT" repository = "https://github.com/tomprogrammer/rust-ascii" [[test]] name = "tests" path = "tests.rs" [dependencies.quickcheck] version = "0.6" optional = true [dependencies.serde] version = "1.0.25" optional = true [dependencies.serde_test] version = "1.0" optional = true [features] default = ["std"] std = [] ascii-0.9.2/LICENSE-APACHE010064400017500001731000000260761344674335400130060ustar0000000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ascii-0.9.2/LICENSE-MIT010064400017500001731000000021541312050715700124710ustar0000000000000000MIT License Copyright (c) 2017 Thomas Bahn and contributors Copyright (c) 2014 The Rust Project Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ascii-0.9.2/README.md010064400017500001731000000036021351034047300123120ustar0000000000000000# ascii A library that provides ASCII-only string and character types, equivalent to the `char`, `str` and `String` types in the standard library. Types and conversion traits are described in the [Documentation](https://docs.rs/ascii). You can include this crate in your cargo project by adding it to the dependencies section in `Cargo.toml`: ```toml [dependencies] ascii = "0.9" ``` ## Using ascii without libstd Most of `AsciiChar` and `AsciiStr` can be used without `std` by disabling the default features. The owned string type `AsciiString` and the conversion trait `IntoAsciiString` as well as all methods referring to these types are unavailable. The `Error` trait is also unavailable, but `description()` is made available as an inherent method for `ToAsciiCharError` and `AsAsciiStrError`. To use the `ascii` crate in `core`-only mode in your cargo project just add the following dependency declaration in `Cargo.toml`: ```toml [dependencies] ascii = { version = "0.9", default-features = false } ``` ## Requirements - The minimum supported Rust version is 1.9.0 - Enabling the quickcheck feature requires Rust 1.12.0 - Enabling the serde feature requires Rust 1.13.0 ## History This package included the Ascii types that were removed from the Rust standard library by the 2014-12 [reform of the `std::ascii` module](https://github.com/rust-lang/rfcs/pull/486). The API changed significantly since then. ## License Licensed under either of * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. ascii-0.9.2/RELEASES.md010064400017500001731000000125251351034046400125640ustar0000000000000000Unreleased ========== * Implement the `IntoAsciiString` trait for `std::ffi::CStr` and `std::ffi::CString` types, and implemented the `AsAsciiStr` trait for `std::ffi::CStr` type. * Implement the `IntoAsciiString` for `std::borrow::Cow`, where the inner types themselves implement `IntoAsciiString`. * Implement conversions between `AsciiString` and `Cow<'a, AsciiStr>`. * Implement the `std::ops::AddAssign` trait for `AsciiString`. * Implement `BorrowMut`, `AsRef<[AsciiChar]>`, `AsRef`, `AsMut<[AsciiChar]>` for `AsciiString`. * Implement `PartialEq<[u8]>` and `PartialEq<[AsciiChar]>` for `AsciiStr`. * Add `AsciiStr::first()`, `AsciiStr::last()` and `AsciiStr::split()` methods. * Implement `DoubleEndedIterator` for `AsciiStr::lines()`. * Implement `AsRef` and `AsMut for AsciiString`. Version 0.8.4 (2017-04-18) ========================== * Fix the tests when running without std. Version 0.8.3 (2017-04-18) ========================== * Bugfix: `::to_ascii_lowercase` did erroneously convert to uppercase. Version 0.8.2 (2017-04-17) ========================== * Implement `IntoAsciiString` for `&'a str` and `&'a [u8]`. * Implement the `quickcheck::Arbitrary` trait for `AsciiChar` and `AsciiString`. The implementation is enabled by the `quickcheck` feature. Version 0.8.1 (2017-02-11) ========================== * Add `Chars`, `CharsMut` and `Lines` iterators. * Implement `std::fmt::Write` for `AsciiString`. Version 0.8.0 (2017-01-02) ========================== Breaking changes: * Return `FromAsciiError` instead of the input when `AsciiString::from_ascii()` or `into_ascii_string()` fails. * Replace the `no_std` feature with the additive `std` feature, which is part of the default features. (Issue #29) * `AsciiChar::is_*()` and `::as_{byte,char}()` take `self` by value instead of by reference. Additions: * Make `AsciiChar` comparable with `char` and `u8`. * Add `AsciiChar::as_printable_char()` and the free functions `caret_encode()` and `caret_decode()`. * Implement some methods from `AsciiExt` and `Error` (which are not in libcore) directly in `core` mode: * `Ascii{Char,Str}::eq_ignore_ascii_case()` * `AsciiChar::to_ascii_{upper,lower}case()` * `AsciiStr::make_ascii_{upper,lower}case()` * `{ToAsciiChar,AsAsciiStr}Error::description()` Version 0.7.1 (2016-08-15) ========================== * Fix the implementation of `AsciiExt::to_ascii_lowercase()` for `AsciiChar` converting to uppercase. (introduced in 0.7.0) Version 0.7.0 (2016-06-25) ========================== * Rename `Ascii` to `AsciiChar` and convert it into an enum. (with a variant for every ASCII character) * Replace `OwnedAsciiCast` with `IntoAsciiString`. * Replace `AsciiCast` with `As[Mut]AsciiStr` and `IntoAsciiChar`. * Add *from[_ascii]_unchecked* methods. * Replace *from_bytes* with *from_ascii* in method names. * Return `std::error::Error`-implementing types instead of `()` and `None` when conversion to `AsciiStr` or `AsciiChar` fails. * Implement `AsciiExt` without the `unstable` Cargo feature flag, which is removed. * Require Rust 1.9 or later. * Add `#[no_std]` support in a Cargo feature. * Implement `From<{&,&mut,Box<}AsciiStr>` for `[Ascii]`, `[u8]` and `str` * Implement `From<{&,&mut,Box<}[Ascii]>`, `As{Ref,Mut}<[Ascii]>` and Default for `AsciiStr` * Implement `From>` for `AsciiString`. * Implement `AsMut` for `AsciiString`. * Stop some `Ascii::is_xxx()` methods from panicking. * Add `Ascii::is_whitespace()`. * Add `AsciiString::as_mut_slice()`. * Add raw pointer methods on `AsciiString`: * `from_raw_parts` * `as_ptr` * `as_mut_ptr` Version 0.6.0 (2015-12-30) ========================== * Add `Ascii::from_byte()` * Add `AsciiStr::trim[_{left,right}]()` Version 0.5.4 (2015-07-29) ========================== Implement `IndexMut` for AsciiStr and AsciiString. Version 0.5.1 (2015-06-13) ========================== * Add `Ascii::from()`. * Implement `Index` for `AsciiStr` and `AsciiString`. * Implement `Default`,`FromIterator`,`Extend` and `Add` for `AsciiString` * Added inherent methods on `AsciiString`: * `with_capacity` * `push_str` * `capacity` * `reserve` * `reserve_exact` * `shrink_to_fit` * `push` * `truncate` * `pop` * `remove` * `insert` * `len` * `is_empty` * `clear` Version 0.5.0 (2015-05-05) ========================== First release compatible with Rust 1.0.0. ascii-0.9.2/src/ascii_char.rs010064400017500001731000000553151344674335400143020ustar0000000000000000#![cfg_attr(rustfmt, rustfmt_skip)] // #[allow(deprecated)] doesn't silence warnings on the method invocations, // which would call the inherent methods if AsciiExt wasn't in scope. #![cfg_attr(feature = "std", allow(deprecated))] #[cfg(feature = "quickcheck")] use quickcheck::{Arbitrary, Gen}; use core::mem; use core::cmp::Ordering; use core::{fmt, char}; #[cfg(feature = "std")] use std::error::Error; #[cfg(feature = "std")] use std::ascii::AsciiExt; #[allow(non_camel_case_types)] /// An ASCII character. It wraps a `u8`, with the highest bit always zero. #[derive(Clone, PartialEq, PartialOrd, Ord, Eq, Hash, Copy)] #[repr(u8)] pub enum AsciiChar { /// `'\0'` Null = 0, /// [Start Of Heading](http://en.wikipedia.org/wiki/Start_of_Heading) SOH = 1, /// [Start Of teXt](http://en.wikipedia.org/wiki/Start_of_Text) SOX = 2, /// [End of TeXt](http://en.wikipedia.org/wiki/End-of-Text_character) ETX = 3, /// [End Of Transmission](http://en.wikipedia.org/wiki/End-of-Transmission_character) EOT = 4, /// [Enquiry](http://en.wikipedia.org/wiki/Enquiry_character) ENQ = 5, /// [Acknowledgement](http://en.wikipedia.org/wiki/Acknowledge_character) ACK = 6, /// [bell / alarm / audible](http://en.wikipedia.org/wiki/Bell_character) /// /// `'\a'` is not recognized by Rust. Bell = 7, /// [Backspace](http://en.wikipedia.org/wiki/Backspace) /// /// `'\b'` is not recognized by Rust. BackSpace = 8, /// `'\t'` Tab = 9, /// `'\n'` LineFeed = 10, /// [Vertical tab](http://en.wikipedia.org/wiki/Vertical_Tab) /// /// `'\v'` is not recognized by Rust. VT = 11, /// [Form Feed](http://en.wikipedia.org/wiki/Form_Feed) /// /// `'\f'` is not recognized by Rust. FF = 12, /// `'\r'` CarriageReturn = 13, /// [Shift In](http://en.wikipedia.org/wiki/Shift_Out_and_Shift_In_characters) SI = 14, /// [Shift Out](http://en.wikipedia.org/wiki/Shift_Out_and_Shift_In_characters) SO = 15, /// [Data Link Escape](http://en.wikipedia.org/wiki/Data_Link_Escape) DLE = 16, /// [Device control 1, often XON](http://en.wikipedia.org/wiki/Device_Control_1) DC1 = 17, /// Device control 2 DC2 = 18, /// Device control 3, Often XOFF DC3 = 19, /// Device control 4 DC4 = 20, /// [Negative AcKnowledgement](http://en.wikipedia.org/wiki/Negative-acknowledge_character) NAK = 21, /// [Synchronous idle](http://en.wikipedia.org/wiki/Synchronous_Idle) SYN = 22, /// [End of Transmission Block](http://en.wikipedia.org/wiki/End-of-Transmission-Block_character) ETB = 23, /// [Cancel](http://en.wikipedia.org/wiki/Cancel_character) CAN = 24, /// [End of Medium](http://en.wikipedia.org/wiki/End_of_Medium) EM = 25, /// [Substitute](http://en.wikipedia.org/wiki/Substitute_character) SUB = 26, /// [Escape](http://en.wikipedia.org/wiki/Escape_character) /// /// `'\e'` is not recognized by Rust. ESC = 27, /// [File Separator](http://en.wikipedia.org/wiki/File_separator) FS = 28, /// [Group Separator](http://en.wikipedia.org/wiki/Group_separator) GS = 29, /// [Record Separator](http://en.wikipedia.org/wiki/Record_separator) RS = 30, /// [Unit Separator](http://en.wikipedia.org/wiki/Unit_separator) US = 31, /// `' '` Space = 32, /// `'!'` Exclamation = 33, /// `'"'` Quotation = 34, /// `'#'` Hash = 35, /// `'$'` Dollar = 36, /// `'%'` Percent = 37, /// `'&'` Ampersand = 38, /// `'\''` Apostrophe = 39, /// `'('` ParenOpen = 40, /// `')'` ParenClose = 41, /// `'*'` Asterisk = 42, /// `'+'` Plus = 43, /// `','` Comma = 44, /// `'-'` Minus = 45, /// `'.'` Dot = 46, /// `'/'` Slash = 47, /// `'0'` _0 = 48, /// `'1'` _1 = 49, /// `'2'` _2 = 50, /// `'3'` _3 = 51, /// `'4'` _4 = 52, /// `'5'` _5 = 53, /// `'6'` _6 = 54, /// `'7'` _7 = 55, /// `'8'` _8 = 56, /// `'9'` _9 = 57, /// `':'` Colon = 58, /// `';'` Semicolon = 59, /// `'<'` LessThan = 60, /// `'='` Equal = 61, /// `'>'` GreaterThan = 62, /// `'?'` Question = 63, /// `'@'` At = 64, /// `'A'` A = 65, /// `'B'` B = 66, /// `'C'` C = 67, /// `'D'` D = 68, /// `'E'` E = 69, /// `'F'` F = 70, /// `'G'` G = 71, /// `'H'` H = 72, /// `'I'` I = 73, /// `'J'` J = 74, /// `'K'` K = 75, /// `'L'` L = 76, /// `'M'` M = 77, /// `'N'` N = 78, /// `'O'` O = 79, /// `'P'` P = 80, /// `'Q'` Q = 81, /// `'R'` R = 82, /// `'S'` S = 83, /// `'T'` T = 84, /// `'U'` U = 85, /// `'V'` V = 86, /// `'W'` W = 87, /// `'X'` X = 88, /// `'Y'` Y = 89, /// `'Z'` Z = 90, /// `'['` BracketOpen = 91, /// `'\'` BackSlash = 92, /// `']'` BracketClose = 93, /// `'_'` Caret = 94, /// `'_'` UnderScore = 95, /// `'`'` Grave = 96, /// `'a'` a = 97, /// `'b'` b = 98, /// `'c'` c = 99, /// `'d'` d = 100, /// `'e'` e = 101, /// `'f'` f = 102, /// `'g'` g = 103, /// `'h'` h = 104, /// `'i'` i = 105, /// `'j'` j = 106, /// `'k'` k = 107, /// `'l'` l = 108, /// `'m'` m = 109, /// `'n'` n = 110, /// `'o'` o = 111, /// `'p'` p = 112, /// `'q'` q = 113, /// `'r'` r = 114, /// `'s'` s = 115, /// `'t'` t = 116, /// `'u'` u = 117, /// `'v'` v = 118, /// `'w'` w = 119, /// `'x'` x = 120, /// `'y'` y = 121, /// `'z'` z = 122, /// `'{'` CurlyBraceOpen = 123, /// `'|'` VerticalBar = 124, /// `'}'` CurlyBraceClose = 125, /// `'~'` Tilde = 126, /// [Delete](http://en.wikipedia.org/wiki/Delete_character) DEL = 127, } impl AsciiChar { /// Constructs an ASCII character from a `u8`, `char` or other character type. /// /// # Failure /// Returns `Err(())` if the character can't be ASCII encoded. /// /// # Example /// ``` /// # use ascii::AsciiChar; /// let a = AsciiChar::from('g').unwrap(); /// assert_eq!(a.as_char(), 'g'); /// ``` #[inline] pub fn from(ch: C) -> Result { ch.to_ascii_char() } /// Constructs an ASCII character from a `char` or `u8` without any checks. #[inline] pub unsafe fn from_unchecked(ch: C) -> Self { ch.to_ascii_char_unchecked() } /// Converts an ASCII character into a `u8`. #[inline] pub fn as_byte(self) -> u8 { self as u8 } /// Converts an ASCII character into a `char`. #[inline] pub fn as_char(self) -> char { self.as_byte() as char } // the following methods are like ctype, and the implementation is inspired by musl /// Check if the character is a letter (a-z, A-Z) #[inline] pub fn is_alphabetic(self) -> bool { let c = self.as_byte() | 0b010_0000; // Turns uppercase into lowercase. c >= b'a' && c <= b'z' } /// Check if the character is a number (0-9) #[inline] pub fn is_digit(self) -> bool { self >= AsciiChar::_0 && self <= AsciiChar::_9 } /// Check if the character is a letter or number #[inline] pub fn is_alphanumeric(self) -> bool { self.is_alphabetic() || self.is_digit() } /// Check if the character is a space or horizontal tab #[inline] pub fn is_blank(self) -> bool { self == AsciiChar::Space || self == AsciiChar::Tab } /// Check if the character is a ' ', '\t', '\n' or '\r' #[inline] pub fn is_whitespace(self) -> bool { self.is_blank() || self == AsciiChar::LineFeed || self == AsciiChar::CarriageReturn } /// Check if the character is a control character /// /// # Examples /// ``` /// use ascii::ToAsciiChar; /// assert_eq!('\0'.to_ascii_char().unwrap().is_control(), true); /// assert_eq!('n'.to_ascii_char().unwrap().is_control(), false); /// assert_eq!(' '.to_ascii_char().unwrap().is_control(), false); /// assert_eq!('\n'.to_ascii_char().unwrap().is_control(), true); /// ``` #[inline] pub fn is_control(self) -> bool { self < AsciiChar::Space || self == AsciiChar::DEL } /// Checks if the character is printable (except space) /// /// # Examples /// ``` /// use ascii::ToAsciiChar; /// assert_eq!('n'.to_ascii_char().unwrap().is_graph(), true); /// assert_eq!(' '.to_ascii_char().unwrap().is_graph(), false); /// assert_eq!('\n'.to_ascii_char().unwrap().is_graph(), false); /// ``` #[inline] pub fn is_graph(self) -> bool { self.as_byte().wrapping_sub(b' ' + 1) < 0x5E } /// Checks if the character is printable (including space) /// /// # Examples /// ``` /// use ascii::ToAsciiChar; /// assert_eq!('n'.to_ascii_char().unwrap().is_print(), true); /// assert_eq!(' '.to_ascii_char().unwrap().is_print(), true); /// assert_eq!('\n'.to_ascii_char().unwrap().is_print(), false); /// ``` #[inline] pub fn is_print(self) -> bool { self.as_byte().wrapping_sub(b' ') < 0x5F } /// Checks if the character is alphabetic and lowercase /// /// # Examples /// ``` /// use ascii::ToAsciiChar; /// assert_eq!('a'.to_ascii_char().unwrap().is_lowercase(), true); /// assert_eq!('A'.to_ascii_char().unwrap().is_lowercase(), false); /// assert_eq!('@'.to_ascii_char().unwrap().is_lowercase(), false); /// ``` #[inline] pub fn is_lowercase(self) -> bool { self.as_byte().wrapping_sub(b'a') < 26 } /// Checks if the character is alphabetic and uppercase /// /// # Examples /// ``` /// use ascii::ToAsciiChar; /// assert_eq!('A'.to_ascii_char().unwrap().is_uppercase(), true); /// assert_eq!('a'.to_ascii_char().unwrap().is_uppercase(), false); /// assert_eq!('@'.to_ascii_char().unwrap().is_uppercase(), false); /// ``` #[inline] pub fn is_uppercase(self) -> bool { self.as_byte().wrapping_sub(b'A') < 26 } /// Checks if the character is punctuation /// /// # Examples /// ``` /// use ascii::ToAsciiChar; /// assert_eq!('n'.to_ascii_char().unwrap().is_punctuation(), false); /// assert_eq!(' '.to_ascii_char().unwrap().is_punctuation(), false); /// assert_eq!('_'.to_ascii_char().unwrap().is_punctuation(), true); /// assert_eq!('~'.to_ascii_char().unwrap().is_punctuation(), true); /// ``` #[inline] pub fn is_punctuation(self) -> bool { self.is_graph() && !self.is_alphanumeric() } /// Checks if the character is a valid hex digit /// /// # Examples /// ``` /// use ascii::ToAsciiChar; /// assert_eq!('5'.to_ascii_char().unwrap().is_hex(), true); /// assert_eq!('a'.to_ascii_char().unwrap().is_hex(), true); /// assert_eq!('F'.to_ascii_char().unwrap().is_hex(), true); /// assert_eq!('G'.to_ascii_char().unwrap().is_hex(), false); /// assert_eq!(' '.to_ascii_char().unwrap().is_hex(), false); /// ``` #[inline] pub fn is_hex(self) -> bool { self.is_digit() || (self.as_byte() | 0x20u8).wrapping_sub(b'a') < 6 } /// Unicode has printable versions of the ASCII control codes, like '␛'. /// /// This function is identical with `.as_char()` /// for all values `.is_printable()` returns true for, /// but replaces the control codes with those unicodes printable versions. /// /// # Examples /// ``` /// # use ascii::ToAsciiChar; /// assert_eq!('\0'.to_ascii_char().unwrap().as_printable_char(), '␀'); /// assert_eq!('\n'.to_ascii_char().unwrap().as_printable_char(), '␊'); /// assert_eq!(' '.to_ascii_char().unwrap().as_printable_char(), ' '); /// assert_eq!('p'.to_ascii_char().unwrap().as_printable_char(), 'p'); /// ``` pub fn as_printable_char(self) -> char { unsafe { match self as u8 { b' '...b'~' => self.as_char(), 127 => '␡', _ => char::from_u32_unchecked(self as u32 + '␀' as u32), } } } /// Replaces letters `a` to `z` with `A` to `Z` pub fn make_ascii_uppercase(&mut self) { *self = self.to_ascii_uppercase() } /// Replaces letters `A` to `Z` with `a` to `z` pub fn make_ascii_lowercase(&mut self) { *self = self.to_ascii_lowercase() } /// Maps letters `a`...`z` to `A`...`Z` and returns everything else unchanged. #[inline] pub fn to_ascii_uppercase(&self) -> Self { unsafe { match *self as u8 { b'a'...b'z' => AsciiChar::from_unchecked(self.as_byte() - (b'a' - b'A')), _ => *self, } } } /// Maps letters `A`...`Z` to `a`...`z` and returns everything else unchanged. #[inline] pub fn to_ascii_lowercase(&self) -> Self { unsafe { match *self as u8 { b'A'...b'Z' => AsciiChar::from_unchecked(self.as_byte() + (b'a' - b'A')), _ => *self, } } } /// Compares two characters case-insensitively. pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool { self.to_ascii_lowercase() == other.to_ascii_lowercase() } } impl fmt::Display for AsciiChar { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.as_char().fmt(f) } } impl fmt::Debug for AsciiChar { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.as_char().fmt(f) } } #[cfg(feature = "std")] impl AsciiExt for AsciiChar { type Owned = AsciiChar; #[inline] fn is_ascii(&self) -> bool { true } #[inline] fn to_ascii_uppercase(&self) -> AsciiChar { unsafe { self.as_byte() .to_ascii_uppercase() .to_ascii_char_unchecked() } } #[inline] fn to_ascii_lowercase(&self) -> AsciiChar { unsafe { self.as_byte() .to_ascii_lowercase() .to_ascii_char_unchecked() } } fn eq_ignore_ascii_case(&self, other: &Self) -> bool { self.as_byte().eq_ignore_ascii_case(&other.as_byte()) } #[inline] fn make_ascii_uppercase(&mut self) { *self = self.to_ascii_uppercase(); } #[inline] fn make_ascii_lowercase(&mut self) { *self = self.to_ascii_lowercase(); } } impl Default for AsciiChar { fn default() -> AsciiChar { AsciiChar::Null } } macro_rules! impl_into_partial_eq_ord {($wider:ty, $to_wider:expr) => { impl From for $wider { #[inline] fn from(a: AsciiChar) -> $wider { $to_wider(a) } } impl PartialEq<$wider> for AsciiChar { #[inline] fn eq(&self, rhs: &$wider) -> bool { $to_wider(*self) == *rhs } } impl PartialEq for $wider { #[inline] fn eq(&self, rhs: &AsciiChar) -> bool { *self == $to_wider(*rhs) } } impl PartialOrd<$wider> for AsciiChar { #[inline] fn partial_cmp(&self, rhs: &$wider) -> Option { $to_wider(*self).partial_cmp(rhs) } } impl PartialOrd for $wider { #[inline] fn partial_cmp(&self, rhs: &AsciiChar) -> Option { self.partial_cmp(&$to_wider(*rhs)) } } }} impl_into_partial_eq_ord!{u8, AsciiChar::as_byte} impl_into_partial_eq_ord!{char, AsciiChar::as_char} /// Error returned by `ToAsciiChar`. #[derive(PartialEq)] pub struct ToAsciiCharError(()); const ERRORMSG_CHAR: &'static str = "not an ASCII character"; #[cfg(not(feature = "std"))] impl ToAsciiCharError { /// Returns a description for this error, like `std::error::Error::description`. #[inline] pub fn description(&self) -> &'static str { ERRORMSG_CHAR } } impl fmt::Debug for ToAsciiCharError { fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result { write!(fmtr, "{}", ERRORMSG_CHAR) } } impl fmt::Display for ToAsciiCharError { fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result { write!(fmtr, "{}", ERRORMSG_CHAR) } } #[cfg(feature = "std")] impl Error for ToAsciiCharError { #[inline] fn description(&self) -> &'static str { ERRORMSG_CHAR } } /// Convert `char`, `u8` and other character types to `AsciiChar`. pub trait ToAsciiChar { /// Convert to `AsciiChar` without checking that it is an ASCII character. unsafe fn to_ascii_char_unchecked(self) -> AsciiChar; /// Convert to `AsciiChar`. fn to_ascii_char(self) -> Result; } impl ToAsciiChar for AsciiChar { #[inline] fn to_ascii_char(self) -> Result { Ok(self) } #[inline] unsafe fn to_ascii_char_unchecked(self) -> AsciiChar { self } } impl ToAsciiChar for u8 { #[inline] fn to_ascii_char(self) -> Result { unsafe { if self <= 0x7F { return Ok(self.to_ascii_char_unchecked()); } } Err(ToAsciiCharError(())) } #[inline] unsafe fn to_ascii_char_unchecked(self) -> AsciiChar { mem::transmute(self) } } impl ToAsciiChar for char { #[inline] fn to_ascii_char(self) -> Result { unsafe { if self as u32 <= 0x7F { return Ok(self.to_ascii_char_unchecked()); } } Err(ToAsciiCharError(())) } #[inline] unsafe fn to_ascii_char_unchecked(self) -> AsciiChar { (self as u8).to_ascii_char_unchecked() } } #[cfg(feature = "quickcheck")] impl Arbitrary for AsciiChar { fn arbitrary(g: &mut G) -> Self { let mode = g.gen_range(0, 100); match mode { 0...14 => { // Control characters unsafe { AsciiChar::from_unchecked(g.gen_range(0, 0x1F) as u8) } } 15...39 => { // Characters often used in programming languages *g.choose(&[ AsciiChar::Space, AsciiChar::Tab, AsciiChar::LineFeed, AsciiChar::Tilde, AsciiChar::Grave, AsciiChar::Exclamation, AsciiChar::At, AsciiChar::Hash, AsciiChar::Dollar, AsciiChar::Percent, AsciiChar::Ampersand, AsciiChar::Asterisk, AsciiChar::ParenOpen, AsciiChar::ParenClose, AsciiChar::UnderScore, AsciiChar::Minus, AsciiChar::Equal, AsciiChar::Plus, AsciiChar::BracketOpen, AsciiChar::BracketClose, AsciiChar::CurlyBraceOpen, AsciiChar::CurlyBraceClose, AsciiChar::Colon, AsciiChar::Semicolon, AsciiChar::Apostrophe, AsciiChar::Quotation, AsciiChar::BackSlash, AsciiChar::VerticalBar, AsciiChar::Caret, AsciiChar::Comma, AsciiChar::LessThan, AsciiChar::GreaterThan, AsciiChar::Dot, AsciiChar::Slash, AsciiChar::Question, AsciiChar::_0, AsciiChar::_1, AsciiChar::_2, AsciiChar::_3, AsciiChar::_3, AsciiChar::_4 , AsciiChar::_6, AsciiChar::_7, AsciiChar::_8, AsciiChar::_9, ]).unwrap() } 40...99 => { // Completely arbitrary characters unsafe { AsciiChar::from_unchecked(g.gen_range(0, 0x7F) as u8) } } _ => unreachable!(), } } fn shrink(&self) -> Box> { Box::new((*self as u8).shrink().filter_map( |x| AsciiChar::from(x).ok(), )) } } #[cfg(test)] mod tests { use super::{AsciiChar, ToAsciiChar, ToAsciiCharError}; use AsciiChar::*; #[test] fn to_ascii_char() { fn generic(ch: C) -> Result { ch.to_ascii_char() } assert_eq!(generic(A), Ok(A)); assert_eq!(generic(b'A'), Ok(A)); assert_eq!(generic('A'), Ok(A)); assert!(generic(200).is_err()); assert!(generic('λ').is_err()); } #[test] fn as_byte_and_char() { assert_eq!(A.as_byte(), b'A'); assert_eq!(A.as_char(), 'A'); } #[test] fn is_digit() { assert_eq!('0'.to_ascii_char().unwrap().is_digit(), true); assert_eq!('9'.to_ascii_char().unwrap().is_digit(), true); assert_eq!('/'.to_ascii_char().unwrap().is_digit(), false); assert_eq!(':'.to_ascii_char().unwrap().is_digit(), false); } #[test] fn is_control() { assert_eq!(US.is_control(), true); assert_eq!(DEL.is_control(), true); assert_eq!(Space.is_control(), false); } #[test] fn cmp_wider() { assert_eq!(A, 'A'); assert_eq!(b'b', b); assert!(a < 'z'); } #[test] fn ascii_case() { assert_eq!(At.to_ascii_lowercase(), At); assert_eq!(At.to_ascii_uppercase(), At); assert_eq!(A.to_ascii_lowercase(), a); assert_eq!(A.to_ascii_uppercase(), A); assert_eq!(a.to_ascii_lowercase(), a); assert_eq!(a.to_ascii_uppercase(), A); assert!(LineFeed.eq_ignore_ascii_case(&LineFeed)); assert!(!LineFeed.eq_ignore_ascii_case(&CarriageReturn)); assert!(z.eq_ignore_ascii_case(&Z)); assert!(Z.eq_ignore_ascii_case(&z)); assert!(!Z.eq_ignore_ascii_case(&DEL)); } #[test] #[cfg(feature = "std")] fn ascii_ext() { #[allow(deprecated)] use std::ascii::AsciiExt; assert!(AsciiExt::is_ascii(&Null)); assert!(AsciiExt::is_ascii(&DEL)); assert!(AsciiExt::eq_ignore_ascii_case(&a, &A)); assert!(!AsciiExt::eq_ignore_ascii_case(&A, &At)); assert_eq!(AsciiExt::to_ascii_lowercase(&A), a); assert_eq!(AsciiExt::to_ascii_uppercase(&A), A); assert_eq!(AsciiExt::to_ascii_lowercase(&a), a); assert_eq!(AsciiExt::to_ascii_uppercase(&a), A); assert_eq!(AsciiExt::to_ascii_lowercase(&At), At); assert_eq!(AsciiExt::to_ascii_uppercase(&At), At); let mut mutable = (A,a); AsciiExt::make_ascii_lowercase(&mut mutable.0); AsciiExt::make_ascii_uppercase(&mut mutable.1); assert_eq!(mutable.0, a); assert_eq!(mutable.1, A); } #[test] #[cfg(feature = "std")] fn fmt_ascii() { assert_eq!(format!("{}", t), "t"); assert_eq!(format!("{:?}", t), "'t'"); assert_eq!(format!("{}", LineFeed), "\n"); assert_eq!(format!("{:?}", LineFeed), "'\\n'"); } } ascii-0.9.2/src/ascii_str.rs010064400017500001731000001016631351034031700141530ustar0000000000000000#![cfg_attr(rustfmt, rustfmt_skip)] // #[allow(deprecated)] doesn't silence warnings on the method invocations, // which would call the inherent methods if AsciiExt wasn't in scope. #![cfg_attr(feature = "std", allow(deprecated))] use core::fmt; use core::ops::{Index, IndexMut, Range, RangeTo, RangeFrom, RangeFull}; use core::slice::{Iter, IterMut}; #[cfg(feature = "std")] use std::error::Error; #[cfg(feature = "std")] use std::ascii::AsciiExt; #[cfg(feature = "std")] use std::ffi::CStr; use ascii_char::AsciiChar; #[cfg(feature = "std")] use ascii_string::AsciiString; /// AsciiStr represents a byte or string slice that only contains ASCII characters. /// /// It wraps an `[AsciiChar]` and implements many of `str`s methods and traits. /// /// It can be created by a checked conversion from a `str` or `[u8]`, or borrowed from an /// `AsciiString`. #[derive(PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct AsciiStr { slice: [AsciiChar], } impl AsciiStr { /// Coerces into an `AsciiStr` slice. pub fn new + ?Sized>(s: &S) -> &AsciiStr { s.as_ref() } /// Converts `&self` to a `&str` slice. #[inline] pub fn as_str(&self) -> &str { From::from(self) } /// Converts `&self` into a byte slice. #[inline] pub fn as_bytes(&self) -> &[u8] { From::from(self) } /// Returns the entire string as slice of `AsciiChar`s. #[inline] pub fn as_slice(&self) -> &[AsciiChar] { &self.slice } /// Returns the entire string as mutable slice of `AsciiChar`s. #[inline] pub fn as_mut_slice(&mut self) -> &mut [AsciiChar] { &mut self.slice } /// Returns a raw pointer to the `AsciiStr`'s buffer. /// /// The caller must ensure that the slice outlives the pointer this function returns, or else it /// will end up pointing to garbage. Modifying the `AsciiStr` may cause it's buffer to be /// reallocated, which would also make any pointers to it invalid. #[inline] pub fn as_ptr(&self) -> *const AsciiChar { self.as_slice().as_ptr() } /// Returns an unsafe mutable pointer to the `AsciiStr`'s buffer. /// /// The caller must ensure that the slice outlives the pointer this function returns, or else it /// will end up pointing to garbage. Modifying the `AsciiStr` may cause it's buffer to be /// reallocated, which would also make any pointers to it invalid. #[inline] pub fn as_mut_ptr(&mut self) -> *mut AsciiChar { self.as_mut_slice().as_mut_ptr() } /// Copies the content of this `AsciiStr` into an owned `AsciiString`. #[cfg(feature = "std")] pub fn to_ascii_string(&self) -> AsciiString { AsciiString::from(self.slice.to_vec()) } /// Converts anything that can represent a byte slice into an `AsciiStr`. /// /// # Examples /// ``` /// # use ascii::AsciiStr; /// let foo = AsciiStr::from_ascii("foo"); /// let err = AsciiStr::from_ascii("Ŋ"); /// assert_eq!(foo.unwrap().as_str(), "foo"); /// assert_eq!(err.unwrap_err().valid_up_to(), 0); /// ``` #[inline] pub fn from_ascii(bytes: &B) -> Result<&AsciiStr, AsAsciiStrError> where B: AsRef<[u8]>, { bytes.as_ref().as_ascii_str() } /// Converts anything that can be represented as a byte slice to an `AsciiStr` without checking /// for non-ASCII characters.. /// /// # Examples /// ``` /// # use ascii::AsciiStr; /// let foo = unsafe{ AsciiStr::from_ascii_unchecked("foo") }; /// assert_eq!(foo.as_str(), "foo"); /// ``` #[inline] pub unsafe fn from_ascii_unchecked(bytes: &B) -> &AsciiStr where B: AsRef<[u8]>, { bytes.as_ref().as_ascii_str_unchecked() } /// Returns the number of characters / bytes in this ASCII sequence. /// /// # Examples /// ``` /// # use ascii::AsciiStr; /// let s = AsciiStr::from_ascii("foo").unwrap(); /// assert_eq!(s.len(), 3); /// ``` #[inline] pub fn len(&self) -> usize { self.slice.len() } /// Returns true if the ASCII slice contains zero bytes. /// /// # Examples /// ``` /// # use ascii::AsciiStr; /// let mut empty = AsciiStr::from_ascii("").unwrap(); /// let mut full = AsciiStr::from_ascii("foo").unwrap(); /// assert!(empty.is_empty()); /// assert!(!full.is_empty()); /// ``` #[inline] pub fn is_empty(&self) -> bool { self.len() == 0 } /// Returns an iterator over the characters of the `AsciiStr`. #[inline] pub fn chars(&self) -> Chars { self.slice.iter() } /// Returns an iterator over the characters of the `AsciiStr` which allows you to modify the /// value of each `AsciiChar`. #[inline] pub fn chars_mut(&mut self) -> CharsMut { self.slice.iter_mut() } /// Returns an iterator over parts of the `AsciiStr` separated by a character. /// /// # Examples /// ``` /// # use ascii::{AsciiStr, AsciiChar}; /// let words = AsciiStr::from_ascii("apple banana lemon").unwrap() /// .split(AsciiChar::Space) /// .map(|a| a.as_str()) /// .collect::>(); /// assert_eq!(words, ["apple", "banana", "lemon"]); /// ``` pub fn split(&self, on: AsciiChar) -> Split { Split { on: on, ended: false, chars: self.chars(), } } /// Returns an iterator over the lines of the `AsciiStr`, which are themselves `AsciiStr`s. /// /// Lines are ended with either `LineFeed` (`\n`), or `CarriageReturn` then `LineFeed` (`\r\n`). /// /// The final line ending is optional. #[inline] pub fn lines(&self) -> Lines { Lines { string: self, } } /// Returns an ASCII string slice with leading and trailing whitespace removed. /// /// # Examples /// ``` /// # use ascii::AsciiStr; /// let example = AsciiStr::from_ascii(" \twhite \tspace \t").unwrap(); /// assert_eq!("white \tspace", example.trim()); /// ``` pub fn trim(&self) -> &Self { self.trim_right().trim_left() } /// Returns an ASCII string slice with leading whitespace removed. /// /// # Examples /// ``` /// # use ascii::AsciiStr; /// let example = AsciiStr::from_ascii(" \twhite \tspace \t").unwrap(); /// assert_eq!("white \tspace \t", example.trim_left()); /// ``` pub fn trim_left(&self) -> &Self { &self[self.chars().take_while(|a| a.is_whitespace()).count()..] } /// Returns an ASCII string slice with trailing whitespace removed. /// /// # Examples /// ``` /// # use ascii::AsciiStr; /// let example = AsciiStr::from_ascii(" \twhite \tspace \t").unwrap(); /// assert_eq!(" \twhite \tspace", example.trim_right()); /// ``` pub fn trim_right(&self) -> &Self { let trimmed = self.chars() .rev() .take_while(|a| a.is_whitespace()) .count(); &self[..self.len() - trimmed] } /// Compares two strings case-insensitively. pub fn eq_ignore_ascii_case(&self, other: &Self) -> bool { self.len() == other.len() && self.chars().zip(other.chars()).all(|(a, b)| { a.eq_ignore_ascii_case(b) }) } /// Replaces lowercase letters with their uppercase equivalent. pub fn make_ascii_uppercase(&mut self) { for a in self.chars_mut() { *a = a.to_ascii_uppercase(); } } /// Replaces uppercase letters with their lowercase equivalent. pub fn make_ascii_lowercase(&mut self) { for a in self.chars_mut() { *a = a.to_ascii_lowercase(); } } /// Returns a copy of this string where letters 'a' to 'z' are mapped to 'A' to 'Z'. #[cfg(feature="std")] pub fn to_ascii_uppercase(&self) -> AsciiString { let mut ascii_string = self.to_ascii_string(); ascii_string.make_ascii_uppercase(); ascii_string } /// Returns a copy of this string where letters 'A' to 'Z' are mapped to 'a' to 'z'. #[cfg(feature="std")] pub fn to_ascii_lowercase(&self) -> AsciiString { let mut ascii_string = self.to_ascii_string(); ascii_string.make_ascii_lowercase(); ascii_string } /// Returns the first character if the string is not empty. #[inline] pub fn first(&self) -> Option { self.slice.first().cloned() } /// Returns the last character if the string is not empty. #[inline] pub fn last(&self) -> Option { self.slice.last().cloned() } } macro_rules! impl_partial_eq { ($wider: ty) => { impl PartialEq<$wider> for AsciiStr { #[inline] fn eq(&self, other: &$wider) -> bool { >::as_ref(self) == other } } impl PartialEq for $wider { #[inline] fn eq(&self, other: &AsciiStr) -> bool { self == >::as_ref(other) } } }; } impl_partial_eq!{str} impl_partial_eq!{[u8]} impl_partial_eq!{[AsciiChar]} #[cfg(feature = "std")] impl ToOwned for AsciiStr { type Owned = AsciiString; #[inline] fn to_owned(&self) -> AsciiString { self.to_ascii_string() } } impl AsRef<[u8]> for AsciiStr { #[inline] fn as_ref(&self) -> &[u8] { self.as_bytes() } } impl AsRef for AsciiStr { #[inline] fn as_ref(&self) -> &str { self.as_str() } } impl AsRef<[AsciiChar]> for AsciiStr { #[inline] fn as_ref(&self) -> &[AsciiChar] { &self.slice } } impl AsMut<[AsciiChar]> for AsciiStr { #[inline] fn as_mut(&mut self) -> &mut [AsciiChar] { &mut self.slice } } impl Default for &'static AsciiStr { #[inline] fn default() -> &'static AsciiStr { From::from(&[] as &[AsciiChar]) } } impl<'a> From<&'a [AsciiChar]> for &'a AsciiStr { #[inline] fn from(slice: &[AsciiChar]) -> &AsciiStr { let ptr = slice as *const [AsciiChar] as *const AsciiStr; unsafe { &*ptr } } } impl<'a> From<&'a mut [AsciiChar]> for &'a mut AsciiStr { #[inline] fn from(slice: &mut [AsciiChar]) -> &mut AsciiStr { let ptr = slice as *mut [AsciiChar] as *mut AsciiStr; unsafe { &mut *ptr } } } #[cfg(feature = "std")] impl From> for Box { #[inline] fn from(owned: Box<[AsciiChar]>) -> Box { let ptr = Box::into_raw(owned) as *mut AsciiStr; unsafe { Box::from_raw(ptr) } } } impl AsRef for [AsciiChar] { #[inline] fn as_ref(&self) -> &AsciiStr { self.into() } } impl AsMut for [AsciiChar] { #[inline] fn as_mut(&mut self) -> &mut AsciiStr { self.into() } } macro_rules! impl_into { ($wider: ty) => { impl<'a> From<&'a AsciiStr> for &'a$wider { #[inline] fn from(slice: &AsciiStr) -> &$wider { let ptr = slice as *const AsciiStr as *const $wider; unsafe { &*ptr } } } impl<'a> From<&'a mut AsciiStr> for &'a mut $wider { #[inline] fn from(slice: &mut AsciiStr) -> &mut $wider { let ptr = slice as *mut AsciiStr as *mut $wider; unsafe { &mut *ptr } } } #[cfg(feature = "std")] impl From> for Box<$wider> { #[inline] fn from(owned: Box) -> Box<$wider> { let ptr = Box::into_raw(owned) as *mut $wider; unsafe { Box::from_raw(ptr) } } } } } impl_into! {[AsciiChar]} impl_into! {[u8]} impl_into! {str} impl fmt::Display for AsciiStr { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self.as_str(), f) } } impl fmt::Debug for AsciiStr { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(self.as_str(), f) } } macro_rules! impl_index { ($idx:ty) => { impl Index<$idx> for AsciiStr { type Output = AsciiStr; #[inline] fn index(&self, index: $idx) -> &AsciiStr { self.slice[index].as_ref() } } impl IndexMut<$idx> for AsciiStr { #[inline] fn index_mut(&mut self, index: $idx) -> &mut AsciiStr { self.slice[index].as_mut() } } } } impl_index! { Range } impl_index! { RangeTo } impl_index! { RangeFrom } impl_index! { RangeFull } impl Index for AsciiStr { type Output = AsciiChar; #[inline] fn index(&self, index: usize) -> &AsciiChar { &self.slice[index] } } impl IndexMut for AsciiStr { #[inline] fn index_mut(&mut self, index: usize) -> &mut AsciiChar { &mut self.slice[index] } } #[cfg(feature = "std")] impl AsciiExt for AsciiStr { type Owned = AsciiString; #[inline] fn is_ascii(&self) -> bool { true } fn to_ascii_uppercase(&self) -> AsciiString { let mut ascii_string = self.to_ascii_string(); ascii_string.make_ascii_uppercase(); ascii_string } fn to_ascii_lowercase(&self) -> AsciiString { let mut ascii_string = self.to_ascii_string(); ascii_string.make_ascii_lowercase(); ascii_string } fn eq_ignore_ascii_case(&self, other: &Self) -> bool { self.len() == other.len() && self.chars().zip(other.chars()).all(|(a, b)| { a.eq_ignore_ascii_case(b) }) } fn make_ascii_uppercase(&mut self) { for ascii in self.chars_mut() { ascii.make_ascii_uppercase(); } } fn make_ascii_lowercase(&mut self) { for ascii in self.chars_mut() { ascii.make_ascii_lowercase(); } } } impl<'a> IntoIterator for &'a AsciiStr { type Item = &'a AsciiChar; type IntoIter = Chars<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.chars() } } impl<'a> IntoIterator for &'a mut AsciiStr { type Item = &'a mut AsciiChar; type IntoIter = CharsMut<'a>; #[inline] fn into_iter(self) -> Self::IntoIter { self.chars_mut() } } /// An immutable iterator over the characters of an `AsciiStr`. pub type Chars<'a> = Iter<'a, AsciiChar>; /// A mutable iterator over the characters of an `AsciiStr`. pub type CharsMut<'a> = IterMut<'a, AsciiChar>; /// An iterator over parts of an `AsciiStr` separated by an `AsciiChar`. /// /// This type is created by [`AsciiChar::split()`](struct.AsciiChar.html#method.split). #[derive(Clone, Debug)] pub struct Split<'a> { on: AsciiChar, ended: bool, chars: Chars<'a> } impl<'a> Iterator for Split<'a> { type Item = &'a AsciiStr; fn next(&mut self) -> Option<&'a AsciiStr> { if !self.ended { let start: &AsciiStr = self.chars.as_slice().into(); let split_on = self.on; if let Some(at) = self.chars.position(|&c| c == split_on) { Some(&start[..at]) } else { self.ended = true; Some(start) } } else { None } } } impl<'a> DoubleEndedIterator for Split<'a> { fn next_back(&mut self) -> Option<&'a AsciiStr> { if !self.ended { let start: &AsciiStr = self.chars.as_slice().into(); let split_on = self.on; if let Some(at) = self.chars.rposition(|&c| c == split_on) { Some(&start[at+1..]) } else { self.ended = true; Some(start) } } else { None } } } /// An iterator over the lines of the internal character array. #[derive(Clone, Debug)] pub struct Lines<'a> { string: &'a AsciiStr, } impl<'a> Iterator for Lines<'a> { type Item = &'a AsciiStr; fn next(&mut self) -> Option<&'a AsciiStr> { if let Some(idx) = self.string .chars() .position(|&chr| chr == AsciiChar::LineFeed) { let line = if idx > 0 && self.string[idx - 1] == AsciiChar::CarriageReturn { &self.string[..idx - 1] } else { &self.string[..idx] }; self.string = &self.string[idx + 1..]; Some(line) } else if self.string.is_empty() { None } else { let line = self.string; self.string = &self.string[..0]; Some(line) } } } impl<'a> DoubleEndedIterator for Lines<'a> { fn next_back(&mut self) -> Option<&'a AsciiStr> { if self.string.is_empty() { return None; } let mut i = self.string.len(); if self.string[i-1] == AsciiChar::LineFeed { i -= 1; if i > 0 && self.string[i-1] == AsciiChar::CarriageReturn { i -= 1; } } self.string = &self.string[..i]; while i > 0 && self.string[i-1] != AsciiChar::LineFeed { i -= 1; } let line = &self.string[i..]; self.string = &self.string[..i]; Some(line) } } /// Error that is returned when a sequence of `u8` are not all ASCII. /// /// Is used by `As[Mut]AsciiStr` and the `from_ascii` method on `AsciiStr` and `AsciiString`. #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct AsAsciiStrError(usize); const ERRORMSG_STR: &'static str = "one or more bytes are not ASCII"; impl AsAsciiStrError { /// Returns the index of the first non-ASCII byte. /// /// It is the maximum index such that `from_ascii(input[..index])` would return `Ok(_)`. #[inline] pub fn valid_up_to(self) -> usize { self.0 } #[cfg(not(feature = "std"))] /// Returns a description for this error, like `std::error::Error::description`. #[inline] pub fn description(&self) -> &'static str { ERRORMSG_STR } } impl fmt::Display for AsAsciiStrError { fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result { write!(fmtr, "the byte at index {} is not ASCII", self.0) } } #[cfg(feature = "std")] impl Error for AsAsciiStrError { #[inline] fn description(&self) -> &'static str { ERRORMSG_STR } } /// Convert slices of bytes to `AsciiStr`. pub trait AsAsciiStr { /// Convert to an ASCII slice without checking for non-ASCII characters. unsafe fn as_ascii_str_unchecked(&self) -> &AsciiStr; /// Convert to an ASCII slice. fn as_ascii_str(&self) -> Result<&AsciiStr, AsAsciiStrError>; } /// Convert mutable slices of bytes to `AsciiStr`. pub trait AsMutAsciiStr { /// Convert to a mutable ASCII slice without checking for non-ASCII characters. unsafe fn as_mut_ascii_str_unchecked(&mut self) -> &mut AsciiStr; /// Convert to a mutable ASCII slice. fn as_mut_ascii_str(&mut self) -> Result<&mut AsciiStr, AsAsciiStrError>; } // These generic implementations mirror the generic implementations for AsRef in core. impl<'a, T: ?Sized> AsAsciiStr for &'a T where T: AsAsciiStr { #[inline] fn as_ascii_str(&self) -> Result<&AsciiStr, AsAsciiStrError> { ::as_ascii_str(*self) } #[inline] unsafe fn as_ascii_str_unchecked(&self) -> &AsciiStr { ::as_ascii_str_unchecked(*self) } } impl<'a, T: ?Sized> AsAsciiStr for &'a mut T where T: AsAsciiStr { #[inline] fn as_ascii_str(&self) -> Result<&AsciiStr, AsAsciiStrError> { ::as_ascii_str(*self) } #[inline] unsafe fn as_ascii_str_unchecked(&self) -> &AsciiStr { ::as_ascii_str_unchecked(*self) } } impl<'a, T: ?Sized> AsMutAsciiStr for &'a mut T where T: AsMutAsciiStr { #[inline] fn as_mut_ascii_str(&mut self) -> Result<&mut AsciiStr, AsAsciiStrError> { ::as_mut_ascii_str(*self) } #[inline] unsafe fn as_mut_ascii_str_unchecked(&mut self) -> &mut AsciiStr { ::as_mut_ascii_str_unchecked(*self) } } impl AsAsciiStr for AsciiStr { #[inline] fn as_ascii_str(&self) -> Result<&AsciiStr, AsAsciiStrError> { Ok(self) } #[inline] unsafe fn as_ascii_str_unchecked(&self) -> &AsciiStr { self } } impl AsMutAsciiStr for AsciiStr { #[inline] fn as_mut_ascii_str(&mut self) -> Result<&mut AsciiStr, AsAsciiStrError> { Ok(self) } #[inline] unsafe fn as_mut_ascii_str_unchecked(&mut self) -> &mut AsciiStr { self } } impl AsAsciiStr for [AsciiChar] { #[inline] fn as_ascii_str(&self) -> Result<&AsciiStr, AsAsciiStrError> { Ok(self.into()) } #[inline] unsafe fn as_ascii_str_unchecked(&self) -> &AsciiStr { self.into() } } impl AsMutAsciiStr for [AsciiChar] { #[inline] fn as_mut_ascii_str(&mut self) -> Result<&mut AsciiStr, AsAsciiStrError> { Ok(self.into()) } #[inline] unsafe fn as_mut_ascii_str_unchecked(&mut self) -> &mut AsciiStr { self.into() } } impl AsAsciiStr for [u8] { fn as_ascii_str(&self) -> Result<&AsciiStr, AsAsciiStrError> { match self.iter().position(|&b| b > 127) { Some(index) => Err(AsAsciiStrError(index)), None => unsafe { Ok(self.as_ascii_str_unchecked()) }, } } #[inline] unsafe fn as_ascii_str_unchecked(&self) -> &AsciiStr { let ptr = self as *const [u8] as *const AsciiStr; &*ptr } } impl AsMutAsciiStr for [u8] { fn as_mut_ascii_str(&mut self) -> Result<&mut AsciiStr, AsAsciiStrError> { match self.iter().position(|&b| b > 127) { Some(index) => Err(AsAsciiStrError(index)), None => unsafe { Ok(self.as_mut_ascii_str_unchecked()) }, } } #[inline] unsafe fn as_mut_ascii_str_unchecked(&mut self) -> &mut AsciiStr { let ptr = self as *mut [u8] as *mut AsciiStr; &mut *ptr } } impl AsAsciiStr for str { fn as_ascii_str(&self) -> Result<&AsciiStr, AsAsciiStrError> { self.as_bytes().as_ascii_str() } #[inline] unsafe fn as_ascii_str_unchecked(&self) -> &AsciiStr { self.as_bytes().as_ascii_str_unchecked() } } impl AsMutAsciiStr for str { fn as_mut_ascii_str(&mut self) -> Result<&mut AsciiStr, AsAsciiStrError> { match self.bytes().position(|b| b > 127) { Some(index) => Err(AsAsciiStrError(index)), None => unsafe { Ok(self.as_mut_ascii_str_unchecked()) }, } } #[inline] unsafe fn as_mut_ascii_str_unchecked(&mut self) -> &mut AsciiStr { let ptr = self as *mut str as *mut AsciiStr; &mut *ptr } } /// Note that the trailing null byte will be removed in the conversion. #[cfg(feature = "std")] impl AsAsciiStr for CStr { #[inline] fn as_ascii_str(&self) -> Result<&AsciiStr, AsAsciiStrError> { self.to_bytes().as_ascii_str() } #[inline] unsafe fn as_ascii_str_unchecked(&self) -> &AsciiStr { self.to_bytes().as_ascii_str_unchecked() } } #[cfg(test)] mod tests { use AsciiChar; use super::{AsciiStr, AsAsciiStr, AsMutAsciiStr, AsAsciiStrError}; #[test] fn generic_as_ascii_str() { fn generic(c: &C) -> Result<&AsciiStr, AsAsciiStrError> { c.as_ascii_str() } let arr = [AsciiChar::A]; let ascii_str: &AsciiStr = arr.as_ref().into(); assert_eq!(generic("A"), Ok(ascii_str)); assert_eq!(generic(&b"A"[..]), Ok(ascii_str)); assert_eq!(generic(ascii_str), Ok(ascii_str)); assert_eq!(generic(&"A"), Ok(ascii_str)); assert_eq!(generic(&ascii_str), Ok(ascii_str)); assert_eq!(generic(&mut "A"), Ok(ascii_str)); } #[cfg(feature = "std")] #[test] fn cstring_as_ascii_str() { use std::ffi::CString; fn generic(c: &C) -> Result<&AsciiStr, AsAsciiStrError> { c.as_ascii_str() } let arr = [AsciiChar::A]; let ascii_str: &AsciiStr = arr.as_ref().into(); let cstr = CString::new("A").unwrap(); assert_eq!(generic(&*cstr), Ok(ascii_str)); } #[test] fn generic_as_mut_ascii_str() { fn generic_mut( c: &mut C, ) -> Result<&mut AsciiStr, AsAsciiStrError> { c.as_mut_ascii_str() } let mut arr_mut = [AsciiChar::B]; let mut ascii_str_mut: &mut AsciiStr = arr_mut.as_mut().into(); // Need a second reference to prevent overlapping mutable borrows let mut arr_mut_2 = [AsciiChar::B]; let ascii_str_mut_2: &mut AsciiStr = arr_mut_2.as_mut().into(); assert_eq!(generic_mut(&mut ascii_str_mut), Ok(&mut *ascii_str_mut_2)); assert_eq!(generic_mut(ascii_str_mut), Ok(&mut *ascii_str_mut_2)); } #[test] #[cfg(feature = "std")] fn as_ascii_str() { macro_rules! err {{$i:expr} => {Err(AsAsciiStrError($i))}} let mut s: String = "abčd".to_string(); let mut b: Vec = s.clone().into(); assert_eq!(s.as_str().as_ascii_str(), err!(2)); assert_eq!(s.as_mut_str().as_mut_ascii_str(), err!(2)); assert_eq!(b.as_slice().as_ascii_str(), err!(2)); assert_eq!(b.as_mut_slice().as_mut_ascii_str(), err!(2)); let mut a = [AsciiChar::a, AsciiChar::b]; assert_eq!((&s[..2]).as_ascii_str(), Ok((&a[..]).into())); assert_eq!((&b[..2]).as_ascii_str(), Ok((&a[..]).into())); let a = Ok((&mut a[..]).into()); assert_eq!((&mut s[..2]).as_mut_ascii_str(), a); assert_eq!((&mut b[..2]).as_mut_ascii_str(), a); } #[test] fn default() { let default: &'static AsciiStr = Default::default(); assert!(default.is_empty()); } #[test] fn as_str() { let b = b"( ;"; let v = AsciiStr::from_ascii(b).unwrap(); assert_eq!(v.as_str(), "( ;"); assert_eq!(AsRef::::as_ref(v), "( ;"); } #[test] fn as_bytes() { let b = b"( ;"; let v = AsciiStr::from_ascii(b).unwrap(); assert_eq!(v.as_bytes(), b"( ;"); assert_eq!(AsRef::<[u8]>::as_ref(v), b"( ;"); } #[test] fn make_ascii_case() { let mut bytes = ([b'a', b'@', b'A'], [b'A', b'@', b'a']); let a = bytes.0.as_mut_ascii_str().unwrap(); let b = bytes.1.as_mut_ascii_str().unwrap(); assert!(a.eq_ignore_ascii_case(b)); assert!(b.eq_ignore_ascii_case(a)); a.make_ascii_lowercase(); b.make_ascii_uppercase(); assert_eq!(a, "a@a"); assert_eq!(b, "A@A"); } #[test] #[cfg(feature = "std")] fn to_ascii_case() { let bytes = ([b'a', b'@', b'A'], [b'A', b'@', b'a']); let a = bytes.0.as_ascii_str().unwrap(); let b = bytes.1.as_ascii_str().unwrap(); assert_eq!(a.to_ascii_lowercase().as_str(), "a@a"); assert_eq!(a.to_ascii_uppercase().as_str(), "A@A"); assert_eq!(b.to_ascii_lowercase().as_str(), "a@a"); assert_eq!(b.to_ascii_uppercase().as_str(), "A@A"); } #[test] #[cfg(feature = "std")] fn ascii_ext() { #[allow(deprecated)] use std::ascii::AsciiExt; assert!(AsciiExt::is_ascii(<&AsciiStr>::default())); let mut mutable = String::from("a@AA@a"); let parts = mutable.split_at_mut(3); let a = parts.0.as_mut_ascii_str().unwrap(); let b = parts.1.as_mut_ascii_str().unwrap(); assert!(AsciiExt::eq_ignore_ascii_case(a, b)); assert_eq!(AsciiExt::to_ascii_lowercase(a).as_str(), "a@a"); assert_eq!(AsciiExt::to_ascii_uppercase(b).as_str(), "A@A"); AsciiExt::make_ascii_uppercase(a); AsciiExt::make_ascii_lowercase(b); assert_eq!(a, "A@A"); assert_eq!(b, "a@a"); } #[test] fn chars_iter() { let chars = &[b'h', b'e', b'l', b'l', b'o', b' ', b'w', b'o', b'r', b'l', b'd', b'\0']; let ascii = AsciiStr::from_ascii(chars).unwrap(); for (achar, byte) in ascii.chars().zip(chars.iter()) { assert_eq!(achar, byte); } } #[test] fn chars_iter_mut() { let chars = &mut [b'h', b'e', b'l', b'l', b'o', b' ', b'w', b'o', b'r', b'l', b'd', b'\0']; let ascii = chars.as_mut_ascii_str().unwrap(); *ascii.chars_mut().next().unwrap() = AsciiChar::H; assert_eq!(ascii[0], b'H'); } #[test] fn lines_iter() { use core::iter::Iterator; let lines: [&str; 4] = ["foo", "bar", "", "baz"]; let joined = "foo\r\nbar\n\nbaz\n"; let ascii = AsciiStr::from_ascii(joined.as_bytes()).unwrap(); for (asciiline, line) in ascii.lines().zip(&lines) { assert_eq!(asciiline, *line); } assert_eq!(ascii.lines().count(), lines.len()); let lines: [&str; 4] = ["foo", "bar", "", "baz"]; let joined = "foo\r\nbar\n\nbaz"; let ascii = AsciiStr::from_ascii(joined.as_bytes()).unwrap(); for (asciiline, line) in ascii.lines().zip(&lines) { assert_eq!(asciiline, *line); } assert_eq!(ascii.lines().count(), lines.len()); let trailing_line_break = b"\n"; let ascii = AsciiStr::from_ascii(&trailing_line_break).unwrap(); let mut line_iter = ascii.lines(); assert_eq!(line_iter.next(), Some(AsciiStr::from_ascii("").unwrap())); assert_eq!(line_iter.next(), None); let empty_lines = b"\n\r\n\n\r\n"; let mut iter_count = 0; let ascii = AsciiStr::from_ascii(&empty_lines).unwrap(); for line in ascii.lines() { iter_count += 1; assert!(line.is_empty()); } assert_eq!(4, iter_count); } #[test] fn lines_iter_rev() { let joined = "foo\r\nbar\n\nbaz\n"; let ascii = AsciiStr::from_ascii(joined.as_bytes()).unwrap(); assert_eq!(ascii.lines().rev().count(), 4); assert_eq!(ascii.lines().rev().count(), joined.lines().rev().count()); for (asciiline, line) in ascii.lines().rev().zip(joined.lines().rev()) { assert_eq!(asciiline, line); } let mut iter = ascii.lines(); assert_eq!(iter.next(), Some("foo".as_ascii_str().unwrap())); assert_eq!(iter.next_back(), Some("baz".as_ascii_str().unwrap())); assert_eq!(iter.next_back(), Some("".as_ascii_str().unwrap())); assert_eq!(iter.next(), Some("bar".as_ascii_str().unwrap())); } #[test] fn lines_iter_empty() { assert_eq!("".as_ascii_str().unwrap().lines().next(), None); assert_eq!("".as_ascii_str().unwrap().lines().next_back(), None); assert_eq!("".lines().next(), None); } #[test] fn split_str() { fn split_equals_str(haystack: &str, needle: char) { let mut strs = haystack.split(needle); let mut asciis = haystack.as_ascii_str().unwrap() .split(AsciiChar::from(needle).unwrap()) .map(|a| a.as_str()); loop { assert_eq!(asciis.size_hint(), strs.size_hint()); let (a, s) = (asciis.next(), strs.next()); assert_eq!(a, s); if a == None { break; } } // test fusedness if str's version is fused if strs.next() == None { assert_eq!(asciis.next(), None); } } split_equals_str("", '='); split_equals_str("1,2,3", ','); split_equals_str("foo;bar;baz;", ';'); split_equals_str("|||", '|'); split_equals_str(" a b c ", ' '); } #[test] fn split_str_rev() { let words = " foo bar baz "; let ascii = words.as_ascii_str().unwrap(); for (word, asciiword) in words.split(' ').rev().zip(ascii.split(AsciiChar::Space).rev()) { assert_eq!(asciiword, word); } let mut iter = ascii.split(AsciiChar::Space); assert_eq!(iter.next(), Some("".as_ascii_str().unwrap())); assert_eq!(iter.next_back(), Some("".as_ascii_str().unwrap())); assert_eq!(iter.next(), Some("foo".as_ascii_str().unwrap())); assert_eq!(iter.next_back(), Some("baz".as_ascii_str().unwrap())); assert_eq!(iter.next_back(), Some("bar".as_ascii_str().unwrap())); assert_eq!(iter.next(), Some("".as_ascii_str().unwrap())); assert_eq!(iter.next_back(), None); } #[test] fn split_str_empty() { let empty = <&AsciiStr>::default(); let mut iter = empty.split(AsciiChar::NAK); assert_eq!(iter.next(), Some(empty)); assert_eq!(iter.next(), None); let mut iter = empty.split(AsciiChar::NAK); assert_eq!(iter.next_back(), Some(empty)); assert_eq!(iter.next_back(), None); assert_eq!("".split('s').next(), Some("")); // str.split() also produces one element } #[test] #[cfg(feature = "std")] fn fmt_ascii_str() { let s = "abc".as_ascii_str().unwrap(); assert_eq!(format!("{}", s), "abc".to_string()); assert_eq!(format!("{:?}", s), "\"abc\"".to_string()); } } ascii-0.9.2/src/ascii_string.rs010064400017500001731000000670031351034031700146500ustar0000000000000000#![cfg_attr(rustfmt, rustfmt_skip)] use std::{fmt, mem}; use std::borrow::{Borrow, BorrowMut, Cow}; use std::error::Error; use std::ffi::{CStr, CString}; use std::any::Any; use std::str::FromStr; use std::ops::{Deref, DerefMut, Add, AddAssign, Index, IndexMut}; use std::iter::FromIterator; #[cfg(feature = "quickcheck")] use quickcheck::{Arbitrary, Gen}; use ascii_char::AsciiChar; use ascii_str::{AsciiStr, AsAsciiStr, AsAsciiStrError}; /// A growable string stored as an ASCII encoded buffer. #[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct AsciiString { vec: Vec, } impl AsciiString { /// Creates a new, empty ASCII string buffer without allocating. /// /// # Examples /// ``` /// # use ascii::AsciiString; /// let mut s = AsciiString::new(); /// ``` #[inline] pub fn new() -> Self { AsciiString { vec: Vec::new() } } /// Creates a new ASCII string buffer with the given capacity. /// The string will be able to hold exactly `capacity` bytes without reallocating. /// If `capacity` is 0, the ASCII string will not allocate. /// /// # Examples /// ``` /// # use ascii::AsciiString; /// let mut s = AsciiString::with_capacity(10); /// ``` #[inline] pub fn with_capacity(capacity: usize) -> Self { AsciiString { vec: Vec::with_capacity(capacity) } } /// Creates a new `AsciiString` from a length, capacity and pointer. /// /// # Safety /// /// This is highly unsafe, due to the number of invariants that aren't checked: /// /// * The memory at `ptr` need to have been previously allocated by the same allocator this /// library uses. /// * `length` needs to be less than or equal to `capacity`. /// * `capacity` needs to be the correct value. /// /// Violating these may cause problems like corrupting the allocator's internal datastructures. /// /// # Examples /// /// Basic usage: /// /// ``` /// # use ascii::AsciiString; /// use std::mem; /// /// unsafe { /// let s = AsciiString::from_ascii("hello").unwrap(); /// let ptr = s.as_ptr(); /// let len = s.len(); /// let capacity = s.capacity(); /// /// mem::forget(s); /// /// let s = AsciiString::from_raw_parts(ptr as *mut _, len, capacity); /// /// assert_eq!(AsciiString::from_ascii("hello").unwrap(), s); /// } /// ``` #[inline] pub unsafe fn from_raw_parts(buf: *mut AsciiChar, length: usize, capacity: usize) -> Self { AsciiString { vec: Vec::from_raw_parts(buf, length, capacity) } } /// Converts a vector of bytes to an `AsciiString` without checking for non-ASCII characters. /// /// # Safety /// This function is unsafe because it does not check that the bytes passed to it are valid /// ASCII characters. If this constraint is violated, it may cause memory unsafety issues with /// future of the `AsciiString`, as the rest of this library assumes that `AsciiString`s are /// ASCII encoded. #[inline] pub unsafe fn from_ascii_unchecked(bytes: B) -> Self where B: Into>, { let mut bytes = bytes.into(); let vec = Vec::from_raw_parts( bytes.as_mut_ptr() as *mut AsciiChar, bytes.len(), bytes.capacity(), ); mem::forget(bytes); AsciiString { vec: vec } } /// Converts anything that can represent a byte buffer into an `AsciiString`. /// /// # Failure /// Returns the byte buffer if not all of the bytes are ASCII characters. /// /// # Examples /// ``` /// # use ascii::AsciiString; /// let foo = AsciiString::from_ascii("foo".to_string()).unwrap(); /// let err = AsciiString::from_ascii("Ŋ".to_string()).unwrap_err(); /// assert_eq!(foo.as_str(), "foo"); /// assert_eq!(err.into_source(), "Ŋ"); /// ``` pub fn from_ascii(bytes: B) -> Result> where B: Into> + AsRef<[u8]>, { unsafe { match bytes.as_ref().as_ascii_str() { Ok(_) => Ok(AsciiString::from_ascii_unchecked(bytes)), Err(e) => Err(FromAsciiError { error: e, owner: bytes, }), } } } /// Pushes the given ASCII string onto this ASCII string buffer. /// /// # Examples /// ``` /// # use ascii::{AsciiString, AsAsciiStr}; /// use std::str::FromStr; /// let mut s = AsciiString::from_str("foo").unwrap(); /// s.push_str("bar".as_ascii_str().unwrap()); /// assert_eq!(s, "foobar".as_ascii_str().unwrap()); /// ``` #[inline] pub fn push_str(&mut self, string: &AsciiStr) { self.vec.extend(string.chars()) } /// Returns the number of bytes that this ASCII string buffer can hold without reallocating. /// /// # Examples /// ``` /// # use ascii::AsciiString; /// let s = String::with_capacity(10); /// assert!(s.capacity() >= 10); /// ``` #[inline] pub fn capacity(&self) -> usize { self.vec.capacity() } /// Reserves capacity for at least `additional` more bytes to be inserted in the given /// `AsciiString`. The collection may reserve more space to avoid frequent reallocations. /// /// # Panics /// Panics if the new capacity overflows `usize`. /// /// # Examples /// ``` /// # use ascii::AsciiString; /// let mut s = AsciiString::new(); /// s.reserve(10); /// assert!(s.capacity() >= 10); /// ``` #[inline] pub fn reserve(&mut self, additional: usize) { self.vec.reserve(additional) } /// Reserves the minimum capacity for exactly `additional` more bytes to be inserted in the /// given `AsciiString`. Does nothing if the capacity is already sufficient. /// /// Note that the allocator may give the collection more space than it requests. Therefore /// capacity can not be relied upon to be precisely minimal. Prefer `reserve` if future /// insertions are expected. /// /// # Panics /// Panics if the new capacity overflows `usize`. /// /// # Examples /// ``` /// # use ascii::AsciiString; /// let mut s = AsciiString::new(); /// s.reserve_exact(10); /// assert!(s.capacity() >= 10); /// ``` #[inline] pub fn reserve_exact(&mut self, additional: usize) { self.vec.reserve_exact(additional) } /// Shrinks the capacity of this ASCII string buffer to match it's length. /// /// # Examples /// ``` /// # use ascii::AsciiString; /// use std::str::FromStr; /// let mut s = AsciiString::from_str("foo").unwrap(); /// s.reserve(100); /// assert!(s.capacity() >= 100); /// s.shrink_to_fit(); /// assert_eq!(s.capacity(), 3); /// ``` #[inline] pub fn shrink_to_fit(&mut self) { self.vec.shrink_to_fit() } /// Adds the given ASCII character to the end of the ASCII string. /// /// # Examples /// ``` /// # use ascii::{ AsciiChar, AsciiString}; /// let mut s = AsciiString::from_ascii("abc").unwrap(); /// s.push(AsciiChar::from('1').unwrap()); /// s.push(AsciiChar::from('2').unwrap()); /// s.push(AsciiChar::from('3').unwrap()); /// assert_eq!(s, "abc123"); /// ``` #[inline] pub fn push(&mut self, ch: AsciiChar) { self.vec.push(ch) } /// Shortens a ASCII string to the specified length. /// /// # Panics /// Panics if `new_len` > current length. /// /// # Examples /// ``` /// # use ascii::AsciiString; /// let mut s = AsciiString::from_ascii("hello").unwrap(); /// s.truncate(2); /// assert_eq!(s, "he"); /// ``` #[inline] pub fn truncate(&mut self, new_len: usize) { self.vec.truncate(new_len) } /// Removes the last character from the ASCII string buffer and returns it. /// Returns `None` if this string buffer is empty. /// /// # Examples /// ``` /// # use ascii::AsciiString; /// let mut s = AsciiString::from_ascii("foo").unwrap(); /// assert_eq!(s.pop().map(|c| c.as_char()), Some('o')); /// assert_eq!(s.pop().map(|c| c.as_char()), Some('o')); /// assert_eq!(s.pop().map(|c| c.as_char()), Some('f')); /// assert_eq!(s.pop(), None); /// ``` #[inline] pub fn pop(&mut self) -> Option { self.vec.pop() } /// Removes the ASCII character at position `idx` from the buffer and returns it. /// /// # Warning /// This is an O(n) operation as it requires copying every element in the buffer. /// /// # Panics /// If `idx` is out of bounds this function will panic. /// /// # Examples /// ``` /// # use ascii::AsciiString; /// let mut s = AsciiString::from_ascii("foo").unwrap(); /// assert_eq!(s.remove(0).as_char(), 'f'); /// assert_eq!(s.remove(1).as_char(), 'o'); /// assert_eq!(s.remove(0).as_char(), 'o'); /// ``` #[inline] pub fn remove(&mut self, idx: usize) -> AsciiChar { self.vec.remove(idx) } /// Inserts an ASCII character into the buffer at position `idx`. /// /// # Warning /// This is an O(n) operation as it requires copying every element in the buffer. /// /// # Panics /// If `idx` is out of bounds this function will panic. /// /// # Examples /// ``` /// # use ascii::{AsciiString,AsciiChar}; /// let mut s = AsciiString::from_ascii("foo").unwrap(); /// s.insert(2, AsciiChar::b); /// assert_eq!(s, "fobo"); /// ``` #[inline] pub fn insert(&mut self, idx: usize, ch: AsciiChar) { self.vec.insert(idx, ch) } /// Returns the number of bytes in this ASCII string. /// /// # Examples /// ``` /// # use ascii::AsciiString; /// let s = AsciiString::from_ascii("foo").unwrap(); /// assert_eq!(s.len(), 3); /// ``` #[inline] pub fn len(&self) -> usize { self.vec.len() } /// Returns true if the ASCII string contains zero bytes. /// /// # Examples /// ``` /// # use ascii::{AsciiChar, AsciiString}; /// let mut s = AsciiString::new(); /// assert!(s.is_empty()); /// s.push(AsciiChar::from('a').unwrap()); /// assert!(!s.is_empty()); /// ``` #[inline] pub fn is_empty(&self) -> bool { self.len() == 0 } /// Truncates the ASCII string, setting length (but not capacity) to zero. /// /// # Examples /// ``` /// # use ascii::AsciiString; /// let mut s = AsciiString::from_ascii("foo").unwrap(); /// s.clear(); /// assert!(s.is_empty()); /// ``` #[inline] pub fn clear(&mut self) { self.vec.clear() } } impl Deref for AsciiString { type Target = AsciiStr; #[inline] fn deref(&self) -> &AsciiStr { self.vec.as_slice().as_ref() } } impl DerefMut for AsciiString { #[inline] fn deref_mut(&mut self) -> &mut AsciiStr { self.vec.as_mut_slice().as_mut() } } impl PartialEq for AsciiString { #[inline] fn eq(&self, other: &str) -> bool { **self == *other } } impl PartialEq for str { #[inline] fn eq(&self, other: &AsciiString) -> bool { **other == *self } } macro_rules! impl_eq { ($lhs:ty, $rhs:ty) => { impl<'a> PartialEq<$rhs> for $lhs { #[inline] fn eq(&self, other: &$rhs) -> bool { PartialEq::eq(&**self, &**other) } #[inline] fn ne(&self, other: &$rhs) -> bool { PartialEq::ne(&**self, &**other) } } } } impl_eq! { AsciiString, String } impl_eq! { String, AsciiString } impl_eq! { &'a AsciiStr, String } impl_eq! { String, &'a AsciiStr } impl_eq! { &'a AsciiStr, AsciiString } impl_eq! { AsciiString, &'a AsciiStr } impl_eq! { &'a str, AsciiString } impl_eq! { AsciiString, &'a str } impl Borrow for AsciiString { #[inline] fn borrow(&self) -> &AsciiStr { &*self } } impl BorrowMut for AsciiString { #[inline] fn borrow_mut(&mut self) -> &mut AsciiStr { &mut*self } } impl From> for AsciiString { #[inline] fn from(vec: Vec) -> Self { AsciiString { vec: vec } } } impl Into> for AsciiString { fn into(self) -> Vec { unsafe { let v = Vec::from_raw_parts( self.vec.as_ptr() as *mut u8, self.vec.len(), self.vec.capacity(), ); // We forget `self` to avoid freeing it at the end of the scope. // Otherwise, the returned `Vec` would point to freed memory. mem::forget(self); v } } } impl<'a> From<&'a AsciiStr> for AsciiString { #[inline] fn from(s: &'a AsciiStr) -> Self { s.to_ascii_string() } } impl<'a> From<&'a [AsciiChar]> for AsciiString { #[inline] fn from(s: &'a [AsciiChar]) -> AsciiString { s.into_iter().map(|c| *c).collect() } } impl Into for AsciiString { #[inline] fn into(self) -> String { unsafe { String::from_utf8_unchecked(self.into()) } } } impl<'a> From> for AsciiString { fn from(cow: Cow<'a,AsciiStr>) -> AsciiString { cow.into_owned() } } impl From for Cow<'static,AsciiStr> { fn from(string: AsciiString) -> Cow<'static,AsciiStr> { Cow::Owned(string) } } impl<'a> From<&'a AsciiStr> for Cow<'a,AsciiStr> { fn from(s: &'a AsciiStr) -> Cow<'a,AsciiStr> { Cow::Borrowed(s) } } impl AsRef for AsciiString { #[inline] fn as_ref(&self) -> &AsciiStr { &*self } } impl AsRef<[AsciiChar]> for AsciiString { #[inline] fn as_ref(&self) -> &[AsciiChar] { &self.vec } } impl AsRef<[u8]> for AsciiString { #[inline] fn as_ref(&self) -> &[u8] { self.as_bytes() } } impl AsRef for AsciiString { #[inline] fn as_ref(&self) -> &str { self.as_str() } } impl AsMut for AsciiString { #[inline] fn as_mut(&mut self) -> &mut AsciiStr { &mut *self } } impl AsMut<[AsciiChar]> for AsciiString { #[inline] fn as_mut(&mut self) -> &mut [AsciiChar] { &mut self.vec } } impl FromStr for AsciiString { type Err = AsAsciiStrError; fn from_str(s: &str) -> Result { s.as_ascii_str().map(AsciiStr::to_ascii_string) } } impl fmt::Display for AsciiString { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&**self, f) } } impl fmt::Debug for AsciiString { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } /// Please note that the `std::fmt::Result` returned by these methods does not support /// transmission of an error other than that an error occurred. impl fmt::Write for AsciiString { fn write_str(&mut self, s: &str) -> fmt::Result { let astr = try!(AsciiStr::from_ascii(s).map_err(|_| fmt::Error)); self.push_str(astr); Ok(()) } fn write_char(&mut self, c: char) -> fmt::Result { let achar = try!(AsciiChar::from(c).map_err(|_| fmt::Error)); self.push(achar); Ok(()) } } impl FromIterator for AsciiString { fn from_iter>(iter: I) -> AsciiString { let mut buf = AsciiString::new(); buf.extend(iter); buf } } impl<'a> FromIterator<&'a AsciiStr> for AsciiString { fn from_iter>(iter: I) -> AsciiString { let mut buf = AsciiString::new(); buf.extend(iter); buf } } impl<'a> FromIterator> for AsciiString { fn from_iter>>(iter: I) -> AsciiString { let mut buf = AsciiString::new(); buf.extend(iter); buf } } impl Extend for AsciiString { fn extend>(&mut self, iterable: I) { let iterator = iterable.into_iter(); let (lower_bound, _) = iterator.size_hint(); self.reserve(lower_bound); for ch in iterator { self.push(ch) } } } impl<'a> Extend<&'a AsciiChar> for AsciiString { fn extend>(&mut self, iter: I) { self.extend(iter.into_iter().cloned()) } } impl<'a> Extend<&'a AsciiStr> for AsciiString { fn extend>(&mut self, iterable: I) { let iterator = iterable.into_iter(); let (lower_bound, _) = iterator.size_hint(); self.reserve(lower_bound); for s in iterator { self.push_str(s) } } } impl<'a> Extend> for AsciiString { fn extend>>(&mut self, iterable: I) { let iterator = iterable.into_iter(); let (lower_bound, _) = iterator.size_hint(); self.reserve(lower_bound); for s in iterator { self.push_str(&*s); } } } impl<'a> Add<&'a AsciiStr> for AsciiString { type Output = AsciiString; #[inline] fn add(mut self, other: &AsciiStr) -> AsciiString { self.push_str(other); self } } impl<'a> AddAssign<&'a AsciiStr> for AsciiString { #[inline] fn add_assign(&mut self, other: &AsciiStr) { self.push_str(other); } } impl Index for AsciiString where AsciiStr: Index, { type Output = >::Output; #[inline] fn index(&self, index: T) -> &>::Output { &(**self)[index] } } impl IndexMut for AsciiString where AsciiStr: IndexMut, { #[inline] fn index_mut(&mut self, index: T) -> &mut >::Output { &mut (**self)[index] } } /// A possible error value when converting an `AsciiString` from a byte vector or string. /// It wraps an `AsAsciiStrError` which you can get through the `ascii_error()` method. /// /// This is the error type for `AsciiString::from_ascii()` and /// `IntoAsciiString::into_ascii_string()`. They will never clone or touch the content of the /// original type; It can be extracted by the `into_source` method. /// /// #Examples /// ``` /// # use ascii::IntoAsciiString; /// let err = "bø!".to_string().into_ascii_string().unwrap_err(); /// assert_eq!(err.ascii_error().valid_up_to(), 1); /// assert_eq!(err.into_source(), "bø!".to_string()); /// ``` #[derive(Clone, Copy, PartialEq, Eq)] pub struct FromAsciiError { error: AsAsciiStrError, owner: O, } impl FromAsciiError { /// Get the position of the first non-ASCII byte or character. #[inline] pub fn ascii_error(&self) -> AsAsciiStrError { self.error } /// Get back the original, unmodified type. #[inline] pub fn into_source(self) -> O { self.owner } } impl fmt::Debug for FromAsciiError { #[inline] fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&self.error, fmtr) } } impl fmt::Display for FromAsciiError { #[inline] fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.error, fmtr) } } impl Error for FromAsciiError { #[inline] fn description(&self) -> &str { self.error.description() } /// Always returns an `AsAsciiStrError` fn cause(&self) -> Option<&Error> { Some(&self.error as &Error) } } /// Convert vectors into `AsciiString`. pub trait IntoAsciiString: Sized { /// Convert to `AsciiString` without checking for non-ASCII characters. unsafe fn into_ascii_string_unchecked(self) -> AsciiString; /// Convert to `AsciiString`. fn into_ascii_string(self) -> Result>; } impl IntoAsciiString for Vec { #[inline] unsafe fn into_ascii_string_unchecked(self) -> AsciiString { AsciiString::from(self) } #[inline] fn into_ascii_string(self) -> Result> { Ok(AsciiString::from(self)) } } impl<'a> IntoAsciiString for &'a [AsciiChar] { #[inline] unsafe fn into_ascii_string_unchecked(self) -> AsciiString { AsciiString::from(self) } #[inline] fn into_ascii_string(self) -> Result> { Ok(AsciiString::from(self)) } } impl<'a> IntoAsciiString for &'a AsciiStr { #[inline] unsafe fn into_ascii_string_unchecked(self) -> AsciiString { AsciiString::from(self) } #[inline] fn into_ascii_string(self) -> Result> { Ok(AsciiString::from(self)) } } macro_rules! impl_into_ascii_string { ('a, $wider:ty) => { impl<'a> IntoAsciiString for $wider { #[inline] unsafe fn into_ascii_string_unchecked(self) -> AsciiString { AsciiString::from_ascii_unchecked(self) } #[inline] fn into_ascii_string(self) -> Result> { AsciiString::from_ascii(self) } } }; ($wider:ty) => { impl IntoAsciiString for $wider { #[inline] unsafe fn into_ascii_string_unchecked(self) -> AsciiString { AsciiString::from_ascii_unchecked(self) } #[inline] fn into_ascii_string(self) -> Result> { AsciiString::from_ascii(self) } } }; } impl_into_ascii_string!{AsciiString} impl_into_ascii_string!{Vec} impl_into_ascii_string!{'a, &'a [u8]} impl_into_ascii_string!{String} impl_into_ascii_string!{'a, &'a str} /// Note that the trailing null byte will be removed in the conversion. impl IntoAsciiString for CString { #[inline] unsafe fn into_ascii_string_unchecked(self) -> AsciiString { AsciiString::from_ascii_unchecked(self.into_bytes()) } fn into_ascii_string(self) -> Result> { AsciiString::from_ascii(self.into_bytes_with_nul()) .map_err(|FromAsciiError { error, owner }| { FromAsciiError { owner: unsafe { // The null byte is preserved from the original // `CString`, so this is safe. CString::from_vec_unchecked(owner) }, error: error, } }) .map(|mut s| { let _nul = s.pop(); debug_assert_eq!(_nul, Some(AsciiChar::Null)); s }) } } /// Note that the trailing null byte will be removed in the conversion. impl<'a> IntoAsciiString for &'a CStr { #[inline] unsafe fn into_ascii_string_unchecked(self) -> AsciiString { AsciiString::from_ascii_unchecked(self.to_bytes()) } fn into_ascii_string(self) -> Result> { AsciiString::from_ascii(self.to_bytes_with_nul()) .map_err(|FromAsciiError { error, owner }| { FromAsciiError { owner: unsafe { CStr::from_ptr(owner.as_ptr() as *const _) }, error: error, } }) .map(|mut s| { let _nul = s.pop(); debug_assert_eq!(_nul, Some(AsciiChar::Null)); s }) } } impl<'a, B: ?Sized> IntoAsciiString for Cow<'a, B> where B: 'a + ToOwned, &'a B: IntoAsciiString, ::Owned: IntoAsciiString, { #[inline] unsafe fn into_ascii_string_unchecked(self) -> AsciiString { IntoAsciiString::into_ascii_string_unchecked(self.into_owned()) } fn into_ascii_string(self) -> Result> { match self { Cow::Owned(b) => { IntoAsciiString::into_ascii_string(b) .map_err(|FromAsciiError { error, owner }| { FromAsciiError { owner: Cow::Owned(owner), error: error, } }) } Cow::Borrowed(b) => { IntoAsciiString::into_ascii_string(b) .map_err(|FromAsciiError { error, owner }| { FromAsciiError { owner: Cow::Borrowed(owner), error: error, } }) } } } } #[cfg(feature = "quickcheck")] impl Arbitrary for AsciiString { fn arbitrary(g: &mut G) -> Self { let size = { let s = g.size(); g.gen_range(0, s) }; let mut s = AsciiString::with_capacity(size); for _ in 0..size { s.push(AsciiChar::arbitrary(g)); } s } fn shrink(&self) -> Box> { let chars: Vec = self.as_slice().to_vec(); Box::new(chars.shrink().map( |x| x.into_iter().collect::(), )) } } #[cfg(test)] mod tests { use std::str::FromStr; use std::ffi::CString; use AsciiChar; use super::{AsciiString, IntoAsciiString}; #[test] fn into_string() { let v = AsciiString::from_ascii(&[40_u8, 32, 59][..]).unwrap(); assert_eq!(Into::::into(v), "( ;".to_string()); } #[test] fn into_bytes() { let v = AsciiString::from_ascii(&[40_u8, 32, 59][..]).unwrap(); assert_eq!(Into::>::into(v), vec![40_u8, 32, 59]) } #[test] fn from_ascii_vec() { let vec = vec![AsciiChar::from('A').unwrap(), AsciiChar::from('B').unwrap()]; assert_eq!(AsciiString::from(vec), AsciiString::from_str("AB").unwrap()); } #[test] fn from_cstring() { let cstring = CString::new("baz").unwrap(); let ascii_str = cstring.clone().into_ascii_string().unwrap(); let expected_chars = &[AsciiChar::b, AsciiChar::a, AsciiChar::z]; assert_eq!(ascii_str.len(), 3); assert_eq!(ascii_str.as_slice(), expected_chars); let ascii_str_unchecked = unsafe { cstring.into_ascii_string_unchecked() }; assert_eq!(ascii_str_unchecked.len(), 3); assert_eq!(ascii_str_unchecked.as_slice(), expected_chars); let sparkle_heart_bytes = vec![240u8, 159, 146, 150]; let cstring = CString::new(sparkle_heart_bytes).unwrap(); let cstr = &*cstring; let ascii_err = cstr.into_ascii_string().unwrap_err(); assert_eq!(ascii_err.into_source(), &*cstring); } #[test] fn fmt_ascii_string() { let s = "abc".to_string().into_ascii_string().unwrap(); assert_eq!(format!("{}", s), "abc".to_string()); assert_eq!(format!("{:?}", s), "\"abc\"".to_string()); } #[test] fn write_fmt() { use std::{fmt, str}; let mut s0 = AsciiString::new(); fmt::write(&mut s0, format_args!("Hello World")).unwrap(); assert_eq!(s0, "Hello World"); let mut s1 = AsciiString::new(); fmt::write(&mut s1, format_args!("{}", 9)).unwrap(); assert_eq!(s1, "9"); let mut s2 = AsciiString::new(); let sparkle_heart_bytes = [240, 159, 146, 150]; let sparkle_heart = str::from_utf8(&sparkle_heart_bytes).unwrap(); assert!(fmt::write(&mut s2, format_args!("{}", sparkle_heart)).is_err()); } } ascii-0.9.2/src/free_functions.rs010064400017500001731000000040571334747531600152230ustar0000000000000000#![cfg_attr(rustfmt, rustfmt_skip)] use ascii_char::{AsciiChar, ToAsciiChar}; // I would like to require C: AsciiExt, but it's not in core. /// Terminals use [caret notation](https://en.wikipedia.org/wiki/Caret_notation) /// to display some typed control codes, such as ^D for EOT and ^Z for SUB. /// /// This function returns the caret notation letter for control codes, /// or `None` for printable characters. /// /// # Examples /// ``` /// # use ascii::{AsciiChar, caret_encode}; /// assert_eq!(caret_encode(b'\0'), Some(AsciiChar::At)); /// assert_eq!(caret_encode(AsciiChar::DEL), Some(AsciiChar::Question)); /// assert_eq!(caret_encode(b'E'), None); /// assert_eq!(caret_encode(b'\n'), Some(AsciiChar::J)); /// ``` pub fn caret_encode>(c: C) -> Option { // The formula is explained in the Wikipedia article. let c = c.into() ^ 0b0100_0000; unsafe { if c >= b'?' && c <= b'_' { Some(c.to_ascii_char_unchecked()) } else { None } } } /// Returns the control code represented by a [caret notation](https://en.wikipedia.org/wiki/Caret_notation) /// letter, or `None` if the letter is not used in caret notation. /// /// This function is the inverse of `caret_encode()`. /// /// # Examples /// /// Basic usage: /// /// ``` /// # use ascii::{AsciiChar, caret_decode}; /// assert_eq!(caret_decode(b'?'), Some(AsciiChar::DEL)); /// assert_eq!(caret_decode(AsciiChar::D), Some(AsciiChar::EOT)); /// assert_eq!(caret_decode(b'\0'), None); /// ``` /// /// Symmetry: /// /// ``` /// # use ascii::{AsciiChar, caret_encode, caret_decode}; /// assert_eq!(caret_encode(AsciiChar::US).and_then(caret_decode), Some(AsciiChar::US)); /// assert_eq!(caret_decode(b'@').and_then(caret_encode), Some(AsciiChar::At)); /// ``` pub fn caret_decode>(c: C) -> Option { // The formula is explained in the Wikipedia article. unsafe { match c.into() { b'?'...b'_' => Some(AsciiChar::from_unchecked(c.into() ^ 0b0100_0000)), _ => None, } } } ascii-0.9.2/src/lib.rs010064400017500001731000000041471344674326700127630ustar0000000000000000#![cfg_attr(rustfmt, rustfmt_skip)] // Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! A library that provides ASCII-only string and character types, equivalent to the `char`, `str` //! and `String` types in the standard library. //! #![cfg_attr(feature = "std", doc="[The documentation for the `core` mode is here](https://tomprogrammer.github.io/rust-ascii/core/ascii/index.html).")] #![cfg_attr(not(feature = "std"), doc="This is the documentation for `core` mode.")] //! Please refer to the readme file to learn about the different feature modes of this crate. //! //! # Requirements //! //! - The minimum supported Rust version is 1.9.0 //! - Enabling the quickcheck feature requires Rust 1.12.0 //! - Enabling the serde feature requires Rust 1.13.0 //! //! # History //! //! This package included the Ascii types that were removed from the Rust standard library by the //! 2014-12 [reform of the `std::ascii` module](https://github.com/rust-lang/rfcs/pull/486). The //! API changed significantly since then. #![cfg_attr(not(feature = "std"), no_std)] #[cfg(feature = "std")] extern crate core; #[cfg(feature = "quickcheck")] extern crate quickcheck; #[cfg(feature = "serde")] extern crate serde; #[cfg(all(test, feature = "serde_test"))] extern crate serde_test; mod ascii_char; mod ascii_str; #[cfg(feature = "std")] mod ascii_string; mod free_functions; #[cfg(feature = "serde")] mod serialization; pub use ascii_char::{AsciiChar, ToAsciiChar, ToAsciiCharError}; pub use ascii_str::{AsciiStr, AsAsciiStr, AsMutAsciiStr, AsAsciiStrError, Chars, CharsMut, Lines}; #[cfg(feature = "std")] pub use ascii_string::{AsciiString, IntoAsciiString, FromAsciiError}; pub use free_functions::{caret_encode, caret_decode}; ascii-0.9.2/src/serialization/ascii_char.rs010064400017500001731000000050161334627724600171510ustar0000000000000000use std::fmt; use serde::de::{Error, Unexpected, Visitor}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use ascii_char::AsciiChar; impl Serialize for AsciiChar { #[inline] fn serialize(&self, serializer: S) -> Result { serializer.serialize_char(self.as_char()) } } struct AsciiCharVisitor; impl<'de> Visitor<'de> for AsciiCharVisitor { type Value = AsciiChar; fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("an ascii character") } #[inline] fn visit_char(self, v: char) -> Result { AsciiChar::from(v).map_err(|_| Error::invalid_value(Unexpected::Char(v), &self)) } #[inline] fn visit_str(self, v: &str) -> Result { if v.len() == 1 { let c = v.chars().next().unwrap(); self.visit_char(c) } else { Err(Error::invalid_value(Unexpected::Str(v), &self)) } } } impl<'de> Deserialize<'de> for AsciiChar { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_char(AsciiCharVisitor) } } #[cfg(test)] mod tests { use super::*; #[cfg(feature = "serde_test")] const ASCII_CHAR: char = 'e'; #[cfg(feature = "serde_test")] const ASCII_STR: &str = "e"; #[cfg(feature = "serde_test")] const UNICODE_CHAR: char = 'é'; #[test] fn basic() { fn assert_serialize() {} assert_serialize::(); fn assert_deserialize<'de, T: Deserialize<'de>>() {} assert_deserialize::(); } #[test] #[cfg(feature = "serde_test")] fn serialize() { use serde_test::{assert_tokens, Token}; let ascii_char = AsciiChar::from(ASCII_CHAR).unwrap(); assert_tokens(&ascii_char, &[Token::Char(ASCII_CHAR)]); } #[test] #[cfg(feature = "serde_test")] fn deserialize() { use serde_test::{assert_de_tokens, assert_de_tokens_error, Token}; let ascii_char = AsciiChar::from(ASCII_CHAR).unwrap(); assert_de_tokens(&ascii_char, &[Token::String(ASCII_STR)]); assert_de_tokens(&ascii_char, &[Token::Str(ASCII_STR)]); assert_de_tokens(&ascii_char, &[Token::BorrowedStr(ASCII_STR)]); assert_de_tokens_error::( &[Token::Char(UNICODE_CHAR)], "invalid value: character `é`, expected an ascii character", ); } } ascii-0.9.2/src/serialization/ascii_str.rs010064400017500001731000000044251334627724600170470ustar0000000000000000use std::fmt; use serde::de::{Error, Unexpected, Visitor}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use ascii_str::AsciiStr; impl Serialize for AsciiStr { #[inline] fn serialize(&self, serializer: S) -> Result { serializer.serialize_str(self.as_str()) } } struct AsciiStrVisitor; impl<'a> Visitor<'a> for AsciiStrVisitor { type Value = &'a AsciiStr; fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("a borrowed ascii string") } fn visit_borrowed_str(self, v: &'a str) -> Result { AsciiStr::from_ascii(v.as_bytes()) .map_err(|_| Error::invalid_value(Unexpected::Str(v), &self)) } fn visit_borrowed_bytes(self, v: &'a [u8]) -> Result { AsciiStr::from_ascii(v).map_err(|_| Error::invalid_value(Unexpected::Bytes(v), &self)) } } impl<'de: 'a, 'a> Deserialize<'de> for &'a AsciiStr { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_str(AsciiStrVisitor) } } #[cfg(test)] mod tests { use super::*; #[cfg(feature = "serde_test")] const ASCII: &str = "Francais"; #[cfg(feature = "serde_test")] const UNICODE: &str = "Français"; #[test] fn basic() { fn assert_serialize() {} assert_serialize::<&AsciiStr>(); fn assert_deserialize<'de, T: Deserialize<'de>>() {} assert_deserialize::<&AsciiStr>(); } #[test] #[cfg(feature = "serde_test")] fn serialize() { use serde_test::{assert_tokens, Token}; let ascii_str = AsciiStr::from_ascii(ASCII).unwrap(); assert_tokens(&ascii_str, &[Token::BorrowedStr(ASCII)]); } #[test] #[cfg(feature = "serde_test")] fn deserialize() { use serde_test::{assert_de_tokens, assert_de_tokens_error, Token}; let ascii_str = AsciiStr::from_ascii(ASCII).unwrap(); assert_de_tokens(&ascii_str, &[Token::BorrowedBytes(ASCII.as_bytes())]); assert_de_tokens_error::<&AsciiStr>( &[Token::BorrowedStr(UNICODE)], "invalid value: string \"Français\", expected a borrowed ascii string", ); } } ascii-0.9.2/src/serialization/ascii_string.rs010064400017500001731000000114471334627724600175470ustar0000000000000000use std::fmt; use serde::de::{Error, Unexpected, Visitor}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use ascii_str::AsciiStr; use ascii_string::AsciiString; impl Serialize for AsciiString { #[inline] fn serialize(&self, serializer: S) -> Result { serializer.serialize_str(self.as_str()) } } struct AsciiStringVisitor; impl<'de> Visitor<'de> for AsciiStringVisitor { type Value = AsciiString; fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("an ascii string") } fn visit_str(self, v: &str) -> Result { AsciiString::from_ascii(v).map_err(|_| Error::invalid_value(Unexpected::Str(v), &self)) } fn visit_string(self, v: String) -> Result { AsciiString::from_ascii(v.as_bytes()) .map_err(|_| Error::invalid_value(Unexpected::Str(&v), &self)) } fn visit_bytes(self, v: &[u8]) -> Result { AsciiString::from_ascii(v).map_err(|_| Error::invalid_value(Unexpected::Bytes(&v), &self)) } fn visit_byte_buf(self, v: Vec) -> Result { AsciiString::from_ascii(v.as_slice()) .map_err(|_| Error::invalid_value(Unexpected::Bytes(&v), &self)) } } struct AsciiStringInPlaceVisitor<'a>(&'a mut AsciiString); impl<'a, 'de> Visitor<'de> for AsciiStringInPlaceVisitor<'a> { type Value = (); fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("an ascii string") } fn visit_str(self, v: &str) -> Result { let ascii_str = match AsciiStr::from_ascii(v.as_bytes()) { Ok(ascii_str) => ascii_str, Err(_) => return Err(Error::invalid_value(Unexpected::Str(v), &self)), }; self.0.clear(); self.0.push_str(ascii_str); Ok(()) } fn visit_string(self, v: String) -> Result { let ascii_string = match AsciiString::from_ascii(v.as_bytes()) { Ok(ascii_string) => ascii_string, Err(_) => return Err(Error::invalid_value(Unexpected::Str(&v), &self)), }; *self.0 = ascii_string; Ok(()) } fn visit_bytes(self, v: &[u8]) -> Result { let ascii_str = match AsciiStr::from_ascii(v) { Ok(ascii_str) => ascii_str, Err(_) => return Err(Error::invalid_value(Unexpected::Bytes(v), &self)), }; self.0.clear(); self.0.push_str(ascii_str); Ok(()) } fn visit_byte_buf(self, v: Vec) -> Result { let ascii_string = match AsciiString::from_ascii(v.as_slice()) { Ok(ascii_string) => ascii_string, Err(_) => return Err(Error::invalid_value(Unexpected::Bytes(&v), &self)), }; *self.0 = ascii_string; Ok(()) } } impl<'de> Deserialize<'de> for AsciiString { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { deserializer.deserialize_string(AsciiStringVisitor) } fn deserialize_in_place(deserializer: D, place: &mut Self) -> Result<(), D::Error> where D: Deserializer<'de>, { deserializer.deserialize_string(AsciiStringInPlaceVisitor(place)) } } #[cfg(test)] mod tests { use super::*; #[cfg(feature = "serde_test")] const ASCII: &str = "Francais"; #[cfg(feature = "serde_test")] const UNICODE: &str = "Français"; #[test] fn basic() { fn assert_serialize() {} assert_serialize::(); fn assert_deserialize<'de, T: Deserialize<'de>>() {} assert_deserialize::(); } #[test] #[cfg(feature = "serde_test")] fn serialize() { use serde_test::{assert_tokens, Token}; let ascii_string = AsciiString::from_ascii(ASCII).unwrap(); assert_tokens(&ascii_string, &[Token::String(ASCII)]); assert_tokens(&ascii_string, &[Token::Str(ASCII)]); assert_tokens(&ascii_string, &[Token::BorrowedStr(ASCII)]); } #[test] #[cfg(feature = "serde_test")] fn deserialize() { use serde_test::{assert_de_tokens, assert_de_tokens_error, Token}; let ascii_string = AsciiString::from_ascii(ASCII).unwrap(); assert_de_tokens(&ascii_string, &[Token::Bytes(ASCII.as_bytes())]); assert_de_tokens(&ascii_string, &[Token::BorrowedBytes(ASCII.as_bytes())]); assert_de_tokens(&ascii_string, &[Token::ByteBuf(ASCII.as_bytes())]); assert_de_tokens_error::( &[Token::String(UNICODE)], "invalid value: string \"Français\", expected an ascii string", ); } } ascii-0.9.2/src/serialization/mod.rs010064400017500001731000000000611334627724600156360ustar0000000000000000mod ascii_char; mod ascii_str; mod ascii_string; ascii-0.9.2/tests.rs010064400017500001731000000101061344674335400125550ustar0000000000000000#![cfg_attr(rustfmt, rustfmt_skip)] extern crate ascii; use ascii::{AsciiChar, AsciiStr, AsAsciiStr}; #[cfg(feature = "std")] use ascii::{AsciiString, IntoAsciiString}; #[test] #[cfg(feature = "std")] fn ascii_vec() { let test = b"( ;"; let a = AsciiStr::from_ascii(test).unwrap(); assert_eq!(test.as_ascii_str(), Ok(a)); assert_eq!("( ;".as_ascii_str(), Ok(a)); let v = test.to_vec(); assert_eq!(v.as_ascii_str(), Ok(a)); assert_eq!("( ;".to_string().as_ascii_str(), Ok(a)); } #[test] fn to_ascii() { assert!("zoä华".as_ascii_str().is_err()); assert!([127_u8, 128, 255].as_ascii_str().is_err()); let arr = [AsciiChar::ParenOpen, AsciiChar::Space, AsciiChar::Semicolon]; let a: &AsciiStr = (&arr[..]).into(); assert_eq!(b"( ;".as_ascii_str(), Ok(a)); assert_eq!("( ;".as_ascii_str(), Ok(a)); } #[test] #[cfg(feature = "std")] fn into_ascii() { let arr = [AsciiChar::ParenOpen, AsciiChar::Space, AsciiChar::Semicolon]; let v = AsciiString::from(arr.to_vec()); assert_eq!(b"( ;".to_vec().into_ascii_string(), Ok(v.clone())); assert_eq!("( ;".to_string().into_ascii_string(), Ok(v.clone())); assert_eq!(b"( ;", AsRef::<[u8]>::as_ref(&v)); let err = "zoä华".to_string().into_ascii_string().unwrap_err(); assert_eq!(Err(err.ascii_error()), "zoä华".as_ascii_str()); assert_eq!(err.into_source(), "zoä华"); let err = vec![127, 128, 255].into_ascii_string().unwrap_err(); assert_eq!(Err(err.ascii_error()), [127, 128, 255].as_ascii_str()); assert_eq!(err.into_source(), &[127, 128, 255]); } #[test] #[cfg(feature = "std")] fn compare_ascii_string_ascii_str() { let v = b"abc"; let ascii_string = AsciiString::from_ascii(&v[..]).unwrap(); let ascii_str = AsciiStr::from_ascii(v).unwrap(); assert!(ascii_string == ascii_str); assert!(ascii_str == ascii_string); } #[test] #[cfg(feature = "std")] fn compare_ascii_string_string() { let v = b"abc"; let string = String::from_utf8(v.to_vec()).unwrap(); let ascii_string = AsciiString::from_ascii(&v[..]).unwrap(); assert!(string == ascii_string); assert!(ascii_string == string); } #[test] #[cfg(feature = "std")] fn compare_ascii_str_string() { let v = b"abc"; let string = String::from_utf8(v.to_vec()).unwrap(); let ascii_str = AsciiStr::from_ascii(&v[..]).unwrap(); assert!(string == ascii_str); assert!(ascii_str == string); } #[test] #[cfg(feature = "std")] fn compare_ascii_string_str() { let v = b"abc"; let sstr = ::std::str::from_utf8(v).unwrap(); let ascii_string = AsciiString::from_ascii(&v[..]).unwrap(); assert!(sstr == ascii_string); assert!(ascii_string == sstr); } #[test] fn compare_ascii_str_str() { let v = b"abc"; let sstr = ::std::str::from_utf8(v).unwrap(); let ascii_str = AsciiStr::from_ascii(v).unwrap(); assert!(sstr == ascii_str); assert!(ascii_str == sstr); } #[test] fn compare_ascii_str_slice() { let b = b"abc".as_ascii_str().unwrap(); let c = b"ab".as_ascii_str().unwrap(); assert_eq!(&b[..2], &c[..]); assert_eq!(c[1].as_char(), 'b'); } #[test] #[cfg(feature = "std")] fn compare_ascii_string_slice() { let b = AsciiString::from_ascii("abc").unwrap(); let c = AsciiString::from_ascii("ab").unwrap(); assert_eq!(&b[..2], &c[..]); assert_eq!(c[1].as_char(), 'b'); } #[test] #[cfg(feature = "std")] fn extend_from_iterator() { use ::std::borrow::Cow; let abc = "abc".as_ascii_str().unwrap(); let mut s = abc.chars().cloned().collect::(); assert_eq!(s, abc); s.extend(abc); assert_eq!(s, "abcabc"); let lines = "one\ntwo\nthree".as_ascii_str().unwrap().lines(); s.extend(lines); assert_eq!(s, "abcabconetwothree"); let cows = "ASCII Ascii ascii".as_ascii_str().unwrap() .split(AsciiChar::Space) .map(|case| { if case.chars().all(|a| a.is_uppercase() ) { Cow::from(case) } else { Cow::from(case.to_ascii_uppercase()) } }); s.extend(cows); assert_eq!(s, "abcabconetwothreeASCIIASCIIASCII"); } ascii-0.9.2/.cargo_vcs_info.json0000644000000001120000000000000121650ustar00{ "git": { "sha1": "0e4c03dc048a041eebb51f5cd6d8c9eb5709e18e" } }