├── .gitignore ├── Cargo.toml ├── README.md ├── .github └── workflows │ └── build.yml ├── LICENSE └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rspark" 3 | version = "0.2.0" 4 | authors = ["reugn "] 5 | license = "MIT" 6 | readme = "README.md" 7 | repository = "https://github.com/reugn/rspark" 8 | documentation = "https://github.com/reugn/rspark" 9 | homepage = "https://crates.io/crates/rspark" 10 | description = "Sparklines for Rust" 11 | keywords = ["cli", "format"] 12 | categories = ["command-line-interface", "value-formatting"] 13 | exclude = ["/.travis.yml", ".gitignore"] 14 | edition = "2018" 15 | 16 | [badges] 17 | travis-ci = { repository = "reugn/rspark" } 18 | 19 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 20 | 21 | [dependencies] 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rspark ▁▂▆▇▁▄█▁ 2 | [![Build](https://github.com/reugn/rspark/actions/workflows/build.yml/badge.svg)](https://github.com/reugn/rspark/actions/workflows/build.yml) 3 | [![Crate](https://img.shields.io/crates/v/rspark.svg)](https://crates.io/crates/rspark) 4 | 5 | Sparklines library for Rust applications. 6 | A Rust port of [spark](https://github.com/holman/spark). 7 | 8 | ## Usage 9 | 10 | Add `rspark` to your `Cargo.toml`: 11 | ```toml 12 | [dependencies] 13 | rspark = "0.2.0" 14 | ``` 15 | 16 | Example: 17 | ```rust 18 | let v = vec![2, 250, 670, 890, 2, 430, 11, 908, 123, 57]; 19 | let res = rspark::render(&v).unwrap(); 20 | assert_eq!("▁▂▆▇▁▄▁█▁▁", res); 21 | ``` 22 | 23 | ## License 24 | MIT -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | push: 8 | 9 | jobs: 10 | test: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | os: [ubuntu-latest, macos-latest, windows-latest] 16 | 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v2 20 | 21 | - name: Install latest stable toolchain 22 | uses: actions-rs/toolchain@v1 23 | with: 24 | toolchain: stable 25 | override: true 26 | 27 | - name: Build 28 | uses: actions-rs/cargo@v1 29 | with: 30 | command: build 31 | args: --release 32 | 33 | - name: Test 34 | uses: actions-rs/cargo@v1 35 | with: 36 | command: test 37 | args: --verbose -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 reugn 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![crate_name = "rspark"] 2 | 3 | pub mod rspark { 4 | 5 | use std::error::Error; 6 | use std::fmt; 7 | 8 | /// The Unicode representation of the graph ticks. 9 | const TICKS: [char; 8] = [ 10 | '\u{2581}', '\u{2582}', '\u{2583}', '\u{2584}', '\u{2585}', '\u{2586}', '\u{2587}', 11 | '\u{2588}', 12 | ]; 13 | 14 | /// Renders a graph for the given numeric vector. 15 | /// 16 | /// # Arguments 17 | /// 18 | /// * `v` - The numeric vector to render a graph. 19 | /// 20 | /// # Examples 21 | /// 22 | /// ``` 23 | /// let v = vec![2, 250, 670, 890, 2, 430, 11, 908, 123, 57]; 24 | /// let res = rspark::rspark::render(&v).unwrap(); 25 | /// assert_eq!("▁▂▆▇▁▄▁█▁▁", res); 26 | /// ``` 27 | pub fn render(v: &Vec) -> Result { 28 | let mut s = String::new(); 29 | render_to(v, &mut s) 30 | .map(|ok_val| ok_val.to_string()) 31 | .map_err(|err_val| err_val) 32 | } 33 | 34 | /// Renders a graph and appends it to the given string. 35 | /// 36 | /// # Arguments 37 | /// 38 | /// * `v` - The numeric vector to render a graph. 39 | /// * `s` - The mutable String pointer to append the graph. 40 | /// 41 | /// # Examples 42 | /// 43 | /// ``` 44 | /// let v = vec![2, 250, 670, 890, 2, 430, 11, 908, 123, 57]; 45 | /// let mut s = String::from(">"); 46 | /// let res = rspark::rspark::render_to(&v, &mut s).unwrap(); 47 | /// assert_eq!(">▁▂▆▇▁▄▁█▁▁", res); 48 | /// ``` 49 | pub fn render_to<'a>(v: &Vec, s: &'a mut String) -> Result<&'a str, RenderError> { 50 | if v.len() < 2 { 51 | return Err(RenderError::InvalidVectorParameter); 52 | } 53 | let max = v.iter().max().unwrap(); 54 | let min = v.iter().min().unwrap(); 55 | let scale = (max - min) as f32 / 7.; 56 | for e in v.iter() { 57 | let i = (e - min) / scale as i32; 58 | (*s).push_str(&TICKS[i as usize].to_string()); 59 | } 60 | Ok(&s[..]) 61 | } 62 | 63 | #[derive(Debug)] 64 | pub enum RenderError { 65 | /// The invalid vector parameter error. 66 | InvalidVectorParameter, 67 | } 68 | 69 | impl fmt::Display for RenderError { 70 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 71 | match *self { 72 | RenderError::InvalidVectorParameter => f.write_str("InvalidVectorParameter"), 73 | } 74 | } 75 | } 76 | 77 | impl Error for RenderError { 78 | fn description(&self) -> &str { 79 | match *self { 80 | RenderError::InvalidVectorParameter => "Invalid vector parameter", 81 | } 82 | } 83 | } 84 | } 85 | 86 | #[cfg(test)] 87 | mod tests { 88 | use super::*; 89 | 90 | #[test] 91 | fn test_render() { 92 | let v = vec![2, 250, 670, 890, 2, 430, 11, 908, 123, 57]; 93 | let res = rspark::render(&v).unwrap(); 94 | println!("{}", res); 95 | assert_eq!("▁▂▆▇▁▄▁█▁▁", res); 96 | } 97 | 98 | #[test] 99 | fn test_render_to() { 100 | let v = vec![2, 250, 670, 890, 2, 430, 11, 908, 123, 57]; 101 | let mut s = String::from(">"); 102 | let res = rspark::render_to(&v, &mut s).unwrap(); 103 | println!("{}", res); 104 | assert_eq!(">▁▂▆▇▁▄▁█▁▁", res); 105 | } 106 | 107 | #[test] 108 | fn test_render_err() { 109 | let v = vec![2]; 110 | let res = rspark::render(&v); 111 | match res { 112 | Ok(_) => panic!("Error expected."), 113 | _ => (), 114 | } 115 | } 116 | } 117 | --------------------------------------------------------------------------------