//! GUI mode — iced 0.12 desktop application. //! //! Two-column layout with 8 calculator panels. use crate::calc::*; use iced::widget::{button, column, container, row, text, text_input, scrollable}; use iced::{ alignment, Application, Command, Element, Length, Settings, Theme, }; // ══════════════════════════════════════════════════════════════════════ // Input parsing helper // ══════════════════════════════════════════════════════════════════════ /// Parse a trimmed input string to f64 with a default fallback. fn parse_f64(raw: &str, fallback: f64) -> f64 { raw.trim().parse().unwrap_or(fallback) } // ══════════════════════════════════════════════════════════════════════ // Per-calculator state // ══════════════════════════════════════════════════════════════════════ struct OhmState { p_in: String, i_in: String, r_in: String, v_in: String, p_out: String, i_out: String, r_out: String, v_out: String, } struct MarginState { cost_in: String, sell_in: String, profit_out: String, margin_pct_out: String, markup_pct_out: String, } struct RoiState { investment_in: String, revenue_in: String, profit_out: String, roi_pct_out: String, } struct TokenState { model: AiModel, input_tok_in: String, output_tok_in: String, input_cost_out: String, output_cost_out: String, total_cost_out: String, } struct ElecState { power_in: String, hours_in: String, days_in: String, rate_in: String, daily_kwh_out: String, monthly_kwh_out: String, monthly_cost_out: String, yearly_cost_out: String, } struct BeState { fixed_in: String, price_in: String, variable_in: String, units_out: String, revenue_out: String, } struct SolarState { panels_in: String, panel_watts_in: String, sun_hours_in: String, rate_in: String, system_cost_in: String, efficiency_in: String, system_kw_out: String, daily_kwh_out: String, monthly_kwh_out: String, yearly_kwh_out: String, monthly_rev_out: String, yearly_rev_out: String, payback_out: String, lifetime_out: String, } struct Print3dState { fil_cost_in: String, weight_in: String, time_in: String, printer_w_in: String, elec_rate_in: String, fail_rate_in: String, sell_price_in: String, fil_kg_out: String, mat_cost_out: String, energy_cost_out: String, cost_per_out: String, profit_out: String, margin_out: String, } // ══════════════════════════════════════════════════════════════════════ // Main application state // ══════════════════════════════════════════════════════════════════════ struct App { ohm: OhmState, margin: MarginState, roi: RoiState, token: TokenState, elec: ElecState, be: BeState, solar: SolarState, print3d: Print3dState, } // ══════════════════════════════════════════════════════════════════════ // Messages // ══════════════════════════════════════════════════════════════════════ #[derive(Debug, Clone)] enum Message { OhmPower(String), OhmCurrent(String), OhmResistance(String), OhmVoltage(String), MarginCost(String), MarginSell(String), RoiInvestment(String), RoiRevenue(String), TokenPrev, TokenNext, TokenInputTok(String), TokenOutputTok(String), ElecPower(String), ElecHours(String), ElecDays(String), ElecRate(String), BeFixed(String), BePrice(String), BeVariable(String), SolarPanels(String), SolarPanelW(String), SolarSunHrs(String), SolarRate(String), SolarSysCost(String), SolarEfficiency(String), P3dFilCost(String), P3dWeight(String), P3dTime(String), P3dPrinterW(String), P3dElecRate(String), P3dFailRate(String), P3dSellPrice(String), } // ══════════════════════════════════════════════════════════════════════ // Application // ══════════════════════════════════════════════════════════════════════ pub fn run() -> iced::Result { App::run(Settings::default()) } impl Application for App { type Executor = iced::executor::Default; type Message = Message; type Theme = Theme; type Flags = (); fn new(_: Self::Flags) -> (Self, Command) { ( Self { ohm: OhmState { p_in: String::new(), i_in: String::new(), r_in: String::new(), v_in: String::new(), p_out: "0 Watts(W)".into(), i_out: "0 Amps(A)".into(), r_out: "0 Ohms(\u{03A9})".into(), v_out: "0 Volts(V)".into(), }, margin: MarginState { cost_in: String::new(), sell_in: String::new(), profit_out: "0".into(), margin_pct_out: "0 %".into(), markup_pct_out: "0 %".into(), }, roi: RoiState { investment_in: String::new(), revenue_in: String::new(), profit_out: "0".into(), roi_pct_out: "0 %".into(), }, token: TokenState { model: AiModel::Gpt4o, input_tok_in: String::new(), output_tok_in: String::new(), input_cost_out: "$0.00".into(), output_cost_out: "$0.00".into(), total_cost_out: "$0.00".into(), }, elec: ElecState { power_in: String::new(), hours_in: String::new(), days_in: String::new(), rate_in: String::new(), daily_kwh_out: "0 kWh".into(), monthly_kwh_out: "0 kWh".into(), monthly_cost_out: "$0.00".into(), yearly_cost_out: "$0.00".into(), }, be: BeState { fixed_in: String::new(), price_in: String::new(), variable_in: String::new(), units_out: "0".into(), revenue_out: "$0.00".into(), }, solar: SolarState { panels_in: String::new(), panel_watts_in: String::new(), sun_hours_in: String::new(), rate_in: String::new(), system_cost_in: String::new(), efficiency_in: String::new(), system_kw_out: "0 kW".into(), daily_kwh_out: "0 kWh".into(), monthly_kwh_out: "0 kWh".into(), yearly_kwh_out: "0 kWh".into(), monthly_rev_out: "$0.00".into(), yearly_rev_out: "$0.00".into(), payback_out: "0 years".into(), lifetime_out: "$0.00".into(), }, print3d: Print3dState { fil_cost_in: String::new(), weight_in: String::new(), time_in: String::new(), printer_w_in: String::new(), elec_rate_in: String::new(), fail_rate_in: String::new(), sell_price_in: String::new(), fil_kg_out: "0 kg".into(), mat_cost_out: "$0.00".into(), energy_cost_out: "$0.00".into(), cost_per_out: "$0.00".into(), profit_out: "$0.00".into(), margin_out: "0 %".into(), }, }, Command::none(), ) } fn title(&self) -> String { "MCP-Relational-Data".into() } fn update(&mut self, msg: Self::Message) -> Command { match msg { Message::OhmPower(s) => { self.ohm.p_in = s; self.ohm.recalculate(); } Message::OhmCurrent(s) => { self.ohm.i_in = s; self.ohm.recalculate(); } Message::OhmResistance(s) => { self.ohm.r_in = s; self.ohm.recalculate(); } Message::OhmVoltage(s) => { self.ohm.v_in = s; self.ohm.recalculate(); } Message::MarginCost(s) => { self.margin.cost_in = s; self.margin.recalculate(); } Message::MarginSell(s) => { self.margin.sell_in = s; self.margin.recalculate(); } Message::RoiInvestment(s) => { self.roi.investment_in = s; self.roi.recalculate(); } Message::RoiRevenue(s) => { self.roi.revenue_in = s; self.roi.recalculate(); } Message::TokenPrev => { self.token.model = prev_model(self.token.model); self.token.recalculate(); } Message::TokenNext => { self.token.model = next_model(self.token.model); self.token.recalculate(); } Message::TokenInputTok(s) => { self.token.input_tok_in = s; self.token.recalculate(); } Message::TokenOutputTok(s) => { self.token.output_tok_in = s; self.token.recalculate(); } Message::ElecPower(s) => { self.elec.power_in = s; self.elec.recalculate(); } Message::ElecHours(s) => { self.elec.hours_in = s; self.elec.recalculate(); } Message::ElecDays(s) => { self.elec.days_in = s; self.elec.recalculate(); } Message::ElecRate(s) => { self.elec.rate_in = s; self.elec.recalculate(); } Message::BeFixed(s) => { self.be.fixed_in = s; self.be.recalculate(); } Message::BePrice(s) => { self.be.price_in = s; self.be.recalculate(); } Message::BeVariable(s) => { self.be.variable_in = s; self.be.recalculate(); } Message::SolarPanels(s) => { self.solar.panels_in = s; self.solar.recalculate(); } Message::SolarPanelW(s) => { self.solar.panel_watts_in = s; self.solar.recalculate(); } Message::SolarSunHrs(s) => { self.solar.sun_hours_in = s; self.solar.recalculate(); } Message::SolarRate(s) => { self.solar.rate_in = s; self.solar.recalculate(); } Message::SolarSysCost(s) => { self.solar.system_cost_in = s; self.solar.recalculate(); } Message::SolarEfficiency(s) => { self.solar.efficiency_in = s; self.solar.recalculate(); } Message::P3dFilCost(s) => { self.print3d.fil_cost_in = s; self.print3d.recalculate(); } Message::P3dWeight(s) => { self.print3d.weight_in = s; self.print3d.recalculate(); } Message::P3dTime(s) => { self.print3d.time_in = s; self.print3d.recalculate(); } Message::P3dPrinterW(s) => { self.print3d.printer_w_in = s; self.print3d.recalculate(); } Message::P3dElecRate(s) => { self.print3d.elec_rate_in = s; self.print3d.recalculate(); } Message::P3dFailRate(s) => { self.print3d.fail_rate_in = s; self.print3d.recalculate(); } Message::P3dSellPrice(s) => { self.print3d.sell_price_in = s; self.print3d.recalculate(); } } Command::none() } fn view(&self) -> Element { let left = column![ ohm_section(&self.ohm), elec_section(&self.elec), solar_section(&self.solar), ] .spacing(16); let right = column![ margin_section(&self.margin), roi_section(&self.roi), token_section(&self.token), be_section(&self.be), print3d_section(&self.print3d), ] .spacing(16); let body = row![left, right] .spacing(16) .padding(16); container(scrollable(body).height(Length::Fill)) .width(Length::Fill) .height(Length::Fill) .padding([8, 16]) .into() } } // ══════════════════════════════════════════════════════════════════════ // Section views // ══════════════════════════════════════════════════════════════════════ fn ohm_section<'a>(s: &'a OhmState) -> Element<'a, Message> { container( column![ text("Ohm's Law & Watts Law").size(15), text("Enter any 2 of 4 values to solve for the remaining 2. \ Applies to circuit design, audio impedance matching, \ MOSFET PSU design, and LED driver sizing.") .size(10), q_block("Power (W)", &s.p_in, &s.p_out, Message::OhmPower), q_block("Current (A)", &s.i_in, &s.i_out, Message::OhmCurrent), q_block("Resistance (\u{03A9})", &s.r_in, &s.r_out, Message::OhmResistance), q_block("Voltage (V)", &s.v_in, &s.v_out, Message::OhmVoltage), ] .spacing(5), ) .padding(12) .width(Length::Fill) .into() } fn margin_section<'a>(s: &'a MarginState) -> Element<'a, Message> { container( column![ text("Margin & Markup").size(15), text("Ecommerce pricing: calculate profit, margin %, and markup % \ from cost and selling price.") .size(10), q_block("Cost Price ($)", &s.cost_in, &s.profit_out, Message::MarginCost), q_block("Selling Price ($)", &s.sell_in, &s.margin_pct_out, Message::MarginSell), io_row("Markup", &s.markup_pct_out), ] .spacing(5), ) .padding(12) .width(Length::Fill) .into() } fn roi_section<'a>(s: &'a RoiState) -> Element<'a, Message> { container( column![ text("Return on Investment (ROI)").size(15), text("Measure profitability of ad spend, marketing campaigns, \ and capital expenditures.") .size(10), q_block("Investment ($)", &s.investment_in, &s.profit_out, Message::RoiInvestment), q_block("Revenue ($)", &s.revenue_in, &s.roi_pct_out, Message::RoiRevenue), ] .spacing(5), ) .padding(12) .width(Length::Fill) .into() } fn token_section<'a>(s: &'a TokenState) -> Element<'a, Message> { container( column![ text("AI Token Cost Estimator").size(15), text("Compare per-million-token pricing across 6 major LLMs \ (GPT-4o, Claude 3.5 Sonnet, etc.).") .size(10), row![ button(text("\u{25C0}")).on_press(Message::TokenPrev), text(s.model.label()).size(12), button(text("\u{25B6}")).on_press(Message::TokenNext), ] .spacing(8) .align_items(alignment::Alignment::Center), q_block("Input Tokens", &s.input_tok_in, &s.input_cost_out, Message::TokenInputTok), q_block("Output Tokens", &s.output_tok_in, &s.output_cost_out, Message::TokenOutputTok), io_row("Total Cost", &s.total_cost_out), ] .spacing(5), ) .padding(12) .width(Length::Fill) .into() } fn elec_section<'a>(s: &'a ElecState) -> Element<'a, Message> { container( column![ text("Electricity Cost").size(15), text("Project monthly and yearly power costs for GPU servers, \ datacenter racks, and mining rigs.") .size(10), q_block("Power (W)", &s.power_in, &s.daily_kwh_out, Message::ElecPower), q_block("Hours / Day", &s.hours_in, &s.monthly_kwh_out, Message::ElecHours), q_block("Days / Month", &s.days_in, &s.monthly_cost_out, Message::ElecDays), q_block("Rate ($/kWh)", &s.rate_in, &s.yearly_cost_out, Message::ElecRate), ] .spacing(5), ) .padding(12) .width(Length::Fill) .into() } fn be_section<'a>(s: &'a BeState) -> Element<'a, Message> { container( column![ text("Break-Even Analysis").size(15), text("How many units must you sell to cover all fixed and variable costs?") .size(10), q_block("Fixed Costs ($)", &s.fixed_in, &s.units_out, Message::BeFixed), q_block("Price per Unit ($)", &s.price_in, &s.revenue_out, Message::BePrice), q_block("Variable Cost / Unit ($)", &s.variable_in, "", Message::BeVariable), ] .spacing(5), ) .padding(12) .width(Length::Fill) .into() } fn solar_section<'a>(s: &'a SolarState) -> Element<'a, Message> { container( column![ text("Solar Panel Revenue").size(15), text("Estimate generation, revenue, payback period, and 25-year \ lifetime earnings for a residential or commercial solar array.") .size(10), q_block("# Panels", &s.panels_in, &s.system_kw_out, Message::SolarPanels), q_block("Watts / Panel", &s.panel_watts_in,&s.daily_kwh_out, Message::SolarPanelW), q_block("Sun Hours / Day",&s.sun_hours_in, &s.monthly_kwh_out, Message::SolarSunHrs), q_block("Sell-Back Rate ($/kWh)", &s.rate_in, &s.yearly_kwh_out, Message::SolarRate), q_block("System Cost ($)", &s.system_cost_in, &s.monthly_rev_out, Message::SolarSysCost), q_block("Efficiency (%)", &s.efficiency_in, &s.yearly_rev_out, Message::SolarEfficiency), io_row("Payback Period", &s.payback_out), io_row("25-Year Revenue", &s.lifetime_out), ] .spacing(5), ) .padding(12) .width(Length::Fill) .into() } fn print3d_section<'a>(s: &'a Print3dState) -> Element<'a, Message> { container( column![ text("3D Print Cost Analysis").size(15), text("Calculate per-print cost including filament, electricity, \ and failure rate. See profit and margin when selling prints.") .size(10), q_block("Filament Cost ($/kg)", &s.fil_cost_in, &s.fil_kg_out, Message::P3dFilCost), q_block("Print Weight (g)", &s.weight_in, &s.mat_cost_out, Message::P3dWeight), q_block("Print Time (h)", &s.time_in, &s.energy_cost_out,Message::P3dTime), q_block("Printer Watts", &s.printer_w_in, &s.cost_per_out, Message::P3dPrinterW), q_block("Electricity Rate ($/kWh)", &s.elec_rate_in, &s.profit_out, Message::P3dElecRate), q_block("Failure Rate (%)", &s.fail_rate_in, &s.margin_out, Message::P3dFailRate), q_block("Sell Price ($)", &s.sell_price_in, "", Message::P3dSellPrice), ] .spacing(5), ) .padding(12) .width(Length::Fill) .into() } // ══════════════════════════════════════════════════════════════════════ // Shared UI helpers // ══════════════════════════════════════════════════════════════════════ fn q_block<'a>( label: &'a str, input_val: &'a str, result_val: &'a str, msg: fn(String) -> Message, ) -> Element<'a, Message> { column![ text(label).size(11), row![ text_input("", input_val) .on_input(msg) .width(Length::FillPortion(3)) .padding(4), text(result_val) .width(Length::FillPortion(4)) .vertical_alignment(alignment::Vertical::Center), ] .spacing(6) .align_items(alignment::Alignment::Center), ] .spacing(2) .into() } fn io_row<'a>(label: &'a str, value: &'a str) -> Element<'a, Message> { row![ text(label).size(11).width(Length::FillPortion(3)), text(value) .size(11) .width(Length::FillPortion(4)) .vertical_alignment(alignment::Vertical::Center), ] .spacing(6) .align_items(alignment::Alignment::Center) .into() } // ══════════════════════════════════════════════════════════════════════ // Recalculate impls (delegate to calc.rs) // ══════════════════════════════════════════════════════════════════════ impl OhmState { fn recalculate(&mut self) { let p = parse_f64(&self.p_in, 0.0); let i = parse_f64(&self.i_in, 0.0); let r = parse_f64(&self.r_in, 0.0); let v = parse_f64(&self.v_in, 0.0); // Reject negative power or resistance — physically invalid. if (self.p_in.trim().parse::().is_ok() && p < 0.0) || (self.r_in.trim().parse::().is_ok() && r < 0.0) { self.p_out = "0 Watts(W)".into(); self.i_out = "0 Amps(A)".into(); self.r_out = "0 Ohms(\u{03A9})".into(); self.v_out = "0 Volts(V)".into(); return; } let (p, i, r, v) = ohm_calculate(p, i, r, v); self.p_out = fmt_eng(p, "Watts(W)"); self.i_out = fmt_eng(i, "Amps(A)"); self.r_out = fmt_eng(r, "Ohms(\u{03A9})"); self.v_out = fmt_eng(v, "Volts(V)"); } } impl MarginState { fn recalculate(&mut self) { let cost = parse_f64(&self.cost_in, 0.0); let sell = parse_f64(&self.sell_in, 0.0); let (profit, margin, markup) = margin_calculate(cost, sell); self.profit_out = fmt_num(profit); self.margin_pct_out = fmt_pct(margin); self.markup_pct_out = fmt_pct(markup); } } impl RoiState { fn recalculate(&mut self) { let inv = parse_f64(&self.investment_in, 0.0); let rev = parse_f64(&self.revenue_in, 0.0); let (profit, roi) = roi_calculate(inv, rev); self.profit_out = fmt_num(profit); self.roi_pct_out = fmt_pct(roi); } } impl TokenState { fn recalculate(&mut self) { let in_tok = parse_f64(&self.input_tok_in, 0.0); let out_tok = parse_f64(&self.output_tok_in, 0.0); let (in_cost, out_cost, total) = token_cost_calculate(self.model, in_tok, out_tok); self.input_cost_out = fmt_dollar(in_cost); self.output_cost_out = fmt_dollar(out_cost); self.total_cost_out = fmt_dollar(total); } } impl ElecState { fn recalculate(&mut self) { let watts = parse_f64(&self.power_in, 0.0); let hours = parse_f64(&self.hours_in, 0.0); let days = parse_f64(&self.days_in, 30.0); let rate = parse_f64(&self.rate_in, 0.0); let (daily, monthly_kwh, monthly_cost, yearly) = electricity_calculate(watts, hours, days, rate); self.daily_kwh_out = fmt_eng(daily, "kWh"); self.monthly_kwh_out = fmt_eng(monthly_kwh, "kWh"); self.monthly_cost_out = fmt_dollar(monthly_cost); self.yearly_cost_out = fmt_dollar(yearly); } } impl BeState { fn recalculate(&mut self) { let fixed = parse_f64(&self.fixed_in, 0.0); let price = parse_f64(&self.price_in, 0.0); let variable = parse_f64(&self.variable_in, 0.0); let (units, revenue) = break_even_calculate(fixed, price, variable); self.units_out = fmt_num(units); self.revenue_out = fmt_dollar(revenue); } } impl SolarState { fn recalculate(&mut self) { let panels = parse_f64(&self.panels_in, 0.0); let pw = parse_f64(&self.panel_watts_in, 0.0); let sun = parse_f64(&self.sun_hours_in, 0.0); let rate = parse_f64(&self.rate_in, 0.0); let cost = parse_f64(&self.system_cost_in, 0.0); let eff = parse_f64(&self.efficiency_in, 80.0); let (kw, daily, monthly, yearly, m_rev, y_rev, payback, lifetime) = solar_calculate(panels, pw, sun, rate, cost, eff); self.system_kw_out = fmt_eng(kw, "kW"); self.daily_kwh_out = fmt_eng(daily, "kWh"); self.monthly_kwh_out = fmt_eng(monthly, "kWh"); self.yearly_kwh_out = fmt_eng(yearly, "kWh"); self.monthly_rev_out = fmt_dollar(m_rev); self.yearly_rev_out = fmt_dollar(y_rev); self.payback_out = format!("{} years", trim_f(payback, 5)); self.lifetime_out = fmt_dollar(lifetime); } } impl Print3dState { fn recalculate(&mut self) { let fc = parse_f64(&self.fil_cost_in, 0.0); let wt = parse_f64(&self.weight_in, 0.0); let tm = parse_f64(&self.time_in, 0.0); let pw = parse_f64(&self.printer_w_in, 0.0); let er = parse_f64(&self.elec_rate_in, 0.0); let fr = parse_f64(&self.fail_rate_in, 0.0); let sp = parse_f64(&self.sell_price_in, 0.0); let (fkg, mat, nrg, cpp, profit, margin) = print3d_calculate(fc, wt, tm, pw, er, fr, sp); self.fil_kg_out = format!("{} kg", trim_f(fkg, 5)); self.mat_cost_out = fmt_dollar(mat); self.energy_cost_out = fmt_dollar(nrg); self.cost_per_out = fmt_dollar(cpp); self.profit_out = fmt_dollar(profit); self.margin_out = fmt_pct(margin); } }