Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ members = [
"logname",
"nice",
"pwd",
"seq",
"sleep",
"true",
"whoami",
Expand Down
13 changes: 13 additions & 0 deletions seq/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "seq"
version = "0.1.0"
authors = ["GrayJack <gr41.j4ck@gmail.com>" , "FrancisMurillo <francisavmurillo@gmail.com>"]
build = "build.rs"
edition = "2018"

[dependencies]
clap = { version = "^2.33.0", features = ["yaml", "wrap_help"] }
rust_decimal = "^1.0.3"

[build-dependencies]
clap = { version = "^2.33.0", features = ["yaml"] }
19 changes: 19 additions & 0 deletions seq/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use std::env;

use clap::{load_yaml, App, Shell};

fn main() {
let yaml = load_yaml!("src/seq.yml");
let mut app = App::from_yaml(yaml);

let out_dir = match env::var("OUT_DIR") {
Ok(dir) => dir,
_ => return
};

app.gen_completions("seq", Shell::Zsh, out_dir.clone());
app.gen_completions("seq", Shell::Fish, out_dir.clone());
app.gen_completions("seq", Shell::Bash, out_dir.clone());
app.gen_completions("seq", Shell::PowerShell, out_dir.clone());
app.gen_completions("seq", Shell::Elvish, out_dir);
}
192 changes: 192 additions & 0 deletions seq/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
#![allow(clippy::suspicious_arithmetic_impl)]
use std::{
cmp::Ordering,
fmt,
ops::{Add, AddAssign},
process,
str::FromStr
};

use clap::{load_yaml, App, AppSettings};
use rust_decimal::Decimal;

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Number {
NegInf,
PosInf,
NaN,
Num(Decimal)
}

use std::cmp::Ordering::*;
use Number::*;

impl Add for Number {
type Output = Self;

fn add(self, other: Self) -> Self {
match (self, other) {
(Num(left), Num(right)) => match left.checked_add(right) {
Some(next_dec) => Num(next_dec),
None => {
if left.is_sign_positive() && right.is_sign_positive() {
PosInf
} else if left.is_sign_negative() && right.is_sign_negative() {
NegInf
} else {
NaN
}
},
},
(NaN, _) | (_, NaN) | (PosInf, NegInf) | (NegInf, PosInf) => NaN,
(PosInf, PosInf) | (PosInf, Num(_)) | (Num(_), PosInf) => PosInf,
(NegInf, NegInf) | (NegInf, Num(_)) | (Num(_), NegInf) => NegInf
}
}
}

impl AddAssign for Number {
fn add_assign(&mut self, other: Self) { *self = *self + other; }
}

impl PartialOrd for Number {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self, other) {
(Num(left_dec), Num(right_dec)) => Some(left_dec.cmp(right_dec)),
(NaN, NaN) => Some(Equal),
(NaN, _) | (_, NaN) => None,
(PosInf, PosInf) | (NegInf, NegInf) => Some(Equal),
(NegInf, PosInf) | (Num(_), PosInf) | (NegInf, Num(_)) => Some(Less),
(PosInf, NegInf) | (PosInf, Num(_)) | (Num(_), NegInf) => Some(Greater)
}
}
}

impl From<f64> for Number {
fn from(number: f64) -> Self {
match number {
value if value.is_infinite() => {
if value.is_sign_positive() {
PosInf
} else {
NegInf
}
},
value if value.is_finite() => Num(Decimal::from_str(&value.to_string()).unwrap()),
_ => NaN
}
}
}

impl fmt::Display for Number {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
PosInf => write!(f, "inf"),
NegInf => write!(f, "-inf"),
NaN => write!(f, "nan"),
Num(value) => write!(f, "{}", value)
}
}
}

#[derive(Clone, Debug)]
pub struct Seq {
current: Number,
step: Number,
stop: Number
}

impl Iterator for Seq {
type Item = Number;

fn next(&mut self) -> Option<Self::Item> {
match self.step {
step @ PosInf | step @ NegInf => Some(step + self.current),
Num(step_dec) => {
if (step_dec.is_sign_positive() && self.current <= self.stop)
|| (step_dec.is_sign_negative() && self.current >= self.stop)
{
let result = self.current;

self.current += self.step;

Some(result)
} else {
None
}
},
NaN => Some(NaN)
}
}
}

fn parse_number(text: &str) -> Result<f64, ()> { text.parse::<f64>().map_err(|_| ()) }

fn check_nan(value: f64) -> Result<f64, ()> { if value.is_nan() { Err(()) } else { Ok(value) } }

fn check_zero(value: f64) -> Result<f64, ()> { if value == 0.0 { Err(()) } else { Ok(value) } }

fn argument_error(field: &str) -> impl Fn(()) -> f64 + '_ {
move |_| {
eprintln!(
"seq: Invalid {}.\nTry 'seq --help' for more information.",
field
);
process::exit(1);
}
}

const DEFAULT_FIRST: &str = "1";
const DEFAULT_INCREMENT: &str = "1";
const DEFAULT_SEPARATOR: &str = "\n";

fn main() {
let yaml = load_yaml!("seq.yml");
let matches = App::from_yaml(yaml)
.setting(AppSettings::AllowNegativeNumbers)
.get_matches();

let (raw_first, raw_increment, raw_last) = match (
matches.value_of("FIRST"),
matches.value_of("SECOND"),
matches.value_of("THIRD")
) {
(Some(last), None, None) => (DEFAULT_FIRST, DEFAULT_INCREMENT, last),
(Some(first), Some(last), None) => (first, DEFAULT_INCREMENT, last),
(Some(first), Some(increment), Some(last)) => (first, increment, last),
_ => {
eprintln!("seq: Missing operands.\nTry 'seq --help' for more information.");
process::exit(1);
}
};

let separator = matches.value_of("SEPARATOR").unwrap_or(DEFAULT_SEPARATOR);

let first = parse_number(raw_first)
.and_then(check_nan)
.unwrap_or_else(argument_error("FIRST"));

let last = parse_number(raw_last)
.and_then(check_nan)
.unwrap_or_else(argument_error("LAST"));

let increment = parse_number(raw_increment)
.and_then(check_nan)
.and_then(check_zero)
.unwrap_or_else(argument_error("INCREMENT"));

let mut iter = (Seq {
current: Number::from(first),
stop: Number::from(last),
step: Number::from(increment)
})
.peekable();

while let Some(index) = iter.next() {
if *&iter.peek().is_some() {
print!("{}{}", index, separator);
} else {
println!("{}", index);
}
}
}
59 changes: 59 additions & 0 deletions seq/src/seq.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: seq
version: "0.0.0"
author: Francis Murillo <francisavmurillo@gmail.com>
about: "Print a sequence of numbers"
long_about: "Print numbers from FIRST to LAST, in steps of INCREMENT."
usage: >

[OPTIONS] <LAST>

[OPTIONS] <FIRST> <LAST>

[OPTIONS] <FIRST> <INCREMENT> <LAST>
args:
- FORMAT:
short: "f"
long: "format"
help: "Use printf style floating-point FORMAT"
takes_value: true
- SEPARATOR:
short: "s"
long: "separator"
help: "Use STRING to separate numbers"
default_value: "\n"
takes_value: true
- WIDTH:
short: "w"
long: "equal-width"
help: "Equalize width by padding with leading zeroes"
takes_value: false
- FIRST:
help: "The initial number to start the sequence."
long_help: >
The initial number to start the sequence.


This must not be NaN. If omitted, defaults to 1.
required: true
- SECOND:
help: "The step/increment added after each preceeding number in the sequence."
long_help: >
The step/increment added after each preceeding number in the
sequence.


If FIRST is greater than LAST, this should be a positive
number; if less than, this should be a negative number. This
must not be 0 or NaN. If omitted, defaults to 1 even if FIRST
is greater than LAST.
required: false
- THIRD:
help: "The limit of the sequence."
long_help: >
The limit of the sequence.


If the next number in a sequence exceeds above or falls below
LAST whether STEP is positive or negative respectively, the
sequence ends. This must not be NaN.
required: false