#![deny(missing_docs)]
use std::error::Error;
use std::fmt::{self, Display};
#[macro_use]
mod macros;
mod build;
pub mod date;
pub mod version;
pub use crate::date::Date;
pub use crate::version::{Channel, RustVersion, StableVersionSpec};
pub fn detect_version() -> Result<crate::RustVersion, VersionDetectionError> {
{
let lock = state::state_mutex()
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner);
match &*lock {
Some(cached) => return Ok(*cached),
_ => {
}
}
}
match build::determine_version() {
Ok(success) => {
{
let mut lock = state::state_mutex()
.write()
.unwrap_or_else(std::sync::PoisonError::into_inner);
*lock = Some(success);
}
Ok(success)
}
Err(failure) => Err(failure),
}
}
#[derive(Debug)]
pub struct VersionDetectionError {
desc: String,
cause: Option<std::io::Error>,
}
impl VersionDetectionError {
pub(crate) fn new(desc: String) -> Self {
VersionDetectionError { desc, cause: None }
}
pub(crate) fn with_cause(desc: String, cause: std::io::Error) -> Self {
VersionDetectionError {
desc,
cause: Some(cause),
}
}
}
impl Display for VersionDetectionError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.desc)?;
if let Some(ref cause) = self.cause {
write!(f, ": {}", cause)?;
}
Ok(())
}
}
impl Error for VersionDetectionError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.cause.as_ref().map(|x| x as _)
}
}
#[allow(unused_imports)]
mod state {
use std::sync::{Once, RwLock};
#[allow(deprecated)] static CACHED_STATE_INIT: Once = std::sync::ONCE_INIT;
static mut CACHED_STATE: Option<RwLock<Option<crate::RustVersion>>> = None;
pub fn state_mutex() -> &'static RwLock<Option<crate::RustVersion>> {
CACHED_STATE_INIT.call_once(|| {
unsafe {
CACHED_STATE = Some(RwLock::new(None));
}
});
unsafe {
match CACHED_STATE {
Some(ref mutex) => mutex,
None => std::hint::unreachable_unchecked(),
}
}
}
}