Weather Man ships two front-ends from a single Rust workspace, backed by a shared, framework-free core that doubles as a drop-in weather API provider library.
| Crate | Description |
|---|---|
weatherman |
Desktop GUI (Iced) — search a city, current conditions, hourly + 7-day forecast |
weatherman-tui |
Terminal UI (Ratatui) — single scrollable page, keyboard-driven, great over SSH |
weatherman-core |
Shared core — domain models, WeatherProvider trait, Open-Meteo backend, geocoding |
Weather data comes from Open-Meteo (no API key required), with geocoding via Nominatim/OpenStreetMap.
The shared core fetches and formats a full forecast with no UI framework:
Imperial units (°F, mph):
Demo GIFs are stored with Git LFS. Run
git lfs installafter cloning to fetch them, or regenerate everything withjust vhs-all.
Run weatherman to launch the Iced desktop app — a city search, a current-
conditions card, a horizontal hourly strip, and a 7-day list with a °C/°F toggle.
# Desktop GUI
cargo install weatherman
# Terminal UI
cargo install weatherman-tuiweatherman- Type a city and press Enter to search; toggle °C/°F with the units button.
- Click any day in the 7-day forecast to expand full details (day-part temperatures, feels-like, sunrise/sunset, wind, UV index, precipitation).
-
Save locations to the sidebar with “+ Save current”, then click to switch
between them. Saved locations and your unit preference persist across sessions
(
<config-dir>/weatherman/settings.json).
weatherman-tui # auto-detected location
weatherman-tui --location "New York" # specific city
weatherman-tui --units imperial # imperial units
weatherman-tui --json # JSON output, no TUIKeys: ↑/↓ or j/k scroll · PgUp/PgDn page · g/G top/bottom · q/Esc quit.
weatherman-core has no GUI/TUI dependencies. The high-level [load_report]
resolves a location and fetches its forecast, and [ForecastView] turns the raw
data into ready-to-render, UI-agnostic strings + condition tones:
use weatherman_core::{load_report, ForecastView, WeatherConfig};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let config = WeatherConfig::default();
let report = load_report(&config, Some("Berlin")).await?;
let view = ForecastView::build(
report.current.as_ref(),
&report.hourly,
&report.daily,
&report.location,
&config,
);
if let Some(c) = &view.current {
println!("{} {} {}", c.emoji, c.condition, c.temperature);
}
Ok(())
}You can also plug in a different backend by implementing the WeatherProvider
trait — see the runnable examples below.
# Fetch and pretty-print a forecast using only the core library (network)
cargo run -p weatherman-core --example report -- Berlin
just example-report Berlin
# Offline demo: a custom WeatherProvider backed by canned data (no network)
cargo run -p weatherman-core --example custom_provider
just example-providerThis repo uses just as a task runner and
nushell for release scripts.
just # list all tasks
just build # build the workspace
just run-gui # launch the GUI
just run-tui # launch the TUI
just test # run all Rust tests
just test-nu # run the Nushell script tests
just test-all-nu # run both Rust and Nushell tests
just check-all # fmt + clippy + test + nu
just vhs-all # regenerate demo GIFs (needs vhs)just release-preview # show unreleased commits
just release 0.3.1 # bump, changelog, commit, tag, push → triggers Release workflowChangelogs are generated with git-cliff from Conventional Commits.
MIT — see LICENSE.
- Weather data by Open-Meteo
- Geocoding by Nominatim/OpenStreetMap



