From 5271af1f45cfbc737ae9b3fd86b0ff780e557150 Mon Sep 17 00:00:00 2001 From: Ivan Steklov <61116326+Gridness@users.noreply.github.com> Date: Sun, 16 Mar 2025 12:40:58 +0300 Subject: [PATCH 1/2] Initial setup of project structure and files. --- .github/workflows/rust.yml | 50 +++++++++++++++++++++ .gitignore | 1 + Cargo.lock | 92 ++++++++++++++++++++++++++++++++++++++ Cargo.toml | 7 +++ src/main.rs | 31 +++++++++++++ 5 files changed, 181 insertions(+) create mode 100644 .github/workflows/rust.yml create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/main.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..ddab0ce --- /dev/null +++ b/.github/workflows/rust.yml @@ -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 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..25a3aba --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,92 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "colored" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "rustroika" +version = "1.0.0" +dependencies = [ + "colored", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e37ac19 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "rustroika" +version = "1.0.0" +edition = "2024" + +[dependencies] +colored = "3.0.0" diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..541a4b2 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,31 @@ +fn main() { + let args: Vec = std::env::args().collect(); + + if args.len() != 4 { + eprintln!( + "Usage: {} ", + args[0] + ); + 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!("Total trips per month: {}", total_trips); + println!("Individual cost: {}", individual_cost); + println!("Monthly pass cost: {}", monthly_cost); + + match individual_cost.cmp(&monthly_cost) { + std::cmp::Ordering::Less => println!("Paying per trip is cheaper!"), + std::cmp::Ordering::Greater => println!("Buying the monthly pass is cheaper!"), + std::cmp::Ordering::Equal => println!("Both options cost the same."), + } +} From 03c06e5462138fe908890fd57757d21cbfbf4b50 Mon Sep 17 00:00:00 2001 From: Ivan Steklov <61116326+Gridness@users.noreply.github.com> Date: Sun, 16 Mar 2025 12:45:19 +0300 Subject: [PATCH 2/2] Enhance trip cost calculator with colored output and detailed discount explanation. Updated usage message format and improved output layout for clarity. Added logic to display cost savings for trip vs. monthly pass options. --- src/main.rs | 69 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index 541a4b2..414debc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,16 @@ +use colored::*; + fn main() { let args: Vec = std::env::args().collect(); if args.len() != 4 { eprintln!( - "Usage: {} ", - args[0] + "{}", + format!( + "Usage: {} ", + args[0] + ) + .red() ); std::process::exit(1); } @@ -19,13 +25,56 @@ fn main() { let individual_cost = full_price_count * ticket_price + discounted_count * (ticket_price / 2); - println!("Total trips per month: {}", total_trips); - println!("Individual cost: {}", individual_cost); - println!("Monthly pass cost: {}", monthly_cost); + 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) + ); - match individual_cost.cmp(&monthly_cost) { - std::cmp::Ordering::Less => println!("Paying per trip is cheaper!"), - std::cmp::Ordering::Greater => println!("Buying the monthly pass is cheaper!"), - std::cmp::Ordering::Equal => println!("Both options cost the same."), - } + 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"); }