//! _qsu_ is a set of tools for integrating a server application against a
//! service subsystem (such as
//! [Windows Services](https://learn.microsoft.com/en-us/windows/win32/services/services), [systemd](https://systemd.io/), or
//! [launchd](https://www.launchd.info/)).
//!
//! It offers a thin runtime wrapper layer with the purpose of abstracting away
//! differences between service subsystems (and also provides the same
//! interface when running the server application as a foreground process).
//! More information about the wrapper runtime can be found in the [rt] module
//! documentation.
//!
//! It also offers helper functions for registering/deregistering a service
//! application with the system's service subsystem.  These are documented in
//! the [installer] module.
//!
//! And it comes with an optional argument parser that can be used to run the
//! service application as a regular program or run it in the platform's
//! service subsystem, as well as provides subcommands for service subsystem
//! integration (creation of systemd unit files, or launchd plist files, etc)
//! See the [argp] module for more information.
//!
//! # Features
//! | Feature     | Function
//! |-------------|----------
//! | `clap`      | Enable `clap` (argument parser) integration.
//! | `installer` | Tools for registering/deregistering services.
//! | `rt`        | Service wrapper (enabled by default).
//! | `systemd`   | systemd integration support.
//! | `tokio`     | Tokio server application type support.
//! | `rocket`    | Rocket server application type support.
//!
//! In addition there's a special `wait-for-debugger` feature that is only used
//! on Windows.  It will make the service runtime halt and wait for a debugger
//! to attach just before starting the Windows Service runtime.  Once a
//! debugger has attached, it will voluntarily trigger a breakpoint.
//!
//! # Service design tips
//!
//! The way services API's are desgined can give an false impression about what
//! "best service application practices" are.  This section is meant to steer
//! the reader away from these misconceptions.
//!
//! ## Do not over-rely on the service subsystem's dependency support
//!
//! It may seem obvious to use the service dependency rules to set up the
//! necessary preconditions for your service, and then holding them to their
//! implied promise when wriing your own service application.  However, this is
//! generally not a good idea.  There _are_ some dependency rules that are
//! meant as "checkpoints", which are fine to use, but if your application
//! needs something like a port mapper, then you can't rely on the port mapper
//! actually being fully available if your service is marked to depend on it.
//!
//! Instead, design your service to "ping" (with whatever means are available)
//! the services you need.  It is the only reliable way to know if a dependency
//! is available or not.
//!
//! ## Don't try too hard to retry
//!
//! If your service critically depends on the availability of another service,
//! then it may be tempting to have your service initialization enter an
//! infinite loop which waits for the depedency to become available.  This is a
//! bad idea.  Service subsystems may have a grace period in which the service
//! application must complete its initialization, and your service must respect
//! that.
//!
//! Your service can retry, and probably should, a few times, but if it fails
//! it should terminate with an error, and let the service subsystem restart
//! the service as appropriate.  As strange as it may seem, outsource the
//! fault tolerance to the service subsystem rather than try to implement it
//! yourself in your service application.  It's what the service subsystems
//! were designed for.

#![cfg_attr(docsrs, feature(doc_cfg))]

mod err;
pub mod lumberjack;

#[cfg(feature = "rt")]
#[cfg_attr(docsrs, doc(cfg(feature = "rt")))]
pub mod rt;

#[cfg(feature = "clap")]
#[cfg_attr(docsrs, doc(cfg(feature = "clap")))]
pub mod argp;

#[cfg(feature = "installer")]
#[cfg_attr(docsrs, doc(cfg(feature = "installer")))]
pub mod installer;

use std::{ffi::OsStr, path::Path};

pub use async_trait::async_trait;

pub use lumberjack::{LumberJack, set_default_log_filters};

pub use err::Error;

#[cfg(any(feature = "rt", feature = "installer"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "rt", feature = "installer"))))]
pub use err::CbErr;

#[cfg(feature = "tokio")]
pub use tokio;

#[cfg(feature = "rocket")]
pub use rocket;

pub use log;
pub use tracing;


#[cfg(feature = "clap")]
#[cfg_attr(docsrs, doc(cfg(feature = "clap")))]
pub use clap;


/// Attempt to derive a default service name based on the executable's name.
///
/// The idea is to get the current executable's file name and strip it's
/// extension (if there is one).  The file stem name is the default service
/// name.  On macos the name will be prefixed by `local.`.
#[must_use]
pub fn default_service_name() -> Option<String> {
  let binary_path = ::std::env::current_exe().ok()?;

  let name = binary_path.file_name()?;
  let name = Path::new(name);
  let name = name.file_stem()?;

  mkname(name)
}

#[cfg(not(target_os = "macos"))]
fn mkname(nm: &OsStr) -> Option<String> {
  nm.to_str().map(String::from)
}

#[cfg(target_os = "macos")]
fn mkname(nm: &OsStr) -> Option<String> {
  nm.to_str().map(|x| format!("local.{x}"))
}

// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :
