#![deny(missing_docs)]
use std;
use std::fmt;
use std::fs::File;
use std::io::prelude::*;
use std::marker::PhantomData;
use std::path::Path;
use std::str::FromStr;
use std::time::SystemTime;
use semver::{Version, VersionReq};
use serde::de::{Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
pub use owo_colors::{OwoColorize, Stream, Style};
use crate::error::*;
#[derive(Debug)]
pub enum Void {}
pub fn string_list<I, T>(mut iter: I, sep: &str, con: &str) -> Option<String>
where
I: Iterator<Item = T>,
T: AsRef<str>,
{
let mut buffer = match iter.next() {
Some(i) => String::from(i.as_ref()),
None => return None,
};
let mut last = match iter.next() {
Some(i) => i,
None => return Some(buffer),
};
let mut had_separator = false;
for i in iter {
buffer.push_str(sep);
buffer.push(' ');
buffer.push_str(last.as_ref());
last = i;
had_separator = true;
}
if had_separator {
buffer.push_str(sep);
}
buffer.push(' ');
buffer.push_str(con);
buffer.push(' ');
buffer.push_str(last.as_ref());
Some(buffer)
}
#[derive(Debug)]
pub struct StringOrStruct<T>(pub T);
impl<T> Serialize for StringOrStruct<T>
where
T: Serialize,
{
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.serialize(serializer)
}
}
impl<'de, T> Deserialize<'de> for StringOrStruct<T>
where
T: Deserialize<'de> + FromStr<Err = Void>,
{
fn deserialize<D>(deserializer: D) -> std::result::Result<StringOrStruct<T>, D::Error>
where
D: Deserializer<'de>,
{
use serde::de;
struct Visitor<T>(PhantomData<T>);
impl<'de, T> de::Visitor<'de> for Visitor<T>
where
T: Deserialize<'de> + FromStr<Err = Void>,
{
type Value = T;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("string or map")
}
fn visit_str<E>(self, value: &str) -> std::result::Result<T, E>
where
E: de::Error,
{
Ok(T::from_str(value).unwrap())
}
fn visit_map<M>(self, visitor: M) -> std::result::Result<T, M::Error>
where
M: de::MapAccess<'de>,
{
T::deserialize(de::value::MapAccessDeserializer::new(visitor))
}
}
deserializer
.deserialize_any(Visitor::<T>(PhantomData))
.map(|v| StringOrStruct(v))
}
}
#[derive(Debug)]
pub struct SeqOrStruct<T, F>(pub T, PhantomData<F>);
impl<T, F> SeqOrStruct<T, F> {
pub fn new(item: T) -> Self {
SeqOrStruct(item, PhantomData)
}
}
impl<T, F> Serialize for SeqOrStruct<T, F>
where
T: Serialize,
{
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.serialize(serializer)
}
}
impl<'de, T, F> Deserialize<'de> for SeqOrStruct<T, F>
where
T: Deserialize<'de> + From<Vec<F>>,
F: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> std::result::Result<SeqOrStruct<T, F>, D::Error>
where
D: Deserializer<'de>,
{
use serde::de;
struct Visitor<T, F>(PhantomData<T>, PhantomData<F>);
impl<'de, T, F> de::Visitor<'de> for Visitor<T, F>
where
T: Deserialize<'de> + From<Vec<F>>,
F: Deserialize<'de>,
{
type Value = T;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("sequence or map")
}
fn visit_seq<A>(self, visitor: A) -> std::result::Result<T, A::Error>
where
A: de::SeqAccess<'de>,
{
let v: Vec<F> = Vec::deserialize(de::value::SeqAccessDeserializer::new(visitor))?;
Ok(T::from(v))
}
fn visit_map<M>(self, visitor: M) -> std::result::Result<T, M::Error>
where
M: de::MapAccess<'de>,
{
T::deserialize(de::value::MapAccessDeserializer::new(visitor))
}
}
deserializer
.deserialize_any(Visitor::<T, F>(PhantomData, PhantomData))
.map(|v| SeqOrStruct(v, PhantomData))
}
}
pub fn read_file(path: &Path) -> std::io::Result<String> {
let mut file = File::open(path)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(contents)
}
pub fn write_file(path: &Path, contents: &str) -> std::io::Result<()> {
let mut file = File::create(path)?;
file.write_all(contents.as_bytes())?;
Ok(())
}
pub fn try_modification_time<P: AsRef<Path>>(path: P) -> Option<SystemTime> {
use std::fs::metadata;
let md = match metadata(path) {
Ok(md) => md,
Err(_) => return None,
};
md.modified().ok()
}
pub fn version_req_top_bound(req: &VersionReq) -> Result<Option<Version>> {
let mut top_bound = Version::new(u64::MAX, u64::MAX, u64::MAX);
let mut found = false; for comp in req.comparators.iter() {
match comp.op {
semver::Op::Exact | semver::Op::LessEq => {
let max_exact = Version {
major: if comp.minor.is_some() {
comp.major
} else {
comp.major + 1
},
minor: if let Some(minor) = comp.minor {
if comp.patch.is_some() {
minor
} else {
minor + 1
}
} else {
0
},
patch: if let Some(patch) = comp.patch {
patch + 1
} else {
0
},
pre: semver::Prerelease::EMPTY,
build: semver::BuildMetadata::EMPTY,
};
if top_bound > max_exact {
found = true;
top_bound = max_exact;
}
}
semver::Op::Greater | semver::Op::GreaterEq => {} semver::Op::Less => {
let max_less = Version {
major: comp.major,
minor: comp.minor.unwrap_or(0),
patch: comp.patch.unwrap_or(0),
pre: semver::Prerelease::EMPTY,
build: semver::BuildMetadata::EMPTY,
};
if top_bound > max_less {
found = true;
top_bound = max_less;
}
}
semver::Op::Tilde => {
let max_tilde = Version {
major: if comp.minor.is_some() {
comp.major
} else {
comp.major + 1
},
minor: if let Some(minor) = comp.minor {
minor + 1
} else {
0
},
patch: 0,
pre: semver::Prerelease::EMPTY,
build: semver::BuildMetadata::EMPTY,
};
if top_bound > max_tilde {
found = true;
top_bound = max_tilde;
}
}
semver::Op::Caret => {
let max_caret = match (comp.minor, comp.patch) {
(_, _) if comp.major > 0 => Version {
major: comp.major + 1,
minor: 0,
patch: 0,
pre: semver::Prerelease::EMPTY,
build: semver::BuildMetadata::EMPTY,
},
(Some(minor), _) if minor > 0 => Version {
major: comp.major,
minor: minor + 1,
patch: 0,
pre: semver::Prerelease::EMPTY,
build: semver::BuildMetadata::EMPTY,
},
(Some(minor), Some(patch)) => Version {
major: comp.major,
minor,
patch: patch + 1,
pre: semver::Prerelease::EMPTY,
build: semver::BuildMetadata::EMPTY,
},
_ => {
return Err(Error::new(format!(
"Cannot extract top bound from version requirement: {}",
req
)));
}
};
if top_bound > max_caret {
found = true;
top_bound = max_caret;
}
}
semver::Op::Wildcard => {
let max_wildcard = Version {
major: if comp.minor.is_some() {
comp.major
} else {
comp.major + 1
},
minor: if let Some(minor) = comp.minor {
minor + 1
} else {
0
},
patch: 0,
pre: semver::Prerelease::EMPTY,
build: semver::BuildMetadata::EMPTY,
};
if top_bound > max_wildcard {
found = true;
top_bound = max_wildcard;
}
}
_ => {
return Err(Error::new(format!(
"Cannot extract top bound from version requirement: {}",
req
)));
}
}
}
if found { Ok(Some(top_bound)) } else { Ok(None) }
}
pub fn version_req_bottom_bound(req: &VersionReq) -> Result<Option<Version>> {
let mut bottom_bound = Version::new(0, 0, 0);
let mut found = false;
for comp in req.comparators.iter() {
match comp.op {
semver::Op::Exact
| semver::Op::GreaterEq
| semver::Op::Tilde
| semver::Op::Caret
| semver::Op::Wildcard => {
let min_exact = Version {
major: comp.major,
minor: comp.minor.unwrap_or(0),
patch: comp.patch.unwrap_or(0),
pre: comp.pre.clone(),
build: semver::BuildMetadata::EMPTY,
};
if bottom_bound < min_exact {
found = true;
bottom_bound = min_exact;
}
}
semver::Op::Greater => {
let min_greater = Version {
major: if comp.minor.is_some() {
comp.major
} else {
comp.major + 1
},
minor: if let Some(minor) = comp.minor {
if comp.patch.is_some() {
minor + 1
} else {
minor
}
} else {
0
},
patch: if let Some(patch) = comp.patch {
patch + 1
} else {
0
},
pre: comp.pre.clone(),
build: semver::BuildMetadata::EMPTY,
};
if bottom_bound < min_greater {
found = true;
bottom_bound = min_greater;
}
}
semver::Op::Less | semver::Op::LessEq => {
}
_ => {
return Err(Error::new(format!(
"Cannot extract bottom bound from version requirement: {}",
req
)));
}
}
}
if found {
Ok(Some(bottom_bound))
} else {
Ok(None)
}
}
pub fn fmt_duration(duration: std::time::Duration) -> String {
match duration.as_millis() {
t if t < 1000 => format!("in {}ms", t),
t if t < 60_000 => format!("in {:.1}s", t as f64 / 1000.0),
t => format!("in {:.1}min", t as f64 / 60000.0),
}
}
#[macro_export]
macro_rules! fmt_with_style {
($item:expr, $style:expr) => {
$crate::util::OwoColorize::if_supports_color(&$item, $crate::util::Stream::Stderr, |t| {
$crate::util::OwoColorize::style(t, $style)
})
};
}
#[macro_export]
macro_rules! fmt_pkg {
($pkg:expr) => {
$crate::fmt_with_style!($pkg, $crate::util::Style::new().bold())
};
}
#[macro_export]
macro_rules! fmt_path {
($pkg:expr) => {
$crate::fmt_with_style!($pkg, $crate::util::Style::new().underline())
};
}
#[macro_export]
macro_rules! fmt_field {
($field:expr) => {
$crate::fmt_with_style!($field, $crate::util::Style::new().italic())
};
}
#[macro_export]
macro_rules! fmt_version {
($ver:expr) => {
$crate::fmt_with_style!($ver, $crate::util::Style::new().cyan())
};
}
#[macro_export]
macro_rules! fmt_stage {
($stage:expr) => {
$crate::fmt_with_style!($stage, $crate::util::Style::new().cyan().bold())
};
}
#[macro_export]
macro_rules! fmt_completed {
($stage:expr) => {
$crate::fmt_with_style!($stage, $crate::util::Style::new().green().bold())
};
}
#[macro_export]
macro_rules! fmt_dim {
($msg:expr) => {
$crate::fmt_with_style!($msg, $crate::util::Style::new().dimmed())
};
}