gload 0.5.1

A command line client for the Gemini protocol.
Documentation
//! This software is licensed as described in the file LICENSE, which
//! you should have received as part of this distribution.
//!
//! You may opt to use, copy, modify, merge, publish, distribute and/or sell
//! copies of the Software, and permit persons to whom the Software is
//! furnished to do so, under the terms of the LICENSE file.
//!
//! This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
//! KIND, either express or implied.
//!
//! SPDX-License-Identifier: BSD-3-Clause

#![cfg(feature = "logging")]

use clap::crate_name;

struct StdLogger {
	verbose: bool,
}

impl log::Log for StdLogger {
	fn enabled(&self, metadata: &log::Metadata) -> bool {
		// Skip messages that aren't ours or rustls
		if !metadata.target().starts_with(crate_name!()) && !metadata.target().starts_with("rustls")
		{
			return false;
		}

		if self.verbose {
			true // all the logs!
		} else {
			metadata.level() <= log::Level::Debug // debug and below
		}
	}

	fn flush(&self) {
		// nothing to flush
	}

	fn log(&self, record: &log::Record) {
		use log::Level;

		if self.enabled(record.metadata()) {
			let args = record.args();

			// Capture rustls logs
			if record.target().starts_with("rustls") {
				let target = record.target();

				// The only log here is "Sending warning alert CloseNotify" as of rustls@0.23.28
				// so we skip it in favor of our own log.
				if target == "rustls::common_state" {
					return;
				}

				// Prettify rustls log targets
				match target {
					"rustls::client::tls12" | "rustls::client::tls13" => {
						eprintln!("* [TLS] {args}")
					}
					"rustls::client::hs" => eprintln!("* [Handshake] {args}"),
					"rustls::client::common" => eprintln!("* [Client] {args}"),
					_ => eprintln!("* [{target}] {args}"),
				}
				return;
			}

			// Send log to stderr or stdout, depending on level
			match record.level() {
				Level::Trace | Level::Debug | Level::Warn | Level::Error => eprintln!("{args}"),
				Level::Info => println!("{args}"),
			}
		}
	}
}

static LOGGER: StdLogger = StdLogger { verbose: false };
static VERBOSE_LOGGER: StdLogger = StdLogger { verbose: true };

/// Prepares the logger. Should only ever be run once.
pub(crate) fn init(verbose: bool) {
	let (logger, level) = if verbose {
		(&LOGGER, log::LevelFilter::Trace)
	} else {
		(&VERBOSE_LOGGER, log::LevelFilter::Info)
	};
	log::set_logger(logger).unwrap_or_else(|err| panic!("failed to initialise the logger: {err}"));
	log::set_max_level(level);
}