├── AUTHORS.txt ├── .gitignore ├── rust-toolchain.toml ├── CODE_OF_CONDUCT.md ├── tarpaulin.toml ├── src ├── lib.rs ├── exit.rs ├── range.rs ├── bidi.rs └── fmt.rs ├── .editorconfig ├── rustfmt-stable.toml ├── LICENSE.txt ├── Cargo.toml ├── Justfile ├── CONTRIBUTING.md ├── CHANGELOG.md ├── rustfmt.toml └── README.md /AUTHORS.txt: -------------------------------------------------------------------------------- 1 | myrrlyn 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "stable" 3 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | This project is subject to the official [Rust code of conduct][coc]. 4 | 5 | As there are no dedicated fora for this project, this is only relevant in the 6 | repository or in communication with me about it. 7 | 8 | [coc]: https://www.rust-lang.org/policies/code-of-conduct 9 | -------------------------------------------------------------------------------- /tarpaulin.toml: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Coverage Configuration # 3 | # # 4 | # This file controls the behavior of `cargo-tarpaulin`, which produces # 5 | # test coverage reports for the project. # 6 | ######################################################################## 7 | 8 | [coverage] 9 | all-features = true 10 | count = true 11 | ignore-panics = true 12 | ignore-tests = true 13 | run-types = [ 14 | "Tests", 15 | ] 16 | 17 | [report] 18 | out = [ 19 | "Html", 20 | "Lcov", 21 | "Xml", 22 | ] 23 | output-dir = "target/tarpaulin" 24 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! `wyz` – myrrlyn’s wyzyrdly library 2 | 3 | This crate consolidates all the small tools and conveniences I’ve built up in my 4 | experience building Rust crates. 5 | 6 | Each module has more documentation about what it contains. The modules are 7 | largely independent, and can be used individually. 8 | !*/ 9 | 10 | #![no_std] 11 | #![cfg_attr(debug_assertions, warn(missing_docs))] 12 | #![cfg_attr(not(debug_assertions), deny(missing_docs))] 13 | 14 | #[cfg(feature = "alloc")] 15 | extern crate alloc; 16 | 17 | #[cfg(feature = "std")] 18 | extern crate std; 19 | 20 | pub mod bidi; 21 | pub mod fmt; 22 | pub mod range; 23 | 24 | #[cfg(feature = "std")] 25 | #[macro_use] 26 | pub mod exit; 27 | 28 | pub use self::{ 29 | bidi::*, 30 | fmt::*, 31 | range::*, 32 | }; 33 | 34 | #[cfg(feature = "std")] 35 | pub use self::exit::*; 36 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Editor Configuration # 3 | # # 4 | # This file controls behavior in conformant editors with respect to some # 5 | # common baseline settings. # 6 | ################################################################################ 7 | 8 | [*] 9 | charset = utf-8 10 | end_of_line = lf 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | [Justfile] 15 | indent_size = 8 16 | indent_style = tab 17 | 18 | [*.md] 19 | indent_size = 2 20 | indent_style = space 21 | 22 | [*.rs] 23 | indent_size = 4 24 | indent_style = tab 25 | 26 | [*.toml] 27 | indent_size = 8 28 | indent_style = tab 29 | -------------------------------------------------------------------------------- /src/exit.rs: -------------------------------------------------------------------------------- 1 | /*! `exit!` macro 2 | 3 | The `exit!` macro simplifies exiting with an error code, and optionally printing 4 | an error message prior to exit. 5 | 6 | # Examples 7 | 8 | This example exits with status `1`. 9 | 10 | ```rust,should_panic 11 | wyz::exit!(); 12 | ``` 13 | 14 | This example exits with status `2`. 15 | 16 | ```rust,should_panic 17 | wyz::exit!(2); 18 | ``` 19 | 20 | This example exits with status `3`, and uses `eprintln!` to print an error 21 | message before exiting. Note that if `stderr` has been closed, this will crash 22 | the program with a panic due to `SIGPIPE`, and *not* call `process::exit()`. 23 | 24 | ```rust,should_panic 25 | wyz::exit!(3, "Error status: {}", "testing"); 26 | ``` 27 | !*/ 28 | 29 | #![cfg(feature = "std")] 30 | 31 | /// `exit!` macro 32 | #[macro_export] 33 | macro_rules! exit { 34 | () => { 35 | $crate::exit!(1); 36 | }; 37 | 38 | ( $num:expr $(,)? ) => { 39 | ::std::process::exit($num); 40 | }; 41 | 42 | ( $num:expr, $fmt:expr $( , $arg:expr )* $(,)? ) => {{ 43 | eprintln!($fmt $( , $arg )*); 44 | $crate::exit!($num); 45 | }}; 46 | } 47 | -------------------------------------------------------------------------------- /rustfmt-stable.toml: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Rust Style Configuration # 3 | # # 4 | # This file controls the operation of `rustfmt` and `cargo fmt`. As the # 5 | # `rustfmt` tool is still unstable, this file only contains the configurations # 6 | # that are stable as of the pinned Rust version in `rust-toolchain`. The file # 7 | # `rustfmt-nightly.toml` contains the configurations that are available as of # 8 | # the nightly Rust release when that file was last touched. # 9 | ################################################################################ 10 | 11 | edition = "2018" 12 | hard_tabs = true 13 | force_explicit_abi = true 14 | max_width = 81 15 | merge_derives = true 16 | newline_style = "Unix" 17 | remove_nested_parens = true 18 | reorder_imports = true 19 | reorder_modules = true 20 | tab_spaces = 4 21 | use_field_init_shorthand = true 22 | use_small_heuristics = "Default" 23 | use_try_shorthand = true 24 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 myrrlyn (Alexander Payne) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Project Manifest # 3 | # # 4 | # This file describes the Rust project to the Cargo build tool for operations. # 5 | ################################################################################ 6 | 7 | [package] 8 | name = "wyz" 9 | version = "0.6.1" 10 | authors = [ 11 | "myrrlyn ", 12 | ] 13 | edition = "2018" 14 | categories = [ 15 | "no-std", 16 | ] 17 | description = "myrrlyn’s utility collection" 18 | documentation = "https://docs.rs/wyz" 19 | homepage = "https://myrrlyn.net/crates/wyz" 20 | include = [ 21 | "Cargo.toml", 22 | "LICENSE.txt", 23 | "README.md", 24 | "src/**/*.rs", 25 | ] 26 | keywords = [ 27 | ] 28 | license = "MIT" 29 | readme = "README.md" 30 | repository = "https://github.com/myrrlyn/wyz" 31 | rust-version = "1.40" 32 | 33 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 34 | 35 | [dependencies] 36 | 37 | [dependencies.once_cell] 38 | version = "1" 39 | optional = true 40 | 41 | [dependencies.tap] 42 | version = "1.0.1" 43 | 44 | 45 | [features] 46 | alloc = [] 47 | default = ["std"] 48 | std = ["alloc"] 49 | -------------------------------------------------------------------------------- /Justfile: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Justfile # 3 | # # 4 | # Set of routines to execute for development work. # 5 | ################################################################################ 6 | 7 | # Run the benchmarks. Currently, this requires the nightly compiler series. 8 | bench: 9 | cargo +nightly bench 10 | 11 | # Build the project, after checking that it is valid. 12 | build: check 13 | cargo build --all-features 14 | 15 | # Runs the checker and linter. 16 | check: format 17 | cargo check --all-features 18 | cargo clippy --all-features 19 | 20 | # Destroys build artifacts. 21 | clean: 22 | cargo clean 23 | 24 | # Development workflow. 25 | dev: format check doc test 26 | 27 | # Documents the project, after checking that it is valid. 28 | doc: check 29 | cargo doc --document-private-items --all-features 30 | 31 | format: 32 | cargo +nightly fmt 33 | 34 | # Runs a Justfile recipe on every change to the workspace. 35 | loop action: 36 | watchexec -- "just {{action}}" 37 | 38 | # Runs the project under the Miri interpreter. This is currently nightly-only. 39 | miri: 40 | cargo +nightly miri test 41 | 42 | # Prepares the project for package deployment. 43 | # 44 | # This allows uncommitted VCS files, as a convenience for development. 45 | package: test doc 46 | cargo package --allow-dirty 47 | 48 | # Publishes the project to crates.io. 49 | # 50 | # This repackages the project and fails on a dirty VCS checkout. 51 | publish: test doc 52 | cargo package # no --allow-dirty this time 53 | cargo publish 54 | 55 | # Runs the test suite. 56 | test: build 57 | cargo test --all-features 58 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guide 2 | 3 | Contributions are absolutely welcome! 4 | 5 | ## Contact Information 6 | 7 | In order of likelihood that I will actionably receive your contact, my 8 | information is: 9 | 10 | - Email: [self@myrrlyn.dev](mailto:self@myrrlyn.dev) 11 | - GitHub: [@myrrlyn](//github.com/myrrlyn) 12 | - Twitter: [@myrrlyn](//twitter.com/myrrlyn) 13 | - Mastodon: [@myrrlyn@cybre.space](//cybre.space/myrrlyn) 14 | - Reddit: [/u/myrrlyn](//reddit.com/u/myrrlyn) 15 | 16 | I am not active on any IRC channels at this time. I am on Discord in the Rust 17 | channel, so you may be able to reach me there, but I don’t know offhand how to 18 | give out Discord profile links. I have a very consistent username scheme and so 19 | anywhere you see my name, it’s *probably* me and I’ll *probably* respond to it. 20 | 21 | ## Preconditions 22 | 23 | Be able to make a Rust project compile. 24 | 25 | Be comfortable using `U+0009 CHARACTER TABULATION` as your indentation setting. 26 | 27 | ## Contributing 28 | 29 | If you have a patch you think is worth inspecting right away, opening a pull 30 | request without prelude is fine, although I would certainly appreciate an 31 | accompanying explanation of what the patch does and why. 32 | 33 | If you have questions, bugs, suggestions, or other contributions of any kind 34 | that do not immediately touch the codebase, you can reach me informally to talk 35 | about them or open an issue. 36 | 37 | I will do my best to respond to all contacts in a timely manner. 38 | 39 | ## Workflow 40 | 41 | This project uses a `Justfile` to contain its workflows. You can install the 42 | `just` tool from Cargo (`cargo install just`), or from any of the sources listed 43 | at https://github.com/casey/just. If you run `just loop dev` in a separate 44 | terminal while you work, or run `just dev` as your editor’s on-save event, you 45 | should be all set. 46 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes will be documented in this file. 4 | 5 | This document is written according to the [Keep a Changelog][kac] style. 6 | 7 | ## 0 8 | 9 | ### 0.6.0 10 | 11 | Strip the `comu` module (migrated to `funty 3.0`) and the `wm` module (an idle 12 | sketch I will never seriously use). 13 | 14 | ### 0.5.0 15 | 16 | Added the `Bidi` iterator adapter, which applies a `.rev()` on construction if a 17 | given condition is true. 18 | 19 | Added the `RangeExt` trait for making some operations on 20 | `>` easier. 21 | 22 | Added a `FmtList` type (and `.fmt_list()` method) which allows anything that can 23 | be borrowed as an iterator to render itself conveniently. 24 | 25 | Added more pointer methods to `Address`, and created a system for working with 26 | references as well as pointers. 27 | 28 | ### 0.4.0 29 | 30 | Add the `comu` module containing the type-system mutability tracking extracted 31 | from `bitvec`. 32 | 33 | ### 0.3.0 34 | 35 | Added a background garbage disposal system in the `wm` module, under the 36 | `garbage` feature. It is accessed by importing the `wm::BgDropExt` trait and 37 | using its `.bg_drop()` method on a value. 38 | 39 | The disposal system manages a single worker thread which receives any type and 40 | runs the appropriate destructor for it. Once initialized, the system remains in 41 | operation until explicitly shut down by the client program; once shut down, all 42 | future deferred-drop objects are destroyed in their local thread as normal. 43 | 44 | This system allows programs to opt in to faster immediate behavior when a value 45 | goes out of scope, with minimal system and per-value cost. 46 | 47 | Removed `tap`, `pipe`, and `conv`. They have been promoted to the [`tap`] crate. 48 | 49 | ### 0.2.0 50 | 51 | Added `conv::TryConv` for fallible directed conversion. 52 | 53 | Added `fmt` module, which supplies behavior to forward any formatting trait to 54 | `Debug`. 55 | 56 | Removed `pretty` module in favor of `fmt`. 57 | 58 | ### 0.1.1 59 | 60 | Fix typos. 61 | 62 | ### 0.1.0 63 | 64 | Initial release, featuring: 65 | 66 | - `conv` 67 | - `exit` 68 | - `pipe` 69 | - `pretty` 70 | - `tap` 71 | 72 | [`tap`]: https://crates.io/crates/tap 73 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Rust Style Configuration # 3 | # # 4 | # This file controls the operation of `rustfmt` and `cargo fmt`. As the # 5 | # `rustfmt` tool is still unstable, this file is only usable by the nightly # 6 | # release of Rust at least as of the date this file was last touched. # 7 | # # 8 | # See https://github.com/rust-lang/rustfmt/blob/master/Configurations.md for a # 9 | # list of all configuration options. # 10 | ################################################################################ 11 | 12 | # Stable as of current nightly 13 | edition = "2018" 14 | fn_args_layout = "Tall" 15 | force_explicit_abi = true 16 | hard_tabs = true 17 | max_width = 81 18 | merge_derives = true 19 | newline_style = "Unix" 20 | print_misformatted_file_names = false 21 | remove_nested_parens = true 22 | reorder_imports = true 23 | reorder_modules = true 24 | tab_spaces = 4 25 | use_field_init_shorthand = true 26 | use_small_heuristics = "Default" 27 | use_try_shorthand = true 28 | 29 | # Still unstable 30 | binop_separator = "Front" 31 | blank_lines_lower_bound = 0 32 | blank_lines_upper_bound = 1 33 | brace_style = "SameLineWhere" 34 | color = "Auto" 35 | combine_control_expr = true 36 | comment_width = 80 37 | condense_wildcard_suffixes = true 38 | control_brace_style = "ClosingNextLine" 39 | disable_all_formatting = false 40 | empty_item_single_line = false 41 | enum_discrim_align_threshold = 40 42 | error_on_line_overflow = false 43 | error_on_unformatted = false 44 | fn_single_line = false 45 | force_multiline_blocks = false 46 | format_code_in_doc_comments = false 47 | format_macro_matchers = true 48 | format_macro_bodies = true 49 | format_strings = true 50 | hide_parse_errors = false 51 | ignore = [] 52 | imports_indent = "Block" 53 | imports_layout = "Vertical" 54 | indent_style = "Block" 55 | inline_attribute_width = 0 56 | license_template_path = "" 57 | match_arm_blocks = true 58 | match_block_trailing_comma = true 59 | merge_imports = true 60 | normalize_comments = false 61 | normalize_doc_attributes = true 62 | overflow_delimited_expr = true 63 | reorder_impl_items = true 64 | report_fixme = "Unnumbered" 65 | report_todo = "Unnumbered" 66 | space_after_colon = true 67 | space_before_colon = false 68 | spaces_around_ranges = true 69 | struct_field_align_threshold = 0 70 | struct_lit_single_line = true 71 | trailing_comma = "Vertical" 72 | trailing_semicolon = true 73 | type_punctuation_density = "Wide" 74 | unstable_features = true 75 | version = "Two" 76 | where_single_line = true 77 | wrap_comments = true 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # `wyz` 4 | 5 | ## myrrlyn’s wyzyrdly library 6 | 7 | [![Latest Version][version_img]][crate_link] 8 | [![MSRV][msrv_img]][crate_link] 9 | [![License][license_img]][license_file] 10 | 11 | [![Documentation][docs_img]][docs_link] 12 | [![Crate Downloads][downloads_img]][crate_link] 13 | 14 |
15 | 16 | I have developed a collection of utility and convenience Rust modules that are 17 | useful to me, and may be useful to you also. 18 | 19 | This crate is a collection of largely-independent small modules. I do not 20 | currently offer features to disable modules independently of each other, but 21 | their compilation cost is small enough to essentially not matter. 22 | 23 | ## Modules 24 | 25 | 1. [`bidi`](#bidi) 26 | 1. [`exit`](#exit) 27 | 1. [`fmt`](#fmt) 28 | 1. [`range`](#range) 29 | 30 | ## `bidi` 31 | 32 | This provides an extension trait for `DoubleEndedIterator` with a method, 33 | `.bidi(cond: bool)`, that sets whether the iterator operates in forward or 34 | reverse by the runtime condition. When the condition is `true`, forward 35 | iteration (with `.next()`, `.nth()`) forwards to the equivalent reverse 36 | methods (`.next_back()`, `.nth_back()`) and vice-versa; when the condition is 37 | `false`, iteration behaves normally. 38 | 39 | This only checks the condition upon initial creation; it is otherwise 40 | branchless. 41 | 42 | ## `exit` 43 | 44 | This is a macro that calls `std::process::exit`. It can return a status code, 45 | and also print a message to `stderr`. 46 | 47 | ```rust 48 | use wyz::exit::exit; 49 | 50 | exit!(); 51 | exit!(2); 52 | exit!(3, "This is a {} message", "failure"); 53 | ``` 54 | 55 | The default call is `std::process::exit(1)`; a call may provide its own exit 56 | code and, in addition, a set of arguments to pass directly to `eprintln!`. The 57 | error message is not guaranteed to be emitted, as `stderr` may be closed at time 58 | of `exit!`. 59 | 60 | ## `fmt` 61 | 62 | Rust uses the `Debug` trait for automatic printing events in several parts of 63 | the standard library. This module provides wrapper types which forward their 64 | `Debug` implementation to a specified other formatting trait. It also implements 65 | extension methods on all types that have format trait implementations to wrap 66 | them in the corresponding shim type. 67 | 68 | ```rust 69 | use wyz::fmt::FmtForward as _; 70 | 71 | let val = 6; 72 | let addr = &val as *const i32; 73 | println!("{:?}", addr.fmt_pointer()); 74 | ``` 75 | 76 | This snippet uses the `Debug` format template, but will print the `Pointer` 77 | implementation of `*const i32`. 78 | 79 | This is useful for fitting your values into an error-handling framework that 80 | only uses `Debug`, such as the `fn main() -> Result` program layout. 81 | 82 | In addition to forwarding each of the scalar traits, this also provides a 83 | `.fmt_list()` that formats any type `T where &T: IntoIterator` as a list. The 84 | list-formatting adapter itself implements all of the scalar formatting traits, 85 | and can also be wrapped in any of the forwarding guards so that it can be sent 86 | to a `Debug` sink: 87 | 88 | ```rust 89 | use wyz::fmt::FmtForward as _; 90 | 91 | let seq = 0 .. 4; 92 | assert_eq!( 93 | format!("{:02b}", seq.fmt_list()), 94 | "[00, 01, 10, 11]", 95 | ); 96 | assert_eq!( 97 | format!( 98 | "{:?}", 99 | seq 100 | .map(|x| (x + 1) * 10) 101 | .fmt_list() 102 | .fmt_lower_hex(), 103 | ), 104 | "[a, 14, 1e, 28]", 105 | ); 106 | ``` 107 | 108 | ## `range` 109 | 110 | This provides an extension trait, `RangeExt`, on `RangeBounds`. It is currently 111 | only used with `R: RangeBounds`, again because it is an MVP for bitvec’s 112 | use rather than a project in its own right. It normalizes arbitrary ranges into 113 | the `Range` concrete type. PRs welcome! 114 | 115 | [crate_link]: https://crates.io/crates/wyz "Crate Link" 116 | [docs_link]: https://docs.rs/wyz/latest/wyz "Documentation" 117 | [docs_img]: https://img.shields.io/docsrs/wyz/latest.svg?style=for-the-badge "Documentation Display" 118 | [downloads_img]: https://img.shields.io/crates/dv/wyz.svg?style=for-the-badge "Crate Downloads" 119 | [license_file]: https://github.com/bitvecto-rs/wyz/blob/master/LICENSE.txt "License File" 120 | [license_img]: https://img.shields.io/crates/l/wyz.svg?style=for-the-badge "License Display" 121 | [msrv_img]: https://img.shields.io/badge/MSRV-1.50-f46623?style=for-the-badge&logo=rust "Minimum Supported Rust Version: 1.50" 122 | [version_img]: https://img.shields.io/crates/v/wyz?color=f46623&style=for-the-badge "wyz version badge" 123 | -------------------------------------------------------------------------------- /src/range.rs: -------------------------------------------------------------------------------- 1 | //! Range utilities. 2 | 3 | use core::ops::{ 4 | Bound, 5 | Range, 6 | RangeBounds, 7 | }; 8 | 9 | /// Extension methods for working with various range types. 10 | pub trait RangeExt: RangeBounds 11 | where T: Ord 12 | { 13 | /// Normalizes a range-like type to a canonical half-open `Range`. 14 | /// 15 | /// ## Parameters 16 | /// 17 | /// - `self`: The range to normalize. 18 | /// - `start`: An optional fallback *inclusive* lower bound. 19 | /// - `end`: An optional fallback *exclusive* upper bound. 20 | /// 21 | /// ## Returns 22 | /// 23 | /// A `Range` whose start and end values are the following, in order of 24 | /// decreasing priority: 25 | /// 26 | /// - `self.start()`, or if absent, the `start` parameter, or if it is 27 | /// `None`, `0`. 28 | /// - `self.end()`, or if absent, the `end` parameter, or if it is `None`, 29 | /// !0`. 30 | fn normalize( 31 | self, 32 | start: impl Into>, 33 | end: impl Into>, 34 | ) -> Range; 35 | 36 | /// Finds the intersection between two range-likes. The produced `Range` 37 | /// spans only the elements common to both. 38 | /// 39 | /// This returns `None` if the ranges do not have an intersection (at least 40 | /// one element present in both ranges). 41 | fn intersection(self, other: R) -> Option> 42 | where R: RangeExt; 43 | 44 | /// Finds the union between two range-likes. The produced `Range` spans all 45 | /// elements present in at least one of them. 46 | /// 47 | /// This returns `None` if the ranges do not have an intersection (at least 48 | /// one element present in both ranges). 49 | fn union(self, other: R) -> Option> 50 | where R: RangeExt; 51 | } 52 | 53 | // TODO(myrrlyn): Use funty to extend this for all integers. 54 | impl RangeExt for R 55 | where R: RangeBounds 56 | { 57 | fn normalize( 58 | self, 59 | start: impl Into>, 60 | end: impl Into>, 61 | ) -> Range { 62 | let start = match self.start_bound() { 63 | Bound::Unbounded => start.into().unwrap_or(0), 64 | Bound::Included(&v) => v, 65 | Bound::Excluded(&v) => v.saturating_add(1), 66 | }; 67 | let end = match self.end_bound() { 68 | Bound::Unbounded => end.into().unwrap_or(!0), 69 | Bound::Included(&v) => v.saturating_add(1), 70 | Bound::Excluded(&v) => v, 71 | }; 72 | if start > end { 73 | end .. start 74 | } 75 | else { 76 | start .. end 77 | } 78 | } 79 | 80 | fn intersection(self, other: R2) -> Option> 81 | where R2: RangeExt { 82 | let Range { start: a1, end: a2 } = self.normalize(None, None); 83 | let Range { start: b1, end: b2 } = other.normalize(None, None); 84 | if b1 < a1 { 85 | return (b1 .. b2).intersection(a1 .. a2); 86 | } 87 | if !(a1 .. a2).contains(&b1) { 88 | return None; 89 | } 90 | let start = a1.max(b1); 91 | let end = a2.min(b2); 92 | if start > end { 93 | Some(end .. start) 94 | } 95 | else { 96 | Some(start .. end) 97 | } 98 | } 99 | 100 | fn union(self, other: R2) -> Option> 101 | where R2: RangeExt { 102 | let Range { start: a1, end: a2 } = self.normalize(None, None); 103 | let Range { start: b1, end: b2 } = other.normalize(None, None); 104 | if b1 < a1 { 105 | return (b1 .. b2).intersection(a1 .. a2); 106 | } 107 | if !(a1 .. a2).contains(&b1) { 108 | return None; 109 | } 110 | let start = a1.min(b1); 111 | let end = a2.max(b2); 112 | if start > end { 113 | Some(end .. start) 114 | } 115 | else { 116 | Some(start .. end) 117 | } 118 | } 119 | } 120 | 121 | #[cfg(test)] 122 | mod tests { 123 | use super::*; 124 | 125 | #[test] 126 | fn normalize() { 127 | let r = (..).normalize(1, 10); 128 | assert!(r.contains(&1)); 129 | assert!(r.contains(&9)); 130 | assert!(!r.contains(&0)); 131 | assert!(!r.contains(&10)); 132 | 133 | let r = (.. 10).normalize(1, 20); 134 | assert!(r.contains(&1)); 135 | assert!(r.contains(&9)); 136 | assert!(!r.contains(&0)); 137 | assert!(!r.contains(&10)); 138 | 139 | let r = (4 ..).normalize(6, 10); 140 | assert!(r.contains(&4)); 141 | assert!(r.contains(&9)); 142 | assert!(!r.contains(&3)); 143 | assert!(!r.contains(&10)); 144 | 145 | let r = (4 ..= 10).normalize(6, 8); 146 | assert!(r.contains(&4)); 147 | assert!(r.contains(&10)); 148 | assert!(!r.contains(&3)); 149 | assert!(!r.contains(&11)); 150 | 151 | let r = (..= 10).normalize(1, 8); 152 | assert!(r.contains(&1)); 153 | assert!(r.contains(&10)); 154 | assert!(!r.contains(&0)); 155 | assert!(!r.contains(&11)); 156 | } 157 | 158 | #[test] 159 | fn intersect() { 160 | let a = 3 .. 10; 161 | let b = 7 ..= 20; 162 | assert_eq!(a.intersection(b), Some(7 .. 10)); 163 | 164 | let c = 3 .. 10; 165 | let d = 13 ..= 20; 166 | assert!(c.intersection(d).is_none()); 167 | } 168 | 169 | #[test] 170 | fn union() { 171 | let a = 3 .. 10; 172 | let b = 7 ..= 20; 173 | assert_eq!(a.union(b), Some(3 .. 21)); 174 | 175 | let c = 3 .. 10; 176 | let d = 13 ..= 20; 177 | assert!(c.union(d).is_none()); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/bidi.rs: -------------------------------------------------------------------------------- 1 | //! A bidirectional iterator that only checks its direction once. 2 | 3 | use core::iter::FusedIterator; 4 | 5 | /** An iterator that conditionally reverses itself upon creation. 6 | 7 | This acts as a conditional `.rev()` adapter: it reverses the direction of 8 | iteration, swapping `.next()` and `.next_back()`, but only if the provided 9 | condition is true. If the condition is false, then iteration proceeds normally. 10 | 11 | The condition is only evaluated when the adapter is constructed, and all calls 12 | to drive the iterator are branchless. 13 | 14 | ## Usage 15 | 16 | This can be constructed directly with `Bidi::new(some_iterator)`, but it is more 17 | conveniently accessed as an extension method on double-ended iterators. Import 18 | `wyz::BidiIterator` or `wyz::bidi::*;` and then call `.bidi()` in your iterator 19 | adapter sequence. 20 | 21 | ## Examples 22 | 23 | This can be used to hand-roll a `memmove` implementation that correctly handles 24 | the case where the destination begins in the source region: 25 | 26 | ```rust 27 | use wyz::bidi::*; 28 | 29 | unsafe fn memmove(from: *const T, to: *mut T, count: usize) { 30 | let src = from .. from.add(count); 31 | let rev = src.contains(&(to as *const T)); 32 | for idx in (0 .. count).bidi(rev) { 33 | to.add(idx).write(from.add(idx).read()); 34 | } 35 | } 36 | ``` 37 | 38 | This example detects if `to` is between `from` and `from.add(count)` and uses 39 | that to determine whether to iterate forward from `0` to `count - 1` or backward 40 | from `count - 1` to `0`. 41 | **/ 42 | pub struct Bidi 43 | where I: DoubleEndedIterator 44 | { 45 | /// The iterator being governed. 46 | inner: I, 47 | /// A pointer to either `I::next` or `I::next_back`. 48 | next: fn(&mut I) -> Option<::Item>, 49 | /// A pointer to either `I::next_back` or `I::next`. 50 | next_back: fn(&mut I) -> Option<::Item>, 51 | /// A pointer to either `I::nth` or `I::nth_back`. 52 | nth: fn(&mut I, usize) -> Option<::Item>, 53 | /// A pointer to either `I::nth_back` or `I::nth`. 54 | nth_back: fn(&mut I, usize) -> Option<::Item>, 55 | } 56 | 57 | impl Bidi 58 | where I: DoubleEndedIterator 59 | { 60 | /// Applies the `Bidi` adapter to a double-ended iterator and selects the 61 | /// direction of traversal. 62 | /// 63 | /// ## Parameters 64 | /// 65 | /// - `iter`: anything that can be made into a double-ended iterator 66 | /// - `cond`: determines whether iteration proceeds ordinarily or reversed 67 | pub fn new(iter: II, cond: bool) -> Self 68 | where II: IntoIterator { 69 | let inner = iter.into_iter(); 70 | if cond { 71 | Self { 72 | inner, 73 | next: ::next_back, 74 | next_back: ::next, 75 | nth: ::nth_back, 76 | nth_back: ::nth, 77 | } 78 | } 79 | else { 80 | Self { 81 | inner, 82 | next: ::next, 83 | next_back: ::next_back, 84 | nth: ::nth, 85 | nth_back: ::nth_back, 86 | } 87 | } 88 | } 89 | } 90 | 91 | impl Iterator for Bidi 92 | where I: DoubleEndedIterator 93 | { 94 | type Item = ::Item; 95 | 96 | #[inline] 97 | fn next(&mut self) -> Option { 98 | (self.next)(&mut self.inner) 99 | } 100 | 101 | #[inline] 102 | fn nth(&mut self, n: usize) -> Option { 103 | (self.nth)(&mut self.inner, n) 104 | } 105 | 106 | #[inline] 107 | #[cfg(not(tarpaulin_include))] 108 | fn size_hint(&self) -> (usize, Option) { 109 | self.inner.size_hint() 110 | } 111 | 112 | #[inline] 113 | #[cfg(not(tarpaulin_include))] 114 | fn count(self) -> usize { 115 | self.inner.count() 116 | } 117 | 118 | #[inline] 119 | #[cfg(not(tarpaulin_include))] 120 | fn last(mut self) -> Option { 121 | self.next_back() 122 | } 123 | } 124 | 125 | impl DoubleEndedIterator for Bidi 126 | where I: DoubleEndedIterator 127 | { 128 | #[inline] 129 | fn next_back(&mut self) -> Option { 130 | (self.next_back)(&mut self.inner) 131 | } 132 | 133 | #[inline] 134 | fn nth_back(&mut self, n: usize) -> Option { 135 | (self.nth_back)(&mut self.inner, n) 136 | } 137 | } 138 | 139 | impl ExactSizeIterator for Bidi 140 | where I: DoubleEndedIterator + ExactSizeIterator 141 | { 142 | #[inline] 143 | #[cfg(not(tarpaulin_include))] 144 | fn len(&self) -> usize { 145 | self.inner.len() 146 | } 147 | } 148 | 149 | impl FusedIterator for Bidi where I: DoubleEndedIterator + FusedIterator 150 | { 151 | } 152 | 153 | /// Extension trait that provides `.bidi()` for all double-ended iterators. 154 | pub trait BidiIterator 155 | where 156 | Self: Sized + IntoIterator, 157 | ::IntoIter: DoubleEndedIterator, 158 | { 159 | /// Conditionally reverses the direction of iteration. 160 | /// 161 | /// When `cond` is true, this adapter swaps the `next` and `nth` methods 162 | /// with `next_back` and `nth_back`. The resulting iterator is equivalent to 163 | /// `if cond { self.rev() } else { self }`. 164 | /// 165 | /// ## Examples 166 | /// 167 | /// ```rust 168 | /// use wyz::BidiIterator; 169 | /// 170 | /// let data = [1, 2, 3]; 171 | /// let mut iter = data.iter().copied().bidi(false); 172 | /// assert_eq!(iter.next(), Some(1)); 173 | /// assert_eq!(iter.next_back(), Some(3)); 174 | /// 175 | /// let mut iter = data.iter().copied().bidi(true); 176 | /// assert_eq!(iter.next(), Some(3)); 177 | /// assert_eq!(iter.next_back(), Some(1)); 178 | /// ``` 179 | fn bidi(self, cond: bool) -> Bidi { 180 | Bidi::new(self, cond) 181 | } 182 | } 183 | 184 | impl BidiIterator for I 185 | where 186 | I: Sized + IntoIterator, 187 | ::IntoIter: DoubleEndedIterator, 188 | { 189 | } 190 | 191 | #[cfg(test)] 192 | mod tests { 193 | use super::*; 194 | 195 | #[test] 196 | fn forward() { 197 | let mut iter = (0 .. 6).bidi(false); 198 | 199 | assert_eq!(iter.next(), Some(0)); 200 | assert_eq!(iter.next_back(), Some(5)); 201 | assert_eq!(iter.nth(1), Some(2)); 202 | assert_eq!(iter.nth_back(1), Some(3)); 203 | assert!(iter.next().is_none()); 204 | } 205 | 206 | #[test] 207 | fn reverse() { 208 | let mut iter = (0 .. 6).bidi(true); 209 | 210 | assert_eq!(iter.next(), Some(5)); 211 | assert_eq!(iter.next_back(), Some(0)); 212 | assert_eq!(iter.nth(1), Some(3)); 213 | assert_eq!(iter.nth_back(1), Some(2)); 214 | assert!(iter.next().is_none()); 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /src/fmt.rs: -------------------------------------------------------------------------------- 1 | /*! Format forwarding 2 | 3 | This module provides wrapper types for each formatting trait other than `Debug` 4 | which, when `Debug`-formatted, forward to the original trait instead of `Debug`. 5 | 6 | Each wrapper type is a tuple struct so that it can be used as a named 7 | constructor, such as in `.map(FmtDisplay)`. In addition, a blanket trait adds 8 | extension methods `.fmt_>()` to provide the corresponding wrap. 9 | 10 | Any modifiers in the format template string or struct modifier are passed 11 | through to the desired trait implementation unchanged. The only effect of the 12 | forwarding types in this module is to change the `?` template character to one 13 | of the other trait signifiers. 14 | !*/ 15 | 16 | use core::{ 17 | fmt::{ 18 | self, 19 | Binary, 20 | Debug, 21 | Display, 22 | Formatter, 23 | LowerExp, 24 | LowerHex, 25 | Octal, 26 | Pointer, 27 | UpperExp, 28 | UpperHex, 29 | }, 30 | ops::{ 31 | Deref, 32 | DerefMut, 33 | }, 34 | }; 35 | 36 | /// Wraps any value with a format-forward to `Debug`. 37 | #[cfg(not(tarpaulin_include))] 38 | pub trait FmtForward: Sized { 39 | /// Causes `self` to use its `Binary` implementation when `Debug`-formatted. 40 | /// 41 | /// ## Examples 42 | /// 43 | /// ```rust 44 | /// # #[cfg(feature = "std")] { 45 | /// use wyz::fmt::*; 46 | /// 47 | /// assert_eq!( 48 | /// format!("{:?}", 3.fmt_binary()), 49 | /// "11", 50 | /// ); 51 | /// # } 52 | /// ``` 53 | #[inline(always)] 54 | fn fmt_binary(self) -> FmtBinary 55 | where Self: Binary { 56 | FmtBinary(self) 57 | } 58 | 59 | /// Causes `self` to use its `Display` implementation when 60 | /// `Debug`-formatted. 61 | /// 62 | /// ## Examples 63 | /// 64 | /// ```rust 65 | /// # #[cfg(feature = "std")] { 66 | /// use wyz::fmt::*; 67 | /// 68 | /// let text = "hello, 69 | /// world!"; 70 | /// assert_eq!( 71 | /// format!("{:?}", text), 72 | /// r#""hello,\nworld!""#, 73 | /// // the render contains the sequence U+005C REVERSE SOLIDUS, 74 | /// // then U+006E LATIN SMALL LETTER N, *not* U+000A LINE FEED. 75 | /// ); 76 | /// assert_eq!( 77 | /// format!("{:?}", text.fmt_display()), 78 | /// "hello,\nworld!", 79 | /// // the render does not have wrapping quotes, and 80 | /// // the newline is printed directly, not escaped. 81 | /// ); 82 | /// # } 83 | /// ``` 84 | #[inline(always)] 85 | fn fmt_display(self) -> FmtDisplay 86 | where Self: Display { 87 | FmtDisplay(self) 88 | } 89 | 90 | /// Causes `self` to use its `LowerExp` implementation when 91 | /// `Debug`-formatted. 92 | /// 93 | /// ## Examples 94 | /// 95 | /// ```rust 96 | /// # #[cfg(feature = "std")] { 97 | /// use wyz::fmt::*; 98 | /// 99 | /// assert_eq!( 100 | /// format!("{:?}", (31.4).fmt_lower_exp()), 101 | /// "3.14e1", 102 | /// ); 103 | /// # } 104 | /// ``` 105 | #[inline(always)] 106 | fn fmt_lower_exp(self) -> FmtLowerExp 107 | where Self: LowerExp { 108 | FmtLowerExp(self) 109 | } 110 | 111 | /// Causes `self` to use its `LowerHex` implementation when 112 | /// `Debug`-formatted. 113 | /// 114 | /// ## Examples 115 | /// 116 | /// ```rust 117 | /// # #[cfg(feature = "std")] { 118 | /// use wyz::fmt::*; 119 | /// 120 | /// assert_eq!( 121 | /// format!("{:?}", 44203.fmt_lower_hex()), 122 | /// "acab", 123 | /// ); 124 | /// # } 125 | /// ``` 126 | #[inline(always)] 127 | fn fmt_lower_hex(self) -> FmtLowerHex 128 | where Self: LowerHex { 129 | FmtLowerHex(self) 130 | } 131 | 132 | /// Causes `self` to use its `Octal` implementation when `Debug`-formatted. 133 | /// 134 | /// ## Examples 135 | /// 136 | /// ```rust 137 | /// # #[cfg(feature = "std")] { 138 | /// use wyz::fmt::*; 139 | /// 140 | /// assert_eq!( 141 | /// format!("{:?}", 493.fmt_octal()), 142 | /// "755", 143 | /// ); 144 | /// # } 145 | /// ``` 146 | #[inline(always)] 147 | fn fmt_octal(self) -> FmtOctal 148 | where Self: Octal { 149 | FmtOctal(self) 150 | } 151 | 152 | /// Causes `self` to use its `Pointer` implementation when 153 | /// `Debug`-formatted. 154 | /// 155 | /// ## Examples 156 | /// 157 | /// ```rust 158 | /// # #[cfg(feature = "std")] { 159 | /// use wyz::fmt::*; 160 | /// 161 | /// let val = 6; 162 | /// let addr = &val as *const i32; 163 | /// println!("{:?}", addr.fmt_pointer()); 164 | /// // prints a memory address. 165 | /// # } 166 | /// ``` 167 | #[inline(always)] 168 | fn fmt_pointer(self) -> FmtPointer 169 | where Self: Pointer { 170 | FmtPointer(self) 171 | } 172 | 173 | /// Causes `self` to use its `UpperExp` implementation when 174 | /// `Debug`-formatted. 175 | /// 176 | /// ## Examples 177 | /// 178 | /// ```rust 179 | /// # #[cfg(feature = "std")] { 180 | /// use wyz::fmt::*; 181 | /// 182 | /// assert_eq!( 183 | /// format!("{:?}", (62.8).fmt_upper_exp()), 184 | /// "6.28E1", 185 | /// ); 186 | /// # } 187 | /// ``` 188 | #[inline(always)] 189 | fn fmt_upper_exp(self) -> FmtUpperExp 190 | where Self: UpperExp { 191 | FmtUpperExp(self) 192 | } 193 | 194 | /// Causes `self` to use its `UpperHex` implementation when 195 | /// `Debug`-formatted. 196 | /// 197 | /// ## Examples 198 | /// 199 | /// ```rust 200 | /// # #[cfg(feature = "std")] { 201 | /// use wyz::fmt::*; 202 | /// 203 | /// assert_eq!( 204 | /// format!("{:?}", 55322.fmt_upper_hex()), 205 | /// "D81A", 206 | /// ); 207 | /// # } 208 | /// ``` 209 | #[inline(always)] 210 | fn fmt_upper_hex(self) -> FmtUpperHex 211 | where Self: UpperHex { 212 | FmtUpperHex(self) 213 | } 214 | 215 | /// Formats each item in a sequence. 216 | /// 217 | /// This wrapper structure conditionally implements all of the formatting 218 | /// traits when `self` can be viewed as an iterator whose *items* implement 219 | /// them. It iterates over `&self` and prints each item according to the 220 | /// formatting specifier provided. 221 | /// 222 | /// ## Examples 223 | /// 224 | /// ```rust 225 | /// # #[cfg(feature = "std")] { 226 | /// use wyz::fmt::*; 227 | /// 228 | /// let seq = [10, 20, 30, 40]; 229 | /// assert_eq!( 230 | /// format!("{:?}", seq.fmt_list().fmt_lower_hex()), 231 | /// "[a, 14, 1e, 28]", 232 | /// ); 233 | /// # } 234 | /// ``` 235 | #[inline(always)] 236 | fn fmt_list(self) -> FmtList 237 | where for<'a> &'a Self: IntoIterator { 238 | FmtList(self) 239 | } 240 | } 241 | 242 | impl FmtForward for T { 243 | } 244 | 245 | /// Forwards a type’s `Binary` formatting implementation to `Debug`. 246 | #[repr(transparent)] 247 | pub struct FmtBinary(pub T); 248 | 249 | /// Forwards a type’s `Display` formatting implementation to `Debug`. 250 | #[repr(transparent)] 251 | pub struct FmtDisplay(pub T); 252 | 253 | /// Renders each element of a stream into a list. 254 | #[repr(transparent)] 255 | pub struct FmtList(pub T) 256 | where for<'a> &'a T: IntoIterator; 257 | 258 | /// Forwards a type’s `LowerExp` formatting implementation to `Debug`. 259 | #[repr(transparent)] 260 | pub struct FmtLowerExp(pub T); 261 | 262 | /// Forwards a type’s `LowerHex` formatting implementation to `Debug`. 263 | #[repr(transparent)] 264 | pub struct FmtLowerHex(pub T); 265 | 266 | /// Forwards a type’s `Octal` formatting implementation to `Debug`. 267 | #[repr(transparent)] 268 | pub struct FmtOctal(pub T); 269 | 270 | /// Forwards a type’s `Pointer` formatting implementation to `Debug`. 271 | #[repr(transparent)] 272 | pub struct FmtPointer(pub T); 273 | 274 | /// Forwards a type’s `UpperExp` formatting implementation to `Debug`. 275 | #[repr(transparent)] 276 | pub struct FmtUpperExp(pub T); 277 | 278 | /// Forwards a type’s `UpperHex` formatting implementation to `Debug`. 279 | #[repr(transparent)] 280 | pub struct FmtUpperHex(pub T); 281 | 282 | macro_rules! fmt { 283 | ($($w:ty => $t:ident),* $(,)?) => { $( 284 | #[cfg(not(tarpaulin_include))] 285 | impl Binary for $w { 286 | #[inline(always)] 287 | fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { 288 | Binary::fmt(&self.0, fmt) 289 | } 290 | } 291 | 292 | impl Debug for $w { 293 | #[inline(always)] 294 | fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { 295 | ::fmt(&self.0, fmt) 296 | } 297 | } 298 | 299 | #[cfg(not(tarpaulin_include))] 300 | impl Display for $w { 301 | #[inline(always)] 302 | fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { 303 | Display::fmt(&self.0, fmt) 304 | } 305 | } 306 | 307 | #[cfg(not(tarpaulin_include))] 308 | impl LowerExp for $w { 309 | #[inline(always)] 310 | fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { 311 | LowerExp::fmt(&self.0, fmt) 312 | } 313 | } 314 | 315 | #[cfg(not(tarpaulin_include))] 316 | impl LowerHex for $w { 317 | #[inline(always)] 318 | fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { 319 | LowerHex::fmt(&self.0, fmt) 320 | } 321 | } 322 | 323 | #[cfg(not(tarpaulin_include))] 324 | impl Octal for $w { 325 | #[inline(always)] 326 | fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { 327 | Octal::fmt(&self.0, fmt) 328 | } 329 | } 330 | 331 | #[cfg(not(tarpaulin_include))] 332 | impl Pointer for $w { 333 | #[inline(always)] 334 | fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { 335 | Pointer::fmt(&self.0, fmt) 336 | } 337 | } 338 | 339 | #[cfg(not(tarpaulin_include))] 340 | impl UpperExp for $w { 341 | #[inline(always)] 342 | fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { 343 | UpperExp::fmt(&self.0, fmt) 344 | } 345 | } 346 | 347 | #[cfg(not(tarpaulin_include))] 348 | impl UpperHex for $w { 349 | #[inline(always)] 350 | fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { 351 | UpperHex::fmt(&self.0, fmt) 352 | } 353 | } 354 | 355 | #[cfg(not(tarpaulin_include))] 356 | impl Deref for $w { 357 | type Target = T; 358 | 359 | #[inline(always)] 360 | fn deref(&self) -> &Self::Target { 361 | &self.0 362 | } 363 | } 364 | 365 | #[cfg(not(tarpaulin_include))] 366 | impl DerefMut for $w { 367 | #[inline(always)] 368 | fn deref_mut(&mut self) -> &mut Self::Target { 369 | &mut self.0 370 | } 371 | } 372 | 373 | #[cfg(not(tarpaulin_include))] 374 | impl AsRef for $w { 375 | #[inline(always)] 376 | fn as_ref(&self) -> &T { 377 | &self.0 378 | } 379 | } 380 | 381 | #[cfg(not(tarpaulin_include))] 382 | impl AsMut for $w { 383 | #[inline(always)] 384 | fn as_mut(&mut self) -> &mut T { 385 | &mut self.0 386 | } 387 | } 388 | )* }; 389 | } 390 | 391 | fmt!( 392 | FmtBinary => Binary, 393 | FmtDisplay => Display, 394 | FmtLowerExp => LowerExp, 395 | FmtLowerHex => LowerHex, 396 | FmtOctal => Octal, 397 | FmtPointer => Pointer, 398 | FmtUpperExp => UpperExp, 399 | FmtUpperHex => UpperHex, 400 | ); 401 | 402 | impl Binary for FmtList 403 | where 404 | for<'a> &'a T: IntoIterator, 405 | for<'a> <&'a T as IntoIterator>::Item: Binary, 406 | { 407 | fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { 408 | fmt.debug_list() 409 | .entries((&self.0).into_iter().map(FmtBinary)) 410 | .finish() 411 | } 412 | } 413 | 414 | impl Debug for FmtList 415 | where 416 | for<'a> &'a T: IntoIterator, 417 | for<'a> <&'a T as IntoIterator>::Item: Debug, 418 | { 419 | fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { 420 | fmt.debug_list().entries((&self.0).into_iter()).finish() 421 | } 422 | } 423 | 424 | impl Display for FmtList 425 | where 426 | for<'a> &'a T: IntoIterator, 427 | for<'a> <&'a T as IntoIterator>::Item: Display, 428 | { 429 | fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { 430 | fmt.debug_list() 431 | .entries((&self.0).into_iter().map(FmtDisplay)) 432 | .finish() 433 | } 434 | } 435 | 436 | impl LowerExp for FmtList 437 | where 438 | for<'a> &'a T: IntoIterator, 439 | for<'a> <&'a T as IntoIterator>::Item: LowerExp, 440 | { 441 | fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { 442 | fmt.debug_list() 443 | .entries((&self.0).into_iter().map(FmtLowerExp)) 444 | .finish() 445 | } 446 | } 447 | 448 | impl LowerHex for FmtList 449 | where 450 | for<'a> &'a T: IntoIterator, 451 | for<'a> <&'a T as IntoIterator>::Item: LowerHex, 452 | { 453 | fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { 454 | fmt.debug_list() 455 | .entries((&self.0).into_iter().map(FmtLowerHex)) 456 | .finish() 457 | } 458 | } 459 | 460 | impl Octal for FmtList 461 | where 462 | for<'a> &'a T: IntoIterator, 463 | for<'a> <&'a T as IntoIterator>::Item: Octal, 464 | { 465 | fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { 466 | fmt.debug_list() 467 | .entries((&self.0).into_iter().map(FmtOctal)) 468 | .finish() 469 | } 470 | } 471 | 472 | impl UpperExp for FmtList 473 | where 474 | for<'a> &'a T: IntoIterator, 475 | for<'a> <&'a T as IntoIterator>::Item: UpperExp, 476 | { 477 | fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { 478 | fmt.debug_list() 479 | .entries((&self.0).into_iter().map(FmtUpperExp)) 480 | .finish() 481 | } 482 | } 483 | 484 | impl UpperHex for FmtList 485 | where 486 | for<'a> &'a T: IntoIterator, 487 | for<'a> <&'a T as IntoIterator>::Item: UpperHex, 488 | { 489 | fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { 490 | fmt.debug_list() 491 | .entries((&self.0).into_iter().map(FmtUpperHex)) 492 | .finish() 493 | } 494 | } 495 | 496 | #[cfg(not(tarpaulin_include))] 497 | impl Deref for FmtList 498 | where for<'a> &'a T: IntoIterator 499 | { 500 | type Target = T; 501 | 502 | #[inline(always)] 503 | fn deref(&self) -> &Self::Target { 504 | &self.0 505 | } 506 | } 507 | 508 | #[cfg(not(tarpaulin_include))] 509 | impl DerefMut for FmtList 510 | where for<'a> &'a T: IntoIterator 511 | { 512 | #[inline(always)] 513 | fn deref_mut(&mut self) -> &mut Self::Target { 514 | &mut self.0 515 | } 516 | } 517 | 518 | #[cfg(not(tarpaulin_include))] 519 | impl AsRef for FmtList 520 | where for<'a> &'a T: IntoIterator 521 | { 522 | #[inline(always)] 523 | fn as_ref(&self) -> &T { 524 | &self.0 525 | } 526 | } 527 | 528 | #[cfg(not(tarpaulin_include))] 529 | impl AsMut for FmtList 530 | where for<'a> &'a T: IntoIterator 531 | { 532 | #[inline(always)] 533 | fn as_mut(&mut self) -> &mut T { 534 | &mut self.0 535 | } 536 | } 537 | 538 | #[cfg(all(test, feature = "alloc"))] 539 | mod tests { 540 | #[cfg(not(feature = "std"))] 541 | use alloc::format; 542 | 543 | #[cfg(feature = "std")] 544 | use std::format; 545 | 546 | use super::*; 547 | 548 | #[test] 549 | fn render_item() { 550 | let num = 29; 551 | 552 | assert_eq!(format!("{:?}", num.fmt_binary()), "11101"); 553 | assert_eq!(format!("{:?}", num.fmt_display()), "29"); 554 | assert_eq!(format!("{:?}", num.fmt_upper_hex()), "1D"); 555 | assert_eq!(format!("{:?}", num.fmt_octal()), "35"); 556 | assert_eq!(format!("{:?}", num.fmt_lower_hex()), "1d"); 557 | 558 | let num = 53.7; 559 | assert_eq!(format!("{:?}", num.fmt_lower_exp()), "5.37e1"); 560 | assert_eq!(format!("{:?}", num.fmt_upper_exp()), "5.37E1"); 561 | } 562 | 563 | #[test] 564 | fn render_list() { 565 | let list = [0, 1, 2, 3]; 566 | assert_eq!(format!("{:02b}", list.fmt_list()), "[00, 01, 10, 11]"); 567 | assert_eq!(format!("{:01?}", list.fmt_list()), "[0, 1, 2, 3]"); 568 | assert_eq!(format!("{:01}", list.fmt_list()), "[0, 1, 2, 3]"); 569 | 570 | let list = [-51.0, -1.2, 1.3, 54.0]; 571 | assert_eq!( 572 | format!("{:e}", list.fmt_list()), 573 | "[-5.1e1, -1.2e0, 1.3e0, 5.4e1]" 574 | ); 575 | assert_eq!( 576 | format!("{:E}", list.fmt_list()), 577 | "[-5.1E1, -1.2E0, 1.3E0, 5.4E1]" 578 | ); 579 | 580 | let list = [0, 10, 20, 30]; 581 | assert_eq!(format!("{:02x}", list.fmt_list()), "[00, 0a, 14, 1e]"); 582 | assert_eq!(format!("{:02o}", list.fmt_list()), "[00, 12, 24, 36]"); 583 | assert_eq!(format!("{:02X}", list.fmt_list()), "[00, 0A, 14, 1E]"); 584 | 585 | assert_eq!( 586 | format!("{:02?}", list.fmt_list().fmt_lower_hex()), 587 | "[00, 0a, 14, 1e]" 588 | ); 589 | } 590 | } 591 | --------------------------------------------------------------------------------