Skip to content
Merged
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
50 changes: 50 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Rust CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

env:
CARGO_TERM_COLOR: always

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose

lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt
- name: Check formatting
run: cargo fmt -- --check
- name: Run clippy
run: cargo clippy -- -D warnings

sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Install cargo-audit
run: cargo install cargo-audit
- name: Run cargo-audit
run: cargo audit
- name: Install cargo-deny
run: cargo install cargo-deny
- name: Run cargo-deny
run: cargo deny check
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
92 changes: 92 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "rustroika"
version = "1.0.0"
edition = "2024"

[dependencies]
colored = "3.0.0"
80 changes: 80 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use colored::*;

fn main() {
let args: Vec<String> = std::env::args().collect();

if args.len() != 4 {
eprintln!(
"{}",
format!(
"Usage: {} <trips/week> <monthly_cost> <ticket_price>",
args[0]
)
.red()
);
std::process::exit(1);
}

let trips_per_week: u32 = args[1].parse().expect("Invalid trips per week");
let monthly_cost: u32 = args[2].parse().expect("Invalid monthly cost");
let ticket_price: u32 = args[3].parse().expect("Invalid ticket price");

let total_trips = trips_per_week * 4;
let full_price_count = (total_trips + 1) / 2;
let discounted_count = total_trips / 2;

let individual_cost = full_price_count * ticket_price + discounted_count * (ticket_price / 2);

println!(
"\n╭{}╴{}╴{}╴{}╴╮",
"─".repeat(14),
"─".repeat(10),
"─".repeat(14),
"─".repeat(17)
);
println!(
"│ {:14} │ {:10} │ {:14} │ {:10} │",
"Total trips".cyan().bold(),
"Monthly".cyan().bold(),
"Individual".cyan().bold(),
"Ticket".cyan().bold()
);
println!(
"│ {:14} │ {:>10} │ {:>14} │ {:>10} │",
format!("{}", total_trips).yellow(),
format!("{} RUB", monthly_cost).yellow(),
format!("{} RUB", individual_cost).yellow(),
format!("{} RUB", ticket_price).yellow()
);
println!(
"╰{}╴{}╴{}╴{}╯",
"─".repeat(14),
"─".repeat(10),
"─".repeat(14),
"─".repeat(18)
);

let message = match individual_cost.cmp(&monthly_cost) {
std::cmp::Ordering::Less => format!(
"🚌 Paying per trip is cheaper by {} RUB!",
monthly_cost - individual_cost
)
.green()
.bold(),
std::cmp::Ordering::Greater => format!(
"💰 Monthly pass saves you {} RUB!",
individual_cost - monthly_cost
)
.bright_green()
.bold(),
std::cmp::Ordering::Equal => "⚖️ Both options cost the same".blue().bold(),
};

println!("\n{}\n", message);

// Add explanation of discount logic
println!("{}", "Note:".bold().underline());
println!("• Subsequent trips within 90 minutes get 50% discount");
println!("• Discounted prices are rounded down (e.g., 63 → 31)");
println!("• Calculation assumes optimal discount usage");
}
Loading