Skip to content

Commit 15ed5c7

Browse files
authored
Make all dependencies optional (#100)
* make all dependencies optional * add a script for running test permutations
1 parent d560c65 commit 15ed5c7

File tree

16 files changed

+819
-409
lines changed

16 files changed

+819
-409
lines changed

.travis.yml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@ rust:
88
before_script:
99
- pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH
1010
script:
11-
- cargo build --verbose
12-
- cargo build --verbose --no-default-features
13-
- cargo test --verbose
14-
- cargo test --verbose --no-default-features
11+
- cargo run -p ci
1512
after_success:
1613
- travis-cargo --only nightly doc-upload
1714

Cargo.toml

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,17 @@ keywords = ["logging", "log", "logger"]
1616
[maintenance]
1717
status = "actively-developed"
1818

19+
[workspace]
20+
members = [
21+
"ci"
22+
]
23+
1924
[dependencies]
2025
log = { version = "0.4", features = ["std"] }
2126
regex = { version = "1.0.3", optional = true }
22-
termcolor = "1"
23-
humantime = "1.1"
24-
atty = "0.2.5"
27+
termcolor = { version = "1.0.2", optional = true }
28+
humantime = { version = "1.1", optional = true }
29+
atty = { version = "0.2.5", optional = true }
2530

2631
[[test]]
2732
name = "regexp_filter"
@@ -32,4 +37,4 @@ name = "log-in-log"
3237
harness = false
3338

3439
[features]
35-
default = ["regex"]
40+
default = ["termcolor", "atty", "humantime", "regex"]

ci/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[package]
2+
name = "ci"
3+
version = "0.0.0"
4+
authors = ["The Rust Project Developers"]
5+
publish = false
6+
7+
[dependencies]

ci/src/main.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
mod task;
2+
mod permute;
3+
4+
fn main() {
5+
let features = [
6+
"termcolor",
7+
"humantime",
8+
"atty",
9+
"regex",
10+
];
11+
12+
// Run a default build
13+
if !task::test(Default::default()) {
14+
panic!("default test execution failed");
15+
}
16+
17+
// Run a set of permutations
18+
let failed = permute::all(&features)
19+
.into_iter()
20+
.filter(|features|
21+
!task::test(task::TestArgs {
22+
features: features.clone(),
23+
default_features: false,
24+
lib_only: true,
25+
}))
26+
.collect::<Vec<_>>();
27+
28+
if failed.len() > 0 {
29+
for failed in failed {
30+
eprintln!("FAIL: {:?}", failed);
31+
}
32+
33+
panic!("test execution failed");
34+
} else {
35+
println!("test execution succeeded");
36+
}
37+
}

ci/src/permute.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use std::collections::BTreeSet;
2+
3+
pub fn all<T>(input: &[T]) -> BTreeSet<BTreeSet<T>> where T: Ord + Eq + Clone {
4+
let mut permutations = BTreeSet::new();
5+
6+
if input.len() == 0 {
7+
return permutations;
8+
}
9+
10+
permutations.insert(input.iter().cloned().collect());
11+
12+
if input.len() > 1 {
13+
for t in input {
14+
let mut p = input
15+
.iter()
16+
.filter(|pt| *pt != t)
17+
.cloned()
18+
.collect::<Vec<_>>();
19+
20+
for pt in all(&p) {
21+
permutations.insert(pt);
22+
}
23+
}
24+
}
25+
26+
permutations
27+
}

ci/src/task.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
use std::collections::BTreeSet;
2+
use std::process::{
3+
Command,
4+
Stdio,
5+
};
6+
7+
pub type Feature = &'static str;
8+
9+
pub struct TestArgs {
10+
pub features: BTreeSet<Feature>,
11+
pub default_features: bool,
12+
pub lib_only: bool,
13+
}
14+
15+
impl Default for TestArgs {
16+
fn default() -> Self {
17+
TestArgs {
18+
features: BTreeSet::new(),
19+
default_features: true,
20+
lib_only: false,
21+
}
22+
}
23+
}
24+
25+
impl TestArgs {
26+
fn features_string(&self) -> Option<String> {
27+
if self.features.len() == 0 {
28+
return None;
29+
}
30+
31+
let s = self.features.iter().fold(String::new(), |mut s, f| {
32+
if s.len() > 0 {
33+
s.push_str(" ");
34+
}
35+
s.push_str(f);
36+
37+
s
38+
});
39+
40+
Some(s)
41+
}
42+
}
43+
44+
pub fn test(args: TestArgs) -> bool {
45+
let features = args.features_string();
46+
47+
let mut command = Command::new("cargo");
48+
49+
command
50+
.stdout(Stdio::inherit())
51+
.stderr(Stdio::inherit())
52+
.arg("test")
53+
.arg("--verbose");
54+
55+
if !args.default_features {
56+
command.arg("--no-default-features");
57+
}
58+
59+
if args.lib_only {
60+
command.arg("--lib");
61+
}
62+
63+
if let Some(ref features) = features {
64+
command.args(&["--features", features]);
65+
}
66+
67+
println!("running {:?}", command);
68+
69+
let status = command
70+
.status()
71+
.expect("Failed to execute command");
72+
73+
if !status.success() {
74+
eprintln!("test execution failed for features: {}", features.as_ref().map(AsRef::as_ref).unwrap_or(""));
75+
false
76+
} else {
77+
true
78+
}
79+
}

examples/custom_format.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ fn init_logger() {
3333
let mut builder = Builder::from_env(env);
3434

3535
// Use a different format for writing log records
36+
// The colors are only available when the `termcolor` dependency is (which it is by default)
37+
#[cfg(feature = "termcolor")]
3638
builder.format(|buf, record| {
3739
let mut style = buf.style();
3840
style.set_bg(fmt::Color::Yellow).set_bold(true);

src/fmt/atty.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
This internal module contains the terminal detection implementation.
3+
4+
If the `atty` crate is available then we use it to detect whether we're
5+
attached to a particular TTY. If the `atty` crate is not available we
6+
assume we're not attached to anything. This effectively prevents styles
7+
from being printed.
8+
*/
9+
10+
#[cfg(feature = "atty")]
11+
mod imp {
12+
use atty;
13+
14+
pub(in ::fmt) fn is_stdout() -> bool {
15+
atty::is(atty::Stream::Stdout)
16+
}
17+
18+
pub(in ::fmt) fn is_stderr() -> bool {
19+
atty::is(atty::Stream::Stderr)
20+
}
21+
}
22+
23+
#[cfg(not(feature = "atty"))]
24+
mod imp {
25+
pub(in ::fmt) fn is_stdout() -> bool {
26+
false
27+
}
28+
29+
pub(in ::fmt) fn is_stderr() -> bool {
30+
false
31+
}
32+
}
33+
34+
pub(in ::fmt) use self::imp::*;

src/fmt/humantime/extern_impl.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
use std::fmt;
2+
use std::time::SystemTime;
3+
4+
use humantime::{format_rfc3339_nanos, format_rfc3339_seconds};
5+
6+
use ::fmt::Formatter;
7+
8+
pub(in ::fmt) mod pub_use_in_super {
9+
pub use super::*;
10+
}
11+
12+
impl Formatter {
13+
/// Get a [`Timestamp`] for the current date and time in UTC.
14+
///
15+
/// # Examples
16+
///
17+
/// Include the current timestamp with the log record:
18+
///
19+
/// ```
20+
/// use std::io::Write;
21+
///
22+
/// let mut builder = env_logger::Builder::new();
23+
///
24+
/// builder.format(|buf, record| {
25+
/// let ts = buf.timestamp();
26+
///
27+
/// writeln!(buf, "{}: {}: {}", ts, record.level(), record.args())
28+
/// });
29+
/// ```
30+
///
31+
/// [`Timestamp`]: struct.Timestamp.html
32+
pub fn timestamp(&self) -> Timestamp {
33+
Timestamp(SystemTime::now())
34+
}
35+
36+
/// Get a [`PreciseTimestamp`] for the current date and time in UTC with nanos.
37+
pub fn precise_timestamp(&self) -> PreciseTimestamp {
38+
PreciseTimestamp(SystemTime::now())
39+
}
40+
}
41+
42+
/// An [RFC3339] formatted timestamp.
43+
///
44+
/// The timestamp implements [`Display`] and can be written to a [`Formatter`].
45+
///
46+
/// [RFC3339]: https://www.ietf.org/rfc/rfc3339.txt
47+
/// [`Display`]: https://doc.rust-lang.org/stable/std/fmt/trait.Display.html
48+
/// [`Formatter`]: struct.Formatter.html
49+
pub struct Timestamp(SystemTime);
50+
51+
/// An [RFC3339] formatted timestamp with nanos.
52+
///
53+
/// [RFC3339]: https://www.ietf.org/rfc/rfc3339.txt
54+
#[derive(Debug)]
55+
pub struct PreciseTimestamp(SystemTime);
56+
57+
impl fmt::Debug for Timestamp {
58+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59+
/// A `Debug` wrapper for `Timestamp` that uses the `Display` implementation.
60+
struct TimestampValue<'a>(&'a Timestamp);
61+
62+
impl<'a> fmt::Debug for TimestampValue<'a> {
63+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64+
fmt::Display::fmt(&self.0, f)
65+
}
66+
}
67+
68+
f.debug_tuple("Timestamp")
69+
.field(&TimestampValue(&self))
70+
.finish()
71+
}
72+
}
73+
74+
impl fmt::Display for Timestamp {
75+
fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result {
76+
format_rfc3339_seconds(self.0).fmt(f)
77+
}
78+
}
79+
80+
impl fmt::Display for PreciseTimestamp {
81+
fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result {
82+
format_rfc3339_nanos(self.0).fmt(f)
83+
}
84+
}

src/fmt/humantime/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
This internal module contains the timestamp implementation.
3+
4+
Its public API is available when the `humantime` crate is available.
5+
*/
6+
7+
#[cfg_attr(feature = "humantime", path = "extern_impl.rs")]
8+
#[cfg_attr(not(feature = "humantime"), path = "shim_impl.rs")]
9+
mod imp;
10+
11+
pub(in ::fmt) use self::imp::*;

0 commit comments

Comments
 (0)