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..414debc --- /dev/null +++ b/src/main.rs @@ -0,0 +1,80 @@ +use colored::*; + +fn main() { + let args: Vec = std::env::args().collect(); + + if args.len() != 4 { + eprintln!( + "{}", + format!( + "Usage: {} ", + 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"); +}