зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset a8c087a36965 (bug 1681811) for failures on test_case_mp4. CLOSED TREE
This commit is contained in:
Родитель
8c5648c6f2
Коммит
24146fd642
|
@ -15,7 +15,7 @@ tag = "v0.4.16"
|
|||
[source."https://github.com/mozilla/mp4parse-rust"]
|
||||
git = "https://github.com/mozilla/mp4parse-rust"
|
||||
replace-with = "vendored-sources"
|
||||
rev = "94fd2f16b1a569a35801c4e7d858c6e1b24020ff"
|
||||
rev = "f7c35a30ff25521bebe64c19d3f306569ecb5385"
|
||||
|
||||
[source."https://github.com/mozilla/application-services"]
|
||||
git = "https://github.com/mozilla/application-services"
|
||||
|
|
|
@ -217,7 +217,7 @@ dependencies = [
|
|||
"bindgen",
|
||||
"cranelift-codegen",
|
||||
"cranelift-wasm",
|
||||
"env_logger 0.8.2",
|
||||
"env_logger",
|
||||
"log",
|
||||
"smallvec",
|
||||
]
|
||||
|
@ -1276,19 +1276,6 @@ dependencies = [
|
|||
"packed_simd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime 1.3.0",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.8.2"
|
||||
|
@ -1296,7 +1283,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime 2.0.1",
|
||||
"humantime",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
|
@ -1373,9 +1360,9 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
|||
|
||||
[[package]]
|
||||
name = "fallible_collections"
|
||||
version = "0.3.1"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9599e8ccc571becb62700174680e54e5c50fc5b4d34c1c56d8915e0325650fea"
|
||||
checksum = "3bda4d04bca84e2331f0ff2ee8300064df3f467e37743d87788c1487a6dd903b"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
@ -1734,7 +1721,7 @@ name = "gecko_logger"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"app_services_logger",
|
||||
"env_logger 0.8.2",
|
||||
"env_logger",
|
||||
"lazy_static",
|
||||
"log",
|
||||
]
|
||||
|
@ -2328,15 +2315,6 @@ version = "1.3.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
|
||||
dependencies = [
|
||||
"quick-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.0.1"
|
||||
|
@ -2522,7 +2500,7 @@ version = "0.1.4"
|
|||
dependencies = [
|
||||
"bindgen",
|
||||
"cmake",
|
||||
"env_logger 0.8.2",
|
||||
"env_logger",
|
||||
"glob",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
|
@ -3257,11 +3235,10 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "mp4parse"
|
||||
version = "0.11.4"
|
||||
source = "git+https://github.com/mozilla/mp4parse-rust?rev=94fd2f16b1a569a35801c4e7d858c6e1b24020ff#94fd2f16b1a569a35801c4e7d858c6e1b24020ff"
|
||||
source = "git+https://github.com/mozilla/mp4parse-rust?rev=f7c35a30ff25521bebe64c19d3f306569ecb5385#f7c35a30ff25521bebe64c19d3f306569ecb5385"
|
||||
dependencies = [
|
||||
"bitreader",
|
||||
"byteorder",
|
||||
"env_logger 0.7.1",
|
||||
"fallible_collections",
|
||||
"hashbrown",
|
||||
"log",
|
||||
|
@ -3276,7 +3253,7 @@ version = "0.1.0"
|
|||
[[package]]
|
||||
name = "mp4parse_capi"
|
||||
version = "0.11.4"
|
||||
source = "git+https://github.com/mozilla/mp4parse-rust?rev=94fd2f16b1a569a35801c4e7d858c6e1b24020ff#94fd2f16b1a569a35801c4e7d858c6e1b24020ff"
|
||||
source = "git+https://github.com/mozilla/mp4parse-rust?rev=f7c35a30ff25521bebe64c19d3f306569ecb5385#f7c35a30ff25521bebe64c19d3f306569ecb5385"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"fallible_collections",
|
||||
|
@ -3321,7 +3298,7 @@ version = "0.4.16"
|
|||
source = "git+https://github.com/mozilla/neqo?tag=v0.4.16#1b0664a1e2b3ac59a5c919d32ddc284dc57634e6"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"env_logger 0.8.2",
|
||||
"env_logger",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"qlog",
|
||||
|
@ -3637,7 +3614,7 @@ version = "0.1.4"
|
|||
dependencies = [
|
||||
"byteorder",
|
||||
"core-foundation",
|
||||
"env_logger 0.8.2",
|
||||
"env_logger",
|
||||
"lazy_static",
|
||||
"libloading 0.5.2",
|
||||
"log",
|
||||
|
@ -3885,7 +3862,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "50ce7c785e06e3a9e6f546c1a30d3d59111a31a21bc294fb1496241a572c9a00"
|
||||
dependencies = [
|
||||
"base64 0.10.1",
|
||||
"humantime 2.0.1",
|
||||
"humantime",
|
||||
"indexmap",
|
||||
"line-wrap",
|
||||
"serde",
|
||||
|
@ -4719,7 +4696,7 @@ name = "smoosh"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"env_logger 0.8.2",
|
||||
"env_logger",
|
||||
"jsparagus",
|
||||
"log",
|
||||
]
|
||||
|
@ -4919,7 +4896,7 @@ dependencies = [
|
|||
"atomic_refcell",
|
||||
"cssparser",
|
||||
"cstr",
|
||||
"env_logger 0.8.2",
|
||||
"env_logger",
|
||||
"geckoservo",
|
||||
"libc",
|
||||
"log",
|
||||
|
|
|
@ -507,24 +507,23 @@ nsAVIFDecoder::DecodeResult nsAVIFDecoder::Decode(
|
|||
return AsVariant(NonDecoderResult::ParseError);
|
||||
}
|
||||
|
||||
AvifImage image = {};
|
||||
Mp4parseStatus status = mp4parse_avif_get_image(mParser, &image);
|
||||
Mp4parseByteData primaryItem = {};
|
||||
Mp4parseStatus status = mp4parse_avif_get_primary_item(mParser, &primaryItem);
|
||||
|
||||
MOZ_LOG(sAVIFLog, LogLevel::Debug,
|
||||
("[this=%p] mp4parse_avif_get_primary_item -> %d; length: %u", this,
|
||||
status, image.primary_item.length));
|
||||
status, primaryItem.length));
|
||||
|
||||
if (status != MP4PARSE_STATUS_OK || !image.primary_item.data ||
|
||||
!image.primary_item.length) {
|
||||
if (status != MP4PARSE_STATUS_OK) {
|
||||
return AsVariant(NonDecoderResult::NoPrimaryItem);
|
||||
}
|
||||
|
||||
layers::PlanarYCbCrData decodedData;
|
||||
DecodeResult decodeResult = AsVariant(NonDecoderResult::MetadataOk);
|
||||
if (StaticPrefs::image_avif_use_dav1d()) {
|
||||
decodeResult = AsVariant(DecodeWithDav1d(image.primary_item, decodedData));
|
||||
decodeResult = AsVariant(DecodeWithDav1d(primaryItem, decodedData));
|
||||
} else {
|
||||
decodeResult = AsVariant(DecodeWithAOM(image.primary_item, decodedData));
|
||||
decodeResult = AsVariant(DecodeWithAOM(primaryItem, decodedData));
|
||||
}
|
||||
bool decodeOK = IsDecodeSuccess(decodeResult);
|
||||
MOZ_LOG(sAVIFLog, LogLevel::Debug,
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{"files":{"CHANGELOG.md":"7c044d74477515ab39287a4caff27eb96daebaed8b9f9b6a1d1c081a7b42d4a7","Cargo.lock":"b1394b6c58241027832cc714a0754902d82aa1f6923ab478c318739462e565ca","Cargo.toml":"2961879155d753ba90ecd98c17875c82007a6973c95867e86bc1ec5bd4f5db41","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"0bf17650e07b88f1486f033643c1e82517caa69410e6faeaa352782d9231d63e","examples/custom_default_format.rs":"ae18cd0e765cf1f16568f9879925861d6f004481f955b58af5ed8fd04b0fca99","examples/custom_format.rs":"b0f41b7a3e6fe7582871281f4244c62c66b0d724bfc678907f67185a784e82b4","examples/custom_logger.rs":"6eeef506681a46925117e8f89395cdf4fea60a0d1f6a420e51768e790272dcde","examples/default.rs":"7ed1c6a8a8fe457a86676bd3a75c07d4ec7fb54147cf2825c9d299a5878a24cd","examples/direct_logger.rs":"ee20c25379c396e5e74e963290a4d8773a86f3fe10193f61fb1efd1c7271faf4","examples/filters_from_code.rs":"7f007b0dfa5a3964f839134824dc3684bf2f3c3d7b4c36c580cd029df5f9308b","src/filter/mod.rs":"5da7e51e7b77efdd4d2b5445e5d0264be2c897909d0f86eb553e16611307aed2","src/filter/regex.rs":"bdf875bac25e089e1e462f5dd01a88678067c24118ecd6268561c6a6af39747d","src/filter/string.rs":"fac54d51189fc0b5d2bff334b7a7e465177b431e3428299e345e1f90062d832e","src/fmt/humantime/extern_impl.rs":"f3087b29eedb8b4d5573621ad206e48a2eac72a77277be3b0e631d7dc9fb7a2e","src/fmt/humantime/mod.rs":"f4111c26cf2ffb85c1d639bd7674d55af7e1736e7e98c52f7be3070046a3253f","src/fmt/humantime/shim_impl.rs":"cce9a252abd5952fa109a72b1dfb85a593d237e22606b2b608a32c69184560e9","src/fmt/mod.rs":"4ab11971a73eb5fe9b40f0bca6dfc404321dd9e2ffcf87d911408e7183dc8362","src/fmt/writer/atty.rs":"69d9dd26c430000cd2d40f9c68b2e77cd492fec22921dd2c16864301252583e0","src/fmt/writer/mod.rs":"1e0feb4dee3ee86c4c24f49566673e99ec85765869105a07a2fc7436d7640cfe","src/fmt/writer/termcolor/extern_impl.rs":"89e9f2e66b914ddc960ad9a4355265a5db5d7be410b139cf2b54ca99207374a7","src/fmt/writer/termcolor/mod.rs":"a790f9391a50cd52be6823e3e55942de13a8d12e23d63765342ae9e8dd6d091c","src/fmt/writer/termcolor/shim_impl.rs":"d93786671d6a89fc2912f77f04b8cb0b82d67277d255d15ac31bfc1bc4464e30","src/lib.rs":"3cbc4f4d3fe51c43fc45a2f435c141f0de5b40b65ba0d2c7d16bb58c04d10898","tests/init-twice-retains-filter.rs":"be5cd2132342d89ede1f5c4266173bb3c4d51cc22a1847f133d299a1c5430ccb","tests/log-in-log.rs":"29fecc65c1e0d1c22d79c97e7ca843ad44a91f27934148d7a05c48899a3f39d8","tests/log_tls_dtors.rs":"7320667d774a9b05037f7bf273fb2574dec0705707692a9cd2f46f4cd5bc68dd","tests/regexp_filter.rs":"a84263c995b534b6479a1d0abadf63f4f0264958ff86d9173d6b2139b82c4dc5"},"package":"44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"}
|
|
@ -1,3 +0,0 @@
|
|||
Changes to this crate are tracked via [GitHub Releases][releases].
|
||||
|
||||
[releases]: https://github.com/sebasmagri/env_logger/releases
|
|
@ -1,212 +0,0 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.6.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
dependencies = [
|
||||
"atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "redox_termios"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ucd-util"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "utf8-ranges"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "wincolor"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[metadata]
|
||||
"checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
|
||||
"checksum atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "af80143d6f7608d746df1520709e5d141c96f240b0e62b0aa41bdfb53374d9d4"
|
||||
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
|
||||
"checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
|
||||
"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
|
||||
"checksum libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "6fd41f331ac7c5b8ac259b8bf82c75c0fb2e469bbf37d2becbba9a6a2221965b"
|
||||
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
|
||||
"checksum memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0a3eb002f0535929f1199681417029ebea04aadc0c7a4224b46be99c7f5d6a16"
|
||||
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
|
||||
"checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd"
|
||||
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
|
||||
"checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f"
|
||||
"checksum regex-syntax 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8c2f35eedad5295fdf00a63d7d4b238135723f92b434ec06774dad15c7ab0861"
|
||||
"checksum termcolor 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4096add70612622289f2fdcdbd5086dc81c1e2675e6ae58d6c4f62a16c6d7f2f"
|
||||
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
|
||||
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
|
||||
"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d"
|
||||
"checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
|
||||
"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
|
||||
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
|
||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9"
|
||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
"checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba"
|
|
@ -1,62 +0,0 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
authors = ["The Rust Project Developers"]
|
||||
description = "A logging implementation for `log` which is configured via an environment\nvariable.\n"
|
||||
documentation = "https://docs.rs/env_logger"
|
||||
readme = "README.md"
|
||||
keywords = ["logging", "log", "logger"]
|
||||
categories = ["development-tools::debugging"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/sebasmagri/env_logger/"
|
||||
|
||||
[[test]]
|
||||
name = "regexp_filter"
|
||||
harness = false
|
||||
|
||||
[[test]]
|
||||
name = "log-in-log"
|
||||
harness = false
|
||||
|
||||
[[test]]
|
||||
name = "log_tls_dtors"
|
||||
harness = false
|
||||
|
||||
[[test]]
|
||||
name = "init-twice-retains-filter"
|
||||
harness = false
|
||||
[dependencies.atty]
|
||||
version = "0.2.5"
|
||||
optional = true
|
||||
|
||||
[dependencies.humantime]
|
||||
version = "1.3"
|
||||
optional = true
|
||||
|
||||
[dependencies.log]
|
||||
version = "0.4.8"
|
||||
features = ["std"]
|
||||
|
||||
[dependencies.regex]
|
||||
version = "1.0.3"
|
||||
optional = true
|
||||
|
||||
[dependencies.termcolor]
|
||||
version = "1.0.2"
|
||||
optional = true
|
||||
|
||||
[features]
|
||||
default = ["termcolor", "atty", "humantime", "regex"]
|
|
@ -1,201 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -1,25 +0,0 @@
|
|||
Copyright (c) 2014 The Rust Project Developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
|
@ -1,150 +0,0 @@
|
|||
env_logger [![Build Status](https://travis-ci.org/sebasmagri/env_logger.svg?branch=master)](https://travis-ci.org/sebasmagri/env_logger) [![Maintenance](https://img.shields.io/badge/maintenance-actively%20maintained-brightgreen.svg)](https://github.com/sebasmagri/env_logger) [![crates.io](https://img.shields.io/crates/v/env_logger.svg)](https://crates.io/crates/env_logger) [![Documentation](https://img.shields.io/badge/docs-current-blue.svg)](https://docs.rs/env_logger)
|
||||
==========
|
||||
|
||||
Implements a logger that can be configured via environment variables.
|
||||
|
||||
## Usage
|
||||
|
||||
### In libraries
|
||||
|
||||
`env_logger` makes sense when used in executables (binary projects). Libraries should use the [`log`](https://doc.rust-lang.org/log) crate instead.
|
||||
|
||||
### In executables
|
||||
|
||||
It must be added along with `log` to the project dependencies:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
log = "0.4.0"
|
||||
env_logger = "0.7.1"
|
||||
```
|
||||
|
||||
`env_logger` must be initialized as early as possible in the project. After it's initialized, you can use the `log` macros to do actual logging.
|
||||
|
||||
```rust
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
|
||||
info!("starting up");
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Then when running the executable, specify a value for the `RUST_LOG`
|
||||
environment variable that corresponds with the log messages you want to show.
|
||||
|
||||
```bash
|
||||
$ RUST_LOG=info ./main
|
||||
[2018-11-03T06:09:06Z INFO default] starting up
|
||||
```
|
||||
|
||||
`env_logger` can be configured in other ways besides an environment variable. See [the examples](https://github.com/sebasmagri/env_logger/tree/master/examples) for more approaches.
|
||||
|
||||
### In tests
|
||||
|
||||
Tests can use the `env_logger` crate to see log messages generated during that test:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
log = "0.4.0"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.7.1"
|
||||
```
|
||||
|
||||
```rust
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
fn add_one(num: i32) -> i32 {
|
||||
info!("add_one called with {}", num);
|
||||
num + 1
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn init() {
|
||||
let _ = env_logger::builder().is_test(true).try_init();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_adds_one() {
|
||||
init();
|
||||
|
||||
info!("can log from the test too");
|
||||
assert_eq!(3, add_one(2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_handles_negative_numbers() {
|
||||
init();
|
||||
|
||||
info!("logging from another test");
|
||||
assert_eq!(-7, add_one(-8));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Assuming the module under test is called `my_lib`, running the tests with the
|
||||
`RUST_LOG` filtering to info messages from this module looks like:
|
||||
|
||||
```bash
|
||||
$ RUST_LOG=my_lib=info cargo test
|
||||
Running target/debug/my_lib-...
|
||||
|
||||
running 2 tests
|
||||
[INFO my_lib::tests] logging from another test
|
||||
[INFO my_lib] add_one called with -8
|
||||
test tests::it_handles_negative_numbers ... ok
|
||||
[INFO my_lib::tests] can log from the test too
|
||||
[INFO my_lib] add_one called with 2
|
||||
test tests::it_adds_one ... ok
|
||||
|
||||
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured
|
||||
```
|
||||
|
||||
Note that `env_logger::try_init()` needs to be called in each test in which you
|
||||
want to enable logging. Additionally, the default behavior of tests to
|
||||
run in parallel means that logging output may be interleaved with test output.
|
||||
Either run tests in a single thread by specifying `RUST_TEST_THREADS=1` or by
|
||||
running one test by specifying its name as an argument to the test binaries as
|
||||
directed by the `cargo test` help docs:
|
||||
|
||||
```bash
|
||||
$ RUST_LOG=my_lib=info cargo test it_adds_one
|
||||
Running target/debug/my_lib-...
|
||||
|
||||
running 1 test
|
||||
[INFO my_lib::tests] can log from the test too
|
||||
[INFO my_lib] add_one called with 2
|
||||
test tests::it_adds_one ... ok
|
||||
|
||||
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
|
||||
```
|
||||
|
||||
## Configuring log target
|
||||
|
||||
By default, `env_logger` logs to stderr. If you want to log to stdout instead,
|
||||
you can use the `Builder` to change the log target:
|
||||
|
||||
```rust
|
||||
use std::env;
|
||||
use env_logger::{Builder, Target};
|
||||
|
||||
let mut builder = Builder::from_default_env();
|
||||
builder.target(Target::Stdout);
|
||||
|
||||
builder.init();
|
||||
```
|
||||
|
||||
## Stability of the default format
|
||||
|
||||
The default format won't optimise for long-term stability, and explicitly makes no guarantees about the stability of its output across major, minor or patch version bumps during `0.x`.
|
||||
|
||||
If you want to capture or interpret the output of `env_logger` programmatically then you should use a custom format.
|
|
@ -1,41 +0,0 @@
|
|||
/*!
|
||||
Disabling parts of the default format.
|
||||
|
||||
Before running this example, try setting the `MY_LOG_LEVEL` environment variable to `info`:
|
||||
|
||||
```no_run,shell
|
||||
$ export MY_LOG_LEVEL='info'
|
||||
```
|
||||
|
||||
Also try setting the `MY_LOG_STYLE` environment variable to `never` to disable colors
|
||||
or `auto` to enable them:
|
||||
|
||||
```no_run,shell
|
||||
$ export MY_LOG_STYLE=never
|
||||
```
|
||||
|
||||
If you want to control the logging output completely, see the `custom_logger` example.
|
||||
*/
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
use env_logger::{Builder, Env};
|
||||
|
||||
fn init_logger() {
|
||||
let env = Env::default()
|
||||
.filter("MY_LOG_LEVEL")
|
||||
.write_style("MY_LOG_STYLE");
|
||||
|
||||
let mut builder = Builder::from_env(env);
|
||||
|
||||
builder.format_level(false).format_timestamp_nanos();
|
||||
|
||||
builder.init();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
init_logger();
|
||||
|
||||
info!("a log from `MyLogger`");
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
/*!
|
||||
Changing the default logging format.
|
||||
|
||||
Before running this example, try setting the `MY_LOG_LEVEL` environment variable to `info`:
|
||||
|
||||
```no_run,shell
|
||||
$ export MY_LOG_LEVEL='info'
|
||||
```
|
||||
|
||||
Also try setting the `MY_LOG_STYLE` environment variable to `never` to disable colors
|
||||
or `auto` to enable them:
|
||||
|
||||
```no_run,shell
|
||||
$ export MY_LOG_STYLE=never
|
||||
```
|
||||
|
||||
If you want to control the logging output completely, see the `custom_logger` example.
|
||||
*/
|
||||
|
||||
#[cfg(all(feature = "termcolor", feature = "humantime"))]
|
||||
fn main() {
|
||||
use env_logger::{fmt, Builder, Env};
|
||||
use std::io::Write;
|
||||
|
||||
fn init_logger() {
|
||||
let env = Env::default()
|
||||
.filter("MY_LOG_LEVEL")
|
||||
.write_style("MY_LOG_STYLE");
|
||||
|
||||
Builder::from_env(env)
|
||||
.format(|buf, record| {
|
||||
let mut style = buf.style();
|
||||
style.set_bg(fmt::Color::Yellow).set_bold(true);
|
||||
|
||||
let timestamp = buf.timestamp();
|
||||
|
||||
writeln!(
|
||||
buf,
|
||||
"My formatted log ({}): {}",
|
||||
timestamp,
|
||||
style.value(record.args())
|
||||
)
|
||||
})
|
||||
.init();
|
||||
}
|
||||
|
||||
init_logger();
|
||||
|
||||
log::info!("a log from `MyLogger`");
|
||||
}
|
||||
|
||||
#[cfg(not(all(feature = "termcolor", feature = "humantime")))]
|
||||
fn main() {}
|
|
@ -1,60 +0,0 @@
|
|||
/*!
|
||||
Using `env_logger` to drive a custom logger.
|
||||
|
||||
Before running this example, try setting the `MY_LOG_LEVEL` environment variable to `info`:
|
||||
|
||||
```no_run,shell
|
||||
$ export MY_LOG_LEVEL='info'
|
||||
```
|
||||
|
||||
If you only want to change the way logs are formatted, look at the `custom_format` example.
|
||||
*/
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
use env_logger::filter::Filter;
|
||||
use log::{Log, Metadata, Record, SetLoggerError};
|
||||
|
||||
struct MyLogger {
|
||||
inner: Filter,
|
||||
}
|
||||
|
||||
impl MyLogger {
|
||||
fn new() -> MyLogger {
|
||||
use env_logger::filter::Builder;
|
||||
let mut builder = Builder::from_env("MY_LOG_LEVEL");
|
||||
|
||||
MyLogger {
|
||||
inner: builder.build(),
|
||||
}
|
||||
}
|
||||
|
||||
fn init() -> Result<(), SetLoggerError> {
|
||||
let logger = Self::new();
|
||||
|
||||
log::set_max_level(logger.inner.filter());
|
||||
log::set_boxed_logger(Box::new(logger))
|
||||
}
|
||||
}
|
||||
|
||||
impl Log for MyLogger {
|
||||
fn enabled(&self, metadata: &Metadata) -> bool {
|
||||
self.inner.enabled(metadata)
|
||||
}
|
||||
|
||||
fn log(&self, record: &Record) {
|
||||
// Check if the record is matched by the logger before logging
|
||||
if self.inner.matches(record) {
|
||||
println!("{} - {}", record.level(), record.args());
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
MyLogger::init().unwrap();
|
||||
|
||||
info!("a log from `MyLogger`");
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
/*!
|
||||
Using `env_logger`.
|
||||
|
||||
Before running this example, try setting the `MY_LOG_LEVEL` environment variable to `info`:
|
||||
|
||||
```no_run,shell
|
||||
$ export MY_LOG_LEVEL='info'
|
||||
```
|
||||
|
||||
Also try setting the `MY_LOG_STYLE` environment variable to `never` to disable colors
|
||||
or `auto` to enable them:
|
||||
|
||||
```no_run,shell
|
||||
$ export MY_LOG_STYLE=never
|
||||
```
|
||||
*/
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
use env_logger::Env;
|
||||
|
||||
fn main() {
|
||||
// The `Env` lets us tweak what the environment
|
||||
// variables to read are and what the default
|
||||
// value is if they're missing
|
||||
let env = Env::default()
|
||||
.filter_or("MY_LOG_LEVEL", "trace")
|
||||
.write_style_or("MY_LOG_STYLE", "always");
|
||||
|
||||
env_logger::init_from_env(env);
|
||||
|
||||
trace!("some trace log");
|
||||
debug!("some debug log");
|
||||
info!("some information log");
|
||||
warn!("some warning log");
|
||||
error!("some error log");
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/*!
|
||||
Using `env_logger::Logger` and the `log::Log` trait directly.
|
||||
|
||||
This example doesn't rely on environment variables, or having a static logger installed.
|
||||
*/
|
||||
|
||||
fn record() -> log::Record<'static> {
|
||||
let error_metadata = log::MetadataBuilder::new()
|
||||
.target("myApp")
|
||||
.level(log::Level::Error)
|
||||
.build();
|
||||
|
||||
log::Record::builder()
|
||||
.metadata(error_metadata)
|
||||
.args(format_args!("Error!"))
|
||||
.line(Some(433))
|
||||
.file(Some("app.rs"))
|
||||
.module_path(Some("server"))
|
||||
.build()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
use log::Log;
|
||||
|
||||
let stylish_logger = env_logger::Builder::new()
|
||||
.filter(None, log::LevelFilter::Error)
|
||||
.write_style(env_logger::WriteStyle::Always)
|
||||
.build();
|
||||
|
||||
let unstylish_logger = env_logger::Builder::new()
|
||||
.filter(None, log::LevelFilter::Error)
|
||||
.write_style(env_logger::WriteStyle::Never)
|
||||
.build();
|
||||
|
||||
stylish_logger.log(&record());
|
||||
unstylish_logger.log(&record());
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
/*!
|
||||
Specify logging filters in code instead of using an environment variable.
|
||||
*/
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
fn main() {
|
||||
env_logger::builder()
|
||||
.filter_level(log::LevelFilter::Trace)
|
||||
.init();
|
||||
|
||||
trace!("some trace log");
|
||||
debug!("some debug log");
|
||||
info!("some information log");
|
||||
warn!("some warning log");
|
||||
error!("some error log");
|
||||
}
|
|
@ -1,616 +0,0 @@
|
|||
//! Filtering for log records.
|
||||
//!
|
||||
//! This module contains the log filtering used by `env_logger` to match records.
|
||||
//! You can use the `Filter` type in your own logger implementation to use the same
|
||||
//! filter parsing and matching as `env_logger`. For more details about the format
|
||||
//! for directive strings see [Enabling Logging].
|
||||
//!
|
||||
//! ## Using `env_logger` in your own logger
|
||||
//!
|
||||
//! You can use `env_logger`'s filtering functionality with your own logger.
|
||||
//! Call [`Builder::parse`] to parse directives from a string when constructing
|
||||
//! your logger. Call [`Filter::matches`] to check whether a record should be
|
||||
//! logged based on the parsed filters when log records are received.
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate log;
|
||||
//! extern crate env_logger;
|
||||
//! use env_logger::filter::Filter;
|
||||
//! use log::{Log, Metadata, Record};
|
||||
//!
|
||||
//! struct MyLogger {
|
||||
//! filter: Filter
|
||||
//! }
|
||||
//!
|
||||
//! impl MyLogger {
|
||||
//! fn new() -> MyLogger {
|
||||
//! use env_logger::filter::Builder;
|
||||
//! let mut builder = Builder::new();
|
||||
//!
|
||||
//! // Parse a directives string from an environment variable
|
||||
//! if let Ok(ref filter) = std::env::var("MY_LOG_LEVEL") {
|
||||
//! builder.parse(filter);
|
||||
//! }
|
||||
//!
|
||||
//! MyLogger {
|
||||
//! filter: builder.build()
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! impl Log for MyLogger {
|
||||
//! fn enabled(&self, metadata: &Metadata) -> bool {
|
||||
//! self.filter.enabled(metadata)
|
||||
//! }
|
||||
//!
|
||||
//! fn log(&self, record: &Record) {
|
||||
//! // Check if the record is matched by the filter
|
||||
//! if self.filter.matches(record) {
|
||||
//! println!("{:?}", record);
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! fn flush(&self) {}
|
||||
//! }
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! [Enabling Logging]: ../index.html#enabling-logging
|
||||
//! [`Builder::parse`]: struct.Builder.html#method.parse
|
||||
//! [`Filter::matches`]: struct.Filter.html#method.matches
|
||||
|
||||
use log::{Level, LevelFilter, Metadata, Record};
|
||||
use std::env;
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
|
||||
#[cfg(feature = "regex")]
|
||||
#[path = "regex.rs"]
|
||||
mod inner;
|
||||
|
||||
#[cfg(not(feature = "regex"))]
|
||||
#[path = "string.rs"]
|
||||
mod inner;
|
||||
|
||||
/// A log filter.
|
||||
///
|
||||
/// This struct can be used to determine whether or not a log record
|
||||
/// should be written to the output.
|
||||
/// Use the [`Builder`] type to parse and construct a `Filter`.
|
||||
///
|
||||
/// [`Builder`]: struct.Builder.html
|
||||
pub struct Filter {
|
||||
directives: Vec<Directive>,
|
||||
filter: Option<inner::Filter>,
|
||||
}
|
||||
|
||||
/// A builder for a log filter.
|
||||
///
|
||||
/// It can be used to parse a set of directives from a string before building
|
||||
/// a [`Filter`] instance.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```
|
||||
/// #[macro_use]
|
||||
/// extern crate log;
|
||||
/// extern crate env_logger;
|
||||
///
|
||||
/// use std::env;
|
||||
/// use std::io;
|
||||
/// use env_logger::filter::Builder;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut builder = Builder::new();
|
||||
///
|
||||
/// // Parse a logging filter from an environment variable.
|
||||
/// if let Ok(rust_log) = env::var("RUST_LOG") {
|
||||
/// builder.parse(&rust_log);
|
||||
/// }
|
||||
///
|
||||
/// let filter = builder.build();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`Filter`]: struct.Filter.html
|
||||
pub struct Builder {
|
||||
directives: Vec<Directive>,
|
||||
filter: Option<inner::Filter>,
|
||||
built: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Directive {
|
||||
name: Option<String>,
|
||||
level: LevelFilter,
|
||||
}
|
||||
|
||||
impl Filter {
|
||||
/// Returns the maximum `LevelFilter` that this filter instance is
|
||||
/// configured to output.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate log;
|
||||
/// extern crate env_logger;
|
||||
///
|
||||
/// use log::LevelFilter;
|
||||
/// use env_logger::filter::Builder;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut builder = Builder::new();
|
||||
/// builder.filter(Some("module1"), LevelFilter::Info);
|
||||
/// builder.filter(Some("module2"), LevelFilter::Error);
|
||||
///
|
||||
/// let filter = builder.build();
|
||||
/// assert_eq!(filter.filter(), LevelFilter::Info);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn filter(&self) -> LevelFilter {
|
||||
self.directives
|
||||
.iter()
|
||||
.map(|d| d.level)
|
||||
.max()
|
||||
.unwrap_or(LevelFilter::Off)
|
||||
}
|
||||
|
||||
/// Checks if this record matches the configured filter.
|
||||
pub fn matches(&self, record: &Record) -> bool {
|
||||
if !self.enabled(record.metadata()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(filter) = self.filter.as_ref() {
|
||||
if !filter.is_match(&*record.args().to_string()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Determines if a log message with the specified metadata would be logged.
|
||||
pub fn enabled(&self, metadata: &Metadata) -> bool {
|
||||
let level = metadata.level();
|
||||
let target = metadata.target();
|
||||
|
||||
enabled(&self.directives, level, target)
|
||||
}
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
/// Initializes the filter builder with defaults.
|
||||
pub fn new() -> Builder {
|
||||
Builder {
|
||||
directives: Vec::new(),
|
||||
filter: None,
|
||||
built: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes the filter builder from an environment.
|
||||
pub fn from_env(env: &str) -> Builder {
|
||||
let mut builder = Builder::new();
|
||||
|
||||
if let Ok(s) = env::var(env) {
|
||||
builder.parse(&s);
|
||||
}
|
||||
|
||||
builder
|
||||
}
|
||||
|
||||
/// Adds a directive to the filter for a specific module.
|
||||
pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self {
|
||||
self.filter(Some(module), level)
|
||||
}
|
||||
|
||||
/// Adds a directive to the filter for all modules.
|
||||
pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self {
|
||||
self.filter(None, level)
|
||||
}
|
||||
|
||||
/// Adds a directive to the filter.
|
||||
///
|
||||
/// The given module (if any) will log at most the specified level provided.
|
||||
/// If no module is provided then the filter will apply to all log messages.
|
||||
pub fn filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self {
|
||||
self.directives.push(Directive {
|
||||
name: module.map(|s| s.to_string()),
|
||||
level,
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Parses the directives string.
|
||||
///
|
||||
/// See the [Enabling Logging] section for more details.
|
||||
///
|
||||
/// [Enabling Logging]: ../index.html#enabling-logging
|
||||
pub fn parse(&mut self, filters: &str) -> &mut Self {
|
||||
let (directives, filter) = parse_spec(filters);
|
||||
|
||||
self.filter = filter;
|
||||
|
||||
for directive in directives {
|
||||
self.directives.push(directive);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Build a log filter.
|
||||
pub fn build(&mut self) -> Filter {
|
||||
assert!(!self.built, "attempt to re-use consumed builder");
|
||||
self.built = true;
|
||||
|
||||
if self.directives.is_empty() {
|
||||
// Adds the default filter if none exist
|
||||
self.directives.push(Directive {
|
||||
name: None,
|
||||
level: LevelFilter::Error,
|
||||
});
|
||||
} else {
|
||||
// Sort the directives by length of their name, this allows a
|
||||
// little more efficient lookup at runtime.
|
||||
self.directives.sort_by(|a, b| {
|
||||
let alen = a.name.as_ref().map(|a| a.len()).unwrap_or(0);
|
||||
let blen = b.name.as_ref().map(|b| b.len()).unwrap_or(0);
|
||||
alen.cmp(&blen)
|
||||
});
|
||||
}
|
||||
|
||||
Filter {
|
||||
directives: mem::replace(&mut self.directives, Vec::new()),
|
||||
filter: mem::replace(&mut self.filter, None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Builder {
|
||||
fn default() -> Self {
|
||||
Builder::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Filter {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Filter")
|
||||
.field("filter", &self.filter)
|
||||
.field("directives", &self.directives)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Builder {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.built {
|
||||
f.debug_struct("Filter").field("built", &true).finish()
|
||||
} else {
|
||||
f.debug_struct("Filter")
|
||||
.field("filter", &self.filter)
|
||||
.field("directives", &self.directives)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=error/foo")
|
||||
/// and return a vector with log directives.
|
||||
fn parse_spec(spec: &str) -> (Vec<Directive>, Option<inner::Filter>) {
|
||||
let mut dirs = Vec::new();
|
||||
|
||||
let mut parts = spec.split('/');
|
||||
let mods = parts.next();
|
||||
let filter = parts.next();
|
||||
if parts.next().is_some() {
|
||||
eprintln!(
|
||||
"warning: invalid logging spec '{}', \
|
||||
ignoring it (too many '/'s)",
|
||||
spec
|
||||
);
|
||||
return (dirs, None);
|
||||
}
|
||||
mods.map(|m| {
|
||||
for s in m.split(',') {
|
||||
if s.len() == 0 {
|
||||
continue;
|
||||
}
|
||||
let mut parts = s.split('=');
|
||||
let (log_level, name) =
|
||||
match (parts.next(), parts.next().map(|s| s.trim()), parts.next()) {
|
||||
(Some(part0), None, None) => {
|
||||
// if the single argument is a log-level string or number,
|
||||
// treat that as a global fallback
|
||||
match part0.parse() {
|
||||
Ok(num) => (num, None),
|
||||
Err(_) => (LevelFilter::max(), Some(part0)),
|
||||
}
|
||||
}
|
||||
(Some(part0), Some(""), None) => (LevelFilter::max(), Some(part0)),
|
||||
(Some(part0), Some(part1), None) => match part1.parse() {
|
||||
Ok(num) => (num, Some(part0)),
|
||||
_ => {
|
||||
eprintln!(
|
||||
"warning: invalid logging spec '{}', \
|
||||
ignoring it",
|
||||
part1
|
||||
);
|
||||
continue;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
eprintln!(
|
||||
"warning: invalid logging spec '{}', \
|
||||
ignoring it",
|
||||
s
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
dirs.push(Directive {
|
||||
name: name.map(|s| s.to_string()),
|
||||
level: log_level,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let filter = filter.map_or(None, |filter| match inner::Filter::new(filter) {
|
||||
Ok(re) => Some(re),
|
||||
Err(e) => {
|
||||
eprintln!("warning: invalid regex filter - {}", e);
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
return (dirs, filter);
|
||||
}
|
||||
|
||||
// Check whether a level and target are enabled by the set of directives.
|
||||
fn enabled(directives: &[Directive], level: Level, target: &str) -> bool {
|
||||
// Search for the longest match, the vector is assumed to be pre-sorted.
|
||||
for directive in directives.iter().rev() {
|
||||
match directive.name {
|
||||
Some(ref name) if !target.starts_with(&**name) => {}
|
||||
Some(..) | None => return level <= directive.level,
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use log::{Level, LevelFilter};
|
||||
|
||||
use super::{enabled, parse_spec, Builder, Directive, Filter};
|
||||
|
||||
fn make_logger_filter(dirs: Vec<Directive>) -> Filter {
|
||||
let mut logger = Builder::new().build();
|
||||
logger.directives = dirs;
|
||||
logger
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_info() {
|
||||
let logger = Builder::new().filter(None, LevelFilter::Info).build();
|
||||
assert!(enabled(&logger.directives, Level::Info, "crate1"));
|
||||
assert!(!enabled(&logger.directives, Level::Debug, "crate1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_beginning_longest_match() {
|
||||
let logger = Builder::new()
|
||||
.filter(Some("crate2"), LevelFilter::Info)
|
||||
.filter(Some("crate2::mod"), LevelFilter::Debug)
|
||||
.filter(Some("crate1::mod1"), LevelFilter::Warn)
|
||||
.build();
|
||||
assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
|
||||
assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_default() {
|
||||
let logger = Builder::new().parse("info,crate1::mod1=warn").build();
|
||||
assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
|
||||
assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_full_path() {
|
||||
let logger = make_logger_filter(vec![
|
||||
Directive {
|
||||
name: Some("crate2".to_string()),
|
||||
level: LevelFilter::Info,
|
||||
},
|
||||
Directive {
|
||||
name: Some("crate1::mod1".to_string()),
|
||||
level: LevelFilter::Warn,
|
||||
},
|
||||
]);
|
||||
assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
|
||||
assert!(!enabled(&logger.directives, Level::Info, "crate1::mod1"));
|
||||
assert!(enabled(&logger.directives, Level::Info, "crate2"));
|
||||
assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_match() {
|
||||
let logger = make_logger_filter(vec![
|
||||
Directive {
|
||||
name: Some("crate2".to_string()),
|
||||
level: LevelFilter::Info,
|
||||
},
|
||||
Directive {
|
||||
name: Some("crate1::mod1".to_string()),
|
||||
level: LevelFilter::Warn,
|
||||
},
|
||||
]);
|
||||
assert!(!enabled(&logger.directives, Level::Warn, "crate3"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_beginning() {
|
||||
let logger = make_logger_filter(vec![
|
||||
Directive {
|
||||
name: Some("crate2".to_string()),
|
||||
level: LevelFilter::Info,
|
||||
},
|
||||
Directive {
|
||||
name: Some("crate1::mod1".to_string()),
|
||||
level: LevelFilter::Warn,
|
||||
},
|
||||
]);
|
||||
assert!(enabled(&logger.directives, Level::Info, "crate2::mod1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_beginning_longest_match() {
|
||||
let logger = make_logger_filter(vec![
|
||||
Directive {
|
||||
name: Some("crate2".to_string()),
|
||||
level: LevelFilter::Info,
|
||||
},
|
||||
Directive {
|
||||
name: Some("crate2::mod".to_string()),
|
||||
level: LevelFilter::Debug,
|
||||
},
|
||||
Directive {
|
||||
name: Some("crate1::mod1".to_string()),
|
||||
level: LevelFilter::Warn,
|
||||
},
|
||||
]);
|
||||
assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
|
||||
assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_default() {
|
||||
let logger = make_logger_filter(vec![
|
||||
Directive {
|
||||
name: None,
|
||||
level: LevelFilter::Info,
|
||||
},
|
||||
Directive {
|
||||
name: Some("crate1::mod1".to_string()),
|
||||
level: LevelFilter::Warn,
|
||||
},
|
||||
]);
|
||||
assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
|
||||
assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zero_level() {
|
||||
let logger = make_logger_filter(vec![
|
||||
Directive {
|
||||
name: None,
|
||||
level: LevelFilter::Info,
|
||||
},
|
||||
Directive {
|
||||
name: Some("crate1::mod1".to_string()),
|
||||
level: LevelFilter::Off,
|
||||
},
|
||||
]);
|
||||
assert!(!enabled(&logger.directives, Level::Error, "crate1::mod1"));
|
||||
assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_valid() {
|
||||
let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug");
|
||||
assert_eq!(dirs.len(), 3);
|
||||
assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
|
||||
assert_eq!(dirs[0].level, LevelFilter::Error);
|
||||
|
||||
assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
|
||||
assert_eq!(dirs[1].level, LevelFilter::max());
|
||||
|
||||
assert_eq!(dirs[2].name, Some("crate2".to_string()));
|
||||
assert_eq!(dirs[2].level, LevelFilter::Debug);
|
||||
assert!(filter.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_invalid_crate() {
|
||||
// test parse_spec with multiple = in specification
|
||||
let (dirs, filter) = parse_spec("crate1::mod1=warn=info,crate2=debug");
|
||||
assert_eq!(dirs.len(), 1);
|
||||
assert_eq!(dirs[0].name, Some("crate2".to_string()));
|
||||
assert_eq!(dirs[0].level, LevelFilter::Debug);
|
||||
assert!(filter.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_invalid_level() {
|
||||
// test parse_spec with 'noNumber' as log level
|
||||
let (dirs, filter) = parse_spec("crate1::mod1=noNumber,crate2=debug");
|
||||
assert_eq!(dirs.len(), 1);
|
||||
assert_eq!(dirs[0].name, Some("crate2".to_string()));
|
||||
assert_eq!(dirs[0].level, LevelFilter::Debug);
|
||||
assert!(filter.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_string_level() {
|
||||
// test parse_spec with 'warn' as log level
|
||||
let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2=warn");
|
||||
assert_eq!(dirs.len(), 1);
|
||||
assert_eq!(dirs[0].name, Some("crate2".to_string()));
|
||||
assert_eq!(dirs[0].level, LevelFilter::Warn);
|
||||
assert!(filter.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_empty_level() {
|
||||
// test parse_spec with '' as log level
|
||||
let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2=");
|
||||
assert_eq!(dirs.len(), 1);
|
||||
assert_eq!(dirs[0].name, Some("crate2".to_string()));
|
||||
assert_eq!(dirs[0].level, LevelFilter::max());
|
||||
assert!(filter.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_global() {
|
||||
// test parse_spec with no crate
|
||||
let (dirs, filter) = parse_spec("warn,crate2=debug");
|
||||
assert_eq!(dirs.len(), 2);
|
||||
assert_eq!(dirs[0].name, None);
|
||||
assert_eq!(dirs[0].level, LevelFilter::Warn);
|
||||
assert_eq!(dirs[1].name, Some("crate2".to_string()));
|
||||
assert_eq!(dirs[1].level, LevelFilter::Debug);
|
||||
assert!(filter.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_valid_filter() {
|
||||
let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug/abc");
|
||||
assert_eq!(dirs.len(), 3);
|
||||
assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
|
||||
assert_eq!(dirs[0].level, LevelFilter::Error);
|
||||
|
||||
assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
|
||||
assert_eq!(dirs[1].level, LevelFilter::max());
|
||||
|
||||
assert_eq!(dirs[2].name, Some("crate2".to_string()));
|
||||
assert_eq!(dirs[2].level, LevelFilter::Debug);
|
||||
assert!(filter.is_some() && filter.unwrap().to_string() == "abc");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_invalid_crate_filter() {
|
||||
let (dirs, filter) = parse_spec("crate1::mod1=error=warn,crate2=debug/a.c");
|
||||
assert_eq!(dirs.len(), 1);
|
||||
assert_eq!(dirs[0].name, Some("crate2".to_string()));
|
||||
assert_eq!(dirs[0].level, LevelFilter::Debug);
|
||||
assert!(filter.is_some() && filter.unwrap().to_string() == "a.c");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_spec_empty_with_filter() {
|
||||
let (dirs, filter) = parse_spec("crate1/a*c");
|
||||
assert_eq!(dirs.len(), 1);
|
||||
assert_eq!(dirs[0].name, Some("crate1".to_string()));
|
||||
assert_eq!(dirs[0].level, LevelFilter::max());
|
||||
assert!(filter.is_some() && filter.unwrap().to_string() == "a*c");
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
extern crate regex;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use self::regex::Regex;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Filter {
|
||||
inner: Regex,
|
||||
}
|
||||
|
||||
impl Filter {
|
||||
pub fn new(spec: &str) -> Result<Filter, String> {
|
||||
match Regex::new(spec) {
|
||||
Ok(r) => Ok(Filter { inner: r }),
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_match(&self, s: &str) -> bool {
|
||||
self.inner.is_match(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Filter {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.inner.fmt(f)
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Filter {
|
||||
inner: String,
|
||||
}
|
||||
|
||||
impl Filter {
|
||||
pub fn new(spec: &str) -> Result<Filter, String> {
|
||||
Ok(Filter {
|
||||
inner: spec.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_match(&self, s: &str) -> bool {
|
||||
s.contains(&self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Filter {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.inner.fmt(f)
|
||||
}
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
use std::fmt;
|
||||
use std::time::SystemTime;
|
||||
|
||||
use humantime::{
|
||||
format_rfc3339_micros, format_rfc3339_millis, format_rfc3339_nanos, format_rfc3339_seconds,
|
||||
};
|
||||
|
||||
use crate::fmt::{Formatter, TimestampPrecision};
|
||||
|
||||
pub(in crate::fmt) mod glob {
|
||||
pub use super::*;
|
||||
}
|
||||
|
||||
impl Formatter {
|
||||
/// Get a [`Timestamp`] for the current date and time in UTC.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Include the current timestamp with the log record:
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Write;
|
||||
///
|
||||
/// let mut builder = env_logger::Builder::new();
|
||||
///
|
||||
/// builder.format(|buf, record| {
|
||||
/// let ts = buf.timestamp();
|
||||
///
|
||||
/// writeln!(buf, "{}: {}: {}", ts, record.level(), record.args())
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// [`Timestamp`]: struct.Timestamp.html
|
||||
pub fn timestamp(&self) -> Timestamp {
|
||||
Timestamp {
|
||||
time: SystemTime::now(),
|
||||
precision: TimestampPrecision::Seconds,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a [`Timestamp`] for the current date and time in UTC with full
|
||||
/// second precision.
|
||||
pub fn timestamp_seconds(&self) -> Timestamp {
|
||||
Timestamp {
|
||||
time: SystemTime::now(),
|
||||
precision: TimestampPrecision::Seconds,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a [`Timestamp`] for the current date and time in UTC with
|
||||
/// millisecond precision.
|
||||
pub fn timestamp_millis(&self) -> Timestamp {
|
||||
Timestamp {
|
||||
time: SystemTime::now(),
|
||||
precision: TimestampPrecision::Millis,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a [`Timestamp`] for the current date and time in UTC with
|
||||
/// microsecond precision.
|
||||
pub fn timestamp_micros(&self) -> Timestamp {
|
||||
Timestamp {
|
||||
time: SystemTime::now(),
|
||||
precision: TimestampPrecision::Micros,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a [`Timestamp`] for the current date and time in UTC with
|
||||
/// nanosecond precision.
|
||||
pub fn timestamp_nanos(&self) -> Timestamp {
|
||||
Timestamp {
|
||||
time: SystemTime::now(),
|
||||
precision: TimestampPrecision::Nanos,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An [RFC3339] formatted timestamp.
|
||||
///
|
||||
/// The timestamp implements [`Display`] and can be written to a [`Formatter`].
|
||||
///
|
||||
/// [RFC3339]: https://www.ietf.org/rfc/rfc3339.txt
|
||||
/// [`Display`]: https://doc.rust-lang.org/stable/std/fmt/trait.Display.html
|
||||
/// [`Formatter`]: struct.Formatter.html
|
||||
pub struct Timestamp {
|
||||
time: SystemTime,
|
||||
precision: TimestampPrecision,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Timestamp {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
/// A `Debug` wrapper for `Timestamp` that uses the `Display` implementation.
|
||||
struct TimestampValue<'a>(&'a Timestamp);
|
||||
|
||||
impl<'a> fmt::Debug for TimestampValue<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
f.debug_tuple("Timestamp")
|
||||
.field(&TimestampValue(&self))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Timestamp {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let formatter = match self.precision {
|
||||
TimestampPrecision::Seconds => format_rfc3339_seconds,
|
||||
TimestampPrecision::Millis => format_rfc3339_millis,
|
||||
TimestampPrecision::Micros => format_rfc3339_micros,
|
||||
TimestampPrecision::Nanos => format_rfc3339_nanos,
|
||||
};
|
||||
|
||||
formatter(self.time).fmt(f)
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
/*
|
||||
This internal module contains the timestamp implementation.
|
||||
|
||||
Its public API is available when the `humantime` crate is available.
|
||||
*/
|
||||
|
||||
#[cfg_attr(feature = "humantime", path = "extern_impl.rs")]
|
||||
#[cfg_attr(not(feature = "humantime"), path = "shim_impl.rs")]
|
||||
mod imp;
|
||||
|
||||
pub(in crate::fmt) use self::imp::*;
|
|
@ -1,5 +0,0 @@
|
|||
/*
|
||||
Timestamps aren't available when we don't have a `humantime` dependency.
|
||||
*/
|
||||
|
||||
pub(in crate::fmt) mod glob {}
|
|
@ -1,489 +0,0 @@
|
|||
//! Formatting for log records.
|
||||
//!
|
||||
//! This module contains a [`Formatter`] that can be used to format log records
|
||||
//! into without needing temporary allocations. Usually you won't need to worry
|
||||
//! about the contents of this module and can use the `Formatter` like an ordinary
|
||||
//! [`Write`].
|
||||
//!
|
||||
//! # Formatting log records
|
||||
//!
|
||||
//! The format used to print log records can be customised using the [`Builder::format`]
|
||||
//! method.
|
||||
//! Custom formats can apply different color and weight to printed values using
|
||||
//! [`Style`] builders.
|
||||
//!
|
||||
//! ```
|
||||
//! use std::io::Write;
|
||||
//!
|
||||
//! let mut builder = env_logger::Builder::new();
|
||||
//!
|
||||
//! builder.format(|buf, record| {
|
||||
//! writeln!(buf, "{}: {}",
|
||||
//! record.level(),
|
||||
//! record.args())
|
||||
//! });
|
||||
//! ```
|
||||
//!
|
||||
//! [`Formatter`]: struct.Formatter.html
|
||||
//! [`Style`]: struct.Style.html
|
||||
//! [`Builder::format`]: ../struct.Builder.html#method.format
|
||||
//! [`Write`]: https://doc.rust-lang.org/stable/std/io/trait.Write.html
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::fmt::Display;
|
||||
use std::io::prelude::*;
|
||||
use std::rc::Rc;
|
||||
use std::{fmt, io, mem};
|
||||
|
||||
use log::Record;
|
||||
|
||||
mod humantime;
|
||||
pub(crate) mod writer;
|
||||
|
||||
pub use self::humantime::glob::*;
|
||||
pub use self::writer::glob::*;
|
||||
|
||||
use self::writer::{Buffer, Writer};
|
||||
|
||||
pub(crate) mod glob {
|
||||
pub use super::{Target, TimestampPrecision, WriteStyle};
|
||||
}
|
||||
|
||||
/// Formatting precision of timestamps.
|
||||
///
|
||||
/// Seconds give precision of full seconds, milliseconds give thousands of a
|
||||
/// second (3 decimal digits), microseconds are millionth of a second (6 decimal
|
||||
/// digits) and nanoseconds are billionth of a second (9 decimal digits).
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum TimestampPrecision {
|
||||
/// Full second precision (0 decimal digits)
|
||||
Seconds,
|
||||
/// Millisecond precision (3 decimal digits)
|
||||
Millis,
|
||||
/// Microsecond precision (6 decimal digits)
|
||||
Micros,
|
||||
/// Nanosecond precision (9 decimal digits)
|
||||
Nanos,
|
||||
}
|
||||
|
||||
/// The default timestamp precision is seconds.
|
||||
impl Default for TimestampPrecision {
|
||||
fn default() -> Self {
|
||||
TimestampPrecision::Seconds
|
||||
}
|
||||
}
|
||||
|
||||
/// A formatter to write logs into.
|
||||
///
|
||||
/// `Formatter` implements the standard [`Write`] trait for writing log records.
|
||||
/// It also supports terminal colors, through the [`style`] method.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Use the [`writeln`] macro to format a log record.
|
||||
/// An instance of a `Formatter` is passed to an `env_logger` format as `buf`:
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Write;
|
||||
///
|
||||
/// let mut builder = env_logger::Builder::new();
|
||||
///
|
||||
/// builder.format(|buf, record| writeln!(buf, "{}: {}", record.level(), record.args()));
|
||||
/// ```
|
||||
///
|
||||
/// [`Write`]: https://doc.rust-lang.org/stable/std/io/trait.Write.html
|
||||
/// [`writeln`]: https://doc.rust-lang.org/stable/std/macro.writeln.html
|
||||
/// [`style`]: #method.style
|
||||
pub struct Formatter {
|
||||
buf: Rc<RefCell<Buffer>>,
|
||||
write_style: WriteStyle,
|
||||
}
|
||||
|
||||
impl Formatter {
|
||||
pub(crate) fn new(writer: &Writer) -> Self {
|
||||
Formatter {
|
||||
buf: Rc::new(RefCell::new(writer.buffer())),
|
||||
write_style: writer.write_style(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn write_style(&self) -> WriteStyle {
|
||||
self.write_style
|
||||
}
|
||||
|
||||
pub(crate) fn print(&self, writer: &Writer) -> io::Result<()> {
|
||||
writer.print(&self.buf.borrow())
|
||||
}
|
||||
|
||||
pub(crate) fn clear(&mut self) {
|
||||
self.buf.borrow_mut().clear()
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Formatter {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.buf.borrow_mut().write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.buf.borrow_mut().flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Formatter {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Formatter").finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Builder {
|
||||
pub format_timestamp: Option<TimestampPrecision>,
|
||||
pub format_module_path: bool,
|
||||
pub format_level: bool,
|
||||
pub format_indent: Option<usize>,
|
||||
#[allow(unknown_lints, bare_trait_objects)]
|
||||
pub custom_format: Option<Box<Fn(&mut Formatter, &Record) -> io::Result<()> + Sync + Send>>,
|
||||
built: bool,
|
||||
}
|
||||
|
||||
impl Default for Builder {
|
||||
fn default() -> Self {
|
||||
Builder {
|
||||
format_timestamp: Some(Default::default()),
|
||||
format_module_path: true,
|
||||
format_level: true,
|
||||
format_indent: Some(4),
|
||||
custom_format: None,
|
||||
built: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
/// Convert the format into a callable function.
|
||||
///
|
||||
/// If the `custom_format` is `Some`, then any `default_format` switches are ignored.
|
||||
/// If the `custom_format` is `None`, then a default format is returned.
|
||||
/// Any `default_format` switches set to `false` won't be written by the format.
|
||||
#[allow(unknown_lints, bare_trait_objects)]
|
||||
pub fn build(&mut self) -> Box<Fn(&mut Formatter, &Record) -> io::Result<()> + Sync + Send> {
|
||||
assert!(!self.built, "attempt to re-use consumed builder");
|
||||
|
||||
let built = mem::replace(
|
||||
self,
|
||||
Builder {
|
||||
built: true,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
if let Some(fmt) = built.custom_format {
|
||||
fmt
|
||||
} else {
|
||||
Box::new(move |buf, record| {
|
||||
let fmt = DefaultFormat {
|
||||
timestamp: built.format_timestamp,
|
||||
module_path: built.format_module_path,
|
||||
level: built.format_level,
|
||||
written_header_value: false,
|
||||
indent: built.format_indent,
|
||||
buf,
|
||||
};
|
||||
|
||||
fmt.write(record)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "termcolor")]
|
||||
type SubtleStyle = StyledValue<'static, &'static str>;
|
||||
#[cfg(not(feature = "termcolor"))]
|
||||
type SubtleStyle = &'static str;
|
||||
|
||||
/// The default format.
|
||||
///
|
||||
/// This format needs to work with any combination of crate features.
|
||||
struct DefaultFormat<'a> {
|
||||
timestamp: Option<TimestampPrecision>,
|
||||
module_path: bool,
|
||||
level: bool,
|
||||
written_header_value: bool,
|
||||
indent: Option<usize>,
|
||||
buf: &'a mut Formatter,
|
||||
}
|
||||
|
||||
impl<'a> DefaultFormat<'a> {
|
||||
fn write(mut self, record: &Record) -> io::Result<()> {
|
||||
self.write_timestamp()?;
|
||||
self.write_level(record)?;
|
||||
self.write_module_path(record)?;
|
||||
self.finish_header()?;
|
||||
|
||||
self.write_args(record)
|
||||
}
|
||||
|
||||
fn subtle_style(&self, text: &'static str) -> SubtleStyle {
|
||||
#[cfg(feature = "termcolor")]
|
||||
{
|
||||
self.buf
|
||||
.style()
|
||||
.set_color(Color::Black)
|
||||
.set_intense(true)
|
||||
.into_value(text)
|
||||
}
|
||||
#[cfg(not(feature = "termcolor"))]
|
||||
{
|
||||
text
|
||||
}
|
||||
}
|
||||
|
||||
fn write_header_value<T>(&mut self, value: T) -> io::Result<()>
|
||||
where
|
||||
T: Display,
|
||||
{
|
||||
if !self.written_header_value {
|
||||
self.written_header_value = true;
|
||||
|
||||
let open_brace = self.subtle_style("[");
|
||||
write!(self.buf, "{}{}", open_brace, value)
|
||||
} else {
|
||||
write!(self.buf, " {}", value)
|
||||
}
|
||||
}
|
||||
|
||||
fn write_level(&mut self, record: &Record) -> io::Result<()> {
|
||||
if !self.level {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let level = {
|
||||
#[cfg(feature = "termcolor")]
|
||||
{
|
||||
self.buf.default_styled_level(record.level())
|
||||
}
|
||||
#[cfg(not(feature = "termcolor"))]
|
||||
{
|
||||
record.level()
|
||||
}
|
||||
};
|
||||
|
||||
self.write_header_value(format_args!("{:<5}", level))
|
||||
}
|
||||
|
||||
fn write_timestamp(&mut self) -> io::Result<()> {
|
||||
#[cfg(feature = "humantime")]
|
||||
{
|
||||
use self::TimestampPrecision::*;
|
||||
let ts = match self.timestamp {
|
||||
None => return Ok(()),
|
||||
Some(Seconds) => self.buf.timestamp_seconds(),
|
||||
Some(Millis) => self.buf.timestamp_millis(),
|
||||
Some(Micros) => self.buf.timestamp_micros(),
|
||||
Some(Nanos) => self.buf.timestamp_nanos(),
|
||||
};
|
||||
|
||||
self.write_header_value(ts)
|
||||
}
|
||||
#[cfg(not(feature = "humantime"))]
|
||||
{
|
||||
// Trick the compiler to think we have used self.timestamp
|
||||
// Workaround for "field is never used: `timestamp`" compiler nag.
|
||||
let _ = self.timestamp;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn write_module_path(&mut self, record: &Record) -> io::Result<()> {
|
||||
if !self.module_path {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(module_path) = record.module_path() {
|
||||
self.write_header_value(module_path)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn finish_header(&mut self) -> io::Result<()> {
|
||||
if self.written_header_value {
|
||||
let close_brace = self.subtle_style("]");
|
||||
write!(self.buf, "{} ", close_brace)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn write_args(&mut self, record: &Record) -> io::Result<()> {
|
||||
match self.indent {
|
||||
// Fast path for no indentation
|
||||
None => writeln!(self.buf, "{}", record.args()),
|
||||
|
||||
Some(indent_count) => {
|
||||
// Create a wrapper around the buffer only if we have to actually indent the message
|
||||
|
||||
struct IndentWrapper<'a, 'b: 'a> {
|
||||
fmt: &'a mut DefaultFormat<'b>,
|
||||
indent_count: usize,
|
||||
}
|
||||
|
||||
impl<'a, 'b> Write for IndentWrapper<'a, 'b> {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let mut first = true;
|
||||
for chunk in buf.split(|&x| x == b'\n') {
|
||||
if !first {
|
||||
write!(self.fmt.buf, "\n{:width$}", "", width = self.indent_count)?;
|
||||
}
|
||||
self.fmt.buf.write_all(chunk)?;
|
||||
first = false;
|
||||
}
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.fmt.buf.flush()
|
||||
}
|
||||
}
|
||||
|
||||
// The explicit scope here is just to make older versions of Rust happy
|
||||
{
|
||||
let mut wrapper = IndentWrapper {
|
||||
fmt: self,
|
||||
indent_count,
|
||||
};
|
||||
write!(wrapper, "{}", record.args())?;
|
||||
}
|
||||
|
||||
writeln!(self.buf)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use log::{Level, Record};
|
||||
|
||||
fn write(fmt: DefaultFormat) -> String {
|
||||
let buf = fmt.buf.buf.clone();
|
||||
|
||||
let record = Record::builder()
|
||||
.args(format_args!("log\nmessage"))
|
||||
.level(Level::Info)
|
||||
.file(Some("test.rs"))
|
||||
.line(Some(144))
|
||||
.module_path(Some("test::path"))
|
||||
.build();
|
||||
|
||||
fmt.write(&record).expect("failed to write record");
|
||||
|
||||
let buf = buf.borrow();
|
||||
String::from_utf8(buf.bytes().to_vec()).expect("failed to read record")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_with_header() {
|
||||
let writer = writer::Builder::new()
|
||||
.write_style(WriteStyle::Never)
|
||||
.build();
|
||||
|
||||
let mut f = Formatter::new(&writer);
|
||||
|
||||
let written = write(DefaultFormat {
|
||||
timestamp: None,
|
||||
module_path: true,
|
||||
level: true,
|
||||
written_header_value: false,
|
||||
indent: None,
|
||||
buf: &mut f,
|
||||
});
|
||||
|
||||
assert_eq!("[INFO test::path] log\nmessage\n", written);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_no_header() {
|
||||
let writer = writer::Builder::new()
|
||||
.write_style(WriteStyle::Never)
|
||||
.build();
|
||||
|
||||
let mut f = Formatter::new(&writer);
|
||||
|
||||
let written = write(DefaultFormat {
|
||||
timestamp: None,
|
||||
module_path: false,
|
||||
level: false,
|
||||
written_header_value: false,
|
||||
indent: None,
|
||||
buf: &mut f,
|
||||
});
|
||||
|
||||
assert_eq!("log\nmessage\n", written);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_indent_spaces() {
|
||||
let writer = writer::Builder::new()
|
||||
.write_style(WriteStyle::Never)
|
||||
.build();
|
||||
|
||||
let mut f = Formatter::new(&writer);
|
||||
|
||||
let written = write(DefaultFormat {
|
||||
timestamp: None,
|
||||
module_path: true,
|
||||
level: true,
|
||||
written_header_value: false,
|
||||
indent: Some(4),
|
||||
buf: &mut f,
|
||||
});
|
||||
|
||||
assert_eq!("[INFO test::path] log\n message\n", written);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_indent_zero_spaces() {
|
||||
let writer = writer::Builder::new()
|
||||
.write_style(WriteStyle::Never)
|
||||
.build();
|
||||
|
||||
let mut f = Formatter::new(&writer);
|
||||
|
||||
let written = write(DefaultFormat {
|
||||
timestamp: None,
|
||||
module_path: true,
|
||||
level: true,
|
||||
written_header_value: false,
|
||||
indent: Some(0),
|
||||
buf: &mut f,
|
||||
});
|
||||
|
||||
assert_eq!("[INFO test::path] log\nmessage\n", written);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format_indent_spaces_no_header() {
|
||||
let writer = writer::Builder::new()
|
||||
.write_style(WriteStyle::Never)
|
||||
.build();
|
||||
|
||||
let mut f = Formatter::new(&writer);
|
||||
|
||||
let written = write(DefaultFormat {
|
||||
timestamp: None,
|
||||
module_path: false,
|
||||
level: false,
|
||||
written_header_value: false,
|
||||
indent: Some(4),
|
||||
buf: &mut f,
|
||||
});
|
||||
|
||||
assert_eq!("log\n message\n", written);
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
This internal module contains the terminal detection implementation.
|
||||
|
||||
If the `atty` crate is available then we use it to detect whether we're
|
||||
attached to a particular TTY. If the `atty` crate is not available we
|
||||
assume we're not attached to anything. This effectively prevents styles
|
||||
from being printed.
|
||||
*/
|
||||
|
||||
#[cfg(feature = "atty")]
|
||||
mod imp {
|
||||
use atty;
|
||||
|
||||
pub(in crate::fmt) fn is_stdout() -> bool {
|
||||
atty::is(atty::Stream::Stdout)
|
||||
}
|
||||
|
||||
pub(in crate::fmt) fn is_stderr() -> bool {
|
||||
atty::is(atty::Stream::Stderr)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "atty"))]
|
||||
mod imp {
|
||||
pub(in crate::fmt) fn is_stdout() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub(in crate::fmt) fn is_stderr() -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::fmt) use self::imp::*;
|
|
@ -1,201 +0,0 @@
|
|||
mod atty;
|
||||
mod termcolor;
|
||||
|
||||
use self::atty::{is_stderr, is_stdout};
|
||||
use self::termcolor::BufferWriter;
|
||||
use std::{fmt, io};
|
||||
|
||||
pub(in crate::fmt) mod glob {
|
||||
pub use super::termcolor::glob::*;
|
||||
pub use super::*;
|
||||
}
|
||||
|
||||
pub(in crate::fmt) use self::termcolor::Buffer;
|
||||
|
||||
/// Log target, either `stdout` or `stderr`.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum Target {
|
||||
/// Logs will be sent to standard output.
|
||||
Stdout,
|
||||
/// Logs will be sent to standard error.
|
||||
Stderr,
|
||||
}
|
||||
|
||||
impl Default for Target {
|
||||
fn default() -> Self {
|
||||
Target::Stderr
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether or not to print styles to the target.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum WriteStyle {
|
||||
/// Try to print styles, but don't force the issue.
|
||||
Auto,
|
||||
/// Try very hard to print styles.
|
||||
Always,
|
||||
/// Never print styles.
|
||||
Never,
|
||||
}
|
||||
|
||||
impl Default for WriteStyle {
|
||||
fn default() -> Self {
|
||||
WriteStyle::Auto
|
||||
}
|
||||
}
|
||||
|
||||
/// A terminal target with color awareness.
|
||||
pub(crate) struct Writer {
|
||||
inner: BufferWriter,
|
||||
write_style: WriteStyle,
|
||||
}
|
||||
|
||||
impl Writer {
|
||||
pub fn write_style(&self) -> WriteStyle {
|
||||
self.write_style
|
||||
}
|
||||
|
||||
pub(in crate::fmt) fn buffer(&self) -> Buffer {
|
||||
self.inner.buffer()
|
||||
}
|
||||
|
||||
pub(in crate::fmt) fn print(&self, buf: &Buffer) -> io::Result<()> {
|
||||
self.inner.print(buf)
|
||||
}
|
||||
}
|
||||
|
||||
/// A builder for a terminal writer.
|
||||
///
|
||||
/// The target and style choice can be configured before building.
|
||||
pub(crate) struct Builder {
|
||||
target: Target,
|
||||
write_style: WriteStyle,
|
||||
is_test: bool,
|
||||
built: bool,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
/// Initialize the writer builder with defaults.
|
||||
pub(crate) fn new() -> Self {
|
||||
Builder {
|
||||
target: Default::default(),
|
||||
write_style: Default::default(),
|
||||
is_test: false,
|
||||
built: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the target to write to.
|
||||
pub(crate) fn target(&mut self, target: Target) -> &mut Self {
|
||||
self.target = target;
|
||||
self
|
||||
}
|
||||
|
||||
/// Parses a style choice string.
|
||||
///
|
||||
/// See the [Disabling colors] section for more details.
|
||||
///
|
||||
/// [Disabling colors]: ../index.html#disabling-colors
|
||||
pub(crate) fn parse_write_style(&mut self, write_style: &str) -> &mut Self {
|
||||
self.write_style(parse_write_style(write_style))
|
||||
}
|
||||
|
||||
/// Whether or not to print style characters when writing.
|
||||
pub(crate) fn write_style(&mut self, write_style: WriteStyle) -> &mut Self {
|
||||
self.write_style = write_style;
|
||||
self
|
||||
}
|
||||
|
||||
/// Whether or not to capture logs for `cargo test`.
|
||||
pub(crate) fn is_test(&mut self, is_test: bool) -> &mut Self {
|
||||
self.is_test = is_test;
|
||||
self
|
||||
}
|
||||
|
||||
/// Build a terminal writer.
|
||||
pub(crate) fn build(&mut self) -> Writer {
|
||||
assert!(!self.built, "attempt to re-use consumed builder");
|
||||
self.built = true;
|
||||
|
||||
let color_choice = match self.write_style {
|
||||
WriteStyle::Auto => {
|
||||
if match self.target {
|
||||
Target::Stderr => is_stderr(),
|
||||
Target::Stdout => is_stdout(),
|
||||
} {
|
||||
WriteStyle::Auto
|
||||
} else {
|
||||
WriteStyle::Never
|
||||
}
|
||||
}
|
||||
color_choice => color_choice,
|
||||
};
|
||||
|
||||
let writer = match self.target {
|
||||
Target::Stderr => BufferWriter::stderr(self.is_test, color_choice),
|
||||
Target::Stdout => BufferWriter::stdout(self.is_test, color_choice),
|
||||
};
|
||||
|
||||
Writer {
|
||||
inner: writer,
|
||||
write_style: self.write_style,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Builder {
|
||||
fn default() -> Self {
|
||||
Builder::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Builder {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Logger")
|
||||
.field("target", &self.target)
|
||||
.field("write_style", &self.write_style)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Writer {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Writer").finish()
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_write_style(spec: &str) -> WriteStyle {
|
||||
match spec {
|
||||
"auto" => WriteStyle::Auto,
|
||||
"always" => WriteStyle::Always,
|
||||
"never" => WriteStyle::Never,
|
||||
_ => Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_write_style_valid() {
|
||||
let inputs = vec![
|
||||
("auto", WriteStyle::Auto),
|
||||
("always", WriteStyle::Always),
|
||||
("never", WriteStyle::Never),
|
||||
];
|
||||
|
||||
for (input, expected) in inputs {
|
||||
assert_eq!(expected, parse_write_style(input));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_write_style_invalid() {
|
||||
let inputs = vec!["", "true", "false", "NEVER!!"];
|
||||
|
||||
for input in inputs {
|
||||
assert_eq!(WriteStyle::Auto, parse_write_style(input));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,486 +0,0 @@
|
|||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::fmt;
|
||||
use std::io::{self, Write};
|
||||
use std::rc::Rc;
|
||||
|
||||
use log::Level;
|
||||
use termcolor::{self, ColorChoice, ColorSpec, WriteColor};
|
||||
|
||||
use crate::fmt::{Formatter, Target, WriteStyle};
|
||||
|
||||
pub(in crate::fmt::writer) mod glob {
|
||||
pub use super::*;
|
||||
}
|
||||
|
||||
impl Formatter {
|
||||
/// Begin a new [`Style`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Create a bold, red colored style and use it to print the log level:
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Write;
|
||||
/// use env_logger::fmt::Color;
|
||||
///
|
||||
/// let mut builder = env_logger::Builder::new();
|
||||
///
|
||||
/// builder.format(|buf, record| {
|
||||
/// let mut level_style = buf.style();
|
||||
///
|
||||
/// level_style.set_color(Color::Red).set_bold(true);
|
||||
///
|
||||
/// writeln!(buf, "{}: {}",
|
||||
/// level_style.value(record.level()),
|
||||
/// record.args())
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// [`Style`]: struct.Style.html
|
||||
pub fn style(&self) -> Style {
|
||||
Style {
|
||||
buf: self.buf.clone(),
|
||||
spec: ColorSpec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the default [`Style`] for the given level.
|
||||
///
|
||||
/// The style can be used to print other values besides the level.
|
||||
pub fn default_level_style(&self, level: Level) -> Style {
|
||||
let mut level_style = self.style();
|
||||
match level {
|
||||
Level::Trace => level_style.set_color(Color::Black).set_intense(true),
|
||||
Level::Debug => level_style.set_color(Color::White),
|
||||
Level::Info => level_style.set_color(Color::Green),
|
||||
Level::Warn => level_style.set_color(Color::Yellow),
|
||||
Level::Error => level_style.set_color(Color::Red).set_bold(true),
|
||||
};
|
||||
level_style
|
||||
}
|
||||
|
||||
/// Get a printable [`Style`] for the given level.
|
||||
///
|
||||
/// The style can only be used to print the level.
|
||||
pub fn default_styled_level(&self, level: Level) -> StyledValue<'static, Level> {
|
||||
self.default_level_style(level).into_value(level)
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::fmt::writer) struct BufferWriter {
|
||||
inner: termcolor::BufferWriter,
|
||||
test_target: Option<Target>,
|
||||
}
|
||||
|
||||
pub(in crate::fmt) struct Buffer {
|
||||
inner: termcolor::Buffer,
|
||||
test_target: Option<Target>,
|
||||
}
|
||||
|
||||
impl BufferWriter {
|
||||
pub(in crate::fmt::writer) fn stderr(is_test: bool, write_style: WriteStyle) -> Self {
|
||||
BufferWriter {
|
||||
inner: termcolor::BufferWriter::stderr(write_style.into_color_choice()),
|
||||
test_target: if is_test { Some(Target::Stderr) } else { None },
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::fmt::writer) fn stdout(is_test: bool, write_style: WriteStyle) -> Self {
|
||||
BufferWriter {
|
||||
inner: termcolor::BufferWriter::stdout(write_style.into_color_choice()),
|
||||
test_target: if is_test { Some(Target::Stdout) } else { None },
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::fmt::writer) fn buffer(&self) -> Buffer {
|
||||
Buffer {
|
||||
inner: self.inner.buffer(),
|
||||
test_target: self.test_target,
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> {
|
||||
if let Some(target) = self.test_target {
|
||||
// This impl uses the `eprint` and `print` macros
|
||||
// instead of `termcolor`'s buffer.
|
||||
// This is so their output can be captured by `cargo test`
|
||||
let log = String::from_utf8_lossy(buf.bytes());
|
||||
|
||||
match target {
|
||||
Target::Stderr => eprint!("{}", log),
|
||||
Target::Stdout => print!("{}", log),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
self.inner.print(&buf.inner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
pub(in crate::fmt) fn clear(&mut self) {
|
||||
self.inner.clear()
|
||||
}
|
||||
|
||||
pub(in crate::fmt) fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.inner.write(buf)
|
||||
}
|
||||
|
||||
pub(in crate::fmt) fn flush(&mut self) -> io::Result<()> {
|
||||
self.inner.flush()
|
||||
}
|
||||
|
||||
pub(in crate::fmt) fn bytes(&self) -> &[u8] {
|
||||
self.inner.as_slice()
|
||||
}
|
||||
|
||||
fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> {
|
||||
// Ignore styles for test captured logs because they can't be printed
|
||||
if self.test_target.is_none() {
|
||||
self.inner.set_color(spec)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) -> io::Result<()> {
|
||||
// Ignore styles for test captured logs because they can't be printed
|
||||
if self.test_target.is_none() {
|
||||
self.inner.reset()
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteStyle {
|
||||
fn into_color_choice(self) -> ColorChoice {
|
||||
match self {
|
||||
WriteStyle::Always => ColorChoice::Always,
|
||||
WriteStyle::Auto => ColorChoice::Auto,
|
||||
WriteStyle::Never => ColorChoice::Never,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A set of styles to apply to the terminal output.
|
||||
///
|
||||
/// Call [`Formatter::style`] to get a `Style` and use the builder methods to
|
||||
/// set styling properties, like [color] and [weight].
|
||||
/// To print a value using the style, wrap it in a call to [`value`] when the log
|
||||
/// record is formatted.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Create a bold, red colored style and use it to print the log level:
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Write;
|
||||
/// use env_logger::fmt::Color;
|
||||
///
|
||||
/// let mut builder = env_logger::Builder::new();
|
||||
///
|
||||
/// builder.format(|buf, record| {
|
||||
/// let mut level_style = buf.style();
|
||||
///
|
||||
/// level_style.set_color(Color::Red).set_bold(true);
|
||||
///
|
||||
/// writeln!(buf, "{}: {}",
|
||||
/// level_style.value(record.level()),
|
||||
/// record.args())
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// Styles can be re-used to output multiple values:
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Write;
|
||||
/// use env_logger::fmt::Color;
|
||||
///
|
||||
/// let mut builder = env_logger::Builder::new();
|
||||
///
|
||||
/// builder.format(|buf, record| {
|
||||
/// let mut bold = buf.style();
|
||||
///
|
||||
/// bold.set_bold(true);
|
||||
///
|
||||
/// writeln!(buf, "{}: {} {}",
|
||||
/// bold.value(record.level()),
|
||||
/// bold.value("some bold text"),
|
||||
/// record.args())
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// [`Formatter::style`]: struct.Formatter.html#method.style
|
||||
/// [color]: #method.set_color
|
||||
/// [weight]: #method.set_bold
|
||||
/// [`value`]: #method.value
|
||||
#[derive(Clone)]
|
||||
pub struct Style {
|
||||
buf: Rc<RefCell<Buffer>>,
|
||||
spec: ColorSpec,
|
||||
}
|
||||
|
||||
/// A value that can be printed using the given styles.
|
||||
///
|
||||
/// It is the result of calling [`Style::value`].
|
||||
///
|
||||
/// [`Style::value`]: struct.Style.html#method.value
|
||||
pub struct StyledValue<'a, T> {
|
||||
style: Cow<'a, Style>,
|
||||
value: T,
|
||||
}
|
||||
|
||||
impl Style {
|
||||
/// Set the text color.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Create a style with red text:
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Write;
|
||||
/// use env_logger::fmt::Color;
|
||||
///
|
||||
/// let mut builder = env_logger::Builder::new();
|
||||
///
|
||||
/// builder.format(|buf, record| {
|
||||
/// let mut style = buf.style();
|
||||
///
|
||||
/// style.set_color(Color::Red);
|
||||
///
|
||||
/// writeln!(buf, "{}", style.value(record.args()))
|
||||
/// });
|
||||
/// ```
|
||||
pub fn set_color(&mut self, color: Color) -> &mut Style {
|
||||
self.spec.set_fg(color.into_termcolor());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the text weight.
|
||||
///
|
||||
/// If `yes` is true then text will be written in bold.
|
||||
/// If `yes` is false then text will be written in the default weight.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Create a style with bold text:
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Write;
|
||||
///
|
||||
/// let mut builder = env_logger::Builder::new();
|
||||
///
|
||||
/// builder.format(|buf, record| {
|
||||
/// let mut style = buf.style();
|
||||
///
|
||||
/// style.set_bold(true);
|
||||
///
|
||||
/// writeln!(buf, "{}", style.value(record.args()))
|
||||
/// });
|
||||
/// ```
|
||||
pub fn set_bold(&mut self, yes: bool) -> &mut Style {
|
||||
self.spec.set_bold(yes);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the text intensity.
|
||||
///
|
||||
/// If `yes` is true then text will be written in a brighter color.
|
||||
/// If `yes` is false then text will be written in the default color.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Create a style with intense text:
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Write;
|
||||
///
|
||||
/// let mut builder = env_logger::Builder::new();
|
||||
///
|
||||
/// builder.format(|buf, record| {
|
||||
/// let mut style = buf.style();
|
||||
///
|
||||
/// style.set_intense(true);
|
||||
///
|
||||
/// writeln!(buf, "{}", style.value(record.args()))
|
||||
/// });
|
||||
/// ```
|
||||
pub fn set_intense(&mut self, yes: bool) -> &mut Style {
|
||||
self.spec.set_intense(yes);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the background color.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Create a style with a yellow background:
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Write;
|
||||
/// use env_logger::fmt::Color;
|
||||
///
|
||||
/// let mut builder = env_logger::Builder::new();
|
||||
///
|
||||
/// builder.format(|buf, record| {
|
||||
/// let mut style = buf.style();
|
||||
///
|
||||
/// style.set_bg(Color::Yellow);
|
||||
///
|
||||
/// writeln!(buf, "{}", style.value(record.args()))
|
||||
/// });
|
||||
/// ```
|
||||
pub fn set_bg(&mut self, color: Color) -> &mut Style {
|
||||
self.spec.set_bg(color.into_termcolor());
|
||||
self
|
||||
}
|
||||
|
||||
/// Wrap a value in the style.
|
||||
///
|
||||
/// The same `Style` can be used to print multiple different values.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Create a bold, red colored style and use it to print the log level:
|
||||
///
|
||||
/// ```
|
||||
/// use std::io::Write;
|
||||
/// use env_logger::fmt::Color;
|
||||
///
|
||||
/// let mut builder = env_logger::Builder::new();
|
||||
///
|
||||
/// builder.format(|buf, record| {
|
||||
/// let mut style = buf.style();
|
||||
///
|
||||
/// style.set_color(Color::Red).set_bold(true);
|
||||
///
|
||||
/// writeln!(buf, "{}: {}",
|
||||
/// style.value(record.level()),
|
||||
/// record.args())
|
||||
/// });
|
||||
/// ```
|
||||
pub fn value<T>(&self, value: T) -> StyledValue<T> {
|
||||
StyledValue {
|
||||
style: Cow::Borrowed(self),
|
||||
value,
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrap a value in the style by taking ownership of it.
|
||||
pub(crate) fn into_value<T>(&mut self, value: T) -> StyledValue<'static, T> {
|
||||
StyledValue {
|
||||
style: Cow::Owned(self.clone()),
|
||||
value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> StyledValue<'a, T> {
|
||||
fn write_fmt<F>(&self, f: F) -> fmt::Result
|
||||
where
|
||||
F: FnOnce() -> fmt::Result,
|
||||
{
|
||||
self.style
|
||||
.buf
|
||||
.borrow_mut()
|
||||
.set_color(&self.style.spec)
|
||||
.map_err(|_| fmt::Error)?;
|
||||
|
||||
// Always try to reset the terminal style, even if writing failed
|
||||
let write = f();
|
||||
let reset = self.style.buf.borrow_mut().reset().map_err(|_| fmt::Error);
|
||||
|
||||
write.and(reset)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Style {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Style").field("spec", &self.spec).finish()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_styled_value_fmt {
|
||||
($($fmt_trait:path),*) => {
|
||||
$(
|
||||
impl<'a, T: $fmt_trait> $fmt_trait for StyledValue<'a, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter)->fmt::Result {
|
||||
self.write_fmt(|| T::fmt(&self.value, f))
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl_styled_value_fmt!(
|
||||
fmt::Debug,
|
||||
fmt::Display,
|
||||
fmt::Pointer,
|
||||
fmt::Octal,
|
||||
fmt::Binary,
|
||||
fmt::UpperHex,
|
||||
fmt::LowerHex,
|
||||
fmt::UpperExp,
|
||||
fmt::LowerExp
|
||||
);
|
||||
|
||||
// The `Color` type is copied from https://github.com/BurntSushi/ripgrep/tree/master/termcolor
|
||||
|
||||
/// The set of available colors for the terminal foreground/background.
|
||||
///
|
||||
/// The `Ansi256` and `Rgb` colors will only output the correct codes when
|
||||
/// paired with the `Ansi` `WriteColor` implementation.
|
||||
///
|
||||
/// The `Ansi256` and `Rgb` color types are not supported when writing colors
|
||||
/// on Windows using the console. If they are used on Windows, then they are
|
||||
/// silently ignored and no colors will be emitted.
|
||||
///
|
||||
/// This set may expand over time.
|
||||
///
|
||||
/// This type has a `FromStr` impl that can parse colors from their human
|
||||
/// readable form. The format is as follows:
|
||||
///
|
||||
/// 1. Any of the explicitly listed colors in English. They are matched
|
||||
/// case insensitively.
|
||||
/// 2. A single 8-bit integer, in either decimal or hexadecimal format.
|
||||
/// 3. A triple of 8-bit integers separated by a comma, where each integer is
|
||||
/// in decimal or hexadecimal format.
|
||||
///
|
||||
/// Hexadecimal numbers are written with a `0x` prefix.
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Color {
|
||||
Black,
|
||||
Blue,
|
||||
Green,
|
||||
Red,
|
||||
Cyan,
|
||||
Magenta,
|
||||
Yellow,
|
||||
White,
|
||||
Ansi256(u8),
|
||||
Rgb(u8, u8, u8),
|
||||
#[doc(hidden)]
|
||||
__Nonexhaustive,
|
||||
}
|
||||
|
||||
impl Color {
|
||||
fn into_termcolor(self) -> Option<termcolor::Color> {
|
||||
match self {
|
||||
Color::Black => Some(termcolor::Color::Black),
|
||||
Color::Blue => Some(termcolor::Color::Blue),
|
||||
Color::Green => Some(termcolor::Color::Green),
|
||||
Color::Red => Some(termcolor::Color::Red),
|
||||
Color::Cyan => Some(termcolor::Color::Cyan),
|
||||
Color::Magenta => Some(termcolor::Color::Magenta),
|
||||
Color::Yellow => Some(termcolor::Color::Yellow),
|
||||
Color::White => Some(termcolor::Color::White),
|
||||
Color::Ansi256(value) => Some(termcolor::Color::Ansi256(value)),
|
||||
Color::Rgb(r, g, b) => Some(termcolor::Color::Rgb(r, g, b)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
/*
|
||||
This internal module contains the style and terminal writing implementation.
|
||||
|
||||
Its public API is available when the `termcolor` crate is available.
|
||||
The terminal printing is shimmed when the `termcolor` crate is not available.
|
||||
*/
|
||||
|
||||
#[cfg_attr(feature = "termcolor", path = "extern_impl.rs")]
|
||||
#[cfg_attr(not(feature = "termcolor"), path = "shim_impl.rs")]
|
||||
mod imp;
|
||||
|
||||
pub(in crate::fmt) use self::imp::*;
|
|
@ -1,63 +0,0 @@
|
|||
use std::io;
|
||||
|
||||
use crate::fmt::{Target, WriteStyle};
|
||||
|
||||
pub(in crate::fmt::writer) mod glob {}
|
||||
|
||||
pub(in crate::fmt::writer) struct BufferWriter {
|
||||
target: Target,
|
||||
}
|
||||
|
||||
pub(in crate::fmt) struct Buffer(Vec<u8>);
|
||||
|
||||
impl BufferWriter {
|
||||
pub(in crate::fmt::writer) fn stderr(_is_test: bool, _write_style: WriteStyle) -> Self {
|
||||
BufferWriter {
|
||||
target: Target::Stderr,
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::fmt::writer) fn stdout(_is_test: bool, _write_style: WriteStyle) -> Self {
|
||||
BufferWriter {
|
||||
target: Target::Stdout,
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::fmt::writer) fn buffer(&self) -> Buffer {
|
||||
Buffer(Vec::new())
|
||||
}
|
||||
|
||||
pub(in crate::fmt::writer) fn print(&self, buf: &Buffer) -> io::Result<()> {
|
||||
// This impl uses the `eprint` and `print` macros
|
||||
// instead of using the streams directly.
|
||||
// This is so their output can be captured by `cargo test`
|
||||
let log = String::from_utf8_lossy(&buf.0);
|
||||
|
||||
match self.target {
|
||||
Target::Stderr => eprint!("{}", log),
|
||||
Target::Stdout => print!("{}", log),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
pub(in crate::fmt) fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
}
|
||||
|
||||
pub(in crate::fmt) fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.extend(buf);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
pub(in crate::fmt) fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(in crate::fmt) fn bytes(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,40 +0,0 @@
|
|||
extern crate env_logger;
|
||||
extern crate log;
|
||||
|
||||
use std::env;
|
||||
use std::process;
|
||||
use std::str;
|
||||
|
||||
fn main() {
|
||||
if env::var("YOU_ARE_TESTING_NOW").is_ok() {
|
||||
// Init from the env (which should set the max level to `Debug`)
|
||||
env_logger::init();
|
||||
|
||||
assert_eq!(log::LevelFilter::Debug, log::max_level());
|
||||
|
||||
// Init again using a different max level
|
||||
// This shouldn't clobber the level that was previously set
|
||||
env_logger::Builder::new()
|
||||
.parse_filters("info")
|
||||
.try_init()
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(log::LevelFilter::Debug, log::max_level());
|
||||
return;
|
||||
}
|
||||
|
||||
let exe = env::current_exe().unwrap();
|
||||
let out = process::Command::new(exe)
|
||||
.env("YOU_ARE_TESTING_NOW", "1")
|
||||
.env("RUST_LOG", "debug")
|
||||
.output()
|
||||
.unwrap_or_else(|e| panic!("Unable to start child process: {}", e));
|
||||
if out.status.success() {
|
||||
return;
|
||||
}
|
||||
|
||||
println!("test failed: {}", out.status);
|
||||
println!("--- stdout\n{}", str::from_utf8(&out.stdout).unwrap());
|
||||
println!("--- stderr\n{}", str::from_utf8(&out.stderr).unwrap());
|
||||
process::exit(1);
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate env_logger;
|
||||
|
||||
use std::env;
|
||||
use std::fmt;
|
||||
use std::process;
|
||||
use std::str;
|
||||
|
||||
struct Foo;
|
||||
|
||||
impl fmt::Display for Foo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
info!("test");
|
||||
f.write_str("bar")
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
if env::var("YOU_ARE_TESTING_NOW").is_ok() {
|
||||
return info!("{}", Foo);
|
||||
}
|
||||
|
||||
let exe = env::current_exe().unwrap();
|
||||
let out = process::Command::new(exe)
|
||||
.env("YOU_ARE_TESTING_NOW", "1")
|
||||
.env("RUST_LOG", "debug")
|
||||
.output()
|
||||
.unwrap_or_else(|e| panic!("Unable to start child process: {}", e));
|
||||
if out.status.success() {
|
||||
return;
|
||||
}
|
||||
|
||||
println!("test failed: {}", out.status);
|
||||
println!("--- stdout\n{}", str::from_utf8(&out.stdout).unwrap());
|
||||
println!("--- stderr\n{}", str::from_utf8(&out.stderr).unwrap());
|
||||
process::exit(1);
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate env_logger;
|
||||
|
||||
use std::env;
|
||||
use std::process;
|
||||
use std::str;
|
||||
use std::thread;
|
||||
|
||||
struct DropMe;
|
||||
|
||||
impl Drop for DropMe {
|
||||
fn drop(&mut self) {
|
||||
debug!("Dropping now");
|
||||
}
|
||||
}
|
||||
|
||||
fn run() {
|
||||
// Use multiple thread local values to increase the chance that our TLS
|
||||
// value will get destroyed after the FORMATTER key in the library
|
||||
thread_local! {
|
||||
static DROP_ME_0: DropMe = DropMe;
|
||||
static DROP_ME_1: DropMe = DropMe;
|
||||
static DROP_ME_2: DropMe = DropMe;
|
||||
static DROP_ME_3: DropMe = DropMe;
|
||||
static DROP_ME_4: DropMe = DropMe;
|
||||
static DROP_ME_5: DropMe = DropMe;
|
||||
static DROP_ME_6: DropMe = DropMe;
|
||||
static DROP_ME_7: DropMe = DropMe;
|
||||
static DROP_ME_8: DropMe = DropMe;
|
||||
static DROP_ME_9: DropMe = DropMe;
|
||||
}
|
||||
DROP_ME_0.with(|_| {});
|
||||
DROP_ME_1.with(|_| {});
|
||||
DROP_ME_2.with(|_| {});
|
||||
DROP_ME_3.with(|_| {});
|
||||
DROP_ME_4.with(|_| {});
|
||||
DROP_ME_5.with(|_| {});
|
||||
DROP_ME_6.with(|_| {});
|
||||
DROP_ME_7.with(|_| {});
|
||||
DROP_ME_8.with(|_| {});
|
||||
DROP_ME_9.with(|_| {});
|
||||
}
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
if env::var("YOU_ARE_TESTING_NOW").is_ok() {
|
||||
// Run on a separate thread because TLS values on the main thread
|
||||
// won't have their destructors run if pthread is used.
|
||||
// https://doc.rust-lang.org/std/thread/struct.LocalKey.html#platform-specific-behavior
|
||||
thread::spawn(run).join().unwrap();
|
||||
} else {
|
||||
let exe = env::current_exe().unwrap();
|
||||
let out = process::Command::new(exe)
|
||||
.env("YOU_ARE_TESTING_NOW", "1")
|
||||
.env("RUST_LOG", "debug")
|
||||
.output()
|
||||
.unwrap_or_else(|e| panic!("Unable to start child process: {}", e));
|
||||
if !out.status.success() {
|
||||
println!("test failed: {}", out.status);
|
||||
println!("--- stdout\n{}", str::from_utf8(&out.stdout).unwrap());
|
||||
println!("--- stderr\n{}", str::from_utf8(&out.stderr).unwrap());
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate env_logger;
|
||||
|
||||
use std::env;
|
||||
use std::process;
|
||||
use std::str;
|
||||
|
||||
fn main() {
|
||||
if env::var("LOG_REGEXP_TEST").ok() == Some(String::from("1")) {
|
||||
child_main();
|
||||
} else {
|
||||
parent_main()
|
||||
}
|
||||
}
|
||||
|
||||
fn child_main() {
|
||||
env_logger::init();
|
||||
info!("XYZ Message");
|
||||
}
|
||||
|
||||
fn run_child(rust_log: String) -> bool {
|
||||
let exe = env::current_exe().unwrap();
|
||||
let out = process::Command::new(exe)
|
||||
.env("LOG_REGEXP_TEST", "1")
|
||||
.env("RUST_LOG", rust_log)
|
||||
.output()
|
||||
.unwrap_or_else(|e| panic!("Unable to start child process: {}", e));
|
||||
str::from_utf8(out.stderr.as_ref())
|
||||
.unwrap()
|
||||
.contains("XYZ Message")
|
||||
}
|
||||
|
||||
fn assert_message_printed(rust_log: &str) {
|
||||
if !run_child(rust_log.to_string()) {
|
||||
panic!("RUST_LOG={} should allow the test log message", rust_log)
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_message_not_printed(rust_log: &str) {
|
||||
if run_child(rust_log.to_string()) {
|
||||
panic!(
|
||||
"RUST_LOG={} should not allow the test log message",
|
||||
rust_log
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn parent_main() {
|
||||
// test normal log severity levels
|
||||
assert_message_printed("info");
|
||||
assert_message_not_printed("warn");
|
||||
|
||||
// test of regular expression filters
|
||||
assert_message_printed("info/XYZ");
|
||||
assert_message_not_printed("info/XXX");
|
||||
}
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"9be745f2a777ab28fdaccf77169f89aad4eb28fee0513628312a00d5e4561c06","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"0621878e61f0d0fda054bcbe02df75192c28bde1ecc8289cbd86aeba2dd72720","README.md":"d05831c6d3375eb1fcb35183ab4ff3fa7b5819068b728a49af3102f5f8fdf7e0","src/arc.rs":"60742619b7404d8c46237e9a3f98c49f75f88e4c24513ebca1d0ddad0274b6d6","src/boxed.rs":"40537576912a01ed8bb3bd6b1c8179632613f37f65459e708d4abc2286933c57","src/btree.rs":"b83820fc2a00e2e34127b3037abde8b945f0ca2785f3def725787e6813c3d3e0","src/btree/map.rs":"4d8710cf6f00bd889045a6144de692d9f752d51089db493e859d55e5ba12430a","src/btree/node.rs":"f6b4557d30ca0e30c7c7b6752c7a2c67432aab5c18c08392a28040326620a109","src/btree/search.rs":"ae78f73f3e56ea277b0a02cc39454447b75e12a6c817ecfee00065b3ddbfff67","src/btree/set.rs":"29cc3bff736007b21e14017d880edbcc7c76c30e0c256e811cae1fff0dad13fa","src/format.rs":"cee32d75cf260b19c8db74b50852bc50b8c47189d22b7424b647d084c4a76857","src/hashmap.rs":"d64ce1acfb3a4ea3e1f0130f5bcf6a3064fce01a0055e0f29163034adaa14173","src/lib.rs":"4cd0ef055208600292ec075f600e940eafcd24bd3e74fe34bba9164842d7f380","src/rc.rs":"102ad49f2201b9f69b50cf5a35af1e0039094936354b12572702551970c2f53c","src/try_clone.rs":"32c790435c71dec116756c284d2b953d382292b7727675740229a6b53d8c8b41","src/vec.rs":"f5170e48333803a49d74b670eb48917c77613d3df97ba8bbfb75340d59898ace"},"package":"9599e8ccc571becb62700174680e54e5c50fc5b4d34c1c56d8915e0325650fea"}
|
||||
{"files":{"Cargo.toml":"2a7958c74d86b964737863c4d869cb8e7ae64ec85632600fd7cef6e87ee0be47","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"0621878e61f0d0fda054bcbe02df75192c28bde1ecc8289cbd86aeba2dd72720","README.md":"6d74a8ddac747e9fd6d8c2000c6d0ae05bc28bb9e47e629e86d4b8d05115f546","src/arc.rs":"60742619b7404d8c46237e9a3f98c49f75f88e4c24513ebca1d0ddad0274b6d6","src/boxed.rs":"40537576912a01ed8bb3bd6b1c8179632613f37f65459e708d4abc2286933c57","src/btree.rs":"b83820fc2a00e2e34127b3037abde8b945f0ca2785f3def725787e6813c3d3e0","src/btree/map.rs":"4d8710cf6f00bd889045a6144de692d9f752d51089db493e859d55e5ba12430a","src/btree/node.rs":"f6b4557d30ca0e30c7c7b6752c7a2c67432aab5c18c08392a28040326620a109","src/btree/search.rs":"ae78f73f3e56ea277b0a02cc39454447b75e12a6c817ecfee00065b3ddbfff67","src/btree/set.rs":"29cc3bff736007b21e14017d880edbcc7c76c30e0c256e811cae1fff0dad13fa","src/format.rs":"cee32d75cf260b19c8db74b50852bc50b8c47189d22b7424b647d084c4a76857","src/hashmap.rs":"cf02762085d9968fc235ef2c0626358661cb21aca2c8c19961b3969225c96dce","src/lib.rs":"4cd0ef055208600292ec075f600e940eafcd24bd3e74fe34bba9164842d7f380","src/rc.rs":"102ad49f2201b9f69b50cf5a35af1e0039094936354b12572702551970c2f53c","src/try_clone.rs":"32c790435c71dec116756c284d2b953d382292b7727675740229a6b53d8c8b41","src/vec.rs":"691e80f89ffd25ea3e0cb287ea9bdecb1528367f21a8b3d995e9b3915c01c8e4"},"package":"3bda4d04bca84e2331f0ff2ee8300064df3f467e37743d87788c1487a6dd903b"}
|
|
@ -13,7 +13,7 @@
|
|||
[package]
|
||||
edition = "2018"
|
||||
name = "fallible_collections"
|
||||
version = "0.3.1"
|
||||
version = "0.2.0"
|
||||
authors = ["vcombey <vcombey@student.42.fr>"]
|
||||
description = "a crate which adds fallible allocation api to std collections"
|
||||
readme = "README.md"
|
||||
|
|
|
@ -20,7 +20,7 @@ Add the following dependency to your Cargo manifest...
|
|||
|
||||
```toml
|
||||
[dependencies]
|
||||
fallible_collections = "0.3.1"
|
||||
fallible_collections = "0.1.3"
|
||||
```
|
||||
|
||||
...and see the [docs](https://docs.rs/fallible_collections) for how to use it.
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
//! Implement Fallible HashMap
|
||||
use super::TryClone;
|
||||
use crate::TryReserveError;
|
||||
use core::borrow::Borrow;
|
||||
use core::default::Default;
|
||||
use core::hash::Hash;
|
||||
|
||||
|
@ -26,7 +25,7 @@ where
|
|||
|
||||
pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
|
||||
where
|
||||
K: Borrow<Q>,
|
||||
K: core::borrow::Borrow<Q>,
|
||||
Q: Hash + Eq,
|
||||
{
|
||||
self.inner.get(k)
|
||||
|
@ -37,36 +36,11 @@ where
|
|||
Ok(self.inner.insert(k, v))
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> hashbrown::hash_map::Iter<'_, K, V> {
|
||||
self.inner.iter()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.inner.len()
|
||||
}
|
||||
|
||||
pub fn remove<Q: ?Sized>(&mut self, k: &Q) -> Option<V>
|
||||
where
|
||||
K: Borrow<Q>,
|
||||
Q: Hash + Eq,
|
||||
{
|
||||
self.inner.remove(k)
|
||||
}
|
||||
|
||||
fn reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
|
||||
self.inner.try_reserve(additional)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> IntoIterator for TryHashMap<K, V> {
|
||||
type Item = (K, V);
|
||||
type IntoIter = hashbrown::hash_map::IntoIter<K, V>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.inner.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> TryClone for TryHashMap<K, V>
|
||||
where
|
||||
K: Eq + Hash + TryClone,
|
||||
|
|
|
@ -64,19 +64,11 @@ pub trait FallibleVec<T> {
|
|||
/// fallible allocation.
|
||||
///
|
||||
/// See the crate documentation for more.
|
||||
#[derive(PartialEq)]
|
||||
#[derive(Default, PartialEq)]
|
||||
pub struct TryVec<T> {
|
||||
inner: Vec<T>,
|
||||
}
|
||||
|
||||
impl<T> Default for TryVec<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
inner: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: core::fmt::Debug> core::fmt::Debug for TryVec<T> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
write!(f, "{:?}", self.inner)
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{"files":{"Cargo.toml":"ae7f884d13f5d849afd58b5db15510396832df7f648b0095e43bfdfa600b2ba3","LICENSE-APACHE":"c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08","LICENSE-MIT":"f6deca8261a8f4a3403dc74c725c46051157fd36c27cd4b100277eb1f303ad11","README.md":"e4bb65f28ddffb11d7eb337e9585947651f2fc11a5e4290f0ca126e21c582c1e","benches/datetime_format.rs":"a44c7ffb3c9515e92828564df8f91a86470343a2fa7e1a08bc2bc7397ba2591c","benches/datetime_parse.rs":"336a241755ccfa546d1840e56a40218c4f98f22d7d274829c5102ec280f2d963","bulk.yaml":"17c2548388e0cd3a63473021a2f1e4ddedee082d79d9167cb31ad06a1890d3fc","src/date.rs":"ea9924869a2484e333df2a845009593dd0062bb3b3e1a5ef9cefc05ec0a41196","src/duration.rs":"a5d4756451f495a3d5df0c872caa6304dd81ae075f0e204c8145c11c7c8f698b","src/lib.rs":"59dcc8a1ed89ecde199d59a7f71e550b461f87858b4becffb0a183994bafdd7c","src/wrapper.rs":"5bbdaf43256b445a8ca3a6b3eded9dd4fa8fb4dadf3683a5bac9648d91117396","vagga.yaml":"59761e5138a3015ef5654fec095ac8f1587cd23529b37822dd540b866fe4cd5b"},"package":"df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"}
|
|
@ -1,37 +0,0 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
name = "humantime"
|
||||
version = "1.3.0"
|
||||
authors = ["Paul Colomiets <paul@colomiets.name>"]
|
||||
description = " A parser and formatter for std::time::{Duration, SystemTime}\n"
|
||||
homepage = "https://github.com/tailhook/humantime"
|
||||
documentation = "https://docs.rs/humantime"
|
||||
readme = "README.md"
|
||||
keywords = ["time", "human", "human-friendly", "parser", "duration"]
|
||||
categories = ["date-and-time"]
|
||||
license = "MIT/Apache-2.0"
|
||||
|
||||
[lib]
|
||||
name = "humantime"
|
||||
path = "src/lib.rs"
|
||||
[dependencies.quick-error]
|
||||
version = "1.0.0"
|
||||
[dev-dependencies.chrono]
|
||||
version = "0.4.0"
|
||||
|
||||
[dev-dependencies.rand]
|
||||
version = "0.4.2"
|
||||
|
||||
[dev-dependencies.time]
|
||||
version = "0.1.39"
|
|
@ -1,202 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
Copyright (c) 2016 The humantime Developers
|
||||
|
||||
Includes parts of http date with the following copyright:
|
||||
Copyright (c) 2016 Pyfisch
|
||||
|
||||
Includes portions of musl libc with the following copyright:
|
||||
Copyright © 2005-2013 Rich Felker
|
||||
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,68 +0,0 @@
|
|||
Human Time
|
||||
==========
|
||||
|
||||
**Status: stable**
|
||||
|
||||
[Documentation](https://docs.rs/humantime) |
|
||||
[Github](https://github.com/tailhook/humantime) |
|
||||
[Crate](https://crates.io/crates/humantime)
|
||||
|
||||
|
||||
Features:
|
||||
|
||||
* Parses durations in free form like `15days 2min 2s`
|
||||
* Formats durations in similar form `2years 2min 12us`
|
||||
* Parses and formats timestamp in `rfc3339` format: `2018-01-01T12:53:00Z`
|
||||
* Parses timestamps in a weaker format: `2018-01-01 12:53:00`
|
||||
|
||||
Timestamp parsing/formatting is super-fast because format is basically
|
||||
fixed.
|
||||
|
||||
Here are some micro-benchmarks:
|
||||
|
||||
```
|
||||
test result: ok. 0 passed; 0 failed; 26 ignored; 0 measured; 0 filtered out
|
||||
|
||||
Running target/release/deps/datetime_format-8facb4ac832d9770
|
||||
|
||||
running 2 tests
|
||||
test rfc3339_chrono ... bench: 737 ns/iter (+/- 37)
|
||||
test rfc3339_humantime_seconds ... bench: 73 ns/iter (+/- 2)
|
||||
|
||||
test result: ok. 0 passed; 0 failed; 0 ignored; 2 measured; 0 filtered out
|
||||
|
||||
Running target/release/deps/datetime_parse-342628f877d7867c
|
||||
|
||||
running 6 tests
|
||||
test datetime_utc_parse_millis ... bench: 228 ns/iter (+/- 11)
|
||||
test datetime_utc_parse_nanos ... bench: 236 ns/iter (+/- 10)
|
||||
test datetime_utc_parse_seconds ... bench: 204 ns/iter (+/- 18)
|
||||
test rfc3339_humantime_millis ... bench: 28 ns/iter (+/- 1)
|
||||
test rfc3339_humantime_nanos ... bench: 36 ns/iter (+/- 2)
|
||||
test rfc3339_humantime_seconds ... bench: 24 ns/iter (+/- 1)
|
||||
|
||||
test result: ok. 0 passed; 0 failed; 0 ignored; 6 measured; 0 filtered out
|
||||
```
|
||||
|
||||
See [humantime-serde] for serde integration (previous crate [serde-humantime] looks unmaintained).
|
||||
|
||||
[serde-humantime]: https://docs.rs/serde-humantime/0.1.1/serde_humantime/
|
||||
[humantime-serde]: https://docs.rs/humantime-serde
|
||||
|
||||
License
|
||||
=======
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, (./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license (./LICENSE-MIT or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
Contribution
|
||||
------------
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally
|
||||
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
||||
license, shall be dual licensed as above, without any additional terms or
|
||||
conditions.
|
|
@ -1,58 +0,0 @@
|
|||
#![feature(test)]
|
||||
extern crate chrono;
|
||||
extern crate humantime;
|
||||
extern crate test;
|
||||
|
||||
use std::io::Write;
|
||||
use std::time::{Duration, UNIX_EPOCH};
|
||||
use humantime::format_rfc3339;
|
||||
|
||||
|
||||
#[bench]
|
||||
fn rfc3339_humantime_seconds(b: &mut test::Bencher) {
|
||||
let time = UNIX_EPOCH + Duration::new(1483228799, 0);
|
||||
let mut buf = Vec::with_capacity(100);
|
||||
b.iter(|| {
|
||||
buf.truncate(0);
|
||||
write!(&mut buf, "{}", format_rfc3339(time)).unwrap()
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn rfc3339_chrono(b: &mut test::Bencher) {
|
||||
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||
use chrono::format::Item;
|
||||
use chrono::format::Item::*;
|
||||
use chrono::format::Numeric::*;
|
||||
use chrono::format::Fixed::*;
|
||||
use chrono::format::Pad::*;
|
||||
|
||||
let time = DateTime::<Utc>::from_utc(
|
||||
NaiveDateTime::from_timestamp(1483228799, 0), Utc);
|
||||
let mut buf = Vec::with_capacity(100);
|
||||
|
||||
// formatting code from env_logger
|
||||
const ITEMS: &'static [Item<'static>] = {
|
||||
&[
|
||||
Numeric(Year, Zero),
|
||||
Literal("-"),
|
||||
Numeric(Month, Zero),
|
||||
Literal("-"),
|
||||
Numeric(Day, Zero),
|
||||
Literal("T"),
|
||||
Numeric(Hour, Zero),
|
||||
Literal(":"),
|
||||
Numeric(Minute, Zero),
|
||||
Literal(":"),
|
||||
Numeric(Second, Zero),
|
||||
Fixed(TimezoneOffsetZ),
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
b.iter(|| {
|
||||
buf.truncate(0);
|
||||
write!(&mut buf, "{}", time.format_with_items(ITEMS.iter().cloned()))
|
||||
.unwrap()
|
||||
});
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
#![feature(test)]
|
||||
extern crate chrono;
|
||||
extern crate humantime;
|
||||
extern crate test;
|
||||
|
||||
use chrono::{DateTime};
|
||||
use humantime::parse_rfc3339;
|
||||
|
||||
|
||||
#[bench]
|
||||
fn rfc3339_humantime_seconds(b: &mut test::Bencher) {
|
||||
b.iter(|| {
|
||||
parse_rfc3339("2018-02-13T23:08:32Z").unwrap()
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn datetime_utc_parse_seconds(b: &mut test::Bencher) {
|
||||
b.iter(|| {
|
||||
DateTime::parse_from_rfc3339("2018-02-13T23:08:32Z").unwrap()
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn rfc3339_humantime_millis(b: &mut test::Bencher) {
|
||||
b.iter(|| {
|
||||
parse_rfc3339("2018-02-13T23:08:32.123Z").unwrap()
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn datetime_utc_parse_millis(b: &mut test::Bencher) {
|
||||
b.iter(|| {
|
||||
DateTime::parse_from_rfc3339("2018-02-13T23:08:32.123Z").unwrap()
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn rfc3339_humantime_nanos(b: &mut test::Bencher) {
|
||||
b.iter(|| {
|
||||
parse_rfc3339("2018-02-13T23:08:32.123456983Z").unwrap()
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn datetime_utc_parse_nanos(b: &mut test::Bencher) {
|
||||
b.iter(|| {
|
||||
DateTime::parse_from_rfc3339("2018-02-13T23:08:32.123456983Z").unwrap()
|
||||
});
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
minimum-bulk: v0.4.5
|
||||
|
||||
versions:
|
||||
|
||||
- file: Cargo.toml
|
||||
block-start: ^\[package\]
|
||||
block-end: ^\[.*\]
|
||||
regex: ^version\s*=\s*"(\S+)"
|
|
@ -1,614 +0,0 @@
|
|||
use std::fmt;
|
||||
use std::str;
|
||||
use std::time::{SystemTime, Duration, UNIX_EPOCH};
|
||||
|
||||
#[cfg(target_os="cloudabi")]
|
||||
mod max {
|
||||
pub const SECONDS: u64 = ::std::u64::MAX / 1_000_000_000;
|
||||
#[allow(unused)]
|
||||
pub const TIMESTAMP: &'static str = "2554-07-21T23:34:33Z";
|
||||
}
|
||||
#[cfg(all(
|
||||
target_pointer_width="32",
|
||||
not(target_os="cloudabi"),
|
||||
not(target_os="windows"),
|
||||
not(all(target_arch="wasm32", not(target_os="emscripten")))
|
||||
))]
|
||||
mod max {
|
||||
pub const SECONDS: u64 = ::std::i32::MAX as u64;
|
||||
#[allow(unused)]
|
||||
pub const TIMESTAMP: &'static str = "2038-01-19T03:14:07Z";
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
target_pointer_width="64",
|
||||
target_os="windows",
|
||||
all(target_arch="wasm32", not(target_os="emscripten")),
|
||||
))]
|
||||
mod max {
|
||||
pub const SECONDS: u64 = 253402300800-1; // last second of year 9999
|
||||
#[allow(unused)]
|
||||
pub const TIMESTAMP: &'static str = "9999-12-31T23:59:59Z";
|
||||
}
|
||||
|
||||
quick_error! {
|
||||
/// Error parsing datetime (timestamp)
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub enum Error {
|
||||
/// Numeric component is out of range
|
||||
OutOfRange {
|
||||
display("numeric component is out of range")
|
||||
}
|
||||
/// Bad character where digit is expected
|
||||
InvalidDigit {
|
||||
display("bad character where digit is expected")
|
||||
}
|
||||
/// Other formatting errors
|
||||
InvalidFormat {
|
||||
display("timestamp format is invalid")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
enum Precision {
|
||||
Smart,
|
||||
Seconds,
|
||||
Millis,
|
||||
Micros,
|
||||
Nanos,
|
||||
}
|
||||
|
||||
/// A wrapper type that allows you to Display a SystemTime
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Rfc3339Timestamp(SystemTime, Precision);
|
||||
|
||||
#[inline]
|
||||
fn two_digits(b1: u8, b2: u8) -> Result<u64, Error> {
|
||||
if b1 < b'0' || b2 < b'0' || b1 > b'9' || b2 > b'9' {
|
||||
return Err(Error::InvalidDigit);
|
||||
}
|
||||
Ok(((b1 - b'0')*10 + (b2 - b'0')) as u64)
|
||||
}
|
||||
|
||||
/// Parse RFC3339 timestamp `2018-02-14T00:28:07Z`
|
||||
///
|
||||
/// Supported feature: any precision of fractional
|
||||
/// digits `2018-02-14T00:28:07.133Z`.
|
||||
///
|
||||
/// Unsupported feature: localized timestamps. Only UTC is supported.
|
||||
pub fn parse_rfc3339(s: &str) -> Result<SystemTime, Error> {
|
||||
if s.len() < "2018-02-14T00:28:07Z".len() {
|
||||
return Err(Error::InvalidFormat);
|
||||
}
|
||||
let b = s.as_bytes();
|
||||
if b[10] != b'T' || b[b.len()-1] != b'Z' {
|
||||
return Err(Error::InvalidFormat);
|
||||
}
|
||||
return parse_rfc3339_weak(s);
|
||||
}
|
||||
|
||||
/// Parse RFC3339-like timestamp `2018-02-14 00:28:07`
|
||||
///
|
||||
/// Supported features:
|
||||
///
|
||||
/// 1. Any precision of fractional digits `2018-02-14 00:28:07.133`.
|
||||
/// 2. Supports timestamp with or without either of `T` or `Z`
|
||||
/// 3. Anything valid for `parse_3339` is valid for this function
|
||||
///
|
||||
/// Unsupported feature: localized timestamps. Only UTC is supported, even if
|
||||
/// `Z` is not specified.
|
||||
///
|
||||
/// This function is intended to use for parsing human input. Whereas
|
||||
/// `parse_rfc3339` is for strings generated programmatically.
|
||||
pub fn parse_rfc3339_weak(s: &str) -> Result<SystemTime, Error> {
|
||||
if s.len() < "2018-02-14T00:28:07".len() {
|
||||
return Err(Error::InvalidFormat);
|
||||
}
|
||||
let b = s.as_bytes(); // for careless slicing
|
||||
if b[4] != b'-' || b[7] != b'-' || (b[10] != b'T' && b[10] != b' ') ||
|
||||
b[13] != b':' || b[16] != b':'
|
||||
{
|
||||
return Err(Error::InvalidFormat);
|
||||
}
|
||||
let year = two_digits(b[0], b[1])? * 100 + two_digits(b[2], b[3])?;
|
||||
let month = two_digits(b[5], b[6])?;
|
||||
let day = two_digits(b[8], b[9])?;
|
||||
let hour = two_digits(b[11], b[12])?;
|
||||
let minute = two_digits(b[14], b[15])?;
|
||||
let mut second = two_digits(b[17], b[18])?;
|
||||
|
||||
if year < 1970 || hour > 23 || minute > 59 || second > 60 {
|
||||
return Err(Error::OutOfRange);
|
||||
}
|
||||
// TODO(tailhook) should we check that leaps second is only on midnight ?
|
||||
if second == 60 {
|
||||
second = 59
|
||||
};
|
||||
let leap_years = ((year - 1) - 1968) / 4 - ((year - 1) - 1900) / 100 +
|
||||
((year - 1) - 1600) / 400;
|
||||
let leap = is_leap_year(year);
|
||||
let (mut ydays, mdays) = match month {
|
||||
1 => (0, 31),
|
||||
2 if leap => (31, 29),
|
||||
2 => (31, 28),
|
||||
3 => (59, 31),
|
||||
4 => (90, 30),
|
||||
5 => (120, 31),
|
||||
6 => (151, 30),
|
||||
7 => (181, 31),
|
||||
8 => (212, 31),
|
||||
9 => (243, 30),
|
||||
10 => (273, 31),
|
||||
11 => (304, 30),
|
||||
12 => (334, 31),
|
||||
_ => return Err(Error::OutOfRange),
|
||||
};
|
||||
if day > mdays || day == 0 {
|
||||
return Err(Error::OutOfRange);
|
||||
}
|
||||
ydays += day - 1;
|
||||
if leap && month > 2 {
|
||||
ydays += 1;
|
||||
}
|
||||
let days = (year - 1970) * 365 + leap_years + ydays;
|
||||
|
||||
let time = second + minute * 60 + hour * 3600;
|
||||
|
||||
let mut nanos = 0;
|
||||
let mut mult = 100_000_000;
|
||||
if b.get(19) == Some(&b'.') {
|
||||
for idx in 20..b.len() {
|
||||
if b[idx] == b'Z' {
|
||||
if idx == b.len()-1 {
|
||||
break;
|
||||
} else {
|
||||
return Err(Error::InvalidDigit);
|
||||
}
|
||||
}
|
||||
if b[idx] < b'0' || b[idx] > b'9' {
|
||||
return Err(Error::InvalidDigit);
|
||||
}
|
||||
nanos += mult * (b[idx] - b'0') as u32;
|
||||
mult /= 10;
|
||||
}
|
||||
} else {
|
||||
if b.len() != 19 && (b.len() > 20 || b[19] != b'Z') {
|
||||
return Err(Error::InvalidFormat);
|
||||
}
|
||||
}
|
||||
|
||||
let total_seconds = time + days * 86400;
|
||||
if total_seconds > max::SECONDS {
|
||||
return Err(Error::OutOfRange);
|
||||
}
|
||||
|
||||
return Ok(UNIX_EPOCH + Duration::new(total_seconds, nanos));
|
||||
}
|
||||
|
||||
fn is_leap_year(y: u64) -> bool {
|
||||
y % 4 == 0 && (!(y % 100 == 0) || y % 400 == 0)
|
||||
}
|
||||
|
||||
/// Format an RFC3339 timestamp `2018-02-14T00:28:07Z`
|
||||
///
|
||||
/// This function formats timestamp with smart precision: i.e. if it has no
|
||||
/// fractional seconds, they aren't written at all. And up to nine digits if
|
||||
/// they are.
|
||||
///
|
||||
/// The value is always UTC and ignores system timezone.
|
||||
pub fn format_rfc3339(system_time: SystemTime) -> Rfc3339Timestamp {
|
||||
return Rfc3339Timestamp(system_time, Precision::Smart);
|
||||
}
|
||||
|
||||
/// Format an RFC3339 timestamp `2018-02-14T00:28:07Z`
|
||||
///
|
||||
/// This format always shows timestamp without fractional seconds.
|
||||
///
|
||||
/// The value is always UTC and ignores system timezone.
|
||||
pub fn format_rfc3339_seconds(system_time: SystemTime) -> Rfc3339Timestamp {
|
||||
return Rfc3339Timestamp(system_time, Precision::Seconds);
|
||||
}
|
||||
|
||||
/// Format an RFC3339 timestamp `2018-02-14T00:28:07.000Z`
|
||||
///
|
||||
/// This format always shows milliseconds even if millisecond value is zero.
|
||||
///
|
||||
/// The value is always UTC and ignores system timezone.
|
||||
pub fn format_rfc3339_millis(system_time: SystemTime) -> Rfc3339Timestamp {
|
||||
return Rfc3339Timestamp(system_time, Precision::Millis);
|
||||
}
|
||||
|
||||
/// Format an RFC3339 timestamp `2018-02-14T00:28:07.000000Z`
|
||||
///
|
||||
/// This format always shows microseconds even if microsecond value is zero.
|
||||
///
|
||||
/// The value is always UTC and ignores system timezone.
|
||||
pub fn format_rfc3339_micros(system_time: SystemTime) -> Rfc3339Timestamp {
|
||||
return Rfc3339Timestamp(system_time, Precision::Micros);
|
||||
}
|
||||
|
||||
/// Format an RFC3339 timestamp `2018-02-14T00:28:07.000000000Z`
|
||||
///
|
||||
/// This format always shows nanoseconds even if nanosecond value is zero.
|
||||
///
|
||||
/// The value is always UTC and ignores system timezone.
|
||||
pub fn format_rfc3339_nanos(system_time: SystemTime) -> Rfc3339Timestamp {
|
||||
return Rfc3339Timestamp(system_time, Precision::Nanos);
|
||||
}
|
||||
|
||||
impl fmt::Display for Rfc3339Timestamp {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::Precision::*;
|
||||
|
||||
let dur = self.0.duration_since(UNIX_EPOCH)
|
||||
.expect("all times should be after the epoch");
|
||||
let secs_since_epoch = dur.as_secs();
|
||||
let nanos = dur.subsec_nanos();
|
||||
|
||||
if secs_since_epoch >= 253402300800 { // year 9999
|
||||
return Err(fmt::Error);
|
||||
}
|
||||
|
||||
/* 2000-03-01 (mod 400 year, immediately after feb29 */
|
||||
const LEAPOCH: i64 = 11017;
|
||||
const DAYS_PER_400Y: i64 = 365*400 + 97;
|
||||
const DAYS_PER_100Y: i64 = 365*100 + 24;
|
||||
const DAYS_PER_4Y: i64 = 365*4 + 1;
|
||||
|
||||
let days = (secs_since_epoch / 86400) as i64 - LEAPOCH;
|
||||
let secs_of_day = secs_since_epoch % 86400;
|
||||
|
||||
let mut qc_cycles = days / DAYS_PER_400Y;
|
||||
let mut remdays = days % DAYS_PER_400Y;
|
||||
|
||||
if remdays < 0 {
|
||||
remdays += DAYS_PER_400Y;
|
||||
qc_cycles -= 1;
|
||||
}
|
||||
|
||||
let mut c_cycles = remdays / DAYS_PER_100Y;
|
||||
if c_cycles == 4 { c_cycles -= 1; }
|
||||
remdays -= c_cycles * DAYS_PER_100Y;
|
||||
|
||||
let mut q_cycles = remdays / DAYS_PER_4Y;
|
||||
if q_cycles == 25 { q_cycles -= 1; }
|
||||
remdays -= q_cycles * DAYS_PER_4Y;
|
||||
|
||||
let mut remyears = remdays / 365;
|
||||
if remyears == 4 { remyears -= 1; }
|
||||
remdays -= remyears * 365;
|
||||
|
||||
let mut year = 2000 +
|
||||
remyears + 4*q_cycles + 100*c_cycles + 400*qc_cycles;
|
||||
|
||||
let months = [31,30,31,30,31,31,30,31,30,31,31,29];
|
||||
let mut mon = 0;
|
||||
for mon_len in months.iter() {
|
||||
mon += 1;
|
||||
if remdays < *mon_len {
|
||||
break;
|
||||
}
|
||||
remdays -= *mon_len;
|
||||
}
|
||||
let mday = remdays+1;
|
||||
let mon = if mon + 2 > 12 {
|
||||
year += 1;
|
||||
mon - 10
|
||||
} else {
|
||||
mon + 2
|
||||
};
|
||||
|
||||
let mut buf: [u8; 30] = [
|
||||
// Too long to write as: b"0000-00-00T00:00:00.000000000Z"
|
||||
b'0', b'0', b'0', b'0', b'-', b'0', b'0', b'-', b'0', b'0', b'T',
|
||||
b'0', b'0', b':', b'0', b'0', b':', b'0', b'0',
|
||||
b'.', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'0', b'Z',
|
||||
];
|
||||
buf[0] = b'0' + (year / 1000) as u8;
|
||||
buf[1] = b'0' + (year / 100 % 10) as u8;
|
||||
buf[2] = b'0' + (year / 10 % 10) as u8;
|
||||
buf[3] = b'0' + (year % 10) as u8;
|
||||
buf[5] = b'0' + (mon / 10) as u8;
|
||||
buf[6] = b'0' + (mon % 10) as u8;
|
||||
buf[8] = b'0' + (mday / 10) as u8;
|
||||
buf[9] = b'0' + (mday % 10) as u8;
|
||||
buf[11] = b'0' + (secs_of_day / 3600 / 10) as u8;
|
||||
buf[12] = b'0' + (secs_of_day / 3600 % 10) as u8;
|
||||
buf[14] = b'0' + (secs_of_day / 60 / 10 % 6) as u8;
|
||||
buf[15] = b'0' + (secs_of_day / 60 % 10) as u8;
|
||||
buf[17] = b'0' + (secs_of_day / 10 % 6) as u8;
|
||||
buf[18] = b'0' + (secs_of_day % 10) as u8;
|
||||
|
||||
let offset = if self.1 == Seconds || nanos == 0 && self.1 == Smart {
|
||||
buf[19] = b'Z';
|
||||
19
|
||||
} else if self.1 == Millis {
|
||||
buf[20] = b'0' + (nanos / 100_000_000) as u8;
|
||||
buf[21] = b'0' + (nanos / 10_000_000 % 10) as u8;
|
||||
buf[22] = b'0' + (nanos / 1_000_000 % 10) as u8;
|
||||
buf[23] = b'Z';
|
||||
23
|
||||
} else if self.1 == Micros {
|
||||
buf[20] = b'0' + (nanos / 100_000_000) as u8;
|
||||
buf[21] = b'0' + (nanos / 10_000_000 % 10) as u8;
|
||||
buf[22] = b'0' + (nanos / 1_000_000 % 10) as u8;
|
||||
buf[23] = b'0' + (nanos / 100_000 % 10) as u8;
|
||||
buf[24] = b'0' + (nanos / 10_000 % 10) as u8;
|
||||
buf[25] = b'0' + (nanos / 1_000 % 10) as u8;
|
||||
buf[26] = b'Z';
|
||||
26
|
||||
} else {
|
||||
buf[20] = b'0' + (nanos / 100_000_000) as u8;
|
||||
buf[21] = b'0' + (nanos / 10_000_000 % 10) as u8;
|
||||
buf[22] = b'0' + (nanos / 1_000_000 % 10) as u8;
|
||||
buf[23] = b'0' + (nanos / 100_000 % 10) as u8;
|
||||
buf[24] = b'0' + (nanos / 10_000 % 10) as u8;
|
||||
buf[25] = b'0' + (nanos / 1_000 % 10) as u8;
|
||||
buf[26] = b'0' + (nanos / 100 % 10) as u8;
|
||||
buf[27] = b'0' + (nanos / 10 % 10) as u8;
|
||||
buf[28] = b'0' + (nanos / 1 % 10) as u8;
|
||||
// 29th is 'Z'
|
||||
29
|
||||
};
|
||||
|
||||
// we know our chars are all ascii
|
||||
f.write_str(unsafe { str::from_utf8_unchecked(&buf[..offset+1]) })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
extern crate time;
|
||||
extern crate rand;
|
||||
|
||||
use std::str::from_utf8;
|
||||
use self::rand::Rng;
|
||||
use std::time::{UNIX_EPOCH, SystemTime, Duration};
|
||||
use super::{parse_rfc3339, parse_rfc3339_weak, format_rfc3339};
|
||||
use super::{format_rfc3339_millis, format_rfc3339_micros};
|
||||
use super::{format_rfc3339_nanos};
|
||||
use super::max;
|
||||
|
||||
fn from_sec(sec: u64) -> (String, SystemTime) {
|
||||
let s = time::at_utc(time::Timespec { sec: sec as i64, nsec: 0 })
|
||||
.rfc3339().to_string();
|
||||
let time = UNIX_EPOCH + Duration::new(sec, 0);
|
||||
return (s, time)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(target_pointer_width="32", target_os="linux"))]
|
||||
fn year_after_2038_fails_gracefully() {
|
||||
// next second
|
||||
assert_eq!(parse_rfc3339("2038-01-19T03:14:08Z").unwrap_err(),
|
||||
super::Error::OutOfRange);
|
||||
assert_eq!(parse_rfc3339("9999-12-31T23:59:59Z").unwrap_err(),
|
||||
super::Error::OutOfRange);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smoke_tests_parse() {
|
||||
assert_eq!(parse_rfc3339("1970-01-01T00:00:00Z").unwrap(),
|
||||
UNIX_EPOCH + Duration::new(0, 0));
|
||||
assert_eq!(parse_rfc3339("1970-01-01T00:00:01Z").unwrap(),
|
||||
UNIX_EPOCH + Duration::new(1, 0));
|
||||
assert_eq!(parse_rfc3339("2018-02-13T23:08:32Z").unwrap(),
|
||||
UNIX_EPOCH + Duration::new(1518563312, 0));
|
||||
assert_eq!(parse_rfc3339("2012-01-01T00:00:00Z").unwrap(),
|
||||
UNIX_EPOCH + Duration::new(1325376000, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smoke_tests_format() {
|
||||
assert_eq!(
|
||||
format_rfc3339(UNIX_EPOCH + Duration::new(0, 0)).to_string(),
|
||||
"1970-01-01T00:00:00Z");
|
||||
assert_eq!(
|
||||
format_rfc3339(UNIX_EPOCH + Duration::new(1, 0)).to_string(),
|
||||
"1970-01-01T00:00:01Z");
|
||||
assert_eq!(
|
||||
format_rfc3339(UNIX_EPOCH + Duration::new(1518563312, 0)).to_string(),
|
||||
"2018-02-13T23:08:32Z");
|
||||
assert_eq!(
|
||||
format_rfc3339(UNIX_EPOCH + Duration::new(1325376000, 0)).to_string(),
|
||||
"2012-01-01T00:00:00Z");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smoke_tests_format_millis() {
|
||||
assert_eq!(
|
||||
format_rfc3339_millis(UNIX_EPOCH +
|
||||
Duration::new(0, 0)).to_string(),
|
||||
"1970-01-01T00:00:00.000Z");
|
||||
assert_eq!(
|
||||
format_rfc3339_millis(UNIX_EPOCH +
|
||||
Duration::new(1518563312, 123_000_000)).to_string(),
|
||||
"2018-02-13T23:08:32.123Z");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smoke_tests_format_micros() {
|
||||
assert_eq!(
|
||||
format_rfc3339_micros(UNIX_EPOCH +
|
||||
Duration::new(0, 0)).to_string(),
|
||||
"1970-01-01T00:00:00.000000Z");
|
||||
assert_eq!(
|
||||
format_rfc3339_micros(UNIX_EPOCH +
|
||||
Duration::new(1518563312, 123_000_000)).to_string(),
|
||||
"2018-02-13T23:08:32.123000Z");
|
||||
assert_eq!(
|
||||
format_rfc3339_micros(UNIX_EPOCH +
|
||||
Duration::new(1518563312, 456_123_000)).to_string(),
|
||||
"2018-02-13T23:08:32.456123Z");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smoke_tests_format_nanos() {
|
||||
assert_eq!(
|
||||
format_rfc3339_nanos(UNIX_EPOCH +
|
||||
Duration::new(0, 0)).to_string(),
|
||||
"1970-01-01T00:00:00.000000000Z");
|
||||
assert_eq!(
|
||||
format_rfc3339_nanos(UNIX_EPOCH +
|
||||
Duration::new(1518563312, 123_000_000)).to_string(),
|
||||
"2018-02-13T23:08:32.123000000Z");
|
||||
assert_eq!(
|
||||
format_rfc3339_nanos(UNIX_EPOCH +
|
||||
Duration::new(1518563312, 789_456_123)).to_string(),
|
||||
"2018-02-13T23:08:32.789456123Z");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upper_bound() {
|
||||
let max = UNIX_EPOCH + Duration::new(max::SECONDS, 0);
|
||||
assert_eq!(parse_rfc3339(&max::TIMESTAMP).unwrap(), max);
|
||||
assert_eq!(format_rfc3339(max).to_string(), max::TIMESTAMP);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leap_second() {
|
||||
assert_eq!(parse_rfc3339("2016-12-31T23:59:60Z").unwrap(),
|
||||
UNIX_EPOCH + Duration::new(1483228799, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn first_731_days() {
|
||||
let year_start = 0; // 1970
|
||||
for day in 0.. (365 * 2 + 1) { // scan leap year and non-leap year
|
||||
let (s, time) = from_sec(year_start + day * 86400);
|
||||
assert_eq!(parse_rfc3339(&s).unwrap(), time);
|
||||
assert_eq!(format_rfc3339(time).to_string(), s);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_731_consecutive_days() {
|
||||
let year_start = 1325376000; // 2012
|
||||
for day in 0.. (365 * 2 + 1) { // scan leap year and non-leap year
|
||||
let (s, time) = from_sec(year_start + day * 86400);
|
||||
assert_eq!(parse_rfc3339(&s).unwrap(), time);
|
||||
assert_eq!(format_rfc3339(time).to_string(), s);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_86400_seconds() {
|
||||
let day_start = 1325376000;
|
||||
for second in 0..86400 { // scan leap year and non-leap year
|
||||
let (s, time) = from_sec(day_start + second);
|
||||
assert_eq!(parse_rfc3339(&s).unwrap(), time);
|
||||
assert_eq!(format_rfc3339(time).to_string(), s);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn random_past() {
|
||||
let upper = SystemTime::now().duration_since(UNIX_EPOCH).unwrap()
|
||||
.as_secs();
|
||||
for _ in 0..10000 {
|
||||
let sec = rand::thread_rng().gen_range(0, upper);
|
||||
let (s, time) = from_sec(sec);
|
||||
assert_eq!(parse_rfc3339(&s).unwrap(), time);
|
||||
assert_eq!(format_rfc3339(time).to_string(), s);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn random_wide_range() {
|
||||
for _ in 0..100000 {
|
||||
let sec = rand::thread_rng().gen_range(0, max::SECONDS);
|
||||
let (s, time) = from_sec(sec);
|
||||
assert_eq!(parse_rfc3339(&s).unwrap(), time);
|
||||
assert_eq!(format_rfc3339(time).to_string(), s);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn milliseconds() {
|
||||
assert_eq!(parse_rfc3339("1970-01-01T00:00:00.123Z").unwrap(),
|
||||
UNIX_EPOCH + Duration::new(0, 123000000));
|
||||
assert_eq!(format_rfc3339(UNIX_EPOCH + Duration::new(0, 123000000))
|
||||
.to_string(), "1970-01-01T00:00:00.123000000Z");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected="OutOfRange")]
|
||||
fn zero_month() {
|
||||
parse_rfc3339("1970-00-01T00:00:00Z").unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected="OutOfRange")]
|
||||
fn big_month() {
|
||||
parse_rfc3339("1970-32-01T00:00:00Z").unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected="OutOfRange")]
|
||||
fn zero_day() {
|
||||
parse_rfc3339("1970-01-00T00:00:00Z").unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected="OutOfRange")]
|
||||
fn big_day() {
|
||||
parse_rfc3339("1970-12-35T00:00:00Z").unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected="OutOfRange")]
|
||||
fn big_day2() {
|
||||
parse_rfc3339("1970-02-30T00:00:00Z").unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected="OutOfRange")]
|
||||
fn big_second() {
|
||||
parse_rfc3339("1970-12-30T00:00:78Z").unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected="OutOfRange")]
|
||||
fn big_minute() {
|
||||
parse_rfc3339("1970-12-30T00:78:00Z").unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected="OutOfRange")]
|
||||
fn big_hour() {
|
||||
parse_rfc3339("1970-12-30T24:00:00Z").unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn break_data() {
|
||||
for pos in 0.."2016-12-31T23:59:60Z".len() {
|
||||
let mut s = b"2016-12-31T23:59:60Z".to_vec();
|
||||
s[pos] = b'x';
|
||||
parse_rfc3339(from_utf8(&s).unwrap()).unwrap_err();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn weak_smoke_tests() {
|
||||
assert_eq!(parse_rfc3339_weak("1970-01-01 00:00:00").unwrap(),
|
||||
UNIX_EPOCH + Duration::new(0, 0));
|
||||
parse_rfc3339("1970-01-01 00:00:00").unwrap_err();
|
||||
|
||||
assert_eq!(parse_rfc3339_weak("1970-01-01 00:00:00.000123").unwrap(),
|
||||
UNIX_EPOCH + Duration::new(0, 123000));
|
||||
parse_rfc3339("1970-01-01 00:00:00.000123").unwrap_err();
|
||||
|
||||
assert_eq!(parse_rfc3339_weak("1970-01-01T00:00:00.000123").unwrap(),
|
||||
UNIX_EPOCH + Duration::new(0, 123000));
|
||||
parse_rfc3339("1970-01-01T00:00:00.000123").unwrap_err();
|
||||
|
||||
assert_eq!(parse_rfc3339_weak("1970-01-01 00:00:00.000123Z").unwrap(),
|
||||
UNIX_EPOCH + Duration::new(0, 123000));
|
||||
parse_rfc3339("1970-01-01 00:00:00.000123Z").unwrap_err();
|
||||
|
||||
assert_eq!(parse_rfc3339_weak("1970-01-01 00:00:00Z").unwrap(),
|
||||
UNIX_EPOCH + Duration::new(0, 0));
|
||||
parse_rfc3339("1970-01-01 00:00:00Z").unwrap_err();
|
||||
}
|
||||
}
|
|
@ -1,411 +0,0 @@
|
|||
use std::fmt;
|
||||
use std::str::Chars;
|
||||
use std::time::Duration;
|
||||
use std::error::Error as StdError;
|
||||
|
||||
quick_error! {
|
||||
/// Error parsing human-friendly duration
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub enum Error {
|
||||
/// Invalid character during parsing
|
||||
///
|
||||
/// More specifically anything that is not alphanumeric is prohibited
|
||||
///
|
||||
/// The field is an byte offset of the character in the string.
|
||||
InvalidCharacter(offset: usize) {
|
||||
display("invalid character at {}", offset)
|
||||
description("invalid character")
|
||||
}
|
||||
/// Non-numeric value where number is expected
|
||||
///
|
||||
/// This usually means that either time unit is broken into words,
|
||||
/// e.g. `m sec` instead of `msec`, or just number is omitted,
|
||||
/// for example `2 hours min` instead of `2 hours 1 min`
|
||||
///
|
||||
/// The field is an byte offset of the errorneous character
|
||||
/// in the string.
|
||||
NumberExpected(offset: usize) {
|
||||
display("expected number at {}", offset)
|
||||
description("expected number")
|
||||
}
|
||||
/// Unit in the number is not one of allowed units
|
||||
///
|
||||
/// See documentation of `parse_duration` for the list of supported
|
||||
/// time units.
|
||||
///
|
||||
/// The two fields are start and end (exclusive) of the slice from
|
||||
/// the original string, containing errorneous value
|
||||
UnknownUnit(start: usize, end: usize) {
|
||||
display("unknown unit at {}-{}", start, end)
|
||||
description("unknown unit")
|
||||
}
|
||||
/// The numeric value is too large
|
||||
///
|
||||
/// Usually this means value is too large to be useful. If user writes
|
||||
/// data in subsecond units, then the maximum is about 3k years. When
|
||||
/// using seconds, or larger units, the limit is even larger.
|
||||
NumberOverflow {
|
||||
display(self_) -> ("{}", self_.description())
|
||||
description("number is too large")
|
||||
}
|
||||
/// The value was an empty string (or consists only whitespace)
|
||||
Empty {
|
||||
display(self_) -> ("{}", self_.description())
|
||||
description("value was empty")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// A wrapper type that allows you to Display a Duration
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FormattedDuration(Duration);
|
||||
|
||||
trait OverflowOp: Sized {
|
||||
fn mul(self, other: Self) -> Result<Self, Error>;
|
||||
fn add(self, other: Self) -> Result<Self, Error>;
|
||||
}
|
||||
|
||||
impl OverflowOp for u64 {
|
||||
fn mul(self, other: Self) -> Result<Self, Error> {
|
||||
self.checked_mul(other).ok_or(Error::NumberOverflow)
|
||||
}
|
||||
fn add(self, other: Self) -> Result<Self, Error> {
|
||||
self.checked_add(other).ok_or(Error::NumberOverflow)
|
||||
}
|
||||
}
|
||||
|
||||
struct Parser<'a> {
|
||||
iter: Chars<'a>,
|
||||
src: &'a str,
|
||||
current: (u64, u64),
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
fn off(&self) -> usize {
|
||||
self.src.len() - self.iter.as_str().len()
|
||||
}
|
||||
|
||||
fn parse_first_char(&mut self) -> Result<Option<u64>, Error> {
|
||||
let off = self.off();
|
||||
for c in self.iter.by_ref() {
|
||||
match c {
|
||||
'0'...'9' => {
|
||||
return Ok(Some(c as u64 - '0' as u64));
|
||||
}
|
||||
c if c.is_whitespace() => continue,
|
||||
_ => {
|
||||
return Err(Error::NumberExpected(off));
|
||||
}
|
||||
}
|
||||
}
|
||||
return Ok(None);
|
||||
}
|
||||
fn parse_unit(&mut self, n: u64, start: usize, end: usize)
|
||||
-> Result<(), Error>
|
||||
{
|
||||
let (mut sec, nsec) = match &self.src[start..end] {
|
||||
"nanos" | "nsec" | "ns" => (0u64, n),
|
||||
"usec" | "us" => (0u64, try!(n.mul(1000))),
|
||||
"millis" | "msec" | "ms" => (0u64, try!(n.mul(1000_000))),
|
||||
"seconds" | "second" | "secs" | "sec" | "s" => (n, 0),
|
||||
"minutes" | "minute" | "min" | "mins" | "m"
|
||||
=> (try!(n.mul(60)), 0),
|
||||
"hours" | "hour" | "hr" | "hrs" | "h" => (try!(n.mul(3600)), 0),
|
||||
"days" | "day" | "d" => (try!(n.mul(86400)), 0),
|
||||
"weeks" | "week" | "w" => (try!(n.mul(86400*7)), 0),
|
||||
"months" | "month" | "M" => (try!(n.mul(2630016)), 0), // 30.44d
|
||||
"years" | "year" | "y" => (try!(n.mul(31557600)), 0), // 365.25d
|
||||
_ => return Err(Error::UnknownUnit(start, end)),
|
||||
};
|
||||
let mut nsec = try!(self.current.1.add(nsec));
|
||||
if nsec > 1000_000_000 {
|
||||
sec = try!(sec.add(nsec / 1000_000_000));
|
||||
nsec %= 1000_000_000;
|
||||
}
|
||||
sec = try!(self.current.0.add(sec));
|
||||
self.current = (sec, nsec);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse(mut self) -> Result<Duration, Error> {
|
||||
let mut n = try!(try!(self.parse_first_char()).ok_or(Error::Empty));
|
||||
'outer: loop {
|
||||
let mut off = self.off();
|
||||
while let Some(c) = self.iter.next() {
|
||||
match c {
|
||||
'0'...'9' => {
|
||||
n = try!(n.checked_mul(10)
|
||||
.and_then(|x| x.checked_add(c as u64 - '0' as u64))
|
||||
.ok_or(Error::NumberOverflow));
|
||||
}
|
||||
c if c.is_whitespace() => {}
|
||||
'a'...'z' | 'A'...'Z' => {
|
||||
break;
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::InvalidCharacter(off));
|
||||
}
|
||||
}
|
||||
off = self.off();
|
||||
}
|
||||
let start = off;
|
||||
let mut off = self.off();
|
||||
while let Some(c) = self.iter.next() {
|
||||
match c {
|
||||
'0'...'9' => {
|
||||
try!(self.parse_unit(n, start, off));
|
||||
n = c as u64 - '0' as u64;
|
||||
continue 'outer;
|
||||
}
|
||||
c if c.is_whitespace() => break,
|
||||
'a'...'z' | 'A'...'Z' => {}
|
||||
_ => {
|
||||
return Err(Error::InvalidCharacter(off));
|
||||
}
|
||||
}
|
||||
off = self.off();
|
||||
}
|
||||
try!(self.parse_unit(n, start, off));
|
||||
n = match try!(self.parse_first_char()) {
|
||||
Some(n) => n,
|
||||
None => return Ok(
|
||||
Duration::new(self.current.0, self.current.1 as u32)),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Parse duration object `1hour 12min 5s`
|
||||
///
|
||||
/// The duration object is a concatenation of time spans. Where each time
|
||||
/// span is an integer number and a suffix. Supported suffixes:
|
||||
///
|
||||
/// * `nsec`, `ns` -- microseconds
|
||||
/// * `usec`, `us` -- microseconds
|
||||
/// * `msec`, `ms` -- milliseconds
|
||||
/// * `seconds`, `second`, `sec`, `s`
|
||||
/// * `minutes`, `minute`, `min`, `m`
|
||||
/// * `hours`, `hour`, `hr`, `h`
|
||||
/// * `days`, `day`, `d`
|
||||
/// * `weeks`, `week`, `w`
|
||||
/// * `months`, `month`, `M` -- defined as 30.44 days
|
||||
/// * `years`, `year`, `y` -- defined as 365.25 days
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::time::Duration;
|
||||
/// use humantime::parse_duration;
|
||||
///
|
||||
/// assert_eq!(parse_duration("2h 37min"), Ok(Duration::new(9420, 0)));
|
||||
/// assert_eq!(parse_duration("32ms"), Ok(Duration::new(0, 32_000_000)));
|
||||
/// ```
|
||||
pub fn parse_duration(s: &str) -> Result<Duration, Error> {
|
||||
Parser {
|
||||
iter: s.chars(),
|
||||
src: s,
|
||||
current: (0, 0),
|
||||
}.parse()
|
||||
}
|
||||
|
||||
/// Formats duration into a human-readable string
|
||||
///
|
||||
/// Note: this format is guaranteed to have same value when using
|
||||
/// parse_duration, but we can change some details of the exact composition
|
||||
/// of the value.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::time::Duration;
|
||||
/// use humantime::format_duration;
|
||||
///
|
||||
/// let val1 = Duration::new(9420, 0);
|
||||
/// assert_eq!(format_duration(val1).to_string(), "2h 37m");
|
||||
/// let val2 = Duration::new(0, 32_000_000);
|
||||
/// assert_eq!(format_duration(val2).to_string(), "32ms");
|
||||
/// ```
|
||||
pub fn format_duration(val: Duration) -> FormattedDuration {
|
||||
FormattedDuration(val)
|
||||
}
|
||||
|
||||
fn item_plural(f: &mut fmt::Formatter, started: &mut bool,
|
||||
name: &str, value: u64)
|
||||
-> fmt::Result
|
||||
{
|
||||
if value > 0 {
|
||||
if *started {
|
||||
f.write_str(" ")?;
|
||||
}
|
||||
write!(f, "{}{}", value, name)?;
|
||||
if value > 1 {
|
||||
f.write_str("s")?;
|
||||
}
|
||||
*started = true;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn item(f: &mut fmt::Formatter, started: &mut bool, name: &str, value: u32)
|
||||
-> fmt::Result
|
||||
{
|
||||
if value > 0 {
|
||||
if *started {
|
||||
f.write_str(" ")?;
|
||||
}
|
||||
write!(f, "{}{}", value, name)?;
|
||||
*started = true;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl fmt::Display for FormattedDuration {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let secs = self.0.as_secs();
|
||||
let nanos = self.0.subsec_nanos();
|
||||
|
||||
if secs == 0 && nanos == 0 {
|
||||
f.write_str("0s")?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let years = secs / 31557600; // 365.25d
|
||||
let ydays = secs % 31557600;
|
||||
let months = ydays / 2630016; // 30.44d
|
||||
let mdays = ydays % 2630016;
|
||||
let days = mdays / 86400;
|
||||
let day_secs = mdays % 86400;
|
||||
let hours = day_secs / 3600;
|
||||
let minutes = day_secs % 3600 / 60;
|
||||
let seconds = day_secs % 60;
|
||||
|
||||
let millis = nanos / 1_000_000;
|
||||
let micros = nanos / 1000 % 1000;
|
||||
let nanosec = nanos % 1000;
|
||||
|
||||
let ref mut started = false;
|
||||
item_plural(f, started, "year", years)?;
|
||||
item_plural(f, started, "month", months)?;
|
||||
item_plural(f, started, "day", days)?;
|
||||
item(f, started, "h", hours as u32)?;
|
||||
item(f, started, "m", minutes as u32)?;
|
||||
item(f, started, "s", seconds as u32)?;
|
||||
item(f, started, "ms", millis)?;
|
||||
item(f, started, "us", micros)?;
|
||||
item(f, started, "ns", nanosec)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
extern crate rand;
|
||||
|
||||
use std::time::Duration;
|
||||
use self::rand::Rng;
|
||||
use super::{parse_duration, format_duration};
|
||||
use super::Error;
|
||||
|
||||
#[test]
|
||||
fn test_units() {
|
||||
assert_eq!(parse_duration("17nsec"), Ok(Duration::new(0, 17)));
|
||||
assert_eq!(parse_duration("17nanos"), Ok(Duration::new(0, 17)));
|
||||
assert_eq!(parse_duration("33ns"), Ok(Duration::new(0, 33)));
|
||||
assert_eq!(parse_duration("3usec"), Ok(Duration::new(0, 3000)));
|
||||
assert_eq!(parse_duration("78us"), Ok(Duration::new(0, 78000)));
|
||||
assert_eq!(parse_duration("31msec"), Ok(Duration::new(0, 31000000)));
|
||||
assert_eq!(parse_duration("31millis"), Ok(Duration::new(0, 31000000)));
|
||||
assert_eq!(parse_duration("6ms"), Ok(Duration::new(0, 6000000)));
|
||||
assert_eq!(parse_duration("3000s"), Ok(Duration::new(3000, 0)));
|
||||
assert_eq!(parse_duration("300sec"), Ok(Duration::new(300, 0)));
|
||||
assert_eq!(parse_duration("300secs"), Ok(Duration::new(300, 0)));
|
||||
assert_eq!(parse_duration("50seconds"), Ok(Duration::new(50, 0)));
|
||||
assert_eq!(parse_duration("1second"), Ok(Duration::new(1, 0)));
|
||||
assert_eq!(parse_duration("100m"), Ok(Duration::new(6000, 0)));
|
||||
assert_eq!(parse_duration("12min"), Ok(Duration::new(720, 0)));
|
||||
assert_eq!(parse_duration("12mins"), Ok(Duration::new(720, 0)));
|
||||
assert_eq!(parse_duration("1minute"), Ok(Duration::new(60, 0)));
|
||||
assert_eq!(parse_duration("7minutes"), Ok(Duration::new(420, 0)));
|
||||
assert_eq!(parse_duration("2h"), Ok(Duration::new(7200, 0)));
|
||||
assert_eq!(parse_duration("7hr"), Ok(Duration::new(25200, 0)));
|
||||
assert_eq!(parse_duration("7hrs"), Ok(Duration::new(25200, 0)));
|
||||
assert_eq!(parse_duration("1hour"), Ok(Duration::new(3600, 0)));
|
||||
assert_eq!(parse_duration("24hours"), Ok(Duration::new(86400, 0)));
|
||||
assert_eq!(parse_duration("1day"), Ok(Duration::new(86400, 0)));
|
||||
assert_eq!(parse_duration("2days"), Ok(Duration::new(172800, 0)));
|
||||
assert_eq!(parse_duration("365d"), Ok(Duration::new(31536000, 0)));
|
||||
assert_eq!(parse_duration("1week"), Ok(Duration::new(604800, 0)));
|
||||
assert_eq!(parse_duration("7weeks"), Ok(Duration::new(4233600, 0)));
|
||||
assert_eq!(parse_duration("52w"), Ok(Duration::new(31449600, 0)));
|
||||
assert_eq!(parse_duration("1month"), Ok(Duration::new(2630016, 0)));
|
||||
assert_eq!(parse_duration("3months"), Ok(Duration::new(3*2630016, 0)));
|
||||
assert_eq!(parse_duration("12M"), Ok(Duration::new(31560192, 0)));
|
||||
assert_eq!(parse_duration("1year"), Ok(Duration::new(31557600, 0)));
|
||||
assert_eq!(parse_duration("7years"), Ok(Duration::new(7*31557600, 0)));
|
||||
assert_eq!(parse_duration("17y"), Ok(Duration::new(536479200, 0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_combo() {
|
||||
assert_eq!(parse_duration("20 min 17 nsec "), Ok(Duration::new(1200, 17)));
|
||||
assert_eq!(parse_duration("2h 15m"), Ok(Duration::new(8100, 0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_86400_seconds() {
|
||||
for second in 0..86400 { // scan leap year and non-leap year
|
||||
let d = Duration::new(second, 0);
|
||||
assert_eq!(d,
|
||||
parse_duration(&format_duration(d).to_string()).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn random_second() {
|
||||
for _ in 0..10000 {
|
||||
let sec = rand::thread_rng().gen_range(0, 253370764800);
|
||||
let d = Duration::new(sec, 0);
|
||||
assert_eq!(d,
|
||||
parse_duration(&format_duration(d).to_string()).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn random_any() {
|
||||
for _ in 0..10000 {
|
||||
let sec = rand::thread_rng().gen_range(0, 253370764800);
|
||||
let nanos = rand::thread_rng().gen_range(0, 1_000_000_000);
|
||||
let d = Duration::new(sec, nanos);
|
||||
assert_eq!(d,
|
||||
parse_duration(&format_duration(d).to_string()).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_overlow() {
|
||||
// Overflow on subseconds is earlier because of how we do conversion
|
||||
// we could fix it, but I don't see any good reason for this
|
||||
assert_eq!(parse_duration("100000000000000000000ns"),
|
||||
Err(Error::NumberOverflow));
|
||||
assert_eq!(parse_duration("100000000000000000us"),
|
||||
Err(Error::NumberOverflow));
|
||||
assert_eq!(parse_duration("100000000000000ms"),
|
||||
Err(Error::NumberOverflow));
|
||||
|
||||
assert_eq!(parse_duration("100000000000000000000s"),
|
||||
Err(Error::NumberOverflow));
|
||||
assert_eq!(parse_duration("10000000000000000000m"),
|
||||
Err(Error::NumberOverflow));
|
||||
assert_eq!(parse_duration("1000000000000000000h"),
|
||||
Err(Error::NumberOverflow));
|
||||
assert_eq!(parse_duration("100000000000000000d"),
|
||||
Err(Error::NumberOverflow));
|
||||
assert_eq!(parse_duration("10000000000000000w"),
|
||||
Err(Error::NumberOverflow));
|
||||
assert_eq!(parse_duration("1000000000000000M"),
|
||||
Err(Error::NumberOverflow));
|
||||
assert_eq!(parse_duration("10000000000000y"),
|
||||
Err(Error::NumberOverflow));
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
//! Human-friendly time parser and formatter
|
||||
//!
|
||||
//! Features:
|
||||
//!
|
||||
//! * Parses durations in free form like `15days 2min 2s`
|
||||
//! * Formats durations in similar form `2years 2min 12us`
|
||||
//! * Parses and formats timestamp in `rfc3339` format: `2018-01-01T12:53:00Z`
|
||||
//! * Parses timestamps in a weaker format: `2018-01-01 12:53:00`
|
||||
//!
|
||||
//! Timestamp parsing/formatting is super-fast because format is basically
|
||||
//! fixed.
|
||||
//!
|
||||
//! See [serde-humantime] for serde integration.
|
||||
//!
|
||||
//! [serde-humantime]: https://docs.rs/serde-humantime/0.1.1/serde_humantime/
|
||||
#![warn(missing_debug_implementations)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#[macro_use] extern crate quick_error;
|
||||
|
||||
mod duration;
|
||||
mod wrapper;
|
||||
mod date;
|
||||
|
||||
pub use duration::{parse_duration, Error as DurationError};
|
||||
pub use duration::{format_duration, FormattedDuration};
|
||||
pub use wrapper::{Duration, Timestamp};
|
||||
pub use date::{parse_rfc3339, parse_rfc3339_weak, Error as TimestampError};
|
||||
pub use date::{
|
||||
format_rfc3339, format_rfc3339_micros, format_rfc3339_millis, format_rfc3339_nanos,
|
||||
format_rfc3339_seconds,
|
||||
};
|
||||
pub use date::{Rfc3339Timestamp};
|
|
@ -1,107 +0,0 @@
|
|||
use std::str::FromStr;
|
||||
use std::ops::Deref;
|
||||
use std::fmt;
|
||||
use std::time::{Duration as StdDuration, SystemTime};
|
||||
|
||||
use duration::{self, parse_duration, format_duration};
|
||||
use date::{self, parse_rfc3339_weak, format_rfc3339};
|
||||
|
||||
/// A wrapper for duration that has `FromStr` implementation
|
||||
///
|
||||
/// This is useful if you want to use it somewhere where `FromStr` is
|
||||
/// expected.
|
||||
///
|
||||
/// See `parse_duration` for the description of the format.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use std::time::Duration;
|
||||
/// let x: Duration;
|
||||
/// x = "12h 5min 2ns".parse::<humantime::Duration>().unwrap().into();
|
||||
/// assert_eq!(x, Duration::new(12*3600 + 5*60, 2))
|
||||
/// ```
|
||||
///
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||
pub struct Duration(StdDuration);
|
||||
|
||||
/// A wrapper for SystemTime that has `FromStr` implementation
|
||||
///
|
||||
/// This is useful if you want to use it somewhere where `FromStr` is
|
||||
/// expected.
|
||||
///
|
||||
/// See `parse_rfc3339_weak` for the description of the format. The "weak"
|
||||
/// format is used as it's more pemissive for human input as this is the
|
||||
/// expected use of the type (e.g. command-line parsing).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use std::time::SystemTime;
|
||||
/// let x: SystemTime;
|
||||
/// x = "2018-02-16T00:31:37Z".parse::<humantime::Timestamp>().unwrap().into();
|
||||
/// assert_eq!(humantime::format_rfc3339(x).to_string(), "2018-02-16T00:31:37Z");
|
||||
/// ```
|
||||
///
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct Timestamp(SystemTime);
|
||||
|
||||
impl AsRef<StdDuration> for Duration {
|
||||
fn as_ref(&self) -> &StdDuration { &self.0 }
|
||||
}
|
||||
|
||||
impl Deref for Duration {
|
||||
type Target = StdDuration;
|
||||
fn deref(&self) -> &StdDuration { &self.0 }
|
||||
}
|
||||
|
||||
impl Into<StdDuration> for Duration {
|
||||
fn into(self) -> StdDuration { self.0 }
|
||||
}
|
||||
|
||||
impl From<StdDuration> for Duration {
|
||||
fn from(dur: StdDuration) -> Duration { Duration(dur) }
|
||||
}
|
||||
|
||||
impl FromStr for Duration {
|
||||
type Err = duration::Error;
|
||||
fn from_str(s: &str) -> Result<Duration, Self::Err> {
|
||||
parse_duration(s).map(Duration)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Duration {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
format_duration(self.0).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<SystemTime> for Timestamp {
|
||||
fn as_ref(&self) -> &SystemTime { &self.0 }
|
||||
}
|
||||
|
||||
impl Deref for Timestamp {
|
||||
type Target = SystemTime;
|
||||
fn deref(&self) -> &SystemTime { &self.0 }
|
||||
}
|
||||
|
||||
impl Into<SystemTime> for Timestamp {
|
||||
fn into(self) -> SystemTime { self.0 }
|
||||
}
|
||||
|
||||
impl From<SystemTime> for Timestamp {
|
||||
fn from(dur: SystemTime) -> Timestamp { Timestamp(dur) }
|
||||
}
|
||||
|
||||
impl FromStr for Timestamp {
|
||||
type Err = date::Error;
|
||||
fn from_str(s: &str) -> Result<Timestamp, Self::Err> {
|
||||
parse_rfc3339_weak(s).map(Timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Timestamp {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
format_rfc3339(self.0).fmt(f)
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
commands:
|
||||
|
||||
cargo: !Command
|
||||
description: Run any cargo command
|
||||
container: ubuntu
|
||||
run: [cargo]
|
||||
|
||||
make: !Command
|
||||
description: Build the library
|
||||
container: ubuntu
|
||||
run: [cargo, build]
|
||||
|
||||
test64: !Command
|
||||
description: Test the 64bit library
|
||||
container: ubuntu
|
||||
environ: { RUST_BACKTRACE: 1 }
|
||||
run: [cargo, test]
|
||||
|
||||
test32: !Command
|
||||
description: Test the 32bit library
|
||||
container: ubuntu32
|
||||
environ: { RUST_BACKTRACE: 1 }
|
||||
run: [cargo, test]
|
||||
|
||||
test: !Command
|
||||
description: Test the 64bit library
|
||||
container: ubuntu
|
||||
environ: { RUST_BACKTRACE: 1 }
|
||||
prerequisites: [test64, test32]
|
||||
run: [echo, okay]
|
||||
|
||||
bench: !Command
|
||||
description: Run benchmarks
|
||||
container: bench
|
||||
environ: { RUST_BACKTRACE: 1 }
|
||||
run: [cargo, bench]
|
||||
|
||||
_bulk: !Command
|
||||
description: Run `bulk` command (for version bookkeeping)
|
||||
container: ubuntu
|
||||
run: [bulk]
|
||||
|
||||
containers:
|
||||
|
||||
ubuntu:
|
||||
setup:
|
||||
- !Ubuntu xenial
|
||||
- !UbuntuUniverse
|
||||
- !Install [ca-certificates, build-essential, vim]
|
||||
|
||||
- !TarInstall
|
||||
url: "https://static.rust-lang.org/dist/rust-1.24.0-x86_64-unknown-linux-gnu.tar.gz"
|
||||
script: "./install.sh --prefix=/usr \
|
||||
--components=rustc,rust-std-x86_64-unknown-linux-gnu,cargo"
|
||||
- &bulk !Tar
|
||||
url: "https://github.com/tailhook/bulk/releases/download/v0.4.10/bulk-v0.4.10.tar.gz"
|
||||
sha256: 481513f8a0306a9857d045497fb5b50b50a51e9ff748909ecf7d2bda1de275ab
|
||||
path: /
|
||||
|
||||
environ:
|
||||
HOME: /work/target
|
||||
USER: pc
|
||||
|
||||
ubuntu32:
|
||||
setup:
|
||||
- !UbuntuRelease
|
||||
codename: xenial
|
||||
arch: i386
|
||||
- !UbuntuUniverse
|
||||
- !Install [ca-certificates, build-essential, vim]
|
||||
|
||||
- !TarInstall
|
||||
url: "https://static.rust-lang.org/dist/rust-1.24.0-i686-unknown-linux-gnu.tar.gz"
|
||||
script: "./install.sh --prefix=/usr \
|
||||
--components=rustc,rust-std-i686-unknown-linux-gnu,cargo"
|
||||
|
||||
environ:
|
||||
HOME: /work/target
|
||||
USER: pc
|
||||
|
||||
bench:
|
||||
setup:
|
||||
- !Ubuntu xenial
|
||||
- !Install [ca-certificates, wget, build-essential]
|
||||
- !TarInstall
|
||||
url: https://static.rust-lang.org/dist/rust-nightly-x86_64-unknown-linux-gnu.tar.gz
|
||||
script: |
|
||||
./install.sh --prefix=/usr \
|
||||
--components=rustc,rust-std-x86_64-unknown-linux-gnu,cargo
|
||||
environ:
|
||||
HOME: /work/target
|
||||
USER: pc
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"105033ac7475000fc0d13cb51973a6fe439a167d05ef79d7db1aa0f82618dedd","benches/avif_benchmark.rs":"e4bdd69c7b434448ef7080bbf488b2f64c2c88121fe1c1f7e9d9c8def3d648ae","src/boxes.rs":"756a3d3638d91fc8e2e58a3e41e663b46d6381539ec12b2e7b6980b205ef4ed2","src/lib.rs":"d5d884e46f7f70301efda1388e6b0c9b0ff7bd0de3eccc0276c45ea9c0c1c055","src/macros.rs":"76c840f9299797527fe71aa5b378ffb01312767372b45cc62deddb19775400ae","src/tests.rs":"20b47bac38d76f0c408485cd980c4bd16cf3f0d3254fff273a16f20c54362ceb","tests/bug-1661347.avif":"a4741189d897401c746492d780bccf4c42dddde8f46d01a791f9656aac2ab164","tests/corrupt/bad-ipma-flags.avif":"ecde7997b97db1910b9dcc7ca8e3c8957da0e83681ea9008c66dc9f12b78ad19","tests/corrupt/bad-ipma-version.avif":"7f9a1a0b4ebbf8d800d22eaae5ff78970cc6b811317db6c1467c6883952b7c9b","tests/corrupt/bug-1655846.avif":"e0a5a06225800fadf05f5352503a4cec11af73eef705c43b4acab5f4a99dea50","tests/corrupt/bug-1661347.avif":"31c26561e1d9eafb60f7c5968b82a0859d203d73f17f26b29276256acee12966","tests/kodim-extents.avif":"e4de6d124535206c3daca797e64cccc6a5b439f93733af52a95b1e82d581a78b","tests/overflow.rs":"16b591d8def1a155b3b997622f6ea255536870d99c3d8f97c51755b77a50de3c","tests/public.rs":"e6365f9d992a318f7b308cd7ba9cd8b78df84b8893af4baf0c1edb5f39342ed9"},"package":null}
|
||||
{"files":{"Cargo.toml":"5f772c84750dde85555ebe9e01e30266c515402b44ac2735f183a5b143c43196","src/boxes.rs":"7f989f8e91d173f323db22a8748ea911dd143c6304f66ccaf6ebc97d68ca5536","src/lib.rs":"4d4152c72ef49ced0a467879c51ff9e2430f704cd13a8eea4dc891b5f5843280","src/macros.rs":"76c840f9299797527fe71aa5b378ffb01312767372b45cc62deddb19775400ae","src/tests.rs":"6c1b8822410f5410d991f553925d3591f9c7ce41891191da8b3da62e783ebb02","tests/bug-1655846.avif":"e0a5a06225800fadf05f5352503a4cec11af73eef705c43b4acab5f4a99dea50","tests/bug-1661347.avif":"31c26561e1d9eafb60f7c5968b82a0859d203d73f17f26b29276256acee12966","tests/overflow.rs":"16b591d8def1a155b3b997622f6ea255536870d99c3d8f97c51755b77a50de3c","tests/public.rs":"5ff2282b0f84f55e25e18dcca9acc5bffde806d885c897354e5f65292f295557"},"package":null}
|
|
@ -27,8 +27,7 @@ travis-ci = { repository = "https://github.com/mozilla/mp4parse-rust" }
|
|||
[dependencies]
|
||||
byteorder = "1.2.1"
|
||||
bitreader = { version = "0.3.2" }
|
||||
env_logger = "0.7.1"
|
||||
fallible_collections = { version = "0.3", features = ["std_io"] }
|
||||
fallible_collections = { version = "0.2", features = ["std_io"] }
|
||||
hashbrown = "0.9"
|
||||
num-traits = "=0.2.10"
|
||||
log = "0.4"
|
||||
|
@ -36,13 +35,5 @@ static_assertions = "1.1.0"
|
|||
|
||||
[dev-dependencies]
|
||||
test-assembler = "0.1.2"
|
||||
env_logger = "0.7.1"
|
||||
walkdir = "2.3.1"
|
||||
criterion = "0.3"
|
||||
|
||||
[[bench]]
|
||||
name = "avif_benchmark"
|
||||
harness = false
|
||||
|
||||
# See https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options
|
||||
[lib]
|
||||
bench = false
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
extern crate criterion;
|
||||
extern crate mp4parse as mp4;
|
||||
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use std::fs::File;
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
c.bench_function("avif_largest", |b| b.iter(|| avif_largest()));
|
||||
}
|
||||
|
||||
criterion_group!(benches, criterion_benchmark);
|
||||
criterion_main!(benches);
|
||||
|
||||
fn avif_largest() {
|
||||
let input = &mut File::open(
|
||||
"av1-avif/testFiles/Netflix/avif/cosmos_frame05000_yuv444_12bpc_bt2020_pq_qlossless.avif",
|
||||
)
|
||||
.expect("Unknown file");
|
||||
assert!(mp4::read_avif(input).is_ok());
|
||||
}
|
|
@ -97,7 +97,6 @@ impl PartialEq<&[u8; 4]> for FourCC {
|
|||
}
|
||||
}
|
||||
|
||||
#[deny(unreachable_patterns)]
|
||||
box_database!(
|
||||
FileTypeBox 0x6674_7970, // "ftyp"
|
||||
MediaDataBox 0x6d64_6174, // "mdat"
|
||||
|
@ -115,13 +114,6 @@ box_database!(
|
|||
MediaHeaderBox 0x6d64_6864, // "mdhd"
|
||||
HandlerBox 0x6864_6c72, // "hdlr"
|
||||
MediaInformationBox 0x6d69_6e66, // "minf"
|
||||
ImageReferenceBox 0x6972_6566, // "iref"
|
||||
ImagePropertiesBox 0x6970_7270, // "iprp"
|
||||
ItemPropertyContainerBox 0x6970_636f, // "ipco"
|
||||
ItemPropertyAssociationBox 0x6970_6d61, // "ipma"
|
||||
ColorInformationBox 0x636f_6c72, // "colr"
|
||||
PixelInformationBox 0x7069_7869, // "pixi"
|
||||
AuxiliaryTypeProperty 0x6175_7843, // "auxC"
|
||||
SampleTableBox 0x7374_626c, // "stbl"
|
||||
SampleDescriptionBox 0x7374_7364, // "stsd"
|
||||
TimeToSampleBox 0x7374_7473, // "stts"
|
||||
|
@ -158,8 +150,8 @@ box_database!(
|
|||
SchemeTypeBox 0x7363_686d, // "schm"
|
||||
MP3AudioSampleEntry 0x2e6d_7033, // ".mp3" - from F4V.
|
||||
CompositionOffsetBox 0x6374_7473, // "ctts"
|
||||
LPCMAudioSampleEntry 0x6c70_636d, // "lpcm" - quicktime atom
|
||||
ALACSpecificBox 0x616c_6163, // "alac" - Also used by ALACSampleEntry
|
||||
LPCMAudioSampleEntry 0x6C70_636D, // "lpcm" - quicktime atom
|
||||
ALACSpecificBox 0x616C_6163, // "alac" - Also used by ALACSampleEntry
|
||||
UuidBox 0x7575_6964, // "uuid"
|
||||
MetadataBox 0x6d65_7461, // "meta"
|
||||
MetadataHeaderBox 0x6d68_6472, // "mhdr"
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -7,6 +7,7 @@
|
|||
|
||||
use super::read_mp4;
|
||||
use super::Error;
|
||||
use super::MediaContext;
|
||||
use fallible_collections::TryRead as _;
|
||||
|
||||
use std::convert::TryInto as _;
|
||||
|
@ -194,7 +195,8 @@ fn read_truncated_ftyp() {
|
|||
.B32(0) // minor version
|
||||
.append_bytes(b"isom")
|
||||
});
|
||||
match read_mp4(&mut stream) {
|
||||
let mut context = MediaContext::new();
|
||||
match read_mp4(&mut stream, &mut context) {
|
||||
Err(Error::UnexpectedEOF) => (),
|
||||
Ok(_) => panic!("expected an error result"),
|
||||
_ => panic!("expected a different error result"),
|
||||
|
@ -344,7 +346,7 @@ fn read_mdhd_invalid_timescale() {
|
|||
assert_eq!(stream.head.name, BoxType::MediaHeaderBox);
|
||||
assert_eq!(stream.head.size, 44);
|
||||
let r = super::parse_mdhd(&mut stream, &mut super::Track::new(0));
|
||||
assert!(r.is_err());
|
||||
assert_eq!(r.is_err(), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -385,7 +387,7 @@ fn read_mvhd_invalid_timescale() {
|
|||
assert_eq!(stream.head.name, BoxType::MovieHeaderBox);
|
||||
assert_eq!(stream.head.size, 120);
|
||||
let r = super::parse_mvhd(&mut stream);
|
||||
assert!(r.is_err());
|
||||
assert_eq!(r.is_err(), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1247,8 +1249,9 @@ fn read_invalid_pssh() {
|
|||
let mut stream = make_box(BoxSize::Auto, b"moov", |s| s.append_bytes(pssh.as_slice()));
|
||||
let mut iter = super::BoxIter::new(&mut stream);
|
||||
let mut stream = iter.next_box().unwrap().unwrap();
|
||||
let mut context = super::MediaContext::new();
|
||||
|
||||
match super::read_moov(&mut stream) {
|
||||
match super::read_moov(&mut stream, &mut context) {
|
||||
Err(Error::InvalidData(s)) => assert_eq!(s, "read_buf size exceeds BUF_SIZE_LIMIT"),
|
||||
_ => panic!("unexpected result with invalid descriptor"),
|
||||
}
|
||||
|
|
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
|
@ -4,7 +4,6 @@
|
|||
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
extern crate mp4parse as mp4;
|
||||
|
||||
use mp4::Error;
|
||||
use std::convert::TryInto;
|
||||
use std::fs::File;
|
||||
use std::io::{Cursor, Read};
|
||||
|
@ -29,15 +28,10 @@ static AUDIO_EME_CBCS_MP4: &str = "tests/bipbop_cbcs_audio_init.mp4";
|
|||
static VIDEO_EME_CBCS_MP4: &str = "tests/bipbop_cbcs_video_init.mp4";
|
||||
static VIDEO_AV1_MP4: &str = "tests/tiny_av1.mp4";
|
||||
static IMAGE_AVIF: &str = "av1-avif/testFiles/Microsoft/Monochrome.avif";
|
||||
static IMAGE_AVIF_EXTENTS: &str = "tests/kodim-extents.avif";
|
||||
static IMAGE_AVIF_ALPHA: &str = "tests/bug-1661347.avif";
|
||||
static IMAGE_AVIF_CORRUPT: &str = "tests/corrupt/bug-1655846.avif";
|
||||
static IMAGE_AVIF_CORRUPT_2: &str = "tests/corrupt/bug-1661347.avif";
|
||||
static IMAGE_AVIF_CORRUPT_3: &str = "tests/corrupt/bad-ipma-version.avif";
|
||||
static IMAGE_AVIF_CORRUPT_4: &str = "tests/corrupt/bad-ipma-flags.avif";
|
||||
static IMAGE_AVIF_CORRUPT: &str = "tests/bug-1655846.avif";
|
||||
static IMAGE_AVIF_CORRUPT_2: &str = "tests/bug-1661347.avif";
|
||||
static IMAGE_AVIF_GRID: &str = "av1-avif/testFiles/Microsoft/Summer_in_Tomsk_720p_5x4_grid.avif";
|
||||
static AVIF_TEST_DIRS: &[&str] = &["tests", "av1-avif/testFiles"];
|
||||
static AVIF_CORRUPT_IMAGES: &str = "tests/corrupt";
|
||||
static AVIF_TEST_DIR: &str = "av1-avif/testFiles";
|
||||
|
||||
// Adapted from https://github.com/GuillaumeGomez/audio-video-metadata/blob/9dff40f565af71d5502e03a2e78ae63df95cfd40/src/metadata.rs#L53
|
||||
#[test]
|
||||
|
@ -48,7 +42,8 @@ fn public_api() {
|
|||
fd.read_to_end(&mut buf).expect("File error");
|
||||
|
||||
let mut c = Cursor::new(&buf);
|
||||
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
|
||||
let mut context = mp4::MediaContext::new();
|
||||
mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
|
||||
assert_eq!(context.timescale, Some(mp4::MediaTimeScale(1000)));
|
||||
for track in context.tracks {
|
||||
match track.track_type {
|
||||
|
@ -168,7 +163,8 @@ fn public_metadata() {
|
|||
fd.read_to_end(&mut buf).expect("File error");
|
||||
|
||||
let mut c = Cursor::new(&buf);
|
||||
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
|
||||
let mut context = mp4::MediaContext::new();
|
||||
mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
|
||||
let udta = context
|
||||
.userdata
|
||||
.expect("didn't find udta")
|
||||
|
@ -235,7 +231,8 @@ fn public_metadata_gnre() {
|
|||
fd.read_to_end(&mut buf).expect("File error");
|
||||
|
||||
let mut c = Cursor::new(&buf);
|
||||
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
|
||||
let mut context = mp4::MediaContext::new();
|
||||
mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
|
||||
let udta = context
|
||||
.userdata
|
||||
.expect("didn't find udta")
|
||||
|
@ -303,7 +300,8 @@ fn public_audio_tenc() {
|
|||
fd.read_to_end(&mut buf).expect("File error");
|
||||
|
||||
let mut c = Cursor::new(&buf);
|
||||
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
|
||||
let mut context = mp4::MediaContext::new();
|
||||
mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
|
||||
for track in context.tracks {
|
||||
let stsd = track.stsd.expect("expected an stsd");
|
||||
let a = match stsd.descriptions.first().expect("expected a SampleEntry") {
|
||||
|
@ -361,7 +359,8 @@ fn public_video_cenc() {
|
|||
fd.read_to_end(&mut buf).expect("File error");
|
||||
|
||||
let mut c = Cursor::new(&buf);
|
||||
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
|
||||
let mut context = mp4::MediaContext::new();
|
||||
mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
|
||||
for track in context.tracks {
|
||||
let stsd = track.stsd.expect("expected an stsd");
|
||||
let v = match stsd.descriptions.first().expect("expected a SampleEntry") {
|
||||
|
@ -433,7 +432,8 @@ fn public_audio_cbcs() {
|
|||
fd.read_to_end(&mut buf).expect("File error");
|
||||
|
||||
let mut c = Cursor::new(&buf);
|
||||
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
|
||||
let mut context = mp4::MediaContext::new();
|
||||
mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
|
||||
for track in context.tracks {
|
||||
let stsd = track.stsd.expect("expected an stsd");
|
||||
assert_eq!(stsd.descriptions.len(), 2);
|
||||
|
@ -515,7 +515,8 @@ fn public_video_cbcs() {
|
|||
fd.read_to_end(&mut buf).expect("File error");
|
||||
|
||||
let mut c = Cursor::new(&buf);
|
||||
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
|
||||
let mut context = mp4::MediaContext::new();
|
||||
mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
|
||||
for track in context.tracks {
|
||||
let stsd = track.stsd.expect("expected an stsd");
|
||||
assert_eq!(stsd.descriptions.len(), 2);
|
||||
|
@ -574,7 +575,8 @@ fn public_video_av1() {
|
|||
fd.read_to_end(&mut buf).expect("File error");
|
||||
|
||||
let mut c = Cursor::new(&buf);
|
||||
let context = mp4::read_mp4(&mut c).expect("read_mp4 failed");
|
||||
let mut context = mp4::MediaContext::new();
|
||||
mp4::read_mp4(&mut c, &mut context).expect("read_mp4 failed");
|
||||
for track in context.tracks {
|
||||
// track part
|
||||
assert_eq!(track.duration, Some(mp4::TrackScaledTime(512, 0)));
|
||||
|
@ -620,92 +622,54 @@ fn public_video_av1() {
|
|||
|
||||
#[test]
|
||||
fn public_avif_primary_item() {
|
||||
let context = &mut mp4::AvifContext::new();
|
||||
let input = &mut File::open(IMAGE_AVIF).expect("Unknown file");
|
||||
let context = mp4::read_avif(input).expect("read_avif failed");
|
||||
assert_eq!(context.primary_item().len(), 6979);
|
||||
assert_eq!(context.primary_item()[0..4], [0x12, 0x00, 0x0a, 0x0a]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_avif_primary_item_split_extents() {
|
||||
let input = &mut File::open(IMAGE_AVIF_EXTENTS).expect("Unknown file");
|
||||
let context = mp4::read_avif(input).expect("read_avif failed");
|
||||
assert_eq!(context.primary_item().len(), 4387);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_avif_alpha_item() {
|
||||
let input = &mut File::open(IMAGE_AVIF_ALPHA).expect("Unknown file");
|
||||
let context = mp4::read_avif(input).expect("read_avif failed");
|
||||
assert!(context.alpha_item().is_some());
|
||||
assert!(!context.premultiplied_alpha);
|
||||
mp4::read_avif(input, context).expect("read_avif failed");
|
||||
assert_eq!(context.primary_item.len(), 6979);
|
||||
assert_eq!(context.primary_item[0..4], [0x12, 0x00, 0x0a, 0x0a]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_avif_bug_1655846() {
|
||||
let context = &mut mp4::AvifContext::new();
|
||||
let input = &mut File::open(IMAGE_AVIF_CORRUPT).expect("Unknown file");
|
||||
assert!(mp4::read_avif(input).is_err());
|
||||
assert!(mp4::read_avif(input, context).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_avif_bug_1661347() {
|
||||
let context = &mut mp4::AvifContext::new();
|
||||
let input = &mut File::open(IMAGE_AVIF_CORRUPT_2).expect("Unknown file");
|
||||
assert!(mp4::read_avif(input).is_err());
|
||||
}
|
||||
|
||||
fn assert_invalid_data<T: std::fmt::Debug>(result: mp4::Result<T>, expected_msg: &str) {
|
||||
match result {
|
||||
Err(Error::InvalidData(msg)) if msg == expected_msg => {}
|
||||
r => panic!(
|
||||
"Expected Err(Error::InvalidData({:?})), found {:?}",
|
||||
expected_msg, r
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_avif_bad_ipma_version() {
|
||||
let input = &mut File::open(IMAGE_AVIF_CORRUPT_3).expect("Unknown file");
|
||||
let expected_msg = "The version 0 should be used unless 32-bit item_ID values are needed";
|
||||
assert_invalid_data(mp4::read_avif(input), expected_msg);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_avif_bad_ipma_flags() {
|
||||
let input = &mut File::open(IMAGE_AVIF_CORRUPT_4).expect("Unknown file");
|
||||
let expected_msg = "flags should be equal to 0 unless there are more than 127 properties in the ItemPropertyContainerBox";
|
||||
assert_invalid_data(mp4::read_avif(input), expected_msg);
|
||||
assert!(mp4::read_avif(input, context).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // Remove when we add support; see https://github.com/mozilla/mp4parse-rust/issues/198
|
||||
fn public_avif_primary_item_is_grid() {
|
||||
let context = &mut mp4::AvifContext::new();
|
||||
let input = &mut File::open(IMAGE_AVIF_GRID).expect("Unknown file");
|
||||
mp4::read_avif(input).expect("read_avif failed");
|
||||
mp4::read_avif(input, context).expect("read_avif failed");
|
||||
// Add some additional checks
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_avif_read_samples() {
|
||||
for dir in AVIF_TEST_DIRS {
|
||||
for entry in walkdir::WalkDir::new(dir) {
|
||||
let entry = entry.expect("AVIF entry");
|
||||
let path = entry.path();
|
||||
if !path.is_file() || path.extension().unwrap_or_default() != "avif" {
|
||||
eprintln!("Skipping {:?}", path);
|
||||
continue; // Skip directories, ReadMe.txt, etc.
|
||||
}
|
||||
if path.parent().unwrap() == Path::new(AVIF_CORRUPT_IMAGES) {
|
||||
eprintln!("Skipping {:?}", path);
|
||||
continue;
|
||||
}
|
||||
if path == Path::new(IMAGE_AVIF_GRID) {
|
||||
eprintln!("Skipping {:?}", path);
|
||||
continue; // Remove when public_avif_primary_item_is_grid passes
|
||||
}
|
||||
println!("parsing {:?}", path);
|
||||
let input = &mut File::open(path).expect("Unknow file");
|
||||
mp4::read_avif(input).expect("read_avif failed");
|
||||
env_logger::init();
|
||||
|
||||
for entry in walkdir::WalkDir::new(AVIF_TEST_DIR) {
|
||||
let entry = entry.expect("AVIF entry");
|
||||
let path = entry.path();
|
||||
if !path.is_file() || path.extension().unwrap_or_default() != "avif" {
|
||||
eprintln!("Skipping {:?}", path);
|
||||
continue; // Skip directories, ReadMe.txt, etc.
|
||||
}
|
||||
if path == Path::new(IMAGE_AVIF_GRID) {
|
||||
eprintln!("Skipping {:?}", path);
|
||||
continue; // Remove when public_avif_primary_item_is_grid passes
|
||||
}
|
||||
println!("parsing {:?}", path);
|
||||
let context = &mut mp4::AvifContext::new();
|
||||
let input = &mut File::open(path).expect("Unknow file");
|
||||
mp4::read_avif(input, context).expect("read_avif failed");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"ba144343a4d38fe6918dd61bb6723e46ac297f506a27370c1d10b49bd8123572","cbindgen.toml":"5c9429f271d6e914d81b63e6509c04ffe84cab11ed3a53a2ed4715e5d5ace80e","examples/dump.rs":"83462422315c22e496960bae922edb23105c0aa272d2b106edd7574ff068513a","src/lib.rs":"d25c2e010186ada3524801e8f1ecdbe42cc4f961880ccca11ba9bdddd42363f7","tests/test_chunk_out_of_range.rs":"b5da583218d98027ed973a29c67434a91a1306f2d2fb39ec4d640d4824c308ce","tests/test_encryption.rs":"ca98516ff423c03b5fcc17b05f993f13b32485e4cf3ba86faf1bea72681d75ce","tests/test_fragment.rs":"e90eb5a4418d30002655466c0c4b3125c7fd70a74b6871471eaa172f1def9db8","tests/test_rotation.rs":"fb43c2f2dfa496d151c33bdd46c0fd3252387c23cc71e2cac9ed0234de715a81","tests/test_sample_table.rs":"185755909b2f4e0ea99604bb423a07623d614a180accdaebd1c98aef2c2e3ae6","tests/test_workaround_stsc.rs":"7dd419f3d55b9a3a039cac57e58a9240a9c8166bcd4356c24f69f731c3ced83b"},"package":null}
|
||||
{"files":{"Cargo.toml":"87fce2f5e05b37d95cf255301762ea20d08a772112da2ade46eb39a76e8d27e0","cbindgen.toml":"5c9429f271d6e914d81b63e6509c04ffe84cab11ed3a53a2ed4715e5d5ace80e","examples/dump.rs":"83462422315c22e496960bae922edb23105c0aa272d2b106edd7574ff068513a","src/lib.rs":"76fb7cd35bbe56463d43f452fd7ebaf5b4619bd661fb71fb45f69e980877b424","tests/test_chunk_out_of_range.rs":"b5da583218d98027ed973a29c67434a91a1306f2d2fb39ec4d640d4824c308ce","tests/test_encryption.rs":"ca98516ff423c03b5fcc17b05f993f13b32485e4cf3ba86faf1bea72681d75ce","tests/test_fragment.rs":"e90eb5a4418d30002655466c0c4b3125c7fd70a74b6871471eaa172f1def9db8","tests/test_rotation.rs":"fb43c2f2dfa496d151c33bdd46c0fd3252387c23cc71e2cac9ed0234de715a81","tests/test_sample_table.rs":"185755909b2f4e0ea99604bb423a07623d614a180accdaebd1c98aef2c2e3ae6","tests/test_workaround_stsc.rs":"7dd419f3d55b9a3a039cac57e58a9240a9c8166bcd4356c24f69f731c3ced83b"},"package":null}
|
|
@ -24,7 +24,7 @@ travis-ci = { repository = "https://github.com/mozilla/mp4parse-rust" }
|
|||
|
||||
[dependencies]
|
||||
byteorder = "1.2.1"
|
||||
fallible_collections = { version = "0.3", features = ["std_io"] }
|
||||
fallible_collections = { version = "0.2", features = ["std_io"] }
|
||||
log = "0.4"
|
||||
mp4parse = {version = "0.11.2", path = "../mp4parse"}
|
||||
num-traits = "=0.2.10"
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
//! Err(_) => -1,
|
||||
//! }
|
||||
//! }
|
||||
//! let capi_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||
//! let mut file = std::fs::File::open(capi_dir + "/../mp4parse/tests/minimal.mp4").unwrap();
|
||||
//!
|
||||
//! let mut file = std::fs::File::open("../mp4parse/tests/minimal.mp4").unwrap();
|
||||
//! let io = mp4parse_capi::Mp4parseIo {
|
||||
//! read: Some(buf_read),
|
||||
//! userdata: &mut file as *mut _ as *mut std::os::raw::c_void
|
||||
|
@ -436,14 +436,6 @@ pub struct Mp4parseParser {
|
|||
video_track_sample_descriptions: TryHashMap<u32, TryVec<Mp4parseTrackVideoSampleInfo>>,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default)]
|
||||
pub struct AvifImage {
|
||||
pub primary_item: Mp4parseByteData,
|
||||
pub alpha_item: Mp4parseByteData,
|
||||
pub premultiplied_alpha: bool,
|
||||
}
|
||||
|
||||
/// A unified interface for the parsers which have different contexts, but
|
||||
/// share the same pattern of construction. This allows unification of
|
||||
/// argument validation from C and minimizes the surface of unsafe code.
|
||||
|
@ -451,11 +443,11 @@ trait ContextParser
|
|||
where
|
||||
Self: Sized,
|
||||
{
|
||||
type Context;
|
||||
type Context: Default;
|
||||
|
||||
fn with_context(context: Self::Context) -> Self;
|
||||
|
||||
fn read<T: Read>(io: &mut T) -> mp4parse::Result<Self::Context>;
|
||||
fn read<T: Read>(io: &mut T, context: &mut Self::Context) -> mp4parse::Result<()>;
|
||||
}
|
||||
|
||||
impl Mp4parseParser {
|
||||
|
@ -478,11 +470,12 @@ impl ContextParser for Mp4parseParser {
|
|||
}
|
||||
}
|
||||
|
||||
fn read<T: Read>(io: &mut T) -> mp4parse::Result<Self::Context> {
|
||||
read_mp4(io)
|
||||
fn read<T: Read>(io: &mut T, context: &mut Self::Context) -> mp4parse::Result<()> {
|
||||
read_mp4(io, context)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Mp4parseAvifParser {
|
||||
context: AvifContext,
|
||||
}
|
||||
|
@ -500,8 +493,8 @@ impl ContextParser for Mp4parseAvifParser {
|
|||
Self { context }
|
||||
}
|
||||
|
||||
fn read<T: Read>(io: &mut T) -> mp4parse::Result<Self::Context> {
|
||||
read_avif(io)
|
||||
fn read<T: Read>(io: &mut T, context: &mut Self::Context) -> mp4parse::Result<()> {
|
||||
read_avif(io, context)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -604,8 +597,10 @@ unsafe fn mp4parse_new_common<P: ContextParser>(
|
|||
fn mp4parse_new_common_safe<T: Read, P: ContextParser>(
|
||||
io: &mut T,
|
||||
) -> Result<*mut P, Mp4parseStatus> {
|
||||
P::read(io)
|
||||
.map(P::with_context)
|
||||
let mut context = P::Context::default();
|
||||
|
||||
P::read(io, &mut context)
|
||||
.map(|_| P::with_context(context))
|
||||
.and_then(|x| TryBox::try_new(x).map_err(mp4parse::Error::from))
|
||||
.map(TryBox::into_raw)
|
||||
.map_err(Mp4parseStatus::from)
|
||||
|
@ -1185,31 +1180,27 @@ fn mp4parse_get_track_video_info_safe(
|
|||
/// # Safety
|
||||
///
|
||||
/// This function is unsafe because it dereferences both the parser and
|
||||
/// avif_image raw pointers passed into it. Callers should ensure the parser
|
||||
/// pointer points to a valid `Mp4parseAvifParser`, and that the avif_image
|
||||
/// pointer points to a valid `AvifImage`. If there was not a previous
|
||||
/// primary_item raw pointers passed into it. Callers should ensure the parser
|
||||
/// pointer points to a valid `Mp4parseAvifParser`, and that the primary_item
|
||||
/// pointer points to a valid `Mp4parseByteData`. If there was not a previous
|
||||
/// successful call to `mp4parse_avif_read()`, no guarantees are made as to
|
||||
/// the state of `avif_image`. If `avif_image.alpha_item` is set to a
|
||||
/// positive `length` and non-null `data`, then the `avif_image` contains an
|
||||
/// valid alpha channel data. Otherwise, the image is opaque.
|
||||
/// the state of `primary_item`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn mp4parse_avif_get_image(
|
||||
pub unsafe extern "C" fn mp4parse_avif_get_primary_item(
|
||||
parser: *mut Mp4parseAvifParser,
|
||||
avif_image: *mut AvifImage,
|
||||
primary_item: *mut Mp4parseByteData,
|
||||
) -> Mp4parseStatus {
|
||||
if parser.is_null() || avif_image.is_null() {
|
||||
if parser.is_null() {
|
||||
return Mp4parseStatus::BadArg;
|
||||
}
|
||||
|
||||
// Initialize fields to default values to ensure all fields are always valid.
|
||||
*avif_image = Default::default();
|
||||
*primary_item = Default::default();
|
||||
|
||||
let context = (*parser).context();
|
||||
|
||||
(*avif_image).primary_item.set_data(context.primary_item());
|
||||
if let Some(context_alpha_item) = context.alpha_item() {
|
||||
(*avif_image).alpha_item.set_data(context_alpha_item);
|
||||
(*avif_image).premultiplied_alpha = context.premultiplied_alpha;
|
||||
}
|
||||
// TODO: check for a valid parsed context. See https://github.com/mozilla/mp4parse-rust/issues/195
|
||||
(*primary_item).set_data(&context.primary_item);
|
||||
|
||||
Mp4parseStatus::Ok
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ description = "Shared Rust code for libxul"
|
|||
geckoservo = { path = "../../../../servo/ports/geckolib" }
|
||||
kvstore = { path = "../../../components/kvstore" }
|
||||
lmdb-rkv-sys = { version = "0.11", features = ["mdb_idl_logn_9"] }
|
||||
mp4parse_capi = { git = "https://github.com/mozilla/mp4parse-rust", rev = "94fd2f16b1a569a35801c4e7d858c6e1b24020ff" }
|
||||
mp4parse_capi = { git = "https://github.com/mozilla/mp4parse-rust", rev = "f7c35a30ff25521bebe64c19d3f306569ecb5385" }
|
||||
nserror = { path = "../../../../xpcom/rust/nserror" }
|
||||
nsstring = { path = "../../../../xpcom/rust/nsstring" }
|
||||
netwerk_helper = { path = "../../../../netwerk/base/rust-helper" }
|
||||
|
|
Загрузка…
Ссылка в новой задаче