├── .gitignore ├── .travis.yml ├── README.tpl ├── Cargo.toml ├── src ├── iter_pair.rs └── lib.rs ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.idea 3 | **/*.rs.bk 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | cache: cargo 3 | script: 4 | - cargo test 5 | deploy: 6 | provider: cargo 7 | token: $API_TOKEN 8 | on: 9 | branch: master 10 | tags: true 11 | -------------------------------------------------------------------------------- /README.tpl: -------------------------------------------------------------------------------- 1 | [![Crates.io](https://img.shields.io/crates/v/human-sort.svg)](https://crates.io/crates/human-sort) 2 | {{badges}} 3 | 4 | # {{crate}} 5 | 6 | {{readme}} 7 | 8 | ## License 9 | 10 | Licensed under {{license}} license. 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "human-sort" 3 | version = "0.2.2" 4 | authors = ["Vlad Nagikh "] 5 | license = "MIT" 6 | repository = "https://github.com/paradakh/human-sort" 7 | description = "Human sort (natural sort) implementation" 8 | keywords = ["sort", "natural", "human", "order"] 9 | readme = "README.md" 10 | edition = "2018" 11 | categories = ["algorithms"] 12 | 13 | [badges] 14 | travis-ci = { repository = "paradakh/human-sort", branch = "master" } 15 | 16 | 17 | [dependencies] 18 | -------------------------------------------------------------------------------- /src/iter_pair.rs: -------------------------------------------------------------------------------- 1 | use std::{iter::Peekable, str::Chars}; 2 | 3 | pub struct IterPair<'a> { 4 | pub fst: Peekable>, 5 | pub lst: Peekable>, 6 | } 7 | 8 | impl<'a> IterPair<'a> { 9 | pub fn from(i1: Chars<'a>, i2: Chars<'a>) -> Self { 10 | Self { 11 | fst: i1.peekable(), 12 | lst: i2.peekable(), 13 | } 14 | } 15 | 16 | pub fn next(&mut self) -> [Option; 2] { 17 | [self.fst.next(), self.lst.next()] 18 | } 19 | 20 | pub fn peek(&mut self) -> [Option<&char>; 2] { 21 | [self.fst.peek(), self.lst.peek()] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Crates.io](https://img.shields.io/crates/v/human-sort.svg)](https://crates.io/crates/human-sort) 2 | [![Build Status](https://travis-ci.org/paradakh/human-sort.svg?branch=master)](https://travis-ci.org/paradakh/human-sort) 3 | 4 | # human-sort 5 | 6 | Utilities to sort and compare strings with numeric symbols in human-friendly order. 7 | 8 | It built over iterators and compare string slices char by char (except for numerals) 9 | until the first difference found without creating Strings or another structures with whole 10 | data from provided &str, so doesn't require lots of memory. 11 | 12 | ## Examples 13 | 14 | ```rust 15 | use human_sort::sort; 16 | 17 | let mut arr = ["file10.txt", "file2.txt", "file1.txt"]; 18 | sort(&mut arr); 19 | 20 | assert_eq!(arr, ["file1.txt", "file2.txt", "file10.txt"]); 21 | ``` 22 | 23 | ```rust 24 | use std::cmp::Ordering; 25 | use human_sort::compare; 26 | 27 | assert_eq!(compare("item200", "item3"), Ordering::Greater); 28 | ``` 29 | 30 | ## License 31 | 32 | Licensed under MIT license. 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Vlad Nagikh 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 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Utilities to sort and compare strings with numeric symbols in human-friendly order. 2 | //! 3 | //! It built over iterators and compare string slices char by char (except for numerals) 4 | //! until the first difference found without creating Strings or another structures with whole 5 | //! data from provided &str, so doesn't require lots of memory. 6 | //! 7 | //! # Examples 8 | //! 9 | //! ``` 10 | //! use human_sort::sort; 11 | //! 12 | //! let mut arr = ["file10.txt", "file2.txt", "file1.txt"]; 13 | //! sort(&mut arr); 14 | //! 15 | //! assert_eq!(arr, ["file1.txt", "file2.txt", "file10.txt"]); 16 | //! ``` 17 | //! 18 | //! ``` 19 | //! use std::cmp::Ordering; 20 | //! use human_sort::compare; 21 | //! 22 | //! assert_eq!(compare("item200", "item3"), Ordering::Greater); 23 | //! ``` 24 | 25 | mod iter_pair; 26 | 27 | use iter_pair::IterPair; 28 | use std::{cmp::Ordering, iter::Peekable, str::Chars}; 29 | 30 | /// Sorts [&str] in human-friendly order 31 | /// 32 | /// # Example 33 | /// 34 | /// ``` 35 | /// use human_sort::sort; 36 | /// 37 | /// let mut arr = ["file10.txt", "file2.txt", "file1.txt"]; 38 | /// sort(&mut arr); 39 | /// 40 | /// assert_eq!(arr, ["file1.txt", "file2.txt", "file10.txt"]); 41 | /// ``` 42 | /// 43 | pub fn sort(arr: &mut [&str]) { 44 | arr.sort_by(|a, b| compare(a, b)); 45 | } 46 | 47 | /// Compares string slices 48 | /// 49 | /// # Example 50 | /// 51 | /// ``` 52 | /// use std::cmp::Ordering; 53 | /// use human_sort::compare; 54 | /// 55 | /// assert_eq!(compare("item200", "item3"), Ordering::Greater); 56 | /// ``` 57 | /// 58 | pub fn compare(s1: &str, s2: &str) -> Ordering { 59 | compare_chars_iters(s1.chars(), s2.chars()).unwrap_or(s1.cmp(s2)) 60 | } 61 | 62 | /// 63 | /// ``` 64 | /// use std::cmp::Ordering; 65 | /// use human_sort::compare_chars_iters; 66 | /// assert_eq!(compare_chars_iters("aaa".chars(), "bbb".chars()), Ok(Ordering::Less)); 67 | /// ``` 68 | /// 69 | pub fn compare_chars_iters<'a>(c1: Chars<'a>, c2: Chars<'a>) -> Result { 70 | let mut iters = IterPair::from(c1, c2); 71 | 72 | while let [Some(x), Some(y)] = iters.peek() { 73 | if x == y { 74 | iters.next(); 75 | } else if x.is_numeric() && y.is_numeric() { 76 | match take_numeric(&mut iters.fst).cmp(&take_numeric(&mut iters.lst)) { 77 | Ordering::Equal => iters.next(), 78 | ref a => return Ok(*a), 79 | }; 80 | } else { 81 | return Ok(x.cmp(y)); 82 | } 83 | } 84 | 85 | Err(()) 86 | } 87 | 88 | fn take_numeric(iter: &mut Peekable) -> u32 { 89 | let mut sum = 0; 90 | 91 | while let Some(p) = iter.peek() { 92 | match p.to_string().parse::() { 93 | Ok(n) => { 94 | sum = sum * 10 + n; 95 | iter.next(); 96 | } 97 | _ => break, 98 | } 99 | } 100 | 101 | sum 102 | } 103 | --------------------------------------------------------------------------------