diff --git a/cc-1.2.62/.cargo-checksum.json b/cc-1.2.62/.cargo-checksum.json deleted file mode 100644 index a48bdd51a0..0000000000 --- a/cc-1.2.62/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{".cargo_vcs_info.json":"7d81f2990233c40ed9edbf2a95740a6b69dd06c9dbc64199c047f4496b43d82a","CHANGELOG.md":"eb499d4a77f09e6d21508e872e2ee4f78f7d2afed9e9921654d6bc3d688f10e1","Cargo.lock":"4dedebe3cf78840025a1008199478ed14bc1a2fd33f01aa3d559f07258180683","Cargo.toml":"b31a35c88facff9879fa4a2cdf9f53c76fc056d8be4db05ccb672fb5ece4f0fe","Cargo.toml.orig":"09ffbc9dc9dc5d406796b5fb4c0020f56d435283e7f26f0d55eb8d7b76b14fc7","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"cc952a506dc726eed3e128d47d62defbac4470bbb0e465f6a043ae8bf0c96d19","clippy.toml":"cf46251b0953d1a3c7a4f8f8941a08d8a416d5fc348dfe3f3c520eb93fcc18ea","src/command_helpers.rs":"650c9682edc3d7732249e327c1026c7cc733f3702f4867b9580223e97522d3c6","src/detect_compiler_family.c":"97ca4b021495611e828becea6187add37414186a16dfedd26c2947cbce6e8b2f","src/flags.rs":"11f22aa9741b0d445b19ab29ea1076cc32a4143316a74855b6bd682004217543","src/lib.rs":"e893112abbdb4784ea895438e90d1e9261c02bcbc712b8d878e8bfd5c5722d78","src/parallel/async_executor.rs":"4ce24435fff6b6555b43fee042c16bd65d4150d0346567f246b9190d85b45983","src/parallel/command_runner.rs":"1d17d9a037de93b31cc7ee6a914a1e1f6c1ac156cf8619f0014dcbaca6a0b215","src/parallel/job_token.rs":"858b684423bce773d04a042f3f9bafbd21deda21bbb1efefb078a20ef5d8a134","src/parallel/mod.rs":"bf156170790d40a76015a8c78cc2a391cbe1b60bb96c1fac1d047d26e342386a","src/parallel/stderr.rs":"840fe8d0ba613c98fa92cbebc374a028a3212ba7dcea58285483223a31acc7eb","src/target.rs":"a9d2d347a3a08015531f00b32bc8a7478f5636822cc8e0237ea9efa3f07d4cd4","src/target/apple.rs":"da9411b2c4db419e0fa39f765ee53b8665b3837fb39827679a53238734a4a1c1","src/target/generated.rs":"2e1d7e5d8fd954bdcc6e45ef16f69af1a798eacb8d212a516aaf8025908f6d36","src/target/llvm.rs":"190fe8d2b204cd4a6e68f2a1aada17ecd6390799564ed25792fcc08ab34710ba","src/target/parser.rs":"e2a5246a8fe46d39616410685cf7418f6b86344c5309bf09817978b182b3b38c","src/tempfile.rs":"3d9a4bd894862a345aa230a61ec266f0c68f4ef9713d1d9c727482e61f1ea7c3","src/tool.rs":"1d279a6f0738f9164ca794c85bd55951aa95e2adc788e75a6bb8133393d6cce8","src/utilities.rs":"ebb59ac01fb9588bcea0d0ee786e5d5a5696b4284a1245657625db03e830b72e"},"package":"a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98"} \ No newline at end of file diff --git a/cc-1.2.62/.cargo_vcs_info.json b/cc-1.2.62/.cargo_vcs_info.json deleted file mode 100644 index 42efaf466e..0000000000 --- a/cc-1.2.62/.cargo_vcs_info.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "git": { - "sha1": "37a5f8f4e1a2eaf4cf483fc0fb97d35761b720c5" - }, - "path_in_vcs": "" -} \ No newline at end of file diff --git a/cc-1.2.62/CHANGELOG.md b/cc-1.2.62/CHANGELOG.md deleted file mode 100644 index d69e859427..0000000000 --- a/cc-1.2.62/CHANGELOG.md +++ /dev/null @@ -1,797 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -## [1.2.62](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.61...cc-v1.2.62) - 2026-05-08 - -### Other - -- Regenerate target info ([#1721](https://github.com/rust-lang/cc-rs/pull/1721)) -- Allow exceptions on wasm platforms ([#1714](https://github.com/rust-lang/cc-rs/pull/1714)) -- Add relibc env ([#1710](https://github.com/rust-lang/cc-rs/pull/1710)) -- recognize sh4 architecture in parse_arch() ([#1712](https://github.com/rust-lang/cc-rs/pull/1712)) - -## [1.2.61](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.60...cc-v1.2.61) - 2026-04-24 - -### Other - -- fix `OutputKind::Capture` documentation ([#1705](https://github.com/rust-lang/cc-rs/pull/1705)) - -## [1.2.60](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.59...cc-v1.2.60) - 2026-04-10 - -### Fixed - -- *(ar)* suppress warnings from `D` modifier probe ([#1700](https://github.com/rust-lang/cc-rs/pull/1700)) - -## [1.2.59](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.58...cc-v1.2.59) - 2026-04-03 - -### Fixed - -- *(ar)* deterministic archives with `D` modifier ([#1697](https://github.com/rust-lang/cc-rs/pull/1697)) - -### Other - -- Regenerate target info ([#1698](https://github.com/rust-lang/cc-rs/pull/1698)) -- Fix target abi parsing for sanitiser targets ([#1695](https://github.com/rust-lang/cc-rs/pull/1695)) - -## [1.2.58](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.57...cc-v1.2.58) - 2026-03-27 - -### Other - -- Update Compile-time Requirements to add info about clang-cl.exe ([#1693](https://github.com/rust-lang/cc-rs/pull/1693)) - -## [1.2.57](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.56...cc-v1.2.57) - 2026-03-13 - -### Other - -- Size archiver batches according to argument length not argument count ([#1689](https://github.com/rust-lang/cc-rs/pull/1689)) -- Added `Build::env` for setting environment variables of compiler invocations and other child processes ([#1656](https://github.com/rust-lang/cc-rs/pull/1656) [#1682](https://github.com/rust-lang/cc-rs/pull/1682)) - -## [1.2.56](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.55...cc-v1.2.56) - 2026-02-13 - -### Other - -- Regenerate target info ([#1676](https://github.com/rust-lang/cc-rs/pull/1676)) -- Fix `clang-cl` target when cross-compiling ([#1670](https://github.com/rust-lang/cc-rs/pull/1670)) - -## [1.2.55](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.54...cc-v1.2.55) - 2026-01-30 - -### Other - -- Regenerate target info ([#1667](https://github.com/rust-lang/cc-rs/pull/1667)) -- Fix RUSTFLAGS typo in test-linker-plugin-lto ([#1665](https://github.com/rust-lang/cc-rs/pull/1665)) -- Disable PIC for armv7-sony-vita-newlibeabihf ([#1664](https://github.com/rust-lang/cc-rs/pull/1664)) - -## [1.2.54](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.53...cc-v1.2.54) - 2026-01-23 - -### Other - -- Fix x86_64-unknown-linux-gnuasan parsing ([#1661](https://github.com/rust-lang/cc-rs/pull/1661)) -- Regenerate target info ([#1660](https://github.com/rust-lang/cc-rs/pull/1660)) - -## [1.2.53](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.52...cc-v1.2.53) - 2026-01-16 - -### Other - -- Add missing RISC-V targets ([#1657](https://github.com/rust-lang/cc-rs/pull/1657)) - -## [1.2.52](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.51...cc-v1.2.52) - 2026-01-09 - -### Other - -- Fix contradictory doc for CC compiler in crate doc ([#1650](https://github.com/rust-lang/cc-rs/pull/1650)) -- Have CUDA compilaion check for sbsa-linux when targeting aarch64. ([#1647](https://github.com/rust-lang/cc-rs/pull/1647)) -- Update link for -Cdwarf-version; Remove -Z (stabilized in 1.88) ([#1648](https://github.com/rust-lang/cc-rs/pull/1648)) -- Fix Build::env_tool to check for .exe on windows ([#1646](https://github.com/rust-lang/cc-rs/pull/1646)) - -## [1.2.51](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.50...cc-v1.2.51) - 2025-12-26 - -### Other - -- Regenerate target info ([#1642](https://github.com/rust-lang/cc-rs/pull/1642)) -- Update Readmes ([#1641](https://github.com/rust-lang/cc-rs/pull/1641)) - -## [1.2.50](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.49...cc-v1.2.50) - 2025-12-19 - -### Other - -- Add tests for `OUT_DIR` escape for '..' file paths (#1631) -- Fix #283: Make warnings(false) actually suppress compiler warnings ([#1633](https://github.com/rust-lang/cc-rs/pull/1633)) - -## [1.2.49](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.48...cc-v1.2.49) - 2025-12-06 - -### Other - -- Fix run_output to prevent infinite blocking ([#1627](https://github.com/rust-lang/cc-rs/pull/1627)) -- Fix detect_family deadlock ([#1626](https://github.com/rust-lang/cc-rs/pull/1626)) -- Fix link in new debug_str doc comment ([#1625](https://github.com/rust-lang/cc-rs/pull/1625)) -- Support more of Cargo's debug levels with Build::debug_str ([#1624](https://github.com/rust-lang/cc-rs/pull/1624)) - -## [1.2.48](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.47...cc-v1.2.48) - 2025-11-28 - -### Other - -- Regenerate target info ([#1620](https://github.com/rust-lang/cc-rs/pull/1620)) - -## [1.2.47](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.46...cc-v1.2.47) - 2025-11-21 - -### Other - -- add helenos linker identifications ([#1615](https://github.com/rust-lang/cc-rs/pull/1615)) - -## [1.2.46](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.45...cc-v1.2.46) - 2025-11-14 - -### Other - -- Add Visual Studio 2026 support ([#1609](https://github.com/rust-lang/cc-rs/pull/1609)) - -## [1.2.45](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.44...cc-v1.2.45) - 2025-11-07 - -### Other - -- Regenerate target info ([#1606](https://github.com/rust-lang/cc-rs/pull/1606)) -- Use a default check for the "env" variable in apple_sdk_name ([#1605](https://github.com/rust-lang/cc-rs/pull/1605)) - -## [1.2.44](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.43...cc-v1.2.44) - 2025-10-31 - -### Other - -- Fix debug assertion for env/abi mismatch ([#1604](https://github.com/rust-lang/cc-rs/pull/1604)) -- Update CHANGELOG for version 1.2.43 ([#1602](https://github.com/rust-lang/cc-rs/pull/1602)) -- Stop passing an invalid target to `llvm-mingw`'s cross-compilation wrappers ([#1495](https://github.com/rust-lang/cc-rs/pull/1495)) - -## [1.2.43](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.42...cc-v1.2.43) - 2025-10-24 - -### Other - -- Mark `static_flag` and `shared_flag` as deprecated ([#1582](https://github.com/rust-lang/cc-rs/pull/1582)) - -## [1.2.42](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.41...cc-v1.2.42) - 2025-10-24 - -### Other - -- Fix check-semver-checks ([#1600](https://github.com/rust-lang/cc-rs/pull/1600)) -- minor improvement for docs ([#1598](https://github.com/rust-lang/cc-rs/pull/1598)) -- Fix linker-plugin-lto: use `-flto=thin` ([#1594](https://github.com/rust-lang/cc-rs/pull/1594)) -- Disable check-buildstd for armv7k-apple-watchos ([#1599](https://github.com/rust-lang/cc-rs/pull/1599)) -- Add elf abi to ppc64 targets ([#1596](https://github.com/rust-lang/cc-rs/pull/1596)) - -## [1.2.41](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.40...cc-v1.2.41) - 2025-10-10 - -### Other - -- Allow using VCToolsVersion to request a specific msvc version ([#1589](https://github.com/rust-lang/cc-rs/pull/1589)) -- Regenerate target info ([#1592](https://github.com/rust-lang/cc-rs/pull/1592)) -- Regenerate windows sys bindings ([#1591](https://github.com/rust-lang/cc-rs/pull/1591)) -- Update windows-bindgen requirement from 0.64 to 0.65 ([#1590](https://github.com/rust-lang/cc-rs/pull/1590)) -- Fix `get_base_archiver_variant` for clang-cl: use `--print-search-dirs` ([#1587](https://github.com/rust-lang/cc-rs/pull/1587)) - -## [1.2.40](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.39...cc-v1.2.40) - 2025-10-03 - -### Other - -- Reorder changelog and remove duplicate Unreleased section ([#1579](https://github.com/rust-lang/cc-rs/pull/1579)) -- Prefer clang if linker-plugin-lto specified ([#1573](https://github.com/rust-lang/cc-rs/pull/1573)) -- Fix building for Mac Catalyst ([#1577](https://github.com/rust-lang/cc-rs/pull/1577)) -- Improve ESP microcontroller targets ([#1574](https://github.com/rust-lang/cc-rs/pull/1574)) - -## [1.2.39](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.38...cc-v1.2.39) - 2025-09-26 - -### Other - -- Fix cross compilation to xtensa-esp32s3-espidf ([#1569](https://github.com/rust-lang/cc-rs/pull/1569)) -- Fix autodetect_wasi_compiler: support non utf-8 path ([#1568](https://github.com/rust-lang/cc-rs/pull/1568)) -- Regenerate target info ([#1567](https://github.com/rust-lang/cc-rs/pull/1567)) -- Fix rustcflags mapping: require -Clinker-plugin-lto for -flto ([#1564](https://github.com/rust-lang/cc-rs/pull/1564)) -- Use `$WASI_SDK_PATH` on WASI targets by default ([#1562](https://github.com/rust-lang/cc-rs/pull/1562)) -- Fix atomicity violations in concurrent cache operations ([#1559](https://github.com/rust-lang/cc-rs/pull/1559)) - -## [1.2.38](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.37...cc-v1.2.38) - 2025-09-19 - -### Other - -- updated the following local packages: find-msvc-tools - -## [1.2.37](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.36...cc-v1.2.37) - 2025-09-12 - -### Other - -- Fix errmsg in RustcCodegenFlags::set_rustc_flag ([#1551](https://github.com/rust-lang/cc-rs/pull/1551)) -- propagate stack protector to Linux C compilers ([#1550](https://github.com/rust-lang/cc-rs/pull/1550)) -- Extract new fn `run_commands_in_parallel` ([#1549](https://github.com/rust-lang/cc-rs/pull/1549)) - -## [1.2.36](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.35...cc-v1.2.36) - 2025-09-05 - -### Other - -- Regenerate windows sys bindings ([#1548](https://github.com/rust-lang/cc-rs/pull/1548)) -- Update windows-bindgen requirement from 0.62 to 0.63 ([#1547](https://github.com/rust-lang/cc-rs/pull/1547)) -- Add fn get_ucrt_dir for find-msvc-tools ([#1546](https://github.com/rust-lang/cc-rs/pull/1546)) -- Regenerate target info ([#1544](https://github.com/rust-lang/cc-rs/pull/1544)) -- fix publish.yml ([#1543](https://github.com/rust-lang/cc-rs/pull/1543)) -- Replace periods with underscores as well when parsing env variables ([#1541](https://github.com/rust-lang/cc-rs/pull/1541)) - -## [1.2.35](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.34...cc-v1.2.35) - 2025-09-01 - -### Fixed - -- fix building for aarch64-apple-visionos-sim on nightly ([#1534](https://github.com/rust-lang/cc-rs/pull/1534)) -- fix tests apple_sdkroot_wrong ([#1530](https://github.com/rust-lang/cc-rs/pull/1530)) - -### Other - -- Regenerate target info ([#1536](https://github.com/rust-lang/cc-rs/pull/1536)) -- Optimize Tool::to_command ([#1535](https://github.com/rust-lang/cc-rs/pull/1535)) -- Extract find-msvc-tools ([#1531](https://github.com/rust-lang/cc-rs/pull/1531)) -- Add prefer_clang_cl_over_msvc ([#1516](https://github.com/rust-lang/cc-rs/pull/1516)) - -## [1.2.34](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.33...cc-v1.2.34) - 2025-08-22 - -### Fixed - -- add `-mcpu=mvp` and `-mmutable-globals` for `wasm32v1-none` ([#1524](https://github.com/rust-lang/cc-rs/pull/1524)) - -### Other - -- Optimize parse_version in find_tools.rs ([#1527](https://github.com/rust-lang/cc-rs/pull/1527)) -- Fallback to manually searching for tool dir ([#1526](https://github.com/rust-lang/cc-rs/pull/1526)) - -## [1.2.33](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.32...cc-v1.2.33) - 2025-08-15 - -### Other - -- Regenerate target info ([#1521](https://github.com/rust-lang/cc-rs/pull/1521)) -- [win][arm64ec] Add testing for Arm64EC Windows ([#1512](https://github.com/rust-lang/cc-rs/pull/1512)) -- Fix parsing of nigthly targets ([#1517](https://github.com/rust-lang/cc-rs/pull/1517)) -- [win][arm64ec] Fix finding assembler and setting is_arm for Arm64EC ([#1511](https://github.com/rust-lang/cc-rs/pull/1511)) - -## [1.2.32](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.31...cc-v1.2.32) - 2025-08-08 - -### Fixed - -- fix new clippy lint introduced in rust 1.89.0 ([#1509](https://github.com/rust-lang/cc-rs/pull/1509)) - -### Other - -- clarify cargo default if no rerun emitted ([#1508](https://github.com/rust-lang/cc-rs/pull/1508)) -- extract compile_objects_sequential ([#1507](https://github.com/rust-lang/cc-rs/pull/1507)) -- Windows `find_tools`: add support for finding Clang ([#1506](https://github.com/rust-lang/cc-rs/pull/1506)) -- Add m68k-unknown-linux-gnu cross-compile target ([#1505](https://github.com/rust-lang/cc-rs/pull/1505)) - -## [1.2.31](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.30...cc-v1.2.31) - 2025-08-01 - -### Other - -- Add doc for using sccache/ccache etc ([#1502](https://github.com/rust-lang/cc-rs/pull/1502)) -- ability to statically link against C++ stdlib ([#1497](https://github.com/rust-lang/cc-rs/pull/1497)) -- Add instructions on using sccache ([#1503](https://github.com/rust-lang/cc-rs/pull/1503)) -- Add support for recognizing some architectures supported by GCC, but not LLVM. ([#1500](https://github.com/rust-lang/cc-rs/pull/1500)) - -## [1.2.30](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.29...cc-v1.2.30) - 2025-07-18 - -### Other - -- define _REENTRANT by default ([#1496](https://github.com/rust-lang/cc-rs/pull/1496)) - -## [1.2.29](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.28...cc-v1.2.29) - 2025-07-05 - -### Other - -- Fix target parsing for powerpc ([#1490](https://github.com/rust-lang/cc-rs/pull/1490)) - -## [1.2.28](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.27...cc-v1.2.28) - 2025-07-04 - -### Other - -- Recognize `mlibc` environment ([#1488](https://github.com/rust-lang/cc-rs/pull/1488)) -- Fix clippy warnings about not using variables in `format!` strings ([#1489](https://github.com/rust-lang/cc-rs/pull/1489)) - -## [1.2.27](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.26...cc-v1.2.27) - 2025-06-13 - -### Other - -- Regenerate windows sys bindings ([#1485](https://github.com/rust-lang/cc-rs/pull/1485)) -- Update windows-bindgen requirement from 0.61 to 0.62 ([#1484](https://github.com/rust-lang/cc-rs/pull/1484)) -- Regenerate target info ([#1483](https://github.com/rust-lang/cc-rs/pull/1483)) - -## [1.2.26](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.25...cc-v1.2.26) - 2025-06-06 - -### Other - -- Also set `SDKROOT` when building apple platforms ([#1475](https://github.com/rust-lang/cc-rs/pull/1475)) -- use windows 2022 in CI ([#1479](https://github.com/rust-lang/cc-rs/pull/1479)) -- Detect -Wslash-u-filename warning on clang-cl ([#1477](https://github.com/rust-lang/cc-rs/pull/1477)) - -## [1.2.25](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.24...cc-v1.2.25) - 2025-05-30 - -### Other - -- make `powerp64` use `powerpc64-linux-gnu` prefix ([#1474](https://github.com/rust-lang/cc-rs/pull/1474)) - -## [1.2.24](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.23...cc-v1.2.24) - 2025-05-23 - -### Other - -- Regenerate windows sys bindings ([#1471](https://github.com/rust-lang/cc-rs/pull/1471)) - -## [1.2.23](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.22...cc-v1.2.23) - 2025-05-16 - -### Other - -- support "vxworks" and "nto" OSes on `get_base_archiver_variant` ([#1456](https://github.com/rust-lang/cc-rs/pull/1456)) - -## [1.2.22](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.21...cc-v1.2.22) - 2025-05-09 - -### Other - -- Add `flags` method to `cc::Build` for adding multiple flags ([#1466](https://github.com/rust-lang/cc-rs/pull/1466)) - -## [1.2.21](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.20...cc-v1.2.21) - 2025-05-02 - -### Other - -- Fix wasm32-unknown-unknown by passing -c ([#1424](https://github.com/rust-lang/cc-rs/pull/1424)) - -## [1.2.20](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.19...cc-v1.2.20) - 2025-04-25 - -### Other - -- Regenerate target info ([#1461](https://github.com/rust-lang/cc-rs/pull/1461)) -- Fix parser.rs on latest rustc nightly ([#1459](https://github.com/rust-lang/cc-rs/pull/1459)) - -## [1.2.19](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.18...cc-v1.2.19) - 2025-04-11 - -### Other - -- Fix musl compilation: Add musl as a prefix fallback ([#1455](https://github.com/rust-lang/cc-rs/pull/1455)) - -## [1.2.18](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.17...cc-v1.2.18) - 2025-04-04 - -### Other - -- Regenerate target info ([#1450](https://github.com/rust-lang/cc-rs/pull/1450)) -- Use `std::thread::available_parallelism` for determining the default number of jobs ([#1447](https://github.com/rust-lang/cc-rs/pull/1447)) -- Fix mips64-openwrt-linux-musl parsing ([#1449](https://github.com/rust-lang/cc-rs/pull/1449)) -- Use compiler prefix `x86_64-linux-musl` ([#1443](https://github.com/rust-lang/cc-rs/pull/1443)) - -## [1.2.17](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.16...cc-v1.2.17) - 2025-03-21 - -### Other - -- Regenerate target info ([#1439](https://github.com/rust-lang/cc-rs/pull/1439)) -- Regenerate windows sys bindings ([#1437](https://github.com/rust-lang/cc-rs/pull/1437)) -- Fix wasm32-wali-linux-musl target parsing ([#1434](https://github.com/rust-lang/cc-rs/pull/1434)) -- Parse `rustc` target names ([#1413](https://github.com/rust-lang/cc-rs/pull/1413)) -- Regenerate target info ([#1429](https://github.com/rust-lang/cc-rs/pull/1429)) -- Added base support for `wasm32-wali-linux-musl` target ([#1373](https://github.com/rust-lang/cc-rs/pull/1373)) - -## [1.2.16](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.15...cc-v1.2.16) - 2025-02-28 - -### Fixed - -- force windows compiler to run in `out_dir` to prevent artifacts in cwd (#1415) - -### Other - -- use `/arch:SSE2` for `x86` target arch (#1425) -- Regenerate windows-sys binding ([#1422](https://github.com/rust-lang/cc-rs/pull/1422)) -- Regenerate target info ([#1418](https://github.com/rust-lang/cc-rs/pull/1418)) -- Add LIB var when compiling flag_check (#1417) -- Change flag ordering ([#1403](https://github.com/rust-lang/cc-rs/pull/1403)) -- Fix archiver detection for musl cross compilation ([#1404](https://github.com/rust-lang/cc-rs/pull/1404)) - -## [1.2.15](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.14...cc-v1.2.15) - 2025-02-21 - -### Other - -- Regenerate target info ([#1406](https://github.com/rust-lang/cc-rs/pull/1406)) -- Always read from all `CFLAGS`-style flags ([#1401](https://github.com/rust-lang/cc-rs/pull/1401)) -- Simplify the error output on failed `Command` invocation ([#1397](https://github.com/rust-lang/cc-rs/pull/1397)) - -## [1.2.14](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.13...cc-v1.2.14) - 2025-02-14 - -### Other - -- Regenerate target info ([#1398](https://github.com/rust-lang/cc-rs/pull/1398)) -- Add support for setting `-gdwarf-{version}` based on RUSTFLAGS ([#1395](https://github.com/rust-lang/cc-rs/pull/1395)) -- Add support for alternative network stack io-sock on QNX 7.1 aarch64 and x86_64 ([#1312](https://github.com/rust-lang/cc-rs/pull/1312)) - -## [1.2.13](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.12...cc-v1.2.13) - 2025-02-08 - -### Other - -- Fix cross-compiling for Apple platforms ([#1389](https://github.com/rust-lang/cc-rs/pull/1389)) - -## [1.2.12](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.11...cc-v1.2.12) - 2025-02-04 - -### Other - -- Split impl Build ([#1382](https://github.com/rust-lang/cc-rs/pull/1382)) -- Don't specify both `-target` and `-mtargetos=` on Apple targets ([#1384](https://github.com/rust-lang/cc-rs/pull/1384)) - -## [1.2.11](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.10...cc-v1.2.11) - 2025-01-31 - -### Other - -- Fix more flag inheritance ([#1380](https://github.com/rust-lang/cc-rs/pull/1380)) -- Include wrapper args. in `stdout` family heuristics to restore classifying `clang --driver-mode=cl` as `Msvc { clang_cl: true }` ([#1378](https://github.com/rust-lang/cc-rs/pull/1378)) -- Constrain `-Clto` and `-Cembed-bitcode` flag inheritance to be `clang`-only ([#1379](https://github.com/rust-lang/cc-rs/pull/1379)) -- Pass deployment target with `-m*-version-min=` ([#1339](https://github.com/rust-lang/cc-rs/pull/1339)) -- Regenerate target info ([#1376](https://github.com/rust-lang/cc-rs/pull/1376)) - -## [1.2.10](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.9...cc-v1.2.10) - 2025-01-17 - -### Other - -- Fix CC_FORCE_DISABLE=0 evaluating to true ([#1371](https://github.com/rust-lang/cc-rs/pull/1371)) -- Regenerate target info ([#1369](https://github.com/rust-lang/cc-rs/pull/1369)) -- Make hidden lifetimes explicit. ([#1366](https://github.com/rust-lang/cc-rs/pull/1366)) - -## [1.2.9](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.8...cc-v1.2.9) - 2025-01-12 - -### Other - -- Don't pass inherited PGO flags to GNU compilers (#1363) -- Adjusted zig cc judgment and avoided zigbuild errors([#1360](https://github.com/rust-lang/cc-rs/pull/1360)) ([#1361](https://github.com/rust-lang/cc-rs/pull/1361)) -- Fix compilation on macOS using clang and fix compilation using zig-cc ([#1364](https://github.com/rust-lang/cc-rs/pull/1364)) - -## [1.2.8](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.7...cc-v1.2.8) - 2025-01-11 - -### Other - -- Add `is_like_clang_cl()` getter (#1357) -- Fix clippy error in lib.rs ([#1356](https://github.com/rust-lang/cc-rs/pull/1356)) -- Regenerate target info ([#1352](https://github.com/rust-lang/cc-rs/pull/1352)) -- Fix compiler family detection issue with clang-cl on macOS ([#1328](https://github.com/rust-lang/cc-rs/pull/1328)) -- Update `windows-bindgen` dependency ([#1347](https://github.com/rust-lang/cc-rs/pull/1347)) -- Fix clippy warnings ([#1346](https://github.com/rust-lang/cc-rs/pull/1346)) - -## [1.2.7](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.6...cc-v1.2.7) - 2025-01-03 - -### Other - -- Regenerate target info ([#1342](https://github.com/rust-lang/cc-rs/pull/1342)) -- Document new supported architecture names in windows::find -- Make is_flag_supported_inner take an &Tool ([#1337](https://github.com/rust-lang/cc-rs/pull/1337)) -- Fix is_flag_supported on msvc ([#1336](https://github.com/rust-lang/cc-rs/pull/1336)) -- Allow using Visual Studio target names in `find_tool` ([#1335](https://github.com/rust-lang/cc-rs/pull/1335)) - -## [1.2.6](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.5...cc-v1.2.6) - 2024-12-27 - -### Other - -- Don't inherit the `/Oy` flag for 64-bit targets ([#1330](https://github.com/rust-lang/cc-rs/pull/1330)) - -## [1.2.5](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.4...cc-v1.2.5) - 2024-12-19 - -### Other - -- Check linking when testing if compiler flags are supported ([#1322](https://github.com/rust-lang/cc-rs/pull/1322)) - -## [1.2.4](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.3...cc-v1.2.4) - 2024-12-13 - -### Other - -- Add support for C/C++ compiler for Neutrino QNX: `qcc` ([#1319](https://github.com/rust-lang/cc-rs/pull/1319)) -- use -maix64 instead of -m64 ([#1307](https://github.com/rust-lang/cc-rs/pull/1307)) - -## [1.2.3](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.2...cc-v1.2.3) - 2024-12-06 - -### Other - -- Improve detection of environment when compiling from msbuild or msvc ([#1310](https://github.com/rust-lang/cc-rs/pull/1310)) -- Better error message when failing on unknown targets ([#1313](https://github.com/rust-lang/cc-rs/pull/1313)) -- Optimize RustcCodegenFlags ([#1305](https://github.com/rust-lang/cc-rs/pull/1305)) - -## [1.2.2](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.1...cc-v1.2.2) - 2024-11-29 - -### Other - -- Inherit flags from rustc ([#1279](https://github.com/rust-lang/cc-rs/pull/1279)) -- Add support for using sccache wrapper with cuda/nvcc ([#1304](https://github.com/rust-lang/cc-rs/pull/1304)) -- Fix msvc stdout not shown on error ([#1303](https://github.com/rust-lang/cc-rs/pull/1303)) -- Regenerate target info ([#1301](https://github.com/rust-lang/cc-rs/pull/1301)) -- Fix compilation of C++ code for armv7-unknown-linux-gnueabihf ([#1298](https://github.com/rust-lang/cc-rs/pull/1298)) -- Fetch target info from Cargo even if `Build::target` is manually set ([#1299](https://github.com/rust-lang/cc-rs/pull/1299)) -- Fix two files with different extensions having the same object name ([#1295](https://github.com/rust-lang/cc-rs/pull/1295)) -- Allow disabling cc's ability to compile via env var CC_FORCE_DISABLE ([#1292](https://github.com/rust-lang/cc-rs/pull/1292)) -- Regenerate target info ([#1293](https://github.com/rust-lang/cc-rs/pull/1293)) - -## [1.2.1](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.0...cc-v1.2.1) - 2024-11-14 - -### Other - -- When invoking `cl -?`, set stdin to null ([#1288](https://github.com/rust-lang/cc-rs/pull/1288)) - -## [1.2.0](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.37...cc-v1.2.0) - 2024-11-11 - -### Added - -- add i686-pc-windows-gnullvm prefix detection ([#1283](https://github.com/rust-lang/cc-rs/pull/1283)) - -### Other - -- Allow only specifying the architecture ([#1285](https://github.com/rust-lang/cc-rs/pull/1285)) -- Fix WASM vs. WASI options ([#1284](https://github.com/rust-lang/cc-rs/pull/1284)) - -## [1.1.37](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.36...cc-v1.1.37) - 2024-11-08 - -### Other - -- Use relative directory for obj files hash ([#1270](https://github.com/rust-lang/cc-rs/pull/1270)) -- Regenerate target info ([#1280](https://github.com/rust-lang/cc-rs/pull/1280)) - -## [1.1.36](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.35...cc-v1.1.36) - 2024-11-05 - -### Other - -- Fix CUDA build with clang++. ([#1273](https://github.com/rust-lang/cc-rs/pull/1273)) - -## [1.1.35](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.34...cc-v1.1.35) - 2024-11-04 - -### Other - -- Remove support for FRC ([#1268](https://github.com/rust-lang/cc-rs/pull/1268)) -- Do not add -fPIC by default on UEFI targets ([#1263](https://github.com/rust-lang/cc-rs/pull/1263)) -- Use -windows-gnu for all UEFI targets ([#1264](https://github.com/rust-lang/cc-rs/pull/1264)) - -## [1.1.34](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.33...cc-v1.1.34) - 2024-11-02 - -### Other - -- Remove redundant flags ([#1256](https://github.com/rust-lang/cc-rs/pull/1256)) - -## [1.1.33](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.32...cc-v1.1.33) - 2024-11-02 - -### Other - -- Reduce size of `cc::Build` and size of generated targets ([#1257](https://github.com/rust-lang/cc-rs/pull/1257)) - -## [1.1.32](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.31...cc-v1.1.32) - 2024-11-02 - -### Other - -- Use `rustc`'s knowledge of LLVM/Clang target triples ([#1252](https://github.com/rust-lang/cc-rs/pull/1252)) -- Use Cargo's target information when possible ([#1225](https://github.com/rust-lang/cc-rs/pull/1225)) - -## [1.1.31](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.30...cc-v1.1.31) - 2024-10-19 - -### Other - -- Add comment explaining why cc does not rebuild on env PATH change ([#1247](https://github.com/rust-lang/cc-rs/pull/1247)) - -## [1.1.30](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.29...cc-v1.1.30) - 2024-10-11 - -### Other - -- Don't pass -fPIC by default on wasm ([#1245](https://github.com/rust-lang/cc-rs/pull/1245)) - -## [1.1.29](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.28...cc-v1.1.29) - 2024-10-11 - -### Other - -- Regenerate target info ([#1243](https://github.com/rust-lang/cc-rs/pull/1243)) - -## [1.1.28](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.27...cc-v1.1.28) - 2024-10-06 - -### Other - -- Environment variables: For one accepting boolean, treat "0", "false" and empty env as false ([#1238](https://github.com/rust-lang/cc-rs/pull/1238)) - -## [1.1.27](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.26...cc-v1.1.27) - 2024-10-06 - -### Other - -- Revert "Use debug version of MSVC runtime library on debug ([#1231](https://github.com/rust-lang/cc-rs/pull/1231))" ([#1237](https://github.com/rust-lang/cc-rs/pull/1237)) -- Disable `CC_ENABLE_DEBUG_OUTPUT` if it is set to "0" ([#1234](https://github.com/rust-lang/cc-rs/pull/1234)) - -## [1.1.26](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.25...cc-v1.1.26) - 2024-10-06 - -### Other - -- Use debug version of MSVC runtime library on debug ([#1231](https://github.com/rust-lang/cc-rs/pull/1231)) - -## [1.1.25](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.24...cc-v1.1.25) - 2024-10-05 - -### Other - -- Remove incorrect "lib" prefixes in CXXSTDLIB doc comments ([#1228](https://github.com/rust-lang/cc-rs/pull/1228)) - -## [1.1.24](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.23...cc-v1.1.24) - 2024-10-01 - -### Other - -- Fix wasm32-wasip1-threads: shared-memory disallowed due to not compiled with 'atomics' or 'bulk-memory' features ([#1221](https://github.com/rust-lang/cc-rs/pull/1221)) -- Reduce the need for the host target triple ([#1224](https://github.com/rust-lang/cc-rs/pull/1224)) -- Add auto cancellation for CI jobs ([#1222](https://github.com/rust-lang/cc-rs/pull/1222)) - -## [1.1.23](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.22...cc-v1.1.23) - 2024-09-30 - -### Other - -- Update doc for detecting changes/upgrades of compilers ([#1218](https://github.com/rust-lang/cc-rs/pull/1218)) - -## [1.1.22](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.21...cc-v1.1.22) - 2024-09-27 - -### Other - -- Don't rerun if PATH changes ([#1215](https://github.com/rust-lang/cc-rs/pull/1215)) - -## [1.1.21](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.20...cc-v1.1.21) - 2024-09-18 - -### Other - -- disable pic for targets that end in `-none` ([#1212](https://github.com/rust-lang/cc-rs/pull/1212)) - -## [1.1.20](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.19...cc-v1.1.20) - 2024-09-17 - -### Other - -- Add buildcache as known Rust and C/C++ compiler wrapper ([#1209](https://github.com/rust-lang/cc-rs/pull/1209)) - -## [1.1.19](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.18...cc-v1.1.19) - 2024-09-15 - -### Other - -- Add support arm64e-apple-darwin ([#1207](https://github.com/rust-lang/cc-rs/pull/1207)) - -## [1.1.18](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.17...cc-v1.1.18) - 2024-09-07 - -### Other -- Fixed unsoundness in `StderrForwarder::forward_available` ([#1203](https://github.com/rust-lang/cc-rs/pull/1203)) - -## [1.1.17](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.16...cc-v1.1.17) - 2024-09-06 - -### Fixed -- fix finding toolchains when invoked by msbuild ([#1201](https://github.com/rust-lang/cc-rs/pull/1201)) - -## [1.1.16](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.15...cc-v1.1.16) - 2024-09-04 - -### Other -- Treat VxWorks wr-cc as a Gnu compiler ([#1198](https://github.com/rust-lang/cc-rs/pull/1198)) - -## [1.1.15](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.14...cc-v1.1.15) - 2024-08-26 - -### Other -- Add -mfloat-abi=hard as a default argument when using any arm/thumb-none-eabihf target ([#1194](https://github.com/rust-lang/cc-rs/pull/1194)) - -## [1.1.14](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.13...cc-v1.1.14) - 2024-08-23 - -### Other -- allow finding tools from path if VisualStudioDir is set - -## [1.1.13](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.12...cc-v1.1.13) - 2024-08-16 - -### Other -- Fix detect family: should detect emscripten as clang, closes [#1185](https://github.com/rust-lang/cc-rs/pull/1185) ([#1186](https://github.com/rust-lang/cc-rs/pull/1186)) - -## [1.1.12](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.11...cc-v1.1.12) - 2024-08-15 - -### Other -- improve docs ([#1183](https://github.com/rust-lang/cc-rs/pull/1183)) - -## [1.1.11](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.10...cc-v1.1.11) - 2024-08-14 - -### Other -- Add support for parsing shell encoded `*FLAGS` ([#1181](https://github.com/rust-lang/cc-rs/pull/1181)) -- Replace vector of tuples with BTreeMap which already is sorted and free of duplicates ([#1177](https://github.com/rust-lang/cc-rs/pull/1177)) - -## [1.1.10](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.9...cc-v1.1.10) - 2024-08-11 - -### Other -- Remap Windows targets triples to their LLVM counterparts ([#1176](https://github.com/rust-lang/cc-rs/pull/1176)) - -## [1.1.9](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.8...cc-v1.1.9) - 2024-08-11 - -### Other -- Add custom CC wrapper to the wrapper whitelist ([#1175](https://github.com/rust-lang/cc-rs/pull/1175)) - -## [1.1.8](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.7...cc-v1.1.8) - 2024-08-06 - -### Other -- Fix broken link in docs.rs ([#1173](https://github.com/rust-lang/cc-rs/pull/1173)) - -## [1.1.7](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.6...cc-v1.1.7) - 2024-07-29 - -### Other -- add `.objects` ([#1166](https://github.com/rust-lang/cc-rs/pull/1166)) - -## [1.1.6](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.5...cc-v1.1.6) - 2024-07-19 - -### Other -- Clippy fixes ([#1163](https://github.com/rust-lang/cc-rs/pull/1163)) - -## [1.1.5](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.4...cc-v1.1.5) - 2024-07-15 - -### Other -- Fix cyclic compilation: Use vendored once_cell ([#1154](https://github.com/rust-lang/cc-rs/pull/1154)) - -## [1.1.4](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.3...cc-v1.1.4) - 2024-07-14 - -### Other -- Support compiling on wasm targets (Supersede [#1068](https://github.com/rust-lang/cc-rs/pull/1068)) ([#1160](https://github.com/rust-lang/cc-rs/pull/1160)) - -## [1.1.3](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.2...cc-v1.1.3) - 2024-07-14 - -### Other -- Reduce msrv to 1.63 ([#1158](https://github.com/rust-lang/cc-rs/pull/1158)) -- Revert "Use raw-dylib for windows-sys ([#1137](https://github.com/rust-lang/cc-rs/pull/1137))" ([#1157](https://github.com/rust-lang/cc-rs/pull/1157)) -- Fix typos ([#1152](https://github.com/rust-lang/cc-rs/pull/1152)) -- Fix `doc_lazy_continuation` lints ([#1153](https://github.com/rust-lang/cc-rs/pull/1153)) - -## [1.1.2](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.1...cc-v1.1.2) - 2024-07-12 - -### Other -- Add empty `jobserver` feature. ([#1150](https://github.com/rust-lang/cc-rs/pull/1150)) - -## [1.1.1](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.0...cc-v1.1.1) - 2024-07-12 - -### Other -- Fix is_flag_supported not respecting emit_rerun_if_env_changed ([#1147](https://github.com/rust-lang/cc-rs/pull/1147)) ([#1148](https://github.com/rust-lang/cc-rs/pull/1148)) - -## [1.1.0](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.106...cc-v1.1.0) - 2024-07-08 - -### Added -- add cargo_output to eliminate last vestiges of stdout pollution ([#1141](https://github.com/rust-lang/cc-rs/pull/1141)) - -## [1.0.106](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.105...cc-v1.0.106) - 2024-07-08 - -### Other -- Drop support for Visual Studio 12 (2013) ([#1046](https://github.com/rust-lang/cc-rs/pull/1046)) -- Use raw-dylib for windows-sys ([#1137](https://github.com/rust-lang/cc-rs/pull/1137)) -- Bump msrv to 1.67 ([#1143](https://github.com/rust-lang/cc-rs/pull/1143)) -- Bump msrv to 1.65 ([#1140](https://github.com/rust-lang/cc-rs/pull/1140)) -- Fix clippy warnings ([#1138](https://github.com/rust-lang/cc-rs/pull/1138)) - -## [1.0.105](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.104...cc-v1.0.105) - 2024-07-07 - -### Other -- Regenerate windows sys bindings ([#1132](https://github.com/rust-lang/cc-rs/pull/1132)) -- Fix generate-windows-sys-bindings ([#1133](https://github.com/rust-lang/cc-rs/pull/1133)) -- Fix gen-windows-sys-binding ([#1130](https://github.com/rust-lang/cc-rs/pull/1130)) -- Fix gen-windows-sys-binding ([#1127](https://github.com/rust-lang/cc-rs/pull/1127)) -- Update windows-bindgen requirement from 0.57 to 0.58 ([#1123](https://github.com/rust-lang/cc-rs/pull/1123)) - -## [1.0.104](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.103...cc-v1.0.104) - 2024-07-01 - -### Other -- Fixed link break about compile-time-requirements ([#1118](https://github.com/rust-lang/cc-rs/pull/1118)) - -## [1.0.103](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.102...cc-v1.0.103) - 2024-06-30 - -### Other -- Fix compilation for wasm: env WASI_SYSROOT should be optional ([#1114](https://github.com/rust-lang/cc-rs/pull/1114)) - -## [1.0.102](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.101...cc-v1.0.102) - 2024-06-29 - -### Other -- Fix invalid wasi targets compatibility ([#1105](https://github.com/rust-lang/cc-rs/pull/1105)) -- Speedup regenerate-target-info and regenerate-windows-sys ([#1110](https://github.com/rust-lang/cc-rs/pull/1110)) - -## [1.0.101](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.100...cc-v1.0.101) - 2024-06-25 - -### Other -- Use `Build::getenv` instead of `env::var*` in anywhere that makes sense ([#1103](https://github.com/rust-lang/cc-rs/pull/1103)) - -## [1.0.100](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.99...cc-v1.0.100) - 2024-06-23 - -### Other -- Update publish.yml to use release-plz ([#1101](https://github.com/rust-lang/cc-rs/pull/1101)) -- Accept `OsStr` instead of `str` for flags ([#1100](https://github.com/rust-lang/cc-rs/pull/1100)) -- Use `dep:` syntax to avoid implicit features. ([#1099](https://github.com/rust-lang/cc-rs/pull/1099)) -- Minor clippy fixes. ([#1098](https://github.com/rust-lang/cc-rs/pull/1098)) -- Fix WASI compilation for C++ ([#1083](https://github.com/rust-lang/cc-rs/pull/1083)) -- Regenerate windows sys bindings ([#1096](https://github.com/rust-lang/cc-rs/pull/1096)) -- Rename regenerate-windows-sys to regenerate-windows-sys.yml ([#1095](https://github.com/rust-lang/cc-rs/pull/1095)) -- Create regenerate-windows-sys.yml ([#1094](https://github.com/rust-lang/cc-rs/pull/1094)) -- Update windows-bindgen requirement from 0.56 to 0.57 ([#1091](https://github.com/rust-lang/cc-rs/pull/1091)) -- Eagerly close tempfile to fix [#1082](https://github.com/rust-lang/cc-rs/pull/1082) ([#1087](https://github.com/rust-lang/cc-rs/pull/1087)) -- Output msvc.exe in the output directory ([#1090](https://github.com/rust-lang/cc-rs/pull/1090)) -- Fix clippy warnings on Windows ([#1088](https://github.com/rust-lang/cc-rs/pull/1088)) -- Don't try to free DLL on drop ([#1089](https://github.com/rust-lang/cc-rs/pull/1089)) -- Fix panic safety issue in StderrForwarder ([#1079](https://github.com/rust-lang/cc-rs/pull/1079)) diff --git a/cc-1.2.62/Cargo.lock b/cc-1.2.62/Cargo.lock deleted file mode 100644 index 611989da9e..0000000000 --- a/cc-1.2.62/Cargo.lock +++ /dev/null @@ -1,492 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - -[[package]] -name = "bitflags" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" - -[[package]] -name = "cc" -version = "1.2.62" -dependencies = [ - "find-msvc-tools", - "jobserver", - "libc", - "shlex", - "tempfile", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys", -] - -[[package]] -name = "fastrand" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" - -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "libc", - "r-efi 5.3.0", - "wasip2", -] - -[[package]] -name = "getrandom" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" -dependencies = [ - "cfg-if", - "libc", - "r-efi 6.0.0", - "wasip2", - "wasip3", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash", -] - -[[package]] -name = "hashbrown" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - -[[package]] -name = "indexmap" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" -dependencies = [ - "equivalent", - "hashbrown 0.17.0", - "serde", - "serde_core", -] - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - -[[package]] -name = "libc" -version = "0.2.186" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" - -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - -[[package]] -name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" - -[[package]] -name = "memchr" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn", -] - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "r-efi" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" - -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", -] - -[[package]] -name = "semver" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.149" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tempfile" -version = "3.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" -dependencies = [ - "fastrand", - "getrandom 0.4.2", - "once_cell", - "rustix", - "windows-sys", -] - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "wasip2" -version = "1.0.3+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" -dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen 0.51.0", -] - -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen" -version = "0.57.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - -[[package]] -name = "zmij" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/cc-1.2.62/Cargo.toml b/cc-1.2.62/Cargo.toml deleted file mode 100644 index 673bcd45c7..0000000000 --- a/cc-1.2.62/Cargo.toml +++ /dev/null @@ -1,79 +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 are reading this file be aware that the original Cargo.toml -# will likely look very different (and much more reasonable). -# See Cargo.toml.orig for the original contents. - -[package] -edition = "2018" -rust-version = "1.63" -name = "cc" -version = "1.2.62" -authors = ["Alex Crichton "] -build = false -exclude = [ - "/.github", - "tests", - "src/bin", -] -autolib = false -autobins = false -autoexamples = false -autotests = false -autobenches = false -description = """ -A build-time dependency for Cargo build scripts to assist in invoking the native -C compiler to compile native C code into a static archive to be linked into Rust -code. -""" -homepage = "https://github.com/rust-lang/cc-rs" -documentation = "https://docs.rs/cc" -readme = "README.md" -keywords = ["build-dependencies"] -categories = ["development-tools::build-utils"] -license = "MIT OR Apache-2.0" -repository = "https://github.com/rust-lang/cc-rs" - -[features] -jobserver = [] -parallel = [ - "dep:libc", - "dep:jobserver", -] - -[lib] -name = "cc" -path = "src/lib.rs" - -[dependencies.find-msvc-tools] -version = "0.1.9" - -[dependencies.jobserver] -version = "0.1.30" -optional = true -default-features = false - -[dependencies.shlex] -version = "1.3.0" - -[dev-dependencies.tempfile] -version = "3" - -[target."cfg(unix)".dependencies.libc] -version = "0.2.62" -optional = true -default-features = false - -[lints.rust.unexpected_cfgs] -level = "allow" -priority = 0 -check-cfg = ["cfg(disable_clang_cl_tests)"] - -[profile.release] -opt-level = 3 -lto = true diff --git a/cc-1.2.62/Cargo.toml.orig b/cc-1.2.62/Cargo.toml.orig deleted file mode 100644 index 35890682e3..0000000000 --- a/cc-1.2.62/Cargo.toml.orig +++ /dev/null @@ -1,59 +0,0 @@ -[package] -name = "cc" -version = "1.2.62" -authors = ["Alex Crichton "] -license = "MIT OR Apache-2.0" -repository = "https://github.com/rust-lang/cc-rs" -homepage = "https://github.com/rust-lang/cc-rs" -documentation = "https://docs.rs/cc" -description = """ -A build-time dependency for Cargo build scripts to assist in invoking the native -C compiler to compile native C code into a static archive to be linked into Rust -code. -""" -keywords = ["build-dependencies"] -readme = "README.md" -categories = ["development-tools::build-utils"] -# The binary target is only used by tests. -exclude = ["/.github", "tests", "src/bin"] -edition = "2018" -rust-version = "1.63" - -[dependencies] -jobserver = { version = "0.1.30", default-features = false, optional = true } -shlex = "1.3.0" -find-msvc-tools = { version = "0.1.9", path = "find-msvc-tools" } - -[target.'cfg(unix)'.dependencies] -# Don't turn on the feature "std" for this, see https://github.com/rust-lang/cargo/issues/4866 -# which is still an issue with `resolver = "1"`. -libc = { version = "0.2.62", default-features = false, optional = true } - -[features] -parallel = ["dep:libc", "dep:jobserver"] -# This is a placeholder feature for people who incorrectly used `cc` with `features = ["jobserver"]` -# so that they aren't broken. This has never enabled `parallel`, so we won't do that. -jobserver = [] - -[dev-dependencies] -tempfile = "3" - -[workspace] -members = [ - "find-msvc-tools", - "dev-tools/cc-test", - "dev-tools/gen-target-info", - "dev-tools/gen-windows-sys-binding", - "dev-tools/wasi-test", -] - -[patch.crates-io] -cc = { path = "." } - -[lints.rust] -unexpected_cfgs = { level = "allow", check-cfg = ['cfg(disable_clang_cl_tests)'] } - -[profile.release] -opt-level = 3 # Or "s" or "z" for different optimization goals -lto = true - diff --git a/cc-1.2.62/src/lib.rs b/cc-1.2.62/src/lib.rs deleted file mode 100644 index 5afd420de3..0000000000 --- a/cc-1.2.62/src/lib.rs +++ /dev/null @@ -1,4505 +0,0 @@ -//! A library for [Cargo build scripts](https://doc.rust-lang.org/cargo/reference/build-scripts.html) -//! to compile a set of C/C++/assembly/CUDA files into a static archive for Cargo -//! to link into the crate being built. This crate does not compile code itself; -//! it calls out to the default compiler for the platform. This crate will -//! automatically detect situations such as cross compilation and -//! [various environment variables](#external-configuration-via-environment-variables) and will build code appropriately. -//! -//! # Example -//! -//! First, you'll want to both add a build script for your crate (`build.rs`) and -//! also add this crate to your `Cargo.toml` via: -//! -//! ```toml -//! [build-dependencies] -//! cc = "1.0" -//! ``` -//! -//! Next up, you'll want to write a build script like so: -//! -//! ```rust,no_run -//! // build.rs -//! cc::Build::new() -//! .file("foo.c") -//! .file("bar.c") -//! .compile("foo"); -//! ``` -//! -//! And that's it! Running `cargo build` should take care of the rest and your Rust -//! application will now have the C files `foo.c` and `bar.c` compiled into a file -//! named `libfoo.a`. If the C files contain -//! -//! ```c -//! void foo_function(void) { ... } -//! ``` -//! -//! and -//! -//! ```c -//! int32_t bar_function(int32_t x) { ... } -//! ``` -//! -//! you can call them from Rust by declaring them in -//! your Rust code like so: -//! -//! ```rust,no_run -//! extern "C" { -//! fn foo_function(); -//! fn bar_function(x: i32) -> i32; -//! } -//! -//! pub fn call() { -//! unsafe { -//! foo_function(); -//! bar_function(42); -//! } -//! } -//! -//! fn main() { -//! call(); -//! } -//! ``` -//! -//! See [the Rustonomicon](https://doc.rust-lang.org/nomicon/ffi.html) for more details. -//! -//! # External configuration via environment variables -//! -//! To control the programs and flags used for building, the builder can set a -//! number of different environment variables. -//! -//! * `CFLAGS` - a series of space separated flags passed to compilers. Note that -//! individual flags cannot currently contain spaces, so doing -//! something like: `-L=foo\ bar` is not possible. -//! * `CC` - the actual C compiler used. Note that this supports passing a known -//! wrapper via `sccache cc`. This compiler must understand the `-c` flag. For -//! certain `TARGET`s, it also is assumed to know about other flags (most -//! common is `-fPIC`). -//! ccache, distcc, sccache, icecc, cachepot and buildcache are supported, -//! for sccache, simply set `CC` to `sccache cc`. -//! For other custom `CC` wrapper, just set `CC_KNOWN_WRAPPER_CUSTOM` -//! to the custom wrapper used in `CC`. -//! * `AR` - the `ar` (archiver) executable to use to build the static library. -//! * `CRATE_CC_NO_DEFAULTS` - the default compiler flags may cause conflicts in -//! some cross compiling scenarios. Setting this variable -//! will disable the generation of default compiler -//! flags. -//! * `CC_ENABLE_DEBUG_OUTPUT` - if set, compiler command invocations and exit codes will -//! be logged to stdout. This is useful for debugging build script issues, but can be -//! overly verbose for normal use. -//! * `CC_SHELL_ESCAPED_FLAGS` - if set, `*FLAGS` will be parsed as if they were shell -//! arguments (similar to `make` and `cmake`) rather than splitting them on each space. -//! For example, with `CFLAGS='a "b c"'`, the compiler will be invoked with 2 arguments - -//! `a` and `b c` - rather than 3: `a`, `"b` and `c"`. -//! * `CXX...` - see [C++ Support](#c-support). -//! * `CC_FORCE_DISABLE` - If set, `cc` will never run any [`Command`]s, and methods that -//! would return an [`Error`]. This is intended for use by third-party build systems -//! which want to be absolutely sure that they are in control of building all -//! dependencies. Note that operations that return [`Tool`]s such as -//! [`Build::get_compiler`] may produce less accurate results as in some cases `cc` runs -//! commands in order to locate compilers. Additionally, this does nothing to prevent -//! users from running [`Tool::to_command`] and executing the [`Command`] themselves. -//! * `RUSTC_WRAPPER` - If set, the specified command will be prefixed to the compiler -//! command. This is useful for projects that want to use -//! [sccache](https://github.com/mozilla/sccache), -//! [buildcache](https://gitlab.com/bits-n-bites/buildcache), or -//! [cachepot](https://github.com/paritytech/cachepot). -//! -//! Furthermore, projects using this crate may specify custom environment variables -//! to be inspected, for example via the `Build::try_flags_from_environment` -//! function. Consult the project’s own documentation or its use of the `cc` crate -//! for any additional variables it may use. -//! -//! Each of these variables can also be supplied with certain prefixes and suffixes, -//! in the following prioritized order: -//! -//! 1. `_` - for example, `CC_x86_64-unknown-linux-gnu` or `CC_thumbv8m.main-none-eabi` -//! 2. `_` - for example, `CC_x86_64_unknown_linux_gnu` or `CC_thumbv8m_main_none_eabi` (both periods and underscores are replaced) -//! 3. `_` - for example, `HOST_CC` or `TARGET_CFLAGS` -//! 4. `` - a plain `CC`, `AR` as above. -//! -//! If none of these variables exist, cc-rs uses built-in defaults. -//! -//! In addition to the above optional environment variables, `cc-rs` has some -//! functions with hard requirements on some variables supplied by [cargo's -//! build-script driver][cargo] that it has the `TARGET`, `OUT_DIR`, `OPT_LEVEL`, -//! and `HOST` variables. -//! -//! [cargo]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#inputs-to-the-build-script -//! -//! # Optional features -//! -//! ## Parallel -//! -//! Currently cc-rs supports parallel compilation (think `make -jN`) but this -//! feature is turned off by default. To enable cc-rs to compile C/C++ in parallel, -//! you can change your dependency to: -//! -//! ```toml -//! [build-dependencies] -//! cc = { version = "1.0", features = ["parallel"] } -//! ``` -//! -//! By default cc-rs will limit parallelism to `$NUM_JOBS`, or if not present it -//! will limit it to the number of cpus on the machine. If you are using cargo, -//! use `-jN` option of `build`, `test` and `run` commands as `$NUM_JOBS` -//! is supplied by cargo. -//! -//! # Compile-time Requirements -//! -//! To work properly this crate needs access to a C compiler when the build script -//! is being run. This crate does not ship a C compiler with it. The compiler -//! required varies per platform, but there are three broad categories: -//! -//! * Unix platforms require `cc` to be the C compiler. This can be found by -//! installing cc/clang on Linux distributions and Xcode on macOS, for example. -//! * Windows platforms targeting MSVC (e.g. your target name ends in `-msvc`) -//! require Visual Studio to be installed. `cc-rs` attempts to locate it, and -//! if it fails, `cl.exe` is expected to be available in `PATH`. This can be -//! set up by running the appropriate developer tools shell. -//! * When using `prefer_clang_cl_over_msvc`, make sure that the `C++ Clang compiler for Windows` component -//! is installed through the Visual Studio Installer, so that `cc-rs` can find `clang-cl.exe`. -//! * Windows platforms targeting MinGW (e.g. your target name ends in `-gnu`) -//! require `cc` to be available in `PATH`. We recommend the -//! [MinGW-w64](https://www.mingw-w64.org/) distribution. -//! You may also acquire it via -//! [MSYS2](https://www.msys2.org/), as explained [here][msys2-help]. Make sure -//! to install the appropriate architecture corresponding to your installation of -//! rustc. GCC from older [MinGW](http://www.mingw.org/) project is compatible -//! only with 32-bit rust compiler. -//! -//! [msys2-help]: https://github.com/rust-lang/rust/blob/master/INSTALL.md#building-on-windows -//! -//! # C++ support -//! -//! `cc-rs` supports C++ libraries compilation by using the `cpp` method on -//! `Build`: -//! -//! ```rust,no_run -//! cc::Build::new() -//! .cpp(true) // Switch to C++ library compilation. -//! .file("foo.cpp") -//! .compile("foo"); -//! ``` -//! -//! For C++ libraries, the `CXX` and `CXXFLAGS` environment variables are used instead of `CC` and `CFLAGS`. -//! -//! The C++ standard library may be linked to the crate target. By default it's `libc++` for macOS, FreeBSD, and OpenBSD, `libc++_shared` for Android, nothing for MSVC, and `libstdc++` for anything else. It can be changed in one of two ways: -//! -//! 1. by using the `cpp_link_stdlib` method on `Build`: -//! ```rust,no_run -//! cc::Build::new() -//! .cpp(true) -//! .file("foo.cpp") -//! .cpp_link_stdlib("stdc++") // use libstdc++ -//! .compile("foo"); -//! ``` -//! 2. by setting the `CXXSTDLIB` environment variable. -//! -//! In particular, for Android you may want to [use `c++_static` if you have at most one shared library](https://developer.android.com/ndk/guides/cpp-support). -//! -//! Remember that C++ does name mangling so `extern "C"` might be required to enable Rust linker to find your functions. -//! -//! # CUDA C++ support -//! -//! `cc-rs` also supports compiling CUDA C++ libraries by using the `cuda` method -//! on `Build`: -//! -//! ```rust,no_run -//! cc::Build::new() -//! // Switch to CUDA C++ library compilation using NVCC. -//! .cuda(true) -//! .cudart("static") -//! // Generate code for Maxwell (GTX 970, 980, 980 Ti, Titan X). -//! .flag("-gencode").flag("arch=compute_52,code=sm_52") -//! // Generate code for Maxwell (Jetson TX1). -//! .flag("-gencode").flag("arch=compute_53,code=sm_53") -//! // Generate code for Pascal (GTX 1070, 1080, 1080 Ti, Titan Xp). -//! .flag("-gencode").flag("arch=compute_61,code=sm_61") -//! // Generate code for Pascal (Tesla P100). -//! .flag("-gencode").flag("arch=compute_60,code=sm_60") -//! // Generate code for Pascal (Jetson TX2). -//! .flag("-gencode").flag("arch=compute_62,code=sm_62") -//! // Generate code in parallel -//! .flag("-t0") -//! .file("bar.cu") -//! .compile("bar"); -//! ``` -//! -//! # Speed up compilation with sccache -//! -//! `cc-rs` does not handle incremental compilation like `make` or `ninja`. It -//! always compiles the all sources, no matter if they have changed or not. -//! This would be time-consuming in large projects. To save compilation time, -//! you can use [sccache](https://github.com/mozilla/sccache) by setting -//! environment variable `RUSTC_WRAPPER=sccache`, which will use cached `.o` -//! files if the sources are unchanged. - -#![doc(html_root_url = "https://docs.rs/cc/1.0")] -#![deny(warnings)] -#![deny(missing_docs)] -#![deny(clippy::disallowed_methods)] -#![warn(clippy::doc_markdown)] - -use std::borrow::Cow; -use std::collections::HashMap; -use std::env; -use std::ffi::{OsStr, OsString}; -use std::fmt::{self, Display}; -use std::fs; -use std::io::{self, Write}; -use std::path::{Component, Path, PathBuf}; -use std::process::{Command, Stdio}; -use std::sync::{Arc, RwLock}; - -use shlex::Shlex; - -#[cfg(feature = "parallel")] -mod parallel; - -mod target; -use self::target::*; - -/// A helper module to looking for windows-specific tools: -/// 1. On Windows host, probe the Windows Registry if needed; -/// 2. On non-Windows host, check specified environment variables. -pub mod windows_registry { - // Regardless of whether this should be in this crate's public API, - // it has been since 2015, so don't break it. - - /// Attempts to find a tool within an MSVC installation using the Windows - /// registry as a point to search from. - /// - /// The `arch_or_target` argument is the architecture or the Rust target name - /// that the tool should work for (e.g. compile or link for). The supported - /// architecture names are: - /// - `"x64"` or `"x86_64"` - /// - `"arm64"` or `"aarch64"` - /// - `"arm64ec"` - /// - `"x86"`, `"i586"` or `"i686"` - /// - `"arm"` or `"thumbv7a"` - /// - /// The `tool` argument is the tool to find. Supported tools include: - /// - MSVC tools: `cl.exe`, `link.exe`, `lib.exe`, etc. - /// - `MSBuild`: `msbuild.exe` - /// - Visual Studio IDE: `devenv.exe` - /// - Clang/LLVM tools: `clang.exe`, `clang++.exe`, `clang-*.exe`, `llvm-*.exe`, `lld.exe`, etc. - /// - /// This function will return `None` if the tool could not be found, or it will - /// return `Some(cmd)` which represents a command that's ready to execute the - /// tool with the appropriate environment variables set. - /// - /// Note that this function always returns `None` for non-MSVC targets (if a - /// full target name was specified). - pub fn find(arch_or_target: &str, tool: &str) -> Option { - ::find_msvc_tools::find(arch_or_target, tool) - } - - /// A version of Visual Studio - #[derive(Debug, PartialEq, Eq, Copy, Clone)] - #[non_exhaustive] - pub enum VsVers { - /// Visual Studio 12 (2013) - #[deprecated = "Visual Studio 12 is no longer supported. cc will never return this value."] - Vs12, - /// Visual Studio 14 (2015) - Vs14, - /// Visual Studio 15 (2017) - Vs15, - /// Visual Studio 16 (2019) - Vs16, - /// Visual Studio 17 (2022) - Vs17, - /// Visual Studio 18 (2026) - Vs18, - } - - /// Find the most recent installed version of Visual Studio - /// - /// This is used by the cmake crate to figure out the correct - /// generator. - pub fn find_vs_version() -> Result { - ::find_msvc_tools::find_vs_version().map(|vers| match vers { - #[allow(deprecated)] - ::find_msvc_tools::VsVers::Vs12 => VsVers::Vs12, - ::find_msvc_tools::VsVers::Vs14 => VsVers::Vs14, - ::find_msvc_tools::VsVers::Vs15 => VsVers::Vs15, - ::find_msvc_tools::VsVers::Vs16 => VsVers::Vs16, - ::find_msvc_tools::VsVers::Vs17 => VsVers::Vs17, - ::find_msvc_tools::VsVers::Vs18 => VsVers::Vs18, - _ => unreachable!("unknown VS version"), - }) - } - - /// Similar to the `find` function above, this function will attempt the same - /// operation (finding a MSVC tool in a local install) but instead returns a - /// [`Tool`](crate::Tool) which may be introspected. - pub fn find_tool(arch_or_target: &str, tool: &str) -> Option { - ::find_msvc_tools::find_tool(arch_or_target, tool).map(crate::Tool::from_find_msvc_tools) - } -} - -mod command_helpers; -use command_helpers::*; - -mod tool; -pub use tool::Tool; -use tool::{CompilerFamilyLookupCache, ToolFamily}; - -mod tempfile; - -mod utilities; -use utilities::*; - -mod flags; -use flags::*; - -#[derive(Debug, Eq, PartialEq, Hash)] -struct CompilerFlag { - compiler: Box, - flag: Box, -} - -#[derive(Debug, Default)] -struct BuildCache { - apple_sdk_root_cache: RwLock, Arc>>, - apple_versions_cache: RwLock, Arc>>, - cached_compiler_family: RwLock, - known_flag_support_status_cache: RwLock>, - target_info_parser: target::TargetInfoParser, -} - -/// A builder for compilation of a native library. -/// -/// A `Build` is the main type of the `cc` crate and is used to control all the -/// various configuration options and such of a compile. You'll find more -/// documentation on each method itself. -#[derive(Clone, Debug)] -pub struct Build { - include_directories: Vec>, - definitions: Vec<(Arc, Option>)>, - objects: Vec>, - flags: Vec>, - flags_supported: Vec>, - ar_flags: Vec>, - asm_flags: Vec>, - no_default_flags: bool, - files: Vec>, - cpp: bool, - cpp_link_stdlib: Option>>, - cpp_link_stdlib_static: bool, - cpp_set_stdlib: Option>, - cuda: bool, - cudart: Option>, - ccbin: bool, - std: Option>, - target: Option>, - /// The host compiler. - /// - /// Try to not access this directly, and instead prefer `cfg!(...)`. - host: Option>, - out_dir: Option>, - opt_level: Option>, - debug: Option>, - force_frame_pointer: Option, - env: Vec<(Arc, Arc)>, - compiler: Option>, - archiver: Option>, - ranlib: Option>, - cargo_output: CargoOutput, - link_lib_modifiers: Vec>, - pic: Option, - use_plt: Option, - static_crt: Option, - shared_flag: Option, - static_flag: Option, - warnings_into_errors: bool, - warnings: Option, - extra_warnings: Option, - emit_rerun_if_env_changed: bool, - shell_escaped_flags: Option, - build_cache: Arc, - inherit_rustflags: bool, - prefer_clang_cl_over_msvc: bool, -} - -/// Represents the types of errors that may occur while using cc-rs. -#[derive(Clone, Debug)] -enum ErrorKind { - /// Error occurred while performing I/O. - IOError, - /// Environment variable not found, with the var in question as extra info. - EnvVarNotFound, - /// Error occurred while using external tools (ie: invocation of compiler). - ToolExecError, - /// Error occurred due to missing external tools. - ToolNotFound, - /// One of the function arguments failed validation. - InvalidArgument, - /// No known macro is defined for the compiler when discovering tool family. - ToolFamilyMacroNotFound, - /// Invalid target. - InvalidTarget, - /// Unknown target. - UnknownTarget, - /// Invalid rustc flag. - InvalidFlag, - #[cfg(feature = "parallel")] - /// jobserver helpthread failure - JobserverHelpThreadError, - /// `cc` has been disabled by an environment variable. - Disabled, -} - -/// Represents an internal error that occurred, with an explanation. -#[derive(Clone, Debug)] -pub struct Error { - /// Describes the kind of error that occurred. - kind: ErrorKind, - /// More explanation of error that occurred. - message: Cow<'static, str>, -} - -impl Error { - fn new(kind: ErrorKind, message: impl Into>) -> Error { - Error { - kind, - message: message.into(), - } - } -} - -impl From for Error { - fn from(e: io::Error) -> Error { - Error::new(ErrorKind::IOError, format!("{e}")) - } -} - -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}: {}", self.kind, self.message) - } -} - -impl std::error::Error for Error {} - -/// Represents an object. -/// -/// This is a source file -> object file pair. -#[derive(Clone, Debug)] -struct Object { - src: PathBuf, - dst: PathBuf, -} - -impl Object { - /// Create a new source file -> object file pair. - fn new(src: PathBuf, dst: PathBuf) -> Object { - Object { src, dst } - } -} - -/// Configure the builder. -impl Build { - /// Construct a new instance of a blank set of configuration. - /// - /// This builder is finished with the [`compile`] function. - /// - /// [`compile`]: struct.Build.html#method.compile - pub fn new() -> Build { - Build { - include_directories: Vec::new(), - definitions: Vec::new(), - objects: Vec::new(), - flags: Vec::new(), - flags_supported: Vec::new(), - ar_flags: Vec::new(), - asm_flags: Vec::new(), - no_default_flags: false, - files: Vec::new(), - shared_flag: None, - static_flag: None, - cpp: false, - cpp_link_stdlib: None, - cpp_link_stdlib_static: false, - cpp_set_stdlib: None, - cuda: false, - cudart: None, - ccbin: true, - std: None, - target: None, - host: None, - out_dir: None, - opt_level: None, - debug: None, - force_frame_pointer: None, - env: Vec::new(), - compiler: None, - archiver: None, - ranlib: None, - cargo_output: CargoOutput::new(), - link_lib_modifiers: Vec::new(), - pic: None, - use_plt: None, - static_crt: None, - warnings: None, - extra_warnings: None, - warnings_into_errors: false, - emit_rerun_if_env_changed: true, - shell_escaped_flags: None, - build_cache: Arc::default(), - inherit_rustflags: true, - prefer_clang_cl_over_msvc: false, - } - } - - /// Add a directory to the `-I` or include path for headers - /// - /// # Example - /// - /// ```no_run - /// use std::path::Path; - /// - /// let library_path = Path::new("/path/to/library"); - /// - /// cc::Build::new() - /// .file("src/foo.c") - /// .include(library_path) - /// .include("src") - /// .compile("foo"); - /// ``` - pub fn include>(&mut self, dir: P) -> &mut Build { - self.include_directories.push(dir.as_ref().into()); - self - } - - /// Add multiple directories to the `-I` include path. - /// - /// # Example - /// - /// ```no_run - /// # use std::path::Path; - /// # let condition = true; - /// # - /// let mut extra_dir = None; - /// if condition { - /// extra_dir = Some(Path::new("/path/to")); - /// } - /// - /// cc::Build::new() - /// .file("src/foo.c") - /// .includes(extra_dir) - /// .compile("foo"); - /// ``` - pub fn includes

(&mut self, dirs: P) -> &mut Build - where - P: IntoIterator, - P::Item: AsRef, - { - for dir in dirs { - self.include(dir); - } - self - } - - /// Specify a `-D` variable with an optional value. - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .define("FOO", "BAR") - /// .define("BAZ", None) - /// .compile("foo"); - /// ``` - pub fn define<'a, V: Into>>(&mut self, var: &str, val: V) -> &mut Build { - self.definitions - .push((var.into(), val.into().map(Into::into))); - self - } - - /// Add an arbitrary object file to link in - pub fn object>(&mut self, obj: P) -> &mut Build { - self.objects.push(obj.as_ref().into()); - self - } - - /// Add arbitrary object files to link in - pub fn objects

(&mut self, objs: P) -> &mut Build - where - P: IntoIterator, - P::Item: AsRef, - { - for obj in objs { - self.object(obj); - } - self - } - - /// Add an arbitrary flag to the invocation of the compiler - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .flag("-ffunction-sections") - /// .compile("foo"); - /// ``` - pub fn flag(&mut self, flag: impl AsRef) -> &mut Build { - self.flags.push(flag.as_ref().into()); - self - } - - /// Add multiple flags to the invocation of the compiler. - /// This is equivalent to calling [`flag`](Self::flag) for each item in the iterator. - /// - /// # Example - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .flags(["-Wall", "-Wextra"]) - /// .compile("foo"); - /// ``` - pub fn flags(&mut self, flags: Iter) -> &mut Build - where - Iter: IntoIterator, - Iter::Item: AsRef, - { - for flag in flags { - self.flag(flag); - } - self - } - - /// Removes a compiler flag that was added by [`Build::flag`]. - /// - /// Will not remove flags added by other means (default flags, - /// flags from env, and so on). - /// - /// # Example - /// ``` - /// cc::Build::new() - /// .file("src/foo.c") - /// .flag("unwanted_flag") - /// .remove_flag("unwanted_flag"); - /// ``` - pub fn remove_flag(&mut self, flag: &str) -> &mut Build { - self.flags.retain(|other_flag| &**other_flag != flag); - self - } - - /// Add a flag to the invocation of the ar - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .file("src/bar.c") - /// .ar_flag("/NODEFAULTLIB:libc.dll") - /// .compile("foo"); - /// ``` - pub fn ar_flag(&mut self, flag: impl AsRef) -> &mut Build { - self.ar_flags.push(flag.as_ref().into()); - self - } - - /// Add a flag that will only be used with assembly files. - /// - /// The flag will be applied to input files with either a `.s` or - /// `.asm` extension (case insensitive). - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .asm_flag("-Wa,-defsym,abc=1") - /// .file("src/foo.S") // The asm flag will be applied here - /// .file("src/bar.c") // The asm flag will not be applied here - /// .compile("foo"); - /// ``` - pub fn asm_flag(&mut self, flag: impl AsRef) -> &mut Build { - self.asm_flags.push(flag.as_ref().into()); - self - } - - /// Add an arbitrary flag to the invocation of the compiler if it supports it - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .flag_if_supported("-Wlogical-op") // only supported by GCC - /// .flag_if_supported("-Wunreachable-code") // only supported by clang - /// .compile("foo"); - /// ``` - pub fn flag_if_supported(&mut self, flag: impl AsRef) -> &mut Build { - self.flags_supported.push(flag.as_ref().into()); - self - } - - /// Add flags from the specified environment variable. - /// - /// Normally the `cc` crate will consult with the standard set of environment - /// variables (such as `CFLAGS` and `CXXFLAGS`) to construct the compiler invocation. Use of - /// this method provides additional levers for the end user to use when configuring the build - /// process. - /// - /// Just like the standard variables, this method will search for an environment variable with - /// appropriate target prefixes, when appropriate. - /// - /// # Examples - /// - /// This method is particularly beneficial in introducing the ability to specify crate-specific - /// flags. - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .try_flags_from_environment(concat!(env!("CARGO_PKG_NAME"), "_CFLAGS")) - /// .expect("the environment variable must be specified and UTF-8") - /// .compile("foo"); - /// ``` - /// - pub fn try_flags_from_environment(&mut self, environ_key: &str) -> Result<&mut Build, Error> { - let flags = self.envflags(environ_key)?.ok_or_else(|| { - Error::new( - ErrorKind::EnvVarNotFound, - format!("could not find environment variable {environ_key}"), - ) - })?; - self.flags.extend( - flags - .into_iter() - .map(|flag| Arc::from(OsString::from(flag).as_os_str())), - ); - Ok(self) - } - - /// Set the `-shared` flag. - /// - /// This will typically be ignored by the compiler when calling [`Self::compile()`] since it only - /// produces static libraries. - /// - /// # Example - /// - /// ```no_run - /// // This will create a library named "liblibfoo.so.a" - /// cc::Build::new() - /// .file("src/foo.c") - /// .shared_flag(true) - /// .compile("libfoo.so"); - /// ``` - #[deprecated = "cc only creates static libraries, setting this does nothing"] - pub fn shared_flag(&mut self, shared_flag: bool) -> &mut Build { - self.shared_flag = Some(shared_flag); - self - } - - /// Set the `-static` flag. - /// - /// This will typically be ignored by the compiler when calling [`Self::compile()`] since it only - /// produces static libraries. - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .shared_flag(true) - /// .static_flag(true) - /// .compile("foo"); - /// ``` - #[deprecated = "cc only creates static libraries, setting this does nothing"] - pub fn static_flag(&mut self, static_flag: bool) -> &mut Build { - self.static_flag = Some(static_flag); - self - } - - /// Disables the generation of default compiler flags. The default compiler - /// flags may cause conflicts in some cross compiling scenarios. - /// - /// Setting the `CRATE_CC_NO_DEFAULTS` environment variable has the same - /// effect as setting this to `true`. The presence of the environment - /// variable and the value of `no_default_flags` will be OR'd together. - pub fn no_default_flags(&mut self, no_default_flags: bool) -> &mut Build { - self.no_default_flags = no_default_flags; - self - } - - /// Add a file which will be compiled - pub fn file>(&mut self, p: P) -> &mut Build { - self.files.push(p.as_ref().into()); - self - } - - /// Add files which will be compiled - pub fn files

(&mut self, p: P) -> &mut Build - where - P: IntoIterator, - P::Item: AsRef, - { - for file in p.into_iter() { - self.file(file); - } - self - } - - /// Get the files which will be compiled - pub fn get_files(&self) -> impl Iterator { - self.files.iter().map(AsRef::as_ref) - } - - /// Set C++ support. - /// - /// The other `cpp_*` options will only become active if this is set to - /// `true`. - /// - /// The name of the C++ standard library to link is decided by: - /// 1. If [`cpp_link_stdlib`](Build::cpp_link_stdlib) is set, use its value. - /// 2. Else if the `CXXSTDLIB` environment variable is set, use its value. - /// 3. Else the default is `c++` for OS X and BSDs, `c++_shared` for Android, - /// `None` for MSVC and `stdc++` for anything else. - pub fn cpp(&mut self, cpp: bool) -> &mut Build { - self.cpp = cpp; - self - } - - /// Set CUDA C++ support. - /// - /// Enabling CUDA will invoke the CUDA compiler, NVCC. While NVCC accepts - /// the most common compiler flags, e.g. `-std=c++17`, some project-specific - /// flags might have to be prefixed with "-Xcompiler" flag, for example as - /// `.flag("-Xcompiler").flag("-fpermissive")`. See the documentation for - /// `nvcc`, the CUDA compiler driver, at - /// for more information. - /// - /// If enabled, this also implicitly enables C++ support. - pub fn cuda(&mut self, cuda: bool) -> &mut Build { - self.cuda = cuda; - if cuda { - self.cpp = true; - self.cudart = Some("static".into()); - } - self - } - - /// Link CUDA run-time. - /// - /// This option mimics the `--cudart` NVCC command-line option. Just like - /// the original it accepts `{none|shared|static}`, with default being - /// `static`. The method has to be invoked after `.cuda(true)`, or not - /// at all, if the default is right for the project. - pub fn cudart(&mut self, cudart: &str) -> &mut Build { - if self.cuda { - self.cudart = Some(cudart.into()); - } - self - } - - /// Set CUDA host compiler. - /// - /// By default, a `-ccbin` flag will be passed to NVCC to specify the - /// underlying host compiler. The value of `-ccbin` is the same as the - /// chosen C++ compiler. This is not always desired, because NVCC might - /// not support that compiler. In this case, you can remove the `-ccbin` - /// flag so that NVCC will choose the host compiler by itself. - pub fn ccbin(&mut self, ccbin: bool) -> &mut Build { - self.ccbin = ccbin; - self - } - - /// Specify the C or C++ language standard version. - /// - /// These values are common to modern versions of GCC, Clang and MSVC: - /// - `c11` for ISO/IEC 9899:2011 - /// - `c17` for ISO/IEC 9899:2018 - /// - `c++14` for ISO/IEC 14882:2014 - /// - `c++17` for ISO/IEC 14882:2017 - /// - `c++20` for ISO/IEC 14882:2020 - /// - /// Other values have less broad support, e.g. MSVC does not support `c++11` - /// (`c++14` is the minimum), `c89` (omit the flag instead) or `c99`. - /// - /// For compiling C++ code, you should also set `.cpp(true)`. - /// - /// The default is that no standard flag is passed to the compiler, so the - /// language version will be the compiler's default. - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/modern.cpp") - /// .cpp(true) - /// .std("c++17") - /// .compile("modern"); - /// ``` - pub fn std(&mut self, std: &str) -> &mut Build { - self.std = Some(std.into()); - self - } - - /// Set warnings into errors flag. - /// - /// Disabled by default. - /// - /// Warning: turning warnings into errors only make sense - /// if you are a developer of the crate using cc-rs. - /// Some warnings only appear on some architecture or - /// specific version of the compiler. Any user of this crate, - /// or any other crate depending on it, could fail during - /// compile time. - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .warnings_into_errors(true) - /// .compile("libfoo.a"); - /// ``` - pub fn warnings_into_errors(&mut self, warnings_into_errors: bool) -> &mut Build { - self.warnings_into_errors = warnings_into_errors; - self - } - - /// Set warnings flags. - /// - /// Adds some flags: - /// - "-Wall" for MSVC. - /// - "-Wall", "-Wextra" for GNU and Clang. - /// - /// Enabled by default. - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .warnings(false) - /// .compile("libfoo.a"); - /// ``` - pub fn warnings(&mut self, warnings: bool) -> &mut Build { - self.warnings = Some(warnings); - self.extra_warnings = Some(warnings); - self - } - - /// Set extra warnings flags. - /// - /// Adds some flags: - /// - nothing for MSVC. - /// - "-Wextra" for GNU and Clang. - /// - /// Enabled by default. - /// - /// # Example - /// - /// ```no_run - /// // Disables -Wextra, -Wall remains enabled: - /// cc::Build::new() - /// .file("src/foo.c") - /// .extra_warnings(false) - /// .compile("libfoo.a"); - /// ``` - pub fn extra_warnings(&mut self, warnings: bool) -> &mut Build { - self.extra_warnings = Some(warnings); - self - } - - /// Set the standard library to link against when compiling with C++ - /// support. - /// - /// If the `CXXSTDLIB` environment variable is set, its value will - /// override the default value, but not the value explicitly set by calling - /// this function. - /// - /// A value of `None` indicates that no automatic linking should happen, - /// otherwise cargo will link against the specified library. - /// - /// The given library name must not contain the `lib` prefix. - /// - /// Common values: - /// - `stdc++` for GNU - /// - `c++` for Clang - /// - `c++_shared` or `c++_static` for Android - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .shared_flag(true) - /// .cpp_link_stdlib("stdc++") - /// .compile("libfoo.so"); - /// ``` - pub fn cpp_link_stdlib<'a, V: Into>>( - &mut self, - cpp_link_stdlib: V, - ) -> &mut Build { - self.cpp_link_stdlib = Some(cpp_link_stdlib.into().map(Arc::from)); - self - } - - /// Force linker to statically link C++ stdlib. By default cc-rs will emit - /// rustc-link flag to link against system C++ stdlib (e.g. libstdc++.so, libc++.so) - /// Provide value of `true` if linking against system library is not desired - /// - /// Note that for `wasm32` target C++ stdlib will always be linked statically - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.cpp") - /// .cpp(true) - /// .cpp_link_stdlib("stdc++") - /// .cpp_link_stdlib_static(true) - /// .compile("foo"); - /// ``` - pub fn cpp_link_stdlib_static(&mut self, is_static: bool) -> &mut Build { - self.cpp_link_stdlib_static = is_static; - self - } - - /// Force the C++ compiler to use the specified standard library. - /// - /// Setting this option will automatically set `cpp_link_stdlib` to the same - /// value. - /// - /// The default value of this option is always `None`. - /// - /// This option has no effect when compiling for a Visual Studio based - /// target. - /// - /// This option sets the `-stdlib` flag, which is only supported by some - /// compilers (clang, icc) but not by others (gcc). The library will not - /// detect which compiler is used, as such it is the responsibility of the - /// caller to ensure that this option is only used in conjunction with a - /// compiler which supports the `-stdlib` flag. - /// - /// A value of `None` indicates that no specific C++ standard library should - /// be used, otherwise `-stdlib` is added to the compile invocation. - /// - /// The given library name must not contain the `lib` prefix. - /// - /// Common values: - /// - `stdc++` for GNU - /// - `c++` for Clang - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .cpp_set_stdlib("c++") - /// .compile("libfoo.a"); - /// ``` - pub fn cpp_set_stdlib<'a, V: Into>>( - &mut self, - cpp_set_stdlib: V, - ) -> &mut Build { - let cpp_set_stdlib = cpp_set_stdlib.into().map(Arc::from); - self.cpp_set_stdlib.clone_from(&cpp_set_stdlib); - self.cpp_link_stdlib = Some(cpp_set_stdlib); - self - } - - /// Configures the `rustc` target this configuration will be compiling - /// for. - /// - /// This will fail if using a target not in a pre-compiled list taken from - /// `rustc +nightly --print target-list`. The list will be updated - /// periodically. - /// - /// You should avoid setting this in build scripts, target information - /// will instead be retrieved from the environment variables `TARGET` and - /// `CARGO_CFG_TARGET_*` that Cargo sets. - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .target("aarch64-linux-android") - /// .compile("foo"); - /// ``` - pub fn target(&mut self, target: &str) -> &mut Build { - self.target = Some(target.into()); - self - } - - /// Configures the host assumed by this configuration. - /// - /// This option is automatically scraped from the `HOST` environment - /// variable by build scripts, so it's not required to call this function. - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .host("arm-linux-gnueabihf") - /// .compile("foo"); - /// ``` - pub fn host(&mut self, host: &str) -> &mut Build { - self.host = Some(host.into()); - self - } - - /// Configures the optimization level of the generated object files. - /// - /// This option is automatically scraped from the `OPT_LEVEL` environment - /// variable by build scripts, so it's not required to call this function. - pub fn opt_level(&mut self, opt_level: u32) -> &mut Build { - self.opt_level = Some(opt_level.to_string().into()); - self - } - - /// Configures the optimization level of the generated object files. - /// - /// This option is automatically scraped from the `OPT_LEVEL` environment - /// variable by build scripts, so it's not required to call this function. - pub fn opt_level_str(&mut self, opt_level: &str) -> &mut Build { - self.opt_level = Some(opt_level.into()); - self - } - - /// Configures whether the compiler will emit debug information when - /// generating object files. - /// - /// This option is automatically scraped from the `DEBUG` environment - /// variable by build scripts, so it's not required to call this function. - pub fn debug(&mut self, debug: bool) -> &mut Build { - self.debug = Some(debug.to_string().into()); - self - } - - /// Configures whether the compiler will emit debug information when - /// generating object files. - /// - /// This should be one of the values accepted by Cargo's [`debug`][1] - /// profile setting, which cc-rs will try to map to the appropriate C - /// compiler flag. - /// - /// This option is automatically scraped from the `DEBUG` environment - /// variable by build scripts, so it's not required to call this function. - /// - /// [1]: https://doc.rust-lang.org/cargo/reference/profiles.html#debug - pub fn debug_str(&mut self, debug: &str) -> &mut Build { - self.debug = Some(debug.into()); - self - } - - /// Configures whether the compiler will emit instructions to store - /// frame pointers during codegen. - /// - /// This option is automatically enabled when debug information is emitted. - /// Otherwise the target platform compiler's default will be used. - /// You can use this option to force a specific setting. - pub fn force_frame_pointer(&mut self, force: bool) -> &mut Build { - self.force_frame_pointer = Some(force); - self - } - - /// Configures the output directory where all object files and static - /// libraries will be located. - /// - /// This option is automatically scraped from the `OUT_DIR` environment - /// variable by build scripts, so it's not required to call this function. - pub fn out_dir>(&mut self, out_dir: P) -> &mut Build { - self.out_dir = Some(out_dir.as_ref().into()); - self - } - - /// Configures the compiler to be used to produce output. - /// - /// This option is automatically determined from the target platform or a - /// number of environment variables, so it's not required to call this - /// function. - pub fn compiler>(&mut self, compiler: P) -> &mut Build { - self.compiler = Some(compiler.as_ref().into()); - self - } - - /// Configures the tool used to assemble archives. - /// - /// This option is automatically determined from the target platform or a - /// number of environment variables, so it's not required to call this - /// function. - pub fn archiver>(&mut self, archiver: P) -> &mut Build { - self.archiver = Some(archiver.as_ref().into()); - self - } - - /// Configures the tool used to index archives. - /// - /// This option is automatically determined from the target platform or a - /// number of environment variables, so it's not required to call this - /// function. - pub fn ranlib>(&mut self, ranlib: P) -> &mut Build { - self.ranlib = Some(ranlib.as_ref().into()); - self - } - - /// Define whether metadata should be emitted for cargo allowing it to - /// automatically link the binary. Defaults to `true`. - /// - /// The emitted metadata is: - /// - /// - `rustc-link-lib=static=`*compiled lib* - /// - `rustc-link-search=native=`*target folder* - /// - When target is MSVC, the ATL-MFC libs are added via `rustc-link-search=native=` - /// - When C++ is enabled, the C++ stdlib is added via `rustc-link-lib` - /// - If `emit_rerun_if_env_changed` is not `false`, `rerun-if-env-changed=`*env* - /// - pub fn cargo_metadata(&mut self, cargo_metadata: bool) -> &mut Build { - self.cargo_output.metadata = cargo_metadata; - self - } - - /// Define whether compile warnings should be emitted for cargo. Defaults to - /// `true`. - /// - /// If disabled, compiler messages will not be printed. - /// Issues unrelated to the compilation will always produce cargo warnings regardless of this setting. - pub fn cargo_warnings(&mut self, cargo_warnings: bool) -> &mut Build { - self.cargo_output.warnings = cargo_warnings; - self - } - - /// Define whether debug information should be emitted for cargo. Defaults to whether - /// or not the environment variable `CC_ENABLE_DEBUG_OUTPUT` is set. - /// - /// If enabled, the compiler will emit debug information when generating object files, - /// such as the command invoked and the exit status. - pub fn cargo_debug(&mut self, cargo_debug: bool) -> &mut Build { - self.cargo_output.debug = cargo_debug; - self - } - - /// Define whether compiler output (to stdout) should be emitted. Defaults to `true` - /// (forward compiler stdout to this process' stdout) - /// - /// Some compilers emit errors to stdout, so if you *really* need stdout to be clean - /// you should also set this to `false`. - pub fn cargo_output(&mut self, cargo_output: bool) -> &mut Build { - self.cargo_output.output = if cargo_output { - OutputKind::Forward - } else { - OutputKind::Discard - }; - self - } - - /// Adds a native library modifier that will be added to the - /// `rustc-link-lib=static:MODIFIERS=LIBRARY_NAME` metadata line - /// emitted for cargo if `cargo_metadata` is enabled. - /// See - /// for the list of modifiers accepted by rustc. - pub fn link_lib_modifier(&mut self, link_lib_modifier: impl AsRef) -> &mut Build { - self.link_lib_modifiers - .push(link_lib_modifier.as_ref().into()); - self - } - - /// Configures whether the compiler will emit position independent code. - /// - /// This option defaults to `false` for `windows-gnu` and bare metal targets and - /// to `true` for all other targets. - pub fn pic(&mut self, pic: bool) -> &mut Build { - self.pic = Some(pic); - self - } - - /// Configures whether the Procedure Linkage Table is used for indirect - /// calls into shared libraries. - /// - /// The PLT is used to provide features like lazy binding, but introduces - /// a small performance loss due to extra pointer indirection. Setting - /// `use_plt` to `false` can provide a small performance increase. - /// - /// Note that skipping the PLT requires a recent version of GCC/Clang. - /// - /// This only applies to ELF targets. It has no effect on other platforms. - pub fn use_plt(&mut self, use_plt: bool) -> &mut Build { - self.use_plt = Some(use_plt); - self - } - - /// Define whether metadata should be emitted for cargo to only trigger - /// rebuild when detected environment changes, by default build script is - /// always run on every compilation if no rerun cargo metadata is emitted. - /// - /// NOTE that cc does not emit metadata to detect changes for `PATH`, since it could - /// be changed every compilation yet does not affect the result of compilation - /// (i.e. rust-analyzer adds temporary directory to `PATH`). - /// - /// cc in general, has no way detecting changes to compiler, as there are so many ways to - /// change it and sidestep the detection, for example the compiler might be wrapped in a script - /// so detecting change of the file, or using checksum won't work. - /// - /// We recommend users to decide for themselves, if they want rebuild if the compiler has been upgraded - /// or changed, and how to detect that. - /// - /// This has no effect if the `cargo_metadata` option is `false`. - /// - /// This option defaults to `true`. - pub fn emit_rerun_if_env_changed(&mut self, emit_rerun_if_env_changed: bool) -> &mut Build { - self.emit_rerun_if_env_changed = emit_rerun_if_env_changed; - self - } - - /// Configures whether the /MT flag or the /MD flag will be passed to msvc build tools. - /// - /// This option defaults to `false`, and affect only msvc targets. - pub fn static_crt(&mut self, static_crt: bool) -> &mut Build { - self.static_crt = Some(static_crt); - self - } - - /// Configure whether *FLAGS variables are parsed using `shlex`, similarly to `make` and - /// `cmake`. - /// - /// This option defaults to `false`. - pub fn shell_escaped_flags(&mut self, shell_escaped_flags: bool) -> &mut Build { - self.shell_escaped_flags = Some(shell_escaped_flags); - self - } - - /// Configure whether cc should automatically inherit compatible flags passed to rustc - /// from `CARGO_ENCODED_RUSTFLAGS`. - /// - /// This option defaults to `true`. - pub fn inherit_rustflags(&mut self, inherit_rustflags: bool) -> &mut Build { - self.inherit_rustflags = inherit_rustflags; - self - } - - /// Prefer to use clang-cl over msvc. - /// - /// This option defaults to `false`. - pub fn prefer_clang_cl_over_msvc(&mut self, prefer_clang_cl_over_msvc: bool) -> &mut Build { - self.prefer_clang_cl_over_msvc = prefer_clang_cl_over_msvc; - self - } - - /// Set an environment variable for compiler invocations and other child processes. - /// - /// `cc` reads a lot of different variables from the current process' environment. It currently - /// allows the following standard environment variables to be overwritten by this function: - /// - `SDKROOT` - /// - `*_DEPLOYMENT_TARGET` - /// - `WASI_SDK_ROOT` - /// - /// The logic here is "environment variables that the C compiler could itself reasonably have - /// read". - pub fn env(&mut self, key: K, val: V) -> &mut Build - where - K: AsRef, - V: AsRef, - { - self.env.push((key.as_ref().into(), val.as_ref().into())); - self - } - - // retained for backwards compatibility only - #[doc(hidden)] - #[deprecated = "use `env` instead"] - pub fn __set_env(&mut self, key: K, val: V) -> &mut Build - where - K: AsRef, - V: AsRef, - { - self.env(key, val) - } -} - -/// Invoke or fetch the compiler or archiver. -impl Build { - /// Run the compiler to test if it accepts the given flag. - /// - /// For a convenience method for setting flags conditionally, - /// see `flag_if_supported()`. - /// - /// It may return error if it's unable to run the compiler with a test file - /// (e.g. the compiler is missing or a write to the `out_dir` failed). - /// - /// Note: Once computed, the result of this call is stored in the - /// `known_flag_support` field. If `is_flag_supported(flag)` - /// is called again, the result will be read from the hash table. - pub fn is_flag_supported(&self, flag: impl AsRef) -> Result { - self.is_flag_supported_inner( - flag.as_ref(), - &self.get_base_compiler()?, - &self.get_target()?, - ) - } - - fn ensure_check_file(&self) -> Result { - let out_dir = self.get_out_dir()?; - let src = if self.cuda { - assert!(self.cpp); - out_dir.join("flag_check.cu") - } else if self.cpp { - out_dir.join("flag_check.cpp") - } else { - out_dir.join("flag_check.c") - }; - - if !src.exists() { - let mut f = fs::File::create(&src)?; - write!(f, "int main(void) {{ return 0; }}")?; - } - - Ok(src) - } - - fn is_flag_supported_inner( - &self, - flag: &OsStr, - tool: &Tool, - target: &TargetInfo<'_>, - ) -> Result { - let compiler_flag = CompilerFlag { - compiler: tool.path().into(), - flag: flag.into(), - }; - - if let Some(is_supported) = self - .build_cache - .known_flag_support_status_cache - .read() - .unwrap() - .get(&compiler_flag) - .cloned() - { - return Ok(is_supported); - } - - let out_dir = self.get_out_dir()?; - let src = self.ensure_check_file()?; - let obj = out_dir.join("flag_check"); - - let mut compiler = { - let mut cfg = Build::new(); - cfg.flag(flag) - .compiler(tool.path()) - .cargo_metadata(self.cargo_output.metadata) - .opt_level(0) - .debug(false) - .cpp(self.cpp) - .cuda(self.cuda) - .inherit_rustflags(false) - .emit_rerun_if_env_changed(self.emit_rerun_if_env_changed); - if let Some(target) = &self.target { - cfg.target(target); - } - if let Some(host) = &self.host { - cfg.host(host); - } - cfg.try_get_compiler()? - }; - - // Clang uses stderr for verbose output, which yields a false positive - // result if the CFLAGS/CXXFLAGS include -v to aid in debugging. - if compiler.family.verbose_stderr() { - compiler.remove_arg("-v".into()); - } - if compiler.is_like_clang() { - // Avoid reporting that the arg is unsupported just because the - // compiler complains that it wasn't used. - compiler.push_cc_arg("-Wno-unused-command-line-argument".into()); - } - - let mut cmd = compiler.to_command(); - command_add_output_file( - &mut cmd, - &obj, - CmdAddOutputFileArgs { - cuda: self.cuda, - is_assembler_msvc: false, - msvc: compiler.is_like_msvc(), - clang: compiler.is_like_clang(), - gnu: compiler.is_like_gnu(), - is_asm: false, - is_arm: is_arm(target), - }, - ); - - // Checking for compiler flags does not require linking (and we _must_ - // avoid making it do so, since it breaks cross-compilation when the C - // compiler isn't configured to be able to link). - // https://github.com/rust-lang/cc-rs/issues/1423 - cmd.arg("-c"); - - if compiler.supports_path_delimiter() { - cmd.arg("--"); - } - - cmd.arg(&src); - - if compiler.is_like_msvc() { - // On MSVC we need to make sure the LIB directory is included - // so the CRT can be found. - for (key, value) in &tool.env { - if key == "LIB" { - cmd.env("LIB", value); - break; - } - } - } - - let output = cmd.current_dir(out_dir).output()?; - let is_supported = output.status.success() && output.stderr.is_empty(); - - self.build_cache - .known_flag_support_status_cache - .write() - .unwrap() - .insert(compiler_flag, is_supported); - - Ok(is_supported) - } - - /// Run the compiler, generating the file `output` - /// - /// This will return a result instead of panicking; see [`Self::compile()`] for - /// the complete description. - pub fn try_compile(&self, output: &str) -> Result<(), Error> { - let mut output_components = Path::new(output).components(); - match (output_components.next(), output_components.next()) { - (Some(Component::Normal(_)), None) => {} - _ => { - return Err(Error::new( - ErrorKind::InvalidArgument, - "argument of `compile` must be a single normal path component", - )); - } - } - - let (lib_name, gnu_lib_name) = if output.starts_with("lib") && output.ends_with(".a") { - (&output[3..output.len() - 2], output.to_owned()) - } else { - let mut gnu = String::with_capacity(5 + output.len()); - gnu.push_str("lib"); - gnu.push_str(output); - gnu.push_str(".a"); - (output, gnu) - }; - let dst = self.get_out_dir()?; - - let objects = objects_from_files(&self.files, &dst)?; - - self.compile_objects(&objects)?; - self.assemble(lib_name, &dst.join(gnu_lib_name), &objects)?; - - let target = self.get_target()?; - if target.env == "msvc" { - let compiler = self.get_base_compiler()?; - let atlmfc_lib = compiler - .env() - .iter() - .find(|&(var, _)| var.as_os_str() == OsStr::new("LIB")) - .and_then(|(_, lib_paths)| { - env::split_paths(lib_paths).find(|path| { - let sub = Path::new("atlmfc/lib"); - path.ends_with(sub) || path.parent().map_or(false, |p| p.ends_with(sub)) - }) - }); - - if let Some(atlmfc_lib) = atlmfc_lib { - self.cargo_output.print_metadata(&format_args!( - "cargo:rustc-link-search=native={}", - atlmfc_lib.display() - )); - } - } - - if self.link_lib_modifiers.is_empty() { - self.cargo_output - .print_metadata(&format_args!("cargo:rustc-link-lib=static={lib_name}")); - } else { - self.cargo_output.print_metadata(&format_args!( - "cargo:rustc-link-lib=static:{}={}", - JoinOsStrs { - slice: &self.link_lib_modifiers, - delimiter: ',' - }, - lib_name - )); - } - self.cargo_output.print_metadata(&format_args!( - "cargo:rustc-link-search=native={}", - dst.display() - )); - - // Add specific C++ libraries, if enabled. - if self.cpp { - if let Some(stdlib) = self.get_cpp_link_stdlib()? { - if self.cpp_link_stdlib_static { - self.cargo_output.print_metadata(&format_args!( - "cargo:rustc-link-lib=static={}", - stdlib.display() - )); - } else { - self.cargo_output - .print_metadata(&format_args!("cargo:rustc-link-lib={}", stdlib.display())); - } - } - // Link c++ lib from WASI sysroot - if target.arch == "wasm32" { - if target.os == "wasi" { - if let Ok(wasi_sysroot) = self.wasi_sysroot() { - self.cargo_output.print_metadata(&format_args!( - "cargo:rustc-flags=-L {}/lib/{} -lstatic=c++ -lstatic=c++abi", - Path::new(&wasi_sysroot).display(), - self.get_raw_target()? - )); - } - } else if target.os == "linux" { - let musl_sysroot = self.wasm_musl_sysroot().unwrap(); - self.cargo_output.print_metadata(&format_args!( - "cargo:rustc-flags=-L {}/lib -lstatic=c++ -lstatic=c++abi", - Path::new(&musl_sysroot).display(), - )); - } - } - } - - let cudart = match &self.cudart { - Some(opt) => opt, // {none|shared|static} - None => "none", - }; - if cudart != "none" { - if let Some(nvcc) = self.which(&self.get_compiler().path, None) { - // Try to figure out the -L search path. If it fails, - // it's on user to specify one by passing it through - // RUSTFLAGS environment variable. - let mut libtst = false; - let mut libdir = nvcc; - libdir.pop(); // remove 'nvcc' - libdir.push(".."); - if cfg!(target_os = "linux") { - libdir.push("targets"); - libdir.push(format!("{}-linux", target.arch)); - if !libdir.exists() && target.arch == "aarch64" { - libdir.pop(); - libdir.push("sbsa-linux"); - } - libdir.push("lib"); - libtst = true; - } else if cfg!(target_env = "msvc") { - libdir.push("lib"); - match target.arch { - "x86_64" => { - libdir.push("x64"); - libtst = true; - } - "x86" => { - libdir.push("Win32"); - libtst = true; - } - _ => libtst = false, - } - } - if libtst && libdir.is_dir() { - self.cargo_output.print_metadata(&format_args!( - "cargo:rustc-link-search=native={}", - libdir.to_str().unwrap() - )); - } - - // And now the -l flag. - let lib = match cudart { - "shared" => "cudart", - "static" => "cudart_static", - bad => panic!("unsupported cudart option: {}", bad), - }; - self.cargo_output - .print_metadata(&format_args!("cargo:rustc-link-lib={lib}")); - } - } - - Ok(()) - } - - /// Run the compiler, generating the file `output` - /// - /// # Library name - /// - /// The `output` string argument determines the file name for the compiled - /// library. The Rust compiler will create an assembly named "lib"+output+".a". - /// MSVC will create a file named output+".lib". - /// - /// The choice of `output` is close to arbitrary, but: - /// - /// - must be nonempty, - /// - must not contain a path separator (`/`), - /// - must be unique across all `compile` invocations made by the same build - /// script. - /// - /// If your build script compiles a single source file, the base name of - /// that source file would usually be reasonable: - /// - /// ```no_run - /// cc::Build::new().file("blobstore.c").compile("blobstore"); - /// ``` - /// - /// Compiling multiple source files, some people use their crate's name, or - /// their crate's name + "-cc". - /// - /// Otherwise, please use your imagination. - /// - /// For backwards compatibility, if `output` starts with "lib" *and* ends - /// with ".a", a second "lib" prefix and ".a" suffix do not get added on, - /// but this usage is deprecated; please omit `lib` and `.a` in the argument - /// that you pass. - /// - /// # Panics - /// - /// Panics if `output` is not formatted correctly or if one of the underlying - /// compiler commands fails. It can also panic if it fails reading file names - /// or creating directories. - pub fn compile(&self, output: &str) { - if let Err(e) = self.try_compile(output) { - fail(&e.message); - } - } - - /// Run the compiler, generating intermediate files, but without linking - /// them into an archive file. - /// - /// This will return a list of compiled object files, in the same order - /// as they were passed in as `file`/`files` methods. - pub fn compile_intermediates(&self) -> Vec { - match self.try_compile_intermediates() { - Ok(v) => v, - Err(e) => fail(&e.message), - } - } - - /// Run the compiler, generating intermediate files, but without linking - /// them into an archive file. - /// - /// This will return a result instead of panicking; see `compile_intermediates()` for the complete description. - pub fn try_compile_intermediates(&self) -> Result, Error> { - let dst = self.get_out_dir()?; - let objects = objects_from_files(&self.files, &dst)?; - - self.compile_objects(&objects)?; - - Ok(objects.into_iter().map(|v| v.dst).collect()) - } - - fn compile_objects(&self, objs: &[Object]) -> Result<(), Error> { - if self.is_disabled() { - return Err(Error::new( - ErrorKind::Disabled, - "the `cc` crate's functionality has been disabled by the `CC_FORCE_DISABLE` environment variable.", - )); - } - - #[cfg(feature = "parallel")] - if objs.len() > 1 { - return parallel::run_commands_in_parallel( - &self.cargo_output, - &mut objs.iter().map(|obj| self.create_compile_object_cmd(obj)), - ); - } - - for obj in objs { - let mut cmd = self.create_compile_object_cmd(obj)?; - run(&mut cmd, &self.cargo_output)?; - } - - Ok(()) - } - - fn create_compile_object_cmd(&self, obj: &Object) -> Result { - let asm_ext = AsmFileExt::from_path(&obj.src); - let is_asm = asm_ext.is_some(); - let target = self.get_target()?; - let msvc = target.env == "msvc"; - let compiler = self.try_get_compiler()?; - - let is_assembler_msvc = msvc && asm_ext == Some(AsmFileExt::DotAsm); - let mut cmd = if is_assembler_msvc { - self.msvc_macro_assembler()? - } else { - compiler.to_command() - }; - let is_arm = is_arm(&target); - command_add_output_file( - &mut cmd, - &obj.dst, - CmdAddOutputFileArgs { - cuda: self.cuda, - is_assembler_msvc, - msvc: compiler.is_like_msvc(), - clang: compiler.is_like_clang(), - gnu: compiler.is_like_gnu(), - is_asm, - is_arm, - }, - ); - // armasm and armasm64 don't require -c option - if !is_assembler_msvc || !is_arm { - cmd.arg("-c"); - } - if self.cuda && self.cuda_file_count() > 1 { - cmd.arg("--device-c"); - } - if is_asm { - cmd.args(self.asm_flags.iter().map(std::ops::Deref::deref)); - } - - if compiler.supports_path_delimiter() && !is_assembler_msvc { - // #513: For `clang-cl`, separate flags/options from the input file. - // When cross-compiling macOS -> Windows, this avoids interpreting - // common `/Users/...` paths as the `/U` flag and triggering - // `-Wslash-u-filename` warning. - cmd.arg("--"); - } - cmd.arg(&obj.src); - - if cfg!(target_os = "macos") { - self.fix_env_for_apple_os(&mut cmd)?; - } - - Ok(cmd) - } - - /// This will return a result instead of panicking; see [`Self::expand()`] for - /// the complete description. - pub fn try_expand(&self) -> Result, Error> { - let compiler = self.try_get_compiler()?; - let mut cmd = compiler.to_command(); - cmd.arg("-E"); - - assert!( - self.files.len() <= 1, - "Expand may only be called for a single file" - ); - - let is_asm = self - .files - .iter() - .map(std::ops::Deref::deref) - .find_map(AsmFileExt::from_path) - .is_some(); - - if compiler.family == (ToolFamily::Msvc { clang_cl: true }) && !is_asm { - // #513: For `clang-cl`, separate flags/options from the input file. - // When cross-compiling macOS -> Windows, this avoids interpreting - // common `/Users/...` paths as the `/U` flag and triggering - // `-Wslash-u-filename` warning. - cmd.arg("--"); - } - - cmd.args(self.files.iter().map(std::ops::Deref::deref)); - - run_output(&mut cmd, &self.cargo_output) - } - - /// Run the compiler, returning the macro-expanded version of the input files. - /// - /// This is only relevant for C and C++ files. - /// - /// # Panics - /// Panics if more than one file is present in the config, or if compiler - /// path has an invalid file name. - /// - /// # Example - /// ```no_run - /// let out = cc::Build::new().file("src/foo.c").expand(); - /// ``` - pub fn expand(&self) -> Vec { - match self.try_expand() { - Err(e) => fail(&e.message), - Ok(v) => v, - } - } - - /// Get the compiler that's in use for this configuration. - /// - /// This function will return a `Tool` which represents the culmination - /// of this configuration at a snapshot in time. The returned compiler can - /// be inspected (e.g. the path, arguments, environment) to forward along to - /// other tools, or the `to_command` method can be used to invoke the - /// compiler itself. - /// - /// This method will take into account all configuration such as debug - /// information, optimization level, include directories, defines, etc. - /// Additionally, the compiler binary in use follows the standard - /// conventions for this path, e.g. looking at the explicitly set compiler, - /// environment variables (a number of which are inspected here), and then - /// falling back to the default configuration. - /// - /// # Panics - /// - /// Panics if an error occurred while determining the architecture. - pub fn get_compiler(&self) -> Tool { - match self.try_get_compiler() { - Ok(tool) => tool, - Err(e) => fail(&e.message), - } - } - - /// Get the compiler that's in use for this configuration. - /// - /// This will return a result instead of panicking; see - /// [`get_compiler()`](Self::get_compiler) for the complete description. - pub fn try_get_compiler(&self) -> Result { - let opt_level = self.get_opt_level()?; - let target = self.get_target()?; - - let mut cmd = self.get_base_compiler()?; - - // The flags below are added in roughly the following order: - // 1. Default flags - // - Controlled by `cc-rs`. - // 2. `rustc`-inherited flags - // - Controlled by `rustc`. - // 3. Builder flags - // - Controlled by the developer using `cc-rs` in e.g. their `build.rs`. - // 4. Environment flags - // - Controlled by the end user. - // - // This is important to allow later flags to override previous ones. - - // Copied from - // - // Disables non-English messages from localized linkers. - // Such messages may cause issues with text encoding on Windows - // and prevent inspection of msvc output in case of errors, which we occasionally do. - // This should be acceptable because other messages from rustc are in English anyway, - // and may also be desirable to improve searchability of the compiler diagnostics. - if matches!(cmd.family, ToolFamily::Msvc { clang_cl: false }) { - cmd.env.push(("VSLANG".into(), "1033".into())); - } else { - cmd.env.push(("LC_ALL".into(), "C".into())); - } - - // Disable default flag generation via `no_default_flags` or environment variable - let no_defaults = self.no_default_flags || self.get_env_boolean("CRATE_CC_NO_DEFAULTS"); - if !no_defaults { - self.add_default_flags(&mut cmd, &target, &opt_level)?; - } - - // Specify various flags that are not considered part of the default flags above. - // FIXME(madsmtm): Should these be considered part of the defaults? If no, why not? - if let Some(ref std) = self.std { - let separator = match cmd.family { - ToolFamily::Msvc { .. } => ':', - ToolFamily::Gnu | ToolFamily::Clang { .. } => '=', - }; - cmd.push_cc_arg(format!("-std{separator}{std}").into()); - } - for directory in self.include_directories.iter() { - cmd.args.push("-I".into()); - cmd.args.push(directory.as_os_str().into()); - } - if self.warnings_into_errors { - let warnings_to_errors_flag = cmd.family.warnings_to_errors_flag().into(); - cmd.push_cc_arg(warnings_to_errors_flag); - } - - // If warnings and/or extra_warnings haven't been explicitly set, - // then we set them only if the environment doesn't already have - // CFLAGS/CXXFLAGS, since those variables presumably already contain - // the desired set of warnings flags. - let envflags = self.envflags(if self.cpp { "CXXFLAGS" } else { "CFLAGS" })?; - match self.warnings { - Some(true) => { - let wflags = cmd.family.warnings_flags().into(); - cmd.push_cc_arg(wflags); - } - Some(false) => { - let wflags = cmd.family.warnings_suppression_flags().into(); - cmd.push_cc_arg(wflags); - } - None => { - if envflags.is_none() { - let wflags = cmd.family.warnings_flags().into(); - cmd.push_cc_arg(wflags); - } - } - } - if self.extra_warnings.unwrap_or(envflags.is_none()) { - if let Some(wflags) = cmd.family.extra_warnings_flags() { - cmd.push_cc_arg(wflags.into()); - } - } - - // Add cc flags inherited from matching rustc flags. - if self.inherit_rustflags { - self.add_inherited_rustflags(&mut cmd, &target)?; - } - - // Set flags configured in the builder (do this second-to-last, to allow these to override - // everything above). - for flag in self.flags.iter() { - cmd.args.push((**flag).into()); - } - for flag in self.flags_supported.iter() { - if self - .is_flag_supported_inner(flag, &cmd, &target) - .unwrap_or(false) - { - cmd.push_cc_arg((**flag).into()); - } - } - for (key, value) in self.definitions.iter() { - if let Some(ref value) = *value { - cmd.args.push(format!("-D{key}={value}").into()); - } else { - cmd.args.push(format!("-D{key}").into()); - } - } - - // Set flags from the environment (do this last, to allow these to override everything else). - if let Some(flags) = &envflags { - for arg in flags { - cmd.push_cc_arg(arg.into()); - } - } - - // Set custom env vars that the user specified with `Build::env`. - // - // Do this last, to allow overwriting the other values above. - for (key, val) in &self.env { - cmd.env.push((key.into(), val.into())); - } - - Ok(cmd) - } - - fn add_default_flags( - &self, - cmd: &mut Tool, - target: &TargetInfo<'_>, - opt_level: &str, - ) -> Result<(), Error> { - let raw_target = self.get_raw_target()?; - // Non-target flags - // If the flag is not conditioned on target variable, it belongs here :) - match cmd.family { - ToolFamily::Msvc { .. } => { - cmd.push_cc_arg("-nologo".into()); - - let crt_flag = match self.static_crt { - Some(true) => "-MT", - Some(false) => "-MD", - None => { - let features = cargo_env_var_os("CARGO_CFG_TARGET_FEATURE"); - let features = features.as_deref().unwrap_or_default(); - if features.to_string_lossy().contains("crt-static") { - "-MT" - } else { - "-MD" - } - } - }; - cmd.push_cc_arg(crt_flag.into()); - - match opt_level { - // Msvc uses /O1 to enable all optimizations that minimize code size. - "z" | "s" | "1" => cmd.push_opt_unless_duplicate("-O1".into()), - // -O3 is a valid value for gcc and clang compilers, but not msvc. Cap to /O2. - "2" | "3" => cmd.push_opt_unless_duplicate("-O2".into()), - _ => {} - } - } - ToolFamily::Gnu | ToolFamily::Clang { .. } => { - // arm-linux-androideabi-gcc 4.8 shipped with Android NDK does - // not support '-Oz' - if opt_level == "z" && !cmd.is_like_clang() { - cmd.push_opt_unless_duplicate("-Os".into()); - } else { - cmd.push_opt_unless_duplicate(format!("-O{opt_level}").into()); - } - - if cmd.is_like_clang() && target.os == "android" { - // For compatibility with code that doesn't use pre-defined `__ANDROID__` macro. - // If compiler used via ndk-build or cmake (officially supported build methods) - // this macros is defined. - // See https://android.googlesource.com/platform/ndk/+/refs/heads/ndk-release-r21/build/cmake/android.toolchain.cmake#456 - // https://android.googlesource.com/platform/ndk/+/refs/heads/ndk-release-r21/build/core/build-binary.mk#141 - cmd.push_opt_unless_duplicate("-DANDROID".into()); - } - - if target.os != "ios" - && target.os != "watchos" - && target.os != "tvos" - && target.os != "visionos" - { - cmd.push_cc_arg("-ffunction-sections".into()); - cmd.push_cc_arg("-fdata-sections".into()); - } - // Disable generation of PIC on bare-metal for now: rust-lld doesn't support this yet - // - // `rustc` also defaults to disable PIC on WASM: - // - if self.pic.unwrap_or( - target.os != "windows" - && target.os != "none" - && target.os != "uefi" - && target.os != "vita" - && target.arch != "wasm32" - && target.arch != "wasm64", - ) { - cmd.push_cc_arg("-fPIC".into()); - // PLT only applies if code is compiled with PIC support, - // and only for ELF targets. - if (target.os == "linux" || target.os == "android") - && !self.use_plt.unwrap_or(true) - { - cmd.push_cc_arg("-fno-plt".into()); - } - } - - if target.os == "wasi" { - // Link clang sysroot - if let Ok(wasi_sysroot) = self.wasi_sysroot() { - cmd.push_cc_arg( - format!("--sysroot={}", Path::new(&wasi_sysroot).display()).into(), - ); - } - - // FIXME(madsmtm): Read from `target_features` instead? - if raw_target.contains("threads") { - cmd.push_cc_arg("-pthread".into()); - } - } - - if target.os == "nto" { - // Select the target with `-V`, see qcc documentation: - // QNX 7.1: https://www.qnx.com/developers/docs/7.1/index.html#com.qnx.doc.neutrino.utilities/topic/q/qcc.html - // QNX 8.0: https://www.qnx.com/developers/docs/8.0/com.qnx.doc.neutrino.utilities/topic/q/qcc.html - // This assumes qcc/q++ as compiler, which is currently the only supported compiler for QNX. - // See for details: https://github.com/rust-lang/cc-rs/pull/1319 - let arg = match target.full_arch { - "x86" | "i586" => "-Vgcc_ntox86_cxx", - "aarch64" => "-Vgcc_ntoaarch64le_cxx", - "x86_64" => "-Vgcc_ntox86_64_cxx", - _ => { - return Err(Error::new( - ErrorKind::InvalidTarget, - format!("Unknown architecture for Neutrino QNX: {}", target.arch), - )) - } - }; - cmd.push_cc_arg(arg.into()); - } - } - } - - if self.get_debug() { - if self.cuda { - // NVCC debug flag - cmd.args.push("-G".into()); - } - let family = cmd.family; - family.add_debug_flags( - cmd, - self.get_debug_str().as_deref().unwrap_or_default(), - self.get_dwarf_version(), - ); - } - - if self.get_force_frame_pointer() { - let family = cmd.family; - family.add_force_frame_pointer(cmd); - } - - if !cmd.is_like_msvc() { - if target.arch == "x86" { - cmd.args.push("-m32".into()); - } else if target.abi == "x32" { - cmd.args.push("-mx32".into()); - } else if target.os == "aix" { - if cmd.family == ToolFamily::Gnu { - cmd.args.push("-maix64".into()); - } else { - cmd.args.push("-m64".into()); - } - } else if target.arch == "x86_64" || target.arch == "powerpc64" { - cmd.args.push("-m64".into()); - } - } - - // Target flags - match cmd.family { - ToolFamily::Clang { .. } => { - if !(cmd.has_internal_target_arg - || (target.os == "android" - && android_clang_compiler_uses_target_arg_internally(&cmd.path))) - { - if target.os == "freebsd" { - // FreeBSD only supports C++11 and above when compiling against libc++ - // (available from FreeBSD 10 onwards). Under FreeBSD, clang uses libc++ by - // default on FreeBSD 10 and newer unless `--target` is manually passed to - // the compiler, in which case its default behavior differs: - // * If --target=xxx-unknown-freebsdX(.Y) is specified and X is greater than - // or equal to 10, clang++ uses libc++ - // * If --target=xxx-unknown-freebsd is specified (without a version), - // clang++ cannot assume libc++ is available and reverts to a default of - // libstdc++ (this behavior was changed in llvm 14). - // - // This breaks C++11 (or greater) builds if targeting FreeBSD with the - // generic xxx-unknown-freebsd target on clang 13 or below *without* - // explicitly specifying that libc++ should be used. - // When cross-compiling, we can't infer from the rust/cargo target name - // which major version of FreeBSD we are targeting, so we need to make sure - // that libc++ is used (unless the user has explicitly specified otherwise). - // There's no compelling reason to use a different approach when compiling - // natively. - if self.cpp && self.cpp_set_stdlib.is_none() { - cmd.push_cc_arg("-stdlib=libc++".into()); - } - } else if target.arch == "wasm32" && target.os == "linux" { - for x in &[ - "atomics", - "bulk-memory", - "mutable-globals", - "sign-ext", - "exception-handling", - ] { - cmd.push_cc_arg(format!("-m{x}").into()); - } - for x in &["wasm-exceptions", "declspec"] { - cmd.push_cc_arg(format!("-f{x}").into()); - } - let musl_sysroot = self.wasm_musl_sysroot().unwrap(); - cmd.push_cc_arg( - format!("--sysroot={}", Path::new(&musl_sysroot).display()).into(), - ); - cmd.push_cc_arg("-pthread".into()); - } - // Pass `--target` with the LLVM target to configure Clang for cross-compiling. - // - // This is **required** for cross-compilation, as it's the only flag that - // consistently forces Clang to change the "toolchain" that is responsible for - // parsing target-specific flags: - // https://github.com/rust-lang/cc-rs/issues/1388 - // https://github.com/llvm/llvm-project/blob/llvmorg-19.1.7/clang/lib/Driver/Driver.cpp#L1359-L1360 - // https://github.com/llvm/llvm-project/blob/llvmorg-19.1.7/clang/lib/Driver/Driver.cpp#L6347-L6532 - // - // This can be confusing, because on e.g. host macOS, you can usually get by - // with `-arch` and `-mtargetos=`. But that only works because the _default_ - // toolchain is `Darwin`, which enables parsing of darwin-specific options. - // - // NOTE: In the past, we passed the deployment version in here on all Apple - // targets, but versioned targets were found to have poor compatibility with - // older versions of Clang, especially when it comes to configuration files: - // https://github.com/rust-lang/cc-rs/issues/1278 - // - // So instead, we pass the deployment target with `-m*-version-min=`, and only - // pass it here on visionOS and Mac Catalyst where that option does not exist: - // https://github.com/rust-lang/cc-rs/issues/1383 - let version = if target.os == "visionos" || target.env == "macabi" { - Some(self.apple_deployment_target(target)) - } else { - None - }; - - let clang_target = - target.llvm_target(&self.get_raw_target()?, version.as_deref()); - cmd.push_cc_arg(format!("--target={clang_target}").into()); - } - } - ToolFamily::Msvc { clang_cl } => { - // This is an undocumented flag from MSVC but helps with making - // builds more reproducible by avoiding putting timestamps into - // files. - cmd.push_cc_arg("-Brepro".into()); - - if clang_cl { - cmd.push_cc_arg( - format!( - "--target={}", - target.llvm_target(&self.get_raw_target()?, None) - ) - .into(), - ); - - if target.arch == "x86" { - // See - // . - // - // NOTE: Rust officially supported Windows targets all require SSE2 as part - // of baseline target features. - // - // NOTE: The same applies for STL. See: - - // , and - - // . - cmd.push_cc_arg("-arch:SSE2".into()); - } - } else if target.full_arch == "i586" { - cmd.push_cc_arg("-arch:IA32".into()); - } else if target.full_arch == "arm64ec" { - cmd.push_cc_arg("-arm64EC".into()); - } - // There is a check in corecrt.h that will generate a - // compilation error if - // _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE is - // not defined to 1. The check was added in Windows - // 8 days because only store apps were allowed on ARM. - // This changed with the release of Windows 10 IoT Core. - // The check will be going away in future versions of - // the SDK, but for all released versions of the - // Windows SDK it is required. - if target.arch == "arm" { - cmd.args - .push("-D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1".into()); - } - } - ToolFamily::Gnu => { - if target.vendor == "kmc" { - cmd.args.push("-finput-charset=utf-8".into()); - } - - if self.static_flag.is_none() { - let features = cargo_env_var_os("CARGO_CFG_TARGET_FEATURE"); - let features = features.as_deref().unwrap_or_default(); - if features.to_string_lossy().contains("crt-static") { - cmd.args.push("-static".into()); - } - } - - // armv7 targets get to use armv7 instructions - if (target.full_arch.starts_with("armv7") - || target.full_arch.starts_with("thumbv7")) - && (target.os == "linux" || target.vendor == "kmc") - { - cmd.args.push("-march=armv7-a".into()); - - if target.abi == "eabihf" { - // lowest common denominator FPU - cmd.args.push("-mfpu=vfpv3-d16".into()); - cmd.args.push("-mfloat-abi=hard".into()); - } - } - - // (x86 Android doesn't say "eabi") - if target.os == "android" && target.full_arch.contains("v7") { - cmd.args.push("-march=armv7-a".into()); - cmd.args.push("-mthumb".into()); - if !target.full_arch.contains("neon") { - // On android we can guarantee some extra float instructions - // (specified in the android spec online) - // NEON guarantees even more; see below. - cmd.args.push("-mfpu=vfpv3-d16".into()); - } - cmd.args.push("-mfloat-abi=softfp".into()); - } - - if target.full_arch.contains("neon") { - cmd.args.push("-mfpu=neon-vfpv4".into()); - } - - if target.full_arch == "armv4t" && target.os == "linux" { - cmd.args.push("-march=armv4t".into()); - cmd.args.push("-marm".into()); - cmd.args.push("-mfloat-abi=soft".into()); - } - - if target.full_arch == "armv5te" && target.os == "linux" { - cmd.args.push("-march=armv5te".into()); - cmd.args.push("-marm".into()); - cmd.args.push("-mfloat-abi=soft".into()); - } - - // For us arm == armv6 by default - if target.full_arch == "arm" && target.os == "linux" { - cmd.args.push("-march=armv6".into()); - cmd.args.push("-marm".into()); - if target.abi == "eabihf" { - cmd.args.push("-mfpu=vfp".into()); - } else { - cmd.args.push("-mfloat-abi=soft".into()); - } - } - - // Turn codegen down on i586 to avoid some instructions. - if target.full_arch == "i586" && target.os == "linux" { - cmd.args.push("-march=pentium".into()); - } - - // Set codegen level for i686 correctly - if target.full_arch == "i686" && target.os == "linux" { - cmd.args.push("-march=i686".into()); - } - - // Looks like `musl-gcc` makes it hard for `-m32` to make its way - // all the way to the linker, so we need to actually instruct the - // linker that we're generating 32-bit executables as well. This'll - // typically only be used for build scripts which transitively use - // these flags that try to compile executables. - if target.arch == "x86" && target.env == "musl" { - cmd.args.push("-Wl,-melf_i386".into()); - } - - if target.arch == "arm" && target.os == "none" && target.abi == "eabihf" { - cmd.args.push("-mfloat-abi=hard".into()) - } - if target.full_arch.starts_with("thumb") { - cmd.args.push("-mthumb".into()); - } - if target.full_arch.starts_with("thumbv6m") { - cmd.args.push("-march=armv6s-m".into()); - } - if target.full_arch.starts_with("thumbv7em") { - cmd.args.push("-march=armv7e-m".into()); - - if target.abi == "eabihf" { - cmd.args.push("-mfpu=fpv4-sp-d16".into()) - } - } - if target.full_arch.starts_with("thumbv7m") { - cmd.args.push("-march=armv7-m".into()); - } - if target.full_arch.starts_with("thumbv8m.base") { - cmd.args.push("-march=armv8-m.base".into()); - } - if target.full_arch.starts_with("thumbv8m.main") { - cmd.args.push("-march=armv8-m.main".into()); - - if target.abi == "eabihf" { - cmd.args.push("-mfpu=fpv5-sp-d16".into()) - } - } - if target.full_arch.starts_with("armebv7r") | target.full_arch.starts_with("armv7r") - { - if target.full_arch.starts_with("armeb") { - cmd.args.push("-mbig-endian".into()); - } else { - cmd.args.push("-mlittle-endian".into()); - } - - // ARM mode - cmd.args.push("-marm".into()); - - // R Profile - cmd.args.push("-march=armv7-r".into()); - - if target.abi == "eabihf" { - // lowest common denominator FPU - // (see Cortex-R4 technical reference manual) - cmd.args.push("-mfpu=vfpv3-d16".into()) - } - } - if target.full_arch.starts_with("armv7a") { - cmd.args.push("-march=armv7-a".into()); - - if target.abi == "eabihf" { - // lowest common denominator FPU - cmd.args.push("-mfpu=vfpv3-d16".into()); - } - } - if target.arch == "riscv32" || target.arch == "riscv64" { - // get the 32i/32imac/32imc/64gc/64imac/... part - let arch = &target.full_arch[5..]; - if arch.starts_with("64") { - if matches!(target.os, "linux" | "freebsd" | "netbsd") { - cmd.args.push(("-march=rv64gc").into()); - cmd.args.push("-mabi=lp64d".into()); - } else { - cmd.args.push(("-march=rv".to_owned() + arch).into()); - cmd.args.push("-mabi=lp64".into()); - } - } else if arch.starts_with("32") { - if target.os == "linux" { - cmd.args.push(("-march=rv32gc").into()); - cmd.args.push("-mabi=ilp32d".into()); - } else { - cmd.args.push(("-march=rv".to_owned() + arch).into()); - cmd.args.push("-mabi=ilp32".into()); - } - } else { - cmd.args.push("-mcmodel=medany".into()); - } - } - } - } - - if raw_target == "wasm32v1-none" { - // `wasm32v1-none` target only exists in `rustc`, so we need to change the compilation flags: - // https://doc.rust-lang.org/rustc/platform-support/wasm32v1-none.html - cmd.push_cc_arg("-mcpu=mvp".into()); - cmd.push_cc_arg("-mmutable-globals".into()); - } - - if target.os == "solaris" || target.os == "illumos" { - // On Solaris and illumos, multi-threaded C programs must be built with `_REENTRANT` - // defined. This configures headers to define APIs appropriately for multi-threaded - // use. This is documented in threads(7), see also https://illumos.org/man/7/threads. - // - // If C code is compiled without multi-threading support but does use multiple threads, - // incorrect behavior may result. One extreme example is that on some systems the - // global errno may be at the same address as the process' first thread's errno; errno - // clobbering may occur to disastrous effect. Conversely, if _REENTRANT is defined - // while it is not actually needed, system headers may define some APIs suboptimally - // but will not result in incorrect behavior. Other code *should* be reasonable under - // such conditions. - // - // We're typically building C code to eventually link into a Rust program. Many Rust - // programs are multi-threaded in some form. So, set the flag by default. - cmd.args.push("-D_REENTRANT".into()); - } - - if target.vendor == "apple" { - self.apple_flags(cmd)?; - } - - if self.static_flag.unwrap_or(false) { - cmd.args.push("-static".into()); - } - if self.shared_flag.unwrap_or(false) { - cmd.args.push("-shared".into()); - } - - if self.cpp { - match (self.cpp_set_stdlib.as_ref(), cmd.family) { - (None, _) => {} - (Some(stdlib), ToolFamily::Gnu) | (Some(stdlib), ToolFamily::Clang { .. }) => { - cmd.push_cc_arg(format!("-stdlib=lib{stdlib}").into()); - } - _ => { - self.cargo_output.print_warning(&format_args!("cpp_set_stdlib is specified, but the {:?} compiler does not support this option, ignored", cmd.family)); - } - } - } - - Ok(()) - } - - fn add_inherited_rustflags( - &self, - cmd: &mut Tool, - target: &TargetInfo<'_>, - ) -> Result<(), Error> { - let env_os = match cargo_env_var_os("CARGO_ENCODED_RUSTFLAGS") { - Some(env) => env, - // No encoded RUSTFLAGS -> nothing to do - None => return Ok(()), - }; - - let env = env_os.to_string_lossy(); - let codegen_flags = RustcCodegenFlags::parse(&env)?; - codegen_flags.cc_flags(self, cmd, target); - Ok(()) - } - - fn msvc_macro_assembler(&self) -> Result { - let target = self.get_target()?; - let tool = match target.arch { - "x86_64" => "ml64.exe", - "arm" => "armasm.exe", - "aarch64" | "arm64ec" => "armasm64.exe", - _ => "ml.exe", - }; - let mut cmd = self - .find_msvc_tools_find(&target, tool) - .unwrap_or_else(|| self.cmd(tool)); - cmd.arg("-nologo"); // undocumented, yet working with armasm[64] - for directory in self.include_directories.iter() { - cmd.arg("-I").arg(&**directory); - } - if is_arm(&target) { - if self.get_debug() { - cmd.arg("-g"); - } - - if target.arch == "arm64ec" { - cmd.args(["-machine", "ARM64EC"]); - } - - for (key, value) in self.definitions.iter() { - cmd.arg("-PreDefine"); - if let Some(ref value) = *value { - if let Ok(i) = value.parse::() { - cmd.arg(format!("{key} SETA {i}")); - } else if value.starts_with('"') && value.ends_with('"') { - cmd.arg(format!("{key} SETS {value}")); - } else { - cmd.arg(format!("{key} SETS \"{value}\"")); - } - } else { - cmd.arg(format!("{} SETL {}", key, "{TRUE}")); - } - } - } else { - if self.get_debug() { - cmd.arg("-Zi"); - } - - for (key, value) in self.definitions.iter() { - if let Some(ref value) = *value { - cmd.arg(format!("-D{key}={value}")); - } else { - cmd.arg(format!("-D{key}")); - } - } - } - - if target.arch == "x86" { - cmd.arg("-safeseh"); - } - - Ok(cmd) - } - - fn assemble(&self, lib_name: &str, dst: &Path, objs: &[Object]) -> Result<(), Error> { - // Delete the destination if it exists as we want to - // create on the first iteration instead of appending. - let _ = fs::remove_file(dst); - - // Add objects to the archive in limited-length batches. This helps keep - // the length of the command line within a reasonable length to avoid - // blowing system limits on limiting platforms like Windows. - // - // Optimistically try the `D` (deterministic) ar modifier, which zeros - // out timestamps, UIDs, and GIDs. If the archiver doesn't support it, - // we remember and stop trying for subsequent batches. - // (`None` -> haven't probed yet) - let mut deterministic_ar: Option = None; - - let mut objs = objs - .iter() - .map(|o| o.dst.as_path()) - .chain(self.objects.iter().map(std::ops::Deref::deref)) - .peekable(); - let mut batch = Vec::new(); - while objs.peek().is_some() { - let mut remaining_len = 4000; - while let Some(path) = - objs.next_if(|peek| batch.is_empty() || peek.as_os_str().len() <= remaining_len) - { - batch.push(path); - remaining_len = remaining_len.saturating_sub(path.as_os_str().len()); - } - self.assemble_progressive(dst, &batch, &mut deterministic_ar)?; - batch.clear(); - } - - if self.cuda && self.cuda_file_count() > 0 { - // Link the device-side code and add it to the target library, - // so that non-CUDA linker can link the final binary. - - let out_dir = self.get_out_dir()?; - let dlink = out_dir.join(lib_name.to_owned() + "_dlink.o"); - let mut nvcc = self.get_compiler().to_command(); - nvcc.arg("--device-link").arg("-o").arg(&dlink).arg(dst); - run(&mut nvcc, &self.cargo_output)?; - self.assemble_progressive(dst, &[dlink.as_path()], &mut deterministic_ar)?; - } - - let target = self.get_target()?; - if target.env == "msvc" { - // The Rust compiler will look for libfoo.a and foo.lib, but the - // MSVC linker will also be passed foo.lib, so be sure that both - // exist for now. - - let lib_dst = dst.with_file_name(format!("{lib_name}.lib")); - let _ = fs::remove_file(&lib_dst); - match fs::hard_link(dst, &lib_dst).or_else(|_| { - // if hard-link fails, just copy (ignoring the number of bytes written) - fs::copy(dst, &lib_dst).map(|_| ()) - }) { - Ok(_) => (), - Err(_) => { - return Err(Error::new( - ErrorKind::IOError, - "Could not copy or create a hard-link to the generated lib file.", - )); - } - }; - } else { - // Non-msvc targets (those using `ar`) need a separate step to add - // the symbol table to archives since our construction command of - // `cq` doesn't add it for us. - let mut ar = self.try_get_archiver()?; - // NOTE: We add `s` even if flags were passed using $ARFLAGS/ar_flag, because `s` - // here represents a _mode_, not an arbitrary flag. Further discussion of this choice - // can be seen in https://github.com/rust-lang/cc-rs/pull/763. - match deterministic_ar { - Some(false) => { - // See comment in `assemble_progressive` for more on ZERO_AR_DATE. - ar.env("ZERO_AR_DATE", "1"); - run(ar.arg("s").arg(dst), &self.cargo_output)?; - } - Some(true) => { - run(ar.arg("sD").arg(dst), &self.cargo_output)?; - } - None => { - if run_silent_on_error(ar.arg("sD").arg(dst), &self.cargo_output).is_err() { - let mut ar = self.try_get_archiver()?; - ar.env("ZERO_AR_DATE", "1"); - run(ar.arg("s").arg(dst), &self.cargo_output)?; - } - } - } - } - - Ok(()) - } - - fn assemble_progressive( - &self, - dst: &Path, - objs: &[&Path], - deterministic_ar: &mut Option, - ) -> Result<(), Error> { - let target = self.get_target()?; - - let (mut cmd, program, any_flags) = self.try_get_archiver_and_flags()?; - if target.env == "msvc" && !program.to_string_lossy().contains("llvm-ar") { - // NOTE: -out: here is an I/O flag, and so must be included even if $ARFLAGS/ar_flag is - // in use. -nologo on the other hand is just a regular flag, and one that we'll skip if - // the caller has explicitly dictated the flags they want. See - // https://github.com/rust-lang/cc-rs/pull/763 for further discussion. - let mut out = OsString::from("-out:"); - out.push(dst); - cmd.arg(out); - if !any_flags { - cmd.arg("-nologo"); - } - // If the library file already exists, add the library name - // as an argument to let lib.exe know we are appending the objs. - if dst.exists() { - cmd.arg(dst); - } - cmd.args(objs); - run(&mut cmd, &self.cargo_output)?; - } else { - // Set an environment variable to tell the OSX archiver to ensure - // that all dates listed in the archive are zero, improving - // determinism of builds. AFAIK there's not really official - // documentation of this but there's a lot of references to it if - // you search google. - // - // You can reproduce this locally on a mac with: - // - // $ touch foo.c - // $ cc -c foo.c -o foo.o - // - // # Notice that these two checksums are different - // $ ar crus libfoo1.a foo.o && sleep 2 && ar crus libfoo2.a foo.o - // $ md5sum libfoo*.a - // - // # Notice that these two checksums are the same - // $ export ZERO_AR_DATE=1 - // $ ar crus libfoo1.a foo.o && sleep 2 && touch foo.o && ar crus libfoo2.a foo.o - // $ md5sum libfoo*.a - // - // In any case if this doesn't end up getting read, it shouldn't - // cause that many issues! - cmd.env("ZERO_AR_DATE", "1"); - - // NOTE: We add cq here regardless of whether $ARFLAGS/ar_flag have been used because - // it dictates the _mode_ ar runs in, which the setter of $ARFLAGS/ar_flag can't - // dictate. See https://github.com/rust-lang/cc-rs/pull/763 for further discussion. - match *deterministic_ar { - Some(false) => { - run(cmd.arg("cq").arg(dst).args(objs), &self.cargo_output)?; - } - Some(true) => { - run(cmd.arg("cqD").arg(dst).args(objs), &self.cargo_output)?; - } - None => { - // Probe: try `D` and remember the result for later batches. - if run_silent_on_error(cmd.arg("cqD").arg(dst).args(objs), &self.cargo_output) - .is_ok() - { - *deterministic_ar = Some(true); - } else { - *deterministic_ar = Some(false); - let (mut cmd, _, _) = self.try_get_archiver_and_flags()?; - cmd.env("ZERO_AR_DATE", "1"); - run(cmd.arg("cq").arg(dst).args(objs), &self.cargo_output)?; - } - } - } - } - - Ok(()) - } - - fn apple_flags(&self, cmd: &mut Tool) -> Result<(), Error> { - let target = self.get_target()?; - - // This is a Darwin/Apple-specific flag that works both on GCC and Clang, but it is only - // necessary on GCC since we specify `-target` on Clang. - // https://gcc.gnu.org/onlinedocs/gcc/Darwin-Options.html#:~:text=arch - // https://clang.llvm.org/docs/CommandGuide/clang.html#cmdoption-arch - if cmd.is_like_gnu() { - let arch = map_darwin_target_from_rust_to_compiler_architecture(&target); - cmd.args.push("-arch".into()); - cmd.args.push(arch.into()); - } - - // Pass the deployment target via `-mmacosx-version-min=`, `-miphoneos-version-min=` and - // similar. Also necessary on GCC, as it forces a compilation error if the compiler is not - // configured for Darwin: https://gcc.gnu.org/onlinedocs/gcc/Darwin-Options.html - // - // On visionOS and Mac Catalyst, there is no -m*-version-min= flag: - // https://github.com/llvm/llvm-project/issues/88271 - // And the workaround to use `-mtargetos=` cannot be used with the `--target` flag that we - // otherwise specify. So we avoid emitting that, and put the version in `--target` instead. - if cmd.is_like_gnu() || !(target.os == "visionos" || target.env == "macabi") { - let min_version = self.apple_deployment_target(&target); - cmd.args - .push(target.apple_version_flag(&min_version).into()); - } - - // AppleClang sometimes requires sysroot even on macOS - if cmd.is_xctoolchain_clang() || target.os != "macos" { - self.cargo_output.print_metadata(&format_args!( - "Detecting {:?} SDK path for {}", - target.os, - target.apple_sdk_name(), - )); - let sdk_path = self.apple_sdk_root(&target)?; - - cmd.args.push("-isysroot".into()); - cmd.args.push(OsStr::new(&sdk_path).to_owned()); - cmd.env - .push(("SDKROOT".into(), OsStr::new(&sdk_path).to_owned())); - - if target.env == "macabi" { - // Mac Catalyst uses the macOS SDK, but to compile against and - // link to iOS-specific frameworks, we should have the support - // library stubs in the include and library search path. - let ios_support = Path::new(&sdk_path).join("System/iOSSupport"); - - cmd.args.extend([ - // Header search path - OsString::from("-isystem"), - ios_support.join("usr/include").into(), - // Framework header search path - OsString::from("-iframework"), - ios_support.join("System/Library/Frameworks").into(), - // Library search path - { - let mut s = OsString::from("-L"); - s.push(ios_support.join("usr/lib")); - s - }, - // Framework linker search path - { - // Technically, we _could_ avoid emitting `-F`, as - // `-iframework` implies it, but let's keep it in for - // clarity. - let mut s = OsString::from("-F"); - s.push(ios_support.join("System/Library/Frameworks")); - s - }, - ]); - } - } - - Ok(()) - } - - fn cmd>(&self, prog: P) -> Command { - let mut cmd = Command::new(prog); - for (a, b) in self.env.iter() { - cmd.env(a, b); - } - cmd - } - - fn prefer_clang(&self) -> bool { - if let Some(env) = cargo_env_var_os("CARGO_ENCODED_RUSTFLAGS") { - env.to_string_lossy().contains("linker-plugin-lto") - } else { - false - } - } - - fn get_base_compiler(&self) -> Result { - let out_dir = self.get_out_dir().ok(); - let out_dir = out_dir.as_deref(); - - if let Some(c) = &self.compiler { - return Ok(Tool::new( - (**c).to_owned(), - &self.build_cache.cached_compiler_family, - &self.cargo_output, - out_dir, - )); - } - let target = self.get_target()?; - let raw_target = self.get_raw_target()?; - - let msvc = if self.prefer_clang_cl_over_msvc { - "clang-cl.exe" - } else { - "cl.exe" - }; - - let (env, gnu, traditional, clang) = if self.cpp { - ("CXX", "g++", "c++", "clang++") - } else { - ("CC", "gcc", "cc", "clang") - }; - - let fallback = Cow::Borrowed(Path::new(traditional)); - let default = if cfg!(target_os = "solaris") || cfg!(target_os = "illumos") { - // On historical Solaris systems, "cc" may have been Sun Studio, which - // is not flag-compatible with "gcc". This history casts a long shadow, - // and many modern illumos distributions today ship GCC as "gcc" without - // also making it available as "cc". - Cow::Borrowed(Path::new(gnu)) - } else if self.prefer_clang() { - self.which(Path::new(clang), None) - .map(Cow::Owned) - .unwrap_or(fallback) - } else { - fallback - }; - - let cl_exe = self.find_msvc_tools_find_tool(&target, msvc); - - let tool_opt: Option = self - .env_tool(env) - .map(|(tool, wrapper, args)| { - // Chop off leading/trailing whitespace to work around - // semi-buggy build scripts which are shared in - // makefiles/configure scripts (where spaces are far more - // lenient) - let mut t = Tool::with_args( - tool, - args.clone(), - &self.build_cache.cached_compiler_family, - &self.cargo_output, - out_dir, - ); - if let Some(cc_wrapper) = wrapper { - t.cc_wrapper_path = Some(Path::new(&cc_wrapper).to_owned()); - } - for arg in args { - t.cc_wrapper_args.push(arg.into()); - } - t - }) - .or_else(|| { - if target.os == "emscripten" { - let tool = if self.cpp { "em++" } else { "emcc" }; - // Windows uses bat file so we have to be a bit more specific - if cfg!(windows) { - let mut t = Tool::with_family( - PathBuf::from("cmd"), - ToolFamily::Clang { zig_cc: false }, - ); - t.args.push("/c".into()); - t.args.push(format!("{tool}.bat").into()); - Some(t) - } else { - Some(Tool::new( - PathBuf::from(tool), - &self.build_cache.cached_compiler_family, - &self.cargo_output, - out_dir, - )) - } - } else { - None - } - }) - .or_else(|| cl_exe.clone()); - - let tool = match tool_opt { - Some(t) => t, - None => { - let compiler: PathBuf = if cfg!(windows) && target.os == "windows" { - if target.env == "msvc" { - msvc.into() - } else { - let cc = if target.abi == "llvm" { clang } else { gnu }; - format!("{cc}.exe").into() - } - } else if target.os == "ios" - || target.os == "watchos" - || target.os == "tvos" - || target.os == "visionos" - { - clang.into() - } else if target.os == "android" { - autodetect_android_compiler(&raw_target, gnu, clang) - } else if target.os == "cloudabi" { - format!( - "{}-{}-{}-{}", - target.full_arch, target.vendor, target.os, traditional - ) - .into() - } else if target.os == "wasi" { - self.autodetect_wasi_compiler(&raw_target, clang) - } else if target.arch == "wasm32" || target.arch == "wasm64" { - // Compiling WASM is not currently supported by GCC, so - // let's default to Clang. - clang.into() - } else if target.os == "vxworks" { - if self.cpp { "wr-c++" } else { "wr-cc" }.into() - } else if target.arch == "arm" && target.vendor == "kmc" { - format!("arm-kmc-eabi-{gnu}").into() - } else if target.arch == "aarch64" && target.vendor == "kmc" { - format!("aarch64-kmc-elf-{gnu}").into() - } else if target.os == "nto" { - // See for details: https://github.com/rust-lang/cc-rs/pull/1319 - if self.cpp { "q++" } else { "qcc" }.into() - } else if self.get_is_cross_compile()? { - let prefix = self.prefix_for_target(&raw_target); - match prefix { - Some(prefix) => { - let cc = if target.abi == "llvm" { clang } else { gnu }; - format!("{prefix}-{cc}").into() - } - None => default.into(), - } - } else { - default.into() - }; - - let mut t = Tool::new( - compiler, - &self.build_cache.cached_compiler_family, - &self.cargo_output, - out_dir, - ); - if let Some(cc_wrapper) = self.rustc_wrapper_fallback() { - t.cc_wrapper_path = Some(Path::new(&cc_wrapper).to_owned()); - } - t - } - }; - - let mut tool = if self.cuda { - assert!( - tool.args.is_empty(), - "CUDA compilation currently assumes empty pre-existing args" - ); - let nvcc = match self.getenv_with_target_prefixes("NVCC") { - Err(_) => PathBuf::from("nvcc"), - Ok(nvcc) => PathBuf::from(&*nvcc), - }; - let mut nvcc_tool = Tool::with_features( - nvcc, - vec![], - self.cuda, - &self.build_cache.cached_compiler_family, - &self.cargo_output, - out_dir, - ); - if self.ccbin { - nvcc_tool - .args - .push(format!("-ccbin={}", tool.path.display()).into()); - } - if let Some(cc_wrapper) = self.rustc_wrapper_fallback() { - nvcc_tool.cc_wrapper_path = Some(Path::new(&cc_wrapper).to_owned()); - } - nvcc_tool.family = tool.family; - nvcc_tool - } else { - tool - }; - - // New "standalone" C/C++ cross-compiler executables from recent Android NDK - // are just shell scripts that call main clang binary (from Android NDK) with - // proper `--target` argument. - // - // For example, armv7a-linux-androideabi16-clang passes - // `--target=armv7a-linux-androideabi16` to clang. - // - // As the shell script calls the main clang binary, the command line limit length - // on Windows is restricted to around 8k characters instead of around 32k characters. - // To remove this limit, we call the main clang binary directly and construct the - // `--target=` ourselves. - if cfg!(windows) && android_clang_compiler_uses_target_arg_internally(&tool.path) { - if let Some(path) = tool.path.file_name() { - let file_name = path.to_str().unwrap().to_owned(); - let (target, clang) = file_name.split_at(file_name.rfind('-').unwrap()); - - tool.has_internal_target_arg = true; - tool.path.set_file_name(clang.trim_start_matches('-')); - tool.path.set_extension("exe"); - tool.args.push(format!("--target={target}").into()); - - // Additionally, shell scripts for target i686-linux-android versions 16 to 24 - // pass the `mstackrealign` option so we do that here as well. - if target.contains("i686-linux-android") { - let (_, version) = target.split_at(target.rfind('d').unwrap() + 1); - if let Ok(version) = version.parse::() { - if version > 15 && version < 25 { - tool.args.push("-mstackrealign".into()); - } - } - } - }; - } - - // Under cross-compilation scenarios, llvm-mingw's clang executable is just a - // wrapper script that calls the actual clang binary with a suitable `--target` - // argument, much like the Android NDK case outlined above. Passing a target - // argument ourselves in this case will result in an error, as they expect - // targets like `x86_64-w64-mingw32`, and we can't always set such a target - // string because it is specific to this MinGW cross-compilation toolchain. - // - // For example, the following command will always fail due to using an unsuitable - // `--target` argument we'd otherwise pass: - // $ /opt/llvm-mingw-20250613-ucrt-ubuntu-22.04-x86_64/bin/x86_64-w64-mingw32-clang --target=x86_64-pc-windows-gnu dummy.c - // - // Code reference: - // https://github.com/mstorsjo/llvm-mingw/blob/a1f6413e5c21fd74b64137b56167f4fba500d1d8/wrappers/clang-target-wrapper.sh#L31 - if !cfg!(windows) && target.os == "windows" && is_llvm_mingw_wrapper(&tool.path) { - tool.has_internal_target_arg = true; - } - - // If we found `cl.exe` in our environment, the tool we're returning is - // an MSVC-like tool, *and* no env vars were set then set env vars for - // the tool that we're returning. - // - // Env vars are needed for things like `link.exe` being put into PATH as - // well as header include paths sometimes. These paths are automatically - // included by default but if the `CC` or `CXX` env vars are set these - // won't be used. This'll ensure that when the env vars are used to - // configure for invocations like `clang-cl` we still get a "works out - // of the box" experience. - if let Some(cl_exe) = cl_exe { - if tool.family == (ToolFamily::Msvc { clang_cl: true }) - && tool.env.is_empty() - && target.env == "msvc" - { - for (k, v) in cl_exe.env.iter() { - tool.env.push((k.to_owned(), v.to_owned())); - } - } - } - - if target.env == "msvc" && tool.family == ToolFamily::Gnu { - self.cargo_output - .print_warning(&"GNU compiler is not supported for this target"); - } - - Ok(tool) - } - - /// Returns a fallback `cc_compiler_wrapper` by introspecting `RUSTC_WRAPPER` - fn rustc_wrapper_fallback(&self) -> Option> { - // No explicit CC wrapper was detected, but check if RUSTC_WRAPPER - // is defined and is a build accelerator that is compatible with - // C/C++ compilers (e.g. sccache) - const VALID_WRAPPERS: &[&str] = &["sccache", "cachepot", "buildcache"]; - - let rustc_wrapper = cargo_env_var_os("RUSTC_WRAPPER")?; - let wrapper_path = Path::new(&rustc_wrapper); - let wrapper_stem = wrapper_path.file_stem()?; - - if VALID_WRAPPERS.contains(&wrapper_stem.to_str()?) { - Some(Cow::Owned(rustc_wrapper)) - } else { - None - } - } - - /// Returns compiler path, optional modifier name from whitelist, and arguments vec - fn env_tool(&self, name: &str) -> Option<(PathBuf, Option>, Vec)> { - let tool = self.getenv_with_target_prefixes(name).ok()?; - let tool = tool.to_string_lossy(); - let tool = tool.trim(); - - if tool.is_empty() { - return None; - } - - // If this is an exact path on the filesystem we don't want to do any - // interpretation at all, just pass it on through. This'll hopefully get - // us to support spaces-in-paths. - if let Some(exe) = check_exe(Path::new(tool).into()) { - return Some((exe, self.rustc_wrapper_fallback(), Vec::new())); - } - - // Ok now we want to handle a couple of scenarios. We'll assume from - // here on out that spaces are splitting separate arguments. Two major - // features we want to support are: - // - // CC='sccache cc' - // - // aka using `sccache` or any other wrapper/caching-like-thing for - // compilations. We want to know what the actual compiler is still, - // though, because our `Tool` API support introspection of it to see - // what compiler is in use. - // - // additionally we want to support - // - // CC='cc -flag' - // - // where the CC env var is used to also pass default flags to the C - // compiler. - // - // It's true that everything here is a bit of a pain, but apparently if - // you're not literally make or bash then you get a lot of bug reports. - let mut known_wrappers = vec![ - "ccache", - "distcc", - "sccache", - "icecc", - "cachepot", - "buildcache", - ]; - let custom_wrapper = self.get_env("CC_KNOWN_WRAPPER_CUSTOM"); - if custom_wrapper.is_some() { - known_wrappers.push(custom_wrapper.as_deref().unwrap().to_str().unwrap()); - } - - let mut parts = tool.split_whitespace(); - let maybe_wrapper = parts.next()?; - - let file_stem = Path::new(maybe_wrapper).file_stem()?.to_str()?; - if known_wrappers.contains(&file_stem) { - if let Some(compiler) = parts.next() { - return Some(( - compiler.into(), - Some(Cow::Owned(maybe_wrapper.into())), - parts.map(|s| s.to_string()).collect(), - )); - } - } - - Some(( - maybe_wrapper.into(), - self.rustc_wrapper_fallback(), - parts.map(|s| s.to_string()).collect(), - )) - } - - /// Returns the C++ standard library: - /// 1. If [`cpp_link_stdlib`](cc::Build::cpp_link_stdlib) is set, uses its value. - /// 2. Else if the `CXXSTDLIB` environment variable is set, uses its value. - /// 3. Else the default is `c++` for OS X and BSDs, `c++_shared` for Android, - /// `None` for MSVC and `stdc++` for anything else. - fn get_cpp_link_stdlib(&self) -> Result>, Error> { - match &self.cpp_link_stdlib { - Some(s) => Ok(s.as_deref().map(Path::new).map(Cow::Borrowed)), - None => { - if let Ok(stdlib) = self.getenv_with_target_prefixes("CXXSTDLIB") { - if stdlib.is_empty() { - Ok(None) - } else { - Ok(Some(Cow::Owned(Path::new(&stdlib).to_owned()))) - } - } else { - let target = self.get_target()?; - if target.env == "msvc" { - Ok(None) - } else if target.vendor == "apple" - || target.os == "freebsd" - || target.os == "openbsd" - || target.os == "aix" - || (target.os == "linux" && target.env == "ohos") - || target.os == "wasi" - { - Ok(Some(Cow::Borrowed(Path::new("c++")))) - } else if target.os == "android" { - Ok(Some(Cow::Borrowed(Path::new("c++_shared")))) - } else { - Ok(Some(Cow::Borrowed(Path::new("stdc++")))) - } - } - } - } - } - - /// Get the archiver (ar) that's in use for this configuration. - /// - /// You can use [`Command::get_program`] to get just the path to the command. - /// - /// This method will take into account all configuration such as debug - /// information, optimization level, include directories, defines, etc. - /// Additionally, the compiler binary in use follows the standard - /// conventions for this path, e.g. looking at the explicitly set compiler, - /// environment variables (a number of which are inspected here), and then - /// falling back to the default configuration. - /// - /// # Panics - /// - /// Panics if an error occurred while determining the architecture. - pub fn get_archiver(&self) -> Command { - match self.try_get_archiver() { - Ok(tool) => tool, - Err(e) => fail(&e.message), - } - } - - /// Get the archiver that's in use for this configuration. - /// - /// This will return a result instead of panicking; - /// see [`Self::get_archiver`] for the complete description. - pub fn try_get_archiver(&self) -> Result { - Ok(self.try_get_archiver_and_flags()?.0) - } - - fn try_get_archiver_and_flags(&self) -> Result<(Command, PathBuf, bool), Error> { - let (mut cmd, name) = self.get_base_archiver()?; - let mut any_flags = false; - if let Some(flags) = self.envflags("ARFLAGS")? { - any_flags = true; - cmd.args(flags); - } - for flag in &self.ar_flags { - any_flags = true; - cmd.arg(&**flag); - } - Ok((cmd, name, any_flags)) - } - - fn get_base_archiver(&self) -> Result<(Command, PathBuf), Error> { - if let Some(ref a) = self.archiver { - let archiver = &**a; - return Ok((self.cmd(archiver), archiver.into())); - } - - self.get_base_archiver_variant("AR", "ar") - } - - /// Get the ranlib that's in use for this configuration. - /// - /// You can use [`Command::get_program`] to get just the path to the command. - /// - /// This method will take into account all configuration such as debug - /// information, optimization level, include directories, defines, etc. - /// Additionally, the compiler binary in use follows the standard - /// conventions for this path, e.g. looking at the explicitly set compiler, - /// environment variables (a number of which are inspected here), and then - /// falling back to the default configuration. - /// - /// # Panics - /// - /// Panics if an error occurred while determining the architecture. - pub fn get_ranlib(&self) -> Command { - match self.try_get_ranlib() { - Ok(tool) => tool, - Err(e) => fail(&e.message), - } - } - - /// Get the ranlib that's in use for this configuration. - /// - /// This will return a result instead of panicking; - /// see [`Self::get_ranlib`] for the complete description. - pub fn try_get_ranlib(&self) -> Result { - let mut cmd = self.get_base_ranlib()?; - if let Some(flags) = self.envflags("RANLIBFLAGS")? { - cmd.args(flags); - } - Ok(cmd) - } - - fn get_base_ranlib(&self) -> Result { - if let Some(ref r) = self.ranlib { - return Ok(self.cmd(&**r)); - } - - Ok(self.get_base_archiver_variant("RANLIB", "ranlib")?.0) - } - - fn get_base_archiver_variant( - &self, - env: &str, - tool: &str, - ) -> Result<(Command, PathBuf), Error> { - let target = self.get_target()?; - let mut name = PathBuf::new(); - let tool_opt: Option = self - .env_tool(env) - .map(|(tool, _wrapper, args)| { - name.clone_from(&tool); - let mut cmd = self.cmd(tool); - cmd.args(args); - cmd - }) - .or_else(|| { - if target.os == "emscripten" { - // Windows use bat files so we have to be a bit more specific - if cfg!(windows) { - let mut cmd = self.cmd("cmd"); - name = format!("em{tool}.bat").into(); - cmd.arg("/c").arg(&name); - Some(cmd) - } else { - name = format!("em{tool}").into(); - Some(self.cmd(&name)) - } - } else if target.arch == "wasm32" || target.arch == "wasm64" { - // Formally speaking one should be able to use this approach, - // parsing -print-search-dirs output, to cover all clang targets, - // including Android SDKs and other cross-compilation scenarios... - // And even extend it to gcc targets by searching for "ar" instead - // of "llvm-ar"... - let compiler = self.get_base_compiler().ok()?; - if compiler.is_like_clang() { - name = format!("llvm-{tool}").into(); - self.search_programs(&compiler.path, &name, &self.cargo_output) - .map(|name| self.cmd(name)) - } else { - None - } - } else { - None - } - }); - - let tool = match tool_opt { - Some(t) => t, - None => { - if target.os == "android" { - name = format!("llvm-{tool}").into(); - match Command::new(&name).arg("--version").status() { - Ok(status) if status.success() => (), - _ => { - // FIXME: Use parsed target. - let raw_target = self.get_raw_target()?; - name = format!("{}-{}", raw_target.replace("armv7", "arm"), tool).into() - } - } - self.cmd(&name) - } else if target.env == "msvc" { - // NOTE: There isn't really a ranlib on msvc, so arguably we should return - // `None` somehow here. But in general, callers will already have to be aware - // of not running ranlib on Windows anyway, so it feels okay to return lib.exe - // here. - - let compiler = self.get_base_compiler()?; - let lib = if compiler.family == (ToolFamily::Msvc { clang_cl: true }) { - self.search_programs( - &compiler.path, - Path::new("llvm-lib"), - &self.cargo_output, - ) - .or_else(|| { - // See if there is 'llvm-lib' next to 'clang-cl' - if let Some(mut cmd) = self.which(&compiler.path, None) { - cmd.pop(); - cmd.push("llvm-lib"); - self.which(&cmd, None) - } else { - None - } - }) - } else { - None - }; - - if let Some(lib) = lib { - name = lib; - self.cmd(&name) - } else { - name = PathBuf::from("lib.exe"); - let mut cmd = match self.find_msvc_tools_find(&target, "lib.exe") { - Some(t) => t, - None => self.cmd("lib.exe"), - }; - if target.full_arch == "arm64ec" { - cmd.arg("/machine:arm64ec"); - } - cmd - } - } else if target.os == "illumos" { - // The default 'ar' on illumos uses a non-standard flags, - // but the OS comes bundled with a GNU-compatible variant. - // - // Use the GNU-variant to match other Unix systems. - name = format!("g{tool}").into(); - self.cmd(&name) - } else if target.os == "vxworks" { - name = format!("wr-{tool}").into(); - self.cmd(&name) - } else if target.os == "nto" { - // Ref: https://www.qnx.com/developers/docs/8.0/com.qnx.doc.neutrino.utilities/topic/a/ar.html - name = match target.full_arch { - "i586" => format!("ntox86-{tool}").into(), - "x86" | "aarch64" | "x86_64" => { - format!("nto{}-{}", target.arch, tool).into() - } - _ => { - return Err(Error::new( - ErrorKind::InvalidTarget, - format!("Unknown architecture for Neutrino QNX: {}", target.arch), - )) - } - }; - self.cmd(&name) - } else if self.get_is_cross_compile()? { - match self.prefix_for_target(&self.get_raw_target()?) { - Some(prefix) => { - // GCC uses $target-gcc-ar, whereas binutils uses $target-ar -- try both. - // Prefer -ar if it exists, as builds of `-gcc-ar` have been observed to be - // outright broken (such as when targeting freebsd with `--disable-lto` - // toolchain where the archiver attempts to load the LTO plugin anyway but - // fails to find one). - // - // The same applies to ranlib. - let chosen = ["", "-gcc"] - .iter() - .filter_map(|infix| { - let target_p = format!("{prefix}{infix}-{tool}"); - let status = Command::new(&target_p) - .arg("--version") - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .status() - .ok()?; - status.success().then_some(target_p) - }) - .next() - .unwrap_or_else(|| tool.to_string()); - name = chosen.into(); - self.cmd(&name) - } - None => { - name = tool.into(); - self.cmd(&name) - } - } - } else { - name = tool.into(); - self.cmd(&name) - } - } - }; - - Ok((tool, name)) - } - - // FIXME: Use parsed target instead of raw target. - fn prefix_for_target(&self, target: &str) -> Option> { - // CROSS_COMPILE is of the form: "arm-linux-gnueabi-" - self.get_env("CROSS_COMPILE") - .as_deref() - .map(|s| s.to_string_lossy().trim_end_matches('-').to_owned()) - .map(Cow::Owned) - .or_else(|| { - // Put aside RUSTC_LINKER's prefix to be used as second choice, after CROSS_COMPILE - cargo_env_var_os("RUSTC_LINKER").and_then(|var| { - var.to_string_lossy() - .strip_suffix("-gcc") - .map(str::to_string) - .map(Cow::Owned) - }) - }) - .or_else(|| { - match target { - // Note: there is no `aarch64-pc-windows-gnu` target, only `-gnullvm` - "aarch64-pc-windows-gnullvm" => Some("aarch64-w64-mingw32"), - "aarch64-uwp-windows-gnu" => Some("aarch64-w64-mingw32"), - "aarch64-unknown-helenos" => Some("aarch64-helenos"), - "aarch64-unknown-linux-gnu" => Some("aarch64-linux-gnu"), - "aarch64-unknown-linux-musl" => Some("aarch64-linux-musl"), - "aarch64-unknown-linux-relibc" => Some("aarch64-linux-relibc"), - "aarch64-unknown-netbsd" => Some("aarch64--netbsd"), - "arm-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"), - "armv4t-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"), - "armv5te-unknown-helenos-eabi" => Some("arm-helenos"), - "armv5te-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"), - "armv5te-unknown-linux-musleabi" => Some("arm-linux-gnueabi"), - "arm-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), - "arm-unknown-linux-musleabi" => Some("arm-linux-musleabi"), - "arm-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), - "arm-unknown-netbsd-eabi" => Some("arm--netbsdelf-eabi"), - "armv6-unknown-netbsd-eabihf" => Some("armv6--netbsdelf-eabihf"), - "armv7-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"), - "armv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), - "armv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), - "armv7neon-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), - "armv7neon-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), - "thumbv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), - "thumbv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), - "thumbv7neon-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), - "thumbv7neon-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), - "armv7-unknown-netbsd-eabihf" => Some("armv7--netbsdelf-eabihf"), - "hexagon-unknown-linux-musl" => Some("hexagon-linux-musl"), - "i586-unknown-linux-musl" => Some("musl"), - "i686-pc-windows-gnu" => Some("i686-w64-mingw32"), - "i686-pc-windows-gnullvm" => Some("i686-w64-mingw32"), - "i686-uwp-windows-gnu" => Some("i686-w64-mingw32"), - "i686-unknown-helenos" => Some("i686-helenos"), - "i686-unknown-linux-gnu" => self.find_working_gnu_prefix(&[ - "i686-linux-gnu", - "x86_64-linux-gnu", // transparently support gcc-multilib - ]), // explicit None if not found, so caller knows to fall back - "i686-unknown-linux-musl" => Some("musl"), - "i686-unknown-netbsd" => Some("i486--netbsdelf"), - "loongarch64-unknown-linux-gnu" => Some("loongarch64-linux-gnu"), - "m68k-unknown-linux-gnu" => Some("m68k-linux-gnu"), - "mips-unknown-linux-gnu" => Some("mips-linux-gnu"), - "mips-unknown-linux-musl" => Some("mips-linux-musl"), - "mipsel-unknown-linux-gnu" => Some("mipsel-linux-gnu"), - "mipsel-unknown-linux-musl" => Some("mipsel-linux-musl"), - "mips64-unknown-linux-gnuabi64" => Some("mips64-linux-gnuabi64"), - "mips64el-unknown-linux-gnuabi64" => Some("mips64el-linux-gnuabi64"), - "mipsisa32r6-unknown-linux-gnu" => Some("mipsisa32r6-linux-gnu"), - "mipsisa32r6el-unknown-linux-gnu" => Some("mipsisa32r6el-linux-gnu"), - "mipsisa64r6-unknown-linux-gnuabi64" => Some("mipsisa64r6-linux-gnuabi64"), - "mipsisa64r6el-unknown-linux-gnuabi64" => Some("mipsisa64r6el-linux-gnuabi64"), - "powerpc-unknown-helenos" => Some("ppc-helenos"), - "powerpc-unknown-linux-gnu" => Some("powerpc-linux-gnu"), - "powerpc-unknown-linux-gnuspe" => Some("powerpc-linux-gnuspe"), - "powerpc-unknown-netbsd" => Some("powerpc--netbsd"), - "powerpc64-unknown-linux-gnu" => Some("powerpc64-linux-gnu"), - "powerpc64le-unknown-linux-gnu" => Some("powerpc64le-linux-gnu"), - "riscv32i-unknown-none-elf" => self.find_working_gnu_prefix(&[ - "riscv32-unknown-elf", - "riscv64-unknown-elf", - "riscv-none-embed", - ]), - "riscv32im-unknown-none-elf" => self.find_working_gnu_prefix(&[ - "riscv32-unknown-elf", - "riscv64-unknown-elf", - "riscv-none-embed", - ]), - "riscv32imac-esp-espidf" => Some("riscv32-esp-elf"), - "riscv32imac-unknown-none-elf" => self.find_working_gnu_prefix(&[ - "riscv32-unknown-elf", - "riscv64-unknown-elf", - "riscv-none-embed", - ]), - "riscv32imafc-unknown-none-elf" => self.find_working_gnu_prefix(&[ - "riscv32-unknown-elf", - "riscv64-unknown-elf", - "riscv-none-embed", - ]), - "riscv32imac-unknown-xous-elf" => self.find_working_gnu_prefix(&[ - "riscv32-unknown-elf", - "riscv64-unknown-elf", - "riscv-none-embed", - ]), - "riscv32imc-esp-espidf" => Some("riscv32-esp-elf"), - "riscv32imc-unknown-none-elf" => self.find_working_gnu_prefix(&[ - "riscv32-unknown-elf", - "riscv64-unknown-elf", - "riscv-none-embed", - ]), - "riscv64gc-unknown-none-elf" => self.find_working_gnu_prefix(&[ - "riscv64-unknown-elf", - "riscv32-unknown-elf", - "riscv-none-embed", - ]), - "riscv64imac-unknown-none-elf" => self.find_working_gnu_prefix(&[ - "riscv64-unknown-elf", - "riscv32-unknown-elf", - "riscv-none-embed", - ]), - "riscv64gc-unknown-linux-gnu" => Some("riscv64-linux-gnu"), - "riscv64a23-unknown-linux-gnu" => Some("riscv64-linux-gnu"), - "riscv32gc-unknown-linux-gnu" => Some("riscv32-linux-gnu"), - "riscv64gc-unknown-linux-musl" => Some("riscv64-linux-musl"), - "riscv32gc-unknown-linux-musl" => Some("riscv32-linux-musl"), - "riscv64gc-unknown-netbsd" => Some("riscv64--netbsd"), - "s390x-unknown-linux-gnu" => Some("s390x-linux-gnu"), - "sparc-unknown-linux-gnu" => Some("sparc-linux-gnu"), - "sparc64-unknown-helenos" => Some("sparc64-helenos"), - "sparc64-unknown-linux-gnu" => Some("sparc64-linux-gnu"), - "sparc64-unknown-netbsd" => Some("sparc64--netbsd"), - "sparcv9-sun-solaris" => Some("sparcv9-sun-solaris"), - "armv7a-none-eabi" => Some("arm-none-eabi"), - "armv7a-none-eabihf" => Some("arm-none-eabi"), - "armebv7r-none-eabi" => Some("arm-none-eabi"), - "armebv7r-none-eabihf" => Some("arm-none-eabi"), - "armv7r-none-eabi" => Some("arm-none-eabi"), - "armv7r-none-eabihf" => Some("arm-none-eabi"), - "armv8r-none-eabihf" => Some("arm-none-eabi"), - "thumbv6m-none-eabi" => Some("arm-none-eabi"), - "thumbv7em-none-eabi" => Some("arm-none-eabi"), - "thumbv7em-none-eabihf" => Some("arm-none-eabi"), - "thumbv7m-none-eabi" => Some("arm-none-eabi"), - "thumbv8m.base-none-eabi" => Some("arm-none-eabi"), - "thumbv8m.main-none-eabi" => Some("arm-none-eabi"), - "thumbv8m.main-none-eabihf" => Some("arm-none-eabi"), - "x86_64-pc-windows-gnu" => Some("x86_64-w64-mingw32"), - "x86_64-pc-windows-gnullvm" => Some("x86_64-w64-mingw32"), - "x86_64-uwp-windows-gnu" => Some("x86_64-w64-mingw32"), - "x86_64-rumprun-netbsd" => Some("x86_64-rumprun-netbsd"), - "x86_64-unknown-helenos" => Some("amd64-helenos"), - "x86_64-unknown-linux-gnu" => self.find_working_gnu_prefix(&[ - "x86_64-linux-gnu", // rustfmt wrap - ]), // explicit None if not found, so caller knows to fall back - "x86_64-unknown-linux-musl" => { - self.find_working_gnu_prefix(&["x86_64-linux-musl", "musl"]) - } - "x86_64-unknown-linux-relibc" => { - self.find_working_gnu_prefix(&["x86_64-linux-relibc", "relibc"]) - } - "x86_64-unknown-netbsd" => Some("x86_64--netbsd"), - "xtensa-esp32-espidf" - | "xtensa-esp32-none-elf" - | "xtensa-esp32s2-espidf" - | "xtensa-esp32s2-none-elf" - | "xtensa-esp32s3-espidf" - | "xtensa-esp32s3-none-elf" => Some("xtensa-esp-elf"), - _ => None, - } - .map(Cow::Borrowed) - }) - } - - /// Some platforms have multiple, compatible, canonical prefixes. Look through - /// each possible prefix for a compiler that exists and return it. The prefixes - /// should be ordered from most-likely to least-likely. - fn find_working_gnu_prefix(&self, prefixes: &[&'static str]) -> Option<&'static str> { - let suffix = if self.cpp { "-g++" } else { "-gcc" }; - let extension = std::env::consts::EXE_SUFFIX; - - // Loop through PATH entries searching for each toolchain. This ensures that we - // are more likely to discover the toolchain early on, because chances are good - // that the desired toolchain is in one of the higher-priority paths. - self.get_env("PATH") - .as_ref() - .and_then(|path_entries| { - env::split_paths(path_entries).find_map(|path_entry| { - for prefix in prefixes { - let target_compiler = format!("{prefix}{suffix}{extension}"); - if path_entry.join(&target_compiler).exists() { - return Some(prefix); - } - } - None - }) - }) - .copied() - // If no toolchain was found, provide the first toolchain that was passed in. - // This toolchain has been shown not to exist, however it will appear in the - // error that is shown to the user which should make it easier to search for - // where it should be obtained. - .or_else(|| prefixes.first().copied()) - } - - fn get_target(&self) -> Result, Error> { - match &self.target { - Some(t) if Some(OsStr::new(&**t)) != cargo_env_var_os("TARGET").as_deref() => { - TargetInfo::from_rustc_target(t) - } - // Fetch target information from environment if not set, or if the - // target was the same as the TARGET environment variable, in - // case the user did `build.target(&env::var("TARGET").unwrap())`. - _ => self - .build_cache - .target_info_parser - .parse_from_cargo_environment_variables(), - } - } - - fn get_raw_target(&self) -> Result, Error> { - match &self.target { - Some(t) => Ok(Cow::Borrowed(t)), - None => cargo_env_var("TARGET").map(Cow::Owned), - } - } - - fn get_is_cross_compile(&self) -> Result { - let target = self.get_raw_target()?; - let host: Cow<'_, str> = match &self.host { - Some(h) => Cow::Borrowed(h), - None => Cow::Owned(cargo_env_var("HOST")?), - }; - Ok(host != target) - } - - fn get_opt_level(&self) -> Result, Error> { - match &self.opt_level { - Some(ol) => Ok(Cow::Borrowed(ol)), - None => cargo_env_var("OPT_LEVEL").map(Cow::Owned), - } - } - - /// Returns true if *any* debug info is enabled. - /// - /// [`get_debug_str`] provides more detail. - fn get_debug(&self) -> bool { - match self.get_debug_str() { - Err(_) => false, - Ok(d) => match &*d { - // From https://doc.rust-lang.org/cargo/reference/profiles.html#debug - "" | "0" | "false" | "none" => false, - _ => true, - }, - } - } - - fn get_debug_str(&self) -> Result, Error> { - match &self.debug { - Some(d) => Ok(Cow::Borrowed(d)), - None => cargo_env_var("DEBUG").map(Cow::Owned), - } - } - - fn get_shell_escaped_flags(&self) -> bool { - self.shell_escaped_flags - .unwrap_or_else(|| self.get_env_boolean("CC_SHELL_ESCAPED_FLAGS")) - } - - fn get_dwarf_version(&self) -> Option { - // Tentatively matches the DWARF version defaults as of rustc 1.62. - let target = self.get_target().ok()?; - if matches!( - target.os, - "android" | "dragonfly" | "freebsd" | "netbsd" | "openbsd" - ) || target.vendor == "apple" - || (target.os == "windows" && target.env == "gnu") - { - Some(2) - } else if target.os == "linux" { - Some(4) - } else { - None - } - } - - fn get_force_frame_pointer(&self) -> bool { - self.force_frame_pointer.unwrap_or_else(|| self.get_debug()) - } - - fn get_out_dir(&self) -> Result, Error> { - match &self.out_dir { - Some(p) => Ok(Cow::Borrowed(&**p)), - None => cargo_env_var_os("OUT_DIR") - .map(PathBuf::from) - .map(Cow::Owned) - .ok_or_else(|| { - Error::new( - ErrorKind::EnvVarNotFound, - "Environment variable OUT_DIR not defined.", - ) - }), - } - } - - /// Look up an environment variable, and tell Cargo that we used it. - fn get_env(&self, v: &str) -> Option { - // Excluding `PATH` prevents spurious rebuilds on Windows, see - // for details. - if self.emit_rerun_if_env_changed && v != "PATH" { - self.cargo_output - .print_metadata(&format_args!("cargo:rerun-if-env-changed={v}")); - } - #[allow(clippy::disallowed_methods)] // We emit rerun-if-env-changed above - let r = env::var_os(v); - self.cargo_output.print_metadata(&format_args!( - "{} = {}", - v, - OptionOsStrDisplay(r.as_deref()) - )); - r - } - - /// Look up an environment variable that's allowed to be overwritten by - /// [`Build::env`]. - /// - /// This is useful for environment variables that the compiler could - /// reasonably read, such as `SDKROOT` and `WASI_SDK_PATH` - for these, we - /// generally want to allow build scripts to overwrite them. - /// - /// On the other hand, we don't want to allow overwriting environment - /// variables that are `CC`-specific such as `CC_FORCE_DISABLE` - /// (`Build::env` applies to child processes, not to `cc` itself). - fn get_env_overridable(&self, key: &str) -> Option> { - // Try to look up in overrides first. - if let Some((_key, val)) = self.env.iter().find(|(k, _)| k.as_ref() == key) { - return Some(Cow::Borrowed(&**val)); - } - - // If not found in overrides, look up from environment. - self.get_env(key).map(Cow::Owned) - } - - /// Get boolean flag that is either true or false. - /// - /// Used for `CC_*`-style flags. - fn get_env_boolean(&self, key: &str) -> bool { - match self.get_env(key) { - // Set -> `true`, unless set to `""`, `"0"`, `"no"` `"false"` - Some(s) => &*s != "0" && &*s != "false" && &*s != "no" && !s.is_empty(), - // Not set -> default to `false`. - None => false, - } - } - - /// The list of environment variables to check for a given env, in order of priority. - fn target_envs(&self, env: &str) -> Result<[String; 4], Error> { - let target = self.get_raw_target()?; - let kind = if self.get_is_cross_compile()? { - "TARGET" - } else { - "HOST" - }; - let target_u = target.replace(['-', '.'], "_"); - - Ok([ - format!("{env}_{target}"), - format!("{env}_{target_u}"), - format!("{kind}_{env}"), - env.to_string(), - ]) - } - - /// Get a single-valued environment variable with target variants. - fn getenv_with_target_prefixes(&self, env: &str) -> Result { - // Take from first environment variable in the environment. - let res = self - .target_envs(env)? - .iter() - .filter_map(|env| self.get_env(env)) - .next(); - - match res { - Some(res) => Ok(res), - None => Err(Error::new( - ErrorKind::EnvVarNotFound, - format!("could not find environment variable {env}"), - )), - } - } - - /// Get values from CFLAGS-style environment variable. - fn envflags(&self, env: &str) -> Result>, Error> { - // Collect from all environment variables, in reverse order as in - // `getenv_with_target_prefixes` precedence (so that `CFLAGS_$TARGET` - // can override flags in `TARGET_CFLAGS`, which overrides those in - // `CFLAGS`). - let mut any_set = false; - let mut res = vec![]; - for env in self.target_envs(env)?.iter().rev() { - if let Some(var) = self.get_env(env) { - any_set = true; - - let var = var.to_string_lossy(); - if self.get_shell_escaped_flags() { - res.extend(Shlex::new(&var)); - } else { - res.extend(var.split_ascii_whitespace().map(ToString::to_string)); - } - } - } - - Ok(if any_set { Some(res) } else { None }) - } - - /// Returns true if `cc` has been disabled by `CC_FORCE_DISABLE`. - fn is_disabled(&self) -> bool { - self.get_env_boolean("CC_FORCE_DISABLE") - } - - fn fix_env_for_apple_os(&self, cmd: &mut Command) -> Result<(), Error> { - let target = self.get_target()?; - if cfg!(target_os = "macos") && target.os == "macos" { - // Additionally, `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at - // "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld", - // although this is apparently ignored when using the linker at "/usr/bin/ld". - cmd.env_remove("IPHONEOS_DEPLOYMENT_TARGET"); - } - Ok(()) - } - - fn apple_sdk_root_inner(&self, sdk: &str) -> Result, Error> { - // Code copied from rustc's compiler/rustc_codegen_ssa/src/back/link.rs. - if let Some(sdkroot) = self.get_env_overridable("SDKROOT") { - let p = Path::new(&sdkroot); - let does_sdkroot_contain = |strings: &[&str]| { - let sdkroot_str = p.to_string_lossy(); - strings.iter().any(|s| sdkroot_str.contains(s)) - }; - match sdk { - // Ignore `SDKROOT` if it's clearly set for the wrong platform. - "appletvos" - if does_sdkroot_contain(&["TVSimulator.platform", "MacOSX.platform"]) => {} - "appletvsimulator" - if does_sdkroot_contain(&["TVOS.platform", "MacOSX.platform"]) => {} - "iphoneos" - if does_sdkroot_contain(&["iPhoneSimulator.platform", "MacOSX.platform"]) => {} - "iphonesimulator" - if does_sdkroot_contain(&["iPhoneOS.platform", "MacOSX.platform"]) => {} - "macosx10.15" - if does_sdkroot_contain(&["iPhoneOS.platform", "iPhoneSimulator.platform"]) => { - } - "watchos" - if does_sdkroot_contain(&["WatchSimulator.platform", "MacOSX.platform"]) => {} - "watchsimulator" - if does_sdkroot_contain(&["WatchOS.platform", "MacOSX.platform"]) => {} - "xros" if does_sdkroot_contain(&["XRSimulator.platform", "MacOSX.platform"]) => {} - "xrsimulator" if does_sdkroot_contain(&["XROS.platform", "MacOSX.platform"]) => {} - // Ignore `SDKROOT` if it's not a valid path. - _ if !p.is_absolute() || p == Path::new("/") || !p.exists() => {} - _ => return Ok(sdkroot), - } - } - - let sdk_path = run_output( - self.cmd("xcrun") - .arg("--show-sdk-path") - .arg("--sdk") - .arg(sdk), - &self.cargo_output, - )?; - - let sdk_path = match String::from_utf8(sdk_path) { - Ok(p) => p, - Err(_) => { - return Err(Error::new( - ErrorKind::IOError, - "Unable to determine Apple SDK path.", - )); - } - }; - Ok(Cow::Owned(sdk_path.trim().into())) - } - - fn apple_sdk_root(&self, target: &TargetInfo<'_>) -> Result, Error> { - let sdk = target.apple_sdk_name(); - - if let Some(ret) = self - .build_cache - .apple_sdk_root_cache - .read() - .expect("apple_sdk_root_cache lock failed") - .get(sdk) - .cloned() - { - return Ok(ret); - } - let sdk_path: Arc = self.apple_sdk_root_inner(sdk)?.into(); - self.build_cache - .apple_sdk_root_cache - .write() - .expect("apple_sdk_root_cache lock failed") - .insert(sdk.into(), sdk_path.clone()); - Ok(sdk_path) - } - - fn apple_deployment_target(&self, target: &TargetInfo<'_>) -> Arc { - let sdk = target.apple_sdk_name(); - if let Some(ret) = self - .build_cache - .apple_versions_cache - .read() - .expect("apple_versions_cache lock failed") - .get(sdk) - .cloned() - { - return ret; - } - - let default_deployment_from_sdk = || -> Option> { - let version = run_output( - self.cmd("xcrun") - .arg("--show-sdk-version") - .arg("--sdk") - .arg(sdk), - &self.cargo_output, - ) - .ok()?; - - Some(Arc::from(std::str::from_utf8(&version).ok()?.trim())) - }; - - let deployment_from_env = |name: &str| -> Option> { - self.get_env_overridable(name)?.to_str().map(Arc::from) - }; - - // Determines if the acquired deployment target is too low to support modern C++ on some Apple platform. - // - // A long time ago they used libstdc++, but since macOS 10.9 and iOS 7 libc++ has been the library the SDKs provide to link against. - // If a `cc`` config wants to use C++, we round up to these versions as the baseline. - let maybe_cpp_version_baseline = |deployment_target_ver: Arc| -> Option> { - if !self.cpp { - return Some(deployment_target_ver); - } - - let mut deployment_target = deployment_target_ver - .split('.') - .map(|v| v.parse::().expect("integer version")); - - match target.os { - "macos" => { - let major = deployment_target.next().unwrap_or(0); - let minor = deployment_target.next().unwrap_or(0); - - // If below 10.9, we ignore it and let the SDK's target definitions handle it. - if major == 10 && minor < 9 { - self.cargo_output.print_warning(&format_args!( - "macOS deployment target ({deployment_target_ver}) too low, it will be increased" - )); - return None; - } - } - "ios" => { - let major = deployment_target.next().unwrap_or(0); - - // If below 10.7, we ignore it and let the SDK's target definitions handle it. - if major < 7 { - self.cargo_output.print_warning(&format_args!( - "iOS deployment target ({deployment_target_ver}) too low, it will be increased" - )); - return None; - } - } - // watchOS, tvOS, visionOS, and others are all new enough that libc++ is their baseline. - _ => {} - } - - // If the deployment target met or exceeded the C++ baseline - Some(deployment_target_ver) - }; - - // The hardcoded minimums here are subject to change in a future compiler release, - // and only exist as last resort fallbacks. Don't consider them stable. - // `cc` doesn't use rustc's `--print deployment-target`` because the compiler's defaults - // don't align well with Apple's SDKs and other third-party libraries that require ~generally~ higher - // deployment targets. rustc isn't interested in those by default though so its fine to be different here. - // - // If no explicit target is passed, `cc` defaults to the current Xcode SDK's `DefaultDeploymentTarget` for better - // compatibility. This is also the crate's historical behavior and what has become a relied-on value. - // - // The ordering of env -> XCode SDK -> old rustc defaults is intentional for performance when using - // an explicit target. - let version: Arc = match target.os { - "macos" => deployment_from_env("MACOSX_DEPLOYMENT_TARGET") - .and_then(maybe_cpp_version_baseline) - .or_else(default_deployment_from_sdk) - .unwrap_or_else(|| { - if target.arch == "aarch64" { - "11.0".into() - } else { - let default: Arc = Arc::from("10.7"); - maybe_cpp_version_baseline(default.clone()).unwrap_or(default) - } - }), - - "ios" => deployment_from_env("IPHONEOS_DEPLOYMENT_TARGET") - .and_then(maybe_cpp_version_baseline) - .or_else(default_deployment_from_sdk) - .unwrap_or_else(|| "7.0".into()), - - "watchos" => deployment_from_env("WATCHOS_DEPLOYMENT_TARGET") - .or_else(default_deployment_from_sdk) - .unwrap_or_else(|| "5.0".into()), - - "tvos" => deployment_from_env("TVOS_DEPLOYMENT_TARGET") - .or_else(default_deployment_from_sdk) - .unwrap_or_else(|| "9.0".into()), - - "visionos" => deployment_from_env("XROS_DEPLOYMENT_TARGET") - .or_else(default_deployment_from_sdk) - .unwrap_or_else(|| "1.0".into()), - - os => unreachable!("unknown Apple OS: {}", os), - }; - - self.build_cache - .apple_versions_cache - .write() - .expect("apple_versions_cache lock failed") - .insert(sdk.into(), version.clone()); - - version - } - - fn wasm_musl_sysroot(&self) -> Result { - if let Some(musl_sysroot_path) = self.get_env("WASM_MUSL_SYSROOT") { - Ok(musl_sysroot_path) - } else { - Err(Error::new( - ErrorKind::EnvVarNotFound, - "Environment variable WASM_MUSL_SYSROOT not defined for wasm32. Download sysroot from GitHub & setup environment variable MUSL_SYSROOT targeting the folder.", - )) - } - } - - fn wasi_sysroot(&self) -> Result { - if let Some(wasi_sysroot_path) = self.get_env("WASI_SYSROOT") { - Ok(wasi_sysroot_path) - } else { - Err(Error::new( - ErrorKind::EnvVarNotFound, - "Environment variable WASI_SYSROOT not defined. Download sysroot from GitHub & setup environment variable WASI_SYSROOT targeting the folder.", - )) - } - } - - fn cuda_file_count(&self) -> usize { - self.files - .iter() - .filter(|file| file.extension() == Some(OsStr::new("cu"))) - .count() - } - - fn which(&self, tool: &Path, path_entries: Option<&OsStr>) -> Option { - // Loop through PATH entries searching for the |tool|. - let find_exe_in_path = |path_entries: &OsStr| -> Option { - env::split_paths(path_entries).find_map(|path_entry| check_exe(path_entry.join(tool))) - }; - - // If |tool| is not just one "word," assume it's an actual path... - if tool.components().count() > 1 { - check_exe(PathBuf::from(tool)) - } else { - path_entries - .and_then(find_exe_in_path) - .or_else(|| find_exe_in_path(&self.get_env("PATH")?)) - } - } - - /// search for |prog| on 'programs' path in '|cc| --print-search-dirs' output - fn search_programs( - &self, - cc: &Path, - prog: &Path, - cargo_output: &CargoOutput, - ) -> Option { - let search_dirs = run_output( - self.cmd(cc).arg("--print-search-dirs"), - // this doesn't concern the compilation so we always want to show warnings. - cargo_output, - ) - .ok()?; - // clang driver appears to be forcing UTF-8 output even on Windows, - // hence from_utf8 is assumed to be usable in all cases. - let search_dirs = std::str::from_utf8(&search_dirs).ok()?; - for dirs in search_dirs.split(['\r', '\n']) { - if let Some(path) = dirs.strip_prefix("programs: =") { - return self.which(prog, Some(OsStr::new(path))); - } - } - None - } - - fn find_msvc_tools_find(&self, target: &TargetInfo<'_>, tool: &str) -> Option { - self.find_msvc_tools_find_tool(target, tool) - .map(|c| c.to_command()) - } - - fn find_msvc_tools_find_tool(&self, target: &TargetInfo<'_>, tool: &str) -> Option { - struct BuildEnvGetter<'s>(&'s Build); - - impl ::find_msvc_tools::EnvGetter for BuildEnvGetter<'_> { - fn get_env(&self, name: &str) -> Option<::find_msvc_tools::Env> { - // TODO: Should we allow overriding these with `Build::env`? - // - self.0.get_env(name).map(::find_msvc_tools::Env::Owned) - } - } - - if target.env != "msvc" { - return None; - } - - ::find_msvc_tools::find_tool_with_env(target.full_arch, tool, &BuildEnvGetter(self)) - .map(Tool::from_find_msvc_tools) - } - - /// Compiling for WASI targets typically uses the [wasi-sdk] project and - /// installations of wasi-sdk are typically indicated with the - /// `WASI_SDK_PATH` environment variable. Check to see if that environment - /// variable exists, and check to see if an appropriate compiler is located - /// there. If that all passes then use that compiler by default, but - /// otherwise fall back to whatever the clang default is since gcc doesn't - /// have support for compiling to wasm. - /// - /// [wasi-sdk]: https://github.com/WebAssembly/wasi-sdk - fn autodetect_wasi_compiler(&self, raw_target: &str, clang: &str) -> PathBuf { - if let Some(path) = self.get_env_overridable("WASI_SDK_PATH") { - let target_clang = Path::new(&path) - .join("bin") - .join(format!("{raw_target}-clang")); - if let Some(path) = self.which(&target_clang, None) { - return path; - } - } - - clang.into() - } -} - -impl Default for Build { - fn default() -> Build { - Build::new() - } -} - -fn fail(s: &str) -> ! { - eprintln!("\n\nerror occurred in cc-rs: {s}\n\n"); - std::process::exit(1); -} - -// Use by default minimum available API level -// See note about naming here -// https://android.googlesource.com/platform/ndk/+/refs/heads/ndk-release-r21/docs/BuildSystemMaintainers.md#Clang -static NEW_STANDALONE_ANDROID_COMPILERS: [&str; 4] = [ - "aarch64-linux-android21-clang", - "armv7a-linux-androideabi16-clang", - "i686-linux-android16-clang", - "x86_64-linux-android21-clang", -]; - -// New "standalone" C/C++ cross-compiler executables from recent Android NDK -// are just shell scripts that call main clang binary (from Android NDK) with -// proper `--target` argument. -// -// For example, armv7a-linux-androideabi16-clang passes -// `--target=armv7a-linux-androideabi16` to clang. -// So to construct proper command line check if -// `--target` argument would be passed or not to clang -fn android_clang_compiler_uses_target_arg_internally(clang_path: &Path) -> bool { - if let Some(filename) = clang_path.file_name() { - if let Some(filename_str) = filename.to_str() { - if let Some(idx) = filename_str.rfind('-') { - return filename_str.split_at(idx).0.contains("android"); - } - } - } - false -} - -fn is_llvm_mingw_wrapper(clang_path: &Path) -> bool { - if let Some(filename) = clang_path - .file_name() - .and_then(|file_name| file_name.to_str()) - { - filename.ends_with("-w64-mingw32-clang") || filename.ends_with("-w64-mingw32-clang++") - } else { - false - } -} - -// FIXME: Use parsed target. -fn autodetect_android_compiler(raw_target: &str, gnu: &str, clang: &str) -> PathBuf { - let new_clang_key = match raw_target { - "aarch64-linux-android" => Some("aarch64"), - "armv7-linux-androideabi" => Some("armv7a"), - "i686-linux-android" => Some("i686"), - "x86_64-linux-android" => Some("x86_64"), - _ => None, - }; - - let new_clang = new_clang_key - .map(|key| { - NEW_STANDALONE_ANDROID_COMPILERS - .iter() - .find(|x| x.starts_with(key)) - }) - .unwrap_or(None); - - if let Some(new_clang) = new_clang { - if Command::new(new_clang).output().is_ok() { - return (*new_clang).into(); - } - } - - let target = raw_target - .replace("armv7neon", "arm") - .replace("armv7", "arm") - .replace("thumbv7neon", "arm") - .replace("thumbv7", "arm"); - let gnu_compiler = format!("{target}-{gnu}"); - let clang_compiler = format!("{target}-{clang}"); - - // On Windows, the Android clang compiler is provided as a `.cmd` file instead - // of a `.exe` file. `std::process::Command` won't run `.cmd` files unless the - // `.cmd` is explicitly appended to the command name, so we do that here. - let clang_compiler_cmd = format!("{target}-{clang}.cmd"); - - // Check if gnu compiler is present - // if not, use clang - if Command::new(&gnu_compiler).output().is_ok() { - gnu_compiler - } else if cfg!(windows) && Command::new(&clang_compiler_cmd).output().is_ok() { - clang_compiler_cmd - } else { - clang_compiler - } - .into() -} - -// Rust and clang/cc don't agree on how to name the target. -fn map_darwin_target_from_rust_to_compiler_architecture<'a>(target: &TargetInfo<'a>) -> &'a str { - match target.full_arch { - "aarch64" => "arm64", - "arm64_32" => "arm64_32", - "arm64e" => "arm64e", - "armv7k" => "armv7k", - "armv7s" => "armv7s", - "i386" => "i386", - "i686" => "i386", - "powerpc" => "ppc", - "powerpc64" => "ppc64", - "x86_64" => "x86_64", - "x86_64h" => "x86_64h", - arch => arch, - } -} - -fn is_arm(target: &TargetInfo<'_>) -> bool { - matches!(target.arch, "aarch64" | "arm64ec" | "arm") -} - -#[derive(Clone, Copy, PartialEq)] -enum AsmFileExt { - /// `.asm` files. On MSVC targets, we assume these should be passed to MASM - /// (`ml{,64}.exe`). - DotAsm, - /// `.s` or `.S` files, which do not have the special handling on MSVC targets. - DotS, -} - -impl AsmFileExt { - fn from_path(file: &Path) -> Option { - if let Some(ext) = file.extension() { - if let Some(ext) = ext.to_str() { - let ext = ext.to_lowercase(); - match &*ext { - "asm" => return Some(AsmFileExt::DotAsm), - "s" => return Some(AsmFileExt::DotS), - _ => return None, - } - } - } - None - } -} - -fn check_exe(mut exe: PathBuf) -> Option { - let exe_ext = std::env::consts::EXE_EXTENSION; - let check = exe.exists() || (!exe_ext.is_empty() && exe.set_extension(exe_ext) && exe.exists()); - check.then_some(exe) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_android_clang_compiler_uses_target_arg_internally() { - for version in 16..21 { - assert!(android_clang_compiler_uses_target_arg_internally( - &PathBuf::from(format!("armv7a-linux-androideabi{}-clang", version)) - )); - assert!(android_clang_compiler_uses_target_arg_internally( - &PathBuf::from(format!("armv7a-linux-androideabi{}-clang++", version)) - )); - } - assert!(!android_clang_compiler_uses_target_arg_internally( - &PathBuf::from("clang-i686-linux-android") - )); - assert!(!android_clang_compiler_uses_target_arg_internally( - &PathBuf::from("clang") - )); - assert!(!android_clang_compiler_uses_target_arg_internally( - &PathBuf::from("clang++") - )); - } -} diff --git a/cc-1.2.63/.cargo-checksum.json b/cc-1.2.63/.cargo-checksum.json deleted file mode 100644 index 794453956f..0000000000 --- a/cc-1.2.63/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{".cargo_vcs_info.json":"8a3073ec491914fac013a1366e43b52b7e3f23dc905b7d9b2f441094fba80a43","CHANGELOG.md":"0cef58d8550186c42168d4885920da7acfb206b8135dc998b1bb620ad46a9daf","Cargo.lock":"c220e42977e0ea3200afbdeba357ccb31e4af62254686f6fa24532e1ad711da8","Cargo.toml":"22a278cfb9d278379c1d06019a18ce0a80cc4702c3ad3ea8e9492e6a86684583","Cargo.toml.orig":"1507d20efbdeb1218e81dee778326a07972b06d9cf7070182bd8d0d1d401c6d3","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"cc952a506dc726eed3e128d47d62defbac4470bbb0e465f6a043ae8bf0c96d19","clippy.toml":"cf46251b0953d1a3c7a4f8f8941a08d8a416d5fc348dfe3f3c520eb93fcc18ea","src/command_helpers.rs":"650c9682edc3d7732249e327c1026c7cc733f3702f4867b9580223e97522d3c6","src/detect_compiler_family.c":"97ca4b021495611e828becea6187add37414186a16dfedd26c2947cbce6e8b2f","src/flags.rs":"11f22aa9741b0d445b19ab29ea1076cc32a4143316a74855b6bd682004217543","src/lib.rs":"e893112abbdb4784ea895438e90d1e9261c02bcbc712b8d878e8bfd5c5722d78","src/parallel/async_executor.rs":"4ce24435fff6b6555b43fee042c16bd65d4150d0346567f246b9190d85b45983","src/parallel/command_runner.rs":"1d17d9a037de93b31cc7ee6a914a1e1f6c1ac156cf8619f0014dcbaca6a0b215","src/parallel/job_token.rs":"858b684423bce773d04a042f3f9bafbd21deda21bbb1efefb078a20ef5d8a134","src/parallel/mod.rs":"bf156170790d40a76015a8c78cc2a391cbe1b60bb96c1fac1d047d26e342386a","src/parallel/stderr.rs":"840fe8d0ba613c98fa92cbebc374a028a3212ba7dcea58285483223a31acc7eb","src/target.rs":"a9d2d347a3a08015531f00b32bc8a7478f5636822cc8e0237ea9efa3f07d4cd4","src/target/apple.rs":"da9411b2c4db419e0fa39f765ee53b8665b3837fb39827679a53238734a4a1c1","src/target/generated.rs":"2e1d7e5d8fd954bdcc6e45ef16f69af1a798eacb8d212a516aaf8025908f6d36","src/target/llvm.rs":"190fe8d2b204cd4a6e68f2a1aada17ecd6390799564ed25792fcc08ab34710ba","src/target/parser.rs":"e2a5246a8fe46d39616410685cf7418f6b86344c5309bf09817978b182b3b38c","src/tempfile.rs":"3d9a4bd894862a345aa230a61ec266f0c68f4ef9713d1d9c727482e61f1ea7c3","src/tool.rs":"1d279a6f0738f9164ca794c85bd55951aa95e2adc788e75a6bb8133393d6cce8","src/utilities.rs":"ebb59ac01fb9588bcea0d0ee786e5d5a5696b4284a1245657625db03e830b72e"},"package":"556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f"} \ No newline at end of file diff --git a/cc-1.2.63/.cargo_vcs_info.json b/cc-1.2.63/.cargo_vcs_info.json deleted file mode 100644 index 814f8f83e8..0000000000 --- a/cc-1.2.63/.cargo_vcs_info.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "git": { - "sha1": "b49589a6a1d207d9d61079ddf599c03ef89b3b55" - }, - "path_in_vcs": "" -} \ No newline at end of file diff --git a/cc-1.2.63/CHANGELOG.md b/cc-1.2.63/CHANGELOG.md deleted file mode 100644 index 366497bdd0..0000000000 --- a/cc-1.2.63/CHANGELOG.md +++ /dev/null @@ -1,803 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -## [1.2.63](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.62...cc-v1.2.63) - 2026-05-29 - -### Other - -- Update shlex requirement from 1.3.0 to 2.0.1 ([#1736](https://github.com/rust-lang/cc-rs/pull/1736)) - -## [1.2.62](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.61...cc-v1.2.62) - 2026-05-08 - -### Other - -- Regenerate target info ([#1721](https://github.com/rust-lang/cc-rs/pull/1721)) -- Allow exceptions on wasm platforms ([#1714](https://github.com/rust-lang/cc-rs/pull/1714)) -- Add relibc env ([#1710](https://github.com/rust-lang/cc-rs/pull/1710)) -- recognize sh4 architecture in parse_arch() ([#1712](https://github.com/rust-lang/cc-rs/pull/1712)) - -## [1.2.61](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.60...cc-v1.2.61) - 2026-04-24 - -### Other - -- fix `OutputKind::Capture` documentation ([#1705](https://github.com/rust-lang/cc-rs/pull/1705)) - -## [1.2.60](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.59...cc-v1.2.60) - 2026-04-10 - -### Fixed - -- *(ar)* suppress warnings from `D` modifier probe ([#1700](https://github.com/rust-lang/cc-rs/pull/1700)) - -## [1.2.59](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.58...cc-v1.2.59) - 2026-04-03 - -### Fixed - -- *(ar)* deterministic archives with `D` modifier ([#1697](https://github.com/rust-lang/cc-rs/pull/1697)) - -### Other - -- Regenerate target info ([#1698](https://github.com/rust-lang/cc-rs/pull/1698)) -- Fix target abi parsing for sanitiser targets ([#1695](https://github.com/rust-lang/cc-rs/pull/1695)) - -## [1.2.58](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.57...cc-v1.2.58) - 2026-03-27 - -### Other - -- Update Compile-time Requirements to add info about clang-cl.exe ([#1693](https://github.com/rust-lang/cc-rs/pull/1693)) - -## [1.2.57](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.56...cc-v1.2.57) - 2026-03-13 - -### Other - -- Size archiver batches according to argument length not argument count ([#1689](https://github.com/rust-lang/cc-rs/pull/1689)) -- Added `Build::env` for setting environment variables of compiler invocations and other child processes ([#1656](https://github.com/rust-lang/cc-rs/pull/1656) [#1682](https://github.com/rust-lang/cc-rs/pull/1682)) - -## [1.2.56](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.55...cc-v1.2.56) - 2026-02-13 - -### Other - -- Regenerate target info ([#1676](https://github.com/rust-lang/cc-rs/pull/1676)) -- Fix `clang-cl` target when cross-compiling ([#1670](https://github.com/rust-lang/cc-rs/pull/1670)) - -## [1.2.55](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.54...cc-v1.2.55) - 2026-01-30 - -### Other - -- Regenerate target info ([#1667](https://github.com/rust-lang/cc-rs/pull/1667)) -- Fix RUSTFLAGS typo in test-linker-plugin-lto ([#1665](https://github.com/rust-lang/cc-rs/pull/1665)) -- Disable PIC for armv7-sony-vita-newlibeabihf ([#1664](https://github.com/rust-lang/cc-rs/pull/1664)) - -## [1.2.54](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.53...cc-v1.2.54) - 2026-01-23 - -### Other - -- Fix x86_64-unknown-linux-gnuasan parsing ([#1661](https://github.com/rust-lang/cc-rs/pull/1661)) -- Regenerate target info ([#1660](https://github.com/rust-lang/cc-rs/pull/1660)) - -## [1.2.53](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.52...cc-v1.2.53) - 2026-01-16 - -### Other - -- Add missing RISC-V targets ([#1657](https://github.com/rust-lang/cc-rs/pull/1657)) - -## [1.2.52](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.51...cc-v1.2.52) - 2026-01-09 - -### Other - -- Fix contradictory doc for CC compiler in crate doc ([#1650](https://github.com/rust-lang/cc-rs/pull/1650)) -- Have CUDA compilaion check for sbsa-linux when targeting aarch64. ([#1647](https://github.com/rust-lang/cc-rs/pull/1647)) -- Update link for -Cdwarf-version; Remove -Z (stabilized in 1.88) ([#1648](https://github.com/rust-lang/cc-rs/pull/1648)) -- Fix Build::env_tool to check for .exe on windows ([#1646](https://github.com/rust-lang/cc-rs/pull/1646)) - -## [1.2.51](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.50...cc-v1.2.51) - 2025-12-26 - -### Other - -- Regenerate target info ([#1642](https://github.com/rust-lang/cc-rs/pull/1642)) -- Update Readmes ([#1641](https://github.com/rust-lang/cc-rs/pull/1641)) - -## [1.2.50](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.49...cc-v1.2.50) - 2025-12-19 - -### Other - -- Add tests for `OUT_DIR` escape for '..' file paths (#1631) -- Fix #283: Make warnings(false) actually suppress compiler warnings ([#1633](https://github.com/rust-lang/cc-rs/pull/1633)) - -## [1.2.49](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.48...cc-v1.2.49) - 2025-12-06 - -### Other - -- Fix run_output to prevent infinite blocking ([#1627](https://github.com/rust-lang/cc-rs/pull/1627)) -- Fix detect_family deadlock ([#1626](https://github.com/rust-lang/cc-rs/pull/1626)) -- Fix link in new debug_str doc comment ([#1625](https://github.com/rust-lang/cc-rs/pull/1625)) -- Support more of Cargo's debug levels with Build::debug_str ([#1624](https://github.com/rust-lang/cc-rs/pull/1624)) - -## [1.2.48](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.47...cc-v1.2.48) - 2025-11-28 - -### Other - -- Regenerate target info ([#1620](https://github.com/rust-lang/cc-rs/pull/1620)) - -## [1.2.47](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.46...cc-v1.2.47) - 2025-11-21 - -### Other - -- add helenos linker identifications ([#1615](https://github.com/rust-lang/cc-rs/pull/1615)) - -## [1.2.46](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.45...cc-v1.2.46) - 2025-11-14 - -### Other - -- Add Visual Studio 2026 support ([#1609](https://github.com/rust-lang/cc-rs/pull/1609)) - -## [1.2.45](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.44...cc-v1.2.45) - 2025-11-07 - -### Other - -- Regenerate target info ([#1606](https://github.com/rust-lang/cc-rs/pull/1606)) -- Use a default check for the "env" variable in apple_sdk_name ([#1605](https://github.com/rust-lang/cc-rs/pull/1605)) - -## [1.2.44](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.43...cc-v1.2.44) - 2025-10-31 - -### Other - -- Fix debug assertion for env/abi mismatch ([#1604](https://github.com/rust-lang/cc-rs/pull/1604)) -- Update CHANGELOG for version 1.2.43 ([#1602](https://github.com/rust-lang/cc-rs/pull/1602)) -- Stop passing an invalid target to `llvm-mingw`'s cross-compilation wrappers ([#1495](https://github.com/rust-lang/cc-rs/pull/1495)) - -## [1.2.43](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.42...cc-v1.2.43) - 2025-10-24 - -### Other - -- Mark `static_flag` and `shared_flag` as deprecated ([#1582](https://github.com/rust-lang/cc-rs/pull/1582)) - -## [1.2.42](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.41...cc-v1.2.42) - 2025-10-24 - -### Other - -- Fix check-semver-checks ([#1600](https://github.com/rust-lang/cc-rs/pull/1600)) -- minor improvement for docs ([#1598](https://github.com/rust-lang/cc-rs/pull/1598)) -- Fix linker-plugin-lto: use `-flto=thin` ([#1594](https://github.com/rust-lang/cc-rs/pull/1594)) -- Disable check-buildstd for armv7k-apple-watchos ([#1599](https://github.com/rust-lang/cc-rs/pull/1599)) -- Add elf abi to ppc64 targets ([#1596](https://github.com/rust-lang/cc-rs/pull/1596)) - -## [1.2.41](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.40...cc-v1.2.41) - 2025-10-10 - -### Other - -- Allow using VCToolsVersion to request a specific msvc version ([#1589](https://github.com/rust-lang/cc-rs/pull/1589)) -- Regenerate target info ([#1592](https://github.com/rust-lang/cc-rs/pull/1592)) -- Regenerate windows sys bindings ([#1591](https://github.com/rust-lang/cc-rs/pull/1591)) -- Update windows-bindgen requirement from 0.64 to 0.65 ([#1590](https://github.com/rust-lang/cc-rs/pull/1590)) -- Fix `get_base_archiver_variant` for clang-cl: use `--print-search-dirs` ([#1587](https://github.com/rust-lang/cc-rs/pull/1587)) - -## [1.2.40](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.39...cc-v1.2.40) - 2025-10-03 - -### Other - -- Reorder changelog and remove duplicate Unreleased section ([#1579](https://github.com/rust-lang/cc-rs/pull/1579)) -- Prefer clang if linker-plugin-lto specified ([#1573](https://github.com/rust-lang/cc-rs/pull/1573)) -- Fix building for Mac Catalyst ([#1577](https://github.com/rust-lang/cc-rs/pull/1577)) -- Improve ESP microcontroller targets ([#1574](https://github.com/rust-lang/cc-rs/pull/1574)) - -## [1.2.39](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.38...cc-v1.2.39) - 2025-09-26 - -### Other - -- Fix cross compilation to xtensa-esp32s3-espidf ([#1569](https://github.com/rust-lang/cc-rs/pull/1569)) -- Fix autodetect_wasi_compiler: support non utf-8 path ([#1568](https://github.com/rust-lang/cc-rs/pull/1568)) -- Regenerate target info ([#1567](https://github.com/rust-lang/cc-rs/pull/1567)) -- Fix rustcflags mapping: require -Clinker-plugin-lto for -flto ([#1564](https://github.com/rust-lang/cc-rs/pull/1564)) -- Use `$WASI_SDK_PATH` on WASI targets by default ([#1562](https://github.com/rust-lang/cc-rs/pull/1562)) -- Fix atomicity violations in concurrent cache operations ([#1559](https://github.com/rust-lang/cc-rs/pull/1559)) - -## [1.2.38](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.37...cc-v1.2.38) - 2025-09-19 - -### Other - -- updated the following local packages: find-msvc-tools - -## [1.2.37](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.36...cc-v1.2.37) - 2025-09-12 - -### Other - -- Fix errmsg in RustcCodegenFlags::set_rustc_flag ([#1551](https://github.com/rust-lang/cc-rs/pull/1551)) -- propagate stack protector to Linux C compilers ([#1550](https://github.com/rust-lang/cc-rs/pull/1550)) -- Extract new fn `run_commands_in_parallel` ([#1549](https://github.com/rust-lang/cc-rs/pull/1549)) - -## [1.2.36](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.35...cc-v1.2.36) - 2025-09-05 - -### Other - -- Regenerate windows sys bindings ([#1548](https://github.com/rust-lang/cc-rs/pull/1548)) -- Update windows-bindgen requirement from 0.62 to 0.63 ([#1547](https://github.com/rust-lang/cc-rs/pull/1547)) -- Add fn get_ucrt_dir for find-msvc-tools ([#1546](https://github.com/rust-lang/cc-rs/pull/1546)) -- Regenerate target info ([#1544](https://github.com/rust-lang/cc-rs/pull/1544)) -- fix publish.yml ([#1543](https://github.com/rust-lang/cc-rs/pull/1543)) -- Replace periods with underscores as well when parsing env variables ([#1541](https://github.com/rust-lang/cc-rs/pull/1541)) - -## [1.2.35](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.34...cc-v1.2.35) - 2025-09-01 - -### Fixed - -- fix building for aarch64-apple-visionos-sim on nightly ([#1534](https://github.com/rust-lang/cc-rs/pull/1534)) -- fix tests apple_sdkroot_wrong ([#1530](https://github.com/rust-lang/cc-rs/pull/1530)) - -### Other - -- Regenerate target info ([#1536](https://github.com/rust-lang/cc-rs/pull/1536)) -- Optimize Tool::to_command ([#1535](https://github.com/rust-lang/cc-rs/pull/1535)) -- Extract find-msvc-tools ([#1531](https://github.com/rust-lang/cc-rs/pull/1531)) -- Add prefer_clang_cl_over_msvc ([#1516](https://github.com/rust-lang/cc-rs/pull/1516)) - -## [1.2.34](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.33...cc-v1.2.34) - 2025-08-22 - -### Fixed - -- add `-mcpu=mvp` and `-mmutable-globals` for `wasm32v1-none` ([#1524](https://github.com/rust-lang/cc-rs/pull/1524)) - -### Other - -- Optimize parse_version in find_tools.rs ([#1527](https://github.com/rust-lang/cc-rs/pull/1527)) -- Fallback to manually searching for tool dir ([#1526](https://github.com/rust-lang/cc-rs/pull/1526)) - -## [1.2.33](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.32...cc-v1.2.33) - 2025-08-15 - -### Other - -- Regenerate target info ([#1521](https://github.com/rust-lang/cc-rs/pull/1521)) -- [win][arm64ec] Add testing for Arm64EC Windows ([#1512](https://github.com/rust-lang/cc-rs/pull/1512)) -- Fix parsing of nigthly targets ([#1517](https://github.com/rust-lang/cc-rs/pull/1517)) -- [win][arm64ec] Fix finding assembler and setting is_arm for Arm64EC ([#1511](https://github.com/rust-lang/cc-rs/pull/1511)) - -## [1.2.32](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.31...cc-v1.2.32) - 2025-08-08 - -### Fixed - -- fix new clippy lint introduced in rust 1.89.0 ([#1509](https://github.com/rust-lang/cc-rs/pull/1509)) - -### Other - -- clarify cargo default if no rerun emitted ([#1508](https://github.com/rust-lang/cc-rs/pull/1508)) -- extract compile_objects_sequential ([#1507](https://github.com/rust-lang/cc-rs/pull/1507)) -- Windows `find_tools`: add support for finding Clang ([#1506](https://github.com/rust-lang/cc-rs/pull/1506)) -- Add m68k-unknown-linux-gnu cross-compile target ([#1505](https://github.com/rust-lang/cc-rs/pull/1505)) - -## [1.2.31](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.30...cc-v1.2.31) - 2025-08-01 - -### Other - -- Add doc for using sccache/ccache etc ([#1502](https://github.com/rust-lang/cc-rs/pull/1502)) -- ability to statically link against C++ stdlib ([#1497](https://github.com/rust-lang/cc-rs/pull/1497)) -- Add instructions on using sccache ([#1503](https://github.com/rust-lang/cc-rs/pull/1503)) -- Add support for recognizing some architectures supported by GCC, but not LLVM. ([#1500](https://github.com/rust-lang/cc-rs/pull/1500)) - -## [1.2.30](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.29...cc-v1.2.30) - 2025-07-18 - -### Other - -- define _REENTRANT by default ([#1496](https://github.com/rust-lang/cc-rs/pull/1496)) - -## [1.2.29](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.28...cc-v1.2.29) - 2025-07-05 - -### Other - -- Fix target parsing for powerpc ([#1490](https://github.com/rust-lang/cc-rs/pull/1490)) - -## [1.2.28](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.27...cc-v1.2.28) - 2025-07-04 - -### Other - -- Recognize `mlibc` environment ([#1488](https://github.com/rust-lang/cc-rs/pull/1488)) -- Fix clippy warnings about not using variables in `format!` strings ([#1489](https://github.com/rust-lang/cc-rs/pull/1489)) - -## [1.2.27](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.26...cc-v1.2.27) - 2025-06-13 - -### Other - -- Regenerate windows sys bindings ([#1485](https://github.com/rust-lang/cc-rs/pull/1485)) -- Update windows-bindgen requirement from 0.61 to 0.62 ([#1484](https://github.com/rust-lang/cc-rs/pull/1484)) -- Regenerate target info ([#1483](https://github.com/rust-lang/cc-rs/pull/1483)) - -## [1.2.26](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.25...cc-v1.2.26) - 2025-06-06 - -### Other - -- Also set `SDKROOT` when building apple platforms ([#1475](https://github.com/rust-lang/cc-rs/pull/1475)) -- use windows 2022 in CI ([#1479](https://github.com/rust-lang/cc-rs/pull/1479)) -- Detect -Wslash-u-filename warning on clang-cl ([#1477](https://github.com/rust-lang/cc-rs/pull/1477)) - -## [1.2.25](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.24...cc-v1.2.25) - 2025-05-30 - -### Other - -- make `powerp64` use `powerpc64-linux-gnu` prefix ([#1474](https://github.com/rust-lang/cc-rs/pull/1474)) - -## [1.2.24](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.23...cc-v1.2.24) - 2025-05-23 - -### Other - -- Regenerate windows sys bindings ([#1471](https://github.com/rust-lang/cc-rs/pull/1471)) - -## [1.2.23](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.22...cc-v1.2.23) - 2025-05-16 - -### Other - -- support "vxworks" and "nto" OSes on `get_base_archiver_variant` ([#1456](https://github.com/rust-lang/cc-rs/pull/1456)) - -## [1.2.22](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.21...cc-v1.2.22) - 2025-05-09 - -### Other - -- Add `flags` method to `cc::Build` for adding multiple flags ([#1466](https://github.com/rust-lang/cc-rs/pull/1466)) - -## [1.2.21](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.20...cc-v1.2.21) - 2025-05-02 - -### Other - -- Fix wasm32-unknown-unknown by passing -c ([#1424](https://github.com/rust-lang/cc-rs/pull/1424)) - -## [1.2.20](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.19...cc-v1.2.20) - 2025-04-25 - -### Other - -- Regenerate target info ([#1461](https://github.com/rust-lang/cc-rs/pull/1461)) -- Fix parser.rs on latest rustc nightly ([#1459](https://github.com/rust-lang/cc-rs/pull/1459)) - -## [1.2.19](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.18...cc-v1.2.19) - 2025-04-11 - -### Other - -- Fix musl compilation: Add musl as a prefix fallback ([#1455](https://github.com/rust-lang/cc-rs/pull/1455)) - -## [1.2.18](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.17...cc-v1.2.18) - 2025-04-04 - -### Other - -- Regenerate target info ([#1450](https://github.com/rust-lang/cc-rs/pull/1450)) -- Use `std::thread::available_parallelism` for determining the default number of jobs ([#1447](https://github.com/rust-lang/cc-rs/pull/1447)) -- Fix mips64-openwrt-linux-musl parsing ([#1449](https://github.com/rust-lang/cc-rs/pull/1449)) -- Use compiler prefix `x86_64-linux-musl` ([#1443](https://github.com/rust-lang/cc-rs/pull/1443)) - -## [1.2.17](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.16...cc-v1.2.17) - 2025-03-21 - -### Other - -- Regenerate target info ([#1439](https://github.com/rust-lang/cc-rs/pull/1439)) -- Regenerate windows sys bindings ([#1437](https://github.com/rust-lang/cc-rs/pull/1437)) -- Fix wasm32-wali-linux-musl target parsing ([#1434](https://github.com/rust-lang/cc-rs/pull/1434)) -- Parse `rustc` target names ([#1413](https://github.com/rust-lang/cc-rs/pull/1413)) -- Regenerate target info ([#1429](https://github.com/rust-lang/cc-rs/pull/1429)) -- Added base support for `wasm32-wali-linux-musl` target ([#1373](https://github.com/rust-lang/cc-rs/pull/1373)) - -## [1.2.16](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.15...cc-v1.2.16) - 2025-02-28 - -### Fixed - -- force windows compiler to run in `out_dir` to prevent artifacts in cwd (#1415) - -### Other - -- use `/arch:SSE2` for `x86` target arch (#1425) -- Regenerate windows-sys binding ([#1422](https://github.com/rust-lang/cc-rs/pull/1422)) -- Regenerate target info ([#1418](https://github.com/rust-lang/cc-rs/pull/1418)) -- Add LIB var when compiling flag_check (#1417) -- Change flag ordering ([#1403](https://github.com/rust-lang/cc-rs/pull/1403)) -- Fix archiver detection for musl cross compilation ([#1404](https://github.com/rust-lang/cc-rs/pull/1404)) - -## [1.2.15](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.14...cc-v1.2.15) - 2025-02-21 - -### Other - -- Regenerate target info ([#1406](https://github.com/rust-lang/cc-rs/pull/1406)) -- Always read from all `CFLAGS`-style flags ([#1401](https://github.com/rust-lang/cc-rs/pull/1401)) -- Simplify the error output on failed `Command` invocation ([#1397](https://github.com/rust-lang/cc-rs/pull/1397)) - -## [1.2.14](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.13...cc-v1.2.14) - 2025-02-14 - -### Other - -- Regenerate target info ([#1398](https://github.com/rust-lang/cc-rs/pull/1398)) -- Add support for setting `-gdwarf-{version}` based on RUSTFLAGS ([#1395](https://github.com/rust-lang/cc-rs/pull/1395)) -- Add support for alternative network stack io-sock on QNX 7.1 aarch64 and x86_64 ([#1312](https://github.com/rust-lang/cc-rs/pull/1312)) - -## [1.2.13](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.12...cc-v1.2.13) - 2025-02-08 - -### Other - -- Fix cross-compiling for Apple platforms ([#1389](https://github.com/rust-lang/cc-rs/pull/1389)) - -## [1.2.12](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.11...cc-v1.2.12) - 2025-02-04 - -### Other - -- Split impl Build ([#1382](https://github.com/rust-lang/cc-rs/pull/1382)) -- Don't specify both `-target` and `-mtargetos=` on Apple targets ([#1384](https://github.com/rust-lang/cc-rs/pull/1384)) - -## [1.2.11](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.10...cc-v1.2.11) - 2025-01-31 - -### Other - -- Fix more flag inheritance ([#1380](https://github.com/rust-lang/cc-rs/pull/1380)) -- Include wrapper args. in `stdout` family heuristics to restore classifying `clang --driver-mode=cl` as `Msvc { clang_cl: true }` ([#1378](https://github.com/rust-lang/cc-rs/pull/1378)) -- Constrain `-Clto` and `-Cembed-bitcode` flag inheritance to be `clang`-only ([#1379](https://github.com/rust-lang/cc-rs/pull/1379)) -- Pass deployment target with `-m*-version-min=` ([#1339](https://github.com/rust-lang/cc-rs/pull/1339)) -- Regenerate target info ([#1376](https://github.com/rust-lang/cc-rs/pull/1376)) - -## [1.2.10](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.9...cc-v1.2.10) - 2025-01-17 - -### Other - -- Fix CC_FORCE_DISABLE=0 evaluating to true ([#1371](https://github.com/rust-lang/cc-rs/pull/1371)) -- Regenerate target info ([#1369](https://github.com/rust-lang/cc-rs/pull/1369)) -- Make hidden lifetimes explicit. ([#1366](https://github.com/rust-lang/cc-rs/pull/1366)) - -## [1.2.9](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.8...cc-v1.2.9) - 2025-01-12 - -### Other - -- Don't pass inherited PGO flags to GNU compilers (#1363) -- Adjusted zig cc judgment and avoided zigbuild errors([#1360](https://github.com/rust-lang/cc-rs/pull/1360)) ([#1361](https://github.com/rust-lang/cc-rs/pull/1361)) -- Fix compilation on macOS using clang and fix compilation using zig-cc ([#1364](https://github.com/rust-lang/cc-rs/pull/1364)) - -## [1.2.8](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.7...cc-v1.2.8) - 2025-01-11 - -### Other - -- Add `is_like_clang_cl()` getter (#1357) -- Fix clippy error in lib.rs ([#1356](https://github.com/rust-lang/cc-rs/pull/1356)) -- Regenerate target info ([#1352](https://github.com/rust-lang/cc-rs/pull/1352)) -- Fix compiler family detection issue with clang-cl on macOS ([#1328](https://github.com/rust-lang/cc-rs/pull/1328)) -- Update `windows-bindgen` dependency ([#1347](https://github.com/rust-lang/cc-rs/pull/1347)) -- Fix clippy warnings ([#1346](https://github.com/rust-lang/cc-rs/pull/1346)) - -## [1.2.7](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.6...cc-v1.2.7) - 2025-01-03 - -### Other - -- Regenerate target info ([#1342](https://github.com/rust-lang/cc-rs/pull/1342)) -- Document new supported architecture names in windows::find -- Make is_flag_supported_inner take an &Tool ([#1337](https://github.com/rust-lang/cc-rs/pull/1337)) -- Fix is_flag_supported on msvc ([#1336](https://github.com/rust-lang/cc-rs/pull/1336)) -- Allow using Visual Studio target names in `find_tool` ([#1335](https://github.com/rust-lang/cc-rs/pull/1335)) - -## [1.2.6](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.5...cc-v1.2.6) - 2024-12-27 - -### Other - -- Don't inherit the `/Oy` flag for 64-bit targets ([#1330](https://github.com/rust-lang/cc-rs/pull/1330)) - -## [1.2.5](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.4...cc-v1.2.5) - 2024-12-19 - -### Other - -- Check linking when testing if compiler flags are supported ([#1322](https://github.com/rust-lang/cc-rs/pull/1322)) - -## [1.2.4](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.3...cc-v1.2.4) - 2024-12-13 - -### Other - -- Add support for C/C++ compiler for Neutrino QNX: `qcc` ([#1319](https://github.com/rust-lang/cc-rs/pull/1319)) -- use -maix64 instead of -m64 ([#1307](https://github.com/rust-lang/cc-rs/pull/1307)) - -## [1.2.3](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.2...cc-v1.2.3) - 2024-12-06 - -### Other - -- Improve detection of environment when compiling from msbuild or msvc ([#1310](https://github.com/rust-lang/cc-rs/pull/1310)) -- Better error message when failing on unknown targets ([#1313](https://github.com/rust-lang/cc-rs/pull/1313)) -- Optimize RustcCodegenFlags ([#1305](https://github.com/rust-lang/cc-rs/pull/1305)) - -## [1.2.2](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.1...cc-v1.2.2) - 2024-11-29 - -### Other - -- Inherit flags from rustc ([#1279](https://github.com/rust-lang/cc-rs/pull/1279)) -- Add support for using sccache wrapper with cuda/nvcc ([#1304](https://github.com/rust-lang/cc-rs/pull/1304)) -- Fix msvc stdout not shown on error ([#1303](https://github.com/rust-lang/cc-rs/pull/1303)) -- Regenerate target info ([#1301](https://github.com/rust-lang/cc-rs/pull/1301)) -- Fix compilation of C++ code for armv7-unknown-linux-gnueabihf ([#1298](https://github.com/rust-lang/cc-rs/pull/1298)) -- Fetch target info from Cargo even if `Build::target` is manually set ([#1299](https://github.com/rust-lang/cc-rs/pull/1299)) -- Fix two files with different extensions having the same object name ([#1295](https://github.com/rust-lang/cc-rs/pull/1295)) -- Allow disabling cc's ability to compile via env var CC_FORCE_DISABLE ([#1292](https://github.com/rust-lang/cc-rs/pull/1292)) -- Regenerate target info ([#1293](https://github.com/rust-lang/cc-rs/pull/1293)) - -## [1.2.1](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.0...cc-v1.2.1) - 2024-11-14 - -### Other - -- When invoking `cl -?`, set stdin to null ([#1288](https://github.com/rust-lang/cc-rs/pull/1288)) - -## [1.2.0](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.37...cc-v1.2.0) - 2024-11-11 - -### Added - -- add i686-pc-windows-gnullvm prefix detection ([#1283](https://github.com/rust-lang/cc-rs/pull/1283)) - -### Other - -- Allow only specifying the architecture ([#1285](https://github.com/rust-lang/cc-rs/pull/1285)) -- Fix WASM vs. WASI options ([#1284](https://github.com/rust-lang/cc-rs/pull/1284)) - -## [1.1.37](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.36...cc-v1.1.37) - 2024-11-08 - -### Other - -- Use relative directory for obj files hash ([#1270](https://github.com/rust-lang/cc-rs/pull/1270)) -- Regenerate target info ([#1280](https://github.com/rust-lang/cc-rs/pull/1280)) - -## [1.1.36](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.35...cc-v1.1.36) - 2024-11-05 - -### Other - -- Fix CUDA build with clang++. ([#1273](https://github.com/rust-lang/cc-rs/pull/1273)) - -## [1.1.35](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.34...cc-v1.1.35) - 2024-11-04 - -### Other - -- Remove support for FRC ([#1268](https://github.com/rust-lang/cc-rs/pull/1268)) -- Do not add -fPIC by default on UEFI targets ([#1263](https://github.com/rust-lang/cc-rs/pull/1263)) -- Use -windows-gnu for all UEFI targets ([#1264](https://github.com/rust-lang/cc-rs/pull/1264)) - -## [1.1.34](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.33...cc-v1.1.34) - 2024-11-02 - -### Other - -- Remove redundant flags ([#1256](https://github.com/rust-lang/cc-rs/pull/1256)) - -## [1.1.33](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.32...cc-v1.1.33) - 2024-11-02 - -### Other - -- Reduce size of `cc::Build` and size of generated targets ([#1257](https://github.com/rust-lang/cc-rs/pull/1257)) - -## [1.1.32](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.31...cc-v1.1.32) - 2024-11-02 - -### Other - -- Use `rustc`'s knowledge of LLVM/Clang target triples ([#1252](https://github.com/rust-lang/cc-rs/pull/1252)) -- Use Cargo's target information when possible ([#1225](https://github.com/rust-lang/cc-rs/pull/1225)) - -## [1.1.31](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.30...cc-v1.1.31) - 2024-10-19 - -### Other - -- Add comment explaining why cc does not rebuild on env PATH change ([#1247](https://github.com/rust-lang/cc-rs/pull/1247)) - -## [1.1.30](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.29...cc-v1.1.30) - 2024-10-11 - -### Other - -- Don't pass -fPIC by default on wasm ([#1245](https://github.com/rust-lang/cc-rs/pull/1245)) - -## [1.1.29](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.28...cc-v1.1.29) - 2024-10-11 - -### Other - -- Regenerate target info ([#1243](https://github.com/rust-lang/cc-rs/pull/1243)) - -## [1.1.28](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.27...cc-v1.1.28) - 2024-10-06 - -### Other - -- Environment variables: For one accepting boolean, treat "0", "false" and empty env as false ([#1238](https://github.com/rust-lang/cc-rs/pull/1238)) - -## [1.1.27](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.26...cc-v1.1.27) - 2024-10-06 - -### Other - -- Revert "Use debug version of MSVC runtime library on debug ([#1231](https://github.com/rust-lang/cc-rs/pull/1231))" ([#1237](https://github.com/rust-lang/cc-rs/pull/1237)) -- Disable `CC_ENABLE_DEBUG_OUTPUT` if it is set to "0" ([#1234](https://github.com/rust-lang/cc-rs/pull/1234)) - -## [1.1.26](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.25...cc-v1.1.26) - 2024-10-06 - -### Other - -- Use debug version of MSVC runtime library on debug ([#1231](https://github.com/rust-lang/cc-rs/pull/1231)) - -## [1.1.25](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.24...cc-v1.1.25) - 2024-10-05 - -### Other - -- Remove incorrect "lib" prefixes in CXXSTDLIB doc comments ([#1228](https://github.com/rust-lang/cc-rs/pull/1228)) - -## [1.1.24](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.23...cc-v1.1.24) - 2024-10-01 - -### Other - -- Fix wasm32-wasip1-threads: shared-memory disallowed due to not compiled with 'atomics' or 'bulk-memory' features ([#1221](https://github.com/rust-lang/cc-rs/pull/1221)) -- Reduce the need for the host target triple ([#1224](https://github.com/rust-lang/cc-rs/pull/1224)) -- Add auto cancellation for CI jobs ([#1222](https://github.com/rust-lang/cc-rs/pull/1222)) - -## [1.1.23](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.22...cc-v1.1.23) - 2024-09-30 - -### Other - -- Update doc for detecting changes/upgrades of compilers ([#1218](https://github.com/rust-lang/cc-rs/pull/1218)) - -## [1.1.22](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.21...cc-v1.1.22) - 2024-09-27 - -### Other - -- Don't rerun if PATH changes ([#1215](https://github.com/rust-lang/cc-rs/pull/1215)) - -## [1.1.21](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.20...cc-v1.1.21) - 2024-09-18 - -### Other - -- disable pic for targets that end in `-none` ([#1212](https://github.com/rust-lang/cc-rs/pull/1212)) - -## [1.1.20](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.19...cc-v1.1.20) - 2024-09-17 - -### Other - -- Add buildcache as known Rust and C/C++ compiler wrapper ([#1209](https://github.com/rust-lang/cc-rs/pull/1209)) - -## [1.1.19](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.18...cc-v1.1.19) - 2024-09-15 - -### Other - -- Add support arm64e-apple-darwin ([#1207](https://github.com/rust-lang/cc-rs/pull/1207)) - -## [1.1.18](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.17...cc-v1.1.18) - 2024-09-07 - -### Other -- Fixed unsoundness in `StderrForwarder::forward_available` ([#1203](https://github.com/rust-lang/cc-rs/pull/1203)) - -## [1.1.17](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.16...cc-v1.1.17) - 2024-09-06 - -### Fixed -- fix finding toolchains when invoked by msbuild ([#1201](https://github.com/rust-lang/cc-rs/pull/1201)) - -## [1.1.16](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.15...cc-v1.1.16) - 2024-09-04 - -### Other -- Treat VxWorks wr-cc as a Gnu compiler ([#1198](https://github.com/rust-lang/cc-rs/pull/1198)) - -## [1.1.15](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.14...cc-v1.1.15) - 2024-08-26 - -### Other -- Add -mfloat-abi=hard as a default argument when using any arm/thumb-none-eabihf target ([#1194](https://github.com/rust-lang/cc-rs/pull/1194)) - -## [1.1.14](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.13...cc-v1.1.14) - 2024-08-23 - -### Other -- allow finding tools from path if VisualStudioDir is set - -## [1.1.13](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.12...cc-v1.1.13) - 2024-08-16 - -### Other -- Fix detect family: should detect emscripten as clang, closes [#1185](https://github.com/rust-lang/cc-rs/pull/1185) ([#1186](https://github.com/rust-lang/cc-rs/pull/1186)) - -## [1.1.12](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.11...cc-v1.1.12) - 2024-08-15 - -### Other -- improve docs ([#1183](https://github.com/rust-lang/cc-rs/pull/1183)) - -## [1.1.11](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.10...cc-v1.1.11) - 2024-08-14 - -### Other -- Add support for parsing shell encoded `*FLAGS` ([#1181](https://github.com/rust-lang/cc-rs/pull/1181)) -- Replace vector of tuples with BTreeMap which already is sorted and free of duplicates ([#1177](https://github.com/rust-lang/cc-rs/pull/1177)) - -## [1.1.10](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.9...cc-v1.1.10) - 2024-08-11 - -### Other -- Remap Windows targets triples to their LLVM counterparts ([#1176](https://github.com/rust-lang/cc-rs/pull/1176)) - -## [1.1.9](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.8...cc-v1.1.9) - 2024-08-11 - -### Other -- Add custom CC wrapper to the wrapper whitelist ([#1175](https://github.com/rust-lang/cc-rs/pull/1175)) - -## [1.1.8](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.7...cc-v1.1.8) - 2024-08-06 - -### Other -- Fix broken link in docs.rs ([#1173](https://github.com/rust-lang/cc-rs/pull/1173)) - -## [1.1.7](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.6...cc-v1.1.7) - 2024-07-29 - -### Other -- add `.objects` ([#1166](https://github.com/rust-lang/cc-rs/pull/1166)) - -## [1.1.6](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.5...cc-v1.1.6) - 2024-07-19 - -### Other -- Clippy fixes ([#1163](https://github.com/rust-lang/cc-rs/pull/1163)) - -## [1.1.5](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.4...cc-v1.1.5) - 2024-07-15 - -### Other -- Fix cyclic compilation: Use vendored once_cell ([#1154](https://github.com/rust-lang/cc-rs/pull/1154)) - -## [1.1.4](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.3...cc-v1.1.4) - 2024-07-14 - -### Other -- Support compiling on wasm targets (Supersede [#1068](https://github.com/rust-lang/cc-rs/pull/1068)) ([#1160](https://github.com/rust-lang/cc-rs/pull/1160)) - -## [1.1.3](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.2...cc-v1.1.3) - 2024-07-14 - -### Other -- Reduce msrv to 1.63 ([#1158](https://github.com/rust-lang/cc-rs/pull/1158)) -- Revert "Use raw-dylib for windows-sys ([#1137](https://github.com/rust-lang/cc-rs/pull/1137))" ([#1157](https://github.com/rust-lang/cc-rs/pull/1157)) -- Fix typos ([#1152](https://github.com/rust-lang/cc-rs/pull/1152)) -- Fix `doc_lazy_continuation` lints ([#1153](https://github.com/rust-lang/cc-rs/pull/1153)) - -## [1.1.2](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.1...cc-v1.1.2) - 2024-07-12 - -### Other -- Add empty `jobserver` feature. ([#1150](https://github.com/rust-lang/cc-rs/pull/1150)) - -## [1.1.1](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.0...cc-v1.1.1) - 2024-07-12 - -### Other -- Fix is_flag_supported not respecting emit_rerun_if_env_changed ([#1147](https://github.com/rust-lang/cc-rs/pull/1147)) ([#1148](https://github.com/rust-lang/cc-rs/pull/1148)) - -## [1.1.0](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.106...cc-v1.1.0) - 2024-07-08 - -### Added -- add cargo_output to eliminate last vestiges of stdout pollution ([#1141](https://github.com/rust-lang/cc-rs/pull/1141)) - -## [1.0.106](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.105...cc-v1.0.106) - 2024-07-08 - -### Other -- Drop support for Visual Studio 12 (2013) ([#1046](https://github.com/rust-lang/cc-rs/pull/1046)) -- Use raw-dylib for windows-sys ([#1137](https://github.com/rust-lang/cc-rs/pull/1137)) -- Bump msrv to 1.67 ([#1143](https://github.com/rust-lang/cc-rs/pull/1143)) -- Bump msrv to 1.65 ([#1140](https://github.com/rust-lang/cc-rs/pull/1140)) -- Fix clippy warnings ([#1138](https://github.com/rust-lang/cc-rs/pull/1138)) - -## [1.0.105](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.104...cc-v1.0.105) - 2024-07-07 - -### Other -- Regenerate windows sys bindings ([#1132](https://github.com/rust-lang/cc-rs/pull/1132)) -- Fix generate-windows-sys-bindings ([#1133](https://github.com/rust-lang/cc-rs/pull/1133)) -- Fix gen-windows-sys-binding ([#1130](https://github.com/rust-lang/cc-rs/pull/1130)) -- Fix gen-windows-sys-binding ([#1127](https://github.com/rust-lang/cc-rs/pull/1127)) -- Update windows-bindgen requirement from 0.57 to 0.58 ([#1123](https://github.com/rust-lang/cc-rs/pull/1123)) - -## [1.0.104](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.103...cc-v1.0.104) - 2024-07-01 - -### Other -- Fixed link break about compile-time-requirements ([#1118](https://github.com/rust-lang/cc-rs/pull/1118)) - -## [1.0.103](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.102...cc-v1.0.103) - 2024-06-30 - -### Other -- Fix compilation for wasm: env WASI_SYSROOT should be optional ([#1114](https://github.com/rust-lang/cc-rs/pull/1114)) - -## [1.0.102](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.101...cc-v1.0.102) - 2024-06-29 - -### Other -- Fix invalid wasi targets compatibility ([#1105](https://github.com/rust-lang/cc-rs/pull/1105)) -- Speedup regenerate-target-info and regenerate-windows-sys ([#1110](https://github.com/rust-lang/cc-rs/pull/1110)) - -## [1.0.101](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.100...cc-v1.0.101) - 2024-06-25 - -### Other -- Use `Build::getenv` instead of `env::var*` in anywhere that makes sense ([#1103](https://github.com/rust-lang/cc-rs/pull/1103)) - -## [1.0.100](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.99...cc-v1.0.100) - 2024-06-23 - -### Other -- Update publish.yml to use release-plz ([#1101](https://github.com/rust-lang/cc-rs/pull/1101)) -- Accept `OsStr` instead of `str` for flags ([#1100](https://github.com/rust-lang/cc-rs/pull/1100)) -- Use `dep:` syntax to avoid implicit features. ([#1099](https://github.com/rust-lang/cc-rs/pull/1099)) -- Minor clippy fixes. ([#1098](https://github.com/rust-lang/cc-rs/pull/1098)) -- Fix WASI compilation for C++ ([#1083](https://github.com/rust-lang/cc-rs/pull/1083)) -- Regenerate windows sys bindings ([#1096](https://github.com/rust-lang/cc-rs/pull/1096)) -- Rename regenerate-windows-sys to regenerate-windows-sys.yml ([#1095](https://github.com/rust-lang/cc-rs/pull/1095)) -- Create regenerate-windows-sys.yml ([#1094](https://github.com/rust-lang/cc-rs/pull/1094)) -- Update windows-bindgen requirement from 0.56 to 0.57 ([#1091](https://github.com/rust-lang/cc-rs/pull/1091)) -- Eagerly close tempfile to fix [#1082](https://github.com/rust-lang/cc-rs/pull/1082) ([#1087](https://github.com/rust-lang/cc-rs/pull/1087)) -- Output msvc.exe in the output directory ([#1090](https://github.com/rust-lang/cc-rs/pull/1090)) -- Fix clippy warnings on Windows ([#1088](https://github.com/rust-lang/cc-rs/pull/1088)) -- Don't try to free DLL on drop ([#1089](https://github.com/rust-lang/cc-rs/pull/1089)) -- Fix panic safety issue in StderrForwarder ([#1079](https://github.com/rust-lang/cc-rs/pull/1079)) diff --git a/cc-1.2.63/Cargo.lock b/cc-1.2.63/Cargo.lock deleted file mode 100644 index 9892a98d1f..0000000000 --- a/cc-1.2.63/Cargo.lock +++ /dev/null @@ -1,492 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - -[[package]] -name = "bitflags" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" - -[[package]] -name = "cc" -version = "1.2.63" -dependencies = [ - "find-msvc-tools", - "jobserver", - "libc", - "shlex", - "tempfile", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys", -] - -[[package]] -name = "fastrand" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" - -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "libc", - "r-efi 5.3.0", - "wasip2", -] - -[[package]] -name = "getrandom" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" -dependencies = [ - "cfg-if", - "libc", - "r-efi 6.0.0", - "wasip2", - "wasip3", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash", -] - -[[package]] -name = "hashbrown" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - -[[package]] -name = "indexmap" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" -dependencies = [ - "equivalent", - "hashbrown 0.17.1", - "serde", - "serde_core", -] - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - -[[package]] -name = "libc" -version = "0.2.186" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" - -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - -[[package]] -name = "log" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" - -[[package]] -name = "memchr" -version = "2.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn", -] - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "r-efi" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" - -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", -] - -[[package]] -name = "semver" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.150" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "shlex" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tempfile" -version = "3.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" -dependencies = [ - "fastrand", - "getrandom 0.4.2", - "once_cell", - "rustix", - "windows-sys", -] - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "wasip2" -version = "1.0.3+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" -dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen 0.51.0", -] - -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap", - "semver", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen" -version = "0.57.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - -[[package]] -name = "zmij" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/cc-1.2.63/Cargo.toml b/cc-1.2.63/Cargo.toml deleted file mode 100644 index cea850e613..0000000000 --- a/cc-1.2.63/Cargo.toml +++ /dev/null @@ -1,79 +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 are reading this file be aware that the original Cargo.toml -# will likely look very different (and much more reasonable). -# See Cargo.toml.orig for the original contents. - -[package] -edition = "2018" -rust-version = "1.63" -name = "cc" -version = "1.2.63" -authors = ["Alex Crichton "] -build = false -exclude = [ - "/.github", - "tests", - "src/bin", -] -autolib = false -autobins = false -autoexamples = false -autotests = false -autobenches = false -description = """ -A build-time dependency for Cargo build scripts to assist in invoking the native -C compiler to compile native C code into a static archive to be linked into Rust -code. -""" -homepage = "https://github.com/rust-lang/cc-rs" -documentation = "https://docs.rs/cc" -readme = "README.md" -keywords = ["build-dependencies"] -categories = ["development-tools::build-utils"] -license = "MIT OR Apache-2.0" -repository = "https://github.com/rust-lang/cc-rs" - -[features] -jobserver = [] -parallel = [ - "dep:libc", - "dep:jobserver", -] - -[lib] -name = "cc" -path = "src/lib.rs" - -[dependencies.find-msvc-tools] -version = "0.1.9" - -[dependencies.jobserver] -version = "0.1.30" -optional = true -default-features = false - -[dependencies.shlex] -version = "2.0.1" - -[dev-dependencies.tempfile] -version = "3" - -[target."cfg(unix)".dependencies.libc] -version = "0.2.62" -optional = true -default-features = false - -[lints.rust.unexpected_cfgs] -level = "allow" -priority = 0 -check-cfg = ["cfg(disable_clang_cl_tests)"] - -[profile.release] -opt-level = 3 -lto = true diff --git a/cc-1.2.63/Cargo.toml.orig b/cc-1.2.63/Cargo.toml.orig deleted file mode 100644 index dd1b2bc7ea..0000000000 --- a/cc-1.2.63/Cargo.toml.orig +++ /dev/null @@ -1,59 +0,0 @@ -[package] -name = "cc" -version = "1.2.63" -authors = ["Alex Crichton "] -license = "MIT OR Apache-2.0" -repository = "https://github.com/rust-lang/cc-rs" -homepage = "https://github.com/rust-lang/cc-rs" -documentation = "https://docs.rs/cc" -description = """ -A build-time dependency for Cargo build scripts to assist in invoking the native -C compiler to compile native C code into a static archive to be linked into Rust -code. -""" -keywords = ["build-dependencies"] -readme = "README.md" -categories = ["development-tools::build-utils"] -# The binary target is only used by tests. -exclude = ["/.github", "tests", "src/bin"] -edition = "2018" -rust-version = "1.63" - -[dependencies] -jobserver = { version = "0.1.30", default-features = false, optional = true } -shlex = "2.0.1" -find-msvc-tools = { version = "0.1.9", path = "find-msvc-tools" } - -[target.'cfg(unix)'.dependencies] -# Don't turn on the feature "std" for this, see https://github.com/rust-lang/cargo/issues/4866 -# which is still an issue with `resolver = "1"`. -libc = { version = "0.2.62", default-features = false, optional = true } - -[features] -parallel = ["dep:libc", "dep:jobserver"] -# This is a placeholder feature for people who incorrectly used `cc` with `features = ["jobserver"]` -# so that they aren't broken. This has never enabled `parallel`, so we won't do that. -jobserver = [] - -[dev-dependencies] -tempfile = "3" - -[workspace] -members = [ - "find-msvc-tools", - "dev-tools/cc-test", - "dev-tools/gen-target-info", - "dev-tools/gen-windows-sys-binding", - "dev-tools/wasi-test", -] - -[patch.crates-io] -cc = { path = "." } - -[lints.rust] -unexpected_cfgs = { level = "allow", check-cfg = ['cfg(disable_clang_cl_tests)'] } - -[profile.release] -opt-level = 3 # Or "s" or "z" for different optimization goals -lto = true - diff --git a/cc-1.2.63/README.md b/cc-1.2.63/README.md deleted file mode 100644 index f1d11190bf..0000000000 --- a/cc-1.2.63/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# cc-rs - -A library for -[Cargo build scripts](https://doc.rust-lang.org/cargo/reference/build-scripts.html) -to compile a set of C/C++/assembly/CUDA files into a static archive for Cargo to -link into the crate being built. This crate does not compile code itself; it -calls out to the default compiler for the platform. This crate will -automatically detect situations such as cross compilation and various -environment variables and will build code appropriately. - -Refer to the [documentation](https://docs.rs/cc) for detailed usage -instructions. - -## License - -This project is licensed under either of - - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or - https://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](LICENSE-MIT) or - https://opensource.org/license/mit) - -at your option. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in cc-rs by you, as defined in the Apache-2.0 license, shall be -dual licensed as above, without any additional terms or conditions. diff --git a/cc-1.2.63/clippy.toml b/cc-1.2.63/clippy.toml deleted file mode 100644 index 79dd5a43b5..0000000000 --- a/cc-1.2.63/clippy.toml +++ /dev/null @@ -1,7 +0,0 @@ -disallowed-methods = [ - { path = "std::env::var_os", reason = "Please use Build::getenv" }, - { path = "std::env::var", reason = "Please use Build::getenv" }, - { path = "std::env::set_var", reason = "use `GlobalEnv::lock().set`" }, - { path = "std::env::remove_var", reason = "use `GlobalEnv::lock().remove`" }, -] -doc-valid-idents = ["AppleClang", "OpenBSD", ".."] diff --git a/cc-1.2.63/src/command_helpers.rs b/cc-1.2.63/src/command_helpers.rs deleted file mode 100644 index 57c49f434e..0000000000 --- a/cc-1.2.63/src/command_helpers.rs +++ /dev/null @@ -1,482 +0,0 @@ -//! Miscellaneous helpers for running commands - -use std::{ - borrow::Cow, - collections::hash_map, - ffi::OsString, - fmt::Display, - fs, - hash::Hasher, - io::{self, Read, Write}, - path::Path, - process::{Child, ChildStderr, Command, Output, Stdio}, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }, -}; - -use crate::{utilities::cargo_env_var_os, Error, ErrorKind, Object}; - -#[derive(Clone, Debug)] -pub(crate) struct CargoOutput { - pub(crate) metadata: bool, - pub(crate) warnings: bool, - pub(crate) debug: bool, - pub(crate) output: OutputKind, - checked_dbg_var: Arc, -} - -/// Different strategies for handling compiler output (to stdout) -#[derive(Clone, Debug)] -pub(crate) enum OutputKind { - /// Forward the output to this process' stdout ([`Stdio::inherit()`]) - Forward, - /// Discard the output ([`Stdio::null()`]) - Discard, - /// Capture the result ([`Stdio::piped()`]) - Capture, -} - -impl CargoOutput { - pub(crate) fn new() -> Self { - #[allow(clippy::disallowed_methods)] - Self { - metadata: true, - warnings: true, - output: OutputKind::Forward, - debug: match std::env::var_os("CC_ENABLE_DEBUG_OUTPUT") { - Some(v) => v != "0" && v != "false" && !v.is_empty(), - None => false, - }, - checked_dbg_var: Arc::new(AtomicBool::new(false)), - } - } - - pub(crate) fn print_metadata(&self, s: &dyn Display) { - if self.metadata { - println!("{s}"); - } - } - - pub(crate) fn print_warning(&self, arg: &dyn Display) { - if self.warnings { - println!("cargo:warning={arg}"); - } - } - - pub(crate) fn print_debug(&self, arg: &dyn Display) { - if self.metadata - && self - .checked_dbg_var - .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) - .is_ok() - { - println!("cargo:rerun-if-env-changed=CC_ENABLE_DEBUG_OUTPUT"); - } - if self.debug { - println!("{arg}"); - } - } - - fn stdio_for_warnings(&self) -> Stdio { - if self.warnings { - Stdio::piped() - } else { - Stdio::null() - } - } - - fn stdio_for_output(&self) -> Stdio { - match self.output { - OutputKind::Capture => Stdio::piped(), - OutputKind::Forward => Stdio::inherit(), - OutputKind::Discard => Stdio::null(), - } - } -} - -pub(crate) struct StderrForwarder { - inner: Option<(ChildStderr, Vec)>, - #[cfg(feature = "parallel")] - is_non_blocking: bool, - #[cfg(feature = "parallel")] - bytes_available_failed: bool, - /// number of bytes buffered in inner - bytes_buffered: usize, -} - -const MIN_BUFFER_CAPACITY: usize = 100; - -impl StderrForwarder { - pub(crate) fn new(child: &mut Child) -> Self { - Self { - inner: child - .stderr - .take() - .map(|stderr| (stderr, Vec::with_capacity(MIN_BUFFER_CAPACITY))), - bytes_buffered: 0, - #[cfg(feature = "parallel")] - is_non_blocking: false, - #[cfg(feature = "parallel")] - bytes_available_failed: false, - } - } - - pub(crate) fn forward_available(&mut self) -> bool { - if let Some((stderr, buffer)) = self.inner.as_mut() { - loop { - // For non-blocking we check to see if there is data available, so we should try to - // read at least that much. For blocking, always read at least the minimum amount. - #[cfg(not(feature = "parallel"))] - let to_reserve = MIN_BUFFER_CAPACITY; - #[cfg(feature = "parallel")] - let to_reserve = if self.is_non_blocking && !self.bytes_available_failed { - match crate::parallel::stderr::bytes_available(stderr) { - #[cfg(windows)] - Ok(0) => break false, - #[cfg(unix)] - Ok(0) => { - // On Unix, depending on the implementation, we may sometimes get 0 in a - // loop (either there is data available or the pipe is broken), so - // continue with the non-blocking read anyway. - MIN_BUFFER_CAPACITY - } - #[cfg(windows)] - Err(_) => { - // On Windows, if we get an error then the pipe is broken, so flush - // the buffer and bail. - if !buffer.is_empty() { - write_warning(&buffer[..]); - } - self.inner = None; - break true; - } - #[cfg(unix)] - Err(_) => { - // On Unix, depending on the implementation, we may get spurious - // errors so make a note not to use bytes_available again and try - // the non-blocking read anyway. - self.bytes_available_failed = true; - MIN_BUFFER_CAPACITY - } - #[cfg(target_family = "wasm")] - Err(_) => panic!("bytes_available should always succeed on wasm"), - Ok(bytes_available) => MIN_BUFFER_CAPACITY.max(bytes_available), - } - } else { - MIN_BUFFER_CAPACITY - }; - if self.bytes_buffered + to_reserve > buffer.len() { - buffer.resize(self.bytes_buffered + to_reserve, 0); - } - - match stderr.read(&mut buffer[self.bytes_buffered..]) { - Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => { - // No data currently, yield back. - break false; - } - Err(err) if err.kind() == std::io::ErrorKind::Interrupted => { - // Interrupted, try again. - continue; - } - Ok(bytes_read) if bytes_read != 0 => { - self.bytes_buffered += bytes_read; - let mut consumed = 0; - for line in buffer[..self.bytes_buffered].split_inclusive(|&b| b == b'\n') { - // Only forward complete lines, leave the rest in the buffer. - if let Some((b'\n', line)) = line.split_last() { - consumed += line.len() + 1; - write_warning(line); - } - } - if consumed > 0 && consumed < self.bytes_buffered { - // Remove the consumed bytes from buffer - buffer.copy_within(consumed.., 0); - } - self.bytes_buffered -= consumed; - } - res => { - // End of stream: flush remaining data and bail. - if self.bytes_buffered > 0 { - write_warning(&buffer[..self.bytes_buffered]); - } - if let Err(err) = res { - write_warning( - format!("Failed to read from child stderr: {err}").as_bytes(), - ); - } - self.inner.take(); - break true; - } - } - } - } else { - true - } - } - - #[cfg(feature = "parallel")] - pub(crate) fn set_non_blocking(&mut self) -> Result<(), Error> { - assert!(!self.is_non_blocking); - - #[cfg(unix)] - if let Some((stderr, _)) = self.inner.as_ref() { - crate::parallel::stderr::set_non_blocking(stderr)?; - } - - self.is_non_blocking = true; - Ok(()) - } - - #[cfg(feature = "parallel")] - pub(crate) fn forward_all(&mut self) { - while !self.forward_available() {} - } - - #[cfg(not(feature = "parallel"))] - fn forward_all(&mut self) { - let forward_result = self.forward_available(); - assert!(forward_result, "Should have consumed all data"); - } -} - -fn write_warning(line: &[u8]) { - let stdout = io::stdout(); - let mut stdout = stdout.lock(); - stdout.write_all(b"cargo:warning=").unwrap(); - stdout.write_all(line).unwrap(); - stdout.write_all(b"\n").unwrap(); -} - -fn wait_on_child( - cmd: &Command, - child: &mut Child, - cargo_output: &CargoOutput, -) -> Result<(), Error> { - StderrForwarder::new(child).forward_all(); - - let status = match child.wait() { - Ok(s) => s, - Err(e) => { - return Err(Error::new( - ErrorKind::ToolExecError, - format!("failed to wait on spawned child process `{cmd:?}`: {e}"), - )); - } - }; - - cargo_output.print_debug(&status); - - if status.success() { - Ok(()) - } else { - Err(Error::new( - ErrorKind::ToolExecError, - format!("command did not execute successfully (status code {status}): {cmd:?}"), - )) - } -} - -/// Find the destination object path for each file in the input source files, -/// and store them in the output Object. -pub(crate) fn objects_from_files(files: &[Arc], dst: &Path) -> Result, Error> { - let mut objects = Vec::with_capacity(files.len()); - for file in files { - let basename = file - .file_name() - .ok_or_else(|| { - Error::new( - ErrorKind::InvalidArgument, - "No file_name for object file path!", - ) - })? - .to_string_lossy(); - let dirname = file - .parent() - .ok_or_else(|| { - Error::new( - ErrorKind::InvalidArgument, - "No parent for object file path!", - ) - })? - .to_string_lossy(); - - // Hash the dirname. This should prevent conflicts if we have multiple - // object files with the same filename in different subfolders. - let mut hasher = hash_map::DefaultHasher::new(); - - // Make the dirname relative (if possible) to avoid full system paths influencing the sha - // and making the output system-dependent - let dirname = if let Some(root) = cargo_env_var_os("CARGO_MANIFEST_DIR") { - let root = root.to_string_lossy(); - Cow::Borrowed(dirname.strip_prefix(&*root).unwrap_or(&dirname)) - } else { - dirname - }; - - hasher.write(dirname.as_bytes()); - if let Some(extension) = file.extension() { - hasher.write(extension.to_string_lossy().as_bytes()); - } - - let obj = dst - .join(format!("{:016x}-{}", hasher.finish(), basename)) - .with_extension("o"); - - match obj.parent() { - Some(s) => fs::create_dir_all(s)?, - None => { - return Err(Error::new( - ErrorKind::InvalidArgument, - "dst is an invalid path with no parent", - )); - } - }; - - objects.push(Object::new(file.to_path_buf(), obj)); - } - - Ok(objects) -} - -pub(crate) fn run(cmd: &mut Command, cargo_output: &CargoOutput) -> Result<(), Error> { - let mut child = spawn(cmd, cargo_output)?; - wait_on_child(cmd, &mut child, cargo_output) -} - -/// Like [`run`], but stderr is only forwarded as `cargo:warning=` when the -/// command succeeds. On failure, stderr is silently discarded. -/// -/// Useful for probe commands where failure is expected and the error -/// message is not actionable. -pub(crate) fn run_silent_on_error( - cmd: &mut Command, - cargo_output: &CargoOutput, -) -> Result<(), Error> { - let Output { - status, - stdout: _, - stderr, - } = spawn_and_wait_for_output(cmd, cargo_output)?; - - cargo_output.print_debug(&status); - - if status.success() { - if cargo_output.warnings { - stderr - .split(|&b| b == b'\n') - .map(|line| line.strip_suffix(b"\r").unwrap_or(line)) - .filter(|line| !line.is_empty()) - .for_each(write_warning); - } - Ok(()) - } else { - Err(Error::new( - ErrorKind::ToolExecError, - format!("command did not execute successfully (status code {status}): {cmd:?}"), - )) - } -} - -pub(crate) fn spawn_and_wait_for_output( - cmd: &mut Command, - cargo_output: &CargoOutput, -) -> Result { - // We specifically need the output to be captured, so override default - let mut captured_cargo_output = cargo_output.clone(); - captured_cargo_output.output = OutputKind::Capture; - spawn(cmd, &captured_cargo_output)? - .wait_with_output() - .map_err(|e| { - Error::new( - ErrorKind::ToolExecError, - format!("failed to wait on spawned child process `{cmd:?}`: {e}"), - ) - }) -} - -pub(crate) fn run_output(cmd: &mut Command, cargo_output: &CargoOutput) -> Result, Error> { - let Output { - status, - stdout, - stderr, - } = spawn_and_wait_for_output(cmd, cargo_output)?; - - stderr - .split(|&b| b == b'\n') - .filter(|part| !part.is_empty()) - .for_each(write_warning); - - cargo_output.print_debug(&status); - - if status.success() { - Ok(stdout) - } else { - Err(Error::new( - ErrorKind::ToolExecError, - format!("command did not execute successfully (status code {status}): {cmd:?}"), - )) - } -} - -pub(crate) fn spawn(cmd: &mut Command, cargo_output: &CargoOutput) -> Result { - struct ResetStderr<'cmd>(&'cmd mut Command); - - impl Drop for ResetStderr<'_> { - fn drop(&mut self) { - // Reset stderr to default to release pipe_writer so that print thread will - // not block forever. - self.0.stderr(Stdio::inherit()); - } - } - - cargo_output.print_debug(&format_args!("running: {cmd:?}")); - - let cmd = ResetStderr(cmd); - let child = cmd - .0 - .stderr(cargo_output.stdio_for_warnings()) - .stdout(cargo_output.stdio_for_output()) - .spawn(); - match child { - Ok(child) => Ok(child), - Err(ref e) if e.kind() == io::ErrorKind::NotFound => { - let extra = if cfg!(windows) { - " (see https://docs.rs/cc/latest/cc/#compile-time-requirements for help)" - } else { - "" - }; - Err(Error::new( - ErrorKind::ToolNotFound, - format!("failed to find tool {:?}: {e}{extra}", cmd.0.get_program()), - )) - } - Err(e) => Err(Error::new( - ErrorKind::ToolExecError, - format!("command `{:?}` failed to start: {e}", cmd.0), - )), - } -} - -pub(crate) struct CmdAddOutputFileArgs { - pub(crate) cuda: bool, - pub(crate) is_assembler_msvc: bool, - pub(crate) msvc: bool, - pub(crate) clang: bool, - pub(crate) gnu: bool, - pub(crate) is_asm: bool, - pub(crate) is_arm: bool, -} - -pub(crate) fn command_add_output_file(cmd: &mut Command, dst: &Path, args: CmdAddOutputFileArgs) { - if args.is_assembler_msvc - || !(!args.msvc || args.clang || args.gnu || args.cuda || (args.is_asm && args.is_arm)) - { - let mut s = OsString::from("-Fo"); - s.push(dst); - cmd.arg(s); - } else { - cmd.arg("-o").arg(dst); - } -} diff --git a/cc-1.2.63/src/detect_compiler_family.c b/cc-1.2.63/src/detect_compiler_family.c deleted file mode 100644 index 601cee6c48..0000000000 --- a/cc-1.2.63/src/detect_compiler_family.c +++ /dev/null @@ -1,15 +0,0 @@ -#ifdef __clang__ -#pragma message "clang" -#endif - -#ifdef __GNUC__ -#pragma message "gcc" -#endif - -#ifdef __EMSCRIPTEN__ -#pragma message "emscripten" -#endif - -#ifdef __VXWORKS__ -#pragma message "VxWorks" -#endif diff --git a/cc-1.2.63/src/flags.rs b/cc-1.2.63/src/flags.rs deleted file mode 100644 index f3830212f0..0000000000 --- a/cc-1.2.63/src/flags.rs +++ /dev/null @@ -1,563 +0,0 @@ -use crate::target::TargetInfo; -use crate::{Build, Error, ErrorKind, Tool, ToolFamily}; -use std::borrow::Cow; -use std::ffi::OsString; - -#[derive(Debug, PartialEq, Default)] -pub(crate) struct RustcCodegenFlags<'a> { - branch_protection: Option<&'a str>, - code_model: Option<&'a str>, - no_vectorize_loops: bool, - no_vectorize_slp: bool, - profile_generate: Option<&'a str>, - profile_use: Option<&'a str>, - control_flow_guard: Option<&'a str>, - lto: Option<&'a str>, - relocation_model: Option<&'a str>, - embed_bitcode: Option, - force_frame_pointers: Option, - no_redzone: Option, - soft_float: Option, - dwarf_version: Option, - stack_protector: Option<&'a str>, - linker_plugin_lto: Option, -} - -impl<'this> RustcCodegenFlags<'this> { - // Parse flags obtained from CARGO_ENCODED_RUSTFLAGS - pub(crate) fn parse(rustflags_env: &'this str) -> Result { - fn is_flag_prefix(flag: &str) -> bool { - [ - "-Z", - "-C", - "--codegen", - "-L", - "-l", - "-o", - "-W", - "--warn", - "-A", - "--allow", - "-D", - "--deny", - "-F", - "--forbid", - ] - .contains(&flag) - } - - fn handle_flag_prefix<'a>(prev: &'a str, curr: &'a str) -> (&'a str, &'a str) { - match prev { - "--codegen" | "-C" => ("-C", curr), - // Handle flags passed like --codegen=code-model=small - _ if curr.starts_with("--codegen=") => ("-C", &curr[10..]), - "-Z" => ("-Z", curr), - "-L" | "-l" | "-o" => (prev, curr), - // Handle lint flags - "-W" | "--warn" => ("-W", curr), - "-A" | "--allow" => ("-A", curr), - "-D" | "--deny" => ("-D", curr), - "-F" | "--forbid" => ("-F", curr), - _ => ("", curr), - } - } - - let mut codegen_flags = Self::default(); - - let mut prev_prefix = None; - for curr in rustflags_env.split("\u{1f}") { - let prev = prev_prefix.take().unwrap_or(""); - if prev.is_empty() && is_flag_prefix(curr) { - prev_prefix = Some(curr); - continue; - } - - let (prefix, rustc_flag) = handle_flag_prefix(prev, curr); - codegen_flags.set_rustc_flag(prefix, rustc_flag)?; - } - - Ok(codegen_flags) - } - - fn set_rustc_flag(&mut self, prefix: &str, flag: &'this str) -> Result<(), Error> { - // Convert a textual representation of a bool-like rustc flag argument into an actual bool - fn arg_to_bool(arg: impl AsRef) -> Option { - match arg.as_ref() { - "y" | "yes" | "on" | "true" => Some(true), - "n" | "no" | "off" | "false" => Some(false), - _ => None, - } - } - - fn arg_to_u32(arg: impl AsRef) -> Option { - arg.as_ref().parse().ok() - } - - let (flag, value) = if let Some((flag, value)) = flag.split_once('=') { - (flag, Some(value)) - } else { - (flag, None) - }; - let flag = if prefix.is_empty() { - Cow::Borrowed(flag) - } else { - Cow::Owned(format!("{prefix}{flag}")) - }; - let flag = flag.as_ref(); - - fn flag_not_empty_generic( - flag: &str, - flag_value: Option, - ) -> Result, Error> { - if let Some(flag_value) = flag_value { - Ok(Some(flag_value)) - } else { - Err(Error::new( - ErrorKind::InvalidFlag, - format!("{flag} must have a value"), - )) - } - } - let flag_not_empty = |flag_value| flag_not_empty_generic(flag, flag_value); - - match flag { - // https://doc.rust-lang.org/rustc/codegen-options/index.html#code-model - "-Ccode-model" => { - self.code_model = flag_not_empty(value)?; - } - // https://doc.rust-lang.org/rustc/codegen-options/index.html#no-vectorize-loops - "-Cno-vectorize-loops" => self.no_vectorize_loops = true, - // https://doc.rust-lang.org/rustc/codegen-options/index.html#no-vectorize-slp - "-Cno-vectorize-slp" => self.no_vectorize_slp = true, - // https://doc.rust-lang.org/rustc/codegen-options/index.html#profile-generate - "-Cprofile-generate" => { - self.profile_generate = flag_not_empty(value)?; - } - // https://doc.rust-lang.org/rustc/codegen-options/index.html#profile-use - "-Cprofile-use" => { - self.profile_use = flag_not_empty(value)?; - } - // https://doc.rust-lang.org/rustc/codegen-options/index.html#control-flow-guard - "-Ccontrol-flow-guard" => self.control_flow_guard = value.or(Some("true")), - // https://doc.rust-lang.org/rustc/codegen-options/index.html#lto - // - // This variable is currently unused, we just keep it in case we need it in future - "-Clto" => self.lto = value.or(Some("true")), - // https://doc.rust-lang.org/rustc/linker-plugin-lto.html - "-Clinker-plugin-lto" => self.linker_plugin_lto = Some(true), - // https://doc.rust-lang.org/rustc/codegen-options/index.html#relocation-model - "-Crelocation-model" => { - self.relocation_model = flag_not_empty(value)?; - } - // https://doc.rust-lang.org/rustc/codegen-options/index.html#embed-bitcode - "-Cembed-bitcode" => self.embed_bitcode = value.map_or(Some(true), arg_to_bool), - // https://doc.rust-lang.org/rustc/codegen-options/index.html#force-frame-pointers - "-Cforce-frame-pointers" => { - self.force_frame_pointers = value.map_or(Some(true), arg_to_bool) - } - // https://doc.rust-lang.org/rustc/codegen-options/index.html#no-redzone - "-Cno-redzone" => self.no_redzone = value.map_or(Some(true), arg_to_bool), - // https://doc.rust-lang.org/rustc/codegen-options/index.html#soft-float - // Note: This flag is now deprecated in rustc. - "-Csoft-float" => self.soft_float = value.map_or(Some(true), arg_to_bool), - // https://doc.rust-lang.org/beta/unstable-book/compiler-flags/branch-protection.html - // FIXME: Drop the -Z variant and update the doc link once the option is stabilised - "-Zbranch-protection" | "-Cbranch-protection" => { - self.branch_protection = flag_not_empty(value)?; - } - // https://doc.rust-lang.org/beta/rustc/codegen-options/index.html#dwarf-version - "-Cdwarf-version" => { - self.dwarf_version = flag_not_empty_generic(flag, value.and_then(arg_to_u32))?; - } - // https://github.com/rust-lang/rust/issues/114903 - // FIXME: Drop the -Z variant and update the doc link once the option is stabilized - "-Zstack-protector" | "-Cstack-protector" => { - self.stack_protector = flag_not_empty(value)?; - } - _ => {} - } - Ok(()) - } - - // Rust and clang/cc don't agree on what equivalent flags should look like. - pub(crate) fn cc_flags(&self, build: &Build, tool: &mut Tool, target: &TargetInfo<'_>) { - let family = tool.family; - // Push `flag` to `flags` if it is supported by the currently used CC - let mut push_if_supported = |flag: OsString| { - if build - .is_flag_supported_inner(&flag, tool, target) - .unwrap_or(false) - { - tool.args.push(flag); - } else { - build.cargo_output.print_warning(&format!( - "Inherited flag {flag:?} is not supported by the currently used CC" - )); - } - }; - - let clang_or_gnu = - matches!(family, ToolFamily::Clang { .. }) || matches!(family, ToolFamily::Gnu); - - // Flags shared between clang and gnu - if clang_or_gnu { - // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mbranch-protection - // https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html#index-mbranch-protection (Aarch64) - // https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html#index-mbranch-protection-1 (ARM) - // https://developer.arm.com/documentation/101754/0619/armclang-Reference/armclang-Command-line-Options/-mbranch-protection - if let Some(value) = self.branch_protection { - push_if_supported( - format!("-mbranch-protection={}", value.replace(",", "+")).into(), - ); - } - // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mcmodel - // https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html (several archs, search for `-mcmodel=`). - // FIXME(madsmtm): Parse the model, to make sure we pass the correct value (depending on arch). - if let Some(value) = self.code_model { - push_if_supported(format!("-mcmodel={value}").into()); - } - // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fno-vectorize - // https://gcc.gnu.org/onlinedocs/gnat_ugn/Vectorization-of-loops.html - if self.no_vectorize_loops { - push_if_supported("-fno-vectorize".into()); - } - // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fno-slp-vectorize - // https://gcc.gnu.org/onlinedocs/gnat_ugn/Vectorization-of-loops.html - if self.no_vectorize_slp { - push_if_supported("-fno-slp-vectorize".into()); - } - if let Some(value) = self.relocation_model { - let cc_flag = match value { - // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fPIC - // https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#index-fPIC - "pic" => Some("-fPIC"), - // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fPIE - // https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#index-fPIE - "pie" => Some("-fPIE"), - // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mdynamic-no-pic - // https://gcc.gnu.org/onlinedocs/gcc/RS_002f6000-and-PowerPC-Options.html#index-mdynamic-no-pic - "dynamic-no-pic" => Some("-mdynamic-no-pic"), - _ => None, - }; - if let Some(cc_flag) = cc_flag { - push_if_supported(cc_flag.into()); - } - } - // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fno-omit-frame-pointer - // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fomit-frame-pointer - // https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fomit-frame-pointer - if let Some(value) = self.force_frame_pointers { - let cc_flag = if value { - "-fno-omit-frame-pointer" - } else { - "-fomit-frame-pointer" - }; - push_if_supported(cc_flag.into()); - } - // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mno-red-zone - // https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html#index-mno-red-zone - // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mred-zone - // https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html#index-mred-zone - if let Some(value) = self.no_redzone { - let cc_flag = if value { "-mno-red-zone" } else { "-mred-zone" }; - push_if_supported(cc_flag.into()); - } - // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-msoft-float - // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mhard-float - // https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html (several archs, search for `-msoft-float`). - // https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html (several archs, search for `-mhard-float`). - if let Some(value) = self.soft_float { - let cc_flag = if value { - "-msoft-float" - } else { - // Do not use -mno-soft-float, that's basically just an alias for -mno-implicit-float. - "-mhard-float" - }; - push_if_supported(cc_flag.into()); - } - // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-gdwarf-2 - // https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html#index-gdwarf - if let Some(value) = self.dwarf_version { - push_if_supported(format!("-gdwarf-{value}").into()); - } - // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fstack-protector - // https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html#index-fstack-protector - if let Some(value) = self.stack_protector { - // don't need to propagate stack-protector on MSVC since /GS is already the default - // https://learn.microsoft.com/en-us/cpp/build/reference/gs-buffer-security-check?view=msvc-170 - // - // Do NOT `stack-protector=none` since it weakens security for C code, - // and `-Zstack-protector=basic` is deprecated and will be removed soon. - let cc_flag = match value { - "strong" => Some("-fstack-protector-strong"), - "all" => Some("-fstack-protector-all"), - _ => None, - }; - if let Some(cc_flag) = cc_flag { - push_if_supported(cc_flag.into()); - } - } - } - - // Compiler-exclusive flags - match family { - ToolFamily::Clang { .. } => { - // GNU and Clang compilers both support the same PGO flags, but they use different libraries and - // different formats for the profile files which are not compatible. - // clang and rustc both internally use llvm, so we want to inherit the PGO flags only for clang. - // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fprofile-generate - if let Some(value) = self.profile_generate { - push_if_supported(format!("-fprofile-generate={value}").into()); - } - // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fprofile-use - if let Some(value) = self.profile_use { - push_if_supported(format!("-fprofile-use={value}").into()); - } - - // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fembed-bitcode - if let Some(value) = self.embed_bitcode { - let cc_val = if value { "all" } else { "off" }; - push_if_supported(format!("-fembed-bitcode={cc_val}").into()); - } - - // https://doc.rust-lang.org/rustc/linker-plugin-lto.html - if self.linker_plugin_lto.unwrap_or(false) { - // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-flto - // In order to use linker-plugin-lto to achieve cross-lang lto, cc has to use thin LTO - // to compile the c/c++ libraries because llvm linker plugin/lld uses thin LTO by default. - // And for thin LTO in linker plugin to work, the archive also has to be compiled using thin LTO, - // since thin LTO generates extra information that fat LTO does not generate that - // is required for thin LTO process. - push_if_supported("-flto=thin".into()); - } - // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mguard - if let Some(value) = self.control_flow_guard { - let cc_val = match value { - "y" | "yes" | "on" | "true" | "checks" => Some("cf"), - "nochecks" => Some("cf-nochecks"), - "n" | "no" | "off" | "false" => Some("none"), - _ => None, - }; - if let Some(cc_val) = cc_val { - push_if_supported(format!("-mguard={cc_val}").into()); - } - } - } - ToolFamily::Gnu => {} - ToolFamily::Msvc { .. } => { - // https://learn.microsoft.com/en-us/cpp/build/reference/guard-enable-control-flow-guard - if let Some(value) = self.control_flow_guard { - let cc_val = match value { - "y" | "yes" | "on" | "true" | "checks" => Some("cf"), - "n" | "no" | "off" | "false" => Some("cf-"), - _ => None, - }; - if let Some(cc_val) = cc_val { - push_if_supported(format!("/guard:{cc_val}").into()); - } - } - // https://learn.microsoft.com/en-us/cpp/build/reference/oy-frame-pointer-omission - if let Some(value) = self.force_frame_pointers { - // Flag is unsupported on 64-bit arches - if !target.arch.contains("64") { - let cc_flag = if value { "/Oy-" } else { "/Oy" }; - push_if_supported(cc_flag.into()); - } - } - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[track_caller] - fn check(env: &str, expected: &RustcCodegenFlags) { - let actual = RustcCodegenFlags::parse(env).unwrap(); - assert_eq!(actual, *expected); - } - - #[test] - fn codegen_type() { - let expected = RustcCodegenFlags { - code_model: Some("tiny"), - ..RustcCodegenFlags::default() - }; - check("-Ccode-model=tiny", &expected); - check("-C\u{1f}code-model=tiny", &expected); - check("--codegen\u{1f}code-model=tiny", &expected); - check("--codegen=code-model=tiny", &expected); - } - - #[test] - fn precedence() { - check( - "-ccode-model=tiny\u{1f}-Ccode-model=small", - &RustcCodegenFlags { - code_model: Some("small"), - ..RustcCodegenFlags::default() - }, - ); - } - - #[test] - fn two_valid_prefixes() { - let expected = RustcCodegenFlags::default(); - check("-L\u{1f}-Clto", &expected); - } - - #[test] - fn stack_protector() { - let expected = RustcCodegenFlags { - stack_protector: Some("strong"), - ..RustcCodegenFlags::default() - }; - check("-Zstack-protector=strong", &expected); - check("-Cstack-protector=strong", &expected); - } - - #[test] - fn three_valid_prefixes() { - let expected = RustcCodegenFlags { - lto: Some("true"), - ..RustcCodegenFlags::default() - }; - check("-L\u{1f}-L\u{1f}-Clto", &expected); - } - - #[test] - fn all_rustc_flags() { - // Throw all possible flags at the parser to catch false positives - let flags = [ - // Set all the flags we recognise first - "-Ccode-model=tiny", - "-Ccontrol-flow-guard=yes", - "-Cembed-bitcode=no", - "-Cforce-frame-pointers=yes", - "-Clto=false", - "-Clink-dead-code=yes", - "-Cno-redzone=yes", - "-Cno-vectorize-loops", - "-Cno-vectorize-slp", - "-Cprofile-generate=fooprofile", - "-Cprofile-use=fooprofile", - "-Crelocation-model=pic", - "-Csoft-float=yes", - "-Zbranch-protection=bti,pac-ret,leaf", - "-Cdwarf-version=5", - "-Zstack-protector=strong", - // Set flags we don't recognise but rustc supports next - // rustc flags - "--cfg", - "a", - "--check-cfg 'cfg(verbose)", - "-L", - "/usr/lib/foo", - "-l", - "static:+whole-archive=mylib", - "--crate-type=dylib", - "--crate-name=foo", - "--edition=2021", - "--emit=asm", - "--print=crate-name", - "-g", - "-O", - "-o", - "foooutput", - "--out-dir", - "foooutdir", - "--target", - "aarch64-unknown-linux-gnu", - "-W", - "missing-docs", - "-D", - "unused-variables", - "--force-warn", - "dead-code", - "-A", - "unused", - "-F", - "unused", - "--cap-lints", - "warn", - "--version", - "--verbose", - "-v", - "--extern", - "foocrate", - "--sysroot", - "fooroot", - "--error-format", - "human", - "--color", - "auto", - "--diagnostic-width", - "80", - "--remap-path-prefix", - "foo=bar", - "--json=artifact", - // Codegen flags - "-Car", - "-Ccodegen-units=1", - "-Ccollapse-macro-debuginfo=yes", - "-Cdebug-assertions=yes", - "-Cdebuginfo=1", - "-Cdefault-linker-libraries=yes", - "-Cdlltool=foo", - "-Cextra-filename=foo", - "-Cforce-unwind-tables=yes", - "-Cincremental=foodir", - "-Cinline-threshold=6", - "-Cinstrument-coverage", - "-Clink-arg=-foo", - "-Clink-args=-foo", - "-Clink-self-contained=yes", - "-Clinker=lld", - "-Clinker-flavor=ld.lld", - "-Clinker-plugin-lto=/path", - "-Cllvm-args=foo", - "-Cmetadata=foo", - "-Cno-prepopulate-passes", - "-Cno-stack-check", - "-Copt-level=3", - "-Coverflow-checks=yes", - "-Cpanic=abort", - "-Cpasses=foopass", - "-Cprefer-dynamic=yes", - "-Crelro-level=partial", - "-Cremark=all", - "-Crpath=yes", - "-Csave-temps=yes", - "-Csplit-debuginfo=packed", - "-Cstrip=symbols", - "-Csymbol-mangling-version=v0", - "-Ctarget-cpu=native", - "-Ctarget-feature=+sve", - // Unstable options - "-Ztune-cpu=machine", - ]; - check( - &flags.join("\u{1f}"), - &RustcCodegenFlags { - code_model: Some("tiny"), - control_flow_guard: Some("yes"), - embed_bitcode: Some(false), - force_frame_pointers: Some(true), - lto: Some("false"), - no_redzone: Some(true), - no_vectorize_loops: true, - no_vectorize_slp: true, - profile_generate: Some("fooprofile"), - profile_use: Some("fooprofile"), - relocation_model: Some("pic"), - soft_float: Some(true), - branch_protection: Some("bti,pac-ret,leaf"), - dwarf_version: Some(5), - stack_protector: Some("strong"), - linker_plugin_lto: Some(true), - }, - ); - } -} diff --git a/cc-1.2.63/src/lib.rs b/cc-1.2.63/src/lib.rs deleted file mode 100644 index 5afd420de3..0000000000 --- a/cc-1.2.63/src/lib.rs +++ /dev/null @@ -1,4505 +0,0 @@ -//! A library for [Cargo build scripts](https://doc.rust-lang.org/cargo/reference/build-scripts.html) -//! to compile a set of C/C++/assembly/CUDA files into a static archive for Cargo -//! to link into the crate being built. This crate does not compile code itself; -//! it calls out to the default compiler for the platform. This crate will -//! automatically detect situations such as cross compilation and -//! [various environment variables](#external-configuration-via-environment-variables) and will build code appropriately. -//! -//! # Example -//! -//! First, you'll want to both add a build script for your crate (`build.rs`) and -//! also add this crate to your `Cargo.toml` via: -//! -//! ```toml -//! [build-dependencies] -//! cc = "1.0" -//! ``` -//! -//! Next up, you'll want to write a build script like so: -//! -//! ```rust,no_run -//! // build.rs -//! cc::Build::new() -//! .file("foo.c") -//! .file("bar.c") -//! .compile("foo"); -//! ``` -//! -//! And that's it! Running `cargo build` should take care of the rest and your Rust -//! application will now have the C files `foo.c` and `bar.c` compiled into a file -//! named `libfoo.a`. If the C files contain -//! -//! ```c -//! void foo_function(void) { ... } -//! ``` -//! -//! and -//! -//! ```c -//! int32_t bar_function(int32_t x) { ... } -//! ``` -//! -//! you can call them from Rust by declaring them in -//! your Rust code like so: -//! -//! ```rust,no_run -//! extern "C" { -//! fn foo_function(); -//! fn bar_function(x: i32) -> i32; -//! } -//! -//! pub fn call() { -//! unsafe { -//! foo_function(); -//! bar_function(42); -//! } -//! } -//! -//! fn main() { -//! call(); -//! } -//! ``` -//! -//! See [the Rustonomicon](https://doc.rust-lang.org/nomicon/ffi.html) for more details. -//! -//! # External configuration via environment variables -//! -//! To control the programs and flags used for building, the builder can set a -//! number of different environment variables. -//! -//! * `CFLAGS` - a series of space separated flags passed to compilers. Note that -//! individual flags cannot currently contain spaces, so doing -//! something like: `-L=foo\ bar` is not possible. -//! * `CC` - the actual C compiler used. Note that this supports passing a known -//! wrapper via `sccache cc`. This compiler must understand the `-c` flag. For -//! certain `TARGET`s, it also is assumed to know about other flags (most -//! common is `-fPIC`). -//! ccache, distcc, sccache, icecc, cachepot and buildcache are supported, -//! for sccache, simply set `CC` to `sccache cc`. -//! For other custom `CC` wrapper, just set `CC_KNOWN_WRAPPER_CUSTOM` -//! to the custom wrapper used in `CC`. -//! * `AR` - the `ar` (archiver) executable to use to build the static library. -//! * `CRATE_CC_NO_DEFAULTS` - the default compiler flags may cause conflicts in -//! some cross compiling scenarios. Setting this variable -//! will disable the generation of default compiler -//! flags. -//! * `CC_ENABLE_DEBUG_OUTPUT` - if set, compiler command invocations and exit codes will -//! be logged to stdout. This is useful for debugging build script issues, but can be -//! overly verbose for normal use. -//! * `CC_SHELL_ESCAPED_FLAGS` - if set, `*FLAGS` will be parsed as if they were shell -//! arguments (similar to `make` and `cmake`) rather than splitting them on each space. -//! For example, with `CFLAGS='a "b c"'`, the compiler will be invoked with 2 arguments - -//! `a` and `b c` - rather than 3: `a`, `"b` and `c"`. -//! * `CXX...` - see [C++ Support](#c-support). -//! * `CC_FORCE_DISABLE` - If set, `cc` will never run any [`Command`]s, and methods that -//! would return an [`Error`]. This is intended for use by third-party build systems -//! which want to be absolutely sure that they are in control of building all -//! dependencies. Note that operations that return [`Tool`]s such as -//! [`Build::get_compiler`] may produce less accurate results as in some cases `cc` runs -//! commands in order to locate compilers. Additionally, this does nothing to prevent -//! users from running [`Tool::to_command`] and executing the [`Command`] themselves. -//! * `RUSTC_WRAPPER` - If set, the specified command will be prefixed to the compiler -//! command. This is useful for projects that want to use -//! [sccache](https://github.com/mozilla/sccache), -//! [buildcache](https://gitlab.com/bits-n-bites/buildcache), or -//! [cachepot](https://github.com/paritytech/cachepot). -//! -//! Furthermore, projects using this crate may specify custom environment variables -//! to be inspected, for example via the `Build::try_flags_from_environment` -//! function. Consult the project’s own documentation or its use of the `cc` crate -//! for any additional variables it may use. -//! -//! Each of these variables can also be supplied with certain prefixes and suffixes, -//! in the following prioritized order: -//! -//! 1. `_` - for example, `CC_x86_64-unknown-linux-gnu` or `CC_thumbv8m.main-none-eabi` -//! 2. `_` - for example, `CC_x86_64_unknown_linux_gnu` or `CC_thumbv8m_main_none_eabi` (both periods and underscores are replaced) -//! 3. `_` - for example, `HOST_CC` or `TARGET_CFLAGS` -//! 4. `` - a plain `CC`, `AR` as above. -//! -//! If none of these variables exist, cc-rs uses built-in defaults. -//! -//! In addition to the above optional environment variables, `cc-rs` has some -//! functions with hard requirements on some variables supplied by [cargo's -//! build-script driver][cargo] that it has the `TARGET`, `OUT_DIR`, `OPT_LEVEL`, -//! and `HOST` variables. -//! -//! [cargo]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#inputs-to-the-build-script -//! -//! # Optional features -//! -//! ## Parallel -//! -//! Currently cc-rs supports parallel compilation (think `make -jN`) but this -//! feature is turned off by default. To enable cc-rs to compile C/C++ in parallel, -//! you can change your dependency to: -//! -//! ```toml -//! [build-dependencies] -//! cc = { version = "1.0", features = ["parallel"] } -//! ``` -//! -//! By default cc-rs will limit parallelism to `$NUM_JOBS`, or if not present it -//! will limit it to the number of cpus on the machine. If you are using cargo, -//! use `-jN` option of `build`, `test` and `run` commands as `$NUM_JOBS` -//! is supplied by cargo. -//! -//! # Compile-time Requirements -//! -//! To work properly this crate needs access to a C compiler when the build script -//! is being run. This crate does not ship a C compiler with it. The compiler -//! required varies per platform, but there are three broad categories: -//! -//! * Unix platforms require `cc` to be the C compiler. This can be found by -//! installing cc/clang on Linux distributions and Xcode on macOS, for example. -//! * Windows platforms targeting MSVC (e.g. your target name ends in `-msvc`) -//! require Visual Studio to be installed. `cc-rs` attempts to locate it, and -//! if it fails, `cl.exe` is expected to be available in `PATH`. This can be -//! set up by running the appropriate developer tools shell. -//! * When using `prefer_clang_cl_over_msvc`, make sure that the `C++ Clang compiler for Windows` component -//! is installed through the Visual Studio Installer, so that `cc-rs` can find `clang-cl.exe`. -//! * Windows platforms targeting MinGW (e.g. your target name ends in `-gnu`) -//! require `cc` to be available in `PATH`. We recommend the -//! [MinGW-w64](https://www.mingw-w64.org/) distribution. -//! You may also acquire it via -//! [MSYS2](https://www.msys2.org/), as explained [here][msys2-help]. Make sure -//! to install the appropriate architecture corresponding to your installation of -//! rustc. GCC from older [MinGW](http://www.mingw.org/) project is compatible -//! only with 32-bit rust compiler. -//! -//! [msys2-help]: https://github.com/rust-lang/rust/blob/master/INSTALL.md#building-on-windows -//! -//! # C++ support -//! -//! `cc-rs` supports C++ libraries compilation by using the `cpp` method on -//! `Build`: -//! -//! ```rust,no_run -//! cc::Build::new() -//! .cpp(true) // Switch to C++ library compilation. -//! .file("foo.cpp") -//! .compile("foo"); -//! ``` -//! -//! For C++ libraries, the `CXX` and `CXXFLAGS` environment variables are used instead of `CC` and `CFLAGS`. -//! -//! The C++ standard library may be linked to the crate target. By default it's `libc++` for macOS, FreeBSD, and OpenBSD, `libc++_shared` for Android, nothing for MSVC, and `libstdc++` for anything else. It can be changed in one of two ways: -//! -//! 1. by using the `cpp_link_stdlib` method on `Build`: -//! ```rust,no_run -//! cc::Build::new() -//! .cpp(true) -//! .file("foo.cpp") -//! .cpp_link_stdlib("stdc++") // use libstdc++ -//! .compile("foo"); -//! ``` -//! 2. by setting the `CXXSTDLIB` environment variable. -//! -//! In particular, for Android you may want to [use `c++_static` if you have at most one shared library](https://developer.android.com/ndk/guides/cpp-support). -//! -//! Remember that C++ does name mangling so `extern "C"` might be required to enable Rust linker to find your functions. -//! -//! # CUDA C++ support -//! -//! `cc-rs` also supports compiling CUDA C++ libraries by using the `cuda` method -//! on `Build`: -//! -//! ```rust,no_run -//! cc::Build::new() -//! // Switch to CUDA C++ library compilation using NVCC. -//! .cuda(true) -//! .cudart("static") -//! // Generate code for Maxwell (GTX 970, 980, 980 Ti, Titan X). -//! .flag("-gencode").flag("arch=compute_52,code=sm_52") -//! // Generate code for Maxwell (Jetson TX1). -//! .flag("-gencode").flag("arch=compute_53,code=sm_53") -//! // Generate code for Pascal (GTX 1070, 1080, 1080 Ti, Titan Xp). -//! .flag("-gencode").flag("arch=compute_61,code=sm_61") -//! // Generate code for Pascal (Tesla P100). -//! .flag("-gencode").flag("arch=compute_60,code=sm_60") -//! // Generate code for Pascal (Jetson TX2). -//! .flag("-gencode").flag("arch=compute_62,code=sm_62") -//! // Generate code in parallel -//! .flag("-t0") -//! .file("bar.cu") -//! .compile("bar"); -//! ``` -//! -//! # Speed up compilation with sccache -//! -//! `cc-rs` does not handle incremental compilation like `make` or `ninja`. It -//! always compiles the all sources, no matter if they have changed or not. -//! This would be time-consuming in large projects. To save compilation time, -//! you can use [sccache](https://github.com/mozilla/sccache) by setting -//! environment variable `RUSTC_WRAPPER=sccache`, which will use cached `.o` -//! files if the sources are unchanged. - -#![doc(html_root_url = "https://docs.rs/cc/1.0")] -#![deny(warnings)] -#![deny(missing_docs)] -#![deny(clippy::disallowed_methods)] -#![warn(clippy::doc_markdown)] - -use std::borrow::Cow; -use std::collections::HashMap; -use std::env; -use std::ffi::{OsStr, OsString}; -use std::fmt::{self, Display}; -use std::fs; -use std::io::{self, Write}; -use std::path::{Component, Path, PathBuf}; -use std::process::{Command, Stdio}; -use std::sync::{Arc, RwLock}; - -use shlex::Shlex; - -#[cfg(feature = "parallel")] -mod parallel; - -mod target; -use self::target::*; - -/// A helper module to looking for windows-specific tools: -/// 1. On Windows host, probe the Windows Registry if needed; -/// 2. On non-Windows host, check specified environment variables. -pub mod windows_registry { - // Regardless of whether this should be in this crate's public API, - // it has been since 2015, so don't break it. - - /// Attempts to find a tool within an MSVC installation using the Windows - /// registry as a point to search from. - /// - /// The `arch_or_target` argument is the architecture or the Rust target name - /// that the tool should work for (e.g. compile or link for). The supported - /// architecture names are: - /// - `"x64"` or `"x86_64"` - /// - `"arm64"` or `"aarch64"` - /// - `"arm64ec"` - /// - `"x86"`, `"i586"` or `"i686"` - /// - `"arm"` or `"thumbv7a"` - /// - /// The `tool` argument is the tool to find. Supported tools include: - /// - MSVC tools: `cl.exe`, `link.exe`, `lib.exe`, etc. - /// - `MSBuild`: `msbuild.exe` - /// - Visual Studio IDE: `devenv.exe` - /// - Clang/LLVM tools: `clang.exe`, `clang++.exe`, `clang-*.exe`, `llvm-*.exe`, `lld.exe`, etc. - /// - /// This function will return `None` if the tool could not be found, or it will - /// return `Some(cmd)` which represents a command that's ready to execute the - /// tool with the appropriate environment variables set. - /// - /// Note that this function always returns `None` for non-MSVC targets (if a - /// full target name was specified). - pub fn find(arch_or_target: &str, tool: &str) -> Option { - ::find_msvc_tools::find(arch_or_target, tool) - } - - /// A version of Visual Studio - #[derive(Debug, PartialEq, Eq, Copy, Clone)] - #[non_exhaustive] - pub enum VsVers { - /// Visual Studio 12 (2013) - #[deprecated = "Visual Studio 12 is no longer supported. cc will never return this value."] - Vs12, - /// Visual Studio 14 (2015) - Vs14, - /// Visual Studio 15 (2017) - Vs15, - /// Visual Studio 16 (2019) - Vs16, - /// Visual Studio 17 (2022) - Vs17, - /// Visual Studio 18 (2026) - Vs18, - } - - /// Find the most recent installed version of Visual Studio - /// - /// This is used by the cmake crate to figure out the correct - /// generator. - pub fn find_vs_version() -> Result { - ::find_msvc_tools::find_vs_version().map(|vers| match vers { - #[allow(deprecated)] - ::find_msvc_tools::VsVers::Vs12 => VsVers::Vs12, - ::find_msvc_tools::VsVers::Vs14 => VsVers::Vs14, - ::find_msvc_tools::VsVers::Vs15 => VsVers::Vs15, - ::find_msvc_tools::VsVers::Vs16 => VsVers::Vs16, - ::find_msvc_tools::VsVers::Vs17 => VsVers::Vs17, - ::find_msvc_tools::VsVers::Vs18 => VsVers::Vs18, - _ => unreachable!("unknown VS version"), - }) - } - - /// Similar to the `find` function above, this function will attempt the same - /// operation (finding a MSVC tool in a local install) but instead returns a - /// [`Tool`](crate::Tool) which may be introspected. - pub fn find_tool(arch_or_target: &str, tool: &str) -> Option { - ::find_msvc_tools::find_tool(arch_or_target, tool).map(crate::Tool::from_find_msvc_tools) - } -} - -mod command_helpers; -use command_helpers::*; - -mod tool; -pub use tool::Tool; -use tool::{CompilerFamilyLookupCache, ToolFamily}; - -mod tempfile; - -mod utilities; -use utilities::*; - -mod flags; -use flags::*; - -#[derive(Debug, Eq, PartialEq, Hash)] -struct CompilerFlag { - compiler: Box, - flag: Box, -} - -#[derive(Debug, Default)] -struct BuildCache { - apple_sdk_root_cache: RwLock, Arc>>, - apple_versions_cache: RwLock, Arc>>, - cached_compiler_family: RwLock, - known_flag_support_status_cache: RwLock>, - target_info_parser: target::TargetInfoParser, -} - -/// A builder for compilation of a native library. -/// -/// A `Build` is the main type of the `cc` crate and is used to control all the -/// various configuration options and such of a compile. You'll find more -/// documentation on each method itself. -#[derive(Clone, Debug)] -pub struct Build { - include_directories: Vec>, - definitions: Vec<(Arc, Option>)>, - objects: Vec>, - flags: Vec>, - flags_supported: Vec>, - ar_flags: Vec>, - asm_flags: Vec>, - no_default_flags: bool, - files: Vec>, - cpp: bool, - cpp_link_stdlib: Option>>, - cpp_link_stdlib_static: bool, - cpp_set_stdlib: Option>, - cuda: bool, - cudart: Option>, - ccbin: bool, - std: Option>, - target: Option>, - /// The host compiler. - /// - /// Try to not access this directly, and instead prefer `cfg!(...)`. - host: Option>, - out_dir: Option>, - opt_level: Option>, - debug: Option>, - force_frame_pointer: Option, - env: Vec<(Arc, Arc)>, - compiler: Option>, - archiver: Option>, - ranlib: Option>, - cargo_output: CargoOutput, - link_lib_modifiers: Vec>, - pic: Option, - use_plt: Option, - static_crt: Option, - shared_flag: Option, - static_flag: Option, - warnings_into_errors: bool, - warnings: Option, - extra_warnings: Option, - emit_rerun_if_env_changed: bool, - shell_escaped_flags: Option, - build_cache: Arc, - inherit_rustflags: bool, - prefer_clang_cl_over_msvc: bool, -} - -/// Represents the types of errors that may occur while using cc-rs. -#[derive(Clone, Debug)] -enum ErrorKind { - /// Error occurred while performing I/O. - IOError, - /// Environment variable not found, with the var in question as extra info. - EnvVarNotFound, - /// Error occurred while using external tools (ie: invocation of compiler). - ToolExecError, - /// Error occurred due to missing external tools. - ToolNotFound, - /// One of the function arguments failed validation. - InvalidArgument, - /// No known macro is defined for the compiler when discovering tool family. - ToolFamilyMacroNotFound, - /// Invalid target. - InvalidTarget, - /// Unknown target. - UnknownTarget, - /// Invalid rustc flag. - InvalidFlag, - #[cfg(feature = "parallel")] - /// jobserver helpthread failure - JobserverHelpThreadError, - /// `cc` has been disabled by an environment variable. - Disabled, -} - -/// Represents an internal error that occurred, with an explanation. -#[derive(Clone, Debug)] -pub struct Error { - /// Describes the kind of error that occurred. - kind: ErrorKind, - /// More explanation of error that occurred. - message: Cow<'static, str>, -} - -impl Error { - fn new(kind: ErrorKind, message: impl Into>) -> Error { - Error { - kind, - message: message.into(), - } - } -} - -impl From for Error { - fn from(e: io::Error) -> Error { - Error::new(ErrorKind::IOError, format!("{e}")) - } -} - -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}: {}", self.kind, self.message) - } -} - -impl std::error::Error for Error {} - -/// Represents an object. -/// -/// This is a source file -> object file pair. -#[derive(Clone, Debug)] -struct Object { - src: PathBuf, - dst: PathBuf, -} - -impl Object { - /// Create a new source file -> object file pair. - fn new(src: PathBuf, dst: PathBuf) -> Object { - Object { src, dst } - } -} - -/// Configure the builder. -impl Build { - /// Construct a new instance of a blank set of configuration. - /// - /// This builder is finished with the [`compile`] function. - /// - /// [`compile`]: struct.Build.html#method.compile - pub fn new() -> Build { - Build { - include_directories: Vec::new(), - definitions: Vec::new(), - objects: Vec::new(), - flags: Vec::new(), - flags_supported: Vec::new(), - ar_flags: Vec::new(), - asm_flags: Vec::new(), - no_default_flags: false, - files: Vec::new(), - shared_flag: None, - static_flag: None, - cpp: false, - cpp_link_stdlib: None, - cpp_link_stdlib_static: false, - cpp_set_stdlib: None, - cuda: false, - cudart: None, - ccbin: true, - std: None, - target: None, - host: None, - out_dir: None, - opt_level: None, - debug: None, - force_frame_pointer: None, - env: Vec::new(), - compiler: None, - archiver: None, - ranlib: None, - cargo_output: CargoOutput::new(), - link_lib_modifiers: Vec::new(), - pic: None, - use_plt: None, - static_crt: None, - warnings: None, - extra_warnings: None, - warnings_into_errors: false, - emit_rerun_if_env_changed: true, - shell_escaped_flags: None, - build_cache: Arc::default(), - inherit_rustflags: true, - prefer_clang_cl_over_msvc: false, - } - } - - /// Add a directory to the `-I` or include path for headers - /// - /// # Example - /// - /// ```no_run - /// use std::path::Path; - /// - /// let library_path = Path::new("/path/to/library"); - /// - /// cc::Build::new() - /// .file("src/foo.c") - /// .include(library_path) - /// .include("src") - /// .compile("foo"); - /// ``` - pub fn include>(&mut self, dir: P) -> &mut Build { - self.include_directories.push(dir.as_ref().into()); - self - } - - /// Add multiple directories to the `-I` include path. - /// - /// # Example - /// - /// ```no_run - /// # use std::path::Path; - /// # let condition = true; - /// # - /// let mut extra_dir = None; - /// if condition { - /// extra_dir = Some(Path::new("/path/to")); - /// } - /// - /// cc::Build::new() - /// .file("src/foo.c") - /// .includes(extra_dir) - /// .compile("foo"); - /// ``` - pub fn includes

(&mut self, dirs: P) -> &mut Build - where - P: IntoIterator, - P::Item: AsRef, - { - for dir in dirs { - self.include(dir); - } - self - } - - /// Specify a `-D` variable with an optional value. - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .define("FOO", "BAR") - /// .define("BAZ", None) - /// .compile("foo"); - /// ``` - pub fn define<'a, V: Into>>(&mut self, var: &str, val: V) -> &mut Build { - self.definitions - .push((var.into(), val.into().map(Into::into))); - self - } - - /// Add an arbitrary object file to link in - pub fn object>(&mut self, obj: P) -> &mut Build { - self.objects.push(obj.as_ref().into()); - self - } - - /// Add arbitrary object files to link in - pub fn objects

(&mut self, objs: P) -> &mut Build - where - P: IntoIterator, - P::Item: AsRef, - { - for obj in objs { - self.object(obj); - } - self - } - - /// Add an arbitrary flag to the invocation of the compiler - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .flag("-ffunction-sections") - /// .compile("foo"); - /// ``` - pub fn flag(&mut self, flag: impl AsRef) -> &mut Build { - self.flags.push(flag.as_ref().into()); - self - } - - /// Add multiple flags to the invocation of the compiler. - /// This is equivalent to calling [`flag`](Self::flag) for each item in the iterator. - /// - /// # Example - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .flags(["-Wall", "-Wextra"]) - /// .compile("foo"); - /// ``` - pub fn flags(&mut self, flags: Iter) -> &mut Build - where - Iter: IntoIterator, - Iter::Item: AsRef, - { - for flag in flags { - self.flag(flag); - } - self - } - - /// Removes a compiler flag that was added by [`Build::flag`]. - /// - /// Will not remove flags added by other means (default flags, - /// flags from env, and so on). - /// - /// # Example - /// ``` - /// cc::Build::new() - /// .file("src/foo.c") - /// .flag("unwanted_flag") - /// .remove_flag("unwanted_flag"); - /// ``` - pub fn remove_flag(&mut self, flag: &str) -> &mut Build { - self.flags.retain(|other_flag| &**other_flag != flag); - self - } - - /// Add a flag to the invocation of the ar - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .file("src/bar.c") - /// .ar_flag("/NODEFAULTLIB:libc.dll") - /// .compile("foo"); - /// ``` - pub fn ar_flag(&mut self, flag: impl AsRef) -> &mut Build { - self.ar_flags.push(flag.as_ref().into()); - self - } - - /// Add a flag that will only be used with assembly files. - /// - /// The flag will be applied to input files with either a `.s` or - /// `.asm` extension (case insensitive). - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .asm_flag("-Wa,-defsym,abc=1") - /// .file("src/foo.S") // The asm flag will be applied here - /// .file("src/bar.c") // The asm flag will not be applied here - /// .compile("foo"); - /// ``` - pub fn asm_flag(&mut self, flag: impl AsRef) -> &mut Build { - self.asm_flags.push(flag.as_ref().into()); - self - } - - /// Add an arbitrary flag to the invocation of the compiler if it supports it - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .flag_if_supported("-Wlogical-op") // only supported by GCC - /// .flag_if_supported("-Wunreachable-code") // only supported by clang - /// .compile("foo"); - /// ``` - pub fn flag_if_supported(&mut self, flag: impl AsRef) -> &mut Build { - self.flags_supported.push(flag.as_ref().into()); - self - } - - /// Add flags from the specified environment variable. - /// - /// Normally the `cc` crate will consult with the standard set of environment - /// variables (such as `CFLAGS` and `CXXFLAGS`) to construct the compiler invocation. Use of - /// this method provides additional levers for the end user to use when configuring the build - /// process. - /// - /// Just like the standard variables, this method will search for an environment variable with - /// appropriate target prefixes, when appropriate. - /// - /// # Examples - /// - /// This method is particularly beneficial in introducing the ability to specify crate-specific - /// flags. - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .try_flags_from_environment(concat!(env!("CARGO_PKG_NAME"), "_CFLAGS")) - /// .expect("the environment variable must be specified and UTF-8") - /// .compile("foo"); - /// ``` - /// - pub fn try_flags_from_environment(&mut self, environ_key: &str) -> Result<&mut Build, Error> { - let flags = self.envflags(environ_key)?.ok_or_else(|| { - Error::new( - ErrorKind::EnvVarNotFound, - format!("could not find environment variable {environ_key}"), - ) - })?; - self.flags.extend( - flags - .into_iter() - .map(|flag| Arc::from(OsString::from(flag).as_os_str())), - ); - Ok(self) - } - - /// Set the `-shared` flag. - /// - /// This will typically be ignored by the compiler when calling [`Self::compile()`] since it only - /// produces static libraries. - /// - /// # Example - /// - /// ```no_run - /// // This will create a library named "liblibfoo.so.a" - /// cc::Build::new() - /// .file("src/foo.c") - /// .shared_flag(true) - /// .compile("libfoo.so"); - /// ``` - #[deprecated = "cc only creates static libraries, setting this does nothing"] - pub fn shared_flag(&mut self, shared_flag: bool) -> &mut Build { - self.shared_flag = Some(shared_flag); - self - } - - /// Set the `-static` flag. - /// - /// This will typically be ignored by the compiler when calling [`Self::compile()`] since it only - /// produces static libraries. - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .shared_flag(true) - /// .static_flag(true) - /// .compile("foo"); - /// ``` - #[deprecated = "cc only creates static libraries, setting this does nothing"] - pub fn static_flag(&mut self, static_flag: bool) -> &mut Build { - self.static_flag = Some(static_flag); - self - } - - /// Disables the generation of default compiler flags. The default compiler - /// flags may cause conflicts in some cross compiling scenarios. - /// - /// Setting the `CRATE_CC_NO_DEFAULTS` environment variable has the same - /// effect as setting this to `true`. The presence of the environment - /// variable and the value of `no_default_flags` will be OR'd together. - pub fn no_default_flags(&mut self, no_default_flags: bool) -> &mut Build { - self.no_default_flags = no_default_flags; - self - } - - /// Add a file which will be compiled - pub fn file>(&mut self, p: P) -> &mut Build { - self.files.push(p.as_ref().into()); - self - } - - /// Add files which will be compiled - pub fn files

(&mut self, p: P) -> &mut Build - where - P: IntoIterator, - P::Item: AsRef, - { - for file in p.into_iter() { - self.file(file); - } - self - } - - /// Get the files which will be compiled - pub fn get_files(&self) -> impl Iterator { - self.files.iter().map(AsRef::as_ref) - } - - /// Set C++ support. - /// - /// The other `cpp_*` options will only become active if this is set to - /// `true`. - /// - /// The name of the C++ standard library to link is decided by: - /// 1. If [`cpp_link_stdlib`](Build::cpp_link_stdlib) is set, use its value. - /// 2. Else if the `CXXSTDLIB` environment variable is set, use its value. - /// 3. Else the default is `c++` for OS X and BSDs, `c++_shared` for Android, - /// `None` for MSVC and `stdc++` for anything else. - pub fn cpp(&mut self, cpp: bool) -> &mut Build { - self.cpp = cpp; - self - } - - /// Set CUDA C++ support. - /// - /// Enabling CUDA will invoke the CUDA compiler, NVCC. While NVCC accepts - /// the most common compiler flags, e.g. `-std=c++17`, some project-specific - /// flags might have to be prefixed with "-Xcompiler" flag, for example as - /// `.flag("-Xcompiler").flag("-fpermissive")`. See the documentation for - /// `nvcc`, the CUDA compiler driver, at - /// for more information. - /// - /// If enabled, this also implicitly enables C++ support. - pub fn cuda(&mut self, cuda: bool) -> &mut Build { - self.cuda = cuda; - if cuda { - self.cpp = true; - self.cudart = Some("static".into()); - } - self - } - - /// Link CUDA run-time. - /// - /// This option mimics the `--cudart` NVCC command-line option. Just like - /// the original it accepts `{none|shared|static}`, with default being - /// `static`. The method has to be invoked after `.cuda(true)`, or not - /// at all, if the default is right for the project. - pub fn cudart(&mut self, cudart: &str) -> &mut Build { - if self.cuda { - self.cudart = Some(cudart.into()); - } - self - } - - /// Set CUDA host compiler. - /// - /// By default, a `-ccbin` flag will be passed to NVCC to specify the - /// underlying host compiler. The value of `-ccbin` is the same as the - /// chosen C++ compiler. This is not always desired, because NVCC might - /// not support that compiler. In this case, you can remove the `-ccbin` - /// flag so that NVCC will choose the host compiler by itself. - pub fn ccbin(&mut self, ccbin: bool) -> &mut Build { - self.ccbin = ccbin; - self - } - - /// Specify the C or C++ language standard version. - /// - /// These values are common to modern versions of GCC, Clang and MSVC: - /// - `c11` for ISO/IEC 9899:2011 - /// - `c17` for ISO/IEC 9899:2018 - /// - `c++14` for ISO/IEC 14882:2014 - /// - `c++17` for ISO/IEC 14882:2017 - /// - `c++20` for ISO/IEC 14882:2020 - /// - /// Other values have less broad support, e.g. MSVC does not support `c++11` - /// (`c++14` is the minimum), `c89` (omit the flag instead) or `c99`. - /// - /// For compiling C++ code, you should also set `.cpp(true)`. - /// - /// The default is that no standard flag is passed to the compiler, so the - /// language version will be the compiler's default. - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/modern.cpp") - /// .cpp(true) - /// .std("c++17") - /// .compile("modern"); - /// ``` - pub fn std(&mut self, std: &str) -> &mut Build { - self.std = Some(std.into()); - self - } - - /// Set warnings into errors flag. - /// - /// Disabled by default. - /// - /// Warning: turning warnings into errors only make sense - /// if you are a developer of the crate using cc-rs. - /// Some warnings only appear on some architecture or - /// specific version of the compiler. Any user of this crate, - /// or any other crate depending on it, could fail during - /// compile time. - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .warnings_into_errors(true) - /// .compile("libfoo.a"); - /// ``` - pub fn warnings_into_errors(&mut self, warnings_into_errors: bool) -> &mut Build { - self.warnings_into_errors = warnings_into_errors; - self - } - - /// Set warnings flags. - /// - /// Adds some flags: - /// - "-Wall" for MSVC. - /// - "-Wall", "-Wextra" for GNU and Clang. - /// - /// Enabled by default. - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .warnings(false) - /// .compile("libfoo.a"); - /// ``` - pub fn warnings(&mut self, warnings: bool) -> &mut Build { - self.warnings = Some(warnings); - self.extra_warnings = Some(warnings); - self - } - - /// Set extra warnings flags. - /// - /// Adds some flags: - /// - nothing for MSVC. - /// - "-Wextra" for GNU and Clang. - /// - /// Enabled by default. - /// - /// # Example - /// - /// ```no_run - /// // Disables -Wextra, -Wall remains enabled: - /// cc::Build::new() - /// .file("src/foo.c") - /// .extra_warnings(false) - /// .compile("libfoo.a"); - /// ``` - pub fn extra_warnings(&mut self, warnings: bool) -> &mut Build { - self.extra_warnings = Some(warnings); - self - } - - /// Set the standard library to link against when compiling with C++ - /// support. - /// - /// If the `CXXSTDLIB` environment variable is set, its value will - /// override the default value, but not the value explicitly set by calling - /// this function. - /// - /// A value of `None` indicates that no automatic linking should happen, - /// otherwise cargo will link against the specified library. - /// - /// The given library name must not contain the `lib` prefix. - /// - /// Common values: - /// - `stdc++` for GNU - /// - `c++` for Clang - /// - `c++_shared` or `c++_static` for Android - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .shared_flag(true) - /// .cpp_link_stdlib("stdc++") - /// .compile("libfoo.so"); - /// ``` - pub fn cpp_link_stdlib<'a, V: Into>>( - &mut self, - cpp_link_stdlib: V, - ) -> &mut Build { - self.cpp_link_stdlib = Some(cpp_link_stdlib.into().map(Arc::from)); - self - } - - /// Force linker to statically link C++ stdlib. By default cc-rs will emit - /// rustc-link flag to link against system C++ stdlib (e.g. libstdc++.so, libc++.so) - /// Provide value of `true` if linking against system library is not desired - /// - /// Note that for `wasm32` target C++ stdlib will always be linked statically - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.cpp") - /// .cpp(true) - /// .cpp_link_stdlib("stdc++") - /// .cpp_link_stdlib_static(true) - /// .compile("foo"); - /// ``` - pub fn cpp_link_stdlib_static(&mut self, is_static: bool) -> &mut Build { - self.cpp_link_stdlib_static = is_static; - self - } - - /// Force the C++ compiler to use the specified standard library. - /// - /// Setting this option will automatically set `cpp_link_stdlib` to the same - /// value. - /// - /// The default value of this option is always `None`. - /// - /// This option has no effect when compiling for a Visual Studio based - /// target. - /// - /// This option sets the `-stdlib` flag, which is only supported by some - /// compilers (clang, icc) but not by others (gcc). The library will not - /// detect which compiler is used, as such it is the responsibility of the - /// caller to ensure that this option is only used in conjunction with a - /// compiler which supports the `-stdlib` flag. - /// - /// A value of `None` indicates that no specific C++ standard library should - /// be used, otherwise `-stdlib` is added to the compile invocation. - /// - /// The given library name must not contain the `lib` prefix. - /// - /// Common values: - /// - `stdc++` for GNU - /// - `c++` for Clang - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .cpp_set_stdlib("c++") - /// .compile("libfoo.a"); - /// ``` - pub fn cpp_set_stdlib<'a, V: Into>>( - &mut self, - cpp_set_stdlib: V, - ) -> &mut Build { - let cpp_set_stdlib = cpp_set_stdlib.into().map(Arc::from); - self.cpp_set_stdlib.clone_from(&cpp_set_stdlib); - self.cpp_link_stdlib = Some(cpp_set_stdlib); - self - } - - /// Configures the `rustc` target this configuration will be compiling - /// for. - /// - /// This will fail if using a target not in a pre-compiled list taken from - /// `rustc +nightly --print target-list`. The list will be updated - /// periodically. - /// - /// You should avoid setting this in build scripts, target information - /// will instead be retrieved from the environment variables `TARGET` and - /// `CARGO_CFG_TARGET_*` that Cargo sets. - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .target("aarch64-linux-android") - /// .compile("foo"); - /// ``` - pub fn target(&mut self, target: &str) -> &mut Build { - self.target = Some(target.into()); - self - } - - /// Configures the host assumed by this configuration. - /// - /// This option is automatically scraped from the `HOST` environment - /// variable by build scripts, so it's not required to call this function. - /// - /// # Example - /// - /// ```no_run - /// cc::Build::new() - /// .file("src/foo.c") - /// .host("arm-linux-gnueabihf") - /// .compile("foo"); - /// ``` - pub fn host(&mut self, host: &str) -> &mut Build { - self.host = Some(host.into()); - self - } - - /// Configures the optimization level of the generated object files. - /// - /// This option is automatically scraped from the `OPT_LEVEL` environment - /// variable by build scripts, so it's not required to call this function. - pub fn opt_level(&mut self, opt_level: u32) -> &mut Build { - self.opt_level = Some(opt_level.to_string().into()); - self - } - - /// Configures the optimization level of the generated object files. - /// - /// This option is automatically scraped from the `OPT_LEVEL` environment - /// variable by build scripts, so it's not required to call this function. - pub fn opt_level_str(&mut self, opt_level: &str) -> &mut Build { - self.opt_level = Some(opt_level.into()); - self - } - - /// Configures whether the compiler will emit debug information when - /// generating object files. - /// - /// This option is automatically scraped from the `DEBUG` environment - /// variable by build scripts, so it's not required to call this function. - pub fn debug(&mut self, debug: bool) -> &mut Build { - self.debug = Some(debug.to_string().into()); - self - } - - /// Configures whether the compiler will emit debug information when - /// generating object files. - /// - /// This should be one of the values accepted by Cargo's [`debug`][1] - /// profile setting, which cc-rs will try to map to the appropriate C - /// compiler flag. - /// - /// This option is automatically scraped from the `DEBUG` environment - /// variable by build scripts, so it's not required to call this function. - /// - /// [1]: https://doc.rust-lang.org/cargo/reference/profiles.html#debug - pub fn debug_str(&mut self, debug: &str) -> &mut Build { - self.debug = Some(debug.into()); - self - } - - /// Configures whether the compiler will emit instructions to store - /// frame pointers during codegen. - /// - /// This option is automatically enabled when debug information is emitted. - /// Otherwise the target platform compiler's default will be used. - /// You can use this option to force a specific setting. - pub fn force_frame_pointer(&mut self, force: bool) -> &mut Build { - self.force_frame_pointer = Some(force); - self - } - - /// Configures the output directory where all object files and static - /// libraries will be located. - /// - /// This option is automatically scraped from the `OUT_DIR` environment - /// variable by build scripts, so it's not required to call this function. - pub fn out_dir>(&mut self, out_dir: P) -> &mut Build { - self.out_dir = Some(out_dir.as_ref().into()); - self - } - - /// Configures the compiler to be used to produce output. - /// - /// This option is automatically determined from the target platform or a - /// number of environment variables, so it's not required to call this - /// function. - pub fn compiler>(&mut self, compiler: P) -> &mut Build { - self.compiler = Some(compiler.as_ref().into()); - self - } - - /// Configures the tool used to assemble archives. - /// - /// This option is automatically determined from the target platform or a - /// number of environment variables, so it's not required to call this - /// function. - pub fn archiver>(&mut self, archiver: P) -> &mut Build { - self.archiver = Some(archiver.as_ref().into()); - self - } - - /// Configures the tool used to index archives. - /// - /// This option is automatically determined from the target platform or a - /// number of environment variables, so it's not required to call this - /// function. - pub fn ranlib>(&mut self, ranlib: P) -> &mut Build { - self.ranlib = Some(ranlib.as_ref().into()); - self - } - - /// Define whether metadata should be emitted for cargo allowing it to - /// automatically link the binary. Defaults to `true`. - /// - /// The emitted metadata is: - /// - /// - `rustc-link-lib=static=`*compiled lib* - /// - `rustc-link-search=native=`*target folder* - /// - When target is MSVC, the ATL-MFC libs are added via `rustc-link-search=native=` - /// - When C++ is enabled, the C++ stdlib is added via `rustc-link-lib` - /// - If `emit_rerun_if_env_changed` is not `false`, `rerun-if-env-changed=`*env* - /// - pub fn cargo_metadata(&mut self, cargo_metadata: bool) -> &mut Build { - self.cargo_output.metadata = cargo_metadata; - self - } - - /// Define whether compile warnings should be emitted for cargo. Defaults to - /// `true`. - /// - /// If disabled, compiler messages will not be printed. - /// Issues unrelated to the compilation will always produce cargo warnings regardless of this setting. - pub fn cargo_warnings(&mut self, cargo_warnings: bool) -> &mut Build { - self.cargo_output.warnings = cargo_warnings; - self - } - - /// Define whether debug information should be emitted for cargo. Defaults to whether - /// or not the environment variable `CC_ENABLE_DEBUG_OUTPUT` is set. - /// - /// If enabled, the compiler will emit debug information when generating object files, - /// such as the command invoked and the exit status. - pub fn cargo_debug(&mut self, cargo_debug: bool) -> &mut Build { - self.cargo_output.debug = cargo_debug; - self - } - - /// Define whether compiler output (to stdout) should be emitted. Defaults to `true` - /// (forward compiler stdout to this process' stdout) - /// - /// Some compilers emit errors to stdout, so if you *really* need stdout to be clean - /// you should also set this to `false`. - pub fn cargo_output(&mut self, cargo_output: bool) -> &mut Build { - self.cargo_output.output = if cargo_output { - OutputKind::Forward - } else { - OutputKind::Discard - }; - self - } - - /// Adds a native library modifier that will be added to the - /// `rustc-link-lib=static:MODIFIERS=LIBRARY_NAME` metadata line - /// emitted for cargo if `cargo_metadata` is enabled. - /// See - /// for the list of modifiers accepted by rustc. - pub fn link_lib_modifier(&mut self, link_lib_modifier: impl AsRef) -> &mut Build { - self.link_lib_modifiers - .push(link_lib_modifier.as_ref().into()); - self - } - - /// Configures whether the compiler will emit position independent code. - /// - /// This option defaults to `false` for `windows-gnu` and bare metal targets and - /// to `true` for all other targets. - pub fn pic(&mut self, pic: bool) -> &mut Build { - self.pic = Some(pic); - self - } - - /// Configures whether the Procedure Linkage Table is used for indirect - /// calls into shared libraries. - /// - /// The PLT is used to provide features like lazy binding, but introduces - /// a small performance loss due to extra pointer indirection. Setting - /// `use_plt` to `false` can provide a small performance increase. - /// - /// Note that skipping the PLT requires a recent version of GCC/Clang. - /// - /// This only applies to ELF targets. It has no effect on other platforms. - pub fn use_plt(&mut self, use_plt: bool) -> &mut Build { - self.use_plt = Some(use_plt); - self - } - - /// Define whether metadata should be emitted for cargo to only trigger - /// rebuild when detected environment changes, by default build script is - /// always run on every compilation if no rerun cargo metadata is emitted. - /// - /// NOTE that cc does not emit metadata to detect changes for `PATH`, since it could - /// be changed every compilation yet does not affect the result of compilation - /// (i.e. rust-analyzer adds temporary directory to `PATH`). - /// - /// cc in general, has no way detecting changes to compiler, as there are so many ways to - /// change it and sidestep the detection, for example the compiler might be wrapped in a script - /// so detecting change of the file, or using checksum won't work. - /// - /// We recommend users to decide for themselves, if they want rebuild if the compiler has been upgraded - /// or changed, and how to detect that. - /// - /// This has no effect if the `cargo_metadata` option is `false`. - /// - /// This option defaults to `true`. - pub fn emit_rerun_if_env_changed(&mut self, emit_rerun_if_env_changed: bool) -> &mut Build { - self.emit_rerun_if_env_changed = emit_rerun_if_env_changed; - self - } - - /// Configures whether the /MT flag or the /MD flag will be passed to msvc build tools. - /// - /// This option defaults to `false`, and affect only msvc targets. - pub fn static_crt(&mut self, static_crt: bool) -> &mut Build { - self.static_crt = Some(static_crt); - self - } - - /// Configure whether *FLAGS variables are parsed using `shlex`, similarly to `make` and - /// `cmake`. - /// - /// This option defaults to `false`. - pub fn shell_escaped_flags(&mut self, shell_escaped_flags: bool) -> &mut Build { - self.shell_escaped_flags = Some(shell_escaped_flags); - self - } - - /// Configure whether cc should automatically inherit compatible flags passed to rustc - /// from `CARGO_ENCODED_RUSTFLAGS`. - /// - /// This option defaults to `true`. - pub fn inherit_rustflags(&mut self, inherit_rustflags: bool) -> &mut Build { - self.inherit_rustflags = inherit_rustflags; - self - } - - /// Prefer to use clang-cl over msvc. - /// - /// This option defaults to `false`. - pub fn prefer_clang_cl_over_msvc(&mut self, prefer_clang_cl_over_msvc: bool) -> &mut Build { - self.prefer_clang_cl_over_msvc = prefer_clang_cl_over_msvc; - self - } - - /// Set an environment variable for compiler invocations and other child processes. - /// - /// `cc` reads a lot of different variables from the current process' environment. It currently - /// allows the following standard environment variables to be overwritten by this function: - /// - `SDKROOT` - /// - `*_DEPLOYMENT_TARGET` - /// - `WASI_SDK_ROOT` - /// - /// The logic here is "environment variables that the C compiler could itself reasonably have - /// read". - pub fn env(&mut self, key: K, val: V) -> &mut Build - where - K: AsRef, - V: AsRef, - { - self.env.push((key.as_ref().into(), val.as_ref().into())); - self - } - - // retained for backwards compatibility only - #[doc(hidden)] - #[deprecated = "use `env` instead"] - pub fn __set_env(&mut self, key: K, val: V) -> &mut Build - where - K: AsRef, - V: AsRef, - { - self.env(key, val) - } -} - -/// Invoke or fetch the compiler or archiver. -impl Build { - /// Run the compiler to test if it accepts the given flag. - /// - /// For a convenience method for setting flags conditionally, - /// see `flag_if_supported()`. - /// - /// It may return error if it's unable to run the compiler with a test file - /// (e.g. the compiler is missing or a write to the `out_dir` failed). - /// - /// Note: Once computed, the result of this call is stored in the - /// `known_flag_support` field. If `is_flag_supported(flag)` - /// is called again, the result will be read from the hash table. - pub fn is_flag_supported(&self, flag: impl AsRef) -> Result { - self.is_flag_supported_inner( - flag.as_ref(), - &self.get_base_compiler()?, - &self.get_target()?, - ) - } - - fn ensure_check_file(&self) -> Result { - let out_dir = self.get_out_dir()?; - let src = if self.cuda { - assert!(self.cpp); - out_dir.join("flag_check.cu") - } else if self.cpp { - out_dir.join("flag_check.cpp") - } else { - out_dir.join("flag_check.c") - }; - - if !src.exists() { - let mut f = fs::File::create(&src)?; - write!(f, "int main(void) {{ return 0; }}")?; - } - - Ok(src) - } - - fn is_flag_supported_inner( - &self, - flag: &OsStr, - tool: &Tool, - target: &TargetInfo<'_>, - ) -> Result { - let compiler_flag = CompilerFlag { - compiler: tool.path().into(), - flag: flag.into(), - }; - - if let Some(is_supported) = self - .build_cache - .known_flag_support_status_cache - .read() - .unwrap() - .get(&compiler_flag) - .cloned() - { - return Ok(is_supported); - } - - let out_dir = self.get_out_dir()?; - let src = self.ensure_check_file()?; - let obj = out_dir.join("flag_check"); - - let mut compiler = { - let mut cfg = Build::new(); - cfg.flag(flag) - .compiler(tool.path()) - .cargo_metadata(self.cargo_output.metadata) - .opt_level(0) - .debug(false) - .cpp(self.cpp) - .cuda(self.cuda) - .inherit_rustflags(false) - .emit_rerun_if_env_changed(self.emit_rerun_if_env_changed); - if let Some(target) = &self.target { - cfg.target(target); - } - if let Some(host) = &self.host { - cfg.host(host); - } - cfg.try_get_compiler()? - }; - - // Clang uses stderr for verbose output, which yields a false positive - // result if the CFLAGS/CXXFLAGS include -v to aid in debugging. - if compiler.family.verbose_stderr() { - compiler.remove_arg("-v".into()); - } - if compiler.is_like_clang() { - // Avoid reporting that the arg is unsupported just because the - // compiler complains that it wasn't used. - compiler.push_cc_arg("-Wno-unused-command-line-argument".into()); - } - - let mut cmd = compiler.to_command(); - command_add_output_file( - &mut cmd, - &obj, - CmdAddOutputFileArgs { - cuda: self.cuda, - is_assembler_msvc: false, - msvc: compiler.is_like_msvc(), - clang: compiler.is_like_clang(), - gnu: compiler.is_like_gnu(), - is_asm: false, - is_arm: is_arm(target), - }, - ); - - // Checking for compiler flags does not require linking (and we _must_ - // avoid making it do so, since it breaks cross-compilation when the C - // compiler isn't configured to be able to link). - // https://github.com/rust-lang/cc-rs/issues/1423 - cmd.arg("-c"); - - if compiler.supports_path_delimiter() { - cmd.arg("--"); - } - - cmd.arg(&src); - - if compiler.is_like_msvc() { - // On MSVC we need to make sure the LIB directory is included - // so the CRT can be found. - for (key, value) in &tool.env { - if key == "LIB" { - cmd.env("LIB", value); - break; - } - } - } - - let output = cmd.current_dir(out_dir).output()?; - let is_supported = output.status.success() && output.stderr.is_empty(); - - self.build_cache - .known_flag_support_status_cache - .write() - .unwrap() - .insert(compiler_flag, is_supported); - - Ok(is_supported) - } - - /// Run the compiler, generating the file `output` - /// - /// This will return a result instead of panicking; see [`Self::compile()`] for - /// the complete description. - pub fn try_compile(&self, output: &str) -> Result<(), Error> { - let mut output_components = Path::new(output).components(); - match (output_components.next(), output_components.next()) { - (Some(Component::Normal(_)), None) => {} - _ => { - return Err(Error::new( - ErrorKind::InvalidArgument, - "argument of `compile` must be a single normal path component", - )); - } - } - - let (lib_name, gnu_lib_name) = if output.starts_with("lib") && output.ends_with(".a") { - (&output[3..output.len() - 2], output.to_owned()) - } else { - let mut gnu = String::with_capacity(5 + output.len()); - gnu.push_str("lib"); - gnu.push_str(output); - gnu.push_str(".a"); - (output, gnu) - }; - let dst = self.get_out_dir()?; - - let objects = objects_from_files(&self.files, &dst)?; - - self.compile_objects(&objects)?; - self.assemble(lib_name, &dst.join(gnu_lib_name), &objects)?; - - let target = self.get_target()?; - if target.env == "msvc" { - let compiler = self.get_base_compiler()?; - let atlmfc_lib = compiler - .env() - .iter() - .find(|&(var, _)| var.as_os_str() == OsStr::new("LIB")) - .and_then(|(_, lib_paths)| { - env::split_paths(lib_paths).find(|path| { - let sub = Path::new("atlmfc/lib"); - path.ends_with(sub) || path.parent().map_or(false, |p| p.ends_with(sub)) - }) - }); - - if let Some(atlmfc_lib) = atlmfc_lib { - self.cargo_output.print_metadata(&format_args!( - "cargo:rustc-link-search=native={}", - atlmfc_lib.display() - )); - } - } - - if self.link_lib_modifiers.is_empty() { - self.cargo_output - .print_metadata(&format_args!("cargo:rustc-link-lib=static={lib_name}")); - } else { - self.cargo_output.print_metadata(&format_args!( - "cargo:rustc-link-lib=static:{}={}", - JoinOsStrs { - slice: &self.link_lib_modifiers, - delimiter: ',' - }, - lib_name - )); - } - self.cargo_output.print_metadata(&format_args!( - "cargo:rustc-link-search=native={}", - dst.display() - )); - - // Add specific C++ libraries, if enabled. - if self.cpp { - if let Some(stdlib) = self.get_cpp_link_stdlib()? { - if self.cpp_link_stdlib_static { - self.cargo_output.print_metadata(&format_args!( - "cargo:rustc-link-lib=static={}", - stdlib.display() - )); - } else { - self.cargo_output - .print_metadata(&format_args!("cargo:rustc-link-lib={}", stdlib.display())); - } - } - // Link c++ lib from WASI sysroot - if target.arch == "wasm32" { - if target.os == "wasi" { - if let Ok(wasi_sysroot) = self.wasi_sysroot() { - self.cargo_output.print_metadata(&format_args!( - "cargo:rustc-flags=-L {}/lib/{} -lstatic=c++ -lstatic=c++abi", - Path::new(&wasi_sysroot).display(), - self.get_raw_target()? - )); - } - } else if target.os == "linux" { - let musl_sysroot = self.wasm_musl_sysroot().unwrap(); - self.cargo_output.print_metadata(&format_args!( - "cargo:rustc-flags=-L {}/lib -lstatic=c++ -lstatic=c++abi", - Path::new(&musl_sysroot).display(), - )); - } - } - } - - let cudart = match &self.cudart { - Some(opt) => opt, // {none|shared|static} - None => "none", - }; - if cudart != "none" { - if let Some(nvcc) = self.which(&self.get_compiler().path, None) { - // Try to figure out the -L search path. If it fails, - // it's on user to specify one by passing it through - // RUSTFLAGS environment variable. - let mut libtst = false; - let mut libdir = nvcc; - libdir.pop(); // remove 'nvcc' - libdir.push(".."); - if cfg!(target_os = "linux") { - libdir.push("targets"); - libdir.push(format!("{}-linux", target.arch)); - if !libdir.exists() && target.arch == "aarch64" { - libdir.pop(); - libdir.push("sbsa-linux"); - } - libdir.push("lib"); - libtst = true; - } else if cfg!(target_env = "msvc") { - libdir.push("lib"); - match target.arch { - "x86_64" => { - libdir.push("x64"); - libtst = true; - } - "x86" => { - libdir.push("Win32"); - libtst = true; - } - _ => libtst = false, - } - } - if libtst && libdir.is_dir() { - self.cargo_output.print_metadata(&format_args!( - "cargo:rustc-link-search=native={}", - libdir.to_str().unwrap() - )); - } - - // And now the -l flag. - let lib = match cudart { - "shared" => "cudart", - "static" => "cudart_static", - bad => panic!("unsupported cudart option: {}", bad), - }; - self.cargo_output - .print_metadata(&format_args!("cargo:rustc-link-lib={lib}")); - } - } - - Ok(()) - } - - /// Run the compiler, generating the file `output` - /// - /// # Library name - /// - /// The `output` string argument determines the file name for the compiled - /// library. The Rust compiler will create an assembly named "lib"+output+".a". - /// MSVC will create a file named output+".lib". - /// - /// The choice of `output` is close to arbitrary, but: - /// - /// - must be nonempty, - /// - must not contain a path separator (`/`), - /// - must be unique across all `compile` invocations made by the same build - /// script. - /// - /// If your build script compiles a single source file, the base name of - /// that source file would usually be reasonable: - /// - /// ```no_run - /// cc::Build::new().file("blobstore.c").compile("blobstore"); - /// ``` - /// - /// Compiling multiple source files, some people use their crate's name, or - /// their crate's name + "-cc". - /// - /// Otherwise, please use your imagination. - /// - /// For backwards compatibility, if `output` starts with "lib" *and* ends - /// with ".a", a second "lib" prefix and ".a" suffix do not get added on, - /// but this usage is deprecated; please omit `lib` and `.a` in the argument - /// that you pass. - /// - /// # Panics - /// - /// Panics if `output` is not formatted correctly or if one of the underlying - /// compiler commands fails. It can also panic if it fails reading file names - /// or creating directories. - pub fn compile(&self, output: &str) { - if let Err(e) = self.try_compile(output) { - fail(&e.message); - } - } - - /// Run the compiler, generating intermediate files, but without linking - /// them into an archive file. - /// - /// This will return a list of compiled object files, in the same order - /// as they were passed in as `file`/`files` methods. - pub fn compile_intermediates(&self) -> Vec { - match self.try_compile_intermediates() { - Ok(v) => v, - Err(e) => fail(&e.message), - } - } - - /// Run the compiler, generating intermediate files, but without linking - /// them into an archive file. - /// - /// This will return a result instead of panicking; see `compile_intermediates()` for the complete description. - pub fn try_compile_intermediates(&self) -> Result, Error> { - let dst = self.get_out_dir()?; - let objects = objects_from_files(&self.files, &dst)?; - - self.compile_objects(&objects)?; - - Ok(objects.into_iter().map(|v| v.dst).collect()) - } - - fn compile_objects(&self, objs: &[Object]) -> Result<(), Error> { - if self.is_disabled() { - return Err(Error::new( - ErrorKind::Disabled, - "the `cc` crate's functionality has been disabled by the `CC_FORCE_DISABLE` environment variable.", - )); - } - - #[cfg(feature = "parallel")] - if objs.len() > 1 { - return parallel::run_commands_in_parallel( - &self.cargo_output, - &mut objs.iter().map(|obj| self.create_compile_object_cmd(obj)), - ); - } - - for obj in objs { - let mut cmd = self.create_compile_object_cmd(obj)?; - run(&mut cmd, &self.cargo_output)?; - } - - Ok(()) - } - - fn create_compile_object_cmd(&self, obj: &Object) -> Result { - let asm_ext = AsmFileExt::from_path(&obj.src); - let is_asm = asm_ext.is_some(); - let target = self.get_target()?; - let msvc = target.env == "msvc"; - let compiler = self.try_get_compiler()?; - - let is_assembler_msvc = msvc && asm_ext == Some(AsmFileExt::DotAsm); - let mut cmd = if is_assembler_msvc { - self.msvc_macro_assembler()? - } else { - compiler.to_command() - }; - let is_arm = is_arm(&target); - command_add_output_file( - &mut cmd, - &obj.dst, - CmdAddOutputFileArgs { - cuda: self.cuda, - is_assembler_msvc, - msvc: compiler.is_like_msvc(), - clang: compiler.is_like_clang(), - gnu: compiler.is_like_gnu(), - is_asm, - is_arm, - }, - ); - // armasm and armasm64 don't require -c option - if !is_assembler_msvc || !is_arm { - cmd.arg("-c"); - } - if self.cuda && self.cuda_file_count() > 1 { - cmd.arg("--device-c"); - } - if is_asm { - cmd.args(self.asm_flags.iter().map(std::ops::Deref::deref)); - } - - if compiler.supports_path_delimiter() && !is_assembler_msvc { - // #513: For `clang-cl`, separate flags/options from the input file. - // When cross-compiling macOS -> Windows, this avoids interpreting - // common `/Users/...` paths as the `/U` flag and triggering - // `-Wslash-u-filename` warning. - cmd.arg("--"); - } - cmd.arg(&obj.src); - - if cfg!(target_os = "macos") { - self.fix_env_for_apple_os(&mut cmd)?; - } - - Ok(cmd) - } - - /// This will return a result instead of panicking; see [`Self::expand()`] for - /// the complete description. - pub fn try_expand(&self) -> Result, Error> { - let compiler = self.try_get_compiler()?; - let mut cmd = compiler.to_command(); - cmd.arg("-E"); - - assert!( - self.files.len() <= 1, - "Expand may only be called for a single file" - ); - - let is_asm = self - .files - .iter() - .map(std::ops::Deref::deref) - .find_map(AsmFileExt::from_path) - .is_some(); - - if compiler.family == (ToolFamily::Msvc { clang_cl: true }) && !is_asm { - // #513: For `clang-cl`, separate flags/options from the input file. - // When cross-compiling macOS -> Windows, this avoids interpreting - // common `/Users/...` paths as the `/U` flag and triggering - // `-Wslash-u-filename` warning. - cmd.arg("--"); - } - - cmd.args(self.files.iter().map(std::ops::Deref::deref)); - - run_output(&mut cmd, &self.cargo_output) - } - - /// Run the compiler, returning the macro-expanded version of the input files. - /// - /// This is only relevant for C and C++ files. - /// - /// # Panics - /// Panics if more than one file is present in the config, or if compiler - /// path has an invalid file name. - /// - /// # Example - /// ```no_run - /// let out = cc::Build::new().file("src/foo.c").expand(); - /// ``` - pub fn expand(&self) -> Vec { - match self.try_expand() { - Err(e) => fail(&e.message), - Ok(v) => v, - } - } - - /// Get the compiler that's in use for this configuration. - /// - /// This function will return a `Tool` which represents the culmination - /// of this configuration at a snapshot in time. The returned compiler can - /// be inspected (e.g. the path, arguments, environment) to forward along to - /// other tools, or the `to_command` method can be used to invoke the - /// compiler itself. - /// - /// This method will take into account all configuration such as debug - /// information, optimization level, include directories, defines, etc. - /// Additionally, the compiler binary in use follows the standard - /// conventions for this path, e.g. looking at the explicitly set compiler, - /// environment variables (a number of which are inspected here), and then - /// falling back to the default configuration. - /// - /// # Panics - /// - /// Panics if an error occurred while determining the architecture. - pub fn get_compiler(&self) -> Tool { - match self.try_get_compiler() { - Ok(tool) => tool, - Err(e) => fail(&e.message), - } - } - - /// Get the compiler that's in use for this configuration. - /// - /// This will return a result instead of panicking; see - /// [`get_compiler()`](Self::get_compiler) for the complete description. - pub fn try_get_compiler(&self) -> Result { - let opt_level = self.get_opt_level()?; - let target = self.get_target()?; - - let mut cmd = self.get_base_compiler()?; - - // The flags below are added in roughly the following order: - // 1. Default flags - // - Controlled by `cc-rs`. - // 2. `rustc`-inherited flags - // - Controlled by `rustc`. - // 3. Builder flags - // - Controlled by the developer using `cc-rs` in e.g. their `build.rs`. - // 4. Environment flags - // - Controlled by the end user. - // - // This is important to allow later flags to override previous ones. - - // Copied from - // - // Disables non-English messages from localized linkers. - // Such messages may cause issues with text encoding on Windows - // and prevent inspection of msvc output in case of errors, which we occasionally do. - // This should be acceptable because other messages from rustc are in English anyway, - // and may also be desirable to improve searchability of the compiler diagnostics. - if matches!(cmd.family, ToolFamily::Msvc { clang_cl: false }) { - cmd.env.push(("VSLANG".into(), "1033".into())); - } else { - cmd.env.push(("LC_ALL".into(), "C".into())); - } - - // Disable default flag generation via `no_default_flags` or environment variable - let no_defaults = self.no_default_flags || self.get_env_boolean("CRATE_CC_NO_DEFAULTS"); - if !no_defaults { - self.add_default_flags(&mut cmd, &target, &opt_level)?; - } - - // Specify various flags that are not considered part of the default flags above. - // FIXME(madsmtm): Should these be considered part of the defaults? If no, why not? - if let Some(ref std) = self.std { - let separator = match cmd.family { - ToolFamily::Msvc { .. } => ':', - ToolFamily::Gnu | ToolFamily::Clang { .. } => '=', - }; - cmd.push_cc_arg(format!("-std{separator}{std}").into()); - } - for directory in self.include_directories.iter() { - cmd.args.push("-I".into()); - cmd.args.push(directory.as_os_str().into()); - } - if self.warnings_into_errors { - let warnings_to_errors_flag = cmd.family.warnings_to_errors_flag().into(); - cmd.push_cc_arg(warnings_to_errors_flag); - } - - // If warnings and/or extra_warnings haven't been explicitly set, - // then we set them only if the environment doesn't already have - // CFLAGS/CXXFLAGS, since those variables presumably already contain - // the desired set of warnings flags. - let envflags = self.envflags(if self.cpp { "CXXFLAGS" } else { "CFLAGS" })?; - match self.warnings { - Some(true) => { - let wflags = cmd.family.warnings_flags().into(); - cmd.push_cc_arg(wflags); - } - Some(false) => { - let wflags = cmd.family.warnings_suppression_flags().into(); - cmd.push_cc_arg(wflags); - } - None => { - if envflags.is_none() { - let wflags = cmd.family.warnings_flags().into(); - cmd.push_cc_arg(wflags); - } - } - } - if self.extra_warnings.unwrap_or(envflags.is_none()) { - if let Some(wflags) = cmd.family.extra_warnings_flags() { - cmd.push_cc_arg(wflags.into()); - } - } - - // Add cc flags inherited from matching rustc flags. - if self.inherit_rustflags { - self.add_inherited_rustflags(&mut cmd, &target)?; - } - - // Set flags configured in the builder (do this second-to-last, to allow these to override - // everything above). - for flag in self.flags.iter() { - cmd.args.push((**flag).into()); - } - for flag in self.flags_supported.iter() { - if self - .is_flag_supported_inner(flag, &cmd, &target) - .unwrap_or(false) - { - cmd.push_cc_arg((**flag).into()); - } - } - for (key, value) in self.definitions.iter() { - if let Some(ref value) = *value { - cmd.args.push(format!("-D{key}={value}").into()); - } else { - cmd.args.push(format!("-D{key}").into()); - } - } - - // Set flags from the environment (do this last, to allow these to override everything else). - if let Some(flags) = &envflags { - for arg in flags { - cmd.push_cc_arg(arg.into()); - } - } - - // Set custom env vars that the user specified with `Build::env`. - // - // Do this last, to allow overwriting the other values above. - for (key, val) in &self.env { - cmd.env.push((key.into(), val.into())); - } - - Ok(cmd) - } - - fn add_default_flags( - &self, - cmd: &mut Tool, - target: &TargetInfo<'_>, - opt_level: &str, - ) -> Result<(), Error> { - let raw_target = self.get_raw_target()?; - // Non-target flags - // If the flag is not conditioned on target variable, it belongs here :) - match cmd.family { - ToolFamily::Msvc { .. } => { - cmd.push_cc_arg("-nologo".into()); - - let crt_flag = match self.static_crt { - Some(true) => "-MT", - Some(false) => "-MD", - None => { - let features = cargo_env_var_os("CARGO_CFG_TARGET_FEATURE"); - let features = features.as_deref().unwrap_or_default(); - if features.to_string_lossy().contains("crt-static") { - "-MT" - } else { - "-MD" - } - } - }; - cmd.push_cc_arg(crt_flag.into()); - - match opt_level { - // Msvc uses /O1 to enable all optimizations that minimize code size. - "z" | "s" | "1" => cmd.push_opt_unless_duplicate("-O1".into()), - // -O3 is a valid value for gcc and clang compilers, but not msvc. Cap to /O2. - "2" | "3" => cmd.push_opt_unless_duplicate("-O2".into()), - _ => {} - } - } - ToolFamily::Gnu | ToolFamily::Clang { .. } => { - // arm-linux-androideabi-gcc 4.8 shipped with Android NDK does - // not support '-Oz' - if opt_level == "z" && !cmd.is_like_clang() { - cmd.push_opt_unless_duplicate("-Os".into()); - } else { - cmd.push_opt_unless_duplicate(format!("-O{opt_level}").into()); - } - - if cmd.is_like_clang() && target.os == "android" { - // For compatibility with code that doesn't use pre-defined `__ANDROID__` macro. - // If compiler used via ndk-build or cmake (officially supported build methods) - // this macros is defined. - // See https://android.googlesource.com/platform/ndk/+/refs/heads/ndk-release-r21/build/cmake/android.toolchain.cmake#456 - // https://android.googlesource.com/platform/ndk/+/refs/heads/ndk-release-r21/build/core/build-binary.mk#141 - cmd.push_opt_unless_duplicate("-DANDROID".into()); - } - - if target.os != "ios" - && target.os != "watchos" - && target.os != "tvos" - && target.os != "visionos" - { - cmd.push_cc_arg("-ffunction-sections".into()); - cmd.push_cc_arg("-fdata-sections".into()); - } - // Disable generation of PIC on bare-metal for now: rust-lld doesn't support this yet - // - // `rustc` also defaults to disable PIC on WASM: - // - if self.pic.unwrap_or( - target.os != "windows" - && target.os != "none" - && target.os != "uefi" - && target.os != "vita" - && target.arch != "wasm32" - && target.arch != "wasm64", - ) { - cmd.push_cc_arg("-fPIC".into()); - // PLT only applies if code is compiled with PIC support, - // and only for ELF targets. - if (target.os == "linux" || target.os == "android") - && !self.use_plt.unwrap_or(true) - { - cmd.push_cc_arg("-fno-plt".into()); - } - } - - if target.os == "wasi" { - // Link clang sysroot - if let Ok(wasi_sysroot) = self.wasi_sysroot() { - cmd.push_cc_arg( - format!("--sysroot={}", Path::new(&wasi_sysroot).display()).into(), - ); - } - - // FIXME(madsmtm): Read from `target_features` instead? - if raw_target.contains("threads") { - cmd.push_cc_arg("-pthread".into()); - } - } - - if target.os == "nto" { - // Select the target with `-V`, see qcc documentation: - // QNX 7.1: https://www.qnx.com/developers/docs/7.1/index.html#com.qnx.doc.neutrino.utilities/topic/q/qcc.html - // QNX 8.0: https://www.qnx.com/developers/docs/8.0/com.qnx.doc.neutrino.utilities/topic/q/qcc.html - // This assumes qcc/q++ as compiler, which is currently the only supported compiler for QNX. - // See for details: https://github.com/rust-lang/cc-rs/pull/1319 - let arg = match target.full_arch { - "x86" | "i586" => "-Vgcc_ntox86_cxx", - "aarch64" => "-Vgcc_ntoaarch64le_cxx", - "x86_64" => "-Vgcc_ntox86_64_cxx", - _ => { - return Err(Error::new( - ErrorKind::InvalidTarget, - format!("Unknown architecture for Neutrino QNX: {}", target.arch), - )) - } - }; - cmd.push_cc_arg(arg.into()); - } - } - } - - if self.get_debug() { - if self.cuda { - // NVCC debug flag - cmd.args.push("-G".into()); - } - let family = cmd.family; - family.add_debug_flags( - cmd, - self.get_debug_str().as_deref().unwrap_or_default(), - self.get_dwarf_version(), - ); - } - - if self.get_force_frame_pointer() { - let family = cmd.family; - family.add_force_frame_pointer(cmd); - } - - if !cmd.is_like_msvc() { - if target.arch == "x86" { - cmd.args.push("-m32".into()); - } else if target.abi == "x32" { - cmd.args.push("-mx32".into()); - } else if target.os == "aix" { - if cmd.family == ToolFamily::Gnu { - cmd.args.push("-maix64".into()); - } else { - cmd.args.push("-m64".into()); - } - } else if target.arch == "x86_64" || target.arch == "powerpc64" { - cmd.args.push("-m64".into()); - } - } - - // Target flags - match cmd.family { - ToolFamily::Clang { .. } => { - if !(cmd.has_internal_target_arg - || (target.os == "android" - && android_clang_compiler_uses_target_arg_internally(&cmd.path))) - { - if target.os == "freebsd" { - // FreeBSD only supports C++11 and above when compiling against libc++ - // (available from FreeBSD 10 onwards). Under FreeBSD, clang uses libc++ by - // default on FreeBSD 10 and newer unless `--target` is manually passed to - // the compiler, in which case its default behavior differs: - // * If --target=xxx-unknown-freebsdX(.Y) is specified and X is greater than - // or equal to 10, clang++ uses libc++ - // * If --target=xxx-unknown-freebsd is specified (without a version), - // clang++ cannot assume libc++ is available and reverts to a default of - // libstdc++ (this behavior was changed in llvm 14). - // - // This breaks C++11 (or greater) builds if targeting FreeBSD with the - // generic xxx-unknown-freebsd target on clang 13 or below *without* - // explicitly specifying that libc++ should be used. - // When cross-compiling, we can't infer from the rust/cargo target name - // which major version of FreeBSD we are targeting, so we need to make sure - // that libc++ is used (unless the user has explicitly specified otherwise). - // There's no compelling reason to use a different approach when compiling - // natively. - if self.cpp && self.cpp_set_stdlib.is_none() { - cmd.push_cc_arg("-stdlib=libc++".into()); - } - } else if target.arch == "wasm32" && target.os == "linux" { - for x in &[ - "atomics", - "bulk-memory", - "mutable-globals", - "sign-ext", - "exception-handling", - ] { - cmd.push_cc_arg(format!("-m{x}").into()); - } - for x in &["wasm-exceptions", "declspec"] { - cmd.push_cc_arg(format!("-f{x}").into()); - } - let musl_sysroot = self.wasm_musl_sysroot().unwrap(); - cmd.push_cc_arg( - format!("--sysroot={}", Path::new(&musl_sysroot).display()).into(), - ); - cmd.push_cc_arg("-pthread".into()); - } - // Pass `--target` with the LLVM target to configure Clang for cross-compiling. - // - // This is **required** for cross-compilation, as it's the only flag that - // consistently forces Clang to change the "toolchain" that is responsible for - // parsing target-specific flags: - // https://github.com/rust-lang/cc-rs/issues/1388 - // https://github.com/llvm/llvm-project/blob/llvmorg-19.1.7/clang/lib/Driver/Driver.cpp#L1359-L1360 - // https://github.com/llvm/llvm-project/blob/llvmorg-19.1.7/clang/lib/Driver/Driver.cpp#L6347-L6532 - // - // This can be confusing, because on e.g. host macOS, you can usually get by - // with `-arch` and `-mtargetos=`. But that only works because the _default_ - // toolchain is `Darwin`, which enables parsing of darwin-specific options. - // - // NOTE: In the past, we passed the deployment version in here on all Apple - // targets, but versioned targets were found to have poor compatibility with - // older versions of Clang, especially when it comes to configuration files: - // https://github.com/rust-lang/cc-rs/issues/1278 - // - // So instead, we pass the deployment target with `-m*-version-min=`, and only - // pass it here on visionOS and Mac Catalyst where that option does not exist: - // https://github.com/rust-lang/cc-rs/issues/1383 - let version = if target.os == "visionos" || target.env == "macabi" { - Some(self.apple_deployment_target(target)) - } else { - None - }; - - let clang_target = - target.llvm_target(&self.get_raw_target()?, version.as_deref()); - cmd.push_cc_arg(format!("--target={clang_target}").into()); - } - } - ToolFamily::Msvc { clang_cl } => { - // This is an undocumented flag from MSVC but helps with making - // builds more reproducible by avoiding putting timestamps into - // files. - cmd.push_cc_arg("-Brepro".into()); - - if clang_cl { - cmd.push_cc_arg( - format!( - "--target={}", - target.llvm_target(&self.get_raw_target()?, None) - ) - .into(), - ); - - if target.arch == "x86" { - // See - // . - // - // NOTE: Rust officially supported Windows targets all require SSE2 as part - // of baseline target features. - // - // NOTE: The same applies for STL. See: - - // , and - - // . - cmd.push_cc_arg("-arch:SSE2".into()); - } - } else if target.full_arch == "i586" { - cmd.push_cc_arg("-arch:IA32".into()); - } else if target.full_arch == "arm64ec" { - cmd.push_cc_arg("-arm64EC".into()); - } - // There is a check in corecrt.h that will generate a - // compilation error if - // _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE is - // not defined to 1. The check was added in Windows - // 8 days because only store apps were allowed on ARM. - // This changed with the release of Windows 10 IoT Core. - // The check will be going away in future versions of - // the SDK, but for all released versions of the - // Windows SDK it is required. - if target.arch == "arm" { - cmd.args - .push("-D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1".into()); - } - } - ToolFamily::Gnu => { - if target.vendor == "kmc" { - cmd.args.push("-finput-charset=utf-8".into()); - } - - if self.static_flag.is_none() { - let features = cargo_env_var_os("CARGO_CFG_TARGET_FEATURE"); - let features = features.as_deref().unwrap_or_default(); - if features.to_string_lossy().contains("crt-static") { - cmd.args.push("-static".into()); - } - } - - // armv7 targets get to use armv7 instructions - if (target.full_arch.starts_with("armv7") - || target.full_arch.starts_with("thumbv7")) - && (target.os == "linux" || target.vendor == "kmc") - { - cmd.args.push("-march=armv7-a".into()); - - if target.abi == "eabihf" { - // lowest common denominator FPU - cmd.args.push("-mfpu=vfpv3-d16".into()); - cmd.args.push("-mfloat-abi=hard".into()); - } - } - - // (x86 Android doesn't say "eabi") - if target.os == "android" && target.full_arch.contains("v7") { - cmd.args.push("-march=armv7-a".into()); - cmd.args.push("-mthumb".into()); - if !target.full_arch.contains("neon") { - // On android we can guarantee some extra float instructions - // (specified in the android spec online) - // NEON guarantees even more; see below. - cmd.args.push("-mfpu=vfpv3-d16".into()); - } - cmd.args.push("-mfloat-abi=softfp".into()); - } - - if target.full_arch.contains("neon") { - cmd.args.push("-mfpu=neon-vfpv4".into()); - } - - if target.full_arch == "armv4t" && target.os == "linux" { - cmd.args.push("-march=armv4t".into()); - cmd.args.push("-marm".into()); - cmd.args.push("-mfloat-abi=soft".into()); - } - - if target.full_arch == "armv5te" && target.os == "linux" { - cmd.args.push("-march=armv5te".into()); - cmd.args.push("-marm".into()); - cmd.args.push("-mfloat-abi=soft".into()); - } - - // For us arm == armv6 by default - if target.full_arch == "arm" && target.os == "linux" { - cmd.args.push("-march=armv6".into()); - cmd.args.push("-marm".into()); - if target.abi == "eabihf" { - cmd.args.push("-mfpu=vfp".into()); - } else { - cmd.args.push("-mfloat-abi=soft".into()); - } - } - - // Turn codegen down on i586 to avoid some instructions. - if target.full_arch == "i586" && target.os == "linux" { - cmd.args.push("-march=pentium".into()); - } - - // Set codegen level for i686 correctly - if target.full_arch == "i686" && target.os == "linux" { - cmd.args.push("-march=i686".into()); - } - - // Looks like `musl-gcc` makes it hard for `-m32` to make its way - // all the way to the linker, so we need to actually instruct the - // linker that we're generating 32-bit executables as well. This'll - // typically only be used for build scripts which transitively use - // these flags that try to compile executables. - if target.arch == "x86" && target.env == "musl" { - cmd.args.push("-Wl,-melf_i386".into()); - } - - if target.arch == "arm" && target.os == "none" && target.abi == "eabihf" { - cmd.args.push("-mfloat-abi=hard".into()) - } - if target.full_arch.starts_with("thumb") { - cmd.args.push("-mthumb".into()); - } - if target.full_arch.starts_with("thumbv6m") { - cmd.args.push("-march=armv6s-m".into()); - } - if target.full_arch.starts_with("thumbv7em") { - cmd.args.push("-march=armv7e-m".into()); - - if target.abi == "eabihf" { - cmd.args.push("-mfpu=fpv4-sp-d16".into()) - } - } - if target.full_arch.starts_with("thumbv7m") { - cmd.args.push("-march=armv7-m".into()); - } - if target.full_arch.starts_with("thumbv8m.base") { - cmd.args.push("-march=armv8-m.base".into()); - } - if target.full_arch.starts_with("thumbv8m.main") { - cmd.args.push("-march=armv8-m.main".into()); - - if target.abi == "eabihf" { - cmd.args.push("-mfpu=fpv5-sp-d16".into()) - } - } - if target.full_arch.starts_with("armebv7r") | target.full_arch.starts_with("armv7r") - { - if target.full_arch.starts_with("armeb") { - cmd.args.push("-mbig-endian".into()); - } else { - cmd.args.push("-mlittle-endian".into()); - } - - // ARM mode - cmd.args.push("-marm".into()); - - // R Profile - cmd.args.push("-march=armv7-r".into()); - - if target.abi == "eabihf" { - // lowest common denominator FPU - // (see Cortex-R4 technical reference manual) - cmd.args.push("-mfpu=vfpv3-d16".into()) - } - } - if target.full_arch.starts_with("armv7a") { - cmd.args.push("-march=armv7-a".into()); - - if target.abi == "eabihf" { - // lowest common denominator FPU - cmd.args.push("-mfpu=vfpv3-d16".into()); - } - } - if target.arch == "riscv32" || target.arch == "riscv64" { - // get the 32i/32imac/32imc/64gc/64imac/... part - let arch = &target.full_arch[5..]; - if arch.starts_with("64") { - if matches!(target.os, "linux" | "freebsd" | "netbsd") { - cmd.args.push(("-march=rv64gc").into()); - cmd.args.push("-mabi=lp64d".into()); - } else { - cmd.args.push(("-march=rv".to_owned() + arch).into()); - cmd.args.push("-mabi=lp64".into()); - } - } else if arch.starts_with("32") { - if target.os == "linux" { - cmd.args.push(("-march=rv32gc").into()); - cmd.args.push("-mabi=ilp32d".into()); - } else { - cmd.args.push(("-march=rv".to_owned() + arch).into()); - cmd.args.push("-mabi=ilp32".into()); - } - } else { - cmd.args.push("-mcmodel=medany".into()); - } - } - } - } - - if raw_target == "wasm32v1-none" { - // `wasm32v1-none` target only exists in `rustc`, so we need to change the compilation flags: - // https://doc.rust-lang.org/rustc/platform-support/wasm32v1-none.html - cmd.push_cc_arg("-mcpu=mvp".into()); - cmd.push_cc_arg("-mmutable-globals".into()); - } - - if target.os == "solaris" || target.os == "illumos" { - // On Solaris and illumos, multi-threaded C programs must be built with `_REENTRANT` - // defined. This configures headers to define APIs appropriately for multi-threaded - // use. This is documented in threads(7), see also https://illumos.org/man/7/threads. - // - // If C code is compiled without multi-threading support but does use multiple threads, - // incorrect behavior may result. One extreme example is that on some systems the - // global errno may be at the same address as the process' first thread's errno; errno - // clobbering may occur to disastrous effect. Conversely, if _REENTRANT is defined - // while it is not actually needed, system headers may define some APIs suboptimally - // but will not result in incorrect behavior. Other code *should* be reasonable under - // such conditions. - // - // We're typically building C code to eventually link into a Rust program. Many Rust - // programs are multi-threaded in some form. So, set the flag by default. - cmd.args.push("-D_REENTRANT".into()); - } - - if target.vendor == "apple" { - self.apple_flags(cmd)?; - } - - if self.static_flag.unwrap_or(false) { - cmd.args.push("-static".into()); - } - if self.shared_flag.unwrap_or(false) { - cmd.args.push("-shared".into()); - } - - if self.cpp { - match (self.cpp_set_stdlib.as_ref(), cmd.family) { - (None, _) => {} - (Some(stdlib), ToolFamily::Gnu) | (Some(stdlib), ToolFamily::Clang { .. }) => { - cmd.push_cc_arg(format!("-stdlib=lib{stdlib}").into()); - } - _ => { - self.cargo_output.print_warning(&format_args!("cpp_set_stdlib is specified, but the {:?} compiler does not support this option, ignored", cmd.family)); - } - } - } - - Ok(()) - } - - fn add_inherited_rustflags( - &self, - cmd: &mut Tool, - target: &TargetInfo<'_>, - ) -> Result<(), Error> { - let env_os = match cargo_env_var_os("CARGO_ENCODED_RUSTFLAGS") { - Some(env) => env, - // No encoded RUSTFLAGS -> nothing to do - None => return Ok(()), - }; - - let env = env_os.to_string_lossy(); - let codegen_flags = RustcCodegenFlags::parse(&env)?; - codegen_flags.cc_flags(self, cmd, target); - Ok(()) - } - - fn msvc_macro_assembler(&self) -> Result { - let target = self.get_target()?; - let tool = match target.arch { - "x86_64" => "ml64.exe", - "arm" => "armasm.exe", - "aarch64" | "arm64ec" => "armasm64.exe", - _ => "ml.exe", - }; - let mut cmd = self - .find_msvc_tools_find(&target, tool) - .unwrap_or_else(|| self.cmd(tool)); - cmd.arg("-nologo"); // undocumented, yet working with armasm[64] - for directory in self.include_directories.iter() { - cmd.arg("-I").arg(&**directory); - } - if is_arm(&target) { - if self.get_debug() { - cmd.arg("-g"); - } - - if target.arch == "arm64ec" { - cmd.args(["-machine", "ARM64EC"]); - } - - for (key, value) in self.definitions.iter() { - cmd.arg("-PreDefine"); - if let Some(ref value) = *value { - if let Ok(i) = value.parse::() { - cmd.arg(format!("{key} SETA {i}")); - } else if value.starts_with('"') && value.ends_with('"') { - cmd.arg(format!("{key} SETS {value}")); - } else { - cmd.arg(format!("{key} SETS \"{value}\"")); - } - } else { - cmd.arg(format!("{} SETL {}", key, "{TRUE}")); - } - } - } else { - if self.get_debug() { - cmd.arg("-Zi"); - } - - for (key, value) in self.definitions.iter() { - if let Some(ref value) = *value { - cmd.arg(format!("-D{key}={value}")); - } else { - cmd.arg(format!("-D{key}")); - } - } - } - - if target.arch == "x86" { - cmd.arg("-safeseh"); - } - - Ok(cmd) - } - - fn assemble(&self, lib_name: &str, dst: &Path, objs: &[Object]) -> Result<(), Error> { - // Delete the destination if it exists as we want to - // create on the first iteration instead of appending. - let _ = fs::remove_file(dst); - - // Add objects to the archive in limited-length batches. This helps keep - // the length of the command line within a reasonable length to avoid - // blowing system limits on limiting platforms like Windows. - // - // Optimistically try the `D` (deterministic) ar modifier, which zeros - // out timestamps, UIDs, and GIDs. If the archiver doesn't support it, - // we remember and stop trying for subsequent batches. - // (`None` -> haven't probed yet) - let mut deterministic_ar: Option = None; - - let mut objs = objs - .iter() - .map(|o| o.dst.as_path()) - .chain(self.objects.iter().map(std::ops::Deref::deref)) - .peekable(); - let mut batch = Vec::new(); - while objs.peek().is_some() { - let mut remaining_len = 4000; - while let Some(path) = - objs.next_if(|peek| batch.is_empty() || peek.as_os_str().len() <= remaining_len) - { - batch.push(path); - remaining_len = remaining_len.saturating_sub(path.as_os_str().len()); - } - self.assemble_progressive(dst, &batch, &mut deterministic_ar)?; - batch.clear(); - } - - if self.cuda && self.cuda_file_count() > 0 { - // Link the device-side code and add it to the target library, - // so that non-CUDA linker can link the final binary. - - let out_dir = self.get_out_dir()?; - let dlink = out_dir.join(lib_name.to_owned() + "_dlink.o"); - let mut nvcc = self.get_compiler().to_command(); - nvcc.arg("--device-link").arg("-o").arg(&dlink).arg(dst); - run(&mut nvcc, &self.cargo_output)?; - self.assemble_progressive(dst, &[dlink.as_path()], &mut deterministic_ar)?; - } - - let target = self.get_target()?; - if target.env == "msvc" { - // The Rust compiler will look for libfoo.a and foo.lib, but the - // MSVC linker will also be passed foo.lib, so be sure that both - // exist for now. - - let lib_dst = dst.with_file_name(format!("{lib_name}.lib")); - let _ = fs::remove_file(&lib_dst); - match fs::hard_link(dst, &lib_dst).or_else(|_| { - // if hard-link fails, just copy (ignoring the number of bytes written) - fs::copy(dst, &lib_dst).map(|_| ()) - }) { - Ok(_) => (), - Err(_) => { - return Err(Error::new( - ErrorKind::IOError, - "Could not copy or create a hard-link to the generated lib file.", - )); - } - }; - } else { - // Non-msvc targets (those using `ar`) need a separate step to add - // the symbol table to archives since our construction command of - // `cq` doesn't add it for us. - let mut ar = self.try_get_archiver()?; - // NOTE: We add `s` even if flags were passed using $ARFLAGS/ar_flag, because `s` - // here represents a _mode_, not an arbitrary flag. Further discussion of this choice - // can be seen in https://github.com/rust-lang/cc-rs/pull/763. - match deterministic_ar { - Some(false) => { - // See comment in `assemble_progressive` for more on ZERO_AR_DATE. - ar.env("ZERO_AR_DATE", "1"); - run(ar.arg("s").arg(dst), &self.cargo_output)?; - } - Some(true) => { - run(ar.arg("sD").arg(dst), &self.cargo_output)?; - } - None => { - if run_silent_on_error(ar.arg("sD").arg(dst), &self.cargo_output).is_err() { - let mut ar = self.try_get_archiver()?; - ar.env("ZERO_AR_DATE", "1"); - run(ar.arg("s").arg(dst), &self.cargo_output)?; - } - } - } - } - - Ok(()) - } - - fn assemble_progressive( - &self, - dst: &Path, - objs: &[&Path], - deterministic_ar: &mut Option, - ) -> Result<(), Error> { - let target = self.get_target()?; - - let (mut cmd, program, any_flags) = self.try_get_archiver_and_flags()?; - if target.env == "msvc" && !program.to_string_lossy().contains("llvm-ar") { - // NOTE: -out: here is an I/O flag, and so must be included even if $ARFLAGS/ar_flag is - // in use. -nologo on the other hand is just a regular flag, and one that we'll skip if - // the caller has explicitly dictated the flags they want. See - // https://github.com/rust-lang/cc-rs/pull/763 for further discussion. - let mut out = OsString::from("-out:"); - out.push(dst); - cmd.arg(out); - if !any_flags { - cmd.arg("-nologo"); - } - // If the library file already exists, add the library name - // as an argument to let lib.exe know we are appending the objs. - if dst.exists() { - cmd.arg(dst); - } - cmd.args(objs); - run(&mut cmd, &self.cargo_output)?; - } else { - // Set an environment variable to tell the OSX archiver to ensure - // that all dates listed in the archive are zero, improving - // determinism of builds. AFAIK there's not really official - // documentation of this but there's a lot of references to it if - // you search google. - // - // You can reproduce this locally on a mac with: - // - // $ touch foo.c - // $ cc -c foo.c -o foo.o - // - // # Notice that these two checksums are different - // $ ar crus libfoo1.a foo.o && sleep 2 && ar crus libfoo2.a foo.o - // $ md5sum libfoo*.a - // - // # Notice that these two checksums are the same - // $ export ZERO_AR_DATE=1 - // $ ar crus libfoo1.a foo.o && sleep 2 && touch foo.o && ar crus libfoo2.a foo.o - // $ md5sum libfoo*.a - // - // In any case if this doesn't end up getting read, it shouldn't - // cause that many issues! - cmd.env("ZERO_AR_DATE", "1"); - - // NOTE: We add cq here regardless of whether $ARFLAGS/ar_flag have been used because - // it dictates the _mode_ ar runs in, which the setter of $ARFLAGS/ar_flag can't - // dictate. See https://github.com/rust-lang/cc-rs/pull/763 for further discussion. - match *deterministic_ar { - Some(false) => { - run(cmd.arg("cq").arg(dst).args(objs), &self.cargo_output)?; - } - Some(true) => { - run(cmd.arg("cqD").arg(dst).args(objs), &self.cargo_output)?; - } - None => { - // Probe: try `D` and remember the result for later batches. - if run_silent_on_error(cmd.arg("cqD").arg(dst).args(objs), &self.cargo_output) - .is_ok() - { - *deterministic_ar = Some(true); - } else { - *deterministic_ar = Some(false); - let (mut cmd, _, _) = self.try_get_archiver_and_flags()?; - cmd.env("ZERO_AR_DATE", "1"); - run(cmd.arg("cq").arg(dst).args(objs), &self.cargo_output)?; - } - } - } - } - - Ok(()) - } - - fn apple_flags(&self, cmd: &mut Tool) -> Result<(), Error> { - let target = self.get_target()?; - - // This is a Darwin/Apple-specific flag that works both on GCC and Clang, but it is only - // necessary on GCC since we specify `-target` on Clang. - // https://gcc.gnu.org/onlinedocs/gcc/Darwin-Options.html#:~:text=arch - // https://clang.llvm.org/docs/CommandGuide/clang.html#cmdoption-arch - if cmd.is_like_gnu() { - let arch = map_darwin_target_from_rust_to_compiler_architecture(&target); - cmd.args.push("-arch".into()); - cmd.args.push(arch.into()); - } - - // Pass the deployment target via `-mmacosx-version-min=`, `-miphoneos-version-min=` and - // similar. Also necessary on GCC, as it forces a compilation error if the compiler is not - // configured for Darwin: https://gcc.gnu.org/onlinedocs/gcc/Darwin-Options.html - // - // On visionOS and Mac Catalyst, there is no -m*-version-min= flag: - // https://github.com/llvm/llvm-project/issues/88271 - // And the workaround to use `-mtargetos=` cannot be used with the `--target` flag that we - // otherwise specify. So we avoid emitting that, and put the version in `--target` instead. - if cmd.is_like_gnu() || !(target.os == "visionos" || target.env == "macabi") { - let min_version = self.apple_deployment_target(&target); - cmd.args - .push(target.apple_version_flag(&min_version).into()); - } - - // AppleClang sometimes requires sysroot even on macOS - if cmd.is_xctoolchain_clang() || target.os != "macos" { - self.cargo_output.print_metadata(&format_args!( - "Detecting {:?} SDK path for {}", - target.os, - target.apple_sdk_name(), - )); - let sdk_path = self.apple_sdk_root(&target)?; - - cmd.args.push("-isysroot".into()); - cmd.args.push(OsStr::new(&sdk_path).to_owned()); - cmd.env - .push(("SDKROOT".into(), OsStr::new(&sdk_path).to_owned())); - - if target.env == "macabi" { - // Mac Catalyst uses the macOS SDK, but to compile against and - // link to iOS-specific frameworks, we should have the support - // library stubs in the include and library search path. - let ios_support = Path::new(&sdk_path).join("System/iOSSupport"); - - cmd.args.extend([ - // Header search path - OsString::from("-isystem"), - ios_support.join("usr/include").into(), - // Framework header search path - OsString::from("-iframework"), - ios_support.join("System/Library/Frameworks").into(), - // Library search path - { - let mut s = OsString::from("-L"); - s.push(ios_support.join("usr/lib")); - s - }, - // Framework linker search path - { - // Technically, we _could_ avoid emitting `-F`, as - // `-iframework` implies it, but let's keep it in for - // clarity. - let mut s = OsString::from("-F"); - s.push(ios_support.join("System/Library/Frameworks")); - s - }, - ]); - } - } - - Ok(()) - } - - fn cmd>(&self, prog: P) -> Command { - let mut cmd = Command::new(prog); - for (a, b) in self.env.iter() { - cmd.env(a, b); - } - cmd - } - - fn prefer_clang(&self) -> bool { - if let Some(env) = cargo_env_var_os("CARGO_ENCODED_RUSTFLAGS") { - env.to_string_lossy().contains("linker-plugin-lto") - } else { - false - } - } - - fn get_base_compiler(&self) -> Result { - let out_dir = self.get_out_dir().ok(); - let out_dir = out_dir.as_deref(); - - if let Some(c) = &self.compiler { - return Ok(Tool::new( - (**c).to_owned(), - &self.build_cache.cached_compiler_family, - &self.cargo_output, - out_dir, - )); - } - let target = self.get_target()?; - let raw_target = self.get_raw_target()?; - - let msvc = if self.prefer_clang_cl_over_msvc { - "clang-cl.exe" - } else { - "cl.exe" - }; - - let (env, gnu, traditional, clang) = if self.cpp { - ("CXX", "g++", "c++", "clang++") - } else { - ("CC", "gcc", "cc", "clang") - }; - - let fallback = Cow::Borrowed(Path::new(traditional)); - let default = if cfg!(target_os = "solaris") || cfg!(target_os = "illumos") { - // On historical Solaris systems, "cc" may have been Sun Studio, which - // is not flag-compatible with "gcc". This history casts a long shadow, - // and many modern illumos distributions today ship GCC as "gcc" without - // also making it available as "cc". - Cow::Borrowed(Path::new(gnu)) - } else if self.prefer_clang() { - self.which(Path::new(clang), None) - .map(Cow::Owned) - .unwrap_or(fallback) - } else { - fallback - }; - - let cl_exe = self.find_msvc_tools_find_tool(&target, msvc); - - let tool_opt: Option = self - .env_tool(env) - .map(|(tool, wrapper, args)| { - // Chop off leading/trailing whitespace to work around - // semi-buggy build scripts which are shared in - // makefiles/configure scripts (where spaces are far more - // lenient) - let mut t = Tool::with_args( - tool, - args.clone(), - &self.build_cache.cached_compiler_family, - &self.cargo_output, - out_dir, - ); - if let Some(cc_wrapper) = wrapper { - t.cc_wrapper_path = Some(Path::new(&cc_wrapper).to_owned()); - } - for arg in args { - t.cc_wrapper_args.push(arg.into()); - } - t - }) - .or_else(|| { - if target.os == "emscripten" { - let tool = if self.cpp { "em++" } else { "emcc" }; - // Windows uses bat file so we have to be a bit more specific - if cfg!(windows) { - let mut t = Tool::with_family( - PathBuf::from("cmd"), - ToolFamily::Clang { zig_cc: false }, - ); - t.args.push("/c".into()); - t.args.push(format!("{tool}.bat").into()); - Some(t) - } else { - Some(Tool::new( - PathBuf::from(tool), - &self.build_cache.cached_compiler_family, - &self.cargo_output, - out_dir, - )) - } - } else { - None - } - }) - .or_else(|| cl_exe.clone()); - - let tool = match tool_opt { - Some(t) => t, - None => { - let compiler: PathBuf = if cfg!(windows) && target.os == "windows" { - if target.env == "msvc" { - msvc.into() - } else { - let cc = if target.abi == "llvm" { clang } else { gnu }; - format!("{cc}.exe").into() - } - } else if target.os == "ios" - || target.os == "watchos" - || target.os == "tvos" - || target.os == "visionos" - { - clang.into() - } else if target.os == "android" { - autodetect_android_compiler(&raw_target, gnu, clang) - } else if target.os == "cloudabi" { - format!( - "{}-{}-{}-{}", - target.full_arch, target.vendor, target.os, traditional - ) - .into() - } else if target.os == "wasi" { - self.autodetect_wasi_compiler(&raw_target, clang) - } else if target.arch == "wasm32" || target.arch == "wasm64" { - // Compiling WASM is not currently supported by GCC, so - // let's default to Clang. - clang.into() - } else if target.os == "vxworks" { - if self.cpp { "wr-c++" } else { "wr-cc" }.into() - } else if target.arch == "arm" && target.vendor == "kmc" { - format!("arm-kmc-eabi-{gnu}").into() - } else if target.arch == "aarch64" && target.vendor == "kmc" { - format!("aarch64-kmc-elf-{gnu}").into() - } else if target.os == "nto" { - // See for details: https://github.com/rust-lang/cc-rs/pull/1319 - if self.cpp { "q++" } else { "qcc" }.into() - } else if self.get_is_cross_compile()? { - let prefix = self.prefix_for_target(&raw_target); - match prefix { - Some(prefix) => { - let cc = if target.abi == "llvm" { clang } else { gnu }; - format!("{prefix}-{cc}").into() - } - None => default.into(), - } - } else { - default.into() - }; - - let mut t = Tool::new( - compiler, - &self.build_cache.cached_compiler_family, - &self.cargo_output, - out_dir, - ); - if let Some(cc_wrapper) = self.rustc_wrapper_fallback() { - t.cc_wrapper_path = Some(Path::new(&cc_wrapper).to_owned()); - } - t - } - }; - - let mut tool = if self.cuda { - assert!( - tool.args.is_empty(), - "CUDA compilation currently assumes empty pre-existing args" - ); - let nvcc = match self.getenv_with_target_prefixes("NVCC") { - Err(_) => PathBuf::from("nvcc"), - Ok(nvcc) => PathBuf::from(&*nvcc), - }; - let mut nvcc_tool = Tool::with_features( - nvcc, - vec![], - self.cuda, - &self.build_cache.cached_compiler_family, - &self.cargo_output, - out_dir, - ); - if self.ccbin { - nvcc_tool - .args - .push(format!("-ccbin={}", tool.path.display()).into()); - } - if let Some(cc_wrapper) = self.rustc_wrapper_fallback() { - nvcc_tool.cc_wrapper_path = Some(Path::new(&cc_wrapper).to_owned()); - } - nvcc_tool.family = tool.family; - nvcc_tool - } else { - tool - }; - - // New "standalone" C/C++ cross-compiler executables from recent Android NDK - // are just shell scripts that call main clang binary (from Android NDK) with - // proper `--target` argument. - // - // For example, armv7a-linux-androideabi16-clang passes - // `--target=armv7a-linux-androideabi16` to clang. - // - // As the shell script calls the main clang binary, the command line limit length - // on Windows is restricted to around 8k characters instead of around 32k characters. - // To remove this limit, we call the main clang binary directly and construct the - // `--target=` ourselves. - if cfg!(windows) && android_clang_compiler_uses_target_arg_internally(&tool.path) { - if let Some(path) = tool.path.file_name() { - let file_name = path.to_str().unwrap().to_owned(); - let (target, clang) = file_name.split_at(file_name.rfind('-').unwrap()); - - tool.has_internal_target_arg = true; - tool.path.set_file_name(clang.trim_start_matches('-')); - tool.path.set_extension("exe"); - tool.args.push(format!("--target={target}").into()); - - // Additionally, shell scripts for target i686-linux-android versions 16 to 24 - // pass the `mstackrealign` option so we do that here as well. - if target.contains("i686-linux-android") { - let (_, version) = target.split_at(target.rfind('d').unwrap() + 1); - if let Ok(version) = version.parse::() { - if version > 15 && version < 25 { - tool.args.push("-mstackrealign".into()); - } - } - } - }; - } - - // Under cross-compilation scenarios, llvm-mingw's clang executable is just a - // wrapper script that calls the actual clang binary with a suitable `--target` - // argument, much like the Android NDK case outlined above. Passing a target - // argument ourselves in this case will result in an error, as they expect - // targets like `x86_64-w64-mingw32`, and we can't always set such a target - // string because it is specific to this MinGW cross-compilation toolchain. - // - // For example, the following command will always fail due to using an unsuitable - // `--target` argument we'd otherwise pass: - // $ /opt/llvm-mingw-20250613-ucrt-ubuntu-22.04-x86_64/bin/x86_64-w64-mingw32-clang --target=x86_64-pc-windows-gnu dummy.c - // - // Code reference: - // https://github.com/mstorsjo/llvm-mingw/blob/a1f6413e5c21fd74b64137b56167f4fba500d1d8/wrappers/clang-target-wrapper.sh#L31 - if !cfg!(windows) && target.os == "windows" && is_llvm_mingw_wrapper(&tool.path) { - tool.has_internal_target_arg = true; - } - - // If we found `cl.exe` in our environment, the tool we're returning is - // an MSVC-like tool, *and* no env vars were set then set env vars for - // the tool that we're returning. - // - // Env vars are needed for things like `link.exe` being put into PATH as - // well as header include paths sometimes. These paths are automatically - // included by default but if the `CC` or `CXX` env vars are set these - // won't be used. This'll ensure that when the env vars are used to - // configure for invocations like `clang-cl` we still get a "works out - // of the box" experience. - if let Some(cl_exe) = cl_exe { - if tool.family == (ToolFamily::Msvc { clang_cl: true }) - && tool.env.is_empty() - && target.env == "msvc" - { - for (k, v) in cl_exe.env.iter() { - tool.env.push((k.to_owned(), v.to_owned())); - } - } - } - - if target.env == "msvc" && tool.family == ToolFamily::Gnu { - self.cargo_output - .print_warning(&"GNU compiler is not supported for this target"); - } - - Ok(tool) - } - - /// Returns a fallback `cc_compiler_wrapper` by introspecting `RUSTC_WRAPPER` - fn rustc_wrapper_fallback(&self) -> Option> { - // No explicit CC wrapper was detected, but check if RUSTC_WRAPPER - // is defined and is a build accelerator that is compatible with - // C/C++ compilers (e.g. sccache) - const VALID_WRAPPERS: &[&str] = &["sccache", "cachepot", "buildcache"]; - - let rustc_wrapper = cargo_env_var_os("RUSTC_WRAPPER")?; - let wrapper_path = Path::new(&rustc_wrapper); - let wrapper_stem = wrapper_path.file_stem()?; - - if VALID_WRAPPERS.contains(&wrapper_stem.to_str()?) { - Some(Cow::Owned(rustc_wrapper)) - } else { - None - } - } - - /// Returns compiler path, optional modifier name from whitelist, and arguments vec - fn env_tool(&self, name: &str) -> Option<(PathBuf, Option>, Vec)> { - let tool = self.getenv_with_target_prefixes(name).ok()?; - let tool = tool.to_string_lossy(); - let tool = tool.trim(); - - if tool.is_empty() { - return None; - } - - // If this is an exact path on the filesystem we don't want to do any - // interpretation at all, just pass it on through. This'll hopefully get - // us to support spaces-in-paths. - if let Some(exe) = check_exe(Path::new(tool).into()) { - return Some((exe, self.rustc_wrapper_fallback(), Vec::new())); - } - - // Ok now we want to handle a couple of scenarios. We'll assume from - // here on out that spaces are splitting separate arguments. Two major - // features we want to support are: - // - // CC='sccache cc' - // - // aka using `sccache` or any other wrapper/caching-like-thing for - // compilations. We want to know what the actual compiler is still, - // though, because our `Tool` API support introspection of it to see - // what compiler is in use. - // - // additionally we want to support - // - // CC='cc -flag' - // - // where the CC env var is used to also pass default flags to the C - // compiler. - // - // It's true that everything here is a bit of a pain, but apparently if - // you're not literally make or bash then you get a lot of bug reports. - let mut known_wrappers = vec![ - "ccache", - "distcc", - "sccache", - "icecc", - "cachepot", - "buildcache", - ]; - let custom_wrapper = self.get_env("CC_KNOWN_WRAPPER_CUSTOM"); - if custom_wrapper.is_some() { - known_wrappers.push(custom_wrapper.as_deref().unwrap().to_str().unwrap()); - } - - let mut parts = tool.split_whitespace(); - let maybe_wrapper = parts.next()?; - - let file_stem = Path::new(maybe_wrapper).file_stem()?.to_str()?; - if known_wrappers.contains(&file_stem) { - if let Some(compiler) = parts.next() { - return Some(( - compiler.into(), - Some(Cow::Owned(maybe_wrapper.into())), - parts.map(|s| s.to_string()).collect(), - )); - } - } - - Some(( - maybe_wrapper.into(), - self.rustc_wrapper_fallback(), - parts.map(|s| s.to_string()).collect(), - )) - } - - /// Returns the C++ standard library: - /// 1. If [`cpp_link_stdlib`](cc::Build::cpp_link_stdlib) is set, uses its value. - /// 2. Else if the `CXXSTDLIB` environment variable is set, uses its value. - /// 3. Else the default is `c++` for OS X and BSDs, `c++_shared` for Android, - /// `None` for MSVC and `stdc++` for anything else. - fn get_cpp_link_stdlib(&self) -> Result>, Error> { - match &self.cpp_link_stdlib { - Some(s) => Ok(s.as_deref().map(Path::new).map(Cow::Borrowed)), - None => { - if let Ok(stdlib) = self.getenv_with_target_prefixes("CXXSTDLIB") { - if stdlib.is_empty() { - Ok(None) - } else { - Ok(Some(Cow::Owned(Path::new(&stdlib).to_owned()))) - } - } else { - let target = self.get_target()?; - if target.env == "msvc" { - Ok(None) - } else if target.vendor == "apple" - || target.os == "freebsd" - || target.os == "openbsd" - || target.os == "aix" - || (target.os == "linux" && target.env == "ohos") - || target.os == "wasi" - { - Ok(Some(Cow::Borrowed(Path::new("c++")))) - } else if target.os == "android" { - Ok(Some(Cow::Borrowed(Path::new("c++_shared")))) - } else { - Ok(Some(Cow::Borrowed(Path::new("stdc++")))) - } - } - } - } - } - - /// Get the archiver (ar) that's in use for this configuration. - /// - /// You can use [`Command::get_program`] to get just the path to the command. - /// - /// This method will take into account all configuration such as debug - /// information, optimization level, include directories, defines, etc. - /// Additionally, the compiler binary in use follows the standard - /// conventions for this path, e.g. looking at the explicitly set compiler, - /// environment variables (a number of which are inspected here), and then - /// falling back to the default configuration. - /// - /// # Panics - /// - /// Panics if an error occurred while determining the architecture. - pub fn get_archiver(&self) -> Command { - match self.try_get_archiver() { - Ok(tool) => tool, - Err(e) => fail(&e.message), - } - } - - /// Get the archiver that's in use for this configuration. - /// - /// This will return a result instead of panicking; - /// see [`Self::get_archiver`] for the complete description. - pub fn try_get_archiver(&self) -> Result { - Ok(self.try_get_archiver_and_flags()?.0) - } - - fn try_get_archiver_and_flags(&self) -> Result<(Command, PathBuf, bool), Error> { - let (mut cmd, name) = self.get_base_archiver()?; - let mut any_flags = false; - if let Some(flags) = self.envflags("ARFLAGS")? { - any_flags = true; - cmd.args(flags); - } - for flag in &self.ar_flags { - any_flags = true; - cmd.arg(&**flag); - } - Ok((cmd, name, any_flags)) - } - - fn get_base_archiver(&self) -> Result<(Command, PathBuf), Error> { - if let Some(ref a) = self.archiver { - let archiver = &**a; - return Ok((self.cmd(archiver), archiver.into())); - } - - self.get_base_archiver_variant("AR", "ar") - } - - /// Get the ranlib that's in use for this configuration. - /// - /// You can use [`Command::get_program`] to get just the path to the command. - /// - /// This method will take into account all configuration such as debug - /// information, optimization level, include directories, defines, etc. - /// Additionally, the compiler binary in use follows the standard - /// conventions for this path, e.g. looking at the explicitly set compiler, - /// environment variables (a number of which are inspected here), and then - /// falling back to the default configuration. - /// - /// # Panics - /// - /// Panics if an error occurred while determining the architecture. - pub fn get_ranlib(&self) -> Command { - match self.try_get_ranlib() { - Ok(tool) => tool, - Err(e) => fail(&e.message), - } - } - - /// Get the ranlib that's in use for this configuration. - /// - /// This will return a result instead of panicking; - /// see [`Self::get_ranlib`] for the complete description. - pub fn try_get_ranlib(&self) -> Result { - let mut cmd = self.get_base_ranlib()?; - if let Some(flags) = self.envflags("RANLIBFLAGS")? { - cmd.args(flags); - } - Ok(cmd) - } - - fn get_base_ranlib(&self) -> Result { - if let Some(ref r) = self.ranlib { - return Ok(self.cmd(&**r)); - } - - Ok(self.get_base_archiver_variant("RANLIB", "ranlib")?.0) - } - - fn get_base_archiver_variant( - &self, - env: &str, - tool: &str, - ) -> Result<(Command, PathBuf), Error> { - let target = self.get_target()?; - let mut name = PathBuf::new(); - let tool_opt: Option = self - .env_tool(env) - .map(|(tool, _wrapper, args)| { - name.clone_from(&tool); - let mut cmd = self.cmd(tool); - cmd.args(args); - cmd - }) - .or_else(|| { - if target.os == "emscripten" { - // Windows use bat files so we have to be a bit more specific - if cfg!(windows) { - let mut cmd = self.cmd("cmd"); - name = format!("em{tool}.bat").into(); - cmd.arg("/c").arg(&name); - Some(cmd) - } else { - name = format!("em{tool}").into(); - Some(self.cmd(&name)) - } - } else if target.arch == "wasm32" || target.arch == "wasm64" { - // Formally speaking one should be able to use this approach, - // parsing -print-search-dirs output, to cover all clang targets, - // including Android SDKs and other cross-compilation scenarios... - // And even extend it to gcc targets by searching for "ar" instead - // of "llvm-ar"... - let compiler = self.get_base_compiler().ok()?; - if compiler.is_like_clang() { - name = format!("llvm-{tool}").into(); - self.search_programs(&compiler.path, &name, &self.cargo_output) - .map(|name| self.cmd(name)) - } else { - None - } - } else { - None - } - }); - - let tool = match tool_opt { - Some(t) => t, - None => { - if target.os == "android" { - name = format!("llvm-{tool}").into(); - match Command::new(&name).arg("--version").status() { - Ok(status) if status.success() => (), - _ => { - // FIXME: Use parsed target. - let raw_target = self.get_raw_target()?; - name = format!("{}-{}", raw_target.replace("armv7", "arm"), tool).into() - } - } - self.cmd(&name) - } else if target.env == "msvc" { - // NOTE: There isn't really a ranlib on msvc, so arguably we should return - // `None` somehow here. But in general, callers will already have to be aware - // of not running ranlib on Windows anyway, so it feels okay to return lib.exe - // here. - - let compiler = self.get_base_compiler()?; - let lib = if compiler.family == (ToolFamily::Msvc { clang_cl: true }) { - self.search_programs( - &compiler.path, - Path::new("llvm-lib"), - &self.cargo_output, - ) - .or_else(|| { - // See if there is 'llvm-lib' next to 'clang-cl' - if let Some(mut cmd) = self.which(&compiler.path, None) { - cmd.pop(); - cmd.push("llvm-lib"); - self.which(&cmd, None) - } else { - None - } - }) - } else { - None - }; - - if let Some(lib) = lib { - name = lib; - self.cmd(&name) - } else { - name = PathBuf::from("lib.exe"); - let mut cmd = match self.find_msvc_tools_find(&target, "lib.exe") { - Some(t) => t, - None => self.cmd("lib.exe"), - }; - if target.full_arch == "arm64ec" { - cmd.arg("/machine:arm64ec"); - } - cmd - } - } else if target.os == "illumos" { - // The default 'ar' on illumos uses a non-standard flags, - // but the OS comes bundled with a GNU-compatible variant. - // - // Use the GNU-variant to match other Unix systems. - name = format!("g{tool}").into(); - self.cmd(&name) - } else if target.os == "vxworks" { - name = format!("wr-{tool}").into(); - self.cmd(&name) - } else if target.os == "nto" { - // Ref: https://www.qnx.com/developers/docs/8.0/com.qnx.doc.neutrino.utilities/topic/a/ar.html - name = match target.full_arch { - "i586" => format!("ntox86-{tool}").into(), - "x86" | "aarch64" | "x86_64" => { - format!("nto{}-{}", target.arch, tool).into() - } - _ => { - return Err(Error::new( - ErrorKind::InvalidTarget, - format!("Unknown architecture for Neutrino QNX: {}", target.arch), - )) - } - }; - self.cmd(&name) - } else if self.get_is_cross_compile()? { - match self.prefix_for_target(&self.get_raw_target()?) { - Some(prefix) => { - // GCC uses $target-gcc-ar, whereas binutils uses $target-ar -- try both. - // Prefer -ar if it exists, as builds of `-gcc-ar` have been observed to be - // outright broken (such as when targeting freebsd with `--disable-lto` - // toolchain where the archiver attempts to load the LTO plugin anyway but - // fails to find one). - // - // The same applies to ranlib. - let chosen = ["", "-gcc"] - .iter() - .filter_map(|infix| { - let target_p = format!("{prefix}{infix}-{tool}"); - let status = Command::new(&target_p) - .arg("--version") - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .status() - .ok()?; - status.success().then_some(target_p) - }) - .next() - .unwrap_or_else(|| tool.to_string()); - name = chosen.into(); - self.cmd(&name) - } - None => { - name = tool.into(); - self.cmd(&name) - } - } - } else { - name = tool.into(); - self.cmd(&name) - } - } - }; - - Ok((tool, name)) - } - - // FIXME: Use parsed target instead of raw target. - fn prefix_for_target(&self, target: &str) -> Option> { - // CROSS_COMPILE is of the form: "arm-linux-gnueabi-" - self.get_env("CROSS_COMPILE") - .as_deref() - .map(|s| s.to_string_lossy().trim_end_matches('-').to_owned()) - .map(Cow::Owned) - .or_else(|| { - // Put aside RUSTC_LINKER's prefix to be used as second choice, after CROSS_COMPILE - cargo_env_var_os("RUSTC_LINKER").and_then(|var| { - var.to_string_lossy() - .strip_suffix("-gcc") - .map(str::to_string) - .map(Cow::Owned) - }) - }) - .or_else(|| { - match target { - // Note: there is no `aarch64-pc-windows-gnu` target, only `-gnullvm` - "aarch64-pc-windows-gnullvm" => Some("aarch64-w64-mingw32"), - "aarch64-uwp-windows-gnu" => Some("aarch64-w64-mingw32"), - "aarch64-unknown-helenos" => Some("aarch64-helenos"), - "aarch64-unknown-linux-gnu" => Some("aarch64-linux-gnu"), - "aarch64-unknown-linux-musl" => Some("aarch64-linux-musl"), - "aarch64-unknown-linux-relibc" => Some("aarch64-linux-relibc"), - "aarch64-unknown-netbsd" => Some("aarch64--netbsd"), - "arm-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"), - "armv4t-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"), - "armv5te-unknown-helenos-eabi" => Some("arm-helenos"), - "armv5te-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"), - "armv5te-unknown-linux-musleabi" => Some("arm-linux-gnueabi"), - "arm-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), - "arm-unknown-linux-musleabi" => Some("arm-linux-musleabi"), - "arm-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), - "arm-unknown-netbsd-eabi" => Some("arm--netbsdelf-eabi"), - "armv6-unknown-netbsd-eabihf" => Some("armv6--netbsdelf-eabihf"), - "armv7-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"), - "armv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), - "armv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), - "armv7neon-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), - "armv7neon-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), - "thumbv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), - "thumbv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), - "thumbv7neon-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), - "thumbv7neon-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), - "armv7-unknown-netbsd-eabihf" => Some("armv7--netbsdelf-eabihf"), - "hexagon-unknown-linux-musl" => Some("hexagon-linux-musl"), - "i586-unknown-linux-musl" => Some("musl"), - "i686-pc-windows-gnu" => Some("i686-w64-mingw32"), - "i686-pc-windows-gnullvm" => Some("i686-w64-mingw32"), - "i686-uwp-windows-gnu" => Some("i686-w64-mingw32"), - "i686-unknown-helenos" => Some("i686-helenos"), - "i686-unknown-linux-gnu" => self.find_working_gnu_prefix(&[ - "i686-linux-gnu", - "x86_64-linux-gnu", // transparently support gcc-multilib - ]), // explicit None if not found, so caller knows to fall back - "i686-unknown-linux-musl" => Some("musl"), - "i686-unknown-netbsd" => Some("i486--netbsdelf"), - "loongarch64-unknown-linux-gnu" => Some("loongarch64-linux-gnu"), - "m68k-unknown-linux-gnu" => Some("m68k-linux-gnu"), - "mips-unknown-linux-gnu" => Some("mips-linux-gnu"), - "mips-unknown-linux-musl" => Some("mips-linux-musl"), - "mipsel-unknown-linux-gnu" => Some("mipsel-linux-gnu"), - "mipsel-unknown-linux-musl" => Some("mipsel-linux-musl"), - "mips64-unknown-linux-gnuabi64" => Some("mips64-linux-gnuabi64"), - "mips64el-unknown-linux-gnuabi64" => Some("mips64el-linux-gnuabi64"), - "mipsisa32r6-unknown-linux-gnu" => Some("mipsisa32r6-linux-gnu"), - "mipsisa32r6el-unknown-linux-gnu" => Some("mipsisa32r6el-linux-gnu"), - "mipsisa64r6-unknown-linux-gnuabi64" => Some("mipsisa64r6-linux-gnuabi64"), - "mipsisa64r6el-unknown-linux-gnuabi64" => Some("mipsisa64r6el-linux-gnuabi64"), - "powerpc-unknown-helenos" => Some("ppc-helenos"), - "powerpc-unknown-linux-gnu" => Some("powerpc-linux-gnu"), - "powerpc-unknown-linux-gnuspe" => Some("powerpc-linux-gnuspe"), - "powerpc-unknown-netbsd" => Some("powerpc--netbsd"), - "powerpc64-unknown-linux-gnu" => Some("powerpc64-linux-gnu"), - "powerpc64le-unknown-linux-gnu" => Some("powerpc64le-linux-gnu"), - "riscv32i-unknown-none-elf" => self.find_working_gnu_prefix(&[ - "riscv32-unknown-elf", - "riscv64-unknown-elf", - "riscv-none-embed", - ]), - "riscv32im-unknown-none-elf" => self.find_working_gnu_prefix(&[ - "riscv32-unknown-elf", - "riscv64-unknown-elf", - "riscv-none-embed", - ]), - "riscv32imac-esp-espidf" => Some("riscv32-esp-elf"), - "riscv32imac-unknown-none-elf" => self.find_working_gnu_prefix(&[ - "riscv32-unknown-elf", - "riscv64-unknown-elf", - "riscv-none-embed", - ]), - "riscv32imafc-unknown-none-elf" => self.find_working_gnu_prefix(&[ - "riscv32-unknown-elf", - "riscv64-unknown-elf", - "riscv-none-embed", - ]), - "riscv32imac-unknown-xous-elf" => self.find_working_gnu_prefix(&[ - "riscv32-unknown-elf", - "riscv64-unknown-elf", - "riscv-none-embed", - ]), - "riscv32imc-esp-espidf" => Some("riscv32-esp-elf"), - "riscv32imc-unknown-none-elf" => self.find_working_gnu_prefix(&[ - "riscv32-unknown-elf", - "riscv64-unknown-elf", - "riscv-none-embed", - ]), - "riscv64gc-unknown-none-elf" => self.find_working_gnu_prefix(&[ - "riscv64-unknown-elf", - "riscv32-unknown-elf", - "riscv-none-embed", - ]), - "riscv64imac-unknown-none-elf" => self.find_working_gnu_prefix(&[ - "riscv64-unknown-elf", - "riscv32-unknown-elf", - "riscv-none-embed", - ]), - "riscv64gc-unknown-linux-gnu" => Some("riscv64-linux-gnu"), - "riscv64a23-unknown-linux-gnu" => Some("riscv64-linux-gnu"), - "riscv32gc-unknown-linux-gnu" => Some("riscv32-linux-gnu"), - "riscv64gc-unknown-linux-musl" => Some("riscv64-linux-musl"), - "riscv32gc-unknown-linux-musl" => Some("riscv32-linux-musl"), - "riscv64gc-unknown-netbsd" => Some("riscv64--netbsd"), - "s390x-unknown-linux-gnu" => Some("s390x-linux-gnu"), - "sparc-unknown-linux-gnu" => Some("sparc-linux-gnu"), - "sparc64-unknown-helenos" => Some("sparc64-helenos"), - "sparc64-unknown-linux-gnu" => Some("sparc64-linux-gnu"), - "sparc64-unknown-netbsd" => Some("sparc64--netbsd"), - "sparcv9-sun-solaris" => Some("sparcv9-sun-solaris"), - "armv7a-none-eabi" => Some("arm-none-eabi"), - "armv7a-none-eabihf" => Some("arm-none-eabi"), - "armebv7r-none-eabi" => Some("arm-none-eabi"), - "armebv7r-none-eabihf" => Some("arm-none-eabi"), - "armv7r-none-eabi" => Some("arm-none-eabi"), - "armv7r-none-eabihf" => Some("arm-none-eabi"), - "armv8r-none-eabihf" => Some("arm-none-eabi"), - "thumbv6m-none-eabi" => Some("arm-none-eabi"), - "thumbv7em-none-eabi" => Some("arm-none-eabi"), - "thumbv7em-none-eabihf" => Some("arm-none-eabi"), - "thumbv7m-none-eabi" => Some("arm-none-eabi"), - "thumbv8m.base-none-eabi" => Some("arm-none-eabi"), - "thumbv8m.main-none-eabi" => Some("arm-none-eabi"), - "thumbv8m.main-none-eabihf" => Some("arm-none-eabi"), - "x86_64-pc-windows-gnu" => Some("x86_64-w64-mingw32"), - "x86_64-pc-windows-gnullvm" => Some("x86_64-w64-mingw32"), - "x86_64-uwp-windows-gnu" => Some("x86_64-w64-mingw32"), - "x86_64-rumprun-netbsd" => Some("x86_64-rumprun-netbsd"), - "x86_64-unknown-helenos" => Some("amd64-helenos"), - "x86_64-unknown-linux-gnu" => self.find_working_gnu_prefix(&[ - "x86_64-linux-gnu", // rustfmt wrap - ]), // explicit None if not found, so caller knows to fall back - "x86_64-unknown-linux-musl" => { - self.find_working_gnu_prefix(&["x86_64-linux-musl", "musl"]) - } - "x86_64-unknown-linux-relibc" => { - self.find_working_gnu_prefix(&["x86_64-linux-relibc", "relibc"]) - } - "x86_64-unknown-netbsd" => Some("x86_64--netbsd"), - "xtensa-esp32-espidf" - | "xtensa-esp32-none-elf" - | "xtensa-esp32s2-espidf" - | "xtensa-esp32s2-none-elf" - | "xtensa-esp32s3-espidf" - | "xtensa-esp32s3-none-elf" => Some("xtensa-esp-elf"), - _ => None, - } - .map(Cow::Borrowed) - }) - } - - /// Some platforms have multiple, compatible, canonical prefixes. Look through - /// each possible prefix for a compiler that exists and return it. The prefixes - /// should be ordered from most-likely to least-likely. - fn find_working_gnu_prefix(&self, prefixes: &[&'static str]) -> Option<&'static str> { - let suffix = if self.cpp { "-g++" } else { "-gcc" }; - let extension = std::env::consts::EXE_SUFFIX; - - // Loop through PATH entries searching for each toolchain. This ensures that we - // are more likely to discover the toolchain early on, because chances are good - // that the desired toolchain is in one of the higher-priority paths. - self.get_env("PATH") - .as_ref() - .and_then(|path_entries| { - env::split_paths(path_entries).find_map(|path_entry| { - for prefix in prefixes { - let target_compiler = format!("{prefix}{suffix}{extension}"); - if path_entry.join(&target_compiler).exists() { - return Some(prefix); - } - } - None - }) - }) - .copied() - // If no toolchain was found, provide the first toolchain that was passed in. - // This toolchain has been shown not to exist, however it will appear in the - // error that is shown to the user which should make it easier to search for - // where it should be obtained. - .or_else(|| prefixes.first().copied()) - } - - fn get_target(&self) -> Result, Error> { - match &self.target { - Some(t) if Some(OsStr::new(&**t)) != cargo_env_var_os("TARGET").as_deref() => { - TargetInfo::from_rustc_target(t) - } - // Fetch target information from environment if not set, or if the - // target was the same as the TARGET environment variable, in - // case the user did `build.target(&env::var("TARGET").unwrap())`. - _ => self - .build_cache - .target_info_parser - .parse_from_cargo_environment_variables(), - } - } - - fn get_raw_target(&self) -> Result, Error> { - match &self.target { - Some(t) => Ok(Cow::Borrowed(t)), - None => cargo_env_var("TARGET").map(Cow::Owned), - } - } - - fn get_is_cross_compile(&self) -> Result { - let target = self.get_raw_target()?; - let host: Cow<'_, str> = match &self.host { - Some(h) => Cow::Borrowed(h), - None => Cow::Owned(cargo_env_var("HOST")?), - }; - Ok(host != target) - } - - fn get_opt_level(&self) -> Result, Error> { - match &self.opt_level { - Some(ol) => Ok(Cow::Borrowed(ol)), - None => cargo_env_var("OPT_LEVEL").map(Cow::Owned), - } - } - - /// Returns true if *any* debug info is enabled. - /// - /// [`get_debug_str`] provides more detail. - fn get_debug(&self) -> bool { - match self.get_debug_str() { - Err(_) => false, - Ok(d) => match &*d { - // From https://doc.rust-lang.org/cargo/reference/profiles.html#debug - "" | "0" | "false" | "none" => false, - _ => true, - }, - } - } - - fn get_debug_str(&self) -> Result, Error> { - match &self.debug { - Some(d) => Ok(Cow::Borrowed(d)), - None => cargo_env_var("DEBUG").map(Cow::Owned), - } - } - - fn get_shell_escaped_flags(&self) -> bool { - self.shell_escaped_flags - .unwrap_or_else(|| self.get_env_boolean("CC_SHELL_ESCAPED_FLAGS")) - } - - fn get_dwarf_version(&self) -> Option { - // Tentatively matches the DWARF version defaults as of rustc 1.62. - let target = self.get_target().ok()?; - if matches!( - target.os, - "android" | "dragonfly" | "freebsd" | "netbsd" | "openbsd" - ) || target.vendor == "apple" - || (target.os == "windows" && target.env == "gnu") - { - Some(2) - } else if target.os == "linux" { - Some(4) - } else { - None - } - } - - fn get_force_frame_pointer(&self) -> bool { - self.force_frame_pointer.unwrap_or_else(|| self.get_debug()) - } - - fn get_out_dir(&self) -> Result, Error> { - match &self.out_dir { - Some(p) => Ok(Cow::Borrowed(&**p)), - None => cargo_env_var_os("OUT_DIR") - .map(PathBuf::from) - .map(Cow::Owned) - .ok_or_else(|| { - Error::new( - ErrorKind::EnvVarNotFound, - "Environment variable OUT_DIR not defined.", - ) - }), - } - } - - /// Look up an environment variable, and tell Cargo that we used it. - fn get_env(&self, v: &str) -> Option { - // Excluding `PATH` prevents spurious rebuilds on Windows, see - // for details. - if self.emit_rerun_if_env_changed && v != "PATH" { - self.cargo_output - .print_metadata(&format_args!("cargo:rerun-if-env-changed={v}")); - } - #[allow(clippy::disallowed_methods)] // We emit rerun-if-env-changed above - let r = env::var_os(v); - self.cargo_output.print_metadata(&format_args!( - "{} = {}", - v, - OptionOsStrDisplay(r.as_deref()) - )); - r - } - - /// Look up an environment variable that's allowed to be overwritten by - /// [`Build::env`]. - /// - /// This is useful for environment variables that the compiler could - /// reasonably read, such as `SDKROOT` and `WASI_SDK_PATH` - for these, we - /// generally want to allow build scripts to overwrite them. - /// - /// On the other hand, we don't want to allow overwriting environment - /// variables that are `CC`-specific such as `CC_FORCE_DISABLE` - /// (`Build::env` applies to child processes, not to `cc` itself). - fn get_env_overridable(&self, key: &str) -> Option> { - // Try to look up in overrides first. - if let Some((_key, val)) = self.env.iter().find(|(k, _)| k.as_ref() == key) { - return Some(Cow::Borrowed(&**val)); - } - - // If not found in overrides, look up from environment. - self.get_env(key).map(Cow::Owned) - } - - /// Get boolean flag that is either true or false. - /// - /// Used for `CC_*`-style flags. - fn get_env_boolean(&self, key: &str) -> bool { - match self.get_env(key) { - // Set -> `true`, unless set to `""`, `"0"`, `"no"` `"false"` - Some(s) => &*s != "0" && &*s != "false" && &*s != "no" && !s.is_empty(), - // Not set -> default to `false`. - None => false, - } - } - - /// The list of environment variables to check for a given env, in order of priority. - fn target_envs(&self, env: &str) -> Result<[String; 4], Error> { - let target = self.get_raw_target()?; - let kind = if self.get_is_cross_compile()? { - "TARGET" - } else { - "HOST" - }; - let target_u = target.replace(['-', '.'], "_"); - - Ok([ - format!("{env}_{target}"), - format!("{env}_{target_u}"), - format!("{kind}_{env}"), - env.to_string(), - ]) - } - - /// Get a single-valued environment variable with target variants. - fn getenv_with_target_prefixes(&self, env: &str) -> Result { - // Take from first environment variable in the environment. - let res = self - .target_envs(env)? - .iter() - .filter_map(|env| self.get_env(env)) - .next(); - - match res { - Some(res) => Ok(res), - None => Err(Error::new( - ErrorKind::EnvVarNotFound, - format!("could not find environment variable {env}"), - )), - } - } - - /// Get values from CFLAGS-style environment variable. - fn envflags(&self, env: &str) -> Result>, Error> { - // Collect from all environment variables, in reverse order as in - // `getenv_with_target_prefixes` precedence (so that `CFLAGS_$TARGET` - // can override flags in `TARGET_CFLAGS`, which overrides those in - // `CFLAGS`). - let mut any_set = false; - let mut res = vec![]; - for env in self.target_envs(env)?.iter().rev() { - if let Some(var) = self.get_env(env) { - any_set = true; - - let var = var.to_string_lossy(); - if self.get_shell_escaped_flags() { - res.extend(Shlex::new(&var)); - } else { - res.extend(var.split_ascii_whitespace().map(ToString::to_string)); - } - } - } - - Ok(if any_set { Some(res) } else { None }) - } - - /// Returns true if `cc` has been disabled by `CC_FORCE_DISABLE`. - fn is_disabled(&self) -> bool { - self.get_env_boolean("CC_FORCE_DISABLE") - } - - fn fix_env_for_apple_os(&self, cmd: &mut Command) -> Result<(), Error> { - let target = self.get_target()?; - if cfg!(target_os = "macos") && target.os == "macos" { - // Additionally, `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at - // "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld", - // although this is apparently ignored when using the linker at "/usr/bin/ld". - cmd.env_remove("IPHONEOS_DEPLOYMENT_TARGET"); - } - Ok(()) - } - - fn apple_sdk_root_inner(&self, sdk: &str) -> Result, Error> { - // Code copied from rustc's compiler/rustc_codegen_ssa/src/back/link.rs. - if let Some(sdkroot) = self.get_env_overridable("SDKROOT") { - let p = Path::new(&sdkroot); - let does_sdkroot_contain = |strings: &[&str]| { - let sdkroot_str = p.to_string_lossy(); - strings.iter().any(|s| sdkroot_str.contains(s)) - }; - match sdk { - // Ignore `SDKROOT` if it's clearly set for the wrong platform. - "appletvos" - if does_sdkroot_contain(&["TVSimulator.platform", "MacOSX.platform"]) => {} - "appletvsimulator" - if does_sdkroot_contain(&["TVOS.platform", "MacOSX.platform"]) => {} - "iphoneos" - if does_sdkroot_contain(&["iPhoneSimulator.platform", "MacOSX.platform"]) => {} - "iphonesimulator" - if does_sdkroot_contain(&["iPhoneOS.platform", "MacOSX.platform"]) => {} - "macosx10.15" - if does_sdkroot_contain(&["iPhoneOS.platform", "iPhoneSimulator.platform"]) => { - } - "watchos" - if does_sdkroot_contain(&["WatchSimulator.platform", "MacOSX.platform"]) => {} - "watchsimulator" - if does_sdkroot_contain(&["WatchOS.platform", "MacOSX.platform"]) => {} - "xros" if does_sdkroot_contain(&["XRSimulator.platform", "MacOSX.platform"]) => {} - "xrsimulator" if does_sdkroot_contain(&["XROS.platform", "MacOSX.platform"]) => {} - // Ignore `SDKROOT` if it's not a valid path. - _ if !p.is_absolute() || p == Path::new("/") || !p.exists() => {} - _ => return Ok(sdkroot), - } - } - - let sdk_path = run_output( - self.cmd("xcrun") - .arg("--show-sdk-path") - .arg("--sdk") - .arg(sdk), - &self.cargo_output, - )?; - - let sdk_path = match String::from_utf8(sdk_path) { - Ok(p) => p, - Err(_) => { - return Err(Error::new( - ErrorKind::IOError, - "Unable to determine Apple SDK path.", - )); - } - }; - Ok(Cow::Owned(sdk_path.trim().into())) - } - - fn apple_sdk_root(&self, target: &TargetInfo<'_>) -> Result, Error> { - let sdk = target.apple_sdk_name(); - - if let Some(ret) = self - .build_cache - .apple_sdk_root_cache - .read() - .expect("apple_sdk_root_cache lock failed") - .get(sdk) - .cloned() - { - return Ok(ret); - } - let sdk_path: Arc = self.apple_sdk_root_inner(sdk)?.into(); - self.build_cache - .apple_sdk_root_cache - .write() - .expect("apple_sdk_root_cache lock failed") - .insert(sdk.into(), sdk_path.clone()); - Ok(sdk_path) - } - - fn apple_deployment_target(&self, target: &TargetInfo<'_>) -> Arc { - let sdk = target.apple_sdk_name(); - if let Some(ret) = self - .build_cache - .apple_versions_cache - .read() - .expect("apple_versions_cache lock failed") - .get(sdk) - .cloned() - { - return ret; - } - - let default_deployment_from_sdk = || -> Option> { - let version = run_output( - self.cmd("xcrun") - .arg("--show-sdk-version") - .arg("--sdk") - .arg(sdk), - &self.cargo_output, - ) - .ok()?; - - Some(Arc::from(std::str::from_utf8(&version).ok()?.trim())) - }; - - let deployment_from_env = |name: &str| -> Option> { - self.get_env_overridable(name)?.to_str().map(Arc::from) - }; - - // Determines if the acquired deployment target is too low to support modern C++ on some Apple platform. - // - // A long time ago they used libstdc++, but since macOS 10.9 and iOS 7 libc++ has been the library the SDKs provide to link against. - // If a `cc`` config wants to use C++, we round up to these versions as the baseline. - let maybe_cpp_version_baseline = |deployment_target_ver: Arc| -> Option> { - if !self.cpp { - return Some(deployment_target_ver); - } - - let mut deployment_target = deployment_target_ver - .split('.') - .map(|v| v.parse::().expect("integer version")); - - match target.os { - "macos" => { - let major = deployment_target.next().unwrap_or(0); - let minor = deployment_target.next().unwrap_or(0); - - // If below 10.9, we ignore it and let the SDK's target definitions handle it. - if major == 10 && minor < 9 { - self.cargo_output.print_warning(&format_args!( - "macOS deployment target ({deployment_target_ver}) too low, it will be increased" - )); - return None; - } - } - "ios" => { - let major = deployment_target.next().unwrap_or(0); - - // If below 10.7, we ignore it and let the SDK's target definitions handle it. - if major < 7 { - self.cargo_output.print_warning(&format_args!( - "iOS deployment target ({deployment_target_ver}) too low, it will be increased" - )); - return None; - } - } - // watchOS, tvOS, visionOS, and others are all new enough that libc++ is their baseline. - _ => {} - } - - // If the deployment target met or exceeded the C++ baseline - Some(deployment_target_ver) - }; - - // The hardcoded minimums here are subject to change in a future compiler release, - // and only exist as last resort fallbacks. Don't consider them stable. - // `cc` doesn't use rustc's `--print deployment-target`` because the compiler's defaults - // don't align well with Apple's SDKs and other third-party libraries that require ~generally~ higher - // deployment targets. rustc isn't interested in those by default though so its fine to be different here. - // - // If no explicit target is passed, `cc` defaults to the current Xcode SDK's `DefaultDeploymentTarget` for better - // compatibility. This is also the crate's historical behavior and what has become a relied-on value. - // - // The ordering of env -> XCode SDK -> old rustc defaults is intentional for performance when using - // an explicit target. - let version: Arc = match target.os { - "macos" => deployment_from_env("MACOSX_DEPLOYMENT_TARGET") - .and_then(maybe_cpp_version_baseline) - .or_else(default_deployment_from_sdk) - .unwrap_or_else(|| { - if target.arch == "aarch64" { - "11.0".into() - } else { - let default: Arc = Arc::from("10.7"); - maybe_cpp_version_baseline(default.clone()).unwrap_or(default) - } - }), - - "ios" => deployment_from_env("IPHONEOS_DEPLOYMENT_TARGET") - .and_then(maybe_cpp_version_baseline) - .or_else(default_deployment_from_sdk) - .unwrap_or_else(|| "7.0".into()), - - "watchos" => deployment_from_env("WATCHOS_DEPLOYMENT_TARGET") - .or_else(default_deployment_from_sdk) - .unwrap_or_else(|| "5.0".into()), - - "tvos" => deployment_from_env("TVOS_DEPLOYMENT_TARGET") - .or_else(default_deployment_from_sdk) - .unwrap_or_else(|| "9.0".into()), - - "visionos" => deployment_from_env("XROS_DEPLOYMENT_TARGET") - .or_else(default_deployment_from_sdk) - .unwrap_or_else(|| "1.0".into()), - - os => unreachable!("unknown Apple OS: {}", os), - }; - - self.build_cache - .apple_versions_cache - .write() - .expect("apple_versions_cache lock failed") - .insert(sdk.into(), version.clone()); - - version - } - - fn wasm_musl_sysroot(&self) -> Result { - if let Some(musl_sysroot_path) = self.get_env("WASM_MUSL_SYSROOT") { - Ok(musl_sysroot_path) - } else { - Err(Error::new( - ErrorKind::EnvVarNotFound, - "Environment variable WASM_MUSL_SYSROOT not defined for wasm32. Download sysroot from GitHub & setup environment variable MUSL_SYSROOT targeting the folder.", - )) - } - } - - fn wasi_sysroot(&self) -> Result { - if let Some(wasi_sysroot_path) = self.get_env("WASI_SYSROOT") { - Ok(wasi_sysroot_path) - } else { - Err(Error::new( - ErrorKind::EnvVarNotFound, - "Environment variable WASI_SYSROOT not defined. Download sysroot from GitHub & setup environment variable WASI_SYSROOT targeting the folder.", - )) - } - } - - fn cuda_file_count(&self) -> usize { - self.files - .iter() - .filter(|file| file.extension() == Some(OsStr::new("cu"))) - .count() - } - - fn which(&self, tool: &Path, path_entries: Option<&OsStr>) -> Option { - // Loop through PATH entries searching for the |tool|. - let find_exe_in_path = |path_entries: &OsStr| -> Option { - env::split_paths(path_entries).find_map(|path_entry| check_exe(path_entry.join(tool))) - }; - - // If |tool| is not just one "word," assume it's an actual path... - if tool.components().count() > 1 { - check_exe(PathBuf::from(tool)) - } else { - path_entries - .and_then(find_exe_in_path) - .or_else(|| find_exe_in_path(&self.get_env("PATH")?)) - } - } - - /// search for |prog| on 'programs' path in '|cc| --print-search-dirs' output - fn search_programs( - &self, - cc: &Path, - prog: &Path, - cargo_output: &CargoOutput, - ) -> Option { - let search_dirs = run_output( - self.cmd(cc).arg("--print-search-dirs"), - // this doesn't concern the compilation so we always want to show warnings. - cargo_output, - ) - .ok()?; - // clang driver appears to be forcing UTF-8 output even on Windows, - // hence from_utf8 is assumed to be usable in all cases. - let search_dirs = std::str::from_utf8(&search_dirs).ok()?; - for dirs in search_dirs.split(['\r', '\n']) { - if let Some(path) = dirs.strip_prefix("programs: =") { - return self.which(prog, Some(OsStr::new(path))); - } - } - None - } - - fn find_msvc_tools_find(&self, target: &TargetInfo<'_>, tool: &str) -> Option { - self.find_msvc_tools_find_tool(target, tool) - .map(|c| c.to_command()) - } - - fn find_msvc_tools_find_tool(&self, target: &TargetInfo<'_>, tool: &str) -> Option { - struct BuildEnvGetter<'s>(&'s Build); - - impl ::find_msvc_tools::EnvGetter for BuildEnvGetter<'_> { - fn get_env(&self, name: &str) -> Option<::find_msvc_tools::Env> { - // TODO: Should we allow overriding these with `Build::env`? - // - self.0.get_env(name).map(::find_msvc_tools::Env::Owned) - } - } - - if target.env != "msvc" { - return None; - } - - ::find_msvc_tools::find_tool_with_env(target.full_arch, tool, &BuildEnvGetter(self)) - .map(Tool::from_find_msvc_tools) - } - - /// Compiling for WASI targets typically uses the [wasi-sdk] project and - /// installations of wasi-sdk are typically indicated with the - /// `WASI_SDK_PATH` environment variable. Check to see if that environment - /// variable exists, and check to see if an appropriate compiler is located - /// there. If that all passes then use that compiler by default, but - /// otherwise fall back to whatever the clang default is since gcc doesn't - /// have support for compiling to wasm. - /// - /// [wasi-sdk]: https://github.com/WebAssembly/wasi-sdk - fn autodetect_wasi_compiler(&self, raw_target: &str, clang: &str) -> PathBuf { - if let Some(path) = self.get_env_overridable("WASI_SDK_PATH") { - let target_clang = Path::new(&path) - .join("bin") - .join(format!("{raw_target}-clang")); - if let Some(path) = self.which(&target_clang, None) { - return path; - } - } - - clang.into() - } -} - -impl Default for Build { - fn default() -> Build { - Build::new() - } -} - -fn fail(s: &str) -> ! { - eprintln!("\n\nerror occurred in cc-rs: {s}\n\n"); - std::process::exit(1); -} - -// Use by default minimum available API level -// See note about naming here -// https://android.googlesource.com/platform/ndk/+/refs/heads/ndk-release-r21/docs/BuildSystemMaintainers.md#Clang -static NEW_STANDALONE_ANDROID_COMPILERS: [&str; 4] = [ - "aarch64-linux-android21-clang", - "armv7a-linux-androideabi16-clang", - "i686-linux-android16-clang", - "x86_64-linux-android21-clang", -]; - -// New "standalone" C/C++ cross-compiler executables from recent Android NDK -// are just shell scripts that call main clang binary (from Android NDK) with -// proper `--target` argument. -// -// For example, armv7a-linux-androideabi16-clang passes -// `--target=armv7a-linux-androideabi16` to clang. -// So to construct proper command line check if -// `--target` argument would be passed or not to clang -fn android_clang_compiler_uses_target_arg_internally(clang_path: &Path) -> bool { - if let Some(filename) = clang_path.file_name() { - if let Some(filename_str) = filename.to_str() { - if let Some(idx) = filename_str.rfind('-') { - return filename_str.split_at(idx).0.contains("android"); - } - } - } - false -} - -fn is_llvm_mingw_wrapper(clang_path: &Path) -> bool { - if let Some(filename) = clang_path - .file_name() - .and_then(|file_name| file_name.to_str()) - { - filename.ends_with("-w64-mingw32-clang") || filename.ends_with("-w64-mingw32-clang++") - } else { - false - } -} - -// FIXME: Use parsed target. -fn autodetect_android_compiler(raw_target: &str, gnu: &str, clang: &str) -> PathBuf { - let new_clang_key = match raw_target { - "aarch64-linux-android" => Some("aarch64"), - "armv7-linux-androideabi" => Some("armv7a"), - "i686-linux-android" => Some("i686"), - "x86_64-linux-android" => Some("x86_64"), - _ => None, - }; - - let new_clang = new_clang_key - .map(|key| { - NEW_STANDALONE_ANDROID_COMPILERS - .iter() - .find(|x| x.starts_with(key)) - }) - .unwrap_or(None); - - if let Some(new_clang) = new_clang { - if Command::new(new_clang).output().is_ok() { - return (*new_clang).into(); - } - } - - let target = raw_target - .replace("armv7neon", "arm") - .replace("armv7", "arm") - .replace("thumbv7neon", "arm") - .replace("thumbv7", "arm"); - let gnu_compiler = format!("{target}-{gnu}"); - let clang_compiler = format!("{target}-{clang}"); - - // On Windows, the Android clang compiler is provided as a `.cmd` file instead - // of a `.exe` file. `std::process::Command` won't run `.cmd` files unless the - // `.cmd` is explicitly appended to the command name, so we do that here. - let clang_compiler_cmd = format!("{target}-{clang}.cmd"); - - // Check if gnu compiler is present - // if not, use clang - if Command::new(&gnu_compiler).output().is_ok() { - gnu_compiler - } else if cfg!(windows) && Command::new(&clang_compiler_cmd).output().is_ok() { - clang_compiler_cmd - } else { - clang_compiler - } - .into() -} - -// Rust and clang/cc don't agree on how to name the target. -fn map_darwin_target_from_rust_to_compiler_architecture<'a>(target: &TargetInfo<'a>) -> &'a str { - match target.full_arch { - "aarch64" => "arm64", - "arm64_32" => "arm64_32", - "arm64e" => "arm64e", - "armv7k" => "armv7k", - "armv7s" => "armv7s", - "i386" => "i386", - "i686" => "i386", - "powerpc" => "ppc", - "powerpc64" => "ppc64", - "x86_64" => "x86_64", - "x86_64h" => "x86_64h", - arch => arch, - } -} - -fn is_arm(target: &TargetInfo<'_>) -> bool { - matches!(target.arch, "aarch64" | "arm64ec" | "arm") -} - -#[derive(Clone, Copy, PartialEq)] -enum AsmFileExt { - /// `.asm` files. On MSVC targets, we assume these should be passed to MASM - /// (`ml{,64}.exe`). - DotAsm, - /// `.s` or `.S` files, which do not have the special handling on MSVC targets. - DotS, -} - -impl AsmFileExt { - fn from_path(file: &Path) -> Option { - if let Some(ext) = file.extension() { - if let Some(ext) = ext.to_str() { - let ext = ext.to_lowercase(); - match &*ext { - "asm" => return Some(AsmFileExt::DotAsm), - "s" => return Some(AsmFileExt::DotS), - _ => return None, - } - } - } - None - } -} - -fn check_exe(mut exe: PathBuf) -> Option { - let exe_ext = std::env::consts::EXE_EXTENSION; - let check = exe.exists() || (!exe_ext.is_empty() && exe.set_extension(exe_ext) && exe.exists()); - check.then_some(exe) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_android_clang_compiler_uses_target_arg_internally() { - for version in 16..21 { - assert!(android_clang_compiler_uses_target_arg_internally( - &PathBuf::from(format!("armv7a-linux-androideabi{}-clang", version)) - )); - assert!(android_clang_compiler_uses_target_arg_internally( - &PathBuf::from(format!("armv7a-linux-androideabi{}-clang++", version)) - )); - } - assert!(!android_clang_compiler_uses_target_arg_internally( - &PathBuf::from("clang-i686-linux-android") - )); - assert!(!android_clang_compiler_uses_target_arg_internally( - &PathBuf::from("clang") - )); - assert!(!android_clang_compiler_uses_target_arg_internally( - &PathBuf::from("clang++") - )); - } -} diff --git a/cc-1.2.63/src/parallel/async_executor.rs b/cc-1.2.63/src/parallel/async_executor.rs deleted file mode 100644 index 9ebd1ad562..0000000000 --- a/cc-1.2.63/src/parallel/async_executor.rs +++ /dev/null @@ -1,118 +0,0 @@ -use std::{ - cell::Cell, - future::Future, - pin::Pin, - ptr, - task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, - thread, - time::Duration, -}; - -use crate::Error; - -const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new( - // Cloning just returns a new no-op raw waker - |_| NOOP_RAW_WAKER, - // `wake` does nothing - |_| {}, - // `wake_by_ref` does nothing - |_| {}, - // Dropping does nothing as we don't allocate anything - |_| {}, -); -const NOOP_RAW_WAKER: RawWaker = RawWaker::new(ptr::null(), &NOOP_WAKER_VTABLE); - -#[derive(Default)] -pub(crate) struct YieldOnce(bool); - -impl Future for YieldOnce { - type Output = (); - - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> { - let flag = &mut std::pin::Pin::into_inner(self).0; - if !*flag { - *flag = true; - Poll::Pending - } else { - Poll::Ready(()) - } - } -} - -/// Execute the futures and return when they are all done. -/// -/// Here we use our own homebrew async executor since cc is used in the build -/// script of many popular projects, pulling in additional dependencies would -/// significantly slow down its compilation. -pub(crate) fn block_on( - mut fut1: Fut1, - mut fut2: Fut2, - has_made_progress: &Cell, -) -> Result<(), Error> -where - Fut1: Future>, - Fut2: Future>, -{ - // Shadows the future so that it can never be moved and is guaranteed - // to be pinned. - // - // The same trick used in `pin!` macro. - // - // TODO: Once MSRV is bumped to 1.68, replace this with `std::pin::pin!` - let mut fut1 = Some(unsafe { Pin::new_unchecked(&mut fut1) }); - let mut fut2 = Some(unsafe { Pin::new_unchecked(&mut fut2) }); - - // TODO: Once `Waker::noop` stablised and our MSRV is bumped to the version - // which it is stablised, replace this with `Waker::noop`. - let waker = unsafe { Waker::from_raw(NOOP_RAW_WAKER) }; - let mut context = Context::from_waker(&waker); - - let mut backoff_cnt = 0; - - loop { - has_made_progress.set(false); - - if let Some(fut) = fut2.as_mut() { - if let Poll::Ready(res) = fut.as_mut().poll(&mut context) { - fut2 = None; - res?; - } - } - - if let Some(fut) = fut1.as_mut() { - if let Poll::Ready(res) = fut.as_mut().poll(&mut context) { - fut1 = None; - res?; - } - } - - if fut1.is_none() && fut2.is_none() { - return Ok(()); - } - - if !has_made_progress.get() { - if backoff_cnt > 3 { - // We have yielded at least three times without making' - // any progress, so we will sleep for a while. - let duration = Duration::from_millis(100 * (backoff_cnt - 3).min(10)); - thread::sleep(duration); - } else { - // Given that we spawned a lot of compilation tasks, it is unlikely - // that OS cannot find other ready task to execute. - // - // If all of them are done, then we will yield them and spawn more, - // or simply return. - // - // Thus this will not be turned into a busy-wait loop and it will not - // waste CPU resource. - thread::yield_now(); - } - } - - backoff_cnt = if has_made_progress.get() { - 0 - } else { - backoff_cnt + 1 - }; - } -} diff --git a/cc-1.2.63/src/parallel/command_runner.rs b/cc-1.2.63/src/parallel/command_runner.rs deleted file mode 100644 index 9393a98ff1..0000000000 --- a/cc-1.2.63/src/parallel/command_runner.rs +++ /dev/null @@ -1,175 +0,0 @@ -use std::{ - cell::Cell, - io::{self, Write as _}, - process::{Child, Command}, -}; - -use crate::{ - parallel::{ - async_executor::{block_on, YieldOnce}, - job_token, - }, - spawn, CargoOutput, Error, ErrorKind, StderrForwarder, -}; - -struct KillOnDrop(Child, StderrForwarder); - -impl Drop for KillOnDrop { - fn drop(&mut self) { - let child = &mut self.0; - - child.kill().ok(); - } -} - -fn cell_update(cell: &Cell, f: F) -where - T: Default, - F: FnOnce(T) -> T, -{ - let old = cell.take(); - let new = f(old); - cell.set(new); -} - -fn try_wait_on_child( - cmd: &Command, - child: &mut Child, - mut stdout: impl io::Write, - stderr_forwarder: &mut StderrForwarder, -) -> Result, Error> { - stderr_forwarder.forward_available(); - - match child.try_wait() { - Ok(Some(status)) => { - stderr_forwarder.forward_all(); - - let _ = writeln!(stdout, "{}", status); - - if status.success() { - Ok(Some(())) - } else { - Err(Error::new( - ErrorKind::ToolExecError, - format!("command did not execute successfully (status code {status}): {cmd:?}"), - )) - } - } - Ok(None) => Ok(None), - Err(e) => { - stderr_forwarder.forward_all(); - Err(Error::new( - ErrorKind::ToolExecError, - format!("failed to wait on spawned child process `{cmd:?}`: {e}"), - )) - } - } -} - -pub(crate) fn run_commands_in_parallel( - cargo_output: &CargoOutput, - cmds: &mut dyn Iterator>, -) -> Result<(), Error> { - // Limit our parallelism globally with a jobserver. - let mut tokens = job_token::ActiveJobTokenServer::new(); - - // When compiling objects in parallel we do a few dirty tricks to speed - // things up: - // - // * First is that we use the `jobserver` crate to limit the parallelism - // of this build script. The `jobserver` crate will use a jobserver - // configured by Cargo for build scripts to ensure that parallelism is - // coordinated across C compilations and Rust compilations. Before we - // compile anything we make sure to wait until we acquire a token. - // - // Note that this jobserver is cached globally so we only used one per - // process and only worry about creating it once. - // - // * Next we use spawn the process to actually compile objects in - // parallel after we've acquired a token to perform some work - // - // With all that in mind we compile all objects in a loop here, after we - // acquire the appropriate tokens, Once all objects have been compiled - // we wait on all the processes and propagate the results of compilation. - - let pendings = Cell::new(Vec::<(Command, KillOnDrop, job_token::JobToken)>::new()); - let is_disconnected = Cell::new(false); - let has_made_progress = Cell::new(false); - - let wait_future = async { - let mut error = None; - // Buffer the stdout - let mut stdout = io::BufWriter::with_capacity(128, io::stdout()); - - loop { - // If the other end of the pipe is already disconnected, then we're not gonna get any new jobs, - // so it doesn't make sense to reuse the tokens; in fact, - // releasing them as soon as possible (once we know that the other end is disconnected) is beneficial. - // Imagine that the last file built takes an hour to finish; in this scenario, - // by not releasing the tokens before that last file is done we would effectively block other processes from - // starting sooner - even though we only need one token for that last file, not N others that were acquired. - - let mut pendings_is_empty = false; - - cell_update(&pendings, |mut pendings| { - // Try waiting on them. - pendings.retain_mut(|(cmd, child, _token)| { - match try_wait_on_child(cmd, &mut child.0, &mut stdout, &mut child.1) { - Ok(Some(())) => { - // Task done, remove the entry - has_made_progress.set(true); - false - } - Ok(None) => true, // Task still not finished, keep the entry - Err(err) => { - // Task fail, remove the entry. - // Since we can only return one error, log the error to make - // sure users always see all the compilation failures. - has_made_progress.set(true); - - if cargo_output.warnings { - let _ = writeln!(stdout, "cargo:warning={}", err); - } - error = Some(err); - - false - } - } - }); - pendings_is_empty = pendings.is_empty(); - pendings - }); - - if pendings_is_empty && is_disconnected.get() { - break if let Some(err) = error { - Err(err) - } else { - Ok(()) - }; - } - - YieldOnce::default().await; - } - }; - let spawn_future = async { - for res in cmds { - let mut cmd = res?; - let token = tokens.acquire().await?; - let mut child = spawn(&mut cmd, cargo_output)?; - let mut stderr_forwarder = StderrForwarder::new(&mut child); - stderr_forwarder.set_non_blocking()?; - - cell_update(&pendings, |mut pendings| { - pendings.push((cmd, KillOnDrop(child, stderr_forwarder), token)); - pendings - }); - - has_made_progress.set(true); - } - is_disconnected.set(true); - - Ok::<_, Error>(()) - }; - - block_on(wait_future, spawn_future, &has_made_progress) -} diff --git a/cc-1.2.63/src/parallel/job_token.rs b/cc-1.2.63/src/parallel/job_token.rs deleted file mode 100644 index fc35f9eac5..0000000000 --- a/cc-1.2.63/src/parallel/job_token.rs +++ /dev/null @@ -1,262 +0,0 @@ -use std::marker::PhantomData; - -use crate::{utilities::OnceLock, Error}; - -pub(crate) struct JobToken(PhantomData<()>); - -impl JobToken { - fn new() -> Self { - Self(PhantomData) - } -} - -impl Drop for JobToken { - fn drop(&mut self) { - match JobTokenServer::new() { - JobTokenServer::Inherited(jobserver) => jobserver.release_token_raw(), - JobTokenServer::InProcess(jobserver) => jobserver.release_token_raw(), - } - } -} - -enum JobTokenServer { - Inherited(inherited_jobserver::JobServer), - InProcess(inprocess_jobserver::JobServer), -} - -impl JobTokenServer { - /// This function returns a static reference to the jobserver because - /// - creating a jobserver from env is a bit fd-unsafe (e.g. the fd might - /// be closed by other jobserver users in the process) and better do it - /// at the start of the program. - /// - in case a jobserver cannot be created from env (e.g. it's not - /// present), we will create a global in-process only jobserver - /// that has to be static so that it will be shared by all cc - /// compilation. - fn new() -> &'static Self { - // TODO: Replace with a OnceLock once MSRV is 1.70 - static JOBSERVER: OnceLock = OnceLock::new(); - - JOBSERVER.get_or_init(|| { - unsafe { inherited_jobserver::JobServer::from_env() } - .map(Self::Inherited) - .unwrap_or_else(|| Self::InProcess(inprocess_jobserver::JobServer::new())) - }) - } -} - -pub(crate) enum ActiveJobTokenServer { - Inherited(inherited_jobserver::ActiveJobServer<'static>), - InProcess(&'static inprocess_jobserver::JobServer), -} - -impl ActiveJobTokenServer { - pub(crate) fn new() -> Self { - match JobTokenServer::new() { - JobTokenServer::Inherited(inherited_jobserver) => { - Self::Inherited(inherited_jobserver.enter_active()) - } - JobTokenServer::InProcess(inprocess_jobserver) => Self::InProcess(inprocess_jobserver), - } - } - - pub(crate) async fn acquire(&mut self) -> Result { - match self { - Self::Inherited(jobserver) => jobserver.acquire().await, - Self::InProcess(jobserver) => Ok(jobserver.acquire().await), - } - } -} - -mod inherited_jobserver { - use super::JobToken; - - use crate::{parallel::async_executor::YieldOnce, Error, ErrorKind}; - - use std::{ - io, mem, - sync::{mpsc, Mutex, MutexGuard, PoisonError}, - }; - - pub(super) struct JobServer { - /// Implicit token for this process which is obtained and will be - /// released in parent. Since `JobTokens` only give back what they got, - /// there should be at most one global implicit token in the wild. - /// - /// Since Rust does not execute any `Drop` for global variables, - /// we can't just put it back to jobserver and then re-acquire it at - /// the end of the process. - /// - /// Use `Mutex` to avoid race between acquire and release. - /// If an `AtomicBool` is used, then it's possible for: - /// - `release_token_raw`: Tries to set `global_implicit_token` to true, but it is already - /// set to `true`, continue to release it to jobserver - /// - `acquire` takes the global implicit token, set `global_implicit_token` to false - /// - `release_token_raw` now writes the token back into the jobserver, while - /// `global_implicit_token` is `false` - /// - /// If the program exits here, then cc effectively increases parallelism by one, which is - /// incorrect, hence we use a `Mutex` here. - global_implicit_token: Mutex, - inner: jobserver::Client, - } - - impl JobServer { - pub(super) unsafe fn from_env() -> Option { - jobserver::Client::from_env().map(|inner| Self { - inner, - global_implicit_token: Mutex::new(true), - }) - } - - fn get_global_implicit_token(&self) -> MutexGuard<'_, bool> { - self.global_implicit_token - .lock() - .unwrap_or_else(PoisonError::into_inner) - } - - /// All tokens except for the global implicit token will be put back into the jobserver - /// immediately and they cannot be cached, since Rust does not call `Drop::drop` on - /// global variables. - pub(super) fn release_token_raw(&self) { - let mut global_implicit_token = self.get_global_implicit_token(); - - if *global_implicit_token { - // There's already a global implicit token, so this token must - // be released back into jobserver. - // - // `release_raw` should not block - let _ = self.inner.release_raw(); - } else { - *global_implicit_token = true; - } - } - - pub(super) fn enter_active(&self) -> ActiveJobServer<'_> { - ActiveJobServer { - jobserver: self, - helper_thread: None, - } - } - } - - struct HelperThread { - inner: jobserver::HelperThread, - /// When rx is dropped, all the token stored within it will be dropped. - rx: mpsc::Receiver>, - } - - impl HelperThread { - fn new(jobserver: &JobServer) -> Result { - let (tx, rx) = mpsc::channel(); - - Ok(Self { - rx, - inner: jobserver.inner.clone().into_helper_thread(move |res| { - let _ = tx.send(res); - })?, - }) - } - } - - pub(crate) struct ActiveJobServer<'a> { - jobserver: &'a JobServer, - helper_thread: Option, - } - - impl ActiveJobServer<'_> { - pub(super) async fn acquire(&mut self) -> Result { - let mut has_requested_token = false; - - loop { - // Fast path - if mem::replace(&mut *self.jobserver.get_global_implicit_token(), false) { - break Ok(JobToken::new()); - } - - match self.jobserver.inner.try_acquire() { - Ok(Some(acquired)) => { - acquired.drop_without_releasing(); - break Ok(JobToken::new()); - } - Ok(None) => YieldOnce::default().await, - Err(err) if err.kind() == io::ErrorKind::Unsupported => { - // Fallback to creating a help thread with blocking acquire - let helper_thread = if let Some(thread) = self.helper_thread.as_ref() { - thread - } else { - self.helper_thread - .insert(HelperThread::new(self.jobserver)?) - }; - - match helper_thread.rx.try_recv() { - Ok(res) => { - let acquired = res?; - acquired.drop_without_releasing(); - break Ok(JobToken::new()); - } - Err(mpsc::TryRecvError::Disconnected) => break Err(Error::new( - ErrorKind::JobserverHelpThreadError, - "jobserver help thread has returned before ActiveJobServer is dropped", - )), - Err(mpsc::TryRecvError::Empty) => { - if !has_requested_token { - helper_thread.inner.request_token(); - has_requested_token = true; - } - YieldOnce::default().await - } - } - } - Err(err) => break Err(err.into()), - } - } - } - } -} - -mod inprocess_jobserver { - use super::JobToken; - - use crate::{parallel::async_executor::YieldOnce, utilities::cargo_env_var_os}; - - use std::sync::atomic::{ - AtomicU32, - Ordering::{AcqRel, Acquire}, - }; - - pub(crate) struct JobServer(AtomicU32); - - impl JobServer { - pub(super) fn new() -> Self { - // Use `NUM_JOBS` if set (it's configured by Cargo) and otherwise - // just fall back to the number of cores on the local machine, or a reasonable - // default if that cannot be determined. - - let parallelism = cargo_env_var_os("NUM_JOBS") - .and_then(|j| j.to_str()?.parse::().ok()) - .or_else(|| Some(std::thread::available_parallelism().ok()?.get() as u32)) - .unwrap_or(4); - - Self(AtomicU32::new(parallelism)) - } - - pub(super) async fn acquire(&self) -> JobToken { - loop { - let res = self - .0 - .fetch_update(AcqRel, Acquire, |tokens| tokens.checked_sub(1)); - - if res.is_ok() { - break JobToken::new(); - } - - YieldOnce::default().await - } - } - - pub(super) fn release_token_raw(&self) { - self.0.fetch_add(1, AcqRel); - } - } -} diff --git a/cc-1.2.63/src/parallel/mod.rs b/cc-1.2.63/src/parallel/mod.rs deleted file mode 100644 index 09dfe125af..0000000000 --- a/cc-1.2.63/src/parallel/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod async_executor; -mod command_runner; -mod job_token; -pub(crate) mod stderr; - -pub(crate) use command_runner::run_commands_in_parallel; diff --git a/cc-1.2.63/src/parallel/stderr.rs b/cc-1.2.63/src/parallel/stderr.rs deleted file mode 100644 index 8533fec9ab..0000000000 --- a/cc-1.2.63/src/parallel/stderr.rs +++ /dev/null @@ -1,91 +0,0 @@ -#![cfg_attr(target_family = "wasm", allow(unused))] -/// Helpers functions for [`ChildStderr`]. -use std::{convert::TryInto, process::ChildStderr}; - -use crate::{Error, ErrorKind}; - -#[cfg(all(not(unix), not(windows), not(target_family = "wasm")))] -compile_error!("Only unix and windows support non-blocking pipes! For other OSes, disable the parallel feature."); - -#[cfg(unix)] -fn get_flags(fd: std::os::unix::io::RawFd) -> Result { - let flags = unsafe { libc::fcntl(fd, libc::F_GETFL, 0) }; - if flags == -1 { - Err(Error::new( - ErrorKind::IOError, - format!( - "Failed to get flags for pipe {}: {}", - fd, - std::io::Error::last_os_error() - ), - )) - } else { - Ok(flags) - } -} - -#[cfg(unix)] -fn set_flags(fd: std::os::unix::io::RawFd, flags: std::os::raw::c_int) -> Result<(), Error> { - if unsafe { libc::fcntl(fd, libc::F_SETFL, flags) } == -1 { - Err(Error::new( - ErrorKind::IOError, - format!( - "Failed to set flags for pipe {}: {}", - fd, - std::io::Error::last_os_error() - ), - )) - } else { - Ok(()) - } -} - -#[cfg(unix)] -pub fn set_non_blocking(pipe: &impl std::os::unix::io::AsRawFd) -> Result<(), Error> { - // On Unix, switch the pipe to non-blocking mode. - // On Windows, we have a different way to be non-blocking. - let fd = pipe.as_raw_fd(); - - let flags = get_flags(fd)?; - set_flags(fd, flags | libc::O_NONBLOCK) -} - -pub fn bytes_available(stderr: &mut ChildStderr) -> Result { - let mut bytes_available = 0; - #[cfg(windows)] - { - use ::find_msvc_tools::windows_sys::PeekNamedPipe; - use std::os::windows::io::AsRawHandle; - use std::ptr::null_mut; - if unsafe { - PeekNamedPipe( - stderr.as_raw_handle(), - null_mut(), - 0, - null_mut(), - &mut bytes_available, - null_mut(), - ) - } == 0 - { - return Err(Error::new( - ErrorKind::IOError, - format!( - "PeekNamedPipe failed with {}", - std::io::Error::last_os_error() - ), - )); - } - } - #[cfg(unix)] - { - use std::os::unix::io::AsRawFd; - if unsafe { libc::ioctl(stderr.as_raw_fd(), libc::FIONREAD, &mut bytes_available) } != 0 { - return Err(Error::new( - ErrorKind::IOError, - format!("ioctl failed with {}", std::io::Error::last_os_error()), - )); - } - } - Ok(bytes_available.try_into().unwrap()) -} diff --git a/cc-1.2.63/src/target.rs b/cc-1.2.63/src/target.rs deleted file mode 100644 index ed432df589..0000000000 --- a/cc-1.2.63/src/target.rs +++ /dev/null @@ -1,41 +0,0 @@ -//! Parsing of `rustc` target names to match the values exposed to Cargo -//! build scripts (`CARGO_CFG_*`). - -mod apple; -mod generated; -mod llvm; -mod parser; - -pub(crate) use parser::TargetInfoParser; - -/// Information specific to a `rustc` target. -/// -/// See . -#[derive(Debug, PartialEq, Clone)] -pub(crate) struct TargetInfo<'a> { - /// The full architecture, including the subarchitecture. - /// - /// This differs from `cfg!(target_arch)`, which only specifies the - /// overall architecture, which is too coarse for certain cases. - pub full_arch: &'a str, - /// The overall target architecture. - /// - /// This is the same as the value of `cfg!(target_arch)`. - pub arch: &'a str, - /// The target vendor. - /// - /// This is the same as the value of `cfg!(target_vendor)`. - pub vendor: &'a str, - /// The operating system, or `none` on bare-metal targets. - /// - /// This is the same as the value of `cfg!(target_os)`. - pub os: &'a str, - /// The environment on top of the operating system. - /// - /// This is the same as the value of `cfg!(target_env)`. - pub env: &'a str, - /// The ABI on top of the operating system. - /// - /// This is the same as the value of `cfg!(target_abi)`. - pub abi: &'a str, -} diff --git a/cc-1.2.63/src/target/apple.rs b/cc-1.2.63/src/target/apple.rs deleted file mode 100644 index 26f17a7409..0000000000 --- a/cc-1.2.63/src/target/apple.rs +++ /dev/null @@ -1,57 +0,0 @@ -use super::TargetInfo; - -impl TargetInfo<'_> { - pub(crate) fn apple_sdk_name(&self) -> &'static str { - match (self.os, self.env) { - // The target_env variable, as written here: - // https://doc.rust-lang.org/reference/conditional-compilation.html#target_env - // is only really used for disambiguation, so we use a "default" case instead of - // checking for a blank string. - ("macos", _) => "macosx", - ("ios", "sim") => "iphonesimulator", - ("ios", "macabi") => "macosx", - ("ios", _) => "iphoneos", - ("tvos", "sim") => "appletvsimulator", - ("tvos", _) => "appletvos", - ("watchos", "sim") => "watchsimulator", - ("watchos", _) => "watchos", - ("visionos", "sim") => "xrsimulator", - ("visionos", _) => "xros", - (os, _) => panic!("invalid Apple target OS {}", os), - } - } - - pub(crate) fn apple_version_flag(&self, min_version: &str) -> String { - // There are many aliases for these, and `-mtargetos=` is preferred on Clang nowadays, but - // for compatibility with older Clang, we use the earliest supported name here. - // - // NOTE: GCC does not support `-miphoneos-version-min=` etc. (because it does not support - // iOS in general), but we specify them anyhow in case we actually have a Clang-like - // compiler disguised as a GNU-like compiler, or in case GCC adds support for these in the - // future. - // - // See also: - // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mmacos-version-min - // https://clang.llvm.org/docs/AttributeReference.html#availability - // https://gcc.gnu.org/onlinedocs/gcc/Darwin-Options.html#index-mmacosx-version-min - match (self.os, self.env) { - // The target_env variable, as written here: - // https://doc.rust-lang.org/reference/conditional-compilation.html#target_env - // is only really used for disambiguation, so we use a "default" case instead of - // checking for a blank string. - ("macos", _) => format!("-mmacosx-version-min={min_version}"), - ("ios", "sim") => format!("-mios-simulator-version-min={min_version}"), - ("ios", "macabi") => format!("-mtargetos=ios{min_version}-macabi"), - ("ios", _) => format!("-miphoneos-version-min={min_version}"), - ("tvos", "sim") => format!("-mappletvsimulator-version-min={min_version}"), - ("tvos", _) => format!("-mappletvos-version-min={min_version}"), - ("watchos", "sim") => format!("-mwatchsimulator-version-min={min_version}"), - ("watchos", _) => format!("-mwatchos-version-min={min_version}"), - // `-mxros-version-min` does not exist - // https://github.com/llvm/llvm-project/issues/88271 - ("visionos", "sim") => format!("-mtargetos=xros{min_version}-simulator"), - ("visionos", _) => format!("-mtargetos=xros{min_version}"), - (os, _) => panic!("invalid Apple target OS {}", os), - } - } -} diff --git a/cc-1.2.63/src/target/generated.rs b/cc-1.2.63/src/target/generated.rs deleted file mode 100644 index cab775bb53..0000000000 --- a/cc-1.2.63/src/target/generated.rs +++ /dev/null @@ -1,338 +0,0 @@ -//! This file is generated code. Please edit the generator in -//! dev-tools/gen-target-info if you need to make changes, or see -//! src/target/llvm.rs if you need to configure a specific LLVM triple. - -#[rustfmt::skip] -pub(crate) const LLVM_TARGETS: &[(&str, &str)] = &[ - ("aarch64-apple-darwin", "arm64-apple-macosx"), - ("aarch64-apple-ios", "arm64-apple-ios"), - ("aarch64-apple-ios-macabi", "arm64-apple-ios-macabi"), - ("aarch64-apple-ios-sim", "arm64-apple-ios-simulator"), - ("aarch64-apple-tvos", "arm64-apple-tvos"), - ("aarch64-apple-tvos-sim", "arm64-apple-tvos-simulator"), - ("aarch64-apple-visionos", "arm64-apple-xros"), - ("aarch64-apple-visionos-sim", "arm64-apple-xros-simulator"), - ("aarch64-apple-watchos", "arm64-apple-watchos"), - ("aarch64-apple-watchos-sim", "arm64-apple-watchos-simulator"), - ("aarch64-fuchsia", "aarch64-fuchsia"), - ("aarch64-kmc-solid_asp3", "aarch64-unknown-none"), - ("aarch64-linux-android", "aarch64-linux-android"), - ("aarch64-nintendo-switch-freestanding", "aarch64-unknown-none"), - ("aarch64-pc-windows-gnullvm", "aarch64-pc-windows-gnu"), - ("aarch64-pc-windows-msvc", "aarch64-pc-windows-msvc"), - ("aarch64-unknown-freebsd", "aarch64-unknown-freebsd"), - ("aarch64-unknown-fuchsia", "aarch64-unknown-fuchsia"), - ("aarch64-unknown-helenos", "aarch64-unknown-helenos"), - ("aarch64-unknown-hermit", "aarch64-unknown-hermit"), - ("aarch64-unknown-illumos", "aarch64-unknown-solaris2.11"), - ("aarch64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"), - ("aarch64-unknown-linux-gnu_ilp32", "aarch64-unknown-linux-gnu_ilp32"), - ("aarch64-unknown-linux-musl", "aarch64-unknown-linux-musl"), - ("aarch64-unknown-linux-ohos", "aarch64-unknown-linux-ohos"), - ("aarch64-unknown-managarm-mlibc", "aarch64-unknown-managarm-mlibc"), - ("aarch64-unknown-netbsd", "aarch64-unknown-netbsd"), - ("aarch64-unknown-none", "aarch64-unknown-none"), - ("aarch64-unknown-none-softfloat", "aarch64-unknown-none"), - ("aarch64-unknown-nto-qnx700", "aarch64-unknown-unknown"), - ("aarch64-unknown-nto-qnx710", "aarch64-unknown-unknown"), - ("aarch64-unknown-nto-qnx710_iosock", "aarch64-unknown-unknown"), - ("aarch64-unknown-nto-qnx800", "aarch64-unknown-unknown"), - ("aarch64-unknown-nuttx", "aarch64-unknown-none"), - ("aarch64-unknown-openbsd", "aarch64-unknown-openbsd"), - ("aarch64-unknown-redox", "aarch64-unknown-redox"), - ("aarch64-unknown-teeos", "aarch64-unknown-none"), - ("aarch64-unknown-trusty", "aarch64-unknown-unknown-musl"), - ("aarch64-unknown-uefi", "aarch64-unknown-windows"), - ("aarch64-uwp-windows-msvc", "aarch64-pc-windows-msvc"), - ("aarch64-wrs-vxworks", "aarch64-unknown-linux-gnu"), - ("aarch64_be-unknown-hermit", "aarch64_be-unknown-hermit"), - ("aarch64_be-unknown-linux-gnu", "aarch64_be-unknown-linux-gnu"), - ("aarch64_be-unknown-linux-gnu_ilp32", "aarch64_be-unknown-linux-gnu_ilp32"), - ("aarch64_be-unknown-linux-musl", "aarch64_be-unknown-linux-musl"), - ("aarch64_be-unknown-netbsd", "aarch64_be-unknown-netbsd"), - ("aarch64_be-unknown-none-softfloat", "aarch64_be-unknown-none"), - ("aarch64v8r-unknown-none", "aarch64-unknown-none"), - ("aarch64v8r-unknown-none-softfloat", "aarch64-unknown-none"), - ("amdgcn-amd-amdhsa", "amdgcn-amd-amdhsa"), - ("arm-linux-androideabi", "arm-linux-androideabi"), - ("arm-unknown-linux-gnueabi", "arm-unknown-linux-gnueabi"), - ("arm-unknown-linux-gnueabihf", "arm-unknown-linux-gnueabihf"), - ("arm-unknown-linux-musleabi", "arm-unknown-linux-musleabi"), - ("arm-unknown-linux-musleabihf", "arm-unknown-linux-musleabihf"), - ("arm64_32-apple-watchos", "arm64_32-apple-watchos"), - ("arm64e-apple-darwin", "arm64e-apple-macosx"), - ("arm64e-apple-ios", "arm64e-apple-ios"), - ("arm64e-apple-tvos", "arm64e-apple-tvos"), - ("arm64ec-pc-windows-msvc", "arm64ec-pc-windows-msvc"), - ("armeb-unknown-linux-gnueabi", "armeb-unknown-linux-gnueabi"), - ("armebv7r-none-eabi", "armebv7r-none-eabi"), - ("armebv7r-none-eabihf", "armebv7r-none-eabihf"), - ("armv4t-none-eabi", "armv4t-none-eabi"), - ("armv4t-unknown-linux-gnueabi", "armv4t-unknown-linux-gnueabi"), - ("armv5te-none-eabi", "armv5te-none-eabi"), - ("armv5te-unknown-linux-gnueabi", "armv5te-unknown-linux-gnueabi"), - ("armv5te-unknown-linux-musleabi", "armv5te-unknown-linux-musleabi"), - ("armv5te-unknown-linux-uclibceabi", "armv5te-unknown-linux-gnueabi"), - ("armv6-none-eabi", "armv6-none-eabi"), - ("armv6-none-eabihf", "armv6-none-eabihf"), - ("armv6-unknown-freebsd", "armv6-unknown-freebsd-gnueabihf"), - ("armv6-unknown-netbsd-eabihf", "armv6-unknown-netbsdelf-eabihf"), - ("armv6k-nintendo-3ds", "armv6k-none-eabihf"), - ("armv7-apple-ios", "armv7-apple-ios7.0.0"), - ("armv7-linux-androideabi", "armv7-none-linux-android"), - ("armv7-rtems-eabihf", "armv7-unknown-none-eabihf"), - ("armv7-sony-vita-newlibeabihf", "thumbv7a-sony-vita-eabihf"), - ("armv7-unknown-freebsd", "armv7-unknown-freebsd-gnueabihf"), - ("armv7-unknown-linux-gnueabi", "armv7-unknown-linux-gnueabi"), - ("armv7-unknown-linux-gnueabihf", "armv7-unknown-linux-gnueabihf"), - ("armv7-unknown-linux-musleabi", "armv7-unknown-linux-musleabi"), - ("armv7-unknown-linux-musleabihf", "armv7-unknown-linux-musleabihf"), - ("armv7-unknown-linux-ohos", "armv7-unknown-linux-ohos"), - ("armv7-unknown-linux-uclibceabi", "armv7-unknown-linux-gnueabi"), - ("armv7-unknown-linux-uclibceabihf", "armv7-unknown-linux-gnueabihf"), - ("armv7-unknown-netbsd-eabihf", "armv7-unknown-netbsdelf-eabihf"), - ("armv7-unknown-trusty", "armv7-unknown-unknown-gnueabi"), - ("armv7-wrs-vxworks-eabihf", "armv7-unknown-linux-gnueabihf"), - ("armv7a-kmc-solid_asp3-eabi", "armv7a-none-eabi"), - ("armv7a-kmc-solid_asp3-eabihf", "armv7a-none-eabihf"), - ("armv7a-none-eabi", "armv7a-none-eabi"), - ("armv7a-none-eabihf", "armv7a-none-eabihf"), - ("armv7a-nuttx-eabi", "armv7a-none-eabi"), - ("armv7a-nuttx-eabihf", "armv7a-none-eabihf"), - ("armv7a-vex-v5", "armv7a-none-eabihf"), - ("armv7k-apple-watchos", "armv7k-apple-watchos"), - ("armv7r-none-eabi", "armv7r-none-eabi"), - ("armv7r-none-eabihf", "armv7r-none-eabihf"), - ("armv7s-apple-ios", "armv7s-apple-ios"), - ("armv8r-none-eabihf", "armv8r-none-eabihf"), - ("asmjs-unknown-emscripten", "wasm32-unknown-emscripten"), - ("avr-none", "avr-unknown-unknown"), - ("avr-unknown-gnu-atmega328", "avr-unknown-unknown"), - ("bpfeb-unknown-none", "bpfeb"), - ("bpfel-unknown-none", "bpfel"), - ("csky-unknown-linux-gnuabiv2", "csky-unknown-linux-gnuabiv2"), - ("csky-unknown-linux-gnuabiv2hf", "csky-unknown-linux-gnuabiv2"), - ("hexagon-unknown-linux-musl", "hexagon-unknown-linux-musl"), - ("hexagon-unknown-none-elf", "hexagon-unknown-none-elf"), - ("hexagon-unknown-qurt", "hexagon-unknown-elf"), - ("i386-apple-ios", "i386-apple-ios-simulator"), - ("i586-pc-windows-msvc", "i586-pc-windows-msvc"), - ("i586-unknown-linux-gnu", "i586-unknown-linux-gnu"), - ("i586-unknown-linux-musl", "i586-unknown-linux-musl"), - ("i586-unknown-netbsd", "i586-unknown-netbsdelf"), - ("i586-unknown-redox", "i586-unknown-redox"), - ("i686-apple-darwin", "i686-apple-macosx"), - ("i686-linux-android", "i686-linux-android"), - ("i686-pc-nto-qnx700", "i586-pc-unknown"), - ("i686-pc-windows-gnu", "i686-pc-windows-gnu"), - ("i686-pc-windows-gnullvm", "i686-pc-windows-gnu"), - ("i686-pc-windows-msvc", "i686-pc-windows-msvc"), - ("i686-unknown-freebsd", "i686-unknown-freebsd"), - ("i686-unknown-haiku", "i686-unknown-haiku"), - ("i686-unknown-helenos", "i686-unknown-helenos"), - ("i686-unknown-hurd-gnu", "i686-unknown-hurd-gnu"), - ("i686-unknown-linux-gnu", "i686-unknown-linux-gnu"), - ("i686-unknown-linux-musl", "i686-unknown-linux-musl"), - ("i686-unknown-netbsd", "i686-unknown-netbsdelf"), - ("i686-unknown-openbsd", "i686-unknown-openbsd"), - ("i686-unknown-uefi", "i686-unknown-windows-gnu"), - ("i686-uwp-windows-gnu", "i686-pc-windows-gnu"), - ("i686-uwp-windows-msvc", "i686-pc-windows-msvc"), - ("i686-win7-windows-gnu", "i686-pc-windows-gnu"), - ("i686-win7-windows-msvc", "i686-pc-windows-msvc"), - ("i686-wrs-vxworks", "i686-unknown-linux-gnu"), - ("loongarch32-unknown-none", "loongarch32-unknown-none"), - ("loongarch32-unknown-none-softfloat", "loongarch32-unknown-none"), - ("loongarch64-unknown-linux-gnu", "loongarch64-unknown-linux-gnu"), - ("loongarch64-unknown-linux-musl", "loongarch64-unknown-linux-musl"), - ("loongarch64-unknown-linux-ohos", "loongarch64-unknown-linux-ohos"), - ("loongarch64-unknown-none", "loongarch64-unknown-none"), - ("loongarch64-unknown-none-softfloat", "loongarch64-unknown-none"), - ("m68k-unknown-linux-gnu", "m68k-unknown-linux-gnu"), - ("m68k-unknown-none-elf", "m68k"), - ("mips-mti-none-elf", "mips"), - ("mips-unknown-linux-gnu", "mips-unknown-linux-gnu"), - ("mips-unknown-linux-musl", "mips-unknown-linux-musl"), - ("mips-unknown-linux-uclibc", "mips-unknown-linux-gnu"), - ("mips64-openwrt-linux-musl", "mips64-unknown-linux-musl"), - ("mips64-unknown-linux-gnuabi64", "mips64-unknown-linux-gnuabi64"), - ("mips64-unknown-linux-muslabi64", "mips64-unknown-linux-musl"), - ("mips64el-unknown-linux-gnuabi64", "mips64el-unknown-linux-gnuabi64"), - ("mips64el-unknown-linux-muslabi64", "mips64el-unknown-linux-musl"), - ("mipsel-mti-none-elf", "mipsel"), - ("mipsel-sony-psp", "mipsel-sony-psp"), - ("mipsel-sony-psx", "mipsel-sony-psx"), - ("mipsel-unknown-linux-gnu", "mipsel-unknown-linux-gnu"), - ("mipsel-unknown-linux-musl", "mipsel-unknown-linux-musl"), - ("mipsel-unknown-linux-uclibc", "mipsel-unknown-linux-gnu"), - ("mipsel-unknown-netbsd", "mipsel-unknown-netbsd"), - ("mipsel-unknown-none", "mipsel-unknown-none"), - ("mipsisa32r6-unknown-linux-gnu", "mipsisa32r6-unknown-linux-gnu"), - ("mipsisa32r6el-unknown-linux-gnu", "mipsisa32r6el-unknown-linux-gnu"), - ("mipsisa64r6-unknown-linux-gnuabi64", "mipsisa64r6-unknown-linux-gnuabi64"), - ("mipsisa64r6el-unknown-linux-gnuabi64", "mipsisa64r6el-unknown-linux-gnuabi64"), - ("msp430-none-elf", "msp430-none-elf"), - ("nvptx64-nvidia-cuda", "nvptx64-nvidia-cuda"), - ("powerpc-unknown-freebsd", "powerpc-unknown-freebsd13.0"), - ("powerpc-unknown-helenos", "powerpc-unknown-helenos"), - ("powerpc-unknown-linux-gnu", "powerpc-unknown-linux-gnu"), - ("powerpc-unknown-linux-gnuspe", "powerpc-unknown-linux-gnuspe"), - ("powerpc-unknown-linux-musl", "powerpc-unknown-linux-musl"), - ("powerpc-unknown-linux-muslspe", "powerpc-unknown-linux-muslspe"), - ("powerpc-unknown-netbsd", "powerpc-unknown-netbsd"), - ("powerpc-unknown-openbsd", "powerpc-unknown-openbsd"), - ("powerpc-wrs-vxworks", "powerpc-unknown-linux-gnu"), - ("powerpc-wrs-vxworks-spe", "powerpc-unknown-linux-gnuspe"), - ("powerpc64-ibm-aix", "powerpc64-ibm-aix"), - ("powerpc64-unknown-freebsd", "powerpc64-unknown-freebsd"), - ("powerpc64-unknown-linux-gnu", "powerpc64-unknown-linux-gnu"), - ("powerpc64-unknown-linux-musl", "powerpc64-unknown-linux-musl"), - ("powerpc64-unknown-openbsd", "powerpc64-unknown-openbsd"), - ("powerpc64-wrs-vxworks", "powerpc64-unknown-linux-gnu"), - ("powerpc64le-unknown-freebsd", "powerpc64le-unknown-freebsd"), - ("powerpc64le-unknown-linux-gnu", "powerpc64le-unknown-linux-gnu"), - ("powerpc64le-unknown-linux-musl", "powerpc64le-unknown-linux-musl"), - ("riscv32-wrs-vxworks", "riscv32-unknown-linux-gnu"), - ("riscv32e-unknown-none-elf", "riscv32"), - ("riscv32em-unknown-none-elf", "riscv32"), - ("riscv32emc-unknown-none-elf", "riscv32"), - ("riscv32gc-unknown-linux-gnu", "riscv32-unknown-linux-gnu"), - ("riscv32gc-unknown-linux-musl", "riscv32-unknown-linux-musl"), - ("riscv32i-unknown-none-elf", "riscv32"), - ("riscv32im-risc0-zkvm-elf", "riscv32"), - ("riscv32im-unknown-none-elf", "riscv32"), - ("riscv32ima-unknown-none-elf", "riscv32"), - ("riscv32imac-esp-espidf", "riscv32"), - ("riscv32imac-unknown-none-elf", "riscv32"), - ("riscv32imac-unknown-nuttx-elf", "riscv32"), - ("riscv32imac-unknown-xous-elf", "riscv32"), - ("riscv32imafc-esp-espidf", "riscv32"), - ("riscv32imafc-unknown-none-elf", "riscv32"), - ("riscv32imafc-unknown-nuttx-elf", "riscv32"), - ("riscv32imc-esp-espidf", "riscv32"), - ("riscv32imc-unknown-none-elf", "riscv32"), - ("riscv32imc-unknown-nuttx-elf", "riscv32"), - ("riscv64-linux-android", "riscv64-linux-android"), - ("riscv64-wrs-vxworks", "riscv64-unknown-linux-gnu"), - ("riscv64a23-unknown-linux-gnu", "riscv64-unknown-linux-gnu"), - ("riscv64gc-unknown-freebsd", "riscv64-unknown-freebsd"), - ("riscv64gc-unknown-fuchsia", "riscv64-unknown-fuchsia"), - ("riscv64gc-unknown-hermit", "riscv64-unknown-hermit"), - ("riscv64gc-unknown-linux-gnu", "riscv64-unknown-linux-gnu"), - ("riscv64gc-unknown-linux-musl", "riscv64-unknown-linux-musl"), - ("riscv64gc-unknown-managarm-mlibc", "riscv64-unknown-managarm-mlibc"), - ("riscv64gc-unknown-netbsd", "riscv64-unknown-netbsd"), - ("riscv64gc-unknown-none-elf", "riscv64"), - ("riscv64gc-unknown-nuttx-elf", "riscv64"), - ("riscv64gc-unknown-openbsd", "riscv64-unknown-openbsd"), - ("riscv64gc-unknown-redox", "riscv64-unknown-redox"), - ("riscv64im-unknown-none-elf", "riscv64"), - ("riscv64imac-unknown-none-elf", "riscv64"), - ("riscv64imac-unknown-nuttx-elf", "riscv64"), - ("s390x-unknown-linux-gnu", "s390x-unknown-linux-gnu"), - ("s390x-unknown-linux-musl", "s390x-unknown-linux-musl"), - ("s390x-unknown-none-softfloat", "s390x-unknown-linux-gnu"), - ("sparc-unknown-linux-gnu", "sparc-unknown-linux-gnu"), - ("sparc-unknown-none-elf", "sparc-unknown-none-elf"), - ("sparc64-unknown-helenos", "sparc64-unknown-helenos"), - ("sparc64-unknown-linux-gnu", "sparc64-unknown-linux-gnu"), - ("sparc64-unknown-netbsd", "sparc64-unknown-netbsd"), - ("sparc64-unknown-openbsd", "sparc64-unknown-openbsd"), - ("sparcv9-sun-solaris", "sparcv9-sun-solaris"), - ("thumbv4t-none-eabi", "thumbv4t-none-eabi"), - ("thumbv5te-none-eabi", "thumbv5te-none-eabi"), - ("thumbv6-none-eabi", "thumbv6-none-eabi"), - ("thumbv6m-none-eabi", "thumbv6m-none-eabi"), - ("thumbv6m-nuttx-eabi", "thumbv6m-none-eabi"), - ("thumbv7a-none-eabi", "thumbv7a-none-eabi"), - ("thumbv7a-none-eabihf", "thumbv7a-none-eabihf"), - ("thumbv7a-nuttx-eabi", "thumbv7a-none-eabi"), - ("thumbv7a-nuttx-eabihf", "thumbv7a-none-eabihf"), - ("thumbv7a-pc-windows-msvc", "thumbv7a-pc-windows-msvc"), - ("thumbv7a-uwp-windows-msvc", "thumbv7a-pc-windows-msvc"), - ("thumbv7em-none-eabi", "thumbv7em-none-eabi"), - ("thumbv7em-none-eabihf", "thumbv7em-none-eabihf"), - ("thumbv7em-nuttx-eabi", "thumbv7em-none-eabi"), - ("thumbv7em-nuttx-eabihf", "thumbv7em-none-eabihf"), - ("thumbv7m-none-eabi", "thumbv7m-none-eabi"), - ("thumbv7m-nuttx-eabi", "thumbv7m-none-eabi"), - ("thumbv7neon-linux-androideabi", "armv7-none-linux-android"), - ("thumbv7neon-unknown-linux-gnueabihf", "armv7-unknown-linux-gnueabihf"), - ("thumbv7neon-unknown-linux-musleabihf", "armv7-unknown-linux-musleabihf"), - ("thumbv7r-none-eabi", "thumbv7r-none-eabi"), - ("thumbv7r-none-eabihf", "thumbv7r-none-eabihf"), - ("thumbv8m.base-none-eabi", "thumbv8m.base-none-eabi"), - ("thumbv8m.base-nuttx-eabi", "thumbv8m.base-none-eabi"), - ("thumbv8m.main-none-eabi", "thumbv8m.main-none-eabi"), - ("thumbv8m.main-none-eabihf", "thumbv8m.main-none-eabihf"), - ("thumbv8m.main-nuttx-eabi", "thumbv8m.main-none-eabi"), - ("thumbv8m.main-nuttx-eabihf", "thumbv8m.main-none-eabihf"), - ("thumbv8r-none-eabihf", "thumbv8r-none-eabihf"), - ("wasm32-unknown-emscripten", "wasm32-unknown-emscripten"), - ("wasm32-unknown-unknown", "wasm32-unknown-unknown"), - ("wasm32-wali-linux-musl", "wasm32-linux-muslwali"), - ("wasm32-wasi", "wasm32-wasi"), - ("wasm32-wasip1", "wasm32-wasip1"), - ("wasm32-wasip1-threads", "wasm32-wasi"), - ("wasm32-wasip2", "wasm32-wasip2"), - ("wasm32-wasip3", "wasm32-wasip3"), - ("wasm32v1-none", "wasm32-unknown-unknown"), - ("wasm64-unknown-unknown", "wasm64-unknown-unknown"), - ("x86_64-apple-darwin", "x86_64-apple-macosx"), - ("x86_64-apple-ios", "x86_64-apple-ios-simulator"), - ("x86_64-apple-ios-macabi", "x86_64-apple-ios-macabi"), - ("x86_64-apple-tvos", "x86_64-apple-tvos-simulator"), - ("x86_64-apple-watchos-sim", "x86_64-apple-watchos-simulator"), - ("x86_64-fortanix-unknown-sgx", "x86_64-elf"), - ("x86_64-fuchsia", "x86_64-fuchsia"), - ("x86_64-linux-android", "x86_64-linux-android"), - ("x86_64-lynx-lynxos178", "x86_64-unknown-unknown-gnu"), - ("x86_64-pc-cygwin", "x86_64-pc-cygwin"), - ("x86_64-pc-nto-qnx710", "x86_64-pc-unknown"), - ("x86_64-pc-nto-qnx710_iosock", "x86_64-pc-unknown"), - ("x86_64-pc-nto-qnx800", "x86_64-pc-unknown"), - ("x86_64-pc-solaris", "x86_64-pc-solaris"), - ("x86_64-pc-windows-gnu", "x86_64-pc-windows-gnu"), - ("x86_64-pc-windows-gnullvm", "x86_64-pc-windows-gnu"), - ("x86_64-pc-windows-msvc", "x86_64-pc-windows-msvc"), - ("x86_64-sun-solaris", "x86_64-pc-solaris"), - ("x86_64-unikraft-linux-musl", "x86_64-unknown-linux-musl"), - ("x86_64-unknown-dragonfly", "x86_64-unknown-dragonfly"), - ("x86_64-unknown-freebsd", "x86_64-unknown-freebsd"), - ("x86_64-unknown-fuchsia", "x86_64-unknown-fuchsia"), - ("x86_64-unknown-haiku", "x86_64-unknown-haiku"), - ("x86_64-unknown-helenos", "x86_64-unknown-helenos"), - ("x86_64-unknown-hermit", "x86_64-unknown-hermit"), - ("x86_64-unknown-hurd-gnu", "x86_64-unknown-hurd-gnu"), - ("x86_64-unknown-illumos", "x86_64-pc-solaris"), - ("x86_64-unknown-l4re-uclibc", "x86_64-unknown-l4re-gnu"), - ("x86_64-unknown-linux-gnu", "x86_64-unknown-linux-gnu"), - ("x86_64-unknown-linux-gnuasan", "x86_64-unknown-linux-gnu"), - ("x86_64-unknown-linux-gnumsan", "x86_64-unknown-linux-gnu"), - ("x86_64-unknown-linux-gnutsan", "x86_64-unknown-linux-gnu"), - ("x86_64-unknown-linux-gnux32", "x86_64-unknown-linux-gnux32"), - ("x86_64-unknown-linux-musl", "x86_64-unknown-linux-musl"), - ("x86_64-unknown-linux-none", "x86_64-unknown-linux-none"), - ("x86_64-unknown-linux-ohos", "x86_64-unknown-linux-ohos"), - ("x86_64-unknown-managarm-mlibc", "x86_64-unknown-managarm-mlibc"), - ("x86_64-unknown-motor", "x86_64-unknown-none-elf"), - ("x86_64-unknown-netbsd", "x86_64-unknown-netbsd"), - ("x86_64-unknown-none", "x86_64-unknown-none-elf"), - ("x86_64-unknown-none-linuxkernel", "x86_64-unknown-none-elf"), - ("x86_64-unknown-openbsd", "x86_64-unknown-openbsd"), - ("x86_64-unknown-redox", "x86_64-unknown-redox"), - ("x86_64-unknown-trusty", "x86_64-unknown-unknown-musl"), - ("x86_64-unknown-uefi", "x86_64-unknown-windows"), - ("x86_64-uwp-windows-gnu", "x86_64-pc-windows-gnu"), - ("x86_64-uwp-windows-msvc", "x86_64-pc-windows-msvc"), - ("x86_64-win7-windows-gnu", "x86_64-pc-windows-gnu"), - ("x86_64-win7-windows-msvc", "x86_64-pc-windows-msvc"), - ("x86_64-wrs-vxworks", "x86_64-unknown-linux-gnu"), - ("x86_64h-apple-darwin", "x86_64h-apple-macosx"), - ("xtensa-esp32-espidf", "xtensa-none-elf"), - ("xtensa-esp32-none-elf", "xtensa-none-elf"), - ("xtensa-esp32s2-espidf", "xtensa-none-elf"), - ("xtensa-esp32s2-none-elf", "xtensa-none-elf"), - ("xtensa-esp32s3-espidf", "xtensa-none-elf"), - ("xtensa-esp32s3-none-elf", "xtensa-none-elf"), -]; diff --git a/cc-1.2.63/src/target/llvm.rs b/cc-1.2.63/src/target/llvm.rs deleted file mode 100644 index cff0995967..0000000000 --- a/cc-1.2.63/src/target/llvm.rs +++ /dev/null @@ -1,316 +0,0 @@ -use std::borrow::Cow; - -use super::{generated, TargetInfo}; - -impl TargetInfo<'_> { - /// The LLVM/Clang target triple. - /// - /// See . - /// - /// Rust and Clang don't really agree on target naming, so we first try to - /// find the matching trible based on `rustc`'s output, but if no such - /// triple exists, we attempt to construct the triple from scratch. - /// - /// NOTE: You should never need to match on this explicitly, use the - /// fields on [`TargetInfo`] instead. - pub(crate) fn llvm_target( - &self, - rustc_target: &str, - version: Option<&str>, - ) -> Cow<'static, str> { - if rustc_target == "armv7-apple-ios" { - // FIXME(madsmtm): Unnecessary once we bump MSRV to Rust 1.74 - return Cow::Borrowed("armv7-apple-ios"); - } else if self.os == "uefi" { - // Override the UEFI LLVM targets. - // - // The rustc mappings (as of 1.82) for the UEFI targets are: - // * i686-unknown-uefi -> i686-unknown-windows-gnu - // * x86_64-unknown-uefi -> x86_64-unknown-windows - // * aarch64-unknown-uefi -> aarch64-unknown-windows - // - // However, in cc-rs all the UEFI targets use - // -windows-gnu. This has been the case since 2021 [1]. - // * i686-unknown-uefi -> i686-unknown-windows-gnu - // * x86_64-unknown-uefi -> x86_64-unknown-windows-gnu - // * aarch64-unknown-uefi -> aarch64-unknown-windows-gnu - // - // For now, override the UEFI mapping to keep the behavior - // of cc-rs unchanged. - // - // TODO: as discussed in [2], it may be possible to switch - // to new UEFI targets added to clang, and regardless it - // would be good to have consistency between rustc and - // cc-rs. - // - // [1]: https://github.com/rust-lang/cc-rs/pull/623 - // [2]: https://github.com/rust-lang/cc-rs/pull/1264 - return Cow::Owned(format!("{}-unknown-windows-gnu", self.full_arch)); - } - - // If no version is requested, let's take the triple directly from - // `rustc` (the logic below is not yet good enough for most targets). - // - // FIXME(madsmtm): This should ideally be removed. - if version.is_none() { - if let Ok(index) = generated::LLVM_TARGETS - .binary_search_by_key(&rustc_target, |(rustc_target, _)| rustc_target) - { - let (_, llvm_target) = &generated::LLVM_TARGETS[index]; - return Cow::Borrowed(llvm_target); - } - } - - // Otherwise, attempt to construct the triple from the target info. - - let arch = match self.full_arch { - riscv32 if riscv32.starts_with("riscv32") => "riscv32", - riscv64 if riscv64.starts_with("riscv64") => "riscv64", - "aarch64" if self.vendor == "apple" => "arm64", - "armv7" if self.vendor == "sony" => "thumbv7a", // FIXME - arch => arch, - }; - let vendor = match self.vendor { - "kmc" | "nintendo" => "unknown", - "unknown" if self.os == "android" => "linux", - "uwp" => "pc", - "espressif" => "", - _ if self.arch == "msp430" => "", - vendor => vendor, - }; - let os = match self.os { - "macos" => "macosx", - "visionos" => "xros", - "uefi" => "windows", - "solid_asp3" | "horizon" | "teeos" | "nuttx" | "espidf" => "none", - "nto" => "unknown", // FIXME - "trusty" => "unknown", // FIXME - os => os, - }; - let version = version.unwrap_or(""); - let env = match self.env { - "newlib" | "nto70" | "nto71" | "nto71_iosock" | "p1" | "p2" | "relibc" | "sgx" - | "uclibc" => "", - "sim" => "simulator", - env => env, - }; - let abi = match self.abi { - "llvm" | "softfloat" | "uwp" | "vec-extabi" => "", - "ilp32" => "_ilp32", - "abi64" => "", - "elfv1" | "elfv2" => "", - abi => abi, - }; - Cow::Owned(match (vendor, env, abi) { - ("", "", "") => format!("{arch}-{os}{version}"), - ("", env, abi) => format!("{arch}-{os}{version}-{env}{abi}"), - (vendor, "", "") => format!("{arch}-{vendor}-{os}{version}"), - (vendor, env, abi) => format!("{arch}-{vendor}-{os}{version}-{env}{abi}"), - }) - } -} - -#[cfg(test)] -mod tests { - use std::process::Command; - - use crate::TargetInfo; - - #[test] - fn test_old_ios_target() { - assert_eq!( - TargetInfo { - full_arch: "armv7", - arch: "armv7", - vendor: "apple", - os: "ios", - env: "", - abi: "", - } - .llvm_target("armv7-apple-ios", None), - "armv7-apple-ios" - ); - } - - #[test] - fn basic_llvm_triple_guessing() { - assert_eq!( - TargetInfo { - full_arch: "aarch64", - arch: "aarch64", - vendor: "unknown", - os: "linux", - env: "", - abi: "", - } - .llvm_target("invalid", None), - "aarch64-unknown-linux" - ); - assert_eq!( - TargetInfo { - full_arch: "x86_64", - arch: "x86_64", - vendor: "unknown", - os: "linux", - env: "gnu", - abi: "", - } - .llvm_target("invalid", None), - "x86_64-unknown-linux-gnu" - ); - assert_eq!( - TargetInfo { - full_arch: "x86_64", - arch: "x86_64", - vendor: "unknown", - os: "linux", - env: "gnu", - abi: "eabi", - } - .llvm_target("invalid", None), - "x86_64-unknown-linux-gnueabi" - ); - assert_eq!( - TargetInfo { - full_arch: "x86_64", - arch: "x86_64", - vendor: "apple", - os: "macos", - env: "", - abi: "", - } - .llvm_target("invalid", None), - "x86_64-apple-macosx" - ); - } - - #[test] - fn llvm_version() { - assert_eq!( - TargetInfo { - full_arch: "aarch64", - arch: "aarch64", - vendor: "apple", - os: "ios", - env: "sim", - abi: "", - } - .llvm_target("aarch64-apple-ios-sim", Some("14.0")), - "arm64-apple-ios14.0-simulator" - ); - assert_eq!( - TargetInfo { - full_arch: "aarch64", - arch: "aarch64", - vendor: "apple", - os: "visionos", - env: "", - abi: "", - } - .llvm_target("aarch64-apple-visionos", Some("2.0")), - "arm64-apple-xros2.0" - ); - assert_eq!( - TargetInfo { - full_arch: "aarch64", - arch: "aarch64", - vendor: "apple", - os: "ios", - env: "", - abi: "macabi", - } - .llvm_target("aarch64-apple-ios-macabi", Some("13.1")), - "arm64-apple-ios13.1-macabi" - ); - } - - #[test] - fn uefi() { - assert_eq!( - TargetInfo { - full_arch: "i686", - arch: "x86", - vendor: "unknown", - os: "uefi", - env: "", - abi: "", - } - .llvm_target("i686-unknown-uefi", None), - "i686-unknown-windows-gnu" - ); - assert_eq!( - TargetInfo { - full_arch: "x86_64", - arch: "x86_64", - vendor: "unknown", - os: "uefi", - env: "", - abi: "", - } - .llvm_target("x86_64-unknown-uefi", None), - "x86_64-unknown-windows-gnu" - ); - assert_eq!( - TargetInfo { - full_arch: "aarch64", - arch: "aarch64", - vendor: "unknown", - os: "uefi", - env: "", - abi: "", - } - .llvm_target("aarch64-unknown-uefi", None), - "aarch64-unknown-windows-gnu" - ); - } - - #[test] - #[ignore = "not yet done"] - fn llvm_for_all_rustc_targets() { - let rustc = std::env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string()); - - let target_list = Command::new(&rustc) - .arg("--print=target-list") - .output() - .unwrap() - .stdout; - let target_list = String::from_utf8(target_list).unwrap(); - - let mut has_failure = false; - for target in target_list.lines() { - let spec_json = Command::new(&rustc) - .arg("--target") - .arg(target) - .arg("-Zunstable-options") - .arg("--print=target-spec-json") - .env("RUSTC_BOOTSTRAP", "1") // Crimes - .output() - .unwrap() - .stdout; - let spec_json = String::from_utf8(spec_json).unwrap(); - - // JSON crimes - let expected = spec_json - .split_once("llvm-target\": \"") - .unwrap() - .1 - .split_once("\"") - .unwrap() - .0; - let actual = TargetInfo::from_rustc_target(target) - .map(|target| target.llvm_target("invalid", None)); - - if Some(expected) != actual.as_deref().ok() { - eprintln!("failed comparing {target}:"); - eprintln!(" expected: Ok({expected:?})"); - eprintln!(" actual: {actual:?}"); - eprintln!(); - has_failure = true; - } - } - - if has_failure { - panic!("failed comparing targets"); - } - } -} diff --git a/cc-1.2.63/src/target/parser.rs b/cc-1.2.63/src/target/parser.rs deleted file mode 100644 index ff7fa50471..0000000000 --- a/cc-1.2.63/src/target/parser.rs +++ /dev/null @@ -1,637 +0,0 @@ -use std::mem; - -use crate::{ - target::TargetInfo, - utilities::{cargo_env_var, OnceLock}, - Error, ErrorKind, -}; - -#[derive(Debug)] -struct TargetInfoParserInner { - full_arch: Box, - arch: Box, - vendor: Box, - os: Box, - env: Box, - abi: Box, -} - -impl TargetInfoParserInner { - fn from_cargo_environment_variables() -> Result { - // `TARGET` must be present. - let target_name = cargo_env_var("TARGET")?; - - // Parse the full architecture name from the target name. - let (full_arch, _rest) = target_name.split_once('-').ok_or(Error::new( - ErrorKind::InvalidTarget, - format!("target `{target_name}` only had a single component (at least two required)"), - ))?; - - let cargo_env = |key, fallback: Option<&str>| match cargo_env_var(key) { - Ok(var) => Ok(var.into_boxed_str()), - Err(err) => match fallback { - Some(fallback) => Ok(fallback.into()), - None => Err(Error::new( - ErrorKind::EnvVarNotFound, - format!( - "did not find fallback information for target `{target_name}`: {}", - err.message - ), - )), - }, - }; - - // Prefer to use `CARGO_ENV_*` if set, since these contain the most - // correct information relative to the current `rustc`, and makes it - // possible to support custom target JSON specs unknown to `rustc`. - // - // NOTE: If the user is using an older `rustc`, that data may be older - // than our pre-generated data, but we still prefer Cargo's view of - // the world, since at least `cc` won't differ from `rustc` in that - // case. - // - // These may not be set in case the user depended on being able to - // just set `TARGET` outside of build scripts; in those cases, fall - // back back to data from the known set of target names instead. - // - // See discussion in #1225 for further details. - let fallback_target = TargetInfo::from_rustc_target(&target_name).ok(); - let ft = fallback_target.as_ref(); - let arch = cargo_env("CARGO_CFG_TARGET_ARCH", ft.map(|t| t.arch))?; - let vendor = cargo_env("CARGO_CFG_TARGET_VENDOR", ft.map(|t| t.vendor))?; - let os = cargo_env("CARGO_CFG_TARGET_OS", ft.map(|t| t.os))?; - let mut env = cargo_env("CARGO_CFG_TARGET_ENV", ft.map(|t| t.env))?; - // `target_abi` was stabilized in Rust 1.78, which is higher than our - // MSRV, so it may not always be available; In that case, fall back to - // `""`, which is _probably_ correct for unknown target names. - let mut abi = cargo_env("CARGO_CFG_TARGET_ABI", ft.map(|t| t.abi)) - .unwrap_or_else(|_| String::default().into_boxed_str()); - - // Remove `macabi` and `sim` from `target_abi` (if present), it's been moved to `target_env`. - // TODO: Remove once MSRV is bumped to 1.91 and `rustc` removes these from `target_abi`. - if matches!(&*abi, "macabi" | "sim") { - debug_assert!( - matches!(&*env, "" | "macabi" | "sim"), - "env/abi mismatch: {:?}, {:?}", - env, - abi, - ); - env = mem::replace(&mut abi, String::default().into_boxed_str()); - } - - Ok(Self { - full_arch: full_arch.to_string().into_boxed_str(), - arch, - vendor, - os, - env, - abi, - }) - } -} - -/// Parser for [`TargetInfo`], contains cached information. -#[derive(Default, Debug)] -pub(crate) struct TargetInfoParser(OnceLock>); - -impl TargetInfoParser { - pub fn parse_from_cargo_environment_variables(&self) -> Result, Error> { - match self - .0 - .get_or_init(TargetInfoParserInner::from_cargo_environment_variables) - { - Ok(TargetInfoParserInner { - full_arch, - arch, - vendor, - os, - env, - abi, - }) => Ok(TargetInfo { - full_arch, - arch, - vendor, - os, - env, - abi, - }), - Err(e) => Err(e.clone()), - } - } -} - -/// Parse the full architecture in the target name into the simpler -/// `cfg(target_arch = "...")` that `rustc` exposes. -fn parse_arch(full_arch: &str) -> Option<&str> { - // NOTE: Some of these don't necessarily match an existing target in - // `rustc`. They're parsed anyhow to be as forward-compatible as possible, - // while still being correct. - // - // See also: - // https://docs.rs/cfg-expr/0.18.0/cfg_expr/targets/index.html - // https://docs.rs/target-lexicon/0.13.2/target_lexicon/enum.Architecture.html - // https://gcc.gnu.org/onlinedocs/gcc/Submodel-Options.html - // `clang -print-targets` - Some(match full_arch { - arch if arch.starts_with("mipsisa32r6") => "mips32r6", // mipsisa32r6 | mipsisa32r6el - arch if arch.starts_with("mipsisa64r6") => "mips64r6", // mipsisa64r6 | mipsisa64r6el - - arch if arch.starts_with("mips64") => "mips64", // mips64 | mips64el - arch if arch.starts_with("mips") => "mips", // mips | mipsel - - arch if arch.starts_with("loongarch64") => "loongarch64", - arch if arch.starts_with("loongarch32") => "loongarch32", - - arch if arch.starts_with("powerpc64") => "powerpc64", // powerpc64 | powerpc64le - arch if arch.starts_with("powerpc") => "powerpc", - arch if arch.starts_with("ppc64") => "powerpc64", - arch if arch.starts_with("ppc") => "powerpc", - - arch if arch.starts_with("x86_64") => "x86_64", // x86_64 | x86_64h - arch if arch.starts_with("i") && arch.ends_with("86") => "x86", // i386 | i586 | i686 - - "arm64ec" => "arm64ec", // https://github.com/rust-lang/rust/issues/131172 - arch if arch.starts_with("aarch64") => "aarch64", // arm64e | arm64_32 - arch if arch.starts_with("arm64") => "aarch64", // aarch64 | aarch64_be - - arch if arch.starts_with("arm") => "arm", // arm | armv7s | armeb | ... - arch if arch.starts_with("thumb") => "arm", // thumbv4t | thumbv7a | thumbv8m | ... - - arch if arch.starts_with("riscv64") => "riscv64", - arch if arch.starts_with("riscv32") => "riscv32", - - arch if arch.starts_with("wasm64") => "wasm64", - arch if arch.starts_with("wasm32") => "wasm32", // wasm32 | wasm32v1 - "asmjs" => "wasm32", - - arch if arch.starts_with("nvptx64") => "nvptx64", - arch if arch.starts_with("nvptx") => "nvptx", - - arch if arch.starts_with("bpf") => "bpf", // bpfeb | bpfel - arch if arch.starts_with("sh4") => "sh4", // sh4 | sh4-unknown-linux-gnu | sh4-unknown-redox - - // https://github.com/bytecodealliance/wasmtime/tree/v30.0.1/pulley - arch if arch.starts_with("pulley64") => "pulley64", - arch if arch.starts_with("pulley32") => "pulley32", - - // https://github.com/Clever-ISA/Clever-ISA - arch if arch.starts_with("clever") => "clever", - - "sparc" | "sparcv7" | "sparcv8" => "sparc", - "sparc64" | "sparcv9" => "sparc64", - - "amdgcn" => "amdgpu", - "avr" => "avr", - "csky" => "csky", - "hexagon" => "hexagon", - "m68k" => "m68k", - "msp430" => "msp430", - "r600" => "r600", - "s390x" => "s390x", - "xtensa" => "xtensa", - - // Arches supported by gcc, but not LLVM. - arch if arch.starts_with("alpha") => "alpha", // DEC Alpha - "hppa" => "hppa", // https://en.wikipedia.org/wiki/PA-RISC, also known as HPPA - arch if arch.starts_with("sh") => "sh", // SuperH - _ => return None, - }) -} - -/// Parse environment and ABI from the last component of the target name. -fn parse_envabi(last_component: &str) -> Option<(&str, &str)> { - let (env, abi) = match last_component { - // Combined environment and ABI - - // gnullvm | gnueabi | gnueabihf | gnuabiv2 | gnuabi64 | gnuspe | gnux32 | gnu_ilp32 - env_and_abi if env_and_abi.starts_with("gnu") => { - let abi = env_and_abi.strip_prefix("gnu").unwrap(); - let abi = abi.strip_prefix("_").unwrap_or(abi); - ("gnu", abi) - } - // musl | musleabi | musleabihf | muslabi64 | muslspe - env_and_abi if env_and_abi.starts_with("musl") => { - ("musl", env_and_abi.strip_prefix("musl").unwrap()) - } - // uclibc | uclibceabi | uclibceabihf - env_and_abi if env_and_abi.starts_with("uclibc") => { - ("uclibc", env_and_abi.strip_prefix("uclibc").unwrap()) - } - // newlib | newlibeabihf - env_and_abi if env_and_abi.starts_with("newlib") => { - ("newlib", env_and_abi.strip_prefix("newlib").unwrap()) - } - - // Environments - "msvc" => ("msvc", ""), - "ohos" => ("ohos", ""), - "qnx700" => ("nto70", ""), - "qnx710_iosock" => ("nto71_iosock", ""), - "qnx710" => ("nto71", ""), - "qnx800" => ("nto80", ""), - "sgx" => ("sgx", ""), - "threads" => ("threads", ""), - "mlibc" => ("mlibc", ""), - "relibc" => ("relibc", ""), - - // ABIs - "abi64" => ("", "abi64"), - "abiv2" => ("", "spe"), - "eabi" => ("", "eabi"), - "eabihf" => ("", "eabihf"), - "macabi" => ("macabi", ""), - "sim" => ("sim", ""), - "softfloat" => ("", "softfloat"), - "spe" => ("", "spe"), - "x32" => ("", "x32"), - - // Badly named targets, ELF is already known from target OS. - // Probably too late to fix now though. - "elf" => ("", ""), - // Undesirable to expose to user code (yet): - // https://github.com/rust-lang/rust/pull/131166#issuecomment-2389541917 - "freestanding" => ("", ""), - - _ => return None, - }; - Some((env, abi)) -} - -impl<'a> TargetInfo<'a> { - pub(crate) fn from_rustc_target(target: &'a str) -> Result { - // FIXME(madsmtm): This target should be renamed, cannot be parsed - // with the means we do below (since `none` must not be interpreted - // as an env/ABI). - if target == "x86_64-unknown-linux-none" { - return Ok(Self { - full_arch: "x86_64", - arch: "x86_64", - vendor: "unknown", - os: "linux", - env: "", - abi: "", - }); - } - - if target == "armv7a-vex-v5" { - return Ok(Self { - full_arch: "armv7a", - arch: "arm", - vendor: "vex", - os: "vexos", - env: "v5", - abi: "eabihf", - }); - } - - let mut components = target.split('-'); - - // Insist that the target name contains at least a valid architecture. - let full_arch = components.next().ok_or(Error::new( - ErrorKind::InvalidTarget, - "target was empty".to_string(), - ))?; - let arch = parse_arch(full_arch).ok_or_else(|| { - Error::new( - ErrorKind::UnknownTarget, - format!("target `{target}` had an unknown architecture"), - ) - })?; - - // Newer target names have begun omitting the vendor, so the only - // component we know must be there is the OS name. - let components: Vec<_> = components.collect(); - let (vendor, os, mut env, mut abi) = match &*components { - [] => { - return Err(Error::new( - ErrorKind::InvalidTarget, - format!("target `{target}` must have at least two components"), - )) - } - // Two components; format is `arch-os`. - [os] => ("unknown", *os, "", ""), - // The three-component case is a bit tricky to handle, it could - // either have the format `arch-vendor-os` or `arch-os-env+abi`. - [vendor_or_os, os_or_envabi] => { - // We differentiate between these by checking if the last - // component is an env/ABI; if it isn't, then it's probably - // an OS instead. - if let Some((env, abi)) = parse_envabi(os_or_envabi) { - ("unknown", *vendor_or_os, env, abi) - } else { - (*vendor_or_os, *os_or_envabi, "", "") - } - } - // Four components; format is `arch-vendor-os-env+abi`. - [vendor, os, envabi] => { - let (env, abi) = parse_envabi(envabi).ok_or_else(|| { - Error::new( - ErrorKind::UnknownTarget, - format!("unknown environment/ABI `{envabi}` in target `{target}`"), - ) - })?; - (*vendor, *os, env, abi) - } - _ => { - return Err(Error::new( - ErrorKind::InvalidTarget, - format!("too many components in target `{target}`"), - )) - } - }; - - // Part of the architecture name is carried over into the ABI. - match full_arch { - // https://github.com/rust-lang/compiler-team/issues/830 - arch if arch.starts_with("riscv32e") => { - abi = "ilp32e"; - } - _ => {} - } - - // Various environment/ABIs are determined based on OS name. - match os { - "3ds" | "rtems" | "espidf" => env = "newlib", - "vxworks" => env = "gnu", - "redox" => env = "relibc", - "aix" => abi = "vec-extabi", - _ => {} - } - - // Extra overrides for badly named targets. - match target { - // Actually simulator targets. - "i386-apple-ios" | "x86_64-apple-ios" | "x86_64-apple-tvos" => { - env = "sim"; - } - // Name should've contained `muslabi64`. - "mips64-openwrt-linux-musl" => { - abi = "abi64"; - } - // Specifies abi even though not in name. - "armv6-unknown-freebsd" | "armv6k-nintendo-3ds" | "armv7-unknown-freebsd" => { - abi = "eabihf"; - } - // Specifies abi even though not in name. - "armv7-unknown-linux-ohos" | "armv7-unknown-trusty" => { - abi = "eabi"; - } - _ => {} - } - - let os = match os { - // Horizon is the common/internal OS name for 3DS and the Switch. - "3ds" | "switch" => "horizon", - // FIXME(madsmtm): macOS targets are badly named. - "darwin" => "macos", - - // WASI targets contain the preview version in them too. Should've - // been `wasi-p1`/`wasi-p2`, but that's probably too late now. - os if os.starts_with("wasi") => { - env = os.strip_prefix("wasi").unwrap(); - "wasi" - } - // FIXME(madsmtm): Badly named targets `*-linux-androideabi`, - // should be `*-android-eabi`. - "androideabi" => { - abi = "eabi"; - "android" - } - - os => os, - }; - - let vendor = match vendor { - // esp, esp32, esp32s2 etc. - vendor if vendor.starts_with("esp") => "espressif", - // FIXME(madsmtm): Badly named targets `*-linux-android*`, - // "linux" makes no sense as the vendor name. - "linux" if os == "android" || os == "androideabi" => "unknown", - // FIXME(madsmtm): Fix in `rustc` after - // https://github.com/rust-lang/compiler-team/issues/850. - "wali" => "unknown", - "lynx" => "unknown", - // Some Linux distributions set their name as the target vendor, - // so we have to assume that it can be an arbitary string. - vendor => vendor, - }; - - // Intentionally also marked as an ABI: - // https://github.com/rust-lang/rust/pull/86922 - if vendor == "fortanix" { - abi = "fortanix"; - } - if vendor == "uwp" { - abi = "uwp"; - } - if ["powerpc64-unknown-linux-gnu", "powerpc64-wrs-vxworks"].contains(&target) { - abi = "elfv1"; - } - if [ - "powerpc64-unknown-freebsd", - "powerpc64-unknown-linux-musl", - "powerpc64-unknown-openbsd", - "powerpc64le-unknown-freebsd", - "powerpc64le-unknown-linux-gnu", - "powerpc64le-unknown-linux-musl", - ] - .contains(&target) - { - abi = "elfv2"; - } - - if ["asan", "msan", "tsan"].contains(&abi) { - abi = ""; - } - - Ok(Self { - full_arch, - arch, - vendor, - os, - env, - abi, - }) - } -} - -#[cfg(test)] -#[allow(unexpected_cfgs)] -mod tests { - use std::process::Command; - - use super::TargetInfo; - use crate::ErrorKind; - - // Test tier 1 targets. - #[test] - fn tier1() { - let targets = [ - "aarch64-unknown-linux-gnu", - "aarch64-apple-darwin", - "i686-pc-windows-gnu", - "i686-pc-windows-msvc", - "i686-unknown-linux-gnu", - "x86_64-apple-darwin", - "x86_64-pc-windows-gnu", - "x86_64-pc-windows-msvc", - "x86_64-unknown-linux-gnu", - ]; - - for target in targets { - // Check that they parse. - let _ = TargetInfo::from_rustc_target(target).unwrap(); - } - } - - // Various custom target names not (or no longer) known by `rustc`. - #[test] - fn parse_extra() { - let targets = [ - "aarch64-unknown-none-gnu", - "aarch64-uwp-windows-gnu", - "arm-frc-linux-gnueabi", - "arm-unknown-netbsd-eabi", - "armv7neon-unknown-linux-gnueabihf", - "armv7neon-unknown-linux-musleabihf", - "thumbv7-unknown-linux-gnueabihf", - "thumbv7-unknown-linux-musleabihf", - "armv7-apple-ios", - "wasm32-wasi", - "x86_64-rumprun-netbsd", - "x86_64-unknown-linux", - "x86_64-alpine-linux-musl", - "x86_64-chimera-linux-musl", - "x86_64-foxkit-linux-musl", - "arm-poky-linux-gnueabi", - "x86_64-unknown-moturus", - "x86_64-unknown-managarm-mlibc", - ]; - - for target in targets { - // Check that they parse. - let _ = TargetInfo::from_rustc_target(target).unwrap(); - } - } - - fn target_from_rustc_cfgs<'a>(target: &'a str, cfgs: &'a str) -> TargetInfo<'a> { - // Cannot determine full architecture from cfgs. - let (full_arch, _rest) = target.split_once('-').expect("target to have arch"); - - let mut target = TargetInfo { - full_arch, - arch: "invalid-none-set", - vendor: "invalid-none-set", - os: "invalid-none-set", - env: "invalid-none-set", - // Not set in older Rust versions - abi: "", - }; - - for cfg in cfgs.lines() { - if let Some((name, value)) = cfg.split_once('=') { - // Remove whitespace, if `rustc` decided to insert any. - let name = name.trim(); - let value = value.trim(); - - // Remove quotes around value. - let value = value.strip_prefix('"').unwrap_or(value); - let value = value.strip_suffix('"').unwrap_or(value); - - match name { - "target_arch" => target.arch = value, - "target_vendor" => target.vendor = value, - "target_os" => target.os = value, - "target_env" => target.env = value, - "target_abi" => target.abi = value, - _ => {} - } - } else { - // Skip cfgs like `debug_assertions` and `unix`. - } - } - - if matches!(target.abi, "macabi" | "sim") { - assert_eq!(target.env, target.abi); - target.abi = ""; - } - - target - } - - #[test] - fn unknown_env_determined_as_unknown() { - let err = TargetInfo::from_rustc_target("aarch64-unknown-linux-bogus").unwrap_err(); - assert!(matches!(err.kind, ErrorKind::UnknownTarget)); - } - - // Used in .github/workflows/test-rustc-targets.yml - #[test] - #[cfg_attr( - not(rustc_target_test), - ignore = "must enable explicitly with --cfg=rustc_target_test" - )] - fn parse_rustc_targets() { - let rustc = std::env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string()); - - let target_list = Command::new(&rustc) - .arg("--print=target-list") - .output() - .unwrap() - .stdout; - let target_list = String::from_utf8(target_list).unwrap(); - - let mut has_failure = false; - for target in target_list.lines() { - let cfgs = Command::new(&rustc) - .arg("--target") - .arg(target) - .arg("--print=cfg") - .output() - .unwrap() - .stdout; - let cfgs = String::from_utf8(cfgs).unwrap(); - - let expected = target_from_rustc_cfgs(target, &cfgs); - let actual = TargetInfo::from_rustc_target(target); - - if Some(&expected) != actual.as_ref().ok() { - eprintln!("failed comparing {target}:"); - eprintln!(" expected: Ok({expected:?})"); - eprintln!(" actual: {actual:?}"); - eprintln!(); - has_failure = true; - } - } - - if has_failure { - panic!("failed comparing targets"); - } - } - - #[test] - fn parses_apple_envs_correctly() { - assert_eq!( - TargetInfo::from_rustc_target("aarch64-apple-ios-macabi").unwrap(), - TargetInfo { - full_arch: "aarch64", - arch: "aarch64", - vendor: "apple", - os: "ios", - env: "macabi", - abi: "", - } - ); - assert_eq!( - TargetInfo::from_rustc_target("aarch64-apple-ios-sim").unwrap(), - TargetInfo { - full_arch: "aarch64", - arch: "aarch64", - vendor: "apple", - os: "ios", - env: "sim", - abi: "", - } - ); - } -} diff --git a/cc-1.2.63/src/tempfile.rs b/cc-1.2.63/src/tempfile.rs deleted file mode 100644 index 310c91b0a0..0000000000 --- a/cc-1.2.63/src/tempfile.rs +++ /dev/null @@ -1,86 +0,0 @@ -#![cfg_attr(target_family = "wasm", allow(unused))] - -use std::{ - collections::hash_map::RandomState, - fs::{remove_file, File, OpenOptions}, - hash::{BuildHasher, Hasher}, - io, os, - path::{Path, PathBuf}, -}; - -#[cfg(not(any(unix, target_family = "wasm", windows)))] -compile_error!("Your system is not supported since cc cannot create named tempfile"); - -fn rand() -> u64 { - RandomState::new().build_hasher().finish() -} - -fn tmpname(suffix: &str) -> String { - format!("{}{}", rand(), suffix) -} - -fn create_named(path: &Path) -> io::Result { - let mut open_options = OpenOptions::new(); - - open_options.read(true).write(true).create_new(true); - - #[cfg(all(unix, not(target_os = "wasi")))] - ::mode(&mut open_options, 0o600); - - #[cfg(windows)] - ::custom_flags( - &mut open_options, - ::find_msvc_tools::windows_sys::FILE_ATTRIBUTE_TEMPORARY, - ); - - open_options.open(path) -} - -pub(super) struct NamedTempfile { - path: PathBuf, - file: Option, -} - -impl NamedTempfile { - pub(super) fn new(base: &Path, suffix: &str) -> io::Result { - for _ in 0..10 { - let path = base.join(tmpname(suffix)); - match create_named(&path) { - Ok(file) => { - return Ok(Self { - file: Some(file), - path, - }) - } - Err(e) if e.kind() == io::ErrorKind::AlreadyExists => continue, - Err(e) => return Err(e), - }; - } - - Err(io::Error::new( - io::ErrorKind::AlreadyExists, - format!( - "too many temporary files exist in base `{}` with suffix `{}`", - base.display(), - suffix - ), - )) - } - - pub(super) fn path(&self) -> &Path { - &self.path - } - - pub(super) fn take_file(&mut self) -> Option { - self.file.take() - } -} - -impl Drop for NamedTempfile { - fn drop(&mut self) { - // On Windows you have to close all handle to it before - // removing the file. - self.file.take(); - let _ = remove_file(&self.path); - } -} diff --git a/cc-1.2.63/src/tool.rs b/cc-1.2.63/src/tool.rs deleted file mode 100644 index 14692a16b3..0000000000 --- a/cc-1.2.63/src/tool.rs +++ /dev/null @@ -1,595 +0,0 @@ -use crate::{ - command_helpers::{run_output, spawn_and_wait_for_output, CargoOutput}, - run, - tempfile::NamedTempfile, - Error, ErrorKind, OutputKind, -}; -use std::{ - borrow::Cow, - collections::HashMap, - env, - ffi::{OsStr, OsString}, - io::Write, - path::{Path, PathBuf}, - process::{Command, Output, Stdio}, - sync::RwLock, -}; - -pub(crate) type CompilerFamilyLookupCache = HashMap]>, ToolFamily>; - -/// Configuration used to represent an invocation of a C compiler. -/// -/// This can be used to figure out what compiler is in use, what the arguments -/// to it are, and what the environment variables look like for the compiler. -/// This can be used to further configure other build systems (e.g. forward -/// along CC and/or CFLAGS) or the `to_command` method can be used to run the -/// compiler itself. -#[derive(Clone, Debug)] -#[allow(missing_docs)] -pub struct Tool { - pub(crate) path: PathBuf, - pub(crate) cc_wrapper_path: Option, - pub(crate) cc_wrapper_args: Vec, - pub(crate) args: Vec, - pub(crate) env: Vec<(OsString, OsString)>, - pub(crate) family: ToolFamily, - pub(crate) cuda: bool, - pub(crate) removed_args: Vec, - pub(crate) has_internal_target_arg: bool, -} - -impl Tool { - pub(crate) fn from_find_msvc_tools(tool: ::find_msvc_tools::Tool) -> Self { - let mut cc_tool = Self::with_family( - tool.path().into(), - ToolFamily::Msvc { - clang_cl: tool.is_clang_cl(), - }, - ); - - cc_tool.env = tool - .env() - .into_iter() - .map(|(k, v)| (k.clone(), v.clone())) - .collect(); - - cc_tool - } - - pub(crate) fn new( - path: PathBuf, - cached_compiler_family: &RwLock, - cargo_output: &CargoOutput, - out_dir: Option<&Path>, - ) -> Self { - Self::with_features( - path, - vec![], - false, - cached_compiler_family, - cargo_output, - out_dir, - ) - } - - pub(crate) fn with_args( - path: PathBuf, - args: Vec, - cached_compiler_family: &RwLock, - cargo_output: &CargoOutput, - out_dir: Option<&Path>, - ) -> Self { - Self::with_features( - path, - args, - false, - cached_compiler_family, - cargo_output, - out_dir, - ) - } - - /// Explicitly set the `ToolFamily`, skipping name-based detection. - pub(crate) fn with_family(path: PathBuf, family: ToolFamily) -> Self { - Self { - path, - cc_wrapper_path: None, - cc_wrapper_args: Vec::new(), - args: Vec::new(), - env: Vec::new(), - family, - cuda: false, - removed_args: Vec::new(), - has_internal_target_arg: false, - } - } - - pub(crate) fn with_features( - path: PathBuf, - args: Vec, - cuda: bool, - cached_compiler_family: &RwLock, - cargo_output: &CargoOutput, - out_dir: Option<&Path>, - ) -> Self { - fn is_zig_cc(path: &Path, cargo_output: &CargoOutput) -> bool { - run_output( - Command::new(path).arg("--version"), - // tool detection issues should always be shown as warnings - cargo_output, - ) - .map(|o| String::from_utf8_lossy(&o).contains("ziglang")) - .unwrap_or_default() - || { - match path.file_name().map(OsStr::to_string_lossy) { - Some(fname) => fname.contains("zig"), - _ => false, - } - } - } - - fn guess_family_from_stdout( - stdout: &str, - path: &Path, - args: &[String], - cargo_output: &CargoOutput, - ) -> Result { - cargo_output.print_debug(&stdout); - - // https://gitlab.kitware.com/cmake/cmake/-/blob/69a2eeb9dff5b60f2f1e5b425002a0fd45b7cadb/Modules/CMakeDetermineCompilerId.cmake#L267-271 - // stdin is set to null to ensure that the help output is never paginated. - let accepts_cl_style_flags = run( - Command::new(path).args(args).arg("-?").stdin(Stdio::null()), - &{ - // the errors are not errors! - let mut cargo_output = cargo_output.clone(); - cargo_output.warnings = cargo_output.debug; - cargo_output.output = OutputKind::Discard; - cargo_output - }, - ) - .is_ok(); - - let clang = stdout.contains(r#""clang""#); - let gcc = stdout.contains(r#""gcc""#); - let emscripten = stdout.contains(r#""emscripten""#); - let vxworks = stdout.contains(r#""VxWorks""#); - - match (clang, accepts_cl_style_flags, gcc, emscripten, vxworks) { - (clang_cl, true, _, false, false) => Ok(ToolFamily::Msvc { clang_cl }), - (true, _, _, _, false) | (_, _, _, true, false) => Ok(ToolFamily::Clang { - zig_cc: is_zig_cc(path, cargo_output), - }), - (false, false, true, _, false) | (_, _, _, _, true) => Ok(ToolFamily::Gnu), - (false, false, false, false, false) => { - cargo_output.print_warning(&"Compiler family detection failed since it does not define `__clang__`, `__GNUC__`, `__EMSCRIPTEN__` or `__VXWORKS__`, also does not accept cl style flag `-?`, fallback to treating it as GNU"); - Err(Error::new( - ErrorKind::ToolFamilyMacroNotFound, - "Expects macro `__clang__`, `__GNUC__` or `__EMSCRIPTEN__`, `__VXWORKS__` or accepts cl style flag `-?`, but found none", - )) - } - } - } - - fn detect_family_inner( - path: &Path, - args: &[String], - cargo_output: &CargoOutput, - out_dir: Option<&Path>, - ) -> Result { - let out_dir = out_dir - .map(Cow::Borrowed) - .unwrap_or_else(|| Cow::Owned(env::temp_dir())); - - // Ensure all the parent directories exist otherwise temp file creation - // will fail - std::fs::create_dir_all(&out_dir).map_err(|err| Error { - kind: ErrorKind::IOError, - message: format!("failed to create OUT_DIR '{}': {}", out_dir.display(), err) - .into(), - })?; - - let mut tmp = - NamedTempfile::new(&out_dir, "detect_compiler_family.c").map_err(|err| Error { - kind: ErrorKind::IOError, - message: format!( - "failed to create detect_compiler_family.c temp file in '{}': {}", - out_dir.display(), - err - ) - .into(), - })?; - let mut tmp_file = tmp.take_file().unwrap(); - tmp_file.write_all(include_bytes!("detect_compiler_family.c"))?; - // Close the file handle *now*, otherwise the compiler may fail to open it on Windows - // (#1082). The file stays on disk and its path remains valid until `tmp` is dropped. - tmp_file.flush()?; - tmp_file.sync_data()?; - drop(tmp_file); - - // When expanding the file, the compiler prints a lot of information to stderr - // that it is not an error, but related to expanding itself. - // - // cc would have to disable warning here to prevent generation of too many warnings. - let mut compiler_detect_output = cargo_output.clone(); - compiler_detect_output.warnings = compiler_detect_output.debug; - - let mut cmd = Command::new(path); - cmd.arg("-E").arg(tmp.path()); - - // The -Wslash-u-filename warning is normally part of stdout. - // But with clang-cl it can be part of stderr instead and exit with a - // non-zero exit code. - let mut captured_cargo_output = compiler_detect_output.clone(); - captured_cargo_output.warnings = true; - let Output { - status, - stdout, - stderr, - } = spawn_and_wait_for_output(&mut cmd, &captured_cargo_output)?; - - let stdout = if [&stdout, &stderr] - .iter() - .any(|o| String::from_utf8_lossy(o).contains("-Wslash-u-filename")) - { - run_output( - Command::new(path).arg("-E").arg("--").arg(tmp.path()), - &compiler_detect_output, - )? - } else { - if !status.success() { - return Err(Error::new( - ErrorKind::ToolExecError, - format!( - "command did not execute successfully (status code {status}): {cmd:?}" - ), - )); - } - - stdout - }; - - let stdout = String::from_utf8_lossy(&stdout); - guess_family_from_stdout(&stdout, path, args, cargo_output) - } - let detect_family = |path: &Path, args: &[String]| -> Result { - let cache_key = [path.as_os_str()] - .iter() - .cloned() - .chain(args.iter().map(OsStr::new)) - .map(Into::into) - .collect(); - if let Some(family) = cached_compiler_family.read().unwrap().get(&cache_key) { - return Ok(*family); - } - - let family = detect_family_inner(path, args, cargo_output, out_dir)?; - cached_compiler_family - .write() - .unwrap() - .insert(cache_key, family); - Ok(family) - }; - - let family = detect_family(&path, &args).unwrap_or_else(|e| { - cargo_output.print_warning(&format_args!( - "Compiler family detection failed due to error: {e}" - )); - match path.file_name().map(OsStr::to_string_lossy) { - Some(fname) if fname.contains("clang-cl") => ToolFamily::Msvc { clang_cl: true }, - Some(fname) if fname.ends_with("cl") || fname == "cl.exe" => { - ToolFamily::Msvc { clang_cl: false } - } - Some(fname) if fname.contains("clang") => { - let is_clang_cl = args - .iter() - .any(|a| a.strip_prefix("--driver-mode=") == Some("cl")); - if is_clang_cl { - ToolFamily::Msvc { clang_cl: true } - } else { - ToolFamily::Clang { - zig_cc: is_zig_cc(&path, cargo_output), - } - } - } - Some(fname) if fname.contains("zig") => ToolFamily::Clang { zig_cc: true }, - _ => ToolFamily::Gnu, - } - }); - - Tool { - path, - cc_wrapper_path: None, - cc_wrapper_args: Vec::new(), - args: Vec::new(), - env: Vec::new(), - family, - cuda, - removed_args: Vec::new(), - has_internal_target_arg: false, - } - } - - /// Add an argument to be stripped from the final command arguments. - pub(crate) fn remove_arg(&mut self, flag: OsString) { - self.removed_args.push(flag); - } - - /// Push an "exotic" flag to the end of the compiler's arguments list. - /// - /// Nvidia compiler accepts only the most common compiler flags like `-D`, - /// `-I`, `-c`, etc. Options meant specifically for the underlying - /// host C++ compiler have to be prefixed with `-Xcompiler`. - /// [Another possible future application for this function is passing - /// clang-specific flags to clang-cl, which otherwise accepts only - /// MSVC-specific options.] - pub(crate) fn push_cc_arg(&mut self, flag: OsString) { - if self.cuda { - self.args.push("-Xcompiler".into()); - } - self.args.push(flag); - } - - /// Checks if an argument or flag has already been specified or conflicts. - /// - /// Currently only checks optimization flags. - pub(crate) fn is_duplicate_opt_arg(&self, flag: &OsString) -> bool { - let flag = flag.to_str().unwrap(); - let mut chars = flag.chars(); - - // Only duplicate check compiler flags - if self.is_like_msvc() { - if chars.next() != Some('/') { - return false; - } - } else if (self.is_like_gnu() || self.is_like_clang()) && chars.next() != Some('-') { - return false; - } - - // Check for existing optimization flags (-O, /O) - if chars.next() == Some('O') { - return self - .args() - .iter() - .any(|a| a.to_str().unwrap_or("").chars().nth(1) == Some('O')); - } - - // TODO Check for existing -m..., -m...=..., /arch:... flags - false - } - - /// Don't push optimization arg if it conflicts with existing args. - pub(crate) fn push_opt_unless_duplicate(&mut self, flag: OsString) { - if self.is_duplicate_opt_arg(&flag) { - eprintln!("Info: Ignoring duplicate arg {:?}", &flag); - } else { - self.push_cc_arg(flag); - } - } - - /// Converts this compiler into a `Command` that's ready to be run. - /// - /// This is useful for when the compiler needs to be executed and the - /// command returned will already have the initial arguments and environment - /// variables configured. - pub fn to_command(&self) -> Command { - let mut cmd = match self.cc_wrapper_path { - Some(ref cc_wrapper_path) => { - let mut cmd = Command::new(cc_wrapper_path); - cmd.arg(&self.path); - cmd - } - None => Command::new(&self.path), - }; - cmd.args(&self.cc_wrapper_args); - - cmd.args(self.args.iter().filter(|a| !self.removed_args.contains(a))); - - for (k, v) in self.env.iter() { - cmd.env(k, v); - } - - cmd - } - - /// Returns the path for this compiler. - /// - /// Note that this may not be a path to a file on the filesystem, e.g. "cc", - /// but rather something which will be resolved when a process is spawned. - pub fn path(&self) -> &Path { - &self.path - } - - /// Returns the default set of arguments to the compiler needed to produce - /// executables for the target this compiler generates. - pub fn args(&self) -> &[OsString] { - &self.args - } - - /// Returns the set of environment variables needed for this compiler to - /// operate. - /// - /// This is typically only used for MSVC compilers currently. - pub fn env(&self) -> &[(OsString, OsString)] { - &self.env - } - - /// Returns the compiler command in format of CC environment variable. - /// Or empty string if CC env was not present - /// - /// This is typically used by configure script - pub fn cc_env(&self) -> OsString { - match self.cc_wrapper_path { - Some(ref cc_wrapper_path) => { - let mut cc_env = cc_wrapper_path.as_os_str().to_owned(); - cc_env.push(" "); - cc_env.push(self.path.to_path_buf().into_os_string()); - for arg in self.cc_wrapper_args.iter() { - cc_env.push(" "); - cc_env.push(arg); - } - cc_env - } - None => OsString::from(""), - } - } - - /// Returns the compiler flags in format of CFLAGS environment variable. - /// Important here - this will not be CFLAGS from env, its internal gcc's flags to use as CFLAGS - /// This is typically used by configure script - pub fn cflags_env(&self) -> OsString { - let mut flags = OsString::new(); - for (i, arg) in self.args.iter().enumerate() { - if i > 0 { - flags.push(" "); - } - flags.push(arg); - } - flags - } - - /// Whether the tool is GNU Compiler Collection-like. - pub fn is_like_gnu(&self) -> bool { - self.family == ToolFamily::Gnu - } - - /// Whether the tool is Clang-like. - pub fn is_like_clang(&self) -> bool { - matches!(self.family, ToolFamily::Clang { .. }) - } - - /// Whether the tool is AppleClang under .xctoolchain - #[cfg(target_vendor = "apple")] - pub(crate) fn is_xctoolchain_clang(&self) -> bool { - let path = self.path.to_string_lossy(); - path.contains(".xctoolchain/") - } - #[cfg(not(target_vendor = "apple"))] - pub(crate) fn is_xctoolchain_clang(&self) -> bool { - false - } - - /// Whether the tool is MSVC-like. - pub fn is_like_msvc(&self) -> bool { - matches!(self.family, ToolFamily::Msvc { .. }) - } - - /// Whether the tool is `clang-cl`-based MSVC-like. - pub fn is_like_clang_cl(&self) -> bool { - matches!(self.family, ToolFamily::Msvc { clang_cl: true }) - } - - /// Supports using `--` delimiter to separate arguments and path to source files. - pub(crate) fn supports_path_delimiter(&self) -> bool { - // homebrew clang and zig-cc does not support this while stock version does - matches!(self.family, ToolFamily::Msvc { clang_cl: true }) && !self.cuda - } -} - -/// Represents the family of tools this tool belongs to. -/// -/// Each family of tools differs in how and what arguments they accept. -/// -/// Detection of a family is done on best-effort basis and may not accurately reflect the tool. -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum ToolFamily { - /// Tool is GNU Compiler Collection-like. - Gnu, - /// Tool is Clang-like. It differs from the GCC in a sense that it accepts superset of flags - /// and its cross-compilation approach is different. - Clang { zig_cc: bool }, - /// Tool is the MSVC cl.exe. - Msvc { clang_cl: bool }, -} - -impl ToolFamily { - /// What the flag to request debug info for this family of tools look like - pub(crate) fn add_debug_flags( - &self, - cmd: &mut Tool, - debug_opt: &str, - dwarf_version: Option, - ) { - match *self { - ToolFamily::Msvc { .. } => { - cmd.push_cc_arg("-Z7".into()); - } - ToolFamily::Gnu | ToolFamily::Clang { .. } => { - match debug_opt { - // From https://doc.rust-lang.org/cargo/reference/profiles.html#debug - "" | "0" | "false" | "none" => { - debug_assert!( - false, - "earlier check should have avoided calling add_debug_flags" - ); - } - - // line-directives-only is LLVM-specific; for GCC we have to treat it like "1" - "line-directives-only" if cmd.is_like_clang() => { - cmd.push_cc_arg("-gline-directives-only".into()); - } - // Clang has -gline-tables-only, but it's an alias for -g1 anyway. - // https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-gline-tables-only - "1" | "limited" | "line-tables-only" | "line-directives-only" => { - cmd.push_cc_arg("-g1".into()); - } - "2" | "true" | "full" => { - cmd.push_cc_arg("-g".into()); - } - _ => { - // Err on the side of including too much info rather than too little. - cmd.push_cc_arg("-g".into()); - } - } - if let Some(v) = dwarf_version { - cmd.push_cc_arg(format!("-gdwarf-{v}").into()); - } - } - } - } - - /// What the flag to force frame pointers. - pub(crate) fn add_force_frame_pointer(&self, cmd: &mut Tool) { - match *self { - ToolFamily::Gnu | ToolFamily::Clang { .. } => { - cmd.push_cc_arg("-fno-omit-frame-pointer".into()); - } - _ => (), - } - } - - /// What the flags to enable all warnings - pub(crate) fn warnings_flags(&self) -> &'static str { - match *self { - ToolFamily::Msvc { .. } => "-W4", - ToolFamily::Gnu | ToolFamily::Clang { .. } => "-Wall", - } - } - - pub(crate) fn warnings_suppression_flags(&self) -> &'static str { - match *self { - ToolFamily::Msvc { .. } => "-W0", - ToolFamily::Gnu | ToolFamily::Clang { .. } => "-w", - } - } - - /// What the flags to enable extra warnings - pub(crate) fn extra_warnings_flags(&self) -> Option<&'static str> { - match *self { - ToolFamily::Msvc { .. } => None, - ToolFamily::Gnu | ToolFamily::Clang { .. } => Some("-Wextra"), - } - } - - /// What the flag to turn warning into errors - pub(crate) fn warnings_to_errors_flag(&self) -> &'static str { - match *self { - ToolFamily::Msvc { .. } => "-WX", - ToolFamily::Gnu | ToolFamily::Clang { .. } => "-Werror", - } - } - - pub(crate) fn verbose_stderr(&self) -> bool { - matches!(*self, ToolFamily::Clang { .. }) - } -} diff --git a/cc-1.2.63/src/utilities.rs b/cc-1.2.63/src/utilities.rs deleted file mode 100644 index b66da630eb..0000000000 --- a/cc-1.2.63/src/utilities.rs +++ /dev/null @@ -1,160 +0,0 @@ -use std::{ - cell::UnsafeCell, - ffi::{OsStr, OsString}, - fmt::{self, Write}, - marker::PhantomData, - mem::MaybeUninit, - panic::{RefUnwindSafe, UnwindSafe}, - path::Path, - sync::Once, -}; - -use crate::{Error, ErrorKind}; - -pub(super) struct JoinOsStrs<'a, T> { - pub(super) slice: &'a [T], - pub(super) delimiter: char, -} - -impl fmt::Display for JoinOsStrs<'_, T> -where - T: AsRef, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let len = self.slice.len(); - for (index, os_str) in self.slice.iter().enumerate() { - // TODO: Use OsStr::display once it is stablised, - // Path and OsStr has the same `Display` impl - write!(f, "{}", Path::new(os_str).display())?; - if index + 1 < len { - f.write_char(self.delimiter)?; - } - } - Ok(()) - } -} - -pub(super) struct OptionOsStrDisplay(pub(super) Option); - -impl fmt::Display for OptionOsStrDisplay -where - T: AsRef, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // TODO: Use OsStr::display once it is stablised - // Path and OsStr has the same `Display` impl - if let Some(os_str) = self.0.as_ref() { - write!(f, "Some({})", Path::new(os_str).display()) - } else { - f.write_str("None") - } - } -} - -pub(crate) struct OnceLock { - once: Once, - value: UnsafeCell>, - _marker: PhantomData, -} - -impl Default for OnceLock { - fn default() -> Self { - Self::new() - } -} - -impl OnceLock { - pub(crate) const fn new() -> Self { - Self { - once: Once::new(), - value: UnsafeCell::new(MaybeUninit::uninit()), - _marker: PhantomData, - } - } - - #[inline] - fn is_initialized(&self) -> bool { - self.once.is_completed() - } - - unsafe fn get_unchecked(&self) -> &T { - debug_assert!(self.is_initialized()); - #[allow(clippy::needless_borrow)] - #[allow(unused_unsafe)] - unsafe { - (&*self.value.get()).assume_init_ref() - } - } - - pub(crate) fn get_or_init(&self, f: impl FnOnce() -> T) -> &T { - self.once.call_once(|| { - unsafe { &mut *self.value.get() }.write(f()); - }); - unsafe { self.get_unchecked() } - } - - pub(crate) fn get(&self) -> Option<&T> { - if self.is_initialized() { - // Safe b/c checked is_initialized - Some(unsafe { self.get_unchecked() }) - } else { - None - } - } -} - -impl fmt::Debug for OnceLock { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut d = f.debug_tuple("OnceLock"); - match self.get() { - Some(v) => d.field(v), - None => d.field(&format_args!("")), - }; - d.finish() - } -} - -unsafe impl Sync for OnceLock {} -unsafe impl Send for OnceLock {} - -impl RefUnwindSafe for OnceLock {} -impl UnwindSafe for OnceLock {} - -impl Drop for OnceLock { - #[inline] - fn drop(&mut self) { - if self.once.is_completed() { - // SAFETY: The cell is initialized and being dropped, so it can't - // be accessed again. - unsafe { self.value.get_mut().assume_init_drop() }; - } - } -} - -/// Access an environment variable that's set by Cargo. -/// -/// -/// -/// Cargo doesn't need to be told about these with `rerun-if-env-changed`, and -/// that we don't want to allow overwriting them with `Build::env`. -#[allow(clippy::disallowed_methods)] // Cargo env, no need for cache busting. -pub(crate) fn cargo_env_var_os(key: &str) -> Option { - std::env::var_os(key) -} - -pub(crate) fn cargo_env_var(key: &str) -> Result { - if let Some(value) = cargo_env_var_os(key) { - match value.into_string() { - Ok(value) => Ok(value), - Err(value) => Err(Error::new( - ErrorKind::EnvVarNotFound, - format!("environment variable {key} is not valid utf-8: {value:?}"), - )), - } - } else { - Err(Error::new( - ErrorKind::EnvVarNotFound, - format!("environment variable {key} not defined"), - )) - } -} diff --git a/cc-1.2.64/.cargo-checksum.json b/cc-1.2.64/.cargo-checksum.json new file mode 100644 index 0000000000..396edf01d7 --- /dev/null +++ b/cc-1.2.64/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo_vcs_info.json":"77db733e5e85e97e9f6b4206500bef5c24f61a9bac7d88c91e30d8377e1bacd2","CHANGELOG.md":"881038baa547cbd72168cababfa2df4f487bc66cb943e97139c4fd4314d552ec","Cargo.lock":"c3868ad922a21974945424d91006966d3ad923407676a8b80603e7ca22251122","Cargo.toml":"f99b3e4e2862760817c897f78b9a919c962605cfa04c43f68e0046c132552dbc","Cargo.toml.orig":"15ec31ca419fdfc70b1e5b063482da03d0299bce012fb40c81cc13af290d31b8","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"cc952a506dc726eed3e128d47d62defbac4470bbb0e465f6a043ae8bf0c96d19","clippy.toml":"cf46251b0953d1a3c7a4f8f8941a08d8a416d5fc348dfe3f3c520eb93fcc18ea","src/command_helpers.rs":"650c9682edc3d7732249e327c1026c7cc733f3702f4867b9580223e97522d3c6","src/detect_compiler_family.c":"97ca4b021495611e828becea6187add37414186a16dfedd26c2947cbce6e8b2f","src/flags.rs":"11f22aa9741b0d445b19ab29ea1076cc32a4143316a74855b6bd682004217543","src/lib.rs":"81899387bcfec723c52e40f50a8af9fef00fb9fd75e0392c5f2658b72e084b4c","src/parallel/async_executor.rs":"4ce24435fff6b6555b43fee042c16bd65d4150d0346567f246b9190d85b45983","src/parallel/command_runner.rs":"1d17d9a037de93b31cc7ee6a914a1e1f6c1ac156cf8619f0014dcbaca6a0b215","src/parallel/job_token.rs":"858b684423bce773d04a042f3f9bafbd21deda21bbb1efefb078a20ef5d8a134","src/parallel/mod.rs":"bf156170790d40a76015a8c78cc2a391cbe1b60bb96c1fac1d047d26e342386a","src/parallel/stderr.rs":"840fe8d0ba613c98fa92cbebc374a028a3212ba7dcea58285483223a31acc7eb","src/target.rs":"a9d2d347a3a08015531f00b32bc8a7478f5636822cc8e0237ea9efa3f07d4cd4","src/target/apple.rs":"da9411b2c4db419e0fa39f765ee53b8665b3837fb39827679a53238734a4a1c1","src/target/generated.rs":"2e1d7e5d8fd954bdcc6e45ef16f69af1a798eacb8d212a516aaf8025908f6d36","src/target/llvm.rs":"190fe8d2b204cd4a6e68f2a1aada17ecd6390799564ed25792fcc08ab34710ba","src/target/parser.rs":"e2a5246a8fe46d39616410685cf7418f6b86344c5309bf09817978b182b3b38c","src/tempfile.rs":"3d9a4bd894862a345aa230a61ec266f0c68f4ef9713d1d9c727482e61f1ea7c3","src/tool.rs":"1d279a6f0738f9164ca794c85bd55951aa95e2adc788e75a6bb8133393d6cce8","src/utilities.rs":"ebb59ac01fb9588bcea0d0ee786e5d5a5696b4284a1245657625db03e830b72e"},"package":"dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f"} \ No newline at end of file diff --git a/cc-1.2.64/.cargo_vcs_info.json b/cc-1.2.64/.cargo_vcs_info.json new file mode 100644 index 0000000000..2eeb2174d0 --- /dev/null +++ b/cc-1.2.64/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "f0c89980a9c521dc5b703a20760900d9770319c0" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/cc-1.2.64/CHANGELOG.md b/cc-1.2.64/CHANGELOG.md new file mode 100644 index 0000000000..3e23ffe448 --- /dev/null +++ b/cc-1.2.64/CHANGELOG.md @@ -0,0 +1,811 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [1.2.64](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.63...cc-v1.2.64) - 2026-06-12 + +### Other + +- Add more bare-metal 32-bit Arm support ([#1753](https://github.com/rust-lang/cc-rs/pull/1753)) +- Remove unnecessary clone ([#1748](https://github.com/rust-lang/cc-rs/pull/1748)) +- Add default compiler prefixes for thumbv7a/thumbv7r/thumbv8r bare-metal targets ([#1742](https://github.com/rust-lang/cc-rs/pull/1742)) + +## [1.2.63](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.62...cc-v1.2.63) - 2026-05-29 + +### Other + +- Update shlex requirement from 1.3.0 to 2.0.1 ([#1736](https://github.com/rust-lang/cc-rs/pull/1736)) + +## [1.2.62](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.61...cc-v1.2.62) - 2026-05-08 + +### Other + +- Regenerate target info ([#1721](https://github.com/rust-lang/cc-rs/pull/1721)) +- Allow exceptions on wasm platforms ([#1714](https://github.com/rust-lang/cc-rs/pull/1714)) +- Add relibc env ([#1710](https://github.com/rust-lang/cc-rs/pull/1710)) +- recognize sh4 architecture in parse_arch() ([#1712](https://github.com/rust-lang/cc-rs/pull/1712)) + +## [1.2.61](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.60...cc-v1.2.61) - 2026-04-24 + +### Other + +- fix `OutputKind::Capture` documentation ([#1705](https://github.com/rust-lang/cc-rs/pull/1705)) + +## [1.2.60](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.59...cc-v1.2.60) - 2026-04-10 + +### Fixed + +- *(ar)* suppress warnings from `D` modifier probe ([#1700](https://github.com/rust-lang/cc-rs/pull/1700)) + +## [1.2.59](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.58...cc-v1.2.59) - 2026-04-03 + +### Fixed + +- *(ar)* deterministic archives with `D` modifier ([#1697](https://github.com/rust-lang/cc-rs/pull/1697)) + +### Other + +- Regenerate target info ([#1698](https://github.com/rust-lang/cc-rs/pull/1698)) +- Fix target abi parsing for sanitiser targets ([#1695](https://github.com/rust-lang/cc-rs/pull/1695)) + +## [1.2.58](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.57...cc-v1.2.58) - 2026-03-27 + +### Other + +- Update Compile-time Requirements to add info about clang-cl.exe ([#1693](https://github.com/rust-lang/cc-rs/pull/1693)) + +## [1.2.57](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.56...cc-v1.2.57) - 2026-03-13 + +### Other + +- Size archiver batches according to argument length not argument count ([#1689](https://github.com/rust-lang/cc-rs/pull/1689)) +- Added `Build::env` for setting environment variables of compiler invocations and other child processes ([#1656](https://github.com/rust-lang/cc-rs/pull/1656) [#1682](https://github.com/rust-lang/cc-rs/pull/1682)) + +## [1.2.56](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.55...cc-v1.2.56) - 2026-02-13 + +### Other + +- Regenerate target info ([#1676](https://github.com/rust-lang/cc-rs/pull/1676)) +- Fix `clang-cl` target when cross-compiling ([#1670](https://github.com/rust-lang/cc-rs/pull/1670)) + +## [1.2.55](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.54...cc-v1.2.55) - 2026-01-30 + +### Other + +- Regenerate target info ([#1667](https://github.com/rust-lang/cc-rs/pull/1667)) +- Fix RUSTFLAGS typo in test-linker-plugin-lto ([#1665](https://github.com/rust-lang/cc-rs/pull/1665)) +- Disable PIC for armv7-sony-vita-newlibeabihf ([#1664](https://github.com/rust-lang/cc-rs/pull/1664)) + +## [1.2.54](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.53...cc-v1.2.54) - 2026-01-23 + +### Other + +- Fix x86_64-unknown-linux-gnuasan parsing ([#1661](https://github.com/rust-lang/cc-rs/pull/1661)) +- Regenerate target info ([#1660](https://github.com/rust-lang/cc-rs/pull/1660)) + +## [1.2.53](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.52...cc-v1.2.53) - 2026-01-16 + +### Other + +- Add missing RISC-V targets ([#1657](https://github.com/rust-lang/cc-rs/pull/1657)) + +## [1.2.52](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.51...cc-v1.2.52) - 2026-01-09 + +### Other + +- Fix contradictory doc for CC compiler in crate doc ([#1650](https://github.com/rust-lang/cc-rs/pull/1650)) +- Have CUDA compilaion check for sbsa-linux when targeting aarch64. ([#1647](https://github.com/rust-lang/cc-rs/pull/1647)) +- Update link for -Cdwarf-version; Remove -Z (stabilized in 1.88) ([#1648](https://github.com/rust-lang/cc-rs/pull/1648)) +- Fix Build::env_tool to check for .exe on windows ([#1646](https://github.com/rust-lang/cc-rs/pull/1646)) + +## [1.2.51](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.50...cc-v1.2.51) - 2025-12-26 + +### Other + +- Regenerate target info ([#1642](https://github.com/rust-lang/cc-rs/pull/1642)) +- Update Readmes ([#1641](https://github.com/rust-lang/cc-rs/pull/1641)) + +## [1.2.50](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.49...cc-v1.2.50) - 2025-12-19 + +### Other + +- Add tests for `OUT_DIR` escape for '..' file paths (#1631) +- Fix #283: Make warnings(false) actually suppress compiler warnings ([#1633](https://github.com/rust-lang/cc-rs/pull/1633)) + +## [1.2.49](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.48...cc-v1.2.49) - 2025-12-06 + +### Other + +- Fix run_output to prevent infinite blocking ([#1627](https://github.com/rust-lang/cc-rs/pull/1627)) +- Fix detect_family deadlock ([#1626](https://github.com/rust-lang/cc-rs/pull/1626)) +- Fix link in new debug_str doc comment ([#1625](https://github.com/rust-lang/cc-rs/pull/1625)) +- Support more of Cargo's debug levels with Build::debug_str ([#1624](https://github.com/rust-lang/cc-rs/pull/1624)) + +## [1.2.48](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.47...cc-v1.2.48) - 2025-11-28 + +### Other + +- Regenerate target info ([#1620](https://github.com/rust-lang/cc-rs/pull/1620)) + +## [1.2.47](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.46...cc-v1.2.47) - 2025-11-21 + +### Other + +- add helenos linker identifications ([#1615](https://github.com/rust-lang/cc-rs/pull/1615)) + +## [1.2.46](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.45...cc-v1.2.46) - 2025-11-14 + +### Other + +- Add Visual Studio 2026 support ([#1609](https://github.com/rust-lang/cc-rs/pull/1609)) + +## [1.2.45](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.44...cc-v1.2.45) - 2025-11-07 + +### Other + +- Regenerate target info ([#1606](https://github.com/rust-lang/cc-rs/pull/1606)) +- Use a default check for the "env" variable in apple_sdk_name ([#1605](https://github.com/rust-lang/cc-rs/pull/1605)) + +## [1.2.44](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.43...cc-v1.2.44) - 2025-10-31 + +### Other + +- Fix debug assertion for env/abi mismatch ([#1604](https://github.com/rust-lang/cc-rs/pull/1604)) +- Update CHANGELOG for version 1.2.43 ([#1602](https://github.com/rust-lang/cc-rs/pull/1602)) +- Stop passing an invalid target to `llvm-mingw`'s cross-compilation wrappers ([#1495](https://github.com/rust-lang/cc-rs/pull/1495)) + +## [1.2.43](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.42...cc-v1.2.43) - 2025-10-24 + +### Other + +- Mark `static_flag` and `shared_flag` as deprecated ([#1582](https://github.com/rust-lang/cc-rs/pull/1582)) + +## [1.2.42](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.41...cc-v1.2.42) - 2025-10-24 + +### Other + +- Fix check-semver-checks ([#1600](https://github.com/rust-lang/cc-rs/pull/1600)) +- minor improvement for docs ([#1598](https://github.com/rust-lang/cc-rs/pull/1598)) +- Fix linker-plugin-lto: use `-flto=thin` ([#1594](https://github.com/rust-lang/cc-rs/pull/1594)) +- Disable check-buildstd for armv7k-apple-watchos ([#1599](https://github.com/rust-lang/cc-rs/pull/1599)) +- Add elf abi to ppc64 targets ([#1596](https://github.com/rust-lang/cc-rs/pull/1596)) + +## [1.2.41](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.40...cc-v1.2.41) - 2025-10-10 + +### Other + +- Allow using VCToolsVersion to request a specific msvc version ([#1589](https://github.com/rust-lang/cc-rs/pull/1589)) +- Regenerate target info ([#1592](https://github.com/rust-lang/cc-rs/pull/1592)) +- Regenerate windows sys bindings ([#1591](https://github.com/rust-lang/cc-rs/pull/1591)) +- Update windows-bindgen requirement from 0.64 to 0.65 ([#1590](https://github.com/rust-lang/cc-rs/pull/1590)) +- Fix `get_base_archiver_variant` for clang-cl: use `--print-search-dirs` ([#1587](https://github.com/rust-lang/cc-rs/pull/1587)) + +## [1.2.40](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.39...cc-v1.2.40) - 2025-10-03 + +### Other + +- Reorder changelog and remove duplicate Unreleased section ([#1579](https://github.com/rust-lang/cc-rs/pull/1579)) +- Prefer clang if linker-plugin-lto specified ([#1573](https://github.com/rust-lang/cc-rs/pull/1573)) +- Fix building for Mac Catalyst ([#1577](https://github.com/rust-lang/cc-rs/pull/1577)) +- Improve ESP microcontroller targets ([#1574](https://github.com/rust-lang/cc-rs/pull/1574)) + +## [1.2.39](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.38...cc-v1.2.39) - 2025-09-26 + +### Other + +- Fix cross compilation to xtensa-esp32s3-espidf ([#1569](https://github.com/rust-lang/cc-rs/pull/1569)) +- Fix autodetect_wasi_compiler: support non utf-8 path ([#1568](https://github.com/rust-lang/cc-rs/pull/1568)) +- Regenerate target info ([#1567](https://github.com/rust-lang/cc-rs/pull/1567)) +- Fix rustcflags mapping: require -Clinker-plugin-lto for -flto ([#1564](https://github.com/rust-lang/cc-rs/pull/1564)) +- Use `$WASI_SDK_PATH` on WASI targets by default ([#1562](https://github.com/rust-lang/cc-rs/pull/1562)) +- Fix atomicity violations in concurrent cache operations ([#1559](https://github.com/rust-lang/cc-rs/pull/1559)) + +## [1.2.38](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.37...cc-v1.2.38) - 2025-09-19 + +### Other + +- updated the following local packages: find-msvc-tools + +## [1.2.37](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.36...cc-v1.2.37) - 2025-09-12 + +### Other + +- Fix errmsg in RustcCodegenFlags::set_rustc_flag ([#1551](https://github.com/rust-lang/cc-rs/pull/1551)) +- propagate stack protector to Linux C compilers ([#1550](https://github.com/rust-lang/cc-rs/pull/1550)) +- Extract new fn `run_commands_in_parallel` ([#1549](https://github.com/rust-lang/cc-rs/pull/1549)) + +## [1.2.36](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.35...cc-v1.2.36) - 2025-09-05 + +### Other + +- Regenerate windows sys bindings ([#1548](https://github.com/rust-lang/cc-rs/pull/1548)) +- Update windows-bindgen requirement from 0.62 to 0.63 ([#1547](https://github.com/rust-lang/cc-rs/pull/1547)) +- Add fn get_ucrt_dir for find-msvc-tools ([#1546](https://github.com/rust-lang/cc-rs/pull/1546)) +- Regenerate target info ([#1544](https://github.com/rust-lang/cc-rs/pull/1544)) +- fix publish.yml ([#1543](https://github.com/rust-lang/cc-rs/pull/1543)) +- Replace periods with underscores as well when parsing env variables ([#1541](https://github.com/rust-lang/cc-rs/pull/1541)) + +## [1.2.35](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.34...cc-v1.2.35) - 2025-09-01 + +### Fixed + +- fix building for aarch64-apple-visionos-sim on nightly ([#1534](https://github.com/rust-lang/cc-rs/pull/1534)) +- fix tests apple_sdkroot_wrong ([#1530](https://github.com/rust-lang/cc-rs/pull/1530)) + +### Other + +- Regenerate target info ([#1536](https://github.com/rust-lang/cc-rs/pull/1536)) +- Optimize Tool::to_command ([#1535](https://github.com/rust-lang/cc-rs/pull/1535)) +- Extract find-msvc-tools ([#1531](https://github.com/rust-lang/cc-rs/pull/1531)) +- Add prefer_clang_cl_over_msvc ([#1516](https://github.com/rust-lang/cc-rs/pull/1516)) + +## [1.2.34](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.33...cc-v1.2.34) - 2025-08-22 + +### Fixed + +- add `-mcpu=mvp` and `-mmutable-globals` for `wasm32v1-none` ([#1524](https://github.com/rust-lang/cc-rs/pull/1524)) + +### Other + +- Optimize parse_version in find_tools.rs ([#1527](https://github.com/rust-lang/cc-rs/pull/1527)) +- Fallback to manually searching for tool dir ([#1526](https://github.com/rust-lang/cc-rs/pull/1526)) + +## [1.2.33](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.32...cc-v1.2.33) - 2025-08-15 + +### Other + +- Regenerate target info ([#1521](https://github.com/rust-lang/cc-rs/pull/1521)) +- [win][arm64ec] Add testing for Arm64EC Windows ([#1512](https://github.com/rust-lang/cc-rs/pull/1512)) +- Fix parsing of nigthly targets ([#1517](https://github.com/rust-lang/cc-rs/pull/1517)) +- [win][arm64ec] Fix finding assembler and setting is_arm for Arm64EC ([#1511](https://github.com/rust-lang/cc-rs/pull/1511)) + +## [1.2.32](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.31...cc-v1.2.32) - 2025-08-08 + +### Fixed + +- fix new clippy lint introduced in rust 1.89.0 ([#1509](https://github.com/rust-lang/cc-rs/pull/1509)) + +### Other + +- clarify cargo default if no rerun emitted ([#1508](https://github.com/rust-lang/cc-rs/pull/1508)) +- extract compile_objects_sequential ([#1507](https://github.com/rust-lang/cc-rs/pull/1507)) +- Windows `find_tools`: add support for finding Clang ([#1506](https://github.com/rust-lang/cc-rs/pull/1506)) +- Add m68k-unknown-linux-gnu cross-compile target ([#1505](https://github.com/rust-lang/cc-rs/pull/1505)) + +## [1.2.31](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.30...cc-v1.2.31) - 2025-08-01 + +### Other + +- Add doc for using sccache/ccache etc ([#1502](https://github.com/rust-lang/cc-rs/pull/1502)) +- ability to statically link against C++ stdlib ([#1497](https://github.com/rust-lang/cc-rs/pull/1497)) +- Add instructions on using sccache ([#1503](https://github.com/rust-lang/cc-rs/pull/1503)) +- Add support for recognizing some architectures supported by GCC, but not LLVM. ([#1500](https://github.com/rust-lang/cc-rs/pull/1500)) + +## [1.2.30](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.29...cc-v1.2.30) - 2025-07-18 + +### Other + +- define _REENTRANT by default ([#1496](https://github.com/rust-lang/cc-rs/pull/1496)) + +## [1.2.29](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.28...cc-v1.2.29) - 2025-07-05 + +### Other + +- Fix target parsing for powerpc ([#1490](https://github.com/rust-lang/cc-rs/pull/1490)) + +## [1.2.28](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.27...cc-v1.2.28) - 2025-07-04 + +### Other + +- Recognize `mlibc` environment ([#1488](https://github.com/rust-lang/cc-rs/pull/1488)) +- Fix clippy warnings about not using variables in `format!` strings ([#1489](https://github.com/rust-lang/cc-rs/pull/1489)) + +## [1.2.27](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.26...cc-v1.2.27) - 2025-06-13 + +### Other + +- Regenerate windows sys bindings ([#1485](https://github.com/rust-lang/cc-rs/pull/1485)) +- Update windows-bindgen requirement from 0.61 to 0.62 ([#1484](https://github.com/rust-lang/cc-rs/pull/1484)) +- Regenerate target info ([#1483](https://github.com/rust-lang/cc-rs/pull/1483)) + +## [1.2.26](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.25...cc-v1.2.26) - 2025-06-06 + +### Other + +- Also set `SDKROOT` when building apple platforms ([#1475](https://github.com/rust-lang/cc-rs/pull/1475)) +- use windows 2022 in CI ([#1479](https://github.com/rust-lang/cc-rs/pull/1479)) +- Detect -Wslash-u-filename warning on clang-cl ([#1477](https://github.com/rust-lang/cc-rs/pull/1477)) + +## [1.2.25](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.24...cc-v1.2.25) - 2025-05-30 + +### Other + +- make `powerp64` use `powerpc64-linux-gnu` prefix ([#1474](https://github.com/rust-lang/cc-rs/pull/1474)) + +## [1.2.24](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.23...cc-v1.2.24) - 2025-05-23 + +### Other + +- Regenerate windows sys bindings ([#1471](https://github.com/rust-lang/cc-rs/pull/1471)) + +## [1.2.23](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.22...cc-v1.2.23) - 2025-05-16 + +### Other + +- support "vxworks" and "nto" OSes on `get_base_archiver_variant` ([#1456](https://github.com/rust-lang/cc-rs/pull/1456)) + +## [1.2.22](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.21...cc-v1.2.22) - 2025-05-09 + +### Other + +- Add `flags` method to `cc::Build` for adding multiple flags ([#1466](https://github.com/rust-lang/cc-rs/pull/1466)) + +## [1.2.21](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.20...cc-v1.2.21) - 2025-05-02 + +### Other + +- Fix wasm32-unknown-unknown by passing -c ([#1424](https://github.com/rust-lang/cc-rs/pull/1424)) + +## [1.2.20](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.19...cc-v1.2.20) - 2025-04-25 + +### Other + +- Regenerate target info ([#1461](https://github.com/rust-lang/cc-rs/pull/1461)) +- Fix parser.rs on latest rustc nightly ([#1459](https://github.com/rust-lang/cc-rs/pull/1459)) + +## [1.2.19](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.18...cc-v1.2.19) - 2025-04-11 + +### Other + +- Fix musl compilation: Add musl as a prefix fallback ([#1455](https://github.com/rust-lang/cc-rs/pull/1455)) + +## [1.2.18](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.17...cc-v1.2.18) - 2025-04-04 + +### Other + +- Regenerate target info ([#1450](https://github.com/rust-lang/cc-rs/pull/1450)) +- Use `std::thread::available_parallelism` for determining the default number of jobs ([#1447](https://github.com/rust-lang/cc-rs/pull/1447)) +- Fix mips64-openwrt-linux-musl parsing ([#1449](https://github.com/rust-lang/cc-rs/pull/1449)) +- Use compiler prefix `x86_64-linux-musl` ([#1443](https://github.com/rust-lang/cc-rs/pull/1443)) + +## [1.2.17](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.16...cc-v1.2.17) - 2025-03-21 + +### Other + +- Regenerate target info ([#1439](https://github.com/rust-lang/cc-rs/pull/1439)) +- Regenerate windows sys bindings ([#1437](https://github.com/rust-lang/cc-rs/pull/1437)) +- Fix wasm32-wali-linux-musl target parsing ([#1434](https://github.com/rust-lang/cc-rs/pull/1434)) +- Parse `rustc` target names ([#1413](https://github.com/rust-lang/cc-rs/pull/1413)) +- Regenerate target info ([#1429](https://github.com/rust-lang/cc-rs/pull/1429)) +- Added base support for `wasm32-wali-linux-musl` target ([#1373](https://github.com/rust-lang/cc-rs/pull/1373)) + +## [1.2.16](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.15...cc-v1.2.16) - 2025-02-28 + +### Fixed + +- force windows compiler to run in `out_dir` to prevent artifacts in cwd (#1415) + +### Other + +- use `/arch:SSE2` for `x86` target arch (#1425) +- Regenerate windows-sys binding ([#1422](https://github.com/rust-lang/cc-rs/pull/1422)) +- Regenerate target info ([#1418](https://github.com/rust-lang/cc-rs/pull/1418)) +- Add LIB var when compiling flag_check (#1417) +- Change flag ordering ([#1403](https://github.com/rust-lang/cc-rs/pull/1403)) +- Fix archiver detection for musl cross compilation ([#1404](https://github.com/rust-lang/cc-rs/pull/1404)) + +## [1.2.15](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.14...cc-v1.2.15) - 2025-02-21 + +### Other + +- Regenerate target info ([#1406](https://github.com/rust-lang/cc-rs/pull/1406)) +- Always read from all `CFLAGS`-style flags ([#1401](https://github.com/rust-lang/cc-rs/pull/1401)) +- Simplify the error output on failed `Command` invocation ([#1397](https://github.com/rust-lang/cc-rs/pull/1397)) + +## [1.2.14](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.13...cc-v1.2.14) - 2025-02-14 + +### Other + +- Regenerate target info ([#1398](https://github.com/rust-lang/cc-rs/pull/1398)) +- Add support for setting `-gdwarf-{version}` based on RUSTFLAGS ([#1395](https://github.com/rust-lang/cc-rs/pull/1395)) +- Add support for alternative network stack io-sock on QNX 7.1 aarch64 and x86_64 ([#1312](https://github.com/rust-lang/cc-rs/pull/1312)) + +## [1.2.13](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.12...cc-v1.2.13) - 2025-02-08 + +### Other + +- Fix cross-compiling for Apple platforms ([#1389](https://github.com/rust-lang/cc-rs/pull/1389)) + +## [1.2.12](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.11...cc-v1.2.12) - 2025-02-04 + +### Other + +- Split impl Build ([#1382](https://github.com/rust-lang/cc-rs/pull/1382)) +- Don't specify both `-target` and `-mtargetos=` on Apple targets ([#1384](https://github.com/rust-lang/cc-rs/pull/1384)) + +## [1.2.11](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.10...cc-v1.2.11) - 2025-01-31 + +### Other + +- Fix more flag inheritance ([#1380](https://github.com/rust-lang/cc-rs/pull/1380)) +- Include wrapper args. in `stdout` family heuristics to restore classifying `clang --driver-mode=cl` as `Msvc { clang_cl: true }` ([#1378](https://github.com/rust-lang/cc-rs/pull/1378)) +- Constrain `-Clto` and `-Cembed-bitcode` flag inheritance to be `clang`-only ([#1379](https://github.com/rust-lang/cc-rs/pull/1379)) +- Pass deployment target with `-m*-version-min=` ([#1339](https://github.com/rust-lang/cc-rs/pull/1339)) +- Regenerate target info ([#1376](https://github.com/rust-lang/cc-rs/pull/1376)) + +## [1.2.10](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.9...cc-v1.2.10) - 2025-01-17 + +### Other + +- Fix CC_FORCE_DISABLE=0 evaluating to true ([#1371](https://github.com/rust-lang/cc-rs/pull/1371)) +- Regenerate target info ([#1369](https://github.com/rust-lang/cc-rs/pull/1369)) +- Make hidden lifetimes explicit. ([#1366](https://github.com/rust-lang/cc-rs/pull/1366)) + +## [1.2.9](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.8...cc-v1.2.9) - 2025-01-12 + +### Other + +- Don't pass inherited PGO flags to GNU compilers (#1363) +- Adjusted zig cc judgment and avoided zigbuild errors([#1360](https://github.com/rust-lang/cc-rs/pull/1360)) ([#1361](https://github.com/rust-lang/cc-rs/pull/1361)) +- Fix compilation on macOS using clang and fix compilation using zig-cc ([#1364](https://github.com/rust-lang/cc-rs/pull/1364)) + +## [1.2.8](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.7...cc-v1.2.8) - 2025-01-11 + +### Other + +- Add `is_like_clang_cl()` getter (#1357) +- Fix clippy error in lib.rs ([#1356](https://github.com/rust-lang/cc-rs/pull/1356)) +- Regenerate target info ([#1352](https://github.com/rust-lang/cc-rs/pull/1352)) +- Fix compiler family detection issue with clang-cl on macOS ([#1328](https://github.com/rust-lang/cc-rs/pull/1328)) +- Update `windows-bindgen` dependency ([#1347](https://github.com/rust-lang/cc-rs/pull/1347)) +- Fix clippy warnings ([#1346](https://github.com/rust-lang/cc-rs/pull/1346)) + +## [1.2.7](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.6...cc-v1.2.7) - 2025-01-03 + +### Other + +- Regenerate target info ([#1342](https://github.com/rust-lang/cc-rs/pull/1342)) +- Document new supported architecture names in windows::find +- Make is_flag_supported_inner take an &Tool ([#1337](https://github.com/rust-lang/cc-rs/pull/1337)) +- Fix is_flag_supported on msvc ([#1336](https://github.com/rust-lang/cc-rs/pull/1336)) +- Allow using Visual Studio target names in `find_tool` ([#1335](https://github.com/rust-lang/cc-rs/pull/1335)) + +## [1.2.6](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.5...cc-v1.2.6) - 2024-12-27 + +### Other + +- Don't inherit the `/Oy` flag for 64-bit targets ([#1330](https://github.com/rust-lang/cc-rs/pull/1330)) + +## [1.2.5](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.4...cc-v1.2.5) - 2024-12-19 + +### Other + +- Check linking when testing if compiler flags are supported ([#1322](https://github.com/rust-lang/cc-rs/pull/1322)) + +## [1.2.4](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.3...cc-v1.2.4) - 2024-12-13 + +### Other + +- Add support for C/C++ compiler for Neutrino QNX: `qcc` ([#1319](https://github.com/rust-lang/cc-rs/pull/1319)) +- use -maix64 instead of -m64 ([#1307](https://github.com/rust-lang/cc-rs/pull/1307)) + +## [1.2.3](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.2...cc-v1.2.3) - 2024-12-06 + +### Other + +- Improve detection of environment when compiling from msbuild or msvc ([#1310](https://github.com/rust-lang/cc-rs/pull/1310)) +- Better error message when failing on unknown targets ([#1313](https://github.com/rust-lang/cc-rs/pull/1313)) +- Optimize RustcCodegenFlags ([#1305](https://github.com/rust-lang/cc-rs/pull/1305)) + +## [1.2.2](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.1...cc-v1.2.2) - 2024-11-29 + +### Other + +- Inherit flags from rustc ([#1279](https://github.com/rust-lang/cc-rs/pull/1279)) +- Add support for using sccache wrapper with cuda/nvcc ([#1304](https://github.com/rust-lang/cc-rs/pull/1304)) +- Fix msvc stdout not shown on error ([#1303](https://github.com/rust-lang/cc-rs/pull/1303)) +- Regenerate target info ([#1301](https://github.com/rust-lang/cc-rs/pull/1301)) +- Fix compilation of C++ code for armv7-unknown-linux-gnueabihf ([#1298](https://github.com/rust-lang/cc-rs/pull/1298)) +- Fetch target info from Cargo even if `Build::target` is manually set ([#1299](https://github.com/rust-lang/cc-rs/pull/1299)) +- Fix two files with different extensions having the same object name ([#1295](https://github.com/rust-lang/cc-rs/pull/1295)) +- Allow disabling cc's ability to compile via env var CC_FORCE_DISABLE ([#1292](https://github.com/rust-lang/cc-rs/pull/1292)) +- Regenerate target info ([#1293](https://github.com/rust-lang/cc-rs/pull/1293)) + +## [1.2.1](https://github.com/rust-lang/cc-rs/compare/cc-v1.2.0...cc-v1.2.1) - 2024-11-14 + +### Other + +- When invoking `cl -?`, set stdin to null ([#1288](https://github.com/rust-lang/cc-rs/pull/1288)) + +## [1.2.0](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.37...cc-v1.2.0) - 2024-11-11 + +### Added + +- add i686-pc-windows-gnullvm prefix detection ([#1283](https://github.com/rust-lang/cc-rs/pull/1283)) + +### Other + +- Allow only specifying the architecture ([#1285](https://github.com/rust-lang/cc-rs/pull/1285)) +- Fix WASM vs. WASI options ([#1284](https://github.com/rust-lang/cc-rs/pull/1284)) + +## [1.1.37](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.36...cc-v1.1.37) - 2024-11-08 + +### Other + +- Use relative directory for obj files hash ([#1270](https://github.com/rust-lang/cc-rs/pull/1270)) +- Regenerate target info ([#1280](https://github.com/rust-lang/cc-rs/pull/1280)) + +## [1.1.36](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.35...cc-v1.1.36) - 2024-11-05 + +### Other + +- Fix CUDA build with clang++. ([#1273](https://github.com/rust-lang/cc-rs/pull/1273)) + +## [1.1.35](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.34...cc-v1.1.35) - 2024-11-04 + +### Other + +- Remove support for FRC ([#1268](https://github.com/rust-lang/cc-rs/pull/1268)) +- Do not add -fPIC by default on UEFI targets ([#1263](https://github.com/rust-lang/cc-rs/pull/1263)) +- Use -windows-gnu for all UEFI targets ([#1264](https://github.com/rust-lang/cc-rs/pull/1264)) + +## [1.1.34](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.33...cc-v1.1.34) - 2024-11-02 + +### Other + +- Remove redundant flags ([#1256](https://github.com/rust-lang/cc-rs/pull/1256)) + +## [1.1.33](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.32...cc-v1.1.33) - 2024-11-02 + +### Other + +- Reduce size of `cc::Build` and size of generated targets ([#1257](https://github.com/rust-lang/cc-rs/pull/1257)) + +## [1.1.32](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.31...cc-v1.1.32) - 2024-11-02 + +### Other + +- Use `rustc`'s knowledge of LLVM/Clang target triples ([#1252](https://github.com/rust-lang/cc-rs/pull/1252)) +- Use Cargo's target information when possible ([#1225](https://github.com/rust-lang/cc-rs/pull/1225)) + +## [1.1.31](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.30...cc-v1.1.31) - 2024-10-19 + +### Other + +- Add comment explaining why cc does not rebuild on env PATH change ([#1247](https://github.com/rust-lang/cc-rs/pull/1247)) + +## [1.1.30](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.29...cc-v1.1.30) - 2024-10-11 + +### Other + +- Don't pass -fPIC by default on wasm ([#1245](https://github.com/rust-lang/cc-rs/pull/1245)) + +## [1.1.29](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.28...cc-v1.1.29) - 2024-10-11 + +### Other + +- Regenerate target info ([#1243](https://github.com/rust-lang/cc-rs/pull/1243)) + +## [1.1.28](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.27...cc-v1.1.28) - 2024-10-06 + +### Other + +- Environment variables: For one accepting boolean, treat "0", "false" and empty env as false ([#1238](https://github.com/rust-lang/cc-rs/pull/1238)) + +## [1.1.27](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.26...cc-v1.1.27) - 2024-10-06 + +### Other + +- Revert "Use debug version of MSVC runtime library on debug ([#1231](https://github.com/rust-lang/cc-rs/pull/1231))" ([#1237](https://github.com/rust-lang/cc-rs/pull/1237)) +- Disable `CC_ENABLE_DEBUG_OUTPUT` if it is set to "0" ([#1234](https://github.com/rust-lang/cc-rs/pull/1234)) + +## [1.1.26](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.25...cc-v1.1.26) - 2024-10-06 + +### Other + +- Use debug version of MSVC runtime library on debug ([#1231](https://github.com/rust-lang/cc-rs/pull/1231)) + +## [1.1.25](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.24...cc-v1.1.25) - 2024-10-05 + +### Other + +- Remove incorrect "lib" prefixes in CXXSTDLIB doc comments ([#1228](https://github.com/rust-lang/cc-rs/pull/1228)) + +## [1.1.24](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.23...cc-v1.1.24) - 2024-10-01 + +### Other + +- Fix wasm32-wasip1-threads: shared-memory disallowed due to not compiled with 'atomics' or 'bulk-memory' features ([#1221](https://github.com/rust-lang/cc-rs/pull/1221)) +- Reduce the need for the host target triple ([#1224](https://github.com/rust-lang/cc-rs/pull/1224)) +- Add auto cancellation for CI jobs ([#1222](https://github.com/rust-lang/cc-rs/pull/1222)) + +## [1.1.23](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.22...cc-v1.1.23) - 2024-09-30 + +### Other + +- Update doc for detecting changes/upgrades of compilers ([#1218](https://github.com/rust-lang/cc-rs/pull/1218)) + +## [1.1.22](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.21...cc-v1.1.22) - 2024-09-27 + +### Other + +- Don't rerun if PATH changes ([#1215](https://github.com/rust-lang/cc-rs/pull/1215)) + +## [1.1.21](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.20...cc-v1.1.21) - 2024-09-18 + +### Other + +- disable pic for targets that end in `-none` ([#1212](https://github.com/rust-lang/cc-rs/pull/1212)) + +## [1.1.20](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.19...cc-v1.1.20) - 2024-09-17 + +### Other + +- Add buildcache as known Rust and C/C++ compiler wrapper ([#1209](https://github.com/rust-lang/cc-rs/pull/1209)) + +## [1.1.19](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.18...cc-v1.1.19) - 2024-09-15 + +### Other + +- Add support arm64e-apple-darwin ([#1207](https://github.com/rust-lang/cc-rs/pull/1207)) + +## [1.1.18](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.17...cc-v1.1.18) - 2024-09-07 + +### Other +- Fixed unsoundness in `StderrForwarder::forward_available` ([#1203](https://github.com/rust-lang/cc-rs/pull/1203)) + +## [1.1.17](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.16...cc-v1.1.17) - 2024-09-06 + +### Fixed +- fix finding toolchains when invoked by msbuild ([#1201](https://github.com/rust-lang/cc-rs/pull/1201)) + +## [1.1.16](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.15...cc-v1.1.16) - 2024-09-04 + +### Other +- Treat VxWorks wr-cc as a Gnu compiler ([#1198](https://github.com/rust-lang/cc-rs/pull/1198)) + +## [1.1.15](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.14...cc-v1.1.15) - 2024-08-26 + +### Other +- Add -mfloat-abi=hard as a default argument when using any arm/thumb-none-eabihf target ([#1194](https://github.com/rust-lang/cc-rs/pull/1194)) + +## [1.1.14](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.13...cc-v1.1.14) - 2024-08-23 + +### Other +- allow finding tools from path if VisualStudioDir is set + +## [1.1.13](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.12...cc-v1.1.13) - 2024-08-16 + +### Other +- Fix detect family: should detect emscripten as clang, closes [#1185](https://github.com/rust-lang/cc-rs/pull/1185) ([#1186](https://github.com/rust-lang/cc-rs/pull/1186)) + +## [1.1.12](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.11...cc-v1.1.12) - 2024-08-15 + +### Other +- improve docs ([#1183](https://github.com/rust-lang/cc-rs/pull/1183)) + +## [1.1.11](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.10...cc-v1.1.11) - 2024-08-14 + +### Other +- Add support for parsing shell encoded `*FLAGS` ([#1181](https://github.com/rust-lang/cc-rs/pull/1181)) +- Replace vector of tuples with BTreeMap which already is sorted and free of duplicates ([#1177](https://github.com/rust-lang/cc-rs/pull/1177)) + +## [1.1.10](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.9...cc-v1.1.10) - 2024-08-11 + +### Other +- Remap Windows targets triples to their LLVM counterparts ([#1176](https://github.com/rust-lang/cc-rs/pull/1176)) + +## [1.1.9](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.8...cc-v1.1.9) - 2024-08-11 + +### Other +- Add custom CC wrapper to the wrapper whitelist ([#1175](https://github.com/rust-lang/cc-rs/pull/1175)) + +## [1.1.8](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.7...cc-v1.1.8) - 2024-08-06 + +### Other +- Fix broken link in docs.rs ([#1173](https://github.com/rust-lang/cc-rs/pull/1173)) + +## [1.1.7](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.6...cc-v1.1.7) - 2024-07-29 + +### Other +- add `.objects` ([#1166](https://github.com/rust-lang/cc-rs/pull/1166)) + +## [1.1.6](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.5...cc-v1.1.6) - 2024-07-19 + +### Other +- Clippy fixes ([#1163](https://github.com/rust-lang/cc-rs/pull/1163)) + +## [1.1.5](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.4...cc-v1.1.5) - 2024-07-15 + +### Other +- Fix cyclic compilation: Use vendored once_cell ([#1154](https://github.com/rust-lang/cc-rs/pull/1154)) + +## [1.1.4](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.3...cc-v1.1.4) - 2024-07-14 + +### Other +- Support compiling on wasm targets (Supersede [#1068](https://github.com/rust-lang/cc-rs/pull/1068)) ([#1160](https://github.com/rust-lang/cc-rs/pull/1160)) + +## [1.1.3](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.2...cc-v1.1.3) - 2024-07-14 + +### Other +- Reduce msrv to 1.63 ([#1158](https://github.com/rust-lang/cc-rs/pull/1158)) +- Revert "Use raw-dylib for windows-sys ([#1137](https://github.com/rust-lang/cc-rs/pull/1137))" ([#1157](https://github.com/rust-lang/cc-rs/pull/1157)) +- Fix typos ([#1152](https://github.com/rust-lang/cc-rs/pull/1152)) +- Fix `doc_lazy_continuation` lints ([#1153](https://github.com/rust-lang/cc-rs/pull/1153)) + +## [1.1.2](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.1...cc-v1.1.2) - 2024-07-12 + +### Other +- Add empty `jobserver` feature. ([#1150](https://github.com/rust-lang/cc-rs/pull/1150)) + +## [1.1.1](https://github.com/rust-lang/cc-rs/compare/cc-v1.1.0...cc-v1.1.1) - 2024-07-12 + +### Other +- Fix is_flag_supported not respecting emit_rerun_if_env_changed ([#1147](https://github.com/rust-lang/cc-rs/pull/1147)) ([#1148](https://github.com/rust-lang/cc-rs/pull/1148)) + +## [1.1.0](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.106...cc-v1.1.0) - 2024-07-08 + +### Added +- add cargo_output to eliminate last vestiges of stdout pollution ([#1141](https://github.com/rust-lang/cc-rs/pull/1141)) + +## [1.0.106](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.105...cc-v1.0.106) - 2024-07-08 + +### Other +- Drop support for Visual Studio 12 (2013) ([#1046](https://github.com/rust-lang/cc-rs/pull/1046)) +- Use raw-dylib for windows-sys ([#1137](https://github.com/rust-lang/cc-rs/pull/1137)) +- Bump msrv to 1.67 ([#1143](https://github.com/rust-lang/cc-rs/pull/1143)) +- Bump msrv to 1.65 ([#1140](https://github.com/rust-lang/cc-rs/pull/1140)) +- Fix clippy warnings ([#1138](https://github.com/rust-lang/cc-rs/pull/1138)) + +## [1.0.105](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.104...cc-v1.0.105) - 2024-07-07 + +### Other +- Regenerate windows sys bindings ([#1132](https://github.com/rust-lang/cc-rs/pull/1132)) +- Fix generate-windows-sys-bindings ([#1133](https://github.com/rust-lang/cc-rs/pull/1133)) +- Fix gen-windows-sys-binding ([#1130](https://github.com/rust-lang/cc-rs/pull/1130)) +- Fix gen-windows-sys-binding ([#1127](https://github.com/rust-lang/cc-rs/pull/1127)) +- Update windows-bindgen requirement from 0.57 to 0.58 ([#1123](https://github.com/rust-lang/cc-rs/pull/1123)) + +## [1.0.104](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.103...cc-v1.0.104) - 2024-07-01 + +### Other +- Fixed link break about compile-time-requirements ([#1118](https://github.com/rust-lang/cc-rs/pull/1118)) + +## [1.0.103](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.102...cc-v1.0.103) - 2024-06-30 + +### Other +- Fix compilation for wasm: env WASI_SYSROOT should be optional ([#1114](https://github.com/rust-lang/cc-rs/pull/1114)) + +## [1.0.102](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.101...cc-v1.0.102) - 2024-06-29 + +### Other +- Fix invalid wasi targets compatibility ([#1105](https://github.com/rust-lang/cc-rs/pull/1105)) +- Speedup regenerate-target-info and regenerate-windows-sys ([#1110](https://github.com/rust-lang/cc-rs/pull/1110)) + +## [1.0.101](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.100...cc-v1.0.101) - 2024-06-25 + +### Other +- Use `Build::getenv` instead of `env::var*` in anywhere that makes sense ([#1103](https://github.com/rust-lang/cc-rs/pull/1103)) + +## [1.0.100](https://github.com/rust-lang/cc-rs/compare/cc-v1.0.99...cc-v1.0.100) - 2024-06-23 + +### Other +- Update publish.yml to use release-plz ([#1101](https://github.com/rust-lang/cc-rs/pull/1101)) +- Accept `OsStr` instead of `str` for flags ([#1100](https://github.com/rust-lang/cc-rs/pull/1100)) +- Use `dep:` syntax to avoid implicit features. ([#1099](https://github.com/rust-lang/cc-rs/pull/1099)) +- Minor clippy fixes. ([#1098](https://github.com/rust-lang/cc-rs/pull/1098)) +- Fix WASI compilation for C++ ([#1083](https://github.com/rust-lang/cc-rs/pull/1083)) +- Regenerate windows sys bindings ([#1096](https://github.com/rust-lang/cc-rs/pull/1096)) +- Rename regenerate-windows-sys to regenerate-windows-sys.yml ([#1095](https://github.com/rust-lang/cc-rs/pull/1095)) +- Create regenerate-windows-sys.yml ([#1094](https://github.com/rust-lang/cc-rs/pull/1094)) +- Update windows-bindgen requirement from 0.56 to 0.57 ([#1091](https://github.com/rust-lang/cc-rs/pull/1091)) +- Eagerly close tempfile to fix [#1082](https://github.com/rust-lang/cc-rs/pull/1082) ([#1087](https://github.com/rust-lang/cc-rs/pull/1087)) +- Output msvc.exe in the output directory ([#1090](https://github.com/rust-lang/cc-rs/pull/1090)) +- Fix clippy warnings on Windows ([#1088](https://github.com/rust-lang/cc-rs/pull/1088)) +- Don't try to free DLL on drop ([#1089](https://github.com/rust-lang/cc-rs/pull/1089)) +- Fix panic safety issue in StderrForwarder ([#1079](https://github.com/rust-lang/cc-rs/pull/1079)) diff --git a/cc-1.2.64/Cargo.lock b/cc-1.2.64/Cargo.lock new file mode 100644 index 0000000000..1d3b755524 --- /dev/null +++ b/cc-1.2.64/Cargo.lock @@ -0,0 +1,492 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "cc" +version = "1.2.64" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", + "tempfile", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "log" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" + +[[package]] +name = "memchr" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/cc-1.2.64/Cargo.toml b/cc-1.2.64/Cargo.toml new file mode 100644 index 0000000000..aee74c4653 --- /dev/null +++ b/cc-1.2.64/Cargo.toml @@ -0,0 +1,79 @@ +# 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 are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +rust-version = "1.63" +name = "cc" +version = "1.2.64" +authors = ["Alex Crichton "] +build = false +exclude = [ + "/.github", + "tests", + "src/bin", +] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = """ +A build-time dependency for Cargo build scripts to assist in invoking the native +C compiler to compile native C code into a static archive to be linked into Rust +code. +""" +homepage = "https://github.com/rust-lang/cc-rs" +documentation = "https://docs.rs/cc" +readme = "README.md" +keywords = ["build-dependencies"] +categories = ["development-tools::build-utils"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-lang/cc-rs" + +[features] +jobserver = [] +parallel = [ + "dep:libc", + "dep:jobserver", +] + +[lib] +name = "cc" +path = "src/lib.rs" + +[dependencies.find-msvc-tools] +version = "0.1.9" + +[dependencies.jobserver] +version = "0.1.30" +optional = true +default-features = false + +[dependencies.shlex] +version = "2.0.1" + +[dev-dependencies.tempfile] +version = "3" + +[target."cfg(unix)".dependencies.libc] +version = "0.2.62" +optional = true +default-features = false + +[lints.rust.unexpected_cfgs] +level = "allow" +priority = 0 +check-cfg = ["cfg(disable_clang_cl_tests)"] + +[profile.release] +opt-level = 3 +lto = true diff --git a/cc-1.2.64/Cargo.toml.orig b/cc-1.2.64/Cargo.toml.orig new file mode 100644 index 0000000000..a68fed5271 --- /dev/null +++ b/cc-1.2.64/Cargo.toml.orig @@ -0,0 +1,59 @@ +[package] +name = "cc" +version = "1.2.64" +authors = ["Alex Crichton "] +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-lang/cc-rs" +homepage = "https://github.com/rust-lang/cc-rs" +documentation = "https://docs.rs/cc" +description = """ +A build-time dependency for Cargo build scripts to assist in invoking the native +C compiler to compile native C code into a static archive to be linked into Rust +code. +""" +keywords = ["build-dependencies"] +readme = "README.md" +categories = ["development-tools::build-utils"] +# The binary target is only used by tests. +exclude = ["/.github", "tests", "src/bin"] +edition = "2018" +rust-version = "1.63" + +[dependencies] +jobserver = { version = "0.1.30", default-features = false, optional = true } +shlex = "2.0.1" +find-msvc-tools = { version = "0.1.9", path = "find-msvc-tools" } + +[target.'cfg(unix)'.dependencies] +# Don't turn on the feature "std" for this, see https://github.com/rust-lang/cargo/issues/4866 +# which is still an issue with `resolver = "1"`. +libc = { version = "0.2.62", default-features = false, optional = true } + +[features] +parallel = ["dep:libc", "dep:jobserver"] +# This is a placeholder feature for people who incorrectly used `cc` with `features = ["jobserver"]` +# so that they aren't broken. This has never enabled `parallel`, so we won't do that. +jobserver = [] + +[dev-dependencies] +tempfile = "3" + +[workspace] +members = [ + "find-msvc-tools", + "dev-tools/cc-test", + "dev-tools/gen-target-info", + "dev-tools/gen-windows-sys-binding", + "dev-tools/wasi-test", +] + +[patch.crates-io] +cc = { path = "." } + +[lints.rust] +unexpected_cfgs = { level = "allow", check-cfg = ['cfg(disable_clang_cl_tests)'] } + +[profile.release] +opt-level = 3 # Or "s" or "z" for different optimization goals +lto = true + diff --git a/cc-1.2.62/LICENSE-APACHE b/cc-1.2.64/LICENSE-APACHE similarity index 100% rename from cc-1.2.62/LICENSE-APACHE rename to cc-1.2.64/LICENSE-APACHE diff --git a/cc-1.2.62/LICENSE-MIT b/cc-1.2.64/LICENSE-MIT similarity index 100% rename from cc-1.2.62/LICENSE-MIT rename to cc-1.2.64/LICENSE-MIT diff --git a/cc-1.2.62/README.md b/cc-1.2.64/README.md similarity index 100% rename from cc-1.2.62/README.md rename to cc-1.2.64/README.md diff --git a/cc-1.2.62/clippy.toml b/cc-1.2.64/clippy.toml similarity index 100% rename from cc-1.2.62/clippy.toml rename to cc-1.2.64/clippy.toml diff --git a/cc-1.2.62/src/command_helpers.rs b/cc-1.2.64/src/command_helpers.rs similarity index 100% rename from cc-1.2.62/src/command_helpers.rs rename to cc-1.2.64/src/command_helpers.rs diff --git a/cc-1.2.62/src/detect_compiler_family.c b/cc-1.2.64/src/detect_compiler_family.c similarity index 100% rename from cc-1.2.62/src/detect_compiler_family.c rename to cc-1.2.64/src/detect_compiler_family.c diff --git a/cc-1.2.62/src/flags.rs b/cc-1.2.64/src/flags.rs similarity index 100% rename from cc-1.2.62/src/flags.rs rename to cc-1.2.64/src/flags.rs diff --git a/cc-1.2.64/src/lib.rs b/cc-1.2.64/src/lib.rs new file mode 100644 index 0000000000..e230a38017 --- /dev/null +++ b/cc-1.2.64/src/lib.rs @@ -0,0 +1,4547 @@ +//! A library for [Cargo build scripts](https://doc.rust-lang.org/cargo/reference/build-scripts.html) +//! to compile a set of C/C++/assembly/CUDA files into a static archive for Cargo +//! to link into the crate being built. This crate does not compile code itself; +//! it calls out to the default compiler for the platform. This crate will +//! automatically detect situations such as cross compilation and +//! [various environment variables](#external-configuration-via-environment-variables) and will build code appropriately. +//! +//! # Example +//! +//! First, you'll want to both add a build script for your crate (`build.rs`) and +//! also add this crate to your `Cargo.toml` via: +//! +//! ```toml +//! [build-dependencies] +//! cc = "1.0" +//! ``` +//! +//! Next up, you'll want to write a build script like so: +//! +//! ```rust,no_run +//! // build.rs +//! cc::Build::new() +//! .file("foo.c") +//! .file("bar.c") +//! .compile("foo"); +//! ``` +//! +//! And that's it! Running `cargo build` should take care of the rest and your Rust +//! application will now have the C files `foo.c` and `bar.c` compiled into a file +//! named `libfoo.a`. If the C files contain +//! +//! ```c +//! void foo_function(void) { ... } +//! ``` +//! +//! and +//! +//! ```c +//! int32_t bar_function(int32_t x) { ... } +//! ``` +//! +//! you can call them from Rust by declaring them in +//! your Rust code like so: +//! +//! ```rust,no_run +//! extern "C" { +//! fn foo_function(); +//! fn bar_function(x: i32) -> i32; +//! } +//! +//! pub fn call() { +//! unsafe { +//! foo_function(); +//! bar_function(42); +//! } +//! } +//! +//! fn main() { +//! call(); +//! } +//! ``` +//! +//! See [the Rustonomicon](https://doc.rust-lang.org/nomicon/ffi.html) for more details. +//! +//! # External configuration via environment variables +//! +//! To control the programs and flags used for building, the builder can set a +//! number of different environment variables. +//! +//! * `CFLAGS` - a series of space separated flags passed to compilers. Note that +//! individual flags cannot currently contain spaces, so doing +//! something like: `-L=foo\ bar` is not possible. +//! * `CC` - the actual C compiler used. Note that this supports passing a known +//! wrapper via `sccache cc`. This compiler must understand the `-c` flag. For +//! certain `TARGET`s, it also is assumed to know about other flags (most +//! common is `-fPIC`). +//! ccache, distcc, sccache, icecc, cachepot and buildcache are supported, +//! for sccache, simply set `CC` to `sccache cc`. +//! For other custom `CC` wrapper, just set `CC_KNOWN_WRAPPER_CUSTOM` +//! to the custom wrapper used in `CC`. +//! * `AR` - the `ar` (archiver) executable to use to build the static library. +//! * `CRATE_CC_NO_DEFAULTS` - the default compiler flags may cause conflicts in +//! some cross compiling scenarios. Setting this variable +//! will disable the generation of default compiler +//! flags. +//! * `CC_ENABLE_DEBUG_OUTPUT` - if set, compiler command invocations and exit codes will +//! be logged to stdout. This is useful for debugging build script issues, but can be +//! overly verbose for normal use. +//! * `CC_SHELL_ESCAPED_FLAGS` - if set, `*FLAGS` will be parsed as if they were shell +//! arguments (similar to `make` and `cmake`) rather than splitting them on each space. +//! For example, with `CFLAGS='a "b c"'`, the compiler will be invoked with 2 arguments - +//! `a` and `b c` - rather than 3: `a`, `"b` and `c"`. +//! * `CXX...` - see [C++ Support](#c-support). +//! * `CC_FORCE_DISABLE` - If set, `cc` will never run any [`Command`]s, and methods that +//! would return an [`Error`]. This is intended for use by third-party build systems +//! which want to be absolutely sure that they are in control of building all +//! dependencies. Note that operations that return [`Tool`]s such as +//! [`Build::get_compiler`] may produce less accurate results as in some cases `cc` runs +//! commands in order to locate compilers. Additionally, this does nothing to prevent +//! users from running [`Tool::to_command`] and executing the [`Command`] themselves. +//! * `RUSTC_WRAPPER` - If set, the specified command will be prefixed to the compiler +//! command. This is useful for projects that want to use +//! [sccache](https://github.com/mozilla/sccache), +//! [buildcache](https://gitlab.com/bits-n-bites/buildcache), or +//! [cachepot](https://github.com/paritytech/cachepot). +//! +//! Furthermore, projects using this crate may specify custom environment variables +//! to be inspected, for example via the `Build::try_flags_from_environment` +//! function. Consult the project’s own documentation or its use of the `cc` crate +//! for any additional variables it may use. +//! +//! Each of these variables can also be supplied with certain prefixes and suffixes, +//! in the following prioritized order: +//! +//! 1. `_` - for example, `CC_x86_64-unknown-linux-gnu` or `CC_thumbv8m.main-none-eabi` +//! 2. `_` - for example, `CC_x86_64_unknown_linux_gnu` or `CC_thumbv8m_main_none_eabi` (both periods and underscores are replaced) +//! 3. `_` - for example, `HOST_CC` or `TARGET_CFLAGS` +//! 4. `` - a plain `CC`, `AR` as above. +//! +//! If none of these variables exist, cc-rs uses built-in defaults. +//! +//! In addition to the above optional environment variables, `cc-rs` has some +//! functions with hard requirements on some variables supplied by [cargo's +//! build-script driver][cargo] that it has the `TARGET`, `OUT_DIR`, `OPT_LEVEL`, +//! and `HOST` variables. +//! +//! [cargo]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#inputs-to-the-build-script +//! +//! # Optional features +//! +//! ## Parallel +//! +//! Currently cc-rs supports parallel compilation (think `make -jN`) but this +//! feature is turned off by default. To enable cc-rs to compile C/C++ in parallel, +//! you can change your dependency to: +//! +//! ```toml +//! [build-dependencies] +//! cc = { version = "1.0", features = ["parallel"] } +//! ``` +//! +//! By default cc-rs will limit parallelism to `$NUM_JOBS`, or if not present it +//! will limit it to the number of cpus on the machine. If you are using cargo, +//! use `-jN` option of `build`, `test` and `run` commands as `$NUM_JOBS` +//! is supplied by cargo. +//! +//! # Compile-time Requirements +//! +//! To work properly this crate needs access to a C compiler when the build script +//! is being run. This crate does not ship a C compiler with it. The compiler +//! required varies per platform, but there are three broad categories: +//! +//! * Unix platforms require `cc` to be the C compiler. This can be found by +//! installing cc/clang on Linux distributions and Xcode on macOS, for example. +//! * Windows platforms targeting MSVC (e.g. your target name ends in `-msvc`) +//! require Visual Studio to be installed. `cc-rs` attempts to locate it, and +//! if it fails, `cl.exe` is expected to be available in `PATH`. This can be +//! set up by running the appropriate developer tools shell. +//! * When using `prefer_clang_cl_over_msvc`, make sure that the `C++ Clang compiler for Windows` component +//! is installed through the Visual Studio Installer, so that `cc-rs` can find `clang-cl.exe`. +//! * Windows platforms targeting MinGW (e.g. your target name ends in `-gnu`) +//! require `cc` to be available in `PATH`. We recommend the +//! [MinGW-w64](https://www.mingw-w64.org/) distribution. +//! You may also acquire it via +//! [MSYS2](https://www.msys2.org/), as explained [here][msys2-help]. Make sure +//! to install the appropriate architecture corresponding to your installation of +//! rustc. GCC from older [MinGW](http://www.mingw.org/) project is compatible +//! only with 32-bit rust compiler. +//! +//! [msys2-help]: https://github.com/rust-lang/rust/blob/master/INSTALL.md#building-on-windows +//! +//! # C++ support +//! +//! `cc-rs` supports C++ libraries compilation by using the `cpp` method on +//! `Build`: +//! +//! ```rust,no_run +//! cc::Build::new() +//! .cpp(true) // Switch to C++ library compilation. +//! .file("foo.cpp") +//! .compile("foo"); +//! ``` +//! +//! For C++ libraries, the `CXX` and `CXXFLAGS` environment variables are used instead of `CC` and `CFLAGS`. +//! +//! The C++ standard library may be linked to the crate target. By default it's `libc++` for macOS, FreeBSD, and OpenBSD, `libc++_shared` for Android, nothing for MSVC, and `libstdc++` for anything else. It can be changed in one of two ways: +//! +//! 1. by using the `cpp_link_stdlib` method on `Build`: +//! ```rust,no_run +//! cc::Build::new() +//! .cpp(true) +//! .file("foo.cpp") +//! .cpp_link_stdlib("stdc++") // use libstdc++ +//! .compile("foo"); +//! ``` +//! 2. by setting the `CXXSTDLIB` environment variable. +//! +//! In particular, for Android you may want to [use `c++_static` if you have at most one shared library](https://developer.android.com/ndk/guides/cpp-support). +//! +//! Remember that C++ does name mangling so `extern "C"` might be required to enable Rust linker to find your functions. +//! +//! # CUDA C++ support +//! +//! `cc-rs` also supports compiling CUDA C++ libraries by using the `cuda` method +//! on `Build`: +//! +//! ```rust,no_run +//! cc::Build::new() +//! // Switch to CUDA C++ library compilation using NVCC. +//! .cuda(true) +//! .cudart("static") +//! // Generate code for Maxwell (GTX 970, 980, 980 Ti, Titan X). +//! .flag("-gencode").flag("arch=compute_52,code=sm_52") +//! // Generate code for Maxwell (Jetson TX1). +//! .flag("-gencode").flag("arch=compute_53,code=sm_53") +//! // Generate code for Pascal (GTX 1070, 1080, 1080 Ti, Titan Xp). +//! .flag("-gencode").flag("arch=compute_61,code=sm_61") +//! // Generate code for Pascal (Tesla P100). +//! .flag("-gencode").flag("arch=compute_60,code=sm_60") +//! // Generate code for Pascal (Jetson TX2). +//! .flag("-gencode").flag("arch=compute_62,code=sm_62") +//! // Generate code in parallel +//! .flag("-t0") +//! .file("bar.cu") +//! .compile("bar"); +//! ``` +//! +//! # Speed up compilation with sccache +//! +//! `cc-rs` does not handle incremental compilation like `make` or `ninja`. It +//! always compiles the all sources, no matter if they have changed or not. +//! This would be time-consuming in large projects. To save compilation time, +//! you can use [sccache](https://github.com/mozilla/sccache) by setting +//! environment variable `RUSTC_WRAPPER=sccache`, which will use cached `.o` +//! files if the sources are unchanged. + +#![doc(html_root_url = "https://docs.rs/cc/1.0")] +#![deny(warnings)] +#![deny(missing_docs)] +#![deny(clippy::disallowed_methods)] +#![warn(clippy::doc_markdown)] + +use std::borrow::Cow; +use std::collections::HashMap; +use std::env; +use std::ffi::{OsStr, OsString}; +use std::fmt::{self, Display}; +use std::fs; +use std::io::{self, Write}; +use std::path::{Component, Path, PathBuf}; +use std::process::{Command, Stdio}; +use std::sync::{Arc, RwLock}; + +use shlex::Shlex; + +#[cfg(feature = "parallel")] +mod parallel; + +mod target; +use self::target::*; + +/// A helper module to looking for windows-specific tools: +/// 1. On Windows host, probe the Windows Registry if needed; +/// 2. On non-Windows host, check specified environment variables. +pub mod windows_registry { + // Regardless of whether this should be in this crate's public API, + // it has been since 2015, so don't break it. + + /// Attempts to find a tool within an MSVC installation using the Windows + /// registry as a point to search from. + /// + /// The `arch_or_target` argument is the architecture or the Rust target name + /// that the tool should work for (e.g. compile or link for). The supported + /// architecture names are: + /// - `"x64"` or `"x86_64"` + /// - `"arm64"` or `"aarch64"` + /// - `"arm64ec"` + /// - `"x86"`, `"i586"` or `"i686"` + /// - `"arm"` or `"thumbv7a"` + /// + /// The `tool` argument is the tool to find. Supported tools include: + /// - MSVC tools: `cl.exe`, `link.exe`, `lib.exe`, etc. + /// - `MSBuild`: `msbuild.exe` + /// - Visual Studio IDE: `devenv.exe` + /// - Clang/LLVM tools: `clang.exe`, `clang++.exe`, `clang-*.exe`, `llvm-*.exe`, `lld.exe`, etc. + /// + /// This function will return `None` if the tool could not be found, or it will + /// return `Some(cmd)` which represents a command that's ready to execute the + /// tool with the appropriate environment variables set. + /// + /// Note that this function always returns `None` for non-MSVC targets (if a + /// full target name was specified). + pub fn find(arch_or_target: &str, tool: &str) -> Option { + ::find_msvc_tools::find(arch_or_target, tool) + } + + /// A version of Visual Studio + #[derive(Debug, PartialEq, Eq, Copy, Clone)] + #[non_exhaustive] + pub enum VsVers { + /// Visual Studio 12 (2013) + #[deprecated = "Visual Studio 12 is no longer supported. cc will never return this value."] + Vs12, + /// Visual Studio 14 (2015) + Vs14, + /// Visual Studio 15 (2017) + Vs15, + /// Visual Studio 16 (2019) + Vs16, + /// Visual Studio 17 (2022) + Vs17, + /// Visual Studio 18 (2026) + Vs18, + } + + /// Find the most recent installed version of Visual Studio + /// + /// This is used by the cmake crate to figure out the correct + /// generator. + pub fn find_vs_version() -> Result { + ::find_msvc_tools::find_vs_version().map(|vers| match vers { + #[allow(deprecated)] + ::find_msvc_tools::VsVers::Vs12 => VsVers::Vs12, + ::find_msvc_tools::VsVers::Vs14 => VsVers::Vs14, + ::find_msvc_tools::VsVers::Vs15 => VsVers::Vs15, + ::find_msvc_tools::VsVers::Vs16 => VsVers::Vs16, + ::find_msvc_tools::VsVers::Vs17 => VsVers::Vs17, + ::find_msvc_tools::VsVers::Vs18 => VsVers::Vs18, + _ => unreachable!("unknown VS version"), + }) + } + + /// Similar to the `find` function above, this function will attempt the same + /// operation (finding a MSVC tool in a local install) but instead returns a + /// [`Tool`](crate::Tool) which may be introspected. + pub fn find_tool(arch_or_target: &str, tool: &str) -> Option { + ::find_msvc_tools::find_tool(arch_or_target, tool).map(crate::Tool::from_find_msvc_tools) + } +} + +mod command_helpers; +use command_helpers::*; + +mod tool; +pub use tool::Tool; +use tool::{CompilerFamilyLookupCache, ToolFamily}; + +mod tempfile; + +mod utilities; +use utilities::*; + +mod flags; +use flags::*; + +#[derive(Debug, Eq, PartialEq, Hash)] +struct CompilerFlag { + compiler: Box, + flag: Box, +} + +#[derive(Debug, Default)] +struct BuildCache { + apple_sdk_root_cache: RwLock, Arc>>, + apple_versions_cache: RwLock, Arc>>, + cached_compiler_family: RwLock, + known_flag_support_status_cache: RwLock>, + target_info_parser: target::TargetInfoParser, +} + +/// A builder for compilation of a native library. +/// +/// A `Build` is the main type of the `cc` crate and is used to control all the +/// various configuration options and such of a compile. You'll find more +/// documentation on each method itself. +#[derive(Clone, Debug)] +pub struct Build { + include_directories: Vec>, + definitions: Vec<(Arc, Option>)>, + objects: Vec>, + flags: Vec>, + flags_supported: Vec>, + ar_flags: Vec>, + asm_flags: Vec>, + no_default_flags: bool, + files: Vec>, + cpp: bool, + cpp_link_stdlib: Option>>, + cpp_link_stdlib_static: bool, + cpp_set_stdlib: Option>, + cuda: bool, + cudart: Option>, + ccbin: bool, + std: Option>, + target: Option>, + /// The host compiler. + /// + /// Try to not access this directly, and instead prefer `cfg!(...)`. + host: Option>, + out_dir: Option>, + opt_level: Option>, + debug: Option>, + force_frame_pointer: Option, + env: Vec<(Arc, Arc)>, + compiler: Option>, + archiver: Option>, + ranlib: Option>, + cargo_output: CargoOutput, + link_lib_modifiers: Vec>, + pic: Option, + use_plt: Option, + static_crt: Option, + shared_flag: Option, + static_flag: Option, + warnings_into_errors: bool, + warnings: Option, + extra_warnings: Option, + emit_rerun_if_env_changed: bool, + shell_escaped_flags: Option, + build_cache: Arc, + inherit_rustflags: bool, + prefer_clang_cl_over_msvc: bool, +} + +/// Represents the types of errors that may occur while using cc-rs. +#[derive(Clone, Debug)] +enum ErrorKind { + /// Error occurred while performing I/O. + IOError, + /// Environment variable not found, with the var in question as extra info. + EnvVarNotFound, + /// Error occurred while using external tools (ie: invocation of compiler). + ToolExecError, + /// Error occurred due to missing external tools. + ToolNotFound, + /// One of the function arguments failed validation. + InvalidArgument, + /// No known macro is defined for the compiler when discovering tool family. + ToolFamilyMacroNotFound, + /// Invalid target. + InvalidTarget, + /// Unknown target. + UnknownTarget, + /// Invalid rustc flag. + InvalidFlag, + #[cfg(feature = "parallel")] + /// jobserver helpthread failure + JobserverHelpThreadError, + /// `cc` has been disabled by an environment variable. + Disabled, +} + +/// Represents an internal error that occurred, with an explanation. +#[derive(Clone, Debug)] +pub struct Error { + /// Describes the kind of error that occurred. + kind: ErrorKind, + /// More explanation of error that occurred. + message: Cow<'static, str>, +} + +impl Error { + fn new(kind: ErrorKind, message: impl Into>) -> Error { + Error { + kind, + message: message.into(), + } + } +} + +impl From for Error { + fn from(e: io::Error) -> Error { + Error::new(ErrorKind::IOError, format!("{e}")) + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}: {}", self.kind, self.message) + } +} + +impl std::error::Error for Error {} + +/// Represents an object. +/// +/// This is a source file -> object file pair. +#[derive(Clone, Debug)] +struct Object { + src: PathBuf, + dst: PathBuf, +} + +impl Object { + /// Create a new source file -> object file pair. + fn new(src: PathBuf, dst: PathBuf) -> Object { + Object { src, dst } + } +} + +/// Configure the builder. +impl Build { + /// Construct a new instance of a blank set of configuration. + /// + /// This builder is finished with the [`compile`] function. + /// + /// [`compile`]: struct.Build.html#method.compile + pub fn new() -> Build { + Build { + include_directories: Vec::new(), + definitions: Vec::new(), + objects: Vec::new(), + flags: Vec::new(), + flags_supported: Vec::new(), + ar_flags: Vec::new(), + asm_flags: Vec::new(), + no_default_flags: false, + files: Vec::new(), + shared_flag: None, + static_flag: None, + cpp: false, + cpp_link_stdlib: None, + cpp_link_stdlib_static: false, + cpp_set_stdlib: None, + cuda: false, + cudart: None, + ccbin: true, + std: None, + target: None, + host: None, + out_dir: None, + opt_level: None, + debug: None, + force_frame_pointer: None, + env: Vec::new(), + compiler: None, + archiver: None, + ranlib: None, + cargo_output: CargoOutput::new(), + link_lib_modifiers: Vec::new(), + pic: None, + use_plt: None, + static_crt: None, + warnings: None, + extra_warnings: None, + warnings_into_errors: false, + emit_rerun_if_env_changed: true, + shell_escaped_flags: None, + build_cache: Arc::default(), + inherit_rustflags: true, + prefer_clang_cl_over_msvc: false, + } + } + + /// Add a directory to the `-I` or include path for headers + /// + /// # Example + /// + /// ```no_run + /// use std::path::Path; + /// + /// let library_path = Path::new("/path/to/library"); + /// + /// cc::Build::new() + /// .file("src/foo.c") + /// .include(library_path) + /// .include("src") + /// .compile("foo"); + /// ``` + pub fn include>(&mut self, dir: P) -> &mut Build { + self.include_directories.push(dir.as_ref().into()); + self + } + + /// Add multiple directories to the `-I` include path. + /// + /// # Example + /// + /// ```no_run + /// # use std::path::Path; + /// # let condition = true; + /// # + /// let mut extra_dir = None; + /// if condition { + /// extra_dir = Some(Path::new("/path/to")); + /// } + /// + /// cc::Build::new() + /// .file("src/foo.c") + /// .includes(extra_dir) + /// .compile("foo"); + /// ``` + pub fn includes

(&mut self, dirs: P) -> &mut Build + where + P: IntoIterator, + P::Item: AsRef, + { + for dir in dirs { + self.include(dir); + } + self + } + + /// Specify a `-D` variable with an optional value. + /// + /// # Example + /// + /// ```no_run + /// cc::Build::new() + /// .file("src/foo.c") + /// .define("FOO", "BAR") + /// .define("BAZ", None) + /// .compile("foo"); + /// ``` + pub fn define<'a, V: Into>>(&mut self, var: &str, val: V) -> &mut Build { + self.definitions + .push((var.into(), val.into().map(Into::into))); + self + } + + /// Add an arbitrary object file to link in + pub fn object>(&mut self, obj: P) -> &mut Build { + self.objects.push(obj.as_ref().into()); + self + } + + /// Add arbitrary object files to link in + pub fn objects

(&mut self, objs: P) -> &mut Build + where + P: IntoIterator, + P::Item: AsRef, + { + for obj in objs { + self.object(obj); + } + self + } + + /// Add an arbitrary flag to the invocation of the compiler + /// + /// # Example + /// + /// ```no_run + /// cc::Build::new() + /// .file("src/foo.c") + /// .flag("-ffunction-sections") + /// .compile("foo"); + /// ``` + pub fn flag(&mut self, flag: impl AsRef) -> &mut Build { + self.flags.push(flag.as_ref().into()); + self + } + + /// Add multiple flags to the invocation of the compiler. + /// This is equivalent to calling [`flag`](Self::flag) for each item in the iterator. + /// + /// # Example + /// ```no_run + /// cc::Build::new() + /// .file("src/foo.c") + /// .flags(["-Wall", "-Wextra"]) + /// .compile("foo"); + /// ``` + pub fn flags(&mut self, flags: Iter) -> &mut Build + where + Iter: IntoIterator, + Iter::Item: AsRef, + { + for flag in flags { + self.flag(flag); + } + self + } + + /// Removes a compiler flag that was added by [`Build::flag`]. + /// + /// Will not remove flags added by other means (default flags, + /// flags from env, and so on). + /// + /// # Example + /// ``` + /// cc::Build::new() + /// .file("src/foo.c") + /// .flag("unwanted_flag") + /// .remove_flag("unwanted_flag"); + /// ``` + pub fn remove_flag(&mut self, flag: &str) -> &mut Build { + self.flags.retain(|other_flag| &**other_flag != flag); + self + } + + /// Add a flag to the invocation of the ar + /// + /// # Example + /// + /// ```no_run + /// cc::Build::new() + /// .file("src/foo.c") + /// .file("src/bar.c") + /// .ar_flag("/NODEFAULTLIB:libc.dll") + /// .compile("foo"); + /// ``` + pub fn ar_flag(&mut self, flag: impl AsRef) -> &mut Build { + self.ar_flags.push(flag.as_ref().into()); + self + } + + /// Add a flag that will only be used with assembly files. + /// + /// The flag will be applied to input files with either a `.s` or + /// `.asm` extension (case insensitive). + /// + /// # Example + /// + /// ```no_run + /// cc::Build::new() + /// .asm_flag("-Wa,-defsym,abc=1") + /// .file("src/foo.S") // The asm flag will be applied here + /// .file("src/bar.c") // The asm flag will not be applied here + /// .compile("foo"); + /// ``` + pub fn asm_flag(&mut self, flag: impl AsRef) -> &mut Build { + self.asm_flags.push(flag.as_ref().into()); + self + } + + /// Add an arbitrary flag to the invocation of the compiler if it supports it + /// + /// # Example + /// + /// ```no_run + /// cc::Build::new() + /// .file("src/foo.c") + /// .flag_if_supported("-Wlogical-op") // only supported by GCC + /// .flag_if_supported("-Wunreachable-code") // only supported by clang + /// .compile("foo"); + /// ``` + pub fn flag_if_supported(&mut self, flag: impl AsRef) -> &mut Build { + self.flags_supported.push(flag.as_ref().into()); + self + } + + /// Add flags from the specified environment variable. + /// + /// Normally the `cc` crate will consult with the standard set of environment + /// variables (such as `CFLAGS` and `CXXFLAGS`) to construct the compiler invocation. Use of + /// this method provides additional levers for the end user to use when configuring the build + /// process. + /// + /// Just like the standard variables, this method will search for an environment variable with + /// appropriate target prefixes, when appropriate. + /// + /// # Examples + /// + /// This method is particularly beneficial in introducing the ability to specify crate-specific + /// flags. + /// + /// ```no_run + /// cc::Build::new() + /// .file("src/foo.c") + /// .try_flags_from_environment(concat!(env!("CARGO_PKG_NAME"), "_CFLAGS")) + /// .expect("the environment variable must be specified and UTF-8") + /// .compile("foo"); + /// ``` + /// + pub fn try_flags_from_environment(&mut self, environ_key: &str) -> Result<&mut Build, Error> { + let flags = self.envflags(environ_key)?.ok_or_else(|| { + Error::new( + ErrorKind::EnvVarNotFound, + format!("could not find environment variable {environ_key}"), + ) + })?; + self.flags.extend( + flags + .into_iter() + .map(|flag| Arc::from(OsString::from(flag).as_os_str())), + ); + Ok(self) + } + + /// Set the `-shared` flag. + /// + /// This will typically be ignored by the compiler when calling [`Self::compile()`] since it only + /// produces static libraries. + /// + /// # Example + /// + /// ```no_run + /// // This will create a library named "liblibfoo.so.a" + /// cc::Build::new() + /// .file("src/foo.c") + /// .shared_flag(true) + /// .compile("libfoo.so"); + /// ``` + #[deprecated = "cc only creates static libraries, setting this does nothing"] + pub fn shared_flag(&mut self, shared_flag: bool) -> &mut Build { + self.shared_flag = Some(shared_flag); + self + } + + /// Set the `-static` flag. + /// + /// This will typically be ignored by the compiler when calling [`Self::compile()`] since it only + /// produces static libraries. + /// + /// # Example + /// + /// ```no_run + /// cc::Build::new() + /// .file("src/foo.c") + /// .shared_flag(true) + /// .static_flag(true) + /// .compile("foo"); + /// ``` + #[deprecated = "cc only creates static libraries, setting this does nothing"] + pub fn static_flag(&mut self, static_flag: bool) -> &mut Build { + self.static_flag = Some(static_flag); + self + } + + /// Disables the generation of default compiler flags. The default compiler + /// flags may cause conflicts in some cross compiling scenarios. + /// + /// Setting the `CRATE_CC_NO_DEFAULTS` environment variable has the same + /// effect as setting this to `true`. The presence of the environment + /// variable and the value of `no_default_flags` will be OR'd together. + pub fn no_default_flags(&mut self, no_default_flags: bool) -> &mut Build { + self.no_default_flags = no_default_flags; + self + } + + /// Add a file which will be compiled + pub fn file>(&mut self, p: P) -> &mut Build { + self.files.push(p.as_ref().into()); + self + } + + /// Add files which will be compiled + pub fn files

(&mut self, p: P) -> &mut Build + where + P: IntoIterator, + P::Item: AsRef, + { + for file in p.into_iter() { + self.file(file); + } + self + } + + /// Get the files which will be compiled + pub fn get_files(&self) -> impl Iterator { + self.files.iter().map(AsRef::as_ref) + } + + /// Set C++ support. + /// + /// The other `cpp_*` options will only become active if this is set to + /// `true`. + /// + /// The name of the C++ standard library to link is decided by: + /// 1. If [`cpp_link_stdlib`](Build::cpp_link_stdlib) is set, use its value. + /// 2. Else if the `CXXSTDLIB` environment variable is set, use its value. + /// 3. Else the default is `c++` for OS X and BSDs, `c++_shared` for Android, + /// `None` for MSVC and `stdc++` for anything else. + pub fn cpp(&mut self, cpp: bool) -> &mut Build { + self.cpp = cpp; + self + } + + /// Set CUDA C++ support. + /// + /// Enabling CUDA will invoke the CUDA compiler, NVCC. While NVCC accepts + /// the most common compiler flags, e.g. `-std=c++17`, some project-specific + /// flags might have to be prefixed with "-Xcompiler" flag, for example as + /// `.flag("-Xcompiler").flag("-fpermissive")`. See the documentation for + /// `nvcc`, the CUDA compiler driver, at + /// for more information. + /// + /// If enabled, this also implicitly enables C++ support. + pub fn cuda(&mut self, cuda: bool) -> &mut Build { + self.cuda = cuda; + if cuda { + self.cpp = true; + self.cudart = Some("static".into()); + } + self + } + + /// Link CUDA run-time. + /// + /// This option mimics the `--cudart` NVCC command-line option. Just like + /// the original it accepts `{none|shared|static}`, with default being + /// `static`. The method has to be invoked after `.cuda(true)`, or not + /// at all, if the default is right for the project. + pub fn cudart(&mut self, cudart: &str) -> &mut Build { + if self.cuda { + self.cudart = Some(cudart.into()); + } + self + } + + /// Set CUDA host compiler. + /// + /// By default, a `-ccbin` flag will be passed to NVCC to specify the + /// underlying host compiler. The value of `-ccbin` is the same as the + /// chosen C++ compiler. This is not always desired, because NVCC might + /// not support that compiler. In this case, you can remove the `-ccbin` + /// flag so that NVCC will choose the host compiler by itself. + pub fn ccbin(&mut self, ccbin: bool) -> &mut Build { + self.ccbin = ccbin; + self + } + + /// Specify the C or C++ language standard version. + /// + /// These values are common to modern versions of GCC, Clang and MSVC: + /// - `c11` for ISO/IEC 9899:2011 + /// - `c17` for ISO/IEC 9899:2018 + /// - `c++14` for ISO/IEC 14882:2014 + /// - `c++17` for ISO/IEC 14882:2017 + /// - `c++20` for ISO/IEC 14882:2020 + /// + /// Other values have less broad support, e.g. MSVC does not support `c++11` + /// (`c++14` is the minimum), `c89` (omit the flag instead) or `c99`. + /// + /// For compiling C++ code, you should also set `.cpp(true)`. + /// + /// The default is that no standard flag is passed to the compiler, so the + /// language version will be the compiler's default. + /// + /// # Example + /// + /// ```no_run + /// cc::Build::new() + /// .file("src/modern.cpp") + /// .cpp(true) + /// .std("c++17") + /// .compile("modern"); + /// ``` + pub fn std(&mut self, std: &str) -> &mut Build { + self.std = Some(std.into()); + self + } + + /// Set warnings into errors flag. + /// + /// Disabled by default. + /// + /// Warning: turning warnings into errors only make sense + /// if you are a developer of the crate using cc-rs. + /// Some warnings only appear on some architecture or + /// specific version of the compiler. Any user of this crate, + /// or any other crate depending on it, could fail during + /// compile time. + /// + /// # Example + /// + /// ```no_run + /// cc::Build::new() + /// .file("src/foo.c") + /// .warnings_into_errors(true) + /// .compile("libfoo.a"); + /// ``` + pub fn warnings_into_errors(&mut self, warnings_into_errors: bool) -> &mut Build { + self.warnings_into_errors = warnings_into_errors; + self + } + + /// Set warnings flags. + /// + /// Adds some flags: + /// - "-Wall" for MSVC. + /// - "-Wall", "-Wextra" for GNU and Clang. + /// + /// Enabled by default. + /// + /// # Example + /// + /// ```no_run + /// cc::Build::new() + /// .file("src/foo.c") + /// .warnings(false) + /// .compile("libfoo.a"); + /// ``` + pub fn warnings(&mut self, warnings: bool) -> &mut Build { + self.warnings = Some(warnings); + self.extra_warnings = Some(warnings); + self + } + + /// Set extra warnings flags. + /// + /// Adds some flags: + /// - nothing for MSVC. + /// - "-Wextra" for GNU and Clang. + /// + /// Enabled by default. + /// + /// # Example + /// + /// ```no_run + /// // Disables -Wextra, -Wall remains enabled: + /// cc::Build::new() + /// .file("src/foo.c") + /// .extra_warnings(false) + /// .compile("libfoo.a"); + /// ``` + pub fn extra_warnings(&mut self, warnings: bool) -> &mut Build { + self.extra_warnings = Some(warnings); + self + } + + /// Set the standard library to link against when compiling with C++ + /// support. + /// + /// If the `CXXSTDLIB` environment variable is set, its value will + /// override the default value, but not the value explicitly set by calling + /// this function. + /// + /// A value of `None` indicates that no automatic linking should happen, + /// otherwise cargo will link against the specified library. + /// + /// The given library name must not contain the `lib` prefix. + /// + /// Common values: + /// - `stdc++` for GNU + /// - `c++` for Clang + /// - `c++_shared` or `c++_static` for Android + /// + /// # Example + /// + /// ```no_run + /// cc::Build::new() + /// .file("src/foo.c") + /// .shared_flag(true) + /// .cpp_link_stdlib("stdc++") + /// .compile("libfoo.so"); + /// ``` + pub fn cpp_link_stdlib<'a, V: Into>>( + &mut self, + cpp_link_stdlib: V, + ) -> &mut Build { + self.cpp_link_stdlib = Some(cpp_link_stdlib.into().map(Arc::from)); + self + } + + /// Force linker to statically link C++ stdlib. By default cc-rs will emit + /// rustc-link flag to link against system C++ stdlib (e.g. libstdc++.so, libc++.so) + /// Provide value of `true` if linking against system library is not desired + /// + /// Note that for `wasm32` target C++ stdlib will always be linked statically + /// + /// # Example + /// + /// ```no_run + /// cc::Build::new() + /// .file("src/foo.cpp") + /// .cpp(true) + /// .cpp_link_stdlib("stdc++") + /// .cpp_link_stdlib_static(true) + /// .compile("foo"); + /// ``` + pub fn cpp_link_stdlib_static(&mut self, is_static: bool) -> &mut Build { + self.cpp_link_stdlib_static = is_static; + self + } + + /// Force the C++ compiler to use the specified standard library. + /// + /// Setting this option will automatically set `cpp_link_stdlib` to the same + /// value. + /// + /// The default value of this option is always `None`. + /// + /// This option has no effect when compiling for a Visual Studio based + /// target. + /// + /// This option sets the `-stdlib` flag, which is only supported by some + /// compilers (clang, icc) but not by others (gcc). The library will not + /// detect which compiler is used, as such it is the responsibility of the + /// caller to ensure that this option is only used in conjunction with a + /// compiler which supports the `-stdlib` flag. + /// + /// A value of `None` indicates that no specific C++ standard library should + /// be used, otherwise `-stdlib` is added to the compile invocation. + /// + /// The given library name must not contain the `lib` prefix. + /// + /// Common values: + /// - `stdc++` for GNU + /// - `c++` for Clang + /// + /// # Example + /// + /// ```no_run + /// cc::Build::new() + /// .file("src/foo.c") + /// .cpp_set_stdlib("c++") + /// .compile("libfoo.a"); + /// ``` + pub fn cpp_set_stdlib<'a, V: Into>>( + &mut self, + cpp_set_stdlib: V, + ) -> &mut Build { + let cpp_set_stdlib = cpp_set_stdlib.into().map(Arc::from); + self.cpp_set_stdlib.clone_from(&cpp_set_stdlib); + self.cpp_link_stdlib = Some(cpp_set_stdlib); + self + } + + /// Configures the `rustc` target this configuration will be compiling + /// for. + /// + /// This will fail if using a target not in a pre-compiled list taken from + /// `rustc +nightly --print target-list`. The list will be updated + /// periodically. + /// + /// You should avoid setting this in build scripts, target information + /// will instead be retrieved from the environment variables `TARGET` and + /// `CARGO_CFG_TARGET_*` that Cargo sets. + /// + /// # Example + /// + /// ```no_run + /// cc::Build::new() + /// .file("src/foo.c") + /// .target("aarch64-linux-android") + /// .compile("foo"); + /// ``` + pub fn target(&mut self, target: &str) -> &mut Build { + self.target = Some(target.into()); + self + } + + /// Configures the host assumed by this configuration. + /// + /// This option is automatically scraped from the `HOST` environment + /// variable by build scripts, so it's not required to call this function. + /// + /// # Example + /// + /// ```no_run + /// cc::Build::new() + /// .file("src/foo.c") + /// .host("arm-linux-gnueabihf") + /// .compile("foo"); + /// ``` + pub fn host(&mut self, host: &str) -> &mut Build { + self.host = Some(host.into()); + self + } + + /// Configures the optimization level of the generated object files. + /// + /// This option is automatically scraped from the `OPT_LEVEL` environment + /// variable by build scripts, so it's not required to call this function. + pub fn opt_level(&mut self, opt_level: u32) -> &mut Build { + self.opt_level = Some(opt_level.to_string().into()); + self + } + + /// Configures the optimization level of the generated object files. + /// + /// This option is automatically scraped from the `OPT_LEVEL` environment + /// variable by build scripts, so it's not required to call this function. + pub fn opt_level_str(&mut self, opt_level: &str) -> &mut Build { + self.opt_level = Some(opt_level.into()); + self + } + + /// Configures whether the compiler will emit debug information when + /// generating object files. + /// + /// This option is automatically scraped from the `DEBUG` environment + /// variable by build scripts, so it's not required to call this function. + pub fn debug(&mut self, debug: bool) -> &mut Build { + self.debug = Some(debug.to_string().into()); + self + } + + /// Configures whether the compiler will emit debug information when + /// generating object files. + /// + /// This should be one of the values accepted by Cargo's [`debug`][1] + /// profile setting, which cc-rs will try to map to the appropriate C + /// compiler flag. + /// + /// This option is automatically scraped from the `DEBUG` environment + /// variable by build scripts, so it's not required to call this function. + /// + /// [1]: https://doc.rust-lang.org/cargo/reference/profiles.html#debug + pub fn debug_str(&mut self, debug: &str) -> &mut Build { + self.debug = Some(debug.into()); + self + } + + /// Configures whether the compiler will emit instructions to store + /// frame pointers during codegen. + /// + /// This option is automatically enabled when debug information is emitted. + /// Otherwise the target platform compiler's default will be used. + /// You can use this option to force a specific setting. + pub fn force_frame_pointer(&mut self, force: bool) -> &mut Build { + self.force_frame_pointer = Some(force); + self + } + + /// Configures the output directory where all object files and static + /// libraries will be located. + /// + /// This option is automatically scraped from the `OUT_DIR` environment + /// variable by build scripts, so it's not required to call this function. + pub fn out_dir>(&mut self, out_dir: P) -> &mut Build { + self.out_dir = Some(out_dir.as_ref().into()); + self + } + + /// Configures the compiler to be used to produce output. + /// + /// This option is automatically determined from the target platform or a + /// number of environment variables, so it's not required to call this + /// function. + pub fn compiler>(&mut self, compiler: P) -> &mut Build { + self.compiler = Some(compiler.as_ref().into()); + self + } + + /// Configures the tool used to assemble archives. + /// + /// This option is automatically determined from the target platform or a + /// number of environment variables, so it's not required to call this + /// function. + pub fn archiver>(&mut self, archiver: P) -> &mut Build { + self.archiver = Some(archiver.as_ref().into()); + self + } + + /// Configures the tool used to index archives. + /// + /// This option is automatically determined from the target platform or a + /// number of environment variables, so it's not required to call this + /// function. + pub fn ranlib>(&mut self, ranlib: P) -> &mut Build { + self.ranlib = Some(ranlib.as_ref().into()); + self + } + + /// Define whether metadata should be emitted for cargo allowing it to + /// automatically link the binary. Defaults to `true`. + /// + /// The emitted metadata is: + /// + /// - `rustc-link-lib=static=`*compiled lib* + /// - `rustc-link-search=native=`*target folder* + /// - When target is MSVC, the ATL-MFC libs are added via `rustc-link-search=native=` + /// - When C++ is enabled, the C++ stdlib is added via `rustc-link-lib` + /// - If `emit_rerun_if_env_changed` is not `false`, `rerun-if-env-changed=`*env* + /// + pub fn cargo_metadata(&mut self, cargo_metadata: bool) -> &mut Build { + self.cargo_output.metadata = cargo_metadata; + self + } + + /// Define whether compile warnings should be emitted for cargo. Defaults to + /// `true`. + /// + /// If disabled, compiler messages will not be printed. + /// Issues unrelated to the compilation will always produce cargo warnings regardless of this setting. + pub fn cargo_warnings(&mut self, cargo_warnings: bool) -> &mut Build { + self.cargo_output.warnings = cargo_warnings; + self + } + + /// Define whether debug information should be emitted for cargo. Defaults to whether + /// or not the environment variable `CC_ENABLE_DEBUG_OUTPUT` is set. + /// + /// If enabled, the compiler will emit debug information when generating object files, + /// such as the command invoked and the exit status. + pub fn cargo_debug(&mut self, cargo_debug: bool) -> &mut Build { + self.cargo_output.debug = cargo_debug; + self + } + + /// Define whether compiler output (to stdout) should be emitted. Defaults to `true` + /// (forward compiler stdout to this process' stdout) + /// + /// Some compilers emit errors to stdout, so if you *really* need stdout to be clean + /// you should also set this to `false`. + pub fn cargo_output(&mut self, cargo_output: bool) -> &mut Build { + self.cargo_output.output = if cargo_output { + OutputKind::Forward + } else { + OutputKind::Discard + }; + self + } + + /// Adds a native library modifier that will be added to the + /// `rustc-link-lib=static:MODIFIERS=LIBRARY_NAME` metadata line + /// emitted for cargo if `cargo_metadata` is enabled. + /// See + /// for the list of modifiers accepted by rustc. + pub fn link_lib_modifier(&mut self, link_lib_modifier: impl AsRef) -> &mut Build { + self.link_lib_modifiers + .push(link_lib_modifier.as_ref().into()); + self + } + + /// Configures whether the compiler will emit position independent code. + /// + /// This option defaults to `false` for `windows-gnu` and bare metal targets and + /// to `true` for all other targets. + pub fn pic(&mut self, pic: bool) -> &mut Build { + self.pic = Some(pic); + self + } + + /// Configures whether the Procedure Linkage Table is used for indirect + /// calls into shared libraries. + /// + /// The PLT is used to provide features like lazy binding, but introduces + /// a small performance loss due to extra pointer indirection. Setting + /// `use_plt` to `false` can provide a small performance increase. + /// + /// Note that skipping the PLT requires a recent version of GCC/Clang. + /// + /// This only applies to ELF targets. It has no effect on other platforms. + pub fn use_plt(&mut self, use_plt: bool) -> &mut Build { + self.use_plt = Some(use_plt); + self + } + + /// Define whether metadata should be emitted for cargo to only trigger + /// rebuild when detected environment changes, by default build script is + /// always run on every compilation if no rerun cargo metadata is emitted. + /// + /// NOTE that cc does not emit metadata to detect changes for `PATH`, since it could + /// be changed every compilation yet does not affect the result of compilation + /// (i.e. rust-analyzer adds temporary directory to `PATH`). + /// + /// cc in general, has no way detecting changes to compiler, as there are so many ways to + /// change it and sidestep the detection, for example the compiler might be wrapped in a script + /// so detecting change of the file, or using checksum won't work. + /// + /// We recommend users to decide for themselves, if they want rebuild if the compiler has been upgraded + /// or changed, and how to detect that. + /// + /// This has no effect if the `cargo_metadata` option is `false`. + /// + /// This option defaults to `true`. + pub fn emit_rerun_if_env_changed(&mut self, emit_rerun_if_env_changed: bool) -> &mut Build { + self.emit_rerun_if_env_changed = emit_rerun_if_env_changed; + self + } + + /// Configures whether the /MT flag or the /MD flag will be passed to msvc build tools. + /// + /// This option defaults to `false`, and affect only msvc targets. + pub fn static_crt(&mut self, static_crt: bool) -> &mut Build { + self.static_crt = Some(static_crt); + self + } + + /// Configure whether *FLAGS variables are parsed using `shlex`, similarly to `make` and + /// `cmake`. + /// + /// This option defaults to `false`. + pub fn shell_escaped_flags(&mut self, shell_escaped_flags: bool) -> &mut Build { + self.shell_escaped_flags = Some(shell_escaped_flags); + self + } + + /// Configure whether cc should automatically inherit compatible flags passed to rustc + /// from `CARGO_ENCODED_RUSTFLAGS`. + /// + /// This option defaults to `true`. + pub fn inherit_rustflags(&mut self, inherit_rustflags: bool) -> &mut Build { + self.inherit_rustflags = inherit_rustflags; + self + } + + /// Prefer to use clang-cl over msvc. + /// + /// This option defaults to `false`. + pub fn prefer_clang_cl_over_msvc(&mut self, prefer_clang_cl_over_msvc: bool) -> &mut Build { + self.prefer_clang_cl_over_msvc = prefer_clang_cl_over_msvc; + self + } + + /// Set an environment variable for compiler invocations and other child processes. + /// + /// `cc` reads a lot of different variables from the current process' environment. It currently + /// allows the following standard environment variables to be overwritten by this function: + /// - `SDKROOT` + /// - `*_DEPLOYMENT_TARGET` + /// - `WASI_SDK_ROOT` + /// + /// The logic here is "environment variables that the C compiler could itself reasonably have + /// read". + pub fn env(&mut self, key: K, val: V) -> &mut Build + where + K: AsRef, + V: AsRef, + { + self.env.push((key.as_ref().into(), val.as_ref().into())); + self + } + + // retained for backwards compatibility only + #[doc(hidden)] + #[deprecated = "use `env` instead"] + pub fn __set_env(&mut self, key: K, val: V) -> &mut Build + where + K: AsRef, + V: AsRef, + { + self.env(key, val) + } +} + +/// Invoke or fetch the compiler or archiver. +impl Build { + /// Run the compiler to test if it accepts the given flag. + /// + /// For a convenience method for setting flags conditionally, + /// see `flag_if_supported()`. + /// + /// It may return error if it's unable to run the compiler with a test file + /// (e.g. the compiler is missing or a write to the `out_dir` failed). + /// + /// Note: Once computed, the result of this call is stored in the + /// `known_flag_support` field. If `is_flag_supported(flag)` + /// is called again, the result will be read from the hash table. + pub fn is_flag_supported(&self, flag: impl AsRef) -> Result { + self.is_flag_supported_inner( + flag.as_ref(), + &self.get_base_compiler()?, + &self.get_target()?, + ) + } + + fn ensure_check_file(&self) -> Result { + let out_dir = self.get_out_dir()?; + let src = if self.cuda { + assert!(self.cpp); + out_dir.join("flag_check.cu") + } else if self.cpp { + out_dir.join("flag_check.cpp") + } else { + out_dir.join("flag_check.c") + }; + + if !src.exists() { + let mut f = fs::File::create(&src)?; + write!(f, "int main(void) {{ return 0; }}")?; + } + + Ok(src) + } + + fn is_flag_supported_inner( + &self, + flag: &OsStr, + tool: &Tool, + target: &TargetInfo<'_>, + ) -> Result { + let compiler_flag = CompilerFlag { + compiler: tool.path().into(), + flag: flag.into(), + }; + + if let Some(is_supported) = self + .build_cache + .known_flag_support_status_cache + .read() + .unwrap() + .get(&compiler_flag) + .cloned() + { + return Ok(is_supported); + } + + let out_dir = self.get_out_dir()?; + let src = self.ensure_check_file()?; + let obj = out_dir.join("flag_check"); + + let mut compiler = { + let mut cfg = Build::new(); + cfg.flag(flag) + .compiler(tool.path()) + .cargo_metadata(self.cargo_output.metadata) + .opt_level(0) + .debug(false) + .cpp(self.cpp) + .cuda(self.cuda) + .inherit_rustflags(false) + .emit_rerun_if_env_changed(self.emit_rerun_if_env_changed); + if let Some(target) = &self.target { + cfg.target(target); + } + if let Some(host) = &self.host { + cfg.host(host); + } + cfg.try_get_compiler()? + }; + + // Clang uses stderr for verbose output, which yields a false positive + // result if the CFLAGS/CXXFLAGS include -v to aid in debugging. + if compiler.family.verbose_stderr() { + compiler.remove_arg("-v".into()); + } + if compiler.is_like_clang() { + // Avoid reporting that the arg is unsupported just because the + // compiler complains that it wasn't used. + compiler.push_cc_arg("-Wno-unused-command-line-argument".into()); + } + + let mut cmd = compiler.to_command(); + command_add_output_file( + &mut cmd, + &obj, + CmdAddOutputFileArgs { + cuda: self.cuda, + is_assembler_msvc: false, + msvc: compiler.is_like_msvc(), + clang: compiler.is_like_clang(), + gnu: compiler.is_like_gnu(), + is_asm: false, + is_arm: is_arm(target), + }, + ); + + // Checking for compiler flags does not require linking (and we _must_ + // avoid making it do so, since it breaks cross-compilation when the C + // compiler isn't configured to be able to link). + // https://github.com/rust-lang/cc-rs/issues/1423 + cmd.arg("-c"); + + if compiler.supports_path_delimiter() { + cmd.arg("--"); + } + + cmd.arg(&src); + + if compiler.is_like_msvc() { + // On MSVC we need to make sure the LIB directory is included + // so the CRT can be found. + for (key, value) in &tool.env { + if key == "LIB" { + cmd.env("LIB", value); + break; + } + } + } + + let output = cmd.current_dir(out_dir).output()?; + let is_supported = output.status.success() && output.stderr.is_empty(); + + self.build_cache + .known_flag_support_status_cache + .write() + .unwrap() + .insert(compiler_flag, is_supported); + + Ok(is_supported) + } + + /// Run the compiler, generating the file `output` + /// + /// This will return a result instead of panicking; see [`Self::compile()`] for + /// the complete description. + pub fn try_compile(&self, output: &str) -> Result<(), Error> { + let mut output_components = Path::new(output).components(); + match (output_components.next(), output_components.next()) { + (Some(Component::Normal(_)), None) => {} + _ => { + return Err(Error::new( + ErrorKind::InvalidArgument, + "argument of `compile` must be a single normal path component", + )); + } + } + + let (lib_name, gnu_lib_name) = if output.starts_with("lib") && output.ends_with(".a") { + (&output[3..output.len() - 2], output.to_owned()) + } else { + let mut gnu = String::with_capacity(5 + output.len()); + gnu.push_str("lib"); + gnu.push_str(output); + gnu.push_str(".a"); + (output, gnu) + }; + let dst = self.get_out_dir()?; + + let objects = objects_from_files(&self.files, &dst)?; + + self.compile_objects(&objects)?; + self.assemble(lib_name, &dst.join(gnu_lib_name), &objects)?; + + let target = self.get_target()?; + if target.env == "msvc" { + let compiler = self.get_base_compiler()?; + let atlmfc_lib = compiler + .env() + .iter() + .find(|&(var, _)| var.as_os_str() == OsStr::new("LIB")) + .and_then(|(_, lib_paths)| { + env::split_paths(lib_paths).find(|path| { + let sub = Path::new("atlmfc/lib"); + path.ends_with(sub) || path.parent().map_or(false, |p| p.ends_with(sub)) + }) + }); + + if let Some(atlmfc_lib) = atlmfc_lib { + self.cargo_output.print_metadata(&format_args!( + "cargo:rustc-link-search=native={}", + atlmfc_lib.display() + )); + } + } + + if self.link_lib_modifiers.is_empty() { + self.cargo_output + .print_metadata(&format_args!("cargo:rustc-link-lib=static={lib_name}")); + } else { + self.cargo_output.print_metadata(&format_args!( + "cargo:rustc-link-lib=static:{}={}", + JoinOsStrs { + slice: &self.link_lib_modifiers, + delimiter: ',' + }, + lib_name + )); + } + self.cargo_output.print_metadata(&format_args!( + "cargo:rustc-link-search=native={}", + dst.display() + )); + + // Add specific C++ libraries, if enabled. + if self.cpp { + if let Some(stdlib) = self.get_cpp_link_stdlib()? { + if self.cpp_link_stdlib_static { + self.cargo_output.print_metadata(&format_args!( + "cargo:rustc-link-lib=static={}", + stdlib.display() + )); + } else { + self.cargo_output + .print_metadata(&format_args!("cargo:rustc-link-lib={}", stdlib.display())); + } + } + // Link c++ lib from WASI sysroot + if target.arch == "wasm32" { + if target.os == "wasi" { + if let Ok(wasi_sysroot) = self.wasi_sysroot() { + self.cargo_output.print_metadata(&format_args!( + "cargo:rustc-flags=-L {}/lib/{} -lstatic=c++ -lstatic=c++abi", + Path::new(&wasi_sysroot).display(), + self.get_raw_target()? + )); + } + } else if target.os == "linux" { + let musl_sysroot = self.wasm_musl_sysroot().unwrap(); + self.cargo_output.print_metadata(&format_args!( + "cargo:rustc-flags=-L {}/lib -lstatic=c++ -lstatic=c++abi", + Path::new(&musl_sysroot).display(), + )); + } + } + } + + let cudart = match &self.cudart { + Some(opt) => opt, // {none|shared|static} + None => "none", + }; + if cudart != "none" { + if let Some(nvcc) = self.which(&self.get_compiler().path, None) { + // Try to figure out the -L search path. If it fails, + // it's on user to specify one by passing it through + // RUSTFLAGS environment variable. + let mut libtst = false; + let mut libdir = nvcc; + libdir.pop(); // remove 'nvcc' + libdir.push(".."); + if cfg!(target_os = "linux") { + libdir.push("targets"); + libdir.push(format!("{}-linux", target.arch)); + if !libdir.exists() && target.arch == "aarch64" { + libdir.pop(); + libdir.push("sbsa-linux"); + } + libdir.push("lib"); + libtst = true; + } else if cfg!(target_env = "msvc") { + libdir.push("lib"); + match target.arch { + "x86_64" => { + libdir.push("x64"); + libtst = true; + } + "x86" => { + libdir.push("Win32"); + libtst = true; + } + _ => libtst = false, + } + } + if libtst && libdir.is_dir() { + self.cargo_output.print_metadata(&format_args!( + "cargo:rustc-link-search=native={}", + libdir.to_str().unwrap() + )); + } + + // And now the -l flag. + let lib = match cudart { + "shared" => "cudart", + "static" => "cudart_static", + bad => panic!("unsupported cudart option: {}", bad), + }; + self.cargo_output + .print_metadata(&format_args!("cargo:rustc-link-lib={lib}")); + } + } + + Ok(()) + } + + /// Run the compiler, generating the file `output` + /// + /// # Library name + /// + /// The `output` string argument determines the file name for the compiled + /// library. The Rust compiler will create an assembly named "lib"+output+".a". + /// MSVC will create a file named output+".lib". + /// + /// The choice of `output` is close to arbitrary, but: + /// + /// - must be nonempty, + /// - must not contain a path separator (`/`), + /// - must be unique across all `compile` invocations made by the same build + /// script. + /// + /// If your build script compiles a single source file, the base name of + /// that source file would usually be reasonable: + /// + /// ```no_run + /// cc::Build::new().file("blobstore.c").compile("blobstore"); + /// ``` + /// + /// Compiling multiple source files, some people use their crate's name, or + /// their crate's name + "-cc". + /// + /// Otherwise, please use your imagination. + /// + /// For backwards compatibility, if `output` starts with "lib" *and* ends + /// with ".a", a second "lib" prefix and ".a" suffix do not get added on, + /// but this usage is deprecated; please omit `lib` and `.a` in the argument + /// that you pass. + /// + /// # Panics + /// + /// Panics if `output` is not formatted correctly or if one of the underlying + /// compiler commands fails. It can also panic if it fails reading file names + /// or creating directories. + pub fn compile(&self, output: &str) { + if let Err(e) = self.try_compile(output) { + fail(&e.message); + } + } + + /// Run the compiler, generating intermediate files, but without linking + /// them into an archive file. + /// + /// This will return a list of compiled object files, in the same order + /// as they were passed in as `file`/`files` methods. + pub fn compile_intermediates(&self) -> Vec { + match self.try_compile_intermediates() { + Ok(v) => v, + Err(e) => fail(&e.message), + } + } + + /// Run the compiler, generating intermediate files, but without linking + /// them into an archive file. + /// + /// This will return a result instead of panicking; see `compile_intermediates()` for the complete description. + pub fn try_compile_intermediates(&self) -> Result, Error> { + let dst = self.get_out_dir()?; + let objects = objects_from_files(&self.files, &dst)?; + + self.compile_objects(&objects)?; + + Ok(objects.into_iter().map(|v| v.dst).collect()) + } + + fn compile_objects(&self, objs: &[Object]) -> Result<(), Error> { + if self.is_disabled() { + return Err(Error::new( + ErrorKind::Disabled, + "the `cc` crate's functionality has been disabled by the `CC_FORCE_DISABLE` environment variable.", + )); + } + + #[cfg(feature = "parallel")] + if objs.len() > 1 { + return parallel::run_commands_in_parallel( + &self.cargo_output, + &mut objs.iter().map(|obj| self.create_compile_object_cmd(obj)), + ); + } + + for obj in objs { + let mut cmd = self.create_compile_object_cmd(obj)?; + run(&mut cmd, &self.cargo_output)?; + } + + Ok(()) + } + + fn create_compile_object_cmd(&self, obj: &Object) -> Result { + let asm_ext = AsmFileExt::from_path(&obj.src); + let is_asm = asm_ext.is_some(); + let target = self.get_target()?; + let msvc = target.env == "msvc"; + let compiler = self.try_get_compiler()?; + + let is_assembler_msvc = msvc && asm_ext == Some(AsmFileExt::DotAsm); + let mut cmd = if is_assembler_msvc { + self.msvc_macro_assembler()? + } else { + compiler.to_command() + }; + let is_arm = is_arm(&target); + command_add_output_file( + &mut cmd, + &obj.dst, + CmdAddOutputFileArgs { + cuda: self.cuda, + is_assembler_msvc, + msvc: compiler.is_like_msvc(), + clang: compiler.is_like_clang(), + gnu: compiler.is_like_gnu(), + is_asm, + is_arm, + }, + ); + // armasm and armasm64 don't require -c option + if !is_assembler_msvc || !is_arm { + cmd.arg("-c"); + } + if self.cuda && self.cuda_file_count() > 1 { + cmd.arg("--device-c"); + } + if is_asm { + cmd.args(self.asm_flags.iter().map(std::ops::Deref::deref)); + } + + if compiler.supports_path_delimiter() && !is_assembler_msvc { + // #513: For `clang-cl`, separate flags/options from the input file. + // When cross-compiling macOS -> Windows, this avoids interpreting + // common `/Users/...` paths as the `/U` flag and triggering + // `-Wslash-u-filename` warning. + cmd.arg("--"); + } + cmd.arg(&obj.src); + + if cfg!(target_os = "macos") { + self.fix_env_for_apple_os(&mut cmd)?; + } + + Ok(cmd) + } + + /// This will return a result instead of panicking; see [`Self::expand()`] for + /// the complete description. + pub fn try_expand(&self) -> Result, Error> { + let compiler = self.try_get_compiler()?; + let mut cmd = compiler.to_command(); + cmd.arg("-E"); + + assert!( + self.files.len() <= 1, + "Expand may only be called for a single file" + ); + + let is_asm = self + .files + .iter() + .map(std::ops::Deref::deref) + .find_map(AsmFileExt::from_path) + .is_some(); + + if compiler.family == (ToolFamily::Msvc { clang_cl: true }) && !is_asm { + // #513: For `clang-cl`, separate flags/options from the input file. + // When cross-compiling macOS -> Windows, this avoids interpreting + // common `/Users/...` paths as the `/U` flag and triggering + // `-Wslash-u-filename` warning. + cmd.arg("--"); + } + + cmd.args(self.files.iter().map(std::ops::Deref::deref)); + + run_output(&mut cmd, &self.cargo_output) + } + + /// Run the compiler, returning the macro-expanded version of the input files. + /// + /// This is only relevant for C and C++ files. + /// + /// # Panics + /// Panics if more than one file is present in the config, or if compiler + /// path has an invalid file name. + /// + /// # Example + /// ```no_run + /// let out = cc::Build::new().file("src/foo.c").expand(); + /// ``` + pub fn expand(&self) -> Vec { + match self.try_expand() { + Err(e) => fail(&e.message), + Ok(v) => v, + } + } + + /// Get the compiler that's in use for this configuration. + /// + /// This function will return a `Tool` which represents the culmination + /// of this configuration at a snapshot in time. The returned compiler can + /// be inspected (e.g. the path, arguments, environment) to forward along to + /// other tools, or the `to_command` method can be used to invoke the + /// compiler itself. + /// + /// This method will take into account all configuration such as debug + /// information, optimization level, include directories, defines, etc. + /// Additionally, the compiler binary in use follows the standard + /// conventions for this path, e.g. looking at the explicitly set compiler, + /// environment variables (a number of which are inspected here), and then + /// falling back to the default configuration. + /// + /// # Panics + /// + /// Panics if an error occurred while determining the architecture. + pub fn get_compiler(&self) -> Tool { + match self.try_get_compiler() { + Ok(tool) => tool, + Err(e) => fail(&e.message), + } + } + + /// Get the compiler that's in use for this configuration. + /// + /// This will return a result instead of panicking; see + /// [`get_compiler()`](Self::get_compiler) for the complete description. + pub fn try_get_compiler(&self) -> Result { + let opt_level = self.get_opt_level()?; + let target = self.get_target()?; + + let mut cmd = self.get_base_compiler()?; + + // The flags below are added in roughly the following order: + // 1. Default flags + // - Controlled by `cc-rs`. + // 2. `rustc`-inherited flags + // - Controlled by `rustc`. + // 3. Builder flags + // - Controlled by the developer using `cc-rs` in e.g. their `build.rs`. + // 4. Environment flags + // - Controlled by the end user. + // + // This is important to allow later flags to override previous ones. + + // Copied from + // + // Disables non-English messages from localized linkers. + // Such messages may cause issues with text encoding on Windows + // and prevent inspection of msvc output in case of errors, which we occasionally do. + // This should be acceptable because other messages from rustc are in English anyway, + // and may also be desirable to improve searchability of the compiler diagnostics. + if matches!(cmd.family, ToolFamily::Msvc { clang_cl: false }) { + cmd.env.push(("VSLANG".into(), "1033".into())); + } else { + cmd.env.push(("LC_ALL".into(), "C".into())); + } + + // Disable default flag generation via `no_default_flags` or environment variable + let no_defaults = self.no_default_flags || self.get_env_boolean("CRATE_CC_NO_DEFAULTS"); + if !no_defaults { + self.add_default_flags(&mut cmd, &target, &opt_level)?; + } + + // Specify various flags that are not considered part of the default flags above. + // FIXME(madsmtm): Should these be considered part of the defaults? If no, why not? + if let Some(ref std) = self.std { + let separator = match cmd.family { + ToolFamily::Msvc { .. } => ':', + ToolFamily::Gnu | ToolFamily::Clang { .. } => '=', + }; + cmd.push_cc_arg(format!("-std{separator}{std}").into()); + } + for directory in self.include_directories.iter() { + cmd.args.push("-I".into()); + cmd.args.push(directory.as_os_str().into()); + } + if self.warnings_into_errors { + let warnings_to_errors_flag = cmd.family.warnings_to_errors_flag().into(); + cmd.push_cc_arg(warnings_to_errors_flag); + } + + // If warnings and/or extra_warnings haven't been explicitly set, + // then we set them only if the environment doesn't already have + // CFLAGS/CXXFLAGS, since those variables presumably already contain + // the desired set of warnings flags. + let envflags = self.envflags(if self.cpp { "CXXFLAGS" } else { "CFLAGS" })?; + match self.warnings { + Some(true) => { + let wflags = cmd.family.warnings_flags().into(); + cmd.push_cc_arg(wflags); + } + Some(false) => { + let wflags = cmd.family.warnings_suppression_flags().into(); + cmd.push_cc_arg(wflags); + } + None => { + if envflags.is_none() { + let wflags = cmd.family.warnings_flags().into(); + cmd.push_cc_arg(wflags); + } + } + } + if self.extra_warnings.unwrap_or(envflags.is_none()) { + if let Some(wflags) = cmd.family.extra_warnings_flags() { + cmd.push_cc_arg(wflags.into()); + } + } + + // Add cc flags inherited from matching rustc flags. + if self.inherit_rustflags { + self.add_inherited_rustflags(&mut cmd, &target)?; + } + + // Set flags configured in the builder (do this second-to-last, to allow these to override + // everything above). + for flag in self.flags.iter() { + cmd.args.push((**flag).into()); + } + for flag in self.flags_supported.iter() { + if self + .is_flag_supported_inner(flag, &cmd, &target) + .unwrap_or(false) + { + cmd.push_cc_arg((**flag).into()); + } + } + for (key, value) in self.definitions.iter() { + if let Some(ref value) = *value { + cmd.args.push(format!("-D{key}={value}").into()); + } else { + cmd.args.push(format!("-D{key}").into()); + } + } + + // Set flags from the environment (do this last, to allow these to override everything else). + if let Some(flags) = &envflags { + for arg in flags { + cmd.push_cc_arg(arg.into()); + } + } + + // Set custom env vars that the user specified with `Build::env`. + // + // Do this last, to allow overwriting the other values above. + for (key, val) in &self.env { + cmd.env.push((key.into(), val.into())); + } + + Ok(cmd) + } + + fn add_default_flags( + &self, + cmd: &mut Tool, + target: &TargetInfo<'_>, + opt_level: &str, + ) -> Result<(), Error> { + let raw_target = self.get_raw_target()?; + // Non-target flags + // If the flag is not conditioned on target variable, it belongs here :) + match cmd.family { + ToolFamily::Msvc { .. } => { + cmd.push_cc_arg("-nologo".into()); + + let crt_flag = match self.static_crt { + Some(true) => "-MT", + Some(false) => "-MD", + None => { + let features = cargo_env_var_os("CARGO_CFG_TARGET_FEATURE"); + let features = features.as_deref().unwrap_or_default(); + if features.to_string_lossy().contains("crt-static") { + "-MT" + } else { + "-MD" + } + } + }; + cmd.push_cc_arg(crt_flag.into()); + + match opt_level { + // Msvc uses /O1 to enable all optimizations that minimize code size. + "z" | "s" | "1" => cmd.push_opt_unless_duplicate("-O1".into()), + // -O3 is a valid value for gcc and clang compilers, but not msvc. Cap to /O2. + "2" | "3" => cmd.push_opt_unless_duplicate("-O2".into()), + _ => {} + } + } + ToolFamily::Gnu | ToolFamily::Clang { .. } => { + // arm-linux-androideabi-gcc 4.8 shipped with Android NDK does + // not support '-Oz' + if opt_level == "z" && !cmd.is_like_clang() { + cmd.push_opt_unless_duplicate("-Os".into()); + } else { + cmd.push_opt_unless_duplicate(format!("-O{opt_level}").into()); + } + + if cmd.is_like_clang() && target.os == "android" { + // For compatibility with code that doesn't use pre-defined `__ANDROID__` macro. + // If compiler used via ndk-build or cmake (officially supported build methods) + // this macros is defined. + // See https://android.googlesource.com/platform/ndk/+/refs/heads/ndk-release-r21/build/cmake/android.toolchain.cmake#456 + // https://android.googlesource.com/platform/ndk/+/refs/heads/ndk-release-r21/build/core/build-binary.mk#141 + cmd.push_opt_unless_duplicate("-DANDROID".into()); + } + + if target.os != "ios" + && target.os != "watchos" + && target.os != "tvos" + && target.os != "visionos" + { + cmd.push_cc_arg("-ffunction-sections".into()); + cmd.push_cc_arg("-fdata-sections".into()); + } + // Disable generation of PIC on bare-metal for now: rust-lld doesn't support this yet + // + // `rustc` also defaults to disable PIC on WASM: + // + if self.pic.unwrap_or( + target.os != "windows" + && target.os != "none" + && target.os != "uefi" + && target.os != "vita" + && target.arch != "wasm32" + && target.arch != "wasm64", + ) { + cmd.push_cc_arg("-fPIC".into()); + // PLT only applies if code is compiled with PIC support, + // and only for ELF targets. + if (target.os == "linux" || target.os == "android") + && !self.use_plt.unwrap_or(true) + { + cmd.push_cc_arg("-fno-plt".into()); + } + } + + if target.os == "wasi" { + // Link clang sysroot + if let Ok(wasi_sysroot) = self.wasi_sysroot() { + cmd.push_cc_arg( + format!("--sysroot={}", Path::new(&wasi_sysroot).display()).into(), + ); + } + + // FIXME(madsmtm): Read from `target_features` instead? + if raw_target.contains("threads") { + cmd.push_cc_arg("-pthread".into()); + } + } + + if target.os == "nto" { + // Select the target with `-V`, see qcc documentation: + // QNX 7.1: https://www.qnx.com/developers/docs/7.1/index.html#com.qnx.doc.neutrino.utilities/topic/q/qcc.html + // QNX 8.0: https://www.qnx.com/developers/docs/8.0/com.qnx.doc.neutrino.utilities/topic/q/qcc.html + // This assumes qcc/q++ as compiler, which is currently the only supported compiler for QNX. + // See for details: https://github.com/rust-lang/cc-rs/pull/1319 + let arg = match target.full_arch { + "x86" | "i586" => "-Vgcc_ntox86_cxx", + "aarch64" => "-Vgcc_ntoaarch64le_cxx", + "x86_64" => "-Vgcc_ntox86_64_cxx", + _ => { + return Err(Error::new( + ErrorKind::InvalidTarget, + format!("Unknown architecture for Neutrino QNX: {}", target.arch), + )) + } + }; + cmd.push_cc_arg(arg.into()); + } + } + } + + if self.get_debug() { + if self.cuda { + // NVCC debug flag + cmd.args.push("-G".into()); + } + let family = cmd.family; + family.add_debug_flags( + cmd, + self.get_debug_str().as_deref().unwrap_or_default(), + self.get_dwarf_version(), + ); + } + + if self.get_force_frame_pointer() { + let family = cmd.family; + family.add_force_frame_pointer(cmd); + } + + if !cmd.is_like_msvc() { + if target.arch == "x86" { + cmd.args.push("-m32".into()); + } else if target.abi == "x32" { + cmd.args.push("-mx32".into()); + } else if target.os == "aix" { + if cmd.family == ToolFamily::Gnu { + cmd.args.push("-maix64".into()); + } else { + cmd.args.push("-m64".into()); + } + } else if target.arch == "x86_64" || target.arch == "powerpc64" { + cmd.args.push("-m64".into()); + } + } + + // Target flags + match cmd.family { + ToolFamily::Clang { .. } => { + if !(cmd.has_internal_target_arg + || (target.os == "android" + && android_clang_compiler_uses_target_arg_internally(&cmd.path))) + { + if target.os == "freebsd" { + // FreeBSD only supports C++11 and above when compiling against libc++ + // (available from FreeBSD 10 onwards). Under FreeBSD, clang uses libc++ by + // default on FreeBSD 10 and newer unless `--target` is manually passed to + // the compiler, in which case its default behavior differs: + // * If --target=xxx-unknown-freebsdX(.Y) is specified and X is greater than + // or equal to 10, clang++ uses libc++ + // * If --target=xxx-unknown-freebsd is specified (without a version), + // clang++ cannot assume libc++ is available and reverts to a default of + // libstdc++ (this behavior was changed in llvm 14). + // + // This breaks C++11 (or greater) builds if targeting FreeBSD with the + // generic xxx-unknown-freebsd target on clang 13 or below *without* + // explicitly specifying that libc++ should be used. + // When cross-compiling, we can't infer from the rust/cargo target name + // which major version of FreeBSD we are targeting, so we need to make sure + // that libc++ is used (unless the user has explicitly specified otherwise). + // There's no compelling reason to use a different approach when compiling + // natively. + if self.cpp && self.cpp_set_stdlib.is_none() { + cmd.push_cc_arg("-stdlib=libc++".into()); + } + } else if target.arch == "wasm32" && target.os == "linux" { + for x in &[ + "atomics", + "bulk-memory", + "mutable-globals", + "sign-ext", + "exception-handling", + ] { + cmd.push_cc_arg(format!("-m{x}").into()); + } + for x in &["wasm-exceptions", "declspec"] { + cmd.push_cc_arg(format!("-f{x}").into()); + } + let musl_sysroot = self.wasm_musl_sysroot().unwrap(); + cmd.push_cc_arg( + format!("--sysroot={}", Path::new(&musl_sysroot).display()).into(), + ); + cmd.push_cc_arg("-pthread".into()); + } + // Pass `--target` with the LLVM target to configure Clang for cross-compiling. + // + // This is **required** for cross-compilation, as it's the only flag that + // consistently forces Clang to change the "toolchain" that is responsible for + // parsing target-specific flags: + // https://github.com/rust-lang/cc-rs/issues/1388 + // https://github.com/llvm/llvm-project/blob/llvmorg-19.1.7/clang/lib/Driver/Driver.cpp#L1359-L1360 + // https://github.com/llvm/llvm-project/blob/llvmorg-19.1.7/clang/lib/Driver/Driver.cpp#L6347-L6532 + // + // This can be confusing, because on e.g. host macOS, you can usually get by + // with `-arch` and `-mtargetos=`. But that only works because the _default_ + // toolchain is `Darwin`, which enables parsing of darwin-specific options. + // + // NOTE: In the past, we passed the deployment version in here on all Apple + // targets, but versioned targets were found to have poor compatibility with + // older versions of Clang, especially when it comes to configuration files: + // https://github.com/rust-lang/cc-rs/issues/1278 + // + // So instead, we pass the deployment target with `-m*-version-min=`, and only + // pass it here on visionOS and Mac Catalyst where that option does not exist: + // https://github.com/rust-lang/cc-rs/issues/1383 + let version = if target.os == "visionos" || target.env == "macabi" { + Some(self.apple_deployment_target(target)) + } else { + None + }; + + let clang_target = + target.llvm_target(&self.get_raw_target()?, version.as_deref()); + cmd.push_cc_arg(format!("--target={clang_target}").into()); + } + } + ToolFamily::Msvc { clang_cl } => { + // This is an undocumented flag from MSVC but helps with making + // builds more reproducible by avoiding putting timestamps into + // files. + cmd.push_cc_arg("-Brepro".into()); + + if clang_cl { + cmd.push_cc_arg( + format!( + "--target={}", + target.llvm_target(&self.get_raw_target()?, None) + ) + .into(), + ); + + if target.arch == "x86" { + // See + // . + // + // NOTE: Rust officially supported Windows targets all require SSE2 as part + // of baseline target features. + // + // NOTE: The same applies for STL. See: - + // , and - + // . + cmd.push_cc_arg("-arch:SSE2".into()); + } + } else if target.full_arch == "i586" { + cmd.push_cc_arg("-arch:IA32".into()); + } else if target.full_arch == "arm64ec" { + cmd.push_cc_arg("-arm64EC".into()); + } + // There is a check in corecrt.h that will generate a + // compilation error if + // _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE is + // not defined to 1. The check was added in Windows + // 8 days because only store apps were allowed on ARM. + // This changed with the release of Windows 10 IoT Core. + // The check will be going away in future versions of + // the SDK, but for all released versions of the + // Windows SDK it is required. + if target.arch == "arm" { + cmd.args + .push("-D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE=1".into()); + } + } + ToolFamily::Gnu => { + if target.vendor == "kmc" { + cmd.args.push("-finput-charset=utf-8".into()); + } + + if self.static_flag.is_none() { + let features = cargo_env_var_os("CARGO_CFG_TARGET_FEATURE"); + let features = features.as_deref().unwrap_or_default(); + if features.to_string_lossy().contains("crt-static") { + cmd.args.push("-static".into()); + } + } + + // armv7 targets get to use armv7 instructions + if (target.full_arch.starts_with("armv7") + || target.full_arch.starts_with("thumbv7")) + && (target.os == "linux" || target.vendor == "kmc") + { + cmd.args.push("-march=armv7-a".into()); + + if target.abi == "eabihf" { + // lowest common denominator FPU + cmd.args.push("-mfpu=vfpv3-d16".into()); + cmd.args.push("-mfloat-abi=hard".into()); + } + } + + // (x86 Android doesn't say "eabi") + if target.os == "android" && target.full_arch.contains("v7") { + cmd.args.push("-march=armv7-a".into()); + cmd.args.push("-mthumb".into()); + if !target.full_arch.contains("neon") { + // On android we can guarantee some extra float instructions + // (specified in the android spec online) + // NEON guarantees even more; see below. + cmd.args.push("-mfpu=vfpv3-d16".into()); + } + cmd.args.push("-mfloat-abi=softfp".into()); + } + + if target.full_arch.contains("neon") { + cmd.args.push("-mfpu=neon-vfpv4".into()); + } + + if target.full_arch == "armv4t" && target.os == "linux" { + cmd.args.push("-march=armv4t".into()); + cmd.args.push("-marm".into()); + cmd.args.push("-mfloat-abi=soft".into()); + } + + if target.full_arch == "armv5te" && target.os == "linux" { + cmd.args.push("-march=armv5te".into()); + cmd.args.push("-marm".into()); + cmd.args.push("-mfloat-abi=soft".into()); + } + + // For us arm == armv6 by default + if target.full_arch == "arm" && target.os == "linux" { + cmd.args.push("-march=armv6".into()); + cmd.args.push("-marm".into()); + if target.abi == "eabihf" { + cmd.args.push("-mfpu=vfp".into()); + } else { + cmd.args.push("-mfloat-abi=soft".into()); + } + } + + // Turn codegen down on i586 to avoid some instructions. + if target.full_arch == "i586" && target.os == "linux" { + cmd.args.push("-march=pentium".into()); + } + + // Set codegen level for i686 correctly + if target.full_arch == "i686" && target.os == "linux" { + cmd.args.push("-march=i686".into()); + } + + // Looks like `musl-gcc` makes it hard for `-m32` to make its way + // all the way to the linker, so we need to actually instruct the + // linker that we're generating 32-bit executables as well. This'll + // typically only be used for build scripts which transitively use + // these flags that try to compile executables. + if target.arch == "x86" && target.env == "musl" { + cmd.args.push("-Wl,-melf_i386".into()); + } + + // + // Arm Target Details + // + + // Set Float ABI for all Arm bare-metal targets using EABIHF + if target.arch == "arm" && target.os == "none" && target.abi == "eabihf" { + cmd.args.push("-mfloat-abi=hard".into()) + } + // Set -mthumb for all Thumb targets + if target.full_arch.starts_with("thumb") { + cmd.args.push("-mthumb".into()); + } + // Armv6-M targets (no FPU available) + if target.full_arch.starts_with("thumbv6m") { + // ARMv6S-M is an old name for "ARMv6-M with SVC support" + // before SVC support became mandatory. Some versions of GAS care + // about the difference. + cmd.args.push("-march=armv6s-m".into()); + } + // Armv7-M targets (no FPU available) + if target.full_arch.starts_with("thumbv7m") { + cmd.args.push("-march=armv7-m".into()); + } + // Armv7E-M targets + if target.full_arch.starts_with("thumbv7em") { + cmd.args.push("-march=armv7e-m".into()); + if target.abi == "eabihf" { + cmd.args.push("-mfpu=fpv4-sp-d16".into()) + } + } + // Armv8-M Baseline (no FPU available) + if target.full_arch.starts_with("thumbv8m.base") { + cmd.args.push("-march=armv8-m.base".into()); + } + // Armv8-M Mainline targets + if target.full_arch.starts_with("thumbv8m.main") { + cmd.args.push("-march=armv8-m.main".into()); + if target.abi == "eabihf" { + cmd.args.push("-mfpu=fpv5-sp-d16".into()) + } + } + // ARMv6 targets + if target.full_arch.starts_with("armv6") + || (target.full_arch.starts_with("thumbv6") + && !target.full_arch.starts_with("thumbv6m")) + { + cmd.args.push("-march=armv6".into()); + if target.abi == "eabihf" { + // lowest common denominator FPU + cmd.args.push("-mfpu=vfpv2".into()); + } + } + // ARMv7-R targets + if target.full_arch.starts_with("armebv7r") + || target.full_arch.starts_with("armv7r") + || target.full_arch.starts_with("thumbv7r") + { + if target.full_arch.starts_with("armeb") { + cmd.args.push("-mbig-endian".into()); + } + cmd.args.push("-march=armv7-r".into()); + if target.abi == "eabihf" { + // lowest common denominator FPU + // (see Cortex-R4 technical reference manual) + cmd.args.push("-mfpu=vfpv3-d16".into()) + } + } + // Armv7-A targets + if target.full_arch.starts_with("armv7a") + || target.full_arch.starts_with("thumbv7a") + { + cmd.args.push("-march=armv7-a".into()); + if target.abi == "eabihf" { + // lowest common denominator FPU + cmd.args.push("-mfpu=vfpv3-d16".into()); + } + } + // Armv8-R targets + if target.full_arch.starts_with("armv8r") + || target.full_arch.starts_with("thumbv8r") + { + cmd.args.push("-march=armv8-r".into()); + if target.abi == "eabihf" { + cmd.args.push("-mfpu=fp-armv8".into()) + } + } + + if target.arch == "riscv32" || target.arch == "riscv64" { + // get the 32i/32imac/32imc/64gc/64imac/... part + let arch = &target.full_arch[5..]; + if arch.starts_with("64") { + if matches!(target.os, "linux" | "freebsd" | "netbsd") { + cmd.args.push(("-march=rv64gc").into()); + cmd.args.push("-mabi=lp64d".into()); + } else { + cmd.args.push(("-march=rv".to_owned() + arch).into()); + cmd.args.push("-mabi=lp64".into()); + } + } else if arch.starts_with("32") { + if target.os == "linux" { + cmd.args.push(("-march=rv32gc").into()); + cmd.args.push("-mabi=ilp32d".into()); + } else { + cmd.args.push(("-march=rv".to_owned() + arch).into()); + cmd.args.push("-mabi=ilp32".into()); + } + } else { + cmd.args.push("-mcmodel=medany".into()); + } + } + } + } + + if raw_target == "wasm32v1-none" { + // `wasm32v1-none` target only exists in `rustc`, so we need to change the compilation flags: + // https://doc.rust-lang.org/rustc/platform-support/wasm32v1-none.html + cmd.push_cc_arg("-mcpu=mvp".into()); + cmd.push_cc_arg("-mmutable-globals".into()); + } + + if target.os == "solaris" || target.os == "illumos" { + // On Solaris and illumos, multi-threaded C programs must be built with `_REENTRANT` + // defined. This configures headers to define APIs appropriately for multi-threaded + // use. This is documented in threads(7), see also https://illumos.org/man/7/threads. + // + // If C code is compiled without multi-threading support but does use multiple threads, + // incorrect behavior may result. One extreme example is that on some systems the + // global errno may be at the same address as the process' first thread's errno; errno + // clobbering may occur to disastrous effect. Conversely, if _REENTRANT is defined + // while it is not actually needed, system headers may define some APIs suboptimally + // but will not result in incorrect behavior. Other code *should* be reasonable under + // such conditions. + // + // We're typically building C code to eventually link into a Rust program. Many Rust + // programs are multi-threaded in some form. So, set the flag by default. + cmd.args.push("-D_REENTRANT".into()); + } + + if target.vendor == "apple" { + self.apple_flags(cmd)?; + } + + if self.static_flag.unwrap_or(false) { + cmd.args.push("-static".into()); + } + if self.shared_flag.unwrap_or(false) { + cmd.args.push("-shared".into()); + } + + if self.cpp { + match (self.cpp_set_stdlib.as_ref(), cmd.family) { + (None, _) => {} + (Some(stdlib), ToolFamily::Gnu) | (Some(stdlib), ToolFamily::Clang { .. }) => { + cmd.push_cc_arg(format!("-stdlib=lib{stdlib}").into()); + } + _ => { + self.cargo_output.print_warning(&format_args!("cpp_set_stdlib is specified, but the {:?} compiler does not support this option, ignored", cmd.family)); + } + } + } + + Ok(()) + } + + fn add_inherited_rustflags( + &self, + cmd: &mut Tool, + target: &TargetInfo<'_>, + ) -> Result<(), Error> { + let env_os = match cargo_env_var_os("CARGO_ENCODED_RUSTFLAGS") { + Some(env) => env, + // No encoded RUSTFLAGS -> nothing to do + None => return Ok(()), + }; + + let env = env_os.to_string_lossy(); + let codegen_flags = RustcCodegenFlags::parse(&env)?; + codegen_flags.cc_flags(self, cmd, target); + Ok(()) + } + + fn msvc_macro_assembler(&self) -> Result { + let target = self.get_target()?; + let tool = match target.arch { + "x86_64" => "ml64.exe", + "arm" => "armasm.exe", + "aarch64" | "arm64ec" => "armasm64.exe", + _ => "ml.exe", + }; + let mut cmd = self + .find_msvc_tools_find(&target, tool) + .unwrap_or_else(|| self.cmd(tool)); + cmd.arg("-nologo"); // undocumented, yet working with armasm[64] + for directory in self.include_directories.iter() { + cmd.arg("-I").arg(&**directory); + } + if is_arm(&target) { + if self.get_debug() { + cmd.arg("-g"); + } + + if target.arch == "arm64ec" { + cmd.args(["-machine", "ARM64EC"]); + } + + for (key, value) in self.definitions.iter() { + cmd.arg("-PreDefine"); + if let Some(ref value) = *value { + if let Ok(i) = value.parse::() { + cmd.arg(format!("{key} SETA {i}")); + } else if value.starts_with('"') && value.ends_with('"') { + cmd.arg(format!("{key} SETS {value}")); + } else { + cmd.arg(format!("{key} SETS \"{value}\"")); + } + } else { + cmd.arg(format!("{} SETL {}", key, "{TRUE}")); + } + } + } else { + if self.get_debug() { + cmd.arg("-Zi"); + } + + for (key, value) in self.definitions.iter() { + if let Some(ref value) = *value { + cmd.arg(format!("-D{key}={value}")); + } else { + cmd.arg(format!("-D{key}")); + } + } + } + + if target.arch == "x86" { + cmd.arg("-safeseh"); + } + + Ok(cmd) + } + + fn assemble(&self, lib_name: &str, dst: &Path, objs: &[Object]) -> Result<(), Error> { + // Delete the destination if it exists as we want to + // create on the first iteration instead of appending. + let _ = fs::remove_file(dst); + + // Add objects to the archive in limited-length batches. This helps keep + // the length of the command line within a reasonable length to avoid + // blowing system limits on limiting platforms like Windows. + // + // Optimistically try the `D` (deterministic) ar modifier, which zeros + // out timestamps, UIDs, and GIDs. If the archiver doesn't support it, + // we remember and stop trying for subsequent batches. + // (`None` -> haven't probed yet) + let mut deterministic_ar: Option = None; + + let mut objs = objs + .iter() + .map(|o| o.dst.as_path()) + .chain(self.objects.iter().map(std::ops::Deref::deref)) + .peekable(); + let mut batch = Vec::new(); + while objs.peek().is_some() { + let mut remaining_len = 4000; + while let Some(path) = + objs.next_if(|peek| batch.is_empty() || peek.as_os_str().len() <= remaining_len) + { + batch.push(path); + remaining_len = remaining_len.saturating_sub(path.as_os_str().len()); + } + self.assemble_progressive(dst, &batch, &mut deterministic_ar)?; + batch.clear(); + } + + if self.cuda && self.cuda_file_count() > 0 { + // Link the device-side code and add it to the target library, + // so that non-CUDA linker can link the final binary. + + let out_dir = self.get_out_dir()?; + let dlink = out_dir.join(lib_name.to_owned() + "_dlink.o"); + let mut nvcc = self.get_compiler().to_command(); + nvcc.arg("--device-link").arg("-o").arg(&dlink).arg(dst); + run(&mut nvcc, &self.cargo_output)?; + self.assemble_progressive(dst, &[dlink.as_path()], &mut deterministic_ar)?; + } + + let target = self.get_target()?; + if target.env == "msvc" { + // The Rust compiler will look for libfoo.a and foo.lib, but the + // MSVC linker will also be passed foo.lib, so be sure that both + // exist for now. + + let lib_dst = dst.with_file_name(format!("{lib_name}.lib")); + let _ = fs::remove_file(&lib_dst); + match fs::hard_link(dst, &lib_dst).or_else(|_| { + // if hard-link fails, just copy (ignoring the number of bytes written) + fs::copy(dst, &lib_dst).map(|_| ()) + }) { + Ok(_) => (), + Err(_) => { + return Err(Error::new( + ErrorKind::IOError, + "Could not copy or create a hard-link to the generated lib file.", + )); + } + }; + } else { + // Non-msvc targets (those using `ar`) need a separate step to add + // the symbol table to archives since our construction command of + // `cq` doesn't add it for us. + let mut ar = self.try_get_archiver()?; + // NOTE: We add `s` even if flags were passed using $ARFLAGS/ar_flag, because `s` + // here represents a _mode_, not an arbitrary flag. Further discussion of this choice + // can be seen in https://github.com/rust-lang/cc-rs/pull/763. + match deterministic_ar { + Some(false) => { + // See comment in `assemble_progressive` for more on ZERO_AR_DATE. + ar.env("ZERO_AR_DATE", "1"); + run(ar.arg("s").arg(dst), &self.cargo_output)?; + } + Some(true) => { + run(ar.arg("sD").arg(dst), &self.cargo_output)?; + } + None => { + if run_silent_on_error(ar.arg("sD").arg(dst), &self.cargo_output).is_err() { + let mut ar = self.try_get_archiver()?; + ar.env("ZERO_AR_DATE", "1"); + run(ar.arg("s").arg(dst), &self.cargo_output)?; + } + } + } + } + + Ok(()) + } + + fn assemble_progressive( + &self, + dst: &Path, + objs: &[&Path], + deterministic_ar: &mut Option, + ) -> Result<(), Error> { + let target = self.get_target()?; + + let (mut cmd, program, any_flags) = self.try_get_archiver_and_flags()?; + if target.env == "msvc" && !program.to_string_lossy().contains("llvm-ar") { + // NOTE: -out: here is an I/O flag, and so must be included even if $ARFLAGS/ar_flag is + // in use. -nologo on the other hand is just a regular flag, and one that we'll skip if + // the caller has explicitly dictated the flags they want. See + // https://github.com/rust-lang/cc-rs/pull/763 for further discussion. + let mut out = OsString::from("-out:"); + out.push(dst); + cmd.arg(out); + if !any_flags { + cmd.arg("-nologo"); + } + // If the library file already exists, add the library name + // as an argument to let lib.exe know we are appending the objs. + if dst.exists() { + cmd.arg(dst); + } + cmd.args(objs); + run(&mut cmd, &self.cargo_output)?; + } else { + // Set an environment variable to tell the OSX archiver to ensure + // that all dates listed in the archive are zero, improving + // determinism of builds. AFAIK there's not really official + // documentation of this but there's a lot of references to it if + // you search google. + // + // You can reproduce this locally on a mac with: + // + // $ touch foo.c + // $ cc -c foo.c -o foo.o + // + // # Notice that these two checksums are different + // $ ar crus libfoo1.a foo.o && sleep 2 && ar crus libfoo2.a foo.o + // $ md5sum libfoo*.a + // + // # Notice that these two checksums are the same + // $ export ZERO_AR_DATE=1 + // $ ar crus libfoo1.a foo.o && sleep 2 && touch foo.o && ar crus libfoo2.a foo.o + // $ md5sum libfoo*.a + // + // In any case if this doesn't end up getting read, it shouldn't + // cause that many issues! + cmd.env("ZERO_AR_DATE", "1"); + + // NOTE: We add cq here regardless of whether $ARFLAGS/ar_flag have been used because + // it dictates the _mode_ ar runs in, which the setter of $ARFLAGS/ar_flag can't + // dictate. See https://github.com/rust-lang/cc-rs/pull/763 for further discussion. + match *deterministic_ar { + Some(false) => { + run(cmd.arg("cq").arg(dst).args(objs), &self.cargo_output)?; + } + Some(true) => { + run(cmd.arg("cqD").arg(dst).args(objs), &self.cargo_output)?; + } + None => { + // Probe: try `D` and remember the result for later batches. + if run_silent_on_error(cmd.arg("cqD").arg(dst).args(objs), &self.cargo_output) + .is_ok() + { + *deterministic_ar = Some(true); + } else { + *deterministic_ar = Some(false); + let (mut cmd, _, _) = self.try_get_archiver_and_flags()?; + cmd.env("ZERO_AR_DATE", "1"); + run(cmd.arg("cq").arg(dst).args(objs), &self.cargo_output)?; + } + } + } + } + + Ok(()) + } + + fn apple_flags(&self, cmd: &mut Tool) -> Result<(), Error> { + let target = self.get_target()?; + + // This is a Darwin/Apple-specific flag that works both on GCC and Clang, but it is only + // necessary on GCC since we specify `-target` on Clang. + // https://gcc.gnu.org/onlinedocs/gcc/Darwin-Options.html#:~:text=arch + // https://clang.llvm.org/docs/CommandGuide/clang.html#cmdoption-arch + if cmd.is_like_gnu() { + let arch = map_darwin_target_from_rust_to_compiler_architecture(&target); + cmd.args.push("-arch".into()); + cmd.args.push(arch.into()); + } + + // Pass the deployment target via `-mmacosx-version-min=`, `-miphoneos-version-min=` and + // similar. Also necessary on GCC, as it forces a compilation error if the compiler is not + // configured for Darwin: https://gcc.gnu.org/onlinedocs/gcc/Darwin-Options.html + // + // On visionOS and Mac Catalyst, there is no -m*-version-min= flag: + // https://github.com/llvm/llvm-project/issues/88271 + // And the workaround to use `-mtargetos=` cannot be used with the `--target` flag that we + // otherwise specify. So we avoid emitting that, and put the version in `--target` instead. + if cmd.is_like_gnu() || !(target.os == "visionos" || target.env == "macabi") { + let min_version = self.apple_deployment_target(&target); + cmd.args + .push(target.apple_version_flag(&min_version).into()); + } + + // AppleClang sometimes requires sysroot even on macOS + if cmd.is_xctoolchain_clang() || target.os != "macos" { + self.cargo_output.print_metadata(&format_args!( + "Detecting {:?} SDK path for {}", + target.os, + target.apple_sdk_name(), + )); + let sdk_path = self.apple_sdk_root(&target)?; + + cmd.args.push("-isysroot".into()); + cmd.args.push(OsStr::new(&sdk_path).to_owned()); + cmd.env + .push(("SDKROOT".into(), OsStr::new(&sdk_path).to_owned())); + + if target.env == "macabi" { + // Mac Catalyst uses the macOS SDK, but to compile against and + // link to iOS-specific frameworks, we should have the support + // library stubs in the include and library search path. + let ios_support = Path::new(&sdk_path).join("System/iOSSupport"); + + cmd.args.extend([ + // Header search path + OsString::from("-isystem"), + ios_support.join("usr/include").into(), + // Framework header search path + OsString::from("-iframework"), + ios_support.join("System/Library/Frameworks").into(), + // Library search path + { + let mut s = OsString::from("-L"); + s.push(ios_support.join("usr/lib")); + s + }, + // Framework linker search path + { + // Technically, we _could_ avoid emitting `-F`, as + // `-iframework` implies it, but let's keep it in for + // clarity. + let mut s = OsString::from("-F"); + s.push(ios_support.join("System/Library/Frameworks")); + s + }, + ]); + } + } + + Ok(()) + } + + fn cmd>(&self, prog: P) -> Command { + let mut cmd = Command::new(prog); + for (a, b) in self.env.iter() { + cmd.env(a, b); + } + cmd + } + + fn prefer_clang(&self) -> bool { + if let Some(env) = cargo_env_var_os("CARGO_ENCODED_RUSTFLAGS") { + env.to_string_lossy().contains("linker-plugin-lto") + } else { + false + } + } + + fn get_base_compiler(&self) -> Result { + let out_dir = self.get_out_dir().ok(); + let out_dir = out_dir.as_deref(); + + if let Some(c) = &self.compiler { + return Ok(Tool::new( + (**c).to_owned(), + &self.build_cache.cached_compiler_family, + &self.cargo_output, + out_dir, + )); + } + let target = self.get_target()?; + let raw_target = self.get_raw_target()?; + + let msvc = if self.prefer_clang_cl_over_msvc { + "clang-cl.exe" + } else { + "cl.exe" + }; + + let (env, gnu, traditional, clang) = if self.cpp { + ("CXX", "g++", "c++", "clang++") + } else { + ("CC", "gcc", "cc", "clang") + }; + + let fallback = Cow::Borrowed(Path::new(traditional)); + let default = if cfg!(target_os = "solaris") || cfg!(target_os = "illumos") { + // On historical Solaris systems, "cc" may have been Sun Studio, which + // is not flag-compatible with "gcc". This history casts a long shadow, + // and many modern illumos distributions today ship GCC as "gcc" without + // also making it available as "cc". + Cow::Borrowed(Path::new(gnu)) + } else if self.prefer_clang() { + self.which(Path::new(clang), None) + .map(Cow::Owned) + .unwrap_or(fallback) + } else { + fallback + }; + + let cl_exe = self.find_msvc_tools_find_tool(&target, msvc); + + let tool_opt: Option = self + .env_tool(env) + .map(|(tool, wrapper, args)| { + // Chop off leading/trailing whitespace to work around + // semi-buggy build scripts which are shared in + // makefiles/configure scripts (where spaces are far more + // lenient) + let mut t = Tool::with_args( + tool, + args.clone(), + &self.build_cache.cached_compiler_family, + &self.cargo_output, + out_dir, + ); + if let Some(cc_wrapper) = wrapper { + t.cc_wrapper_path = Some(Path::new(&cc_wrapper).to_owned()); + } + for arg in args { + t.cc_wrapper_args.push(arg.into()); + } + t + }) + .or_else(|| { + if target.os == "emscripten" { + let tool = if self.cpp { "em++" } else { "emcc" }; + // Windows uses bat file so we have to be a bit more specific + if cfg!(windows) { + let mut t = Tool::with_family( + PathBuf::from("cmd"), + ToolFamily::Clang { zig_cc: false }, + ); + t.args.push("/c".into()); + t.args.push(format!("{tool}.bat").into()); + Some(t) + } else { + Some(Tool::new( + PathBuf::from(tool), + &self.build_cache.cached_compiler_family, + &self.cargo_output, + out_dir, + )) + } + } else { + None + } + }) + .or_else(|| cl_exe.clone()); + + let tool = match tool_opt { + Some(t) => t, + None => { + let compiler: PathBuf = if cfg!(windows) && target.os == "windows" { + if target.env == "msvc" { + msvc.into() + } else { + let cc = if target.abi == "llvm" { clang } else { gnu }; + format!("{cc}.exe").into() + } + } else if target.os == "ios" + || target.os == "watchos" + || target.os == "tvos" + || target.os == "visionos" + { + clang.into() + } else if target.os == "android" { + autodetect_android_compiler(&raw_target, gnu, clang) + } else if target.os == "cloudabi" { + format!( + "{}-{}-{}-{}", + target.full_arch, target.vendor, target.os, traditional + ) + .into() + } else if target.os == "wasi" { + self.autodetect_wasi_compiler(&raw_target, clang) + } else if target.arch == "wasm32" || target.arch == "wasm64" { + // Compiling WASM is not currently supported by GCC, so + // let's default to Clang. + clang.into() + } else if target.os == "vxworks" { + if self.cpp { "wr-c++" } else { "wr-cc" }.into() + } else if target.arch == "arm" && target.vendor == "kmc" { + format!("arm-kmc-eabi-{gnu}").into() + } else if target.arch == "aarch64" && target.vendor == "kmc" { + format!("aarch64-kmc-elf-{gnu}").into() + } else if target.os == "nto" { + // See for details: https://github.com/rust-lang/cc-rs/pull/1319 + if self.cpp { "q++" } else { "qcc" }.into() + } else if self.get_is_cross_compile()? { + let prefix = self.prefix_for_target(&raw_target); + match prefix { + Some(prefix) => { + let cc = if target.abi == "llvm" { clang } else { gnu }; + format!("{prefix}-{cc}").into() + } + None => default.into(), + } + } else { + default.into() + }; + + let mut t = Tool::new( + compiler, + &self.build_cache.cached_compiler_family, + &self.cargo_output, + out_dir, + ); + if let Some(cc_wrapper) = self.rustc_wrapper_fallback() { + t.cc_wrapper_path = Some(Path::new(&cc_wrapper).to_owned()); + } + t + } + }; + + let mut tool = if self.cuda { + assert!( + tool.args.is_empty(), + "CUDA compilation currently assumes empty pre-existing args" + ); + let nvcc = match self.getenv_with_target_prefixes("NVCC") { + Err(_) => PathBuf::from("nvcc"), + Ok(nvcc) => PathBuf::from(&*nvcc), + }; + let mut nvcc_tool = Tool::with_features( + nvcc, + vec![], + self.cuda, + &self.build_cache.cached_compiler_family, + &self.cargo_output, + out_dir, + ); + if self.ccbin { + nvcc_tool + .args + .push(format!("-ccbin={}", tool.path.display()).into()); + } + if let Some(cc_wrapper) = self.rustc_wrapper_fallback() { + nvcc_tool.cc_wrapper_path = Some(Path::new(&cc_wrapper).to_owned()); + } + nvcc_tool.family = tool.family; + nvcc_tool + } else { + tool + }; + + // New "standalone" C/C++ cross-compiler executables from recent Android NDK + // are just shell scripts that call main clang binary (from Android NDK) with + // proper `--target` argument. + // + // For example, armv7a-linux-androideabi16-clang passes + // `--target=armv7a-linux-androideabi16` to clang. + // + // As the shell script calls the main clang binary, the command line limit length + // on Windows is restricted to around 8k characters instead of around 32k characters. + // To remove this limit, we call the main clang binary directly and construct the + // `--target=` ourselves. + if cfg!(windows) && android_clang_compiler_uses_target_arg_internally(&tool.path) { + if let Some(path) = tool.path.file_name() { + let file_name = path.to_str().unwrap().to_owned(); + let (target, clang) = file_name.split_at(file_name.rfind('-').unwrap()); + + tool.has_internal_target_arg = true; + tool.path.set_file_name(clang.trim_start_matches('-')); + tool.path.set_extension("exe"); + tool.args.push(format!("--target={target}").into()); + + // Additionally, shell scripts for target i686-linux-android versions 16 to 24 + // pass the `mstackrealign` option so we do that here as well. + if target.contains("i686-linux-android") { + let (_, version) = target.split_at(target.rfind('d').unwrap() + 1); + if let Ok(version) = version.parse::() { + if version > 15 && version < 25 { + tool.args.push("-mstackrealign".into()); + } + } + } + }; + } + + // Under cross-compilation scenarios, llvm-mingw's clang executable is just a + // wrapper script that calls the actual clang binary with a suitable `--target` + // argument, much like the Android NDK case outlined above. Passing a target + // argument ourselves in this case will result in an error, as they expect + // targets like `x86_64-w64-mingw32`, and we can't always set such a target + // string because it is specific to this MinGW cross-compilation toolchain. + // + // For example, the following command will always fail due to using an unsuitable + // `--target` argument we'd otherwise pass: + // $ /opt/llvm-mingw-20250613-ucrt-ubuntu-22.04-x86_64/bin/x86_64-w64-mingw32-clang --target=x86_64-pc-windows-gnu dummy.c + // + // Code reference: + // https://github.com/mstorsjo/llvm-mingw/blob/a1f6413e5c21fd74b64137b56167f4fba500d1d8/wrappers/clang-target-wrapper.sh#L31 + if !cfg!(windows) && target.os == "windows" && is_llvm_mingw_wrapper(&tool.path) { + tool.has_internal_target_arg = true; + } + + // If we found `cl.exe` in our environment, the tool we're returning is + // an MSVC-like tool, *and* no env vars were set then set env vars for + // the tool that we're returning. + // + // Env vars are needed for things like `link.exe` being put into PATH as + // well as header include paths sometimes. These paths are automatically + // included by default but if the `CC` or `CXX` env vars are set these + // won't be used. This'll ensure that when the env vars are used to + // configure for invocations like `clang-cl` we still get a "works out + // of the box" experience. + if let Some(cl_exe) = cl_exe { + if tool.family == (ToolFamily::Msvc { clang_cl: true }) + && tool.env.is_empty() + && target.env == "msvc" + { + for (k, v) in cl_exe.env.iter() { + tool.env.push((k.to_owned(), v.to_owned())); + } + } + } + + if target.env == "msvc" && tool.family == ToolFamily::Gnu { + self.cargo_output + .print_warning(&"GNU compiler is not supported for this target"); + } + + Ok(tool) + } + + /// Returns a fallback `cc_compiler_wrapper` by introspecting `RUSTC_WRAPPER` + fn rustc_wrapper_fallback(&self) -> Option> { + // No explicit CC wrapper was detected, but check if RUSTC_WRAPPER + // is defined and is a build accelerator that is compatible with + // C/C++ compilers (e.g. sccache) + const VALID_WRAPPERS: &[&str] = &["sccache", "cachepot", "buildcache"]; + + let rustc_wrapper = cargo_env_var_os("RUSTC_WRAPPER")?; + let wrapper_path = Path::new(&rustc_wrapper); + let wrapper_stem = wrapper_path.file_stem()?; + + if VALID_WRAPPERS.contains(&wrapper_stem.to_str()?) { + Some(Cow::Owned(rustc_wrapper)) + } else { + None + } + } + + /// Returns compiler path, optional modifier name from whitelist, and arguments vec + fn env_tool(&self, name: &str) -> Option<(PathBuf, Option>, Vec)> { + let tool = self.getenv_with_target_prefixes(name).ok()?; + let tool = tool.to_string_lossy(); + let tool = tool.trim(); + + if tool.is_empty() { + return None; + } + + // If this is an exact path on the filesystem we don't want to do any + // interpretation at all, just pass it on through. This'll hopefully get + // us to support spaces-in-paths. + if let Some(exe) = check_exe(Path::new(tool).into()) { + return Some((exe, self.rustc_wrapper_fallback(), Vec::new())); + } + + // Ok now we want to handle a couple of scenarios. We'll assume from + // here on out that spaces are splitting separate arguments. Two major + // features we want to support are: + // + // CC='sccache cc' + // + // aka using `sccache` or any other wrapper/caching-like-thing for + // compilations. We want to know what the actual compiler is still, + // though, because our `Tool` API support introspection of it to see + // what compiler is in use. + // + // additionally we want to support + // + // CC='cc -flag' + // + // where the CC env var is used to also pass default flags to the C + // compiler. + // + // It's true that everything here is a bit of a pain, but apparently if + // you're not literally make or bash then you get a lot of bug reports. + let mut known_wrappers = vec![ + "ccache", + "distcc", + "sccache", + "icecc", + "cachepot", + "buildcache", + ]; + let custom_wrapper = self.get_env("CC_KNOWN_WRAPPER_CUSTOM"); + if custom_wrapper.is_some() { + known_wrappers.push(custom_wrapper.as_deref().unwrap().to_str().unwrap()); + } + + let mut parts = tool.split_whitespace(); + let maybe_wrapper = parts.next()?; + + let file_stem = Path::new(maybe_wrapper).file_stem()?.to_str()?; + if known_wrappers.contains(&file_stem) { + if let Some(compiler) = parts.next() { + return Some(( + compiler.into(), + Some(Cow::Owned(maybe_wrapper.into())), + parts.map(|s| s.to_string()).collect(), + )); + } + } + + Some(( + maybe_wrapper.into(), + self.rustc_wrapper_fallback(), + parts.map(|s| s.to_string()).collect(), + )) + } + + /// Returns the C++ standard library: + /// 1. If [`cpp_link_stdlib`](cc::Build::cpp_link_stdlib) is set, uses its value. + /// 2. Else if the `CXXSTDLIB` environment variable is set, uses its value. + /// 3. Else the default is `c++` for OS X and BSDs, `c++_shared` for Android, + /// `None` for MSVC and `stdc++` for anything else. + fn get_cpp_link_stdlib(&self) -> Result>, Error> { + match &self.cpp_link_stdlib { + Some(s) => Ok(s.as_deref().map(Path::new).map(Cow::Borrowed)), + None => { + if let Ok(stdlib) = self.getenv_with_target_prefixes("CXXSTDLIB") { + if stdlib.is_empty() { + Ok(None) + } else { + Ok(Some(Cow::Owned(Path::new(&stdlib).to_owned()))) + } + } else { + let target = self.get_target()?; + if target.env == "msvc" { + Ok(None) + } else if target.vendor == "apple" + || target.os == "freebsd" + || target.os == "openbsd" + || target.os == "aix" + || (target.os == "linux" && target.env == "ohos") + || target.os == "wasi" + { + Ok(Some(Cow::Borrowed(Path::new("c++")))) + } else if target.os == "android" { + Ok(Some(Cow::Borrowed(Path::new("c++_shared")))) + } else { + Ok(Some(Cow::Borrowed(Path::new("stdc++")))) + } + } + } + } + } + + /// Get the archiver (ar) that's in use for this configuration. + /// + /// You can use [`Command::get_program`] to get just the path to the command. + /// + /// This method will take into account all configuration such as debug + /// information, optimization level, include directories, defines, etc. + /// Additionally, the compiler binary in use follows the standard + /// conventions for this path, e.g. looking at the explicitly set compiler, + /// environment variables (a number of which are inspected here), and then + /// falling back to the default configuration. + /// + /// # Panics + /// + /// Panics if an error occurred while determining the architecture. + pub fn get_archiver(&self) -> Command { + match self.try_get_archiver() { + Ok(tool) => tool, + Err(e) => fail(&e.message), + } + } + + /// Get the archiver that's in use for this configuration. + /// + /// This will return a result instead of panicking; + /// see [`Self::get_archiver`] for the complete description. + pub fn try_get_archiver(&self) -> Result { + Ok(self.try_get_archiver_and_flags()?.0) + } + + fn try_get_archiver_and_flags(&self) -> Result<(Command, PathBuf, bool), Error> { + let (mut cmd, name) = self.get_base_archiver()?; + let mut any_flags = false; + if let Some(flags) = self.envflags("ARFLAGS")? { + any_flags = true; + cmd.args(flags); + } + for flag in &self.ar_flags { + any_flags = true; + cmd.arg(&**flag); + } + Ok((cmd, name, any_flags)) + } + + fn get_base_archiver(&self) -> Result<(Command, PathBuf), Error> { + if let Some(ref a) = self.archiver { + let archiver = &**a; + return Ok((self.cmd(archiver), archiver.into())); + } + + self.get_base_archiver_variant("AR", "ar") + } + + /// Get the ranlib that's in use for this configuration. + /// + /// You can use [`Command::get_program`] to get just the path to the command. + /// + /// This method will take into account all configuration such as debug + /// information, optimization level, include directories, defines, etc. + /// Additionally, the compiler binary in use follows the standard + /// conventions for this path, e.g. looking at the explicitly set compiler, + /// environment variables (a number of which are inspected here), and then + /// falling back to the default configuration. + /// + /// # Panics + /// + /// Panics if an error occurred while determining the architecture. + pub fn get_ranlib(&self) -> Command { + match self.try_get_ranlib() { + Ok(tool) => tool, + Err(e) => fail(&e.message), + } + } + + /// Get the ranlib that's in use for this configuration. + /// + /// This will return a result instead of panicking; + /// see [`Self::get_ranlib`] for the complete description. + pub fn try_get_ranlib(&self) -> Result { + let mut cmd = self.get_base_ranlib()?; + if let Some(flags) = self.envflags("RANLIBFLAGS")? { + cmd.args(flags); + } + Ok(cmd) + } + + fn get_base_ranlib(&self) -> Result { + if let Some(ref r) = self.ranlib { + return Ok(self.cmd(&**r)); + } + + Ok(self.get_base_archiver_variant("RANLIB", "ranlib")?.0) + } + + fn get_base_archiver_variant( + &self, + env: &str, + tool: &str, + ) -> Result<(Command, PathBuf), Error> { + let target = self.get_target()?; + let mut name = PathBuf::new(); + let tool_opt: Option = self + .env_tool(env) + .map(|(tool, _wrapper, args)| { + name.clone_from(&tool); + let mut cmd = self.cmd(tool); + cmd.args(args); + cmd + }) + .or_else(|| { + if target.os == "emscripten" { + // Windows use bat files so we have to be a bit more specific + if cfg!(windows) { + let mut cmd = self.cmd("cmd"); + name = format!("em{tool}.bat").into(); + cmd.arg("/c").arg(&name); + Some(cmd) + } else { + name = format!("em{tool}").into(); + Some(self.cmd(&name)) + } + } else if target.arch == "wasm32" || target.arch == "wasm64" { + // Formally speaking one should be able to use this approach, + // parsing -print-search-dirs output, to cover all clang targets, + // including Android SDKs and other cross-compilation scenarios... + // And even extend it to gcc targets by searching for "ar" instead + // of "llvm-ar"... + let compiler = self.get_base_compiler().ok()?; + if compiler.is_like_clang() { + name = format!("llvm-{tool}").into(); + self.search_programs(&compiler.path, &name, &self.cargo_output) + .map(|name| self.cmd(name)) + } else { + None + } + } else { + None + } + }); + + let tool = match tool_opt { + Some(t) => t, + None => { + if target.os == "android" { + name = format!("llvm-{tool}").into(); + match Command::new(&name).arg("--version").status() { + Ok(status) if status.success() => (), + _ => { + // FIXME: Use parsed target. + let raw_target = self.get_raw_target()?; + name = format!("{}-{}", raw_target.replace("armv7", "arm"), tool).into() + } + } + self.cmd(&name) + } else if target.env == "msvc" { + // NOTE: There isn't really a ranlib on msvc, so arguably we should return + // `None` somehow here. But in general, callers will already have to be aware + // of not running ranlib on Windows anyway, so it feels okay to return lib.exe + // here. + + let compiler = self.get_base_compiler()?; + let lib = if compiler.family == (ToolFamily::Msvc { clang_cl: true }) { + self.search_programs( + &compiler.path, + Path::new("llvm-lib"), + &self.cargo_output, + ) + .or_else(|| { + // See if there is 'llvm-lib' next to 'clang-cl' + if let Some(mut cmd) = self.which(&compiler.path, None) { + cmd.pop(); + cmd.push("llvm-lib"); + self.which(&cmd, None) + } else { + None + } + }) + } else { + None + }; + + if let Some(lib) = lib { + name = lib; + self.cmd(&name) + } else { + name = PathBuf::from("lib.exe"); + let mut cmd = match self.find_msvc_tools_find(&target, "lib.exe") { + Some(t) => t, + None => self.cmd("lib.exe"), + }; + if target.full_arch == "arm64ec" { + cmd.arg("/machine:arm64ec"); + } + cmd + } + } else if target.os == "illumos" { + // The default 'ar' on illumos uses a non-standard flags, + // but the OS comes bundled with a GNU-compatible variant. + // + // Use the GNU-variant to match other Unix systems. + name = format!("g{tool}").into(); + self.cmd(&name) + } else if target.os == "vxworks" { + name = format!("wr-{tool}").into(); + self.cmd(&name) + } else if target.os == "nto" { + // Ref: https://www.qnx.com/developers/docs/8.0/com.qnx.doc.neutrino.utilities/topic/a/ar.html + name = match target.full_arch { + "i586" => format!("ntox86-{tool}").into(), + "x86" | "aarch64" | "x86_64" => { + format!("nto{}-{}", target.arch, tool).into() + } + _ => { + return Err(Error::new( + ErrorKind::InvalidTarget, + format!("Unknown architecture for Neutrino QNX: {}", target.arch), + )) + } + }; + self.cmd(&name) + } else if self.get_is_cross_compile()? { + match self.prefix_for_target(&self.get_raw_target()?) { + Some(prefix) => { + // GCC uses $target-gcc-ar, whereas binutils uses $target-ar -- try both. + // Prefer -ar if it exists, as builds of `-gcc-ar` have been observed to be + // outright broken (such as when targeting freebsd with `--disable-lto` + // toolchain where the archiver attempts to load the LTO plugin anyway but + // fails to find one). + // + // The same applies to ranlib. + let chosen = ["", "-gcc"] + .iter() + .filter_map(|infix| { + let target_p = format!("{prefix}{infix}-{tool}"); + let status = Command::new(&target_p) + .arg("--version") + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status() + .ok()?; + status.success().then_some(target_p) + }) + .next() + .unwrap_or_else(|| tool.to_string()); + name = chosen.into(); + self.cmd(&name) + } + None => { + name = tool.into(); + self.cmd(&name) + } + } + } else { + name = tool.into(); + self.cmd(&name) + } + } + }; + + Ok((tool, name)) + } + + // FIXME: Use parsed target instead of raw target. + fn prefix_for_target(&self, target: &str) -> Option> { + // CROSS_COMPILE is of the form: "arm-linux-gnueabi-" + self.get_env("CROSS_COMPILE") + .as_deref() + .map(|s| s.to_string_lossy().trim_end_matches('-').to_owned()) + .map(Cow::Owned) + .or_else(|| { + // Put aside RUSTC_LINKER's prefix to be used as second choice, after CROSS_COMPILE + cargo_env_var_os("RUSTC_LINKER").and_then(|var| { + var.to_string_lossy() + .strip_suffix("-gcc") + .map(str::to_string) + .map(Cow::Owned) + }) + }) + .or_else(|| { + match target { + // Note: there is no `aarch64-pc-windows-gnu` target, only `-gnullvm` + "aarch64-pc-windows-gnullvm" => Some("aarch64-w64-mingw32"), + "aarch64-uwp-windows-gnu" => Some("aarch64-w64-mingw32"), + "aarch64-unknown-helenos" => Some("aarch64-helenos"), + "aarch64-unknown-linux-gnu" => Some("aarch64-linux-gnu"), + "aarch64-unknown-linux-musl" => Some("aarch64-linux-musl"), + "aarch64-unknown-linux-relibc" => Some("aarch64-linux-relibc"), + "aarch64-unknown-netbsd" => Some("aarch64--netbsd"), + "arm-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"), + "armv4t-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"), + "armv5te-unknown-helenos-eabi" => Some("arm-helenos"), + "armv5te-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"), + "armv5te-unknown-linux-musleabi" => Some("arm-linux-gnueabi"), + "arm-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), + "arm-unknown-linux-musleabi" => Some("arm-linux-musleabi"), + "arm-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), + "arm-unknown-netbsd-eabi" => Some("arm--netbsdelf-eabi"), + "armv6-unknown-netbsd-eabihf" => Some("armv6--netbsdelf-eabihf"), + "armv7-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"), + "armv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), + "armv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), + "armv7neon-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), + "armv7neon-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), + "thumbv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), + "thumbv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), + "thumbv7neon-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"), + "thumbv7neon-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"), + "armv7-unknown-netbsd-eabihf" => Some("armv7--netbsdelf-eabihf"), + "hexagon-unknown-linux-musl" => Some("hexagon-linux-musl"), + "i586-unknown-linux-musl" => Some("musl"), + "i686-pc-windows-gnu" => Some("i686-w64-mingw32"), + "i686-pc-windows-gnullvm" => Some("i686-w64-mingw32"), + "i686-uwp-windows-gnu" => Some("i686-w64-mingw32"), + "i686-unknown-helenos" => Some("i686-helenos"), + "i686-unknown-linux-gnu" => self.find_working_gnu_prefix(&[ + "i686-linux-gnu", + "x86_64-linux-gnu", // transparently support gcc-multilib + ]), // explicit None if not found, so caller knows to fall back + "i686-unknown-linux-musl" => Some("musl"), + "i686-unknown-netbsd" => Some("i486--netbsdelf"), + "loongarch64-unknown-linux-gnu" => Some("loongarch64-linux-gnu"), + "m68k-unknown-linux-gnu" => Some("m68k-linux-gnu"), + "mips-unknown-linux-gnu" => Some("mips-linux-gnu"), + "mips-unknown-linux-musl" => Some("mips-linux-musl"), + "mipsel-unknown-linux-gnu" => Some("mipsel-linux-gnu"), + "mipsel-unknown-linux-musl" => Some("mipsel-linux-musl"), + "mips64-unknown-linux-gnuabi64" => Some("mips64-linux-gnuabi64"), + "mips64el-unknown-linux-gnuabi64" => Some("mips64el-linux-gnuabi64"), + "mipsisa32r6-unknown-linux-gnu" => Some("mipsisa32r6-linux-gnu"), + "mipsisa32r6el-unknown-linux-gnu" => Some("mipsisa32r6el-linux-gnu"), + "mipsisa64r6-unknown-linux-gnuabi64" => Some("mipsisa64r6-linux-gnuabi64"), + "mipsisa64r6el-unknown-linux-gnuabi64" => Some("mipsisa64r6el-linux-gnuabi64"), + "powerpc-unknown-helenos" => Some("ppc-helenos"), + "powerpc-unknown-linux-gnu" => Some("powerpc-linux-gnu"), + "powerpc-unknown-linux-gnuspe" => Some("powerpc-linux-gnuspe"), + "powerpc-unknown-netbsd" => Some("powerpc--netbsd"), + "powerpc64-unknown-linux-gnu" => Some("powerpc64-linux-gnu"), + "powerpc64le-unknown-linux-gnu" => Some("powerpc64le-linux-gnu"), + "riscv32i-unknown-none-elf" => self.find_working_gnu_prefix(&[ + "riscv32-unknown-elf", + "riscv64-unknown-elf", + "riscv-none-embed", + ]), + "riscv32im-unknown-none-elf" => self.find_working_gnu_prefix(&[ + "riscv32-unknown-elf", + "riscv64-unknown-elf", + "riscv-none-embed", + ]), + "riscv32imac-esp-espidf" => Some("riscv32-esp-elf"), + "riscv32imac-unknown-none-elf" => self.find_working_gnu_prefix(&[ + "riscv32-unknown-elf", + "riscv64-unknown-elf", + "riscv-none-embed", + ]), + "riscv32imafc-unknown-none-elf" => self.find_working_gnu_prefix(&[ + "riscv32-unknown-elf", + "riscv64-unknown-elf", + "riscv-none-embed", + ]), + "riscv32imac-unknown-xous-elf" => self.find_working_gnu_prefix(&[ + "riscv32-unknown-elf", + "riscv64-unknown-elf", + "riscv-none-embed", + ]), + "riscv32imc-esp-espidf" => Some("riscv32-esp-elf"), + "riscv32imc-unknown-none-elf" => self.find_working_gnu_prefix(&[ + "riscv32-unknown-elf", + "riscv64-unknown-elf", + "riscv-none-embed", + ]), + "riscv64gc-unknown-none-elf" => self.find_working_gnu_prefix(&[ + "riscv64-unknown-elf", + "riscv32-unknown-elf", + "riscv-none-embed", + ]), + "riscv64imac-unknown-none-elf" => self.find_working_gnu_prefix(&[ + "riscv64-unknown-elf", + "riscv32-unknown-elf", + "riscv-none-embed", + ]), + "riscv64gc-unknown-linux-gnu" => Some("riscv64-linux-gnu"), + "riscv64a23-unknown-linux-gnu" => Some("riscv64-linux-gnu"), + "riscv32gc-unknown-linux-gnu" => Some("riscv32-linux-gnu"), + "riscv64gc-unknown-linux-musl" => Some("riscv64-linux-musl"), + "riscv32gc-unknown-linux-musl" => Some("riscv32-linux-musl"), + "riscv64gc-unknown-netbsd" => Some("riscv64--netbsd"), + "s390x-unknown-linux-gnu" => Some("s390x-linux-gnu"), + "sparc-unknown-linux-gnu" => Some("sparc-linux-gnu"), + "sparc64-unknown-helenos" => Some("sparc64-helenos"), + "sparc64-unknown-linux-gnu" => Some("sparc64-linux-gnu"), + "sparc64-unknown-netbsd" => Some("sparc64--netbsd"), + "sparcv9-sun-solaris" => Some("sparcv9-sun-solaris"), + "armv4t-none-eabi" => Some("arm-none-eabi"), + "armv5te-none-eabi" => Some("arm-none-eabi"), + "armv6-none-eabi" => Some("arm-none-eabi"), + "armv6-none-eabihf" => Some("arm-none-eabi"), + "armv7a-none-eabi" => Some("arm-none-eabi"), + "armv7a-none-eabihf" => Some("arm-none-eabi"), + "armebv7r-none-eabi" => Some("arm-none-eabi"), + "armebv7r-none-eabihf" => Some("arm-none-eabi"), + "armv7r-none-eabi" => Some("arm-none-eabi"), + "armv7r-none-eabihf" => Some("arm-none-eabi"), + "armv8r-none-eabihf" => Some("arm-none-eabi"), + "thumbv4t-none-eabi" => Some("arm-none-eabi"), + "thumbv5te-none-eabi" => Some("arm-none-eabi"), + "thumbv6-none-eabi" => Some("arm-none-eabi"), + "thumbv7a-none-eabi" => Some("arm-none-eabi"), + "thumbv7a-none-eabihf" => Some("arm-none-eabi"), + "thumbv7r-none-eabi" => Some("arm-none-eabi"), + "thumbv7r-none-eabihf" => Some("arm-none-eabi"), + "thumbv8r-none-eabihf" => Some("arm-none-eabi"), + "thumbv6m-none-eabi" => Some("arm-none-eabi"), + "thumbv7em-none-eabi" => Some("arm-none-eabi"), + "thumbv7em-none-eabihf" => Some("arm-none-eabi"), + "thumbv7m-none-eabi" => Some("arm-none-eabi"), + "thumbv8m.base-none-eabi" => Some("arm-none-eabi"), + "thumbv8m.main-none-eabi" => Some("arm-none-eabi"), + "thumbv8m.main-none-eabihf" => Some("arm-none-eabi"), + "x86_64-pc-windows-gnu" => Some("x86_64-w64-mingw32"), + "x86_64-pc-windows-gnullvm" => Some("x86_64-w64-mingw32"), + "x86_64-uwp-windows-gnu" => Some("x86_64-w64-mingw32"), + "x86_64-rumprun-netbsd" => Some("x86_64-rumprun-netbsd"), + "x86_64-unknown-helenos" => Some("amd64-helenos"), + "x86_64-unknown-linux-gnu" => self.find_working_gnu_prefix(&[ + "x86_64-linux-gnu", // rustfmt wrap + ]), // explicit None if not found, so caller knows to fall back + "x86_64-unknown-linux-musl" => { + self.find_working_gnu_prefix(&["x86_64-linux-musl", "musl"]) + } + "x86_64-unknown-linux-relibc" => { + self.find_working_gnu_prefix(&["x86_64-linux-relibc", "relibc"]) + } + "x86_64-unknown-netbsd" => Some("x86_64--netbsd"), + "xtensa-esp32-espidf" + | "xtensa-esp32-none-elf" + | "xtensa-esp32s2-espidf" + | "xtensa-esp32s2-none-elf" + | "xtensa-esp32s3-espidf" + | "xtensa-esp32s3-none-elf" => Some("xtensa-esp-elf"), + _ => None, + } + .map(Cow::Borrowed) + }) + } + + /// Some platforms have multiple, compatible, canonical prefixes. Look through + /// each possible prefix for a compiler that exists and return it. The prefixes + /// should be ordered from most-likely to least-likely. + fn find_working_gnu_prefix(&self, prefixes: &[&'static str]) -> Option<&'static str> { + let suffix = if self.cpp { "-g++" } else { "-gcc" }; + let extension = std::env::consts::EXE_SUFFIX; + + // Loop through PATH entries searching for each toolchain. This ensures that we + // are more likely to discover the toolchain early on, because chances are good + // that the desired toolchain is in one of the higher-priority paths. + self.get_env("PATH") + .as_ref() + .and_then(|path_entries| { + env::split_paths(path_entries).find_map(|path_entry| { + for prefix in prefixes { + let target_compiler = format!("{prefix}{suffix}{extension}"); + if path_entry.join(&target_compiler).exists() { + return Some(prefix); + } + } + None + }) + }) + .copied() + // If no toolchain was found, provide the first toolchain that was passed in. + // This toolchain has been shown not to exist, however it will appear in the + // error that is shown to the user which should make it easier to search for + // where it should be obtained. + .or_else(|| prefixes.first().copied()) + } + + fn get_target(&self) -> Result, Error> { + match &self.target { + Some(t) if Some(OsStr::new(&**t)) != cargo_env_var_os("TARGET").as_deref() => { + TargetInfo::from_rustc_target(t) + } + // Fetch target information from environment if not set, or if the + // target was the same as the TARGET environment variable, in + // case the user did `build.target(&env::var("TARGET").unwrap())`. + _ => self + .build_cache + .target_info_parser + .parse_from_cargo_environment_variables(), + } + } + + fn get_raw_target(&self) -> Result, Error> { + match &self.target { + Some(t) => Ok(Cow::Borrowed(t)), + None => cargo_env_var("TARGET").map(Cow::Owned), + } + } + + fn get_is_cross_compile(&self) -> Result { + let target = self.get_raw_target()?; + let host: Cow<'_, str> = match &self.host { + Some(h) => Cow::Borrowed(h), + None => Cow::Owned(cargo_env_var("HOST")?), + }; + Ok(host != target) + } + + fn get_opt_level(&self) -> Result, Error> { + match &self.opt_level { + Some(ol) => Ok(Cow::Borrowed(ol)), + None => cargo_env_var("OPT_LEVEL").map(Cow::Owned), + } + } + + /// Returns true if *any* debug info is enabled. + /// + /// [`get_debug_str`] provides more detail. + fn get_debug(&self) -> bool { + match self.get_debug_str() { + Err(_) => false, + Ok(d) => match &*d { + // From https://doc.rust-lang.org/cargo/reference/profiles.html#debug + "" | "0" | "false" | "none" => false, + _ => true, + }, + } + } + + fn get_debug_str(&self) -> Result, Error> { + match &self.debug { + Some(d) => Ok(Cow::Borrowed(d)), + None => cargo_env_var("DEBUG").map(Cow::Owned), + } + } + + fn get_shell_escaped_flags(&self) -> bool { + self.shell_escaped_flags + .unwrap_or_else(|| self.get_env_boolean("CC_SHELL_ESCAPED_FLAGS")) + } + + fn get_dwarf_version(&self) -> Option { + // Tentatively matches the DWARF version defaults as of rustc 1.62. + let target = self.get_target().ok()?; + if matches!( + target.os, + "android" | "dragonfly" | "freebsd" | "netbsd" | "openbsd" + ) || target.vendor == "apple" + || (target.os == "windows" && target.env == "gnu") + { + Some(2) + } else if target.os == "linux" { + Some(4) + } else { + None + } + } + + fn get_force_frame_pointer(&self) -> bool { + self.force_frame_pointer.unwrap_or_else(|| self.get_debug()) + } + + fn get_out_dir(&self) -> Result, Error> { + match &self.out_dir { + Some(p) => Ok(Cow::Borrowed(&**p)), + None => cargo_env_var_os("OUT_DIR") + .map(PathBuf::from) + .map(Cow::Owned) + .ok_or_else(|| { + Error::new( + ErrorKind::EnvVarNotFound, + "Environment variable OUT_DIR not defined.", + ) + }), + } + } + + /// Look up an environment variable, and tell Cargo that we used it. + fn get_env(&self, v: &str) -> Option { + // Excluding `PATH` prevents spurious rebuilds on Windows, see + // for details. + if self.emit_rerun_if_env_changed && v != "PATH" { + self.cargo_output + .print_metadata(&format_args!("cargo:rerun-if-env-changed={v}")); + } + #[allow(clippy::disallowed_methods)] // We emit rerun-if-env-changed above + let r = env::var_os(v); + self.cargo_output.print_metadata(&format_args!( + "{} = {}", + v, + OptionOsStrDisplay(r.as_deref()) + )); + r + } + + /// Look up an environment variable that's allowed to be overwritten by + /// [`Build::env`]. + /// + /// This is useful for environment variables that the compiler could + /// reasonably read, such as `SDKROOT` and `WASI_SDK_PATH` - for these, we + /// generally want to allow build scripts to overwrite them. + /// + /// On the other hand, we don't want to allow overwriting environment + /// variables that are `CC`-specific such as `CC_FORCE_DISABLE` + /// (`Build::env` applies to child processes, not to `cc` itself). + fn get_env_overridable(&self, key: &str) -> Option> { + // Try to look up in overrides first. + if let Some((_key, val)) = self.env.iter().find(|(k, _)| k.as_ref() == key) { + return Some(Cow::Borrowed(&**val)); + } + + // If not found in overrides, look up from environment. + self.get_env(key).map(Cow::Owned) + } + + /// Get boolean flag that is either true or false. + /// + /// Used for `CC_*`-style flags. + fn get_env_boolean(&self, key: &str) -> bool { + match self.get_env(key) { + // Set -> `true`, unless set to `""`, `"0"`, `"no"` `"false"` + Some(s) => &*s != "0" && &*s != "false" && &*s != "no" && !s.is_empty(), + // Not set -> default to `false`. + None => false, + } + } + + /// The list of environment variables to check for a given env, in order of priority. + fn target_envs(&self, env: &str) -> Result<[String; 4], Error> { + let target = self.get_raw_target()?; + let kind = if self.get_is_cross_compile()? { + "TARGET" + } else { + "HOST" + }; + let target_u = target.replace(['-', '.'], "_"); + + Ok([ + format!("{env}_{target}"), + format!("{env}_{target_u}"), + format!("{kind}_{env}"), + env.to_string(), + ]) + } + + /// Get a single-valued environment variable with target variants. + fn getenv_with_target_prefixes(&self, env: &str) -> Result { + // Take from first environment variable in the environment. + let res = self + .target_envs(env)? + .iter() + .filter_map(|env| self.get_env(env)) + .next(); + + match res { + Some(res) => Ok(res), + None => Err(Error::new( + ErrorKind::EnvVarNotFound, + format!("could not find environment variable {env}"), + )), + } + } + + /// Get values from CFLAGS-style environment variable. + fn envflags(&self, env: &str) -> Result>, Error> { + // Collect from all environment variables, in reverse order as in + // `getenv_with_target_prefixes` precedence (so that `CFLAGS_$TARGET` + // can override flags in `TARGET_CFLAGS`, which overrides those in + // `CFLAGS`). + let mut any_set = false; + let mut res = vec![]; + for env in self.target_envs(env)?.iter().rev() { + if let Some(var) = self.get_env(env) { + any_set = true; + + let var = var.to_string_lossy(); + if self.get_shell_escaped_flags() { + res.extend(Shlex::new(&var)); + } else { + res.extend(var.split_ascii_whitespace().map(ToString::to_string)); + } + } + } + + Ok(if any_set { Some(res) } else { None }) + } + + /// Returns true if `cc` has been disabled by `CC_FORCE_DISABLE`. + fn is_disabled(&self) -> bool { + self.get_env_boolean("CC_FORCE_DISABLE") + } + + fn fix_env_for_apple_os(&self, cmd: &mut Command) -> Result<(), Error> { + let target = self.get_target()?; + if cfg!(target_os = "macos") && target.os == "macos" { + // Additionally, `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at + // "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld", + // although this is apparently ignored when using the linker at "/usr/bin/ld". + cmd.env_remove("IPHONEOS_DEPLOYMENT_TARGET"); + } + Ok(()) + } + + fn apple_sdk_root_inner(&self, sdk: &str) -> Result, Error> { + // Code copied from rustc's compiler/rustc_codegen_ssa/src/back/link.rs. + if let Some(sdkroot) = self.get_env_overridable("SDKROOT") { + let p = Path::new(&sdkroot); + let does_sdkroot_contain = |strings: &[&str]| { + let sdkroot_str = p.to_string_lossy(); + strings.iter().any(|s| sdkroot_str.contains(s)) + }; + match sdk { + // Ignore `SDKROOT` if it's clearly set for the wrong platform. + "appletvos" + if does_sdkroot_contain(&["TVSimulator.platform", "MacOSX.platform"]) => {} + "appletvsimulator" + if does_sdkroot_contain(&["TVOS.platform", "MacOSX.platform"]) => {} + "iphoneos" + if does_sdkroot_contain(&["iPhoneSimulator.platform", "MacOSX.platform"]) => {} + "iphonesimulator" + if does_sdkroot_contain(&["iPhoneOS.platform", "MacOSX.platform"]) => {} + "macosx10.15" + if does_sdkroot_contain(&["iPhoneOS.platform", "iPhoneSimulator.platform"]) => { + } + "watchos" + if does_sdkroot_contain(&["WatchSimulator.platform", "MacOSX.platform"]) => {} + "watchsimulator" + if does_sdkroot_contain(&["WatchOS.platform", "MacOSX.platform"]) => {} + "xros" if does_sdkroot_contain(&["XRSimulator.platform", "MacOSX.platform"]) => {} + "xrsimulator" if does_sdkroot_contain(&["XROS.platform", "MacOSX.platform"]) => {} + // Ignore `SDKROOT` if it's not a valid path. + _ if !p.is_absolute() || p == Path::new("/") || !p.exists() => {} + _ => return Ok(sdkroot), + } + } + + let sdk_path = run_output( + self.cmd("xcrun") + .arg("--show-sdk-path") + .arg("--sdk") + .arg(sdk), + &self.cargo_output, + )?; + + let sdk_path = match String::from_utf8(sdk_path) { + Ok(p) => p, + Err(_) => { + return Err(Error::new( + ErrorKind::IOError, + "Unable to determine Apple SDK path.", + )); + } + }; + Ok(Cow::Owned(sdk_path.trim().into())) + } + + fn apple_sdk_root(&self, target: &TargetInfo<'_>) -> Result, Error> { + let sdk = target.apple_sdk_name(); + + if let Some(ret) = self + .build_cache + .apple_sdk_root_cache + .read() + .expect("apple_sdk_root_cache lock failed") + .get(sdk) + .cloned() + { + return Ok(ret); + } + let sdk_path: Arc = self.apple_sdk_root_inner(sdk)?.into(); + self.build_cache + .apple_sdk_root_cache + .write() + .expect("apple_sdk_root_cache lock failed") + .insert(sdk.into(), sdk_path.clone()); + Ok(sdk_path) + } + + fn apple_deployment_target(&self, target: &TargetInfo<'_>) -> Arc { + let sdk = target.apple_sdk_name(); + if let Some(ret) = self + .build_cache + .apple_versions_cache + .read() + .expect("apple_versions_cache lock failed") + .get(sdk) + .cloned() + { + return ret; + } + + let default_deployment_from_sdk = || -> Option> { + let version = run_output( + self.cmd("xcrun") + .arg("--show-sdk-version") + .arg("--sdk") + .arg(sdk), + &self.cargo_output, + ) + .ok()?; + + Some(Arc::from(std::str::from_utf8(&version).ok()?.trim())) + }; + + let deployment_from_env = |name: &str| -> Option> { + self.get_env_overridable(name)?.to_str().map(Arc::from) + }; + + // Determines if the acquired deployment target is too low to support modern C++ on some Apple platform. + // + // A long time ago they used libstdc++, but since macOS 10.9 and iOS 7 libc++ has been the library the SDKs provide to link against. + // If a `cc`` config wants to use C++, we round up to these versions as the baseline. + let maybe_cpp_version_baseline = |deployment_target_ver: Arc| -> Option> { + if !self.cpp { + return Some(deployment_target_ver); + } + + let mut deployment_target = deployment_target_ver + .split('.') + .map(|v| v.parse::().expect("integer version")); + + match target.os { + "macos" => { + let major = deployment_target.next().unwrap_or(0); + let minor = deployment_target.next().unwrap_or(0); + + // If below 10.9, we ignore it and let the SDK's target definitions handle it. + if major == 10 && minor < 9 { + self.cargo_output.print_warning(&format_args!( + "macOS deployment target ({deployment_target_ver}) too low, it will be increased" + )); + return None; + } + } + "ios" => { + let major = deployment_target.next().unwrap_or(0); + + // If below 10.7, we ignore it and let the SDK's target definitions handle it. + if major < 7 { + self.cargo_output.print_warning(&format_args!( + "iOS deployment target ({deployment_target_ver}) too low, it will be increased" + )); + return None; + } + } + // watchOS, tvOS, visionOS, and others are all new enough that libc++ is their baseline. + _ => {} + } + + // If the deployment target met or exceeded the C++ baseline + Some(deployment_target_ver) + }; + + // The hardcoded minimums here are subject to change in a future compiler release, + // and only exist as last resort fallbacks. Don't consider them stable. + // `cc` doesn't use rustc's `--print deployment-target`` because the compiler's defaults + // don't align well with Apple's SDKs and other third-party libraries that require ~generally~ higher + // deployment targets. rustc isn't interested in those by default though so its fine to be different here. + // + // If no explicit target is passed, `cc` defaults to the current Xcode SDK's `DefaultDeploymentTarget` for better + // compatibility. This is also the crate's historical behavior and what has become a relied-on value. + // + // The ordering of env -> XCode SDK -> old rustc defaults is intentional for performance when using + // an explicit target. + let version: Arc = match target.os { + "macos" => deployment_from_env("MACOSX_DEPLOYMENT_TARGET") + .and_then(maybe_cpp_version_baseline) + .or_else(default_deployment_from_sdk) + .unwrap_or_else(|| { + if target.arch == "aarch64" { + "11.0".into() + } else { + let default: Arc = Arc::from("10.7"); + maybe_cpp_version_baseline(default.clone()).unwrap_or(default) + } + }), + + "ios" => deployment_from_env("IPHONEOS_DEPLOYMENT_TARGET") + .and_then(maybe_cpp_version_baseline) + .or_else(default_deployment_from_sdk) + .unwrap_or_else(|| "7.0".into()), + + "watchos" => deployment_from_env("WATCHOS_DEPLOYMENT_TARGET") + .or_else(default_deployment_from_sdk) + .unwrap_or_else(|| "5.0".into()), + + "tvos" => deployment_from_env("TVOS_DEPLOYMENT_TARGET") + .or_else(default_deployment_from_sdk) + .unwrap_or_else(|| "9.0".into()), + + "visionos" => deployment_from_env("XROS_DEPLOYMENT_TARGET") + .or_else(default_deployment_from_sdk) + .unwrap_or_else(|| "1.0".into()), + + os => unreachable!("unknown Apple OS: {}", os), + }; + + self.build_cache + .apple_versions_cache + .write() + .expect("apple_versions_cache lock failed") + .insert(sdk.into(), version.clone()); + + version + } + + fn wasm_musl_sysroot(&self) -> Result { + if let Some(musl_sysroot_path) = self.get_env("WASM_MUSL_SYSROOT") { + Ok(musl_sysroot_path) + } else { + Err(Error::new( + ErrorKind::EnvVarNotFound, + "Environment variable WASM_MUSL_SYSROOT not defined for wasm32. Download sysroot from GitHub & setup environment variable MUSL_SYSROOT targeting the folder.", + )) + } + } + + fn wasi_sysroot(&self) -> Result { + if let Some(wasi_sysroot_path) = self.get_env("WASI_SYSROOT") { + Ok(wasi_sysroot_path) + } else { + Err(Error::new( + ErrorKind::EnvVarNotFound, + "Environment variable WASI_SYSROOT not defined. Download sysroot from GitHub & setup environment variable WASI_SYSROOT targeting the folder.", + )) + } + } + + fn cuda_file_count(&self) -> usize { + self.files + .iter() + .filter(|file| file.extension() == Some(OsStr::new("cu"))) + .count() + } + + fn which(&self, tool: &Path, path_entries: Option<&OsStr>) -> Option { + // Loop through PATH entries searching for the |tool|. + let find_exe_in_path = |path_entries: &OsStr| -> Option { + env::split_paths(path_entries).find_map(|path_entry| check_exe(path_entry.join(tool))) + }; + + // If |tool| is not just one "word," assume it's an actual path... + if tool.components().count() > 1 { + check_exe(PathBuf::from(tool)) + } else { + path_entries + .and_then(find_exe_in_path) + .or_else(|| find_exe_in_path(&self.get_env("PATH")?)) + } + } + + /// search for |prog| on 'programs' path in '|cc| --print-search-dirs' output + fn search_programs( + &self, + cc: &Path, + prog: &Path, + cargo_output: &CargoOutput, + ) -> Option { + let search_dirs = run_output( + self.cmd(cc).arg("--print-search-dirs"), + // this doesn't concern the compilation so we always want to show warnings. + cargo_output, + ) + .ok()?; + // clang driver appears to be forcing UTF-8 output even on Windows, + // hence from_utf8 is assumed to be usable in all cases. + let search_dirs = std::str::from_utf8(&search_dirs).ok()?; + for dirs in search_dirs.split(['\r', '\n']) { + if let Some(path) = dirs.strip_prefix("programs: =") { + return self.which(prog, Some(OsStr::new(path))); + } + } + None + } + + fn find_msvc_tools_find(&self, target: &TargetInfo<'_>, tool: &str) -> Option { + self.find_msvc_tools_find_tool(target, tool) + .map(|c| c.to_command()) + } + + fn find_msvc_tools_find_tool(&self, target: &TargetInfo<'_>, tool: &str) -> Option { + struct BuildEnvGetter<'s>(&'s Build); + + impl ::find_msvc_tools::EnvGetter for BuildEnvGetter<'_> { + fn get_env(&self, name: &str) -> Option<::find_msvc_tools::Env> { + // TODO: Should we allow overriding these with `Build::env`? + // + self.0.get_env(name).map(::find_msvc_tools::Env::Owned) + } + } + + if target.env != "msvc" { + return None; + } + + ::find_msvc_tools::find_tool_with_env(target.full_arch, tool, &BuildEnvGetter(self)) + .map(Tool::from_find_msvc_tools) + } + + /// Compiling for WASI targets typically uses the [wasi-sdk] project and + /// installations of wasi-sdk are typically indicated with the + /// `WASI_SDK_PATH` environment variable. Check to see if that environment + /// variable exists, and check to see if an appropriate compiler is located + /// there. If that all passes then use that compiler by default, but + /// otherwise fall back to whatever the clang default is since gcc doesn't + /// have support for compiling to wasm. + /// + /// [wasi-sdk]: https://github.com/WebAssembly/wasi-sdk + fn autodetect_wasi_compiler(&self, raw_target: &str, clang: &str) -> PathBuf { + if let Some(path) = self.get_env_overridable("WASI_SDK_PATH") { + let target_clang = Path::new(&path) + .join("bin") + .join(format!("{raw_target}-clang")); + if let Some(path) = self.which(&target_clang, None) { + return path; + } + } + + clang.into() + } +} + +impl Default for Build { + fn default() -> Build { + Build::new() + } +} + +fn fail(s: &str) -> ! { + eprintln!("\n\nerror occurred in cc-rs: {s}\n\n"); + std::process::exit(1); +} + +// Use by default minimum available API level +// See note about naming here +// https://android.googlesource.com/platform/ndk/+/refs/heads/ndk-release-r21/docs/BuildSystemMaintainers.md#Clang +static NEW_STANDALONE_ANDROID_COMPILERS: [&str; 4] = [ + "aarch64-linux-android21-clang", + "armv7a-linux-androideabi16-clang", + "i686-linux-android16-clang", + "x86_64-linux-android21-clang", +]; + +// New "standalone" C/C++ cross-compiler executables from recent Android NDK +// are just shell scripts that call main clang binary (from Android NDK) with +// proper `--target` argument. +// +// For example, armv7a-linux-androideabi16-clang passes +// `--target=armv7a-linux-androideabi16` to clang. +// So to construct proper command line check if +// `--target` argument would be passed or not to clang +fn android_clang_compiler_uses_target_arg_internally(clang_path: &Path) -> bool { + if let Some(filename) = clang_path.file_name() { + if let Some(filename_str) = filename.to_str() { + if let Some(idx) = filename_str.rfind('-') { + return filename_str.split_at(idx).0.contains("android"); + } + } + } + false +} + +fn is_llvm_mingw_wrapper(clang_path: &Path) -> bool { + if let Some(filename) = clang_path + .file_name() + .and_then(|file_name| file_name.to_str()) + { + filename.ends_with("-w64-mingw32-clang") || filename.ends_with("-w64-mingw32-clang++") + } else { + false + } +} + +// FIXME: Use parsed target. +fn autodetect_android_compiler(raw_target: &str, gnu: &str, clang: &str) -> PathBuf { + let new_clang_key = match raw_target { + "aarch64-linux-android" => Some("aarch64"), + "armv7-linux-androideabi" => Some("armv7a"), + "i686-linux-android" => Some("i686"), + "x86_64-linux-android" => Some("x86_64"), + _ => None, + }; + + let new_clang = new_clang_key + .map(|key| { + NEW_STANDALONE_ANDROID_COMPILERS + .iter() + .find(|x| x.starts_with(key)) + }) + .unwrap_or(None); + + if let Some(new_clang) = new_clang { + if Command::new(new_clang).output().is_ok() { + return (*new_clang).into(); + } + } + + let target = raw_target + .replace("armv7neon", "arm") + .replace("armv7", "arm") + .replace("thumbv7neon", "arm") + .replace("thumbv7", "arm"); + let gnu_compiler = format!("{target}-{gnu}"); + let clang_compiler = format!("{target}-{clang}"); + + // On Windows, the Android clang compiler is provided as a `.cmd` file instead + // of a `.exe` file. `std::process::Command` won't run `.cmd` files unless the + // `.cmd` is explicitly appended to the command name, so we do that here. + let clang_compiler_cmd = format!("{target}-{clang}.cmd"); + + // Check if gnu compiler is present + // if not, use clang + if Command::new(&gnu_compiler).output().is_ok() { + gnu_compiler + } else if cfg!(windows) && Command::new(&clang_compiler_cmd).output().is_ok() { + clang_compiler_cmd + } else { + clang_compiler + } + .into() +} + +// Rust and clang/cc don't agree on how to name the target. +fn map_darwin_target_from_rust_to_compiler_architecture<'a>(target: &TargetInfo<'a>) -> &'a str { + match target.full_arch { + "aarch64" => "arm64", + "arm64_32" => "arm64_32", + "arm64e" => "arm64e", + "armv7k" => "armv7k", + "armv7s" => "armv7s", + "i386" => "i386", + "i686" => "i386", + "powerpc" => "ppc", + "powerpc64" => "ppc64", + "x86_64" => "x86_64", + "x86_64h" => "x86_64h", + arch => arch, + } +} + +fn is_arm(target: &TargetInfo<'_>) -> bool { + matches!(target.arch, "aarch64" | "arm64ec" | "arm") +} + +#[derive(Clone, Copy, PartialEq)] +enum AsmFileExt { + /// `.asm` files. On MSVC targets, we assume these should be passed to MASM + /// (`ml{,64}.exe`). + DotAsm, + /// `.s` or `.S` files, which do not have the special handling on MSVC targets. + DotS, +} + +impl AsmFileExt { + fn from_path(file: &Path) -> Option { + if let Some(ext) = file.extension() { + if let Some(ext) = ext.to_str() { + let ext = ext.to_lowercase(); + match &*ext { + "asm" => return Some(AsmFileExt::DotAsm), + "s" => return Some(AsmFileExt::DotS), + _ => return None, + } + } + } + None + } +} + +fn check_exe(mut exe: PathBuf) -> Option { + let exe_ext = std::env::consts::EXE_EXTENSION; + let check = exe.exists() || (!exe_ext.is_empty() && exe.set_extension(exe_ext) && exe.exists()); + check.then_some(exe) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_android_clang_compiler_uses_target_arg_internally() { + for version in 16..21 { + assert!(android_clang_compiler_uses_target_arg_internally( + &PathBuf::from(format!("armv7a-linux-androideabi{}-clang", version)) + )); + assert!(android_clang_compiler_uses_target_arg_internally( + &PathBuf::from(format!("armv7a-linux-androideabi{}-clang++", version)) + )); + } + assert!(!android_clang_compiler_uses_target_arg_internally( + &PathBuf::from("clang-i686-linux-android") + )); + assert!(!android_clang_compiler_uses_target_arg_internally( + &PathBuf::from("clang") + )); + assert!(!android_clang_compiler_uses_target_arg_internally( + &PathBuf::from("clang++") + )); + } +} diff --git a/cc-1.2.62/src/parallel/async_executor.rs b/cc-1.2.64/src/parallel/async_executor.rs similarity index 100% rename from cc-1.2.62/src/parallel/async_executor.rs rename to cc-1.2.64/src/parallel/async_executor.rs diff --git a/cc-1.2.62/src/parallel/command_runner.rs b/cc-1.2.64/src/parallel/command_runner.rs similarity index 100% rename from cc-1.2.62/src/parallel/command_runner.rs rename to cc-1.2.64/src/parallel/command_runner.rs diff --git a/cc-1.2.62/src/parallel/job_token.rs b/cc-1.2.64/src/parallel/job_token.rs similarity index 100% rename from cc-1.2.62/src/parallel/job_token.rs rename to cc-1.2.64/src/parallel/job_token.rs diff --git a/cc-1.2.62/src/parallel/mod.rs b/cc-1.2.64/src/parallel/mod.rs similarity index 100% rename from cc-1.2.62/src/parallel/mod.rs rename to cc-1.2.64/src/parallel/mod.rs diff --git a/cc-1.2.62/src/parallel/stderr.rs b/cc-1.2.64/src/parallel/stderr.rs similarity index 100% rename from cc-1.2.62/src/parallel/stderr.rs rename to cc-1.2.64/src/parallel/stderr.rs diff --git a/cc-1.2.62/src/target.rs b/cc-1.2.64/src/target.rs similarity index 100% rename from cc-1.2.62/src/target.rs rename to cc-1.2.64/src/target.rs diff --git a/cc-1.2.62/src/target/apple.rs b/cc-1.2.64/src/target/apple.rs similarity index 100% rename from cc-1.2.62/src/target/apple.rs rename to cc-1.2.64/src/target/apple.rs diff --git a/cc-1.2.62/src/target/generated.rs b/cc-1.2.64/src/target/generated.rs similarity index 100% rename from cc-1.2.62/src/target/generated.rs rename to cc-1.2.64/src/target/generated.rs diff --git a/cc-1.2.62/src/target/llvm.rs b/cc-1.2.64/src/target/llvm.rs similarity index 100% rename from cc-1.2.62/src/target/llvm.rs rename to cc-1.2.64/src/target/llvm.rs diff --git a/cc-1.2.62/src/target/parser.rs b/cc-1.2.64/src/target/parser.rs similarity index 100% rename from cc-1.2.62/src/target/parser.rs rename to cc-1.2.64/src/target/parser.rs diff --git a/cc-1.2.62/src/tempfile.rs b/cc-1.2.64/src/tempfile.rs similarity index 100% rename from cc-1.2.62/src/tempfile.rs rename to cc-1.2.64/src/tempfile.rs diff --git a/cc-1.2.62/src/tool.rs b/cc-1.2.64/src/tool.rs similarity index 100% rename from cc-1.2.62/src/tool.rs rename to cc-1.2.64/src/tool.rs diff --git a/cc-1.2.62/src/utilities.rs b/cc-1.2.64/src/utilities.rs similarity index 100% rename from cc-1.2.62/src/utilities.rs rename to cc-1.2.64/src/utilities.rs diff --git a/chrono-0.4.44/.cargo-checksum.json b/chrono-0.4.44/.cargo-checksum.json deleted file mode 100644 index 90524798df..0000000000 --- a/chrono-0.4.44/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{".cargo_vcs_info.json":"0f0ab43ec1fab0db9cc2440e9cbe440f2904f9fe71286758f92093c68407a40f","CITATION.cff":"fe53d4b3457065ae58342941d06c479070ab5018af1b9cd927897bb0ef968f65","Cargo.lock":"3cfa5d1c0233a5f43966c0ce5b4ef607f2ddc70e76cabec70b36f50c1af97bc7","Cargo.toml":"8cb862b63f6e580915890cef820dc52814ce95f8b76b168f028dd19f7fe4ba89","Cargo.toml.orig":"9fca73b7fd60324a626b187aae27ad8ee970242261d68d715e3aa0aa445fd2c4","LICENSE.txt":"946c9835d8034d24404f8cfec5f4654cee5dad17e944afc3d06d742cf2882831","README.md":"7be722f3881a8ce9a3c6fbc427bfcf2c3888f7f7bdd8681dbf65e6161686da02","src/date.rs":"abb1b68ec5cec15383d0ec01134bb634c59fbe86bf4039f191accd21ab887b01","src/datetime/mod.rs":"1ccc3656a30e1c7115b3db850c121e4b148d6c9ace74fd5d840532ddb4a36f5f","src/datetime/serde.rs":"71f3ff0b9f484efa52c5abf811cbcd0da5d9f01b6bb0c9cc577886831fd6231c","src/datetime/tests.rs":"98f1d803a1ce0a3a78a0c45b2523dbb70e0d49e77563e22611956be5c0d55108","src/format/formatting.rs":"b6d096228cd87ae78a71a1a1cf8e1b933d2259eb33c9020824287d603ab432e5","src/format/locales.rs":"d49de3da43941c94b1108f8fe84915cc5e1c13a1f1c84fd7356f9b7c61f19242","src/format/mod.rs":"485fdb1e2a85d72114e41f15a632d0c53cc4114fd4c381014aaf91e9c2f691af","src/format/parse.rs":"0114cc8f7cee11af7fd091f874c609c71e2ba094e2d793459f440150bb3cb1ed","src/format/parsed.rs":"369844ce5079271e526b8420b70d25a69dbfc8dc6ad2a534ea36fd9e80edd093","src/format/scan.rs":"100da6368c449f45b26c44ffe29892d91f814a3bc98ed0cd1c3679e2852b7b56","src/format/strftime.rs":"928e684b1a98a08dd5e3a3253ee19e20b555f20331d5d3a8917017fc09e515fc","src/lib.rs":"87deb76ae6338b65dc31f6b31b59c2a30e93f5549f0690be06b551ed7bad36b1","src/month.rs":"00ceab10b90f69a54b46d5aa6e88d6b279ebbe424270045598b6c702372dab48","src/naive/date/mod.rs":"e2ffd9af71e593b2475ce9f3fd3d37fe861e762f1e920e92a35c13a2b0cd882b","src/naive/date/tests.rs":"0e1c240e3df5b4b83f5a508f3742d5b59ea280c4043742a29ca47f76abafe17f","src/naive/datetime/mod.rs":"6239fbfc3fb872a5ee0f24643c95af2e266f70a9ab0812628e0349a694976b22","src/naive/datetime/serde.rs":"0424ad2cfc4b53d5398ea2ade2825e2acd465cf9c49d7df6dbe579c38f409804","src/naive/datetime/tests.rs":"ad845811e0b5917ca1fe1224cc120005b669a2b8c482155dc39a5c538fb376d4","src/naive/internals.rs":"6586264f7b20794b659716c1426da9ef999d6f0f62b1222378365cf9055b1d53","src/naive/isoweek.rs":"24073861ba94371490f178c0636b063689616199e9216b469c8158c3ccfc278f","src/naive/mod.rs":"e8926dd7aaeb85597ca552fe19ba911880b52972a8429fdb304cc68f33c8ae83","src/naive/time/mod.rs":"dad88f7e02c3a552e135a2c7a843d71a86696d29e48808988b2df6c8210d2672","src/naive/time/serde.rs":"6c260c6d0c45dec113f3dcff6056450c524511c37f7c30da9324ad80aff2288b","src/naive/time/tests.rs":"4b485a2d7f4ce7b66ca3a433699fa3facb8b9ee0ff0b3661437247c2626d4a52","src/offset/fixed.rs":"a3f8d6f18b92566dbc47760febb066f7460cf30edb59ee46a178cf01e2ea34ed","src/offset/local/mod.rs":"6731157ff784c55d45fac7608089d47b38cf023f6251744e5febe5891e376fb3","src/offset/local/tz_data.rs":"1c8681efab819aec0a270bc016c2416c7d7201523fce7c7e4d9cb891e74fc378","src/offset/local/tz_info/mod.rs":"aed33c3310e73d02fc1434e7c820bcd38fd76d56d7e6d348b1bb2a1a1b39fee3","src/offset/local/tz_info/parser.rs":"ec21d8739a86fb4e77551733e13af9964fbc01f80c87d7a164f6185ca9928c22","src/offset/local/tz_info/rule.rs":"bfc9e6257aeaaa23edef69b00acee58233846702eb969b8011d1b2425d15d10c","src/offset/local/tz_info/timezone.rs":"b61c4d1be526ab011027fb6a2d1c4e61cc1f4f1eb6ff56d3f7c0b425dfc64feb","src/offset/local/unix.rs":"b9900a80a669f32b3f9112087c269b1bf877d5ed1d5a388702938e1caa864d8a","src/offset/local/win_bindings.rs":"2bdb949f451ad9f15146712471d08e7e2660d1049250cbef9ce6c8d0f0062252","src/offset/local/windows.rs":"e9909f06e84c6334433fe24e1db14ca61c116889df9db6903917a3083fdd4606","src/offset/mod.rs":"641a632cdec0c966d558231f1e94cdddb1a447f51410b7d56445e65bb6878c25","src/offset/utc.rs":"81eac3e383a218d209f8a6d6b3e15a1c11bf526f80b133887baa1939c9d0e766","src/round.rs":"cbd7567c5203318e72955d6fa405a89b4d068b62331a8cdf9dfb634eec0b802b","src/time_delta.rs":"647ea215f6c9c58060c3f82db09147d857a4075890e331e55e6256e02cdbd425","src/traits.rs":"40386bdfe14fdcc04c43f3fb0399624cc054dfe569e2f8136d282920824f3fce","src/weekday.rs":"7db31835c30067cf5b1aa9e6e75f4c9da36bfeb07a549139d94ff24c828b2ea5","src/weekday_set.rs":"be1827f17ca4d5945421d71dbe3f3688eb5dde15ff4471fffa2bb783e3feb0a9","tests/dateutils.rs":"e2307421ce438e7af8fbbfde997e3920836d94c5ca5911871119ed6c13f15a0c","tests/wasm.rs":"252f16aeeaacbf26ca268b9a5e53aee3560cd1f071142a7a16d1509a02758a17","tests/win_bindings.rs":"38ff2f3966cd99ba3bd45f43e1e0dbce047db4cca8cbf3738ed8f18558bf9a9d"},"package":"c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"} \ No newline at end of file diff --git a/chrono-0.4.44/.cargo_vcs_info.json b/chrono-0.4.44/.cargo_vcs_info.json deleted file mode 100644 index 45e92d1cf9..0000000000 --- a/chrono-0.4.44/.cargo_vcs_info.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "git": { - "sha1": "c14b4599d07ef36ffa1f8a531fb0bc7eb3b42464" - }, - "path_in_vcs": "" -} \ No newline at end of file diff --git a/chrono-0.4.44/CITATION.cff b/chrono-0.4.44/CITATION.cff deleted file mode 100644 index b1d4b976df..0000000000 --- a/chrono-0.4.44/CITATION.cff +++ /dev/null @@ -1,33 +0,0 @@ -# Parser settings. -cff-version: 1.2.0 -message: Please cite this crate using these information. - -# Version information. -date-released: 2026-01-09 -version: 0.4.43 - -# Project information. -abstract: Date and time library for Rust -authors: - - alias: quodlibetor - family-names: Maister - given-names: Brandon W. - - alias: djc - family-names: Ochtman - given-names: Dirkjan - - alias: lifthrasiir - family-names: Seonghoon - given-names: Kang - - alias: esheppa - family-names: Sheppard - given-names: Eric - - alias: pitdicker - family-names: Dicker - given-names: Paul -license: - - Apache-2.0 - - MIT -repository-artifact: https://crates.io/crates/chrono -repository-code: https://github.com/chronotope/chrono -title: chrono -url: https://docs.rs/chrono diff --git a/chrono-0.4.44/Cargo.lock b/chrono-0.4.44/Cargo.lock deleted file mode 100644 index dfdad09f05..0000000000 --- a/chrono-0.4.44/Cargo.lock +++ /dev/null @@ -1,845 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "arbitrary" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" -dependencies = [ - "derive_arbitrary", -] - -[[package]] -name = "autocfg" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "bstr" -version = "1.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" -dependencies = [ - "memchr", - "regex-automata", - "serde", -] - -[[package]] -name = "bumpalo" -version = "3.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" - -[[package]] -name = "bytecheck" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" -dependencies = [ - "bytecheck_derive", - "ptr_meta", -] - -[[package]] -name = "bytecheck_derive" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "cc" -version = "1.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af" -dependencies = [ - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.44" -dependencies = [ - "arbitrary", - "bincode", - "defmt", - "iana-time-zone", - "js-sys", - "num-traits", - "pure-rust-locales", - "rkyv", - "serde", - "serde_derive", - "serde_json", - "similar-asserts", - "wasm-bindgen", - "wasm-bindgen-test", - "windows-bindgen", - "windows-link", -] - -[[package]] -name = "console" -version = "0.15.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b" -dependencies = [ - "encode_unicode", - "libc", - "once_cell", - "windows-sys", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "defmt" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78" -dependencies = [ - "bitflags", - "defmt-macros", -] - -[[package]] -name = "defmt-macros" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e" -dependencies = [ - "defmt-parser", - "proc-macro-error2", - "proc-macro2", - "quote", - "syn 2.0.98", -] - -[[package]] -name = "defmt-parser" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" -dependencies = [ - "thiserror", -] - -[[package]] -name = "derive_arbitrary" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", -] - -[[package]] -name = "encode_unicode" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "itoa" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" - -[[package]] -name = "js-sys" -version = "0.3.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "libc" -version = "0.2.170" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" - -[[package]] -name = "log" -version = "0.4.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" - -[[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "minicov" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" -dependencies = [ - "cc", - "walkdir", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" - -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", - "syn 2.0.98", -] - -[[package]] -name = "proc-macro2" -version = "1.0.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "ptr_meta" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" -dependencies = [ - "ptr_meta_derive", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "pure-rust-locales" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "869675ad2d7541aea90c6d88c81f46a7f4ea9af8cd0395d38f11a95126998a0d" -dependencies = [ - "defmt", -] - -[[package]] -name = "quote" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "regex-automata" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" - -[[package]] -name = "rend" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" -dependencies = [ - "bytecheck", -] - -[[package]] -name = "rkyv" -version = "0.7.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" -dependencies = [ - "bitvec", - "bytecheck", - "hashbrown", - "ptr_meta", - "rend", - "rkyv_derive", - "seahash", - "tinyvec", -] - -[[package]] -name = "rkyv_derive" -version = "0.7.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "rustversion" -version = "1.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" - -[[package]] -name = "ryu" -version = "1.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - -[[package]] -name = "serde" -version = "1.0.218" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.218" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", -] - -[[package]] -name = "serde_json" -version = "1.0.139" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "similar" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" -dependencies = [ - "bstr", - "unicode-segmentation", -] - -[[package]] -name = "similar-asserts" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b441962c817e33508847a22bd82f03a30cff43642dc2fae8b050566121eb9a" -dependencies = [ - "console", - "similar", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.98" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "thiserror" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", -] - -[[package]] -name = "tinyvec" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "unicode-ident" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" - -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.98", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" -dependencies = [ - "cfg-if", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wasm-bindgen-test" -version = "0.3.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66c8d5e33ca3b6d9fa3b4676d774c5778031d27a578c2b007f905acf816152c3" -dependencies = [ - "js-sys", - "minicov", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-bindgen-test-macro", -] - -[[package]] -name = "wasm-bindgen-test-macro" -version = "0.3.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", -] - -[[package]] -name = "web-sys" -version = "0.3.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "windows-bindgen" -version = "0.66.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b7ec123a4eadd44d1f44f76804316b477b2537abed9a2ab950b3c54afa1fcf" -dependencies = [ - "serde", - "serde_json", - "windows-threading", -] - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows-threading" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] diff --git a/chrono-0.4.44/Cargo.toml b/chrono-0.4.44/Cargo.toml deleted file mode 100644 index 754812a7e3..0000000000 --- a/chrono-0.4.44/Cargo.toml +++ /dev/null @@ -1,181 +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 are reading this file be aware that the original Cargo.toml -# will likely look very different (and much more reasonable). -# See Cargo.toml.orig for the original contents. - -[package] -edition = "2021" -rust-version = "1.62.0" -name = "chrono" -version = "0.4.44" -build = false -include = [ - "src/*", - "tests/*.rs", - "LICENSE.txt", - "CITATION.cff", -] -autolib = false -autobins = false -autoexamples = false -autotests = false -autobenches = false -description = "Date and time library for Rust" -homepage = "https://github.com/chronotope/chrono" -documentation = "https://docs.rs/chrono/" -readme = "README.md" -keywords = [ - "date", - "time", - "calendar", -] -categories = ["date-and-time"] -license = "MIT OR Apache-2.0" -repository = "https://github.com/chronotope/chrono" - -[package.metadata.docs.rs] -features = [ - "arbitrary", - "rkyv", - "serde", - "unstable-locales", -] -rustdoc-args = [ - "--cfg", - "docsrs", -] - -[package.metadata.playground] -features = ["serde"] - -[features] -__internal_bench = [] -alloc = [] -clock = [ - "winapi", - "iana-time-zone", - "now", -] -core-error = [] -default = [ - "clock", - "std", - "oldtime", - "wasmbind", -] -defmt = [ - "dep:defmt", - "pure-rust-locales?/defmt", -] -libc = [] -now = ["std"] -oldtime = [] -rkyv = [ - "dep:rkyv", - "rkyv/size_32", -] -rkyv-16 = [ - "dep:rkyv", - "rkyv?/size_16", -] -rkyv-32 = [ - "dep:rkyv", - "rkyv?/size_32", -] -rkyv-64 = [ - "dep:rkyv", - "rkyv?/size_64", -] -rkyv-validation = ["rkyv?/validation"] -std = ["alloc"] -unstable-locales = ["pure-rust-locales"] -wasmbind = [ - "wasm-bindgen", - "js-sys", -] -winapi = ["windows-link"] - -[lib] -name = "chrono" -path = "src/lib.rs" - -[[test]] -name = "dateutils" -path = "tests/dateutils.rs" - -[[test]] -name = "wasm" -path = "tests/wasm.rs" - -[[test]] -name = "win_bindings" -path = "tests/win_bindings.rs" - -[dependencies.arbitrary] -version = "1.0.0" -features = ["derive"] -optional = true - -[dependencies.defmt] -version = "1.0.1" -optional = true - -[dependencies.num-traits] -version = "0.2" -default-features = false - -[dependencies.pure-rust-locales] -version = "0.8.2" -optional = true - -[dependencies.rkyv] -version = "0.7.43" -optional = true -default-features = false - -[dependencies.serde] -version = "1.0.99" -optional = true -default-features = false - -[dev-dependencies.bincode] -version = "1.3.0" - -[dev-dependencies.serde_derive] -version = "1" -default-features = false - -[dev-dependencies.serde_json] -version = "1" - -[dev-dependencies.similar-asserts] -version = "1.6.1" - -[dev-dependencies.windows-bindgen] -version = "0.66" - -[target.'cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))'.dependencies.js-sys] -version = "0.3" -optional = true - -[target.'cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))'.dependencies.wasm-bindgen] -version = "0.2" -optional = true - -[target.'cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))'.dev-dependencies.wasm-bindgen-test] -version = "0.3" - -[target."cfg(unix)".dependencies.iana-time-zone] -version = "0.1.45" -features = ["fallback"] -optional = true - -[target."cfg(windows)".dependencies.windows-link] -version = "0.2" -optional = true diff --git a/chrono-0.4.44/Cargo.toml.orig b/chrono-0.4.44/Cargo.toml.orig deleted file mode 100644 index 50b6fa7590..0000000000 --- a/chrono-0.4.44/Cargo.toml.orig +++ /dev/null @@ -1,75 +0,0 @@ -[package] -name = "chrono" -version = "0.4.44" -description = "Date and time library for Rust" -homepage = "https://github.com/chronotope/chrono" -documentation = "https://docs.rs/chrono/" -repository = "https://github.com/chronotope/chrono" -keywords = ["date", "time", "calendar"] -categories = ["date-and-time"] -readme = "README.md" -license = "MIT OR Apache-2.0" -include = ["src/*", "tests/*.rs", "LICENSE.txt", "CITATION.cff"] -edition = "2021" -rust-version = "1.62.0" - -[lib] -name = "chrono" - -[features] -# Don't forget to adjust `ALL_NON_EXCLUSIVE_FEATURES` in CI scripts when adding a feature or an optional dependency. -default = ["clock", "std", "oldtime", "wasmbind"] -alloc = [] -defmt = ["dep:defmt", "pure-rust-locales?/defmt"] -libc = [] -winapi = ["windows-link"] -std = ["alloc"] -clock = ["winapi", "iana-time-zone", "now"] -now = ["std"] -core-error = [] -oldtime = [] -wasmbind = ["wasm-bindgen", "js-sys"] -unstable-locales = ["pure-rust-locales"] -# Note that rkyv-16, rkyv-32, and rkyv-64 are mutually exclusive. -rkyv = ["dep:rkyv", "rkyv/size_32"] -rkyv-16 = ["dep:rkyv", "rkyv?/size_16"] -rkyv-32 = ["dep:rkyv", "rkyv?/size_32"] -rkyv-64 = ["dep:rkyv", "rkyv?/size_64"] -rkyv-validation = ["rkyv?/validation"] -# Features for internal use only: -__internal_bench = [] - -[dependencies] -num-traits = { version = "0.2", default-features = false } -serde = { version = "1.0.99", default-features = false, optional = true } -pure-rust-locales = { version = "0.8.2", optional = true } -rkyv = { version = "0.7.43", optional = true, default-features = false } -arbitrary = { version = "1.0.0", features = ["derive"], optional = true } -defmt = { version = "1.0.1", optional = true } - -[target.'cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))'.dependencies] -wasm-bindgen = { version = "0.2", optional = true } -js-sys = { version = "0.3", optional = true } # contains FFI bindings for the JS Date API - -[target.'cfg(windows)'.dependencies] -windows-link = { version = "0.2", optional = true } - -[target.'cfg(unix)'.dependencies] -iana-time-zone = { version = "0.1.45", optional = true, features = ["fallback"] } - -[dev-dependencies] -serde_json = { version = "1" } -serde_derive = { version = "1", default-features = false } -similar-asserts = { version = "1.6.1" } -bincode = { version = "1.3.0" } -windows-bindgen = { version = "0.66" } # MSRV is 1.74 - -[target.'cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))'.dev-dependencies] -wasm-bindgen-test = "0.3" - -[package.metadata.docs.rs] -features = ["arbitrary", "rkyv", "serde", "unstable-locales"] -rustdoc-args = ["--cfg", "docsrs"] - -[package.metadata.playground] -features = ["serde"] diff --git a/chrono-0.4.44/LICENSE.txt b/chrono-0.4.44/LICENSE.txt deleted file mode 100644 index 480fdf7e47..0000000000 --- a/chrono-0.4.44/LICENSE.txt +++ /dev/null @@ -1,240 +0,0 @@ -Rust-chrono is dual-licensed under The MIT License [1] and -Apache 2.0 License [2]. Copyright (c) 2014--2026, Kang Seonghoon and -contributors. - -Nota Bene: This is same as the Rust Project's own license. - - -[1]: , which is reproduced below: - -~~~~ -The MIT License (MIT) - -Copyright (c) 2014, Kang Seonghoon. - -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. -~~~~ - - -[2]: , which is reproduced below: - -~~~~ - 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. -~~~~ - diff --git a/chrono-0.4.44/README.md b/chrono-0.4.44/README.md deleted file mode 100644 index 58e16f3c7f..0000000000 --- a/chrono-0.4.44/README.md +++ /dev/null @@ -1,92 +0,0 @@ -[Chrono][docsrs]: Timezone-aware date and time handling -======================================== - -[![Chrono GitHub Actions][gh-image]][gh-checks] -[![Chrono on crates.io][cratesio-image]][cratesio] -[![Chrono on docs.rs][docsrs-image]][docsrs] -[![Chat][discord-image]][discord] -[![codecov.io][codecov-img]][codecov-link] - -[gh-image]: https://github.com/chronotope/chrono/actions/workflows/test.yml/badge.svg?branch=main -[gh-checks]: https://github.com/chronotope/chrono/actions/workflows/test.yml?query=branch%3Amain -[cratesio-image]: https://img.shields.io/crates/v/chrono.svg -[cratesio]: https://crates.io/crates/chrono -[docsrs-image]: https://docs.rs/chrono/badge.svg -[docsrs]: https://docs.rs/chrono -[discord-image]: https://img.shields.io/discord/976380008299917365?logo=discord -[discord]: https://discord.gg/sXpav4PS7M -[codecov-img]: https://img.shields.io/codecov/c/github/chronotope/chrono?logo=codecov -[codecov-link]: https://codecov.io/gh/chronotope/chrono - -Chrono aims to provide all functionality needed to do correct operations on dates and times in the -[proleptic Gregorian calendar](https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar): - -* The [`DateTime`](https://docs.rs/chrono/latest/chrono/struct.DateTime.html) type is timezone-aware - by default, with separate timezone-naive types. -* Operations that may produce an invalid or ambiguous date and time return `Option` or - [`MappedLocalTime`](https://docs.rs/chrono/latest/chrono/offset/enum.MappedLocalTime.html). -* Configurable parsing and formatting with an `strftime` inspired date and time formatting syntax. -* The [`Local`](https://docs.rs/chrono/latest/chrono/offset/struct.Local.html) timezone works with - the current timezone of the OS. -* Types and operations are implemented to be reasonably efficient. - -Timezone data is not shipped with chrono by default to limit binary sizes. Use the companion crate -[Chrono-TZ](https://crates.io/crates/chrono-tz) or [`tzfile`](https://crates.io/crates/tzfile) for -full timezone support. - -## Documentation - -See [docs.rs](https://docs.rs/chrono/latest/chrono/) for the API reference. - -## Limitations - -* Only the proleptic Gregorian calendar (i.e. extended to support older dates) is supported. -* Date types are limited to about +/- 262,000 years from the common epoch. -* Time types are limited to nanosecond accuracy. -* Leap seconds can be represented, but Chrono does not fully support them. - See [Leap Second Handling](https://docs.rs/chrono/latest/chrono/naive/struct.NaiveTime.html#leap-second-handling). - -## Crate features - -Default features: - -* `alloc`: Enable features that depend on allocation (primarily string formatting). -* `std`: Enables functionality that depends on the standard library. This is a superset of `alloc` - and adds interoperation with standard library types and traits. -* `clock`: Enables reading the local timezone (`Local`). This is a superset of `now`. -* `now`: Enables reading the system time (`now`). -* `wasmbind`: Interface with the JS Date API for the `wasm32` target. - -Optional features: - -* `serde`: Enable serialization/deserialization via [serde]. -* `rkyv`: Deprecated, use the `rkyv-*` features. -* `rkyv-16`: Enable serialization/deserialization via [rkyv], using 16-bit integers for integral `*size` types. -* `rkyv-32`: Enable serialization/deserialization via [rkyv], using 32-bit integers for integral `*size` types. -* `rkyv-64`: Enable serialization/deserialization via [rkyv], using 64-bit integers for integral `*size` types. -* `rkyv-validation`: Enable rkyv validation support using `bytecheck`. -* `arbitrary`: Construct arbitrary instances of a type with the Arbitrary crate. -* `unstable-locales`: Enable localization. This adds various methods with a `_localized` suffix. - The implementation and API may change or even be removed in a patch release. Feedback welcome. -* `oldtime`: This feature no longer has any effect; it used to offer compatibility with the `time` 0.1 crate. - -Note: The `rkyv{,-16,-32,-64}` features are mutually exclusive. - -[serde]: https://github.com/serde-rs/serde -[rkyv]: https://github.com/rkyv/rkyv - -## Rust version requirements - -The Minimum Supported Rust Version (MSRV) is currently **Rust 1.62.0**. - -The MSRV is explicitly tested in CI. It may be bumped in minor releases, but this is not done -lightly. - -## License - -This project is licensed under either of - -* [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -* [MIT License](https://opensource.org/licenses/MIT) - -at your option. diff --git a/chrono-0.4.44/src/date.rs b/chrono-0.4.44/src/date.rs deleted file mode 100644 index 9356dd052b..0000000000 --- a/chrono-0.4.44/src/date.rs +++ /dev/null @@ -1,677 +0,0 @@ -// This is a part of Chrono. -// See README.md and LICENSE.txt for details. - -//! ISO 8601 calendar date with time zone. -#![allow(deprecated)] - -#[cfg(feature = "alloc")] -use core::borrow::Borrow; -use core::cmp::Ordering; -use core::ops::{Add, AddAssign, Sub, SubAssign}; -use core::{fmt, hash}; - -#[cfg(feature = "rkyv")] -use rkyv::{Archive, Deserialize, Serialize}; - -#[cfg(all(feature = "unstable-locales", feature = "alloc"))] -use crate::format::Locale; -#[cfg(feature = "alloc")] -use crate::format::{DelayedFormat, Item, StrftimeItems}; -use crate::naive::{IsoWeek, NaiveDate, NaiveTime}; -use crate::offset::{TimeZone, Utc}; -use crate::{DateTime, Datelike, TimeDelta, Weekday}; - -/// ISO 8601 calendar date with time zone. -/// -/// You almost certainly want to be using a [`NaiveDate`] instead of this type. -/// -/// This type primarily exists to aid in the construction of DateTimes that -/// have a timezone by way of the [`TimeZone`] datelike constructors (e.g. -/// [`TimeZone::ymd`]). -/// -/// This type should be considered ambiguous at best, due to the inherent lack -/// of precision required for the time zone resolution. -/// -/// There are some guarantees on the usage of `Date`: -/// -/// - If properly constructed via [`TimeZone::ymd`] and others without an error, -/// the corresponding local date should exist for at least a moment. -/// (It may still have a gap from the offset changes.) -/// -/// - The `TimeZone` is free to assign *any* [`Offset`](crate::offset::Offset) to the -/// local date, as long as that offset did occur in given day. -/// -/// For example, if `2015-03-08T01:59-08:00` is followed by `2015-03-08T03:00-07:00`, -/// it may produce either `2015-03-08-08:00` or `2015-03-08-07:00` -/// but *not* `2015-03-08+00:00` and others. -/// -/// - Once constructed as a full `DateTime`, [`DateTime::date`] and other associated -/// methods should return those for the original `Date`. For example, if `dt = -/// tz.ymd_opt(y,m,d).unwrap().hms(h,n,s)` were valid, `dt.date() == tz.ymd_opt(y,m,d).unwrap()`. -/// -/// - The date is timezone-agnostic up to one day (i.e. practically always), -/// so the local date and UTC date should be equal for most cases -/// even though the raw calculation between `NaiveDate` and `TimeDelta` may not. -#[deprecated(since = "0.4.23", note = "Use `NaiveDate` or `DateTime` instead")] -#[derive(Clone)] -#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))] -pub struct Date { - date: NaiveDate, - offset: Tz::Offset, -} - -/// The minimum possible `Date`. -#[allow(deprecated)] -#[deprecated(since = "0.4.20", note = "Use Date::MIN_UTC instead")] -pub const MIN_DATE: Date = Date::::MIN_UTC; -/// The maximum possible `Date`. -#[allow(deprecated)] -#[deprecated(since = "0.4.20", note = "Use Date::MAX_UTC instead")] -pub const MAX_DATE: Date = Date::::MAX_UTC; - -impl Date { - /// Makes a new `Date` with given *UTC* date and offset. - /// The local date should be constructed via the `TimeZone` trait. - #[inline] - #[must_use] - pub fn from_utc(date: NaiveDate, offset: Tz::Offset) -> Date { - Date { date, offset } - } - - /// Makes a new `DateTime` from the current date and given `NaiveTime`. - /// The offset in the current date is preserved. - /// - /// Returns `None` on invalid datetime. - #[inline] - #[must_use] - pub fn and_time(&self, time: NaiveTime) -> Option> { - let localdt = self.naive_local().and_time(time); - self.timezone().from_local_datetime(&localdt).single() - } - - /// Makes a new `DateTime` from the current date, hour, minute and second. - /// The offset in the current date is preserved. - /// - /// Panics on invalid hour, minute and/or second. - #[deprecated(since = "0.4.23", note = "Use and_hms_opt() instead")] - #[inline] - #[must_use] - pub fn and_hms(&self, hour: u32, min: u32, sec: u32) -> DateTime { - self.and_hms_opt(hour, min, sec).expect("invalid time") - } - - /// Makes a new `DateTime` from the current date, hour, minute and second. - /// The offset in the current date is preserved. - /// - /// Returns `None` on invalid hour, minute and/or second. - #[inline] - #[must_use] - pub fn and_hms_opt(&self, hour: u32, min: u32, sec: u32) -> Option> { - NaiveTime::from_hms_opt(hour, min, sec).and_then(|time| self.and_time(time)) - } - - /// Makes a new `DateTime` from the current date, hour, minute, second and millisecond. - /// The millisecond part can exceed 1,000 in order to represent the leap second. - /// The offset in the current date is preserved. - /// - /// Panics on invalid hour, minute, second and/or millisecond. - #[deprecated(since = "0.4.23", note = "Use and_hms_milli_opt() instead")] - #[inline] - #[must_use] - pub fn and_hms_milli(&self, hour: u32, min: u32, sec: u32, milli: u32) -> DateTime { - self.and_hms_milli_opt(hour, min, sec, milli).expect("invalid time") - } - - /// Makes a new `DateTime` from the current date, hour, minute, second and millisecond. - /// The millisecond part can exceed 1,000 in order to represent the leap second. - /// The offset in the current date is preserved. - /// - /// Returns `None` on invalid hour, minute, second and/or millisecond. - #[inline] - #[must_use] - pub fn and_hms_milli_opt( - &self, - hour: u32, - min: u32, - sec: u32, - milli: u32, - ) -> Option> { - NaiveTime::from_hms_milli_opt(hour, min, sec, milli).and_then(|time| self.and_time(time)) - } - - /// Makes a new `DateTime` from the current date, hour, minute, second and microsecond. - /// The microsecond part can exceed 1,000,000 in order to represent the leap second. - /// The offset in the current date is preserved. - /// - /// Panics on invalid hour, minute, second and/or microsecond. - #[deprecated(since = "0.4.23", note = "Use and_hms_micro_opt() instead")] - #[inline] - #[must_use] - pub fn and_hms_micro(&self, hour: u32, min: u32, sec: u32, micro: u32) -> DateTime { - self.and_hms_micro_opt(hour, min, sec, micro).expect("invalid time") - } - - /// Makes a new `DateTime` from the current date, hour, minute, second and microsecond. - /// The microsecond part can exceed 1,000,000 in order to represent the leap second. - /// The offset in the current date is preserved. - /// - /// Returns `None` on invalid hour, minute, second and/or microsecond. - #[inline] - #[must_use] - pub fn and_hms_micro_opt( - &self, - hour: u32, - min: u32, - sec: u32, - micro: u32, - ) -> Option> { - NaiveTime::from_hms_micro_opt(hour, min, sec, micro).and_then(|time| self.and_time(time)) - } - - /// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond. - /// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second. - /// The offset in the current date is preserved. - /// - /// Panics on invalid hour, minute, second and/or nanosecond. - #[deprecated(since = "0.4.23", note = "Use and_hms_nano_opt() instead")] - #[inline] - #[must_use] - pub fn and_hms_nano(&self, hour: u32, min: u32, sec: u32, nano: u32) -> DateTime { - self.and_hms_nano_opt(hour, min, sec, nano).expect("invalid time") - } - - /// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond. - /// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second. - /// The offset in the current date is preserved. - /// - /// Returns `None` on invalid hour, minute, second and/or nanosecond. - #[inline] - #[must_use] - pub fn and_hms_nano_opt( - &self, - hour: u32, - min: u32, - sec: u32, - nano: u32, - ) -> Option> { - NaiveTime::from_hms_nano_opt(hour, min, sec, nano).and_then(|time| self.and_time(time)) - } - - /// Makes a new `Date` for the next date. - /// - /// Panics when `self` is the last representable date. - #[deprecated(since = "0.4.23", note = "Use succ_opt() instead")] - #[inline] - #[must_use] - pub fn succ(&self) -> Date { - self.succ_opt().expect("out of bound") - } - - /// Makes a new `Date` for the next date. - /// - /// Returns `None` when `self` is the last representable date. - #[inline] - #[must_use] - pub fn succ_opt(&self) -> Option> { - self.date.succ_opt().map(|date| Date::from_utc(date, self.offset.clone())) - } - - /// Makes a new `Date` for the prior date. - /// - /// Panics when `self` is the first representable date. - #[deprecated(since = "0.4.23", note = "Use pred_opt() instead")] - #[inline] - #[must_use] - pub fn pred(&self) -> Date { - self.pred_opt().expect("out of bound") - } - - /// Makes a new `Date` for the prior date. - /// - /// Returns `None` when `self` is the first representable date. - #[inline] - #[must_use] - pub fn pred_opt(&self) -> Option> { - self.date.pred_opt().map(|date| Date::from_utc(date, self.offset.clone())) - } - - /// Retrieves an associated offset from UTC. - #[inline] - #[must_use] - pub fn offset(&self) -> &Tz::Offset { - &self.offset - } - - /// Retrieves an associated time zone. - #[inline] - #[must_use] - pub fn timezone(&self) -> Tz { - TimeZone::from_offset(&self.offset) - } - - /// Changes the associated time zone. - /// This does not change the actual `Date` (but will change the string representation). - #[inline] - #[must_use] - pub fn with_timezone(&self, tz: &Tz2) -> Date { - tz.from_utc_date(&self.date) - } - - /// Adds given `TimeDelta` to the current date. - /// - /// Returns `None` when it will result in overflow. - #[inline] - #[must_use] - pub fn checked_add_signed(self, rhs: TimeDelta) -> Option> { - let date = self.date.checked_add_signed(rhs)?; - Some(Date { date, offset: self.offset }) - } - - /// Subtracts given `TimeDelta` from the current date. - /// - /// Returns `None` when it will result in overflow. - #[inline] - #[must_use] - pub fn checked_sub_signed(self, rhs: TimeDelta) -> Option> { - let date = self.date.checked_sub_signed(rhs)?; - Some(Date { date, offset: self.offset }) - } - - /// Subtracts another `Date` from the current date. - /// Returns a `TimeDelta` of integral numbers. - /// - /// This does not overflow or underflow at all, - /// as all possible output fits in the range of `TimeDelta`. - #[inline] - #[must_use] - pub fn signed_duration_since(self, rhs: Date) -> TimeDelta { - self.date.signed_duration_since(rhs.date) - } - - /// Returns a view to the naive UTC date. - #[inline] - #[must_use] - pub fn naive_utc(&self) -> NaiveDate { - self.date - } - - /// Returns a view to the naive local date. - /// - /// This is technically the same as [`naive_utc`](#method.naive_utc) - /// because the offset is restricted to never exceed one day, - /// but provided for the consistency. - #[inline] - #[must_use] - pub fn naive_local(&self) -> NaiveDate { - self.date - } - - /// Returns the number of whole years from the given `base` until `self`. - #[must_use] - pub fn years_since(&self, base: Self) -> Option { - self.date.years_since(base.date) - } - - /// The minimum possible `Date`. - pub const MIN_UTC: Date = Date { date: NaiveDate::MIN, offset: Utc }; - /// The maximum possible `Date`. - pub const MAX_UTC: Date = Date { date: NaiveDate::MAX, offset: Utc }; -} - -/// Maps the local date to other date with given conversion function. -fn map_local(d: &Date, mut f: F) -> Option> -where - F: FnMut(NaiveDate) -> Option, -{ - f(d.naive_local()).and_then(|date| d.timezone().from_local_date(&date).single()) -} - -impl Date -where - Tz::Offset: fmt::Display, -{ - /// Formats the date with the specified formatting items. - #[cfg(feature = "alloc")] - #[inline] - #[must_use] - pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat - where - I: Iterator + Clone, - B: Borrow>, - { - DelayedFormat::new_with_offset(Some(self.naive_local()), None, &self.offset, items) - } - - /// Formats the date with the specified format string. - /// See the [`crate::format::strftime`] module - /// on the supported escape sequences. - #[cfg(feature = "alloc")] - #[inline] - #[must_use] - pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat> { - self.format_with_items(StrftimeItems::new(fmt)) - } - - /// Formats the date with the specified formatting items and locale. - #[cfg(all(feature = "unstable-locales", feature = "alloc"))] - #[inline] - #[must_use] - pub fn format_localized_with_items<'a, I, B>( - &self, - items: I, - locale: Locale, - ) -> DelayedFormat - where - I: Iterator + Clone, - B: Borrow>, - { - DelayedFormat::new_with_offset_and_locale( - Some(self.naive_local()), - None, - &self.offset, - items, - locale, - ) - } - - /// Formats the date with the specified format string and locale. - /// See the [`crate::format::strftime`] module - /// on the supported escape sequences. - #[cfg(all(feature = "unstable-locales", feature = "alloc"))] - #[inline] - #[must_use] - pub fn format_localized<'a>( - &self, - fmt: &'a str, - locale: Locale, - ) -> DelayedFormat> { - self.format_localized_with_items(StrftimeItems::new_with_locale(fmt, locale), locale) - } -} - -impl Datelike for Date { - #[inline] - fn year(&self) -> i32 { - self.naive_local().year() - } - #[inline] - fn month(&self) -> u32 { - self.naive_local().month() - } - #[inline] - fn month0(&self) -> u32 { - self.naive_local().month0() - } - #[inline] - fn day(&self) -> u32 { - self.naive_local().day() - } - #[inline] - fn day0(&self) -> u32 { - self.naive_local().day0() - } - #[inline] - fn ordinal(&self) -> u32 { - self.naive_local().ordinal() - } - #[inline] - fn ordinal0(&self) -> u32 { - self.naive_local().ordinal0() - } - #[inline] - fn weekday(&self) -> Weekday { - self.naive_local().weekday() - } - #[inline] - fn iso_week(&self) -> IsoWeek { - self.naive_local().iso_week() - } - - #[inline] - fn with_year(&self, year: i32) -> Option> { - map_local(self, |date| date.with_year(year)) - } - - #[inline] - fn with_month(&self, month: u32) -> Option> { - map_local(self, |date| date.with_month(month)) - } - - #[inline] - fn with_month0(&self, month0: u32) -> Option> { - map_local(self, |date| date.with_month0(month0)) - } - - #[inline] - fn with_day(&self, day: u32) -> Option> { - map_local(self, |date| date.with_day(day)) - } - - #[inline] - fn with_day0(&self, day0: u32) -> Option> { - map_local(self, |date| date.with_day0(day0)) - } - - #[inline] - fn with_ordinal(&self, ordinal: u32) -> Option> { - map_local(self, |date| date.with_ordinal(ordinal)) - } - - #[inline] - fn with_ordinal0(&self, ordinal0: u32) -> Option> { - map_local(self, |date| date.with_ordinal0(ordinal0)) - } -} - -// we need them as automatic impls cannot handle associated types -impl Copy for Date where ::Offset: Copy {} -unsafe impl Send for Date where ::Offset: Send {} - -impl PartialEq> for Date { - fn eq(&self, other: &Date) -> bool { - self.date == other.date - } -} - -impl Eq for Date {} - -impl PartialOrd for Date { - fn partial_cmp(&self, other: &Date) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Date { - fn cmp(&self, other: &Date) -> Ordering { - self.date.cmp(&other.date) - } -} - -impl hash::Hash for Date { - fn hash(&self, state: &mut H) { - self.date.hash(state) - } -} - -impl Add for Date { - type Output = Date; - - #[inline] - #[track_caller] - fn add(self, rhs: TimeDelta) -> Date { - self.checked_add_signed(rhs).expect("`Date + TimeDelta` overflowed") - } -} - -impl AddAssign for Date { - #[inline] - #[track_caller] - fn add_assign(&mut self, rhs: TimeDelta) { - self.date = self.date.checked_add_signed(rhs).expect("`Date + TimeDelta` overflowed"); - } -} - -impl Sub for Date { - type Output = Date; - - #[inline] - #[track_caller] - fn sub(self, rhs: TimeDelta) -> Date { - self.checked_sub_signed(rhs).expect("`Date - TimeDelta` overflowed") - } -} - -impl SubAssign for Date { - #[inline] - #[track_caller] - fn sub_assign(&mut self, rhs: TimeDelta) { - self.date = self.date.checked_sub_signed(rhs).expect("`Date - TimeDelta` overflowed"); - } -} - -impl Sub> for Date { - type Output = TimeDelta; - - #[inline] - fn sub(self, rhs: Date) -> TimeDelta { - self.signed_duration_since(rhs) - } -} - -impl fmt::Debug for Date { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.naive_local().fmt(f)?; - self.offset.fmt(f) - } -} - -impl fmt::Display for Date -where - Tz::Offset: fmt::Display, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.naive_local().fmt(f)?; - self.offset.fmt(f) - } -} - -#[cfg(feature = "defmt")] -impl defmt::Format for Date -where - Tz::Offset: defmt::Format, -{ - fn format(&self, fmt: defmt::Formatter) { - defmt::write!(fmt, "{}{}", self.naive_local(), self.offset); - } -} - -// Note that implementation of Arbitrary cannot be automatically derived for Date, due to -// the nontrivial bound ::Offset: Arbitrary. -#[cfg(all(feature = "arbitrary", feature = "std"))] -impl<'a, Tz> arbitrary::Arbitrary<'a> for Date -where - Tz: TimeZone, - ::Offset: arbitrary::Arbitrary<'a>, -{ - fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result> { - let date = NaiveDate::arbitrary(u)?; - let offset = ::Offset::arbitrary(u)?; - Ok(Date::from_utc(date, offset)) - } -} - -#[cfg(test)] -mod tests { - use super::Date; - - use crate::{FixedOffset, NaiveDate, TimeDelta, Utc}; - - #[cfg(feature = "clock")] - use crate::offset::{Local, TimeZone}; - - #[test] - #[cfg(feature = "clock")] - fn test_years_elapsed() { - const WEEKS_PER_YEAR: f32 = 52.1775; - - // This is always at least one year because 1 year = 52.1775 weeks. - let one_year_ago = Utc::today() - TimeDelta::weeks((WEEKS_PER_YEAR * 1.5).ceil() as i64); - // A bit more than 2 years. - let two_year_ago = Utc::today() - TimeDelta::weeks((WEEKS_PER_YEAR * 2.5).ceil() as i64); - - assert_eq!(Utc::today().years_since(one_year_ago), Some(1)); - assert_eq!(Utc::today().years_since(two_year_ago), Some(2)); - - // If the given DateTime is later than now, the function will always return 0. - let future = Utc::today() + TimeDelta::weeks(12); - assert_eq!(Utc::today().years_since(future), None); - } - - #[test] - fn test_date_add_assign() { - let naivedate = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap(); - let date = Date::::from_utc(naivedate, Utc); - let mut date_add = date; - - date_add += TimeDelta::days(5); - assert_eq!(date_add, date + TimeDelta::days(5)); - - let timezone = FixedOffset::east_opt(60 * 60).unwrap(); - let date = date.with_timezone(&timezone); - let date_add = date_add.with_timezone(&timezone); - - assert_eq!(date_add, date + TimeDelta::days(5)); - - let timezone = FixedOffset::west_opt(2 * 60 * 60).unwrap(); - let date = date.with_timezone(&timezone); - let date_add = date_add.with_timezone(&timezone); - - assert_eq!(date_add, date + TimeDelta::days(5)); - } - - #[test] - #[cfg(feature = "clock")] - fn test_date_add_assign_local() { - let naivedate = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap(); - - let date = Local.from_utc_date(&naivedate); - let mut date_add = date; - - date_add += TimeDelta::days(5); - assert_eq!(date_add, date + TimeDelta::days(5)); - } - - #[test] - fn test_date_sub_assign() { - let naivedate = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap(); - let date = Date::::from_utc(naivedate, Utc); - let mut date_sub = date; - - date_sub -= TimeDelta::days(5); - assert_eq!(date_sub, date - TimeDelta::days(5)); - - let timezone = FixedOffset::east_opt(60 * 60).unwrap(); - let date = date.with_timezone(&timezone); - let date_sub = date_sub.with_timezone(&timezone); - - assert_eq!(date_sub, date - TimeDelta::days(5)); - - let timezone = FixedOffset::west_opt(2 * 60 * 60).unwrap(); - let date = date.with_timezone(&timezone); - let date_sub = date_sub.with_timezone(&timezone); - - assert_eq!(date_sub, date - TimeDelta::days(5)); - } - - #[test] - #[cfg(feature = "clock")] - fn test_date_sub_assign_local() { - let naivedate = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap(); - - let date = Local.from_utc_date(&naivedate); - let mut date_sub = date; - - date_sub -= TimeDelta::days(5); - assert_eq!(date_sub, date - TimeDelta::days(5)); - } -} diff --git a/chrono-0.4.44/src/datetime/mod.rs b/chrono-0.4.44/src/datetime/mod.rs deleted file mode 100644 index 272976a015..0000000000 --- a/chrono-0.4.44/src/datetime/mod.rs +++ /dev/null @@ -1,2016 +0,0 @@ -// This is a part of Chrono. -// See README.md and LICENSE.txt for details. - -//! ISO 8601 date and time with time zone. - -#[cfg(all(feature = "alloc", not(feature = "std"), not(test)))] -use alloc::string::String; -use core::borrow::Borrow; -use core::cmp::Ordering; -use core::fmt::Write; -use core::ops::{Add, AddAssign, Sub, SubAssign}; -use core::time::Duration; -use core::{fmt, hash, str}; -#[cfg(feature = "std")] -use std::time::{SystemTime, UNIX_EPOCH}; - -#[allow(deprecated)] -use crate::Date; -#[cfg(all(feature = "unstable-locales", feature = "alloc"))] -use crate::format::Locale; -#[cfg(feature = "alloc")] -use crate::format::{DelayedFormat, SecondsFormat, write_rfc2822, write_rfc3339}; -use crate::format::{ - Fixed, Item, ParseError, ParseResult, Parsed, StrftimeItems, parse, parse_and_remainder, - parse_rfc3339, -}; -use crate::naive::{Days, IsoWeek, NaiveDate, NaiveDateTime, NaiveTime}; -#[cfg(feature = "clock")] -use crate::offset::Local; -use crate::offset::{FixedOffset, LocalResult, Offset, TimeZone, Utc}; -use crate::{Datelike, Months, TimeDelta, Timelike, Weekday}; -use crate::{expect, try_opt}; - -#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))] -use rkyv::{Archive, Deserialize, Serialize}; - -/// documented at re-export site -#[cfg(feature = "serde")] -pub(super) mod serde; - -#[cfg(test)] -mod tests; - -/// ISO 8601 combined date and time with time zone. -/// -/// There are some constructors implemented here (the `from_*` methods), but -/// the general-purpose constructors are all via the methods on the -/// [`TimeZone`](./offset/trait.TimeZone.html) implementations. -#[derive(Clone)] -#[cfg_attr( - any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"), - derive(Archive, Deserialize, Serialize), - archive(compare(PartialEq, PartialOrd)) -)] -#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] -pub struct DateTime { - datetime: NaiveDateTime, - offset: Tz::Offset, -} - -/// The minimum possible `DateTime`. -#[deprecated(since = "0.4.20", note = "Use DateTime::MIN_UTC instead")] -pub const MIN_DATETIME: DateTime = DateTime::::MIN_UTC; -/// The maximum possible `DateTime`. -#[deprecated(since = "0.4.20", note = "Use DateTime::MAX_UTC instead")] -pub const MAX_DATETIME: DateTime = DateTime::::MAX_UTC; - -impl DateTime { - /// Makes a new `DateTime` from its components: a `NaiveDateTime` in UTC and an `Offset`. - /// - /// This is a low-level method, intended for use cases such as deserializing a `DateTime` or - /// passing it through FFI. - /// - /// For regular use you will probably want to use a method such as - /// [`TimeZone::from_local_datetime`] or [`NaiveDateTime::and_local_timezone`] instead. - /// - /// # Example - /// - /// ``` - /// # #[cfg(feature = "clock")] { - /// use chrono::{DateTime, Local}; - /// - /// let dt = Local::now(); - /// // Get components - /// let naive_utc = dt.naive_utc(); - /// let offset = dt.offset().clone(); - /// // Serialize, pass through FFI... and recreate the `DateTime`: - /// let dt_new = DateTime::::from_naive_utc_and_offset(naive_utc, offset); - /// assert_eq!(dt, dt_new); - /// # } - /// ``` - #[inline] - #[must_use] - pub const fn from_naive_utc_and_offset( - datetime: NaiveDateTime, - offset: Tz::Offset, - ) -> DateTime { - DateTime { datetime, offset } - } - - /// Makes a new `DateTime` from its components: a `NaiveDateTime` in UTC and an `Offset`. - #[inline] - #[must_use] - #[deprecated( - since = "0.4.27", - note = "Use TimeZone::from_utc_datetime() or DateTime::from_naive_utc_and_offset instead" - )] - pub fn from_utc(datetime: NaiveDateTime, offset: Tz::Offset) -> DateTime { - DateTime { datetime, offset } - } - - /// Makes a new `DateTime` from a `NaiveDateTime` in *local* time and an `Offset`. - /// - /// # Panics - /// - /// Panics if the local datetime can't be converted to UTC because it would be out of range. - /// - /// This can happen if `datetime` is near the end of the representable range of `NaiveDateTime`, - /// and the offset from UTC pushes it beyond that. - #[inline] - #[must_use] - #[deprecated( - since = "0.4.27", - note = "Use TimeZone::from_local_datetime() or NaiveDateTime::and_local_timezone instead" - )] - pub fn from_local(datetime: NaiveDateTime, offset: Tz::Offset) -> DateTime { - let datetime_utc = datetime - offset.fix(); - - DateTime { datetime: datetime_utc, offset } - } - - /// Retrieves the date component with an associated timezone. - /// - /// Unless you are immediately planning on turning this into a `DateTime` - /// with the same timezone you should use the [`date_naive`](DateTime::date_naive) method. - /// - /// [`NaiveDate`] is a more well-defined type, and has more traits implemented on it, - /// so should be preferred to [`Date`] any time you truly want to operate on dates. - /// - /// # Panics - /// - /// [`DateTime`] internally stores the date and time in UTC with a [`NaiveDateTime`]. This - /// method will panic if the offset from UTC would push the local date outside of the - /// representable range of a [`Date`]. - #[inline] - #[deprecated(since = "0.4.23", note = "Use `date_naive()` instead")] - #[allow(deprecated)] - #[must_use] - pub fn date(&self) -> Date { - Date::from_utc(self.naive_local().date(), self.offset.clone()) - } - - /// Retrieves the date component. - /// - /// # Panics - /// - /// [`DateTime`] internally stores the date and time in UTC with a [`NaiveDateTime`]. This - /// method will panic if the offset from UTC would push the local date outside of the - /// representable range of a [`NaiveDate`]. - /// - /// # Example - /// - /// ``` - /// use chrono::prelude::*; - /// - /// let date: DateTime = Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap(); - /// let other: DateTime = - /// FixedOffset::east_opt(23).unwrap().with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap(); - /// assert_eq!(date.date_naive(), other.date_naive()); - /// ``` - #[inline] - #[must_use] - pub fn date_naive(&self) -> NaiveDate { - self.naive_local().date() - } - - /// Retrieves the time component. - #[inline] - #[must_use] - pub fn time(&self) -> NaiveTime { - self.datetime.time() + self.offset.fix() - } - - /// Returns the number of non-leap seconds since January 1, 1970 0:00:00 UTC - /// (aka "UNIX timestamp"). - /// - /// The reverse operation of creating a [`DateTime`] from a timestamp can be performed - /// using [`from_timestamp`](DateTime::from_timestamp) or [`TimeZone::timestamp_opt`]. - /// - /// ``` - /// use chrono::{DateTime, TimeZone, Utc}; - /// - /// let dt: DateTime = Utc.with_ymd_and_hms(2015, 5, 15, 0, 0, 0).unwrap(); - /// assert_eq!(dt.timestamp(), 1431648000); - /// - /// assert_eq!(DateTime::from_timestamp(dt.timestamp(), dt.timestamp_subsec_nanos()).unwrap(), dt); - /// ``` - #[inline] - #[must_use] - pub const fn timestamp(&self) -> i64 { - let gregorian_day = self.datetime.date().num_days_from_ce() as i64; - let seconds_from_midnight = self.datetime.time().num_seconds_from_midnight() as i64; - (gregorian_day - UNIX_EPOCH_DAY) * 86_400 + seconds_from_midnight - } - - /// Returns the number of non-leap-milliseconds since January 1, 1970 UTC. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, Utc}; - /// - /// let dt = NaiveDate::from_ymd_opt(1970, 1, 1) - /// .unwrap() - /// .and_hms_milli_opt(0, 0, 1, 444) - /// .unwrap() - /// .and_local_timezone(Utc) - /// .unwrap(); - /// assert_eq!(dt.timestamp_millis(), 1_444); - /// - /// let dt = NaiveDate::from_ymd_opt(2001, 9, 9) - /// .unwrap() - /// .and_hms_milli_opt(1, 46, 40, 555) - /// .unwrap() - /// .and_local_timezone(Utc) - /// .unwrap(); - /// assert_eq!(dt.timestamp_millis(), 1_000_000_000_555); - /// ``` - #[inline] - #[must_use] - pub const fn timestamp_millis(&self) -> i64 { - let as_ms = self.timestamp() * 1000; - as_ms + self.timestamp_subsec_millis() as i64 - } - - /// Returns the number of non-leap-microseconds since January 1, 1970 UTC. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, Utc}; - /// - /// let dt = NaiveDate::from_ymd_opt(1970, 1, 1) - /// .unwrap() - /// .and_hms_micro_opt(0, 0, 1, 444) - /// .unwrap() - /// .and_local_timezone(Utc) - /// .unwrap(); - /// assert_eq!(dt.timestamp_micros(), 1_000_444); - /// - /// let dt = NaiveDate::from_ymd_opt(2001, 9, 9) - /// .unwrap() - /// .and_hms_micro_opt(1, 46, 40, 555) - /// .unwrap() - /// .and_local_timezone(Utc) - /// .unwrap(); - /// assert_eq!(dt.timestamp_micros(), 1_000_000_000_000_555); - /// ``` - #[inline] - #[must_use] - pub const fn timestamp_micros(&self) -> i64 { - let as_us = self.timestamp() * 1_000_000; - as_us + self.timestamp_subsec_micros() as i64 - } - - /// Returns the number of non-leap-nanoseconds since January 1, 1970 UTC. - /// - /// # Panics - /// - /// An `i64` with nanosecond precision can span a range of ~584 years. This function panics on - /// an out of range `DateTime`. - /// - /// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:43.145224192 - /// and 2262-04-11T23:47:16.854775807. - #[deprecated(since = "0.4.31", note = "use `timestamp_nanos_opt()` instead")] - #[inline] - #[must_use] - pub const fn timestamp_nanos(&self) -> i64 { - expect( - self.timestamp_nanos_opt(), - "value can not be represented in a timestamp with nanosecond precision.", - ) - } - - /// Returns the number of non-leap-nanoseconds since January 1, 1970 UTC. - /// - /// # Errors - /// - /// An `i64` with nanosecond precision can span a range of ~584 years. This function returns - /// `None` on an out of range `DateTime`. - /// - /// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:43.145224192 - /// and 2262-04-11T23:47:16.854775807. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, Utc}; - /// - /// let dt = NaiveDate::from_ymd_opt(1970, 1, 1) - /// .unwrap() - /// .and_hms_nano_opt(0, 0, 1, 444) - /// .unwrap() - /// .and_local_timezone(Utc) - /// .unwrap(); - /// assert_eq!(dt.timestamp_nanos_opt(), Some(1_000_000_444)); - /// - /// let dt = NaiveDate::from_ymd_opt(2001, 9, 9) - /// .unwrap() - /// .and_hms_nano_opt(1, 46, 40, 555) - /// .unwrap() - /// .and_local_timezone(Utc) - /// .unwrap(); - /// assert_eq!(dt.timestamp_nanos_opt(), Some(1_000_000_000_000_000_555)); - /// - /// let dt = NaiveDate::from_ymd_opt(1677, 9, 21) - /// .unwrap() - /// .and_hms_nano_opt(0, 12, 43, 145_224_192) - /// .unwrap() - /// .and_local_timezone(Utc) - /// .unwrap(); - /// assert_eq!(dt.timestamp_nanos_opt(), Some(-9_223_372_036_854_775_808)); - /// - /// let dt = NaiveDate::from_ymd_opt(2262, 4, 11) - /// .unwrap() - /// .and_hms_nano_opt(23, 47, 16, 854_775_807) - /// .unwrap() - /// .and_local_timezone(Utc) - /// .unwrap(); - /// assert_eq!(dt.timestamp_nanos_opt(), Some(9_223_372_036_854_775_807)); - /// - /// let dt = NaiveDate::from_ymd_opt(1677, 9, 21) - /// .unwrap() - /// .and_hms_nano_opt(0, 12, 43, 145_224_191) - /// .unwrap() - /// .and_local_timezone(Utc) - /// .unwrap(); - /// assert_eq!(dt.timestamp_nanos_opt(), None); - /// - /// let dt = NaiveDate::from_ymd_opt(2262, 4, 11) - /// .unwrap() - /// .and_hms_nano_opt(23, 47, 16, 854_775_808) - /// .unwrap() - /// .and_local_timezone(Utc) - /// .unwrap(); - /// assert_eq!(dt.timestamp_nanos_opt(), None); - /// ``` - #[inline] - #[must_use] - pub const fn timestamp_nanos_opt(&self) -> Option { - let mut timestamp = self.timestamp(); - let mut subsec_nanos = self.timestamp_subsec_nanos() as i64; - // `(timestamp * 1_000_000_000) + subsec_nanos` may create a temporary that underflows while - // the final value can be represented as an `i64`. - // As workaround we converting the negative case to: - // `((timestamp + 1) * 1_000_000_000) + (ns - 1_000_000_000)`` - // - // Also see . - if timestamp < 0 { - subsec_nanos -= 1_000_000_000; - timestamp += 1; - } - try_opt!(timestamp.checked_mul(1_000_000_000)).checked_add(subsec_nanos) - } - - /// Returns the number of milliseconds since the last second boundary. - /// - /// In event of a leap second this may exceed 999. - #[inline] - #[must_use] - pub const fn timestamp_subsec_millis(&self) -> u32 { - self.timestamp_subsec_nanos() / 1_000_000 - } - - /// Returns the number of microseconds since the last second boundary. - /// - /// In event of a leap second this may exceed 999,999. - #[inline] - #[must_use] - pub const fn timestamp_subsec_micros(&self) -> u32 { - self.timestamp_subsec_nanos() / 1_000 - } - - /// Returns the number of nanoseconds since the last second boundary - /// - /// In event of a leap second this may exceed 999,999,999. - #[inline] - #[must_use] - pub const fn timestamp_subsec_nanos(&self) -> u32 { - self.datetime.time().nanosecond() - } - - /// Retrieves an associated offset from UTC. - #[inline] - #[must_use] - pub const fn offset(&self) -> &Tz::Offset { - &self.offset - } - - /// Retrieves an associated time zone. - #[inline] - #[must_use] - pub fn timezone(&self) -> Tz { - TimeZone::from_offset(&self.offset) - } - - /// Changes the associated time zone. - /// The returned `DateTime` references the same instant of time from the perspective of the - /// provided time zone. - #[inline] - #[must_use] - pub fn with_timezone(&self, tz: &Tz2) -> DateTime { - tz.from_utc_datetime(&self.datetime) - } - - /// Fix the offset from UTC to its current value, dropping the associated timezone information. - /// This is useful for converting a generic `DateTime` to `DateTime`. - #[inline] - #[must_use] - pub fn fixed_offset(&self) -> DateTime { - self.with_timezone(&self.offset().fix()) - } - - /// Turn this `DateTime` into a `DateTime`, dropping the offset and associated timezone - /// information. - #[inline] - #[must_use] - pub const fn to_utc(&self) -> DateTime { - DateTime { datetime: self.datetime, offset: Utc } - } - - /// Adds given `TimeDelta` to the current date and time. - /// - /// # Errors - /// - /// Returns `None` if the resulting date would be out of range. - #[inline] - #[must_use] - pub fn checked_add_signed(self, rhs: TimeDelta) -> Option> { - let datetime = self.datetime.checked_add_signed(rhs)?; - let tz = self.timezone(); - Some(tz.from_utc_datetime(&datetime)) - } - - /// Adds given `Months` to the current date and time. - /// - /// Uses the last day of the month if the day does not exist in the resulting month. - /// - /// See [`NaiveDate::checked_add_months`] for more details on behavior. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The local time at the resulting date does not exist or is ambiguous, for example during a - /// daylight saving time transition. - /// - The resulting UTC datetime would be out of range. - /// - The resulting local datetime would be out of range (unless `months` is zero). - #[must_use] - pub fn checked_add_months(self, months: Months) -> Option> { - // `NaiveDate::checked_add_months` has a fast path for `Months(0)` that does not validate - // the resulting date, with which we can return `Some` even for an out of range local - // datetime. - self.overflowing_naive_local() - .checked_add_months(months)? - .and_local_timezone(Tz::from_offset(&self.offset)) - .single() - } - - /// Subtracts given `TimeDelta` from the current date and time. - /// - /// # Errors - /// - /// Returns `None` if the resulting date would be out of range. - #[inline] - #[must_use] - pub fn checked_sub_signed(self, rhs: TimeDelta) -> Option> { - let datetime = self.datetime.checked_sub_signed(rhs)?; - let tz = self.timezone(); - Some(tz.from_utc_datetime(&datetime)) - } - - /// Subtracts given `Months` from the current date and time. - /// - /// Uses the last day of the month if the day does not exist in the resulting month. - /// - /// See [`NaiveDate::checked_sub_months`] for more details on behavior. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The local time at the resulting date does not exist or is ambiguous, for example during a - /// daylight saving time transition. - /// - The resulting UTC datetime would be out of range. - /// - The resulting local datetime would be out of range (unless `months` is zero). - #[must_use] - pub fn checked_sub_months(self, months: Months) -> Option> { - // `NaiveDate::checked_sub_months` has a fast path for `Months(0)` that does not validate - // the resulting date, with which we can return `Some` even for an out of range local - // datetime. - self.overflowing_naive_local() - .checked_sub_months(months)? - .and_local_timezone(Tz::from_offset(&self.offset)) - .single() - } - - /// Add a duration in [`Days`] to the date part of the `DateTime`. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The local time at the resulting date does not exist or is ambiguous, for example during a - /// daylight saving time transition. - /// - The resulting UTC datetime would be out of range. - /// - The resulting local datetime would be out of range (unless `days` is zero). - #[must_use] - pub fn checked_add_days(self, days: Days) -> Option { - if days == Days::new(0) { - return Some(self); - } - // `NaiveDate::add_days` has a fast path if the result remains within the same year, that - // does not validate the resulting date. This allows us to return `Some` even for an out of - // range local datetime when adding `Days(0)`. - self.overflowing_naive_local() - .checked_add_days(days) - .and_then(|dt| self.timezone().from_local_datetime(&dt).single()) - .filter(|dt| dt <= &DateTime::::MAX_UTC) - } - - /// Subtract a duration in [`Days`] from the date part of the `DateTime`. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The local time at the resulting date does not exist or is ambiguous, for example during a - /// daylight saving time transition. - /// - The resulting UTC datetime would be out of range. - /// - The resulting local datetime would be out of range (unless `days` is zero). - #[must_use] - pub fn checked_sub_days(self, days: Days) -> Option { - // `NaiveDate::add_days` has a fast path if the result remains within the same year, that - // does not validate the resulting date. This allows us to return `Some` even for an out of - // range local datetime when adding `Days(0)`. - self.overflowing_naive_local() - .checked_sub_days(days) - .and_then(|dt| self.timezone().from_local_datetime(&dt).single()) - .filter(|dt| dt >= &DateTime::::MIN_UTC) - } - - /// Subtracts another `DateTime` from the current date and time. - /// This does not overflow or underflow at all. - #[inline] - #[must_use] - pub fn signed_duration_since( - self, - rhs: impl Borrow>, - ) -> TimeDelta { - self.datetime.signed_duration_since(rhs.borrow().datetime) - } - - /// Returns a view to the naive UTC datetime. - #[inline] - #[must_use] - pub const fn naive_utc(&self) -> NaiveDateTime { - self.datetime - } - - /// Returns a view to the naive local datetime. - /// - /// # Panics - /// - /// [`DateTime`] internally stores the date and time in UTC with a [`NaiveDateTime`]. This - /// method will panic if the offset from UTC would push the local datetime outside of the - /// representable range of a [`NaiveDateTime`]. - #[inline] - #[must_use] - pub fn naive_local(&self) -> NaiveDateTime { - self.datetime - .checked_add_offset(self.offset.fix()) - .expect("Local time out of range for `NaiveDateTime`") - } - - /// Returns the naive local datetime. - /// - /// This makes use of the buffer space outside of the representable range of values of - /// `NaiveDateTime`. The result can be used as intermediate value, but should never be exposed - /// outside chrono. - #[inline] - #[must_use] - pub(crate) fn overflowing_naive_local(&self) -> NaiveDateTime { - self.datetime.overflowing_add_offset(self.offset.fix()) - } - - /// Retrieve the elapsed years from now to the given [`DateTime`]. - /// - /// # Errors - /// - /// Returns `None` if `base > self`. - #[must_use] - pub fn years_since(&self, base: Self) -> Option { - let mut years = self.year() - base.year(); - let earlier_time = - (self.month(), self.day(), self.time()) < (base.month(), base.day(), base.time()); - - years -= match earlier_time { - true => 1, - false => 0, - }; - - match years >= 0 { - true => Some(years as u32), - false => None, - } - } - - /// Returns an RFC 2822 date and time string such as `Tue, 1 Jul 2003 10:52:37 +0200`. - /// - /// # Panics - /// - /// Panics if the date can not be represented in this format: the year may not be negative and - /// can not have more than 4 digits. - #[cfg(feature = "alloc")] - #[must_use] - #[track_caller] - pub fn to_rfc2822(&self) -> String { - let mut result = String::with_capacity(32); - write_rfc2822(&mut result, self.overflowing_naive_local(), self.offset.fix()) - .expect("date cannot be represented by RFC 2822"); - result - } - - /// Returns an RFC 3339 and ISO 8601 date and time string such as `1996-12-19T16:39:57-08:00`. - #[cfg(feature = "alloc")] - #[must_use] - pub fn to_rfc3339(&self) -> String { - // For some reason a string with a capacity less than 32 is ca 20% slower when benchmarking. - let mut result = String::with_capacity(32); - let naive = self.overflowing_naive_local(); - let offset = self.offset.fix(); - write_rfc3339(&mut result, naive, offset, SecondsFormat::AutoSi, false) - .expect("writing rfc3339 datetime to string should never fail"); - result - } - - /// Return an RFC 3339 and ISO 8601 date and time string with subseconds - /// formatted as per `SecondsFormat`. - /// - /// If `use_z` is true and the timezone is UTC (offset 0), uses `Z` as - /// per [`Fixed::TimezoneOffsetColonZ`]. If `use_z` is false, uses - /// [`Fixed::TimezoneOffsetColon`] - /// - /// # Examples - /// - /// ```rust - /// # use chrono::{FixedOffset, SecondsFormat, TimeZone, NaiveDate}; - /// let dt = NaiveDate::from_ymd_opt(2018, 1, 26) - /// .unwrap() - /// .and_hms_micro_opt(18, 30, 9, 453_829) - /// .unwrap() - /// .and_utc(); - /// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Millis, false), "2018-01-26T18:30:09.453+00:00"); - /// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Millis, true), "2018-01-26T18:30:09.453Z"); - /// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Secs, true), "2018-01-26T18:30:09Z"); - /// - /// let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap(); - /// let dt = pst - /// .from_local_datetime( - /// &NaiveDate::from_ymd_opt(2018, 1, 26) - /// .unwrap() - /// .and_hms_micro_opt(10, 30, 9, 453_829) - /// .unwrap(), - /// ) - /// .unwrap(); - /// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Secs, true), "2018-01-26T10:30:09+08:00"); - /// ``` - #[cfg(feature = "alloc")] - #[must_use] - pub fn to_rfc3339_opts(&self, secform: SecondsFormat, use_z: bool) -> String { - let mut result = String::with_capacity(38); - write_rfc3339(&mut result, self.naive_local(), self.offset.fix(), secform, use_z) - .expect("writing rfc3339 datetime to string should never fail"); - result - } - - /// Set the time to a new fixed time on the existing date. - /// - /// # Errors - /// - /// Returns `LocalResult::None` if the datetime is at the edge of the representable range for a - /// `DateTime`, and `with_time` would push the value in UTC out of range. - /// - /// # Example - /// - /// ``` - /// # #[cfg(feature = "clock")] { - /// use chrono::{Local, NaiveTime}; - /// - /// let noon = NaiveTime::from_hms_opt(12, 0, 0).unwrap(); - /// let today_noon = Local::now().with_time(noon); - /// let today_midnight = Local::now().with_time(NaiveTime::MIN); - /// - /// assert_eq!(today_noon.single().unwrap().time(), noon); - /// assert_eq!(today_midnight.single().unwrap().time(), NaiveTime::MIN); - /// # } - /// ``` - #[must_use] - pub fn with_time(&self, time: NaiveTime) -> LocalResult { - self.timezone().from_local_datetime(&self.overflowing_naive_local().date().and_time(time)) - } - - /// The minimum possible `DateTime`. - pub const MIN_UTC: DateTime = DateTime { datetime: NaiveDateTime::MIN, offset: Utc }; - /// The maximum possible `DateTime`. - pub const MAX_UTC: DateTime = DateTime { datetime: NaiveDateTime::MAX, offset: Utc }; -} - -impl DateTime { - /// Makes a new `DateTime` from the number of non-leap seconds - /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp"). - /// - /// This is a convenience wrapper around [`DateTime::from_timestamp`], - /// which is useful in functions like [`Iterator::map`] to avoid a closure. - /// - /// This is guaranteed to round-trip with regard to [`timestamp`](DateTime::timestamp). - /// - /// If you need to create a `DateTime` with a [`TimeZone`] different from [`Utc`], use - /// [`TimeZone::timestamp_opt`] or [`DateTime::with_timezone`]; if you need to create a - /// `DateTime` with more precision, use [`DateTime::from_timestamp_micros`], - /// [`DateTime::from_timestamp_millis`], or [`DateTime::from_timestamp_nanos`]. - /// - /// # Errors - /// - /// Returns `None` on out-of-range number of seconds, - /// otherwise returns `Some(DateTime {...})`. - /// - /// # Examples - /// - /// Using [`Option::and_then`]: - /// - /// ``` - /// # use chrono::DateTime; - /// let maybe_timestamp: Option = Some(1431648000); - /// let maybe_dt = maybe_timestamp.and_then(DateTime::from_timestamp_secs); - /// - /// assert!(maybe_dt.is_some()); - /// assert_eq!(maybe_dt.unwrap().to_string(), "2015-05-15 00:00:00 UTC"); - /// ``` - /// - /// Using [`Iterator::map`]: - /// - /// ``` - /// # use chrono::{DateTime, Utc}; - /// let v = vec![i64::MIN, 1_000_000_000, 1_234_567_890, i64::MAX]; - /// let timestamps: Vec>> = v - /// .into_iter() - /// .map(DateTime::from_timestamp_secs) - /// .collect(); - /// - /// assert_eq!(vec![ - /// None, - /// Some(DateTime::parse_from_rfc3339("2001-09-09 01:46:40Z").unwrap().to_utc()), - /// Some(DateTime::parse_from_rfc3339("2009-02-13 23:31:30Z").unwrap().to_utc()), - /// None, - /// ], timestamps); - /// ``` - /// - #[inline] - #[must_use] - pub const fn from_timestamp_secs(secs: i64) -> Option { - Self::from_timestamp(secs, 0) - } - - /// Makes a new `DateTime` from the number of non-leap seconds - /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp") - /// and the number of nanoseconds since the last whole non-leap second. - /// - /// This is guaranteed to round-trip with regard to [`timestamp`](DateTime::timestamp) and - /// [`timestamp_subsec_nanos`](DateTime::timestamp_subsec_nanos). - /// - /// If you need to create a `DateTime` with a [`TimeZone`] different from [`Utc`], use - /// [`TimeZone::timestamp_opt`] or [`DateTime::with_timezone`]. - /// - /// The nanosecond part can exceed 1,000,000,000 in order to represent a - /// [leap second](NaiveTime#leap-second-handling), but only when `secs % 60 == 59`. - /// (The true "UNIX timestamp" cannot represent a leap second unambiguously.) - /// - /// # Errors - /// - /// Returns `None` on out-of-range number of seconds and/or - /// invalid nanosecond, otherwise returns `Some(DateTime {...})`. - /// - /// # Example - /// - /// ``` - /// use chrono::DateTime; - /// - /// let dt = DateTime::from_timestamp(1431648000, 0).expect("invalid timestamp"); - /// - /// assert_eq!(dt.to_string(), "2015-05-15 00:00:00 UTC"); - /// assert_eq!(DateTime::from_timestamp(dt.timestamp(), dt.timestamp_subsec_nanos()).unwrap(), dt); - /// ``` - #[inline] - #[must_use] - pub const fn from_timestamp(secs: i64, nsecs: u32) -> Option { - let days = secs.div_euclid(86_400) + UNIX_EPOCH_DAY; - let secs = secs.rem_euclid(86_400); - if days < i32::MIN as i64 || days > i32::MAX as i64 { - return None; - } - let date = try_opt!(NaiveDate::from_num_days_from_ce_opt(days as i32)); - let time = try_opt!(NaiveTime::from_num_seconds_from_midnight_opt(secs as u32, nsecs)); - Some(date.and_time(time).and_utc()) - } - - /// Makes a new `DateTime` from the number of non-leap milliseconds - /// since January 1, 1970 0:00:00.000 UTC (aka "UNIX timestamp"). - /// - /// This is guaranteed to round-trip with [`timestamp_millis`](DateTime::timestamp_millis). - /// - /// If you need to create a `DateTime` with a [`TimeZone`] different from [`Utc`], use - /// [`TimeZone::timestamp_millis_opt`] or [`DateTime::with_timezone`]. - /// - /// # Errors - /// - /// Returns `None` on out-of-range number of milliseconds, otherwise returns `Some(DateTime {...})`. - /// - /// # Example - /// - /// ``` - /// use chrono::DateTime; - /// - /// let dt = DateTime::from_timestamp_millis(947638923004).expect("invalid timestamp"); - /// - /// assert_eq!(dt.to_string(), "2000-01-12 01:02:03.004 UTC"); - /// assert_eq!(DateTime::from_timestamp_millis(dt.timestamp_millis()).unwrap(), dt); - /// ``` - #[inline] - #[must_use] - pub const fn from_timestamp_millis(millis: i64) -> Option { - let secs = millis.div_euclid(1000); - let nsecs = millis.rem_euclid(1000) as u32 * 1_000_000; - Self::from_timestamp(secs, nsecs) - } - - /// Creates a new `DateTime` from the number of non-leap microseconds - /// since January 1, 1970 0:00:00.000 UTC (aka "UNIX timestamp"). - /// - /// This is guaranteed to round-trip with [`timestamp_micros`](DateTime::timestamp_micros). - /// - /// If you need to create a `DateTime` with a [`TimeZone`] different from [`Utc`], use - /// [`TimeZone::timestamp_micros`] or [`DateTime::with_timezone`]. - /// - /// # Errors - /// - /// Returns `None` if the number of microseconds would be out of range for a `NaiveDateTime` - /// (more than ca. 262,000 years away from common era) - /// - /// # Example - /// - /// ``` - /// use chrono::DateTime; - /// - /// let timestamp_micros: i64 = 1662921288000000; // Sun, 11 Sep 2022 18:34:48 UTC - /// let dt = DateTime::from_timestamp_micros(timestamp_micros); - /// assert!(dt.is_some()); - /// assert_eq!(timestamp_micros, dt.expect("invalid timestamp").timestamp_micros()); - /// - /// // Negative timestamps (before the UNIX epoch) are supported as well. - /// let timestamp_micros: i64 = -2208936075000000; // Mon, 1 Jan 1900 14:38:45 UTC - /// let dt = DateTime::from_timestamp_micros(timestamp_micros); - /// assert!(dt.is_some()); - /// assert_eq!(timestamp_micros, dt.expect("invalid timestamp").timestamp_micros()); - /// ``` - #[inline] - #[must_use] - pub const fn from_timestamp_micros(micros: i64) -> Option { - let secs = micros.div_euclid(1_000_000); - let nsecs = micros.rem_euclid(1_000_000) as u32 * 1000; - Self::from_timestamp(secs, nsecs) - } - - /// Creates a new [`DateTime`] from the number of non-leap nanoseconds - /// since January 1, 1970 0:00:00.000 UTC (aka "UNIX timestamp"). - /// - /// This is guaranteed to round-trip with [`timestamp_nanos`](DateTime::timestamp_nanos). - /// - /// If you need to create a `DateTime` with a [`TimeZone`] different from [`Utc`], use - /// [`TimeZone::timestamp_nanos`] or [`DateTime::with_timezone`]. - /// - /// The UNIX epoch starts on midnight, January 1, 1970, UTC. - /// - /// An `i64` with nanosecond precision can span a range of ~584 years. Because all values can - /// be represented as a `DateTime` this method never fails. - /// - /// # Example - /// - /// ``` - /// use chrono::DateTime; - /// - /// let timestamp_nanos: i64 = 1662921288_000_000_000; // Sun, 11 Sep 2022 18:34:48 UTC - /// let dt = DateTime::from_timestamp_nanos(timestamp_nanos); - /// assert_eq!(timestamp_nanos, dt.timestamp_nanos_opt().unwrap()); - /// - /// // Negative timestamps (before the UNIX epoch) are supported as well. - /// let timestamp_nanos: i64 = -2208936075_000_000_000; // Mon, 1 Jan 1900 14:38:45 UTC - /// let dt = DateTime::from_timestamp_nanos(timestamp_nanos); - /// assert_eq!(timestamp_nanos, dt.timestamp_nanos_opt().unwrap()); - /// ``` - #[inline] - #[must_use] - pub const fn from_timestamp_nanos(nanos: i64) -> Self { - let secs = nanos.div_euclid(1_000_000_000); - let nsecs = nanos.rem_euclid(1_000_000_000) as u32; - expect(Self::from_timestamp(secs, nsecs), "timestamp in nanos is always in range") - } - - /// The Unix Epoch, 1970-01-01 00:00:00 UTC. - pub const UNIX_EPOCH: Self = - expect(NaiveDate::from_ymd_opt(1970, 1, 1), "").and_time(NaiveTime::MIN).and_utc(); -} - -impl Default for DateTime { - fn default() -> Self { - Utc.from_utc_datetime(&NaiveDateTime::default()) - } -} - -#[cfg(feature = "clock")] -impl Default for DateTime { - fn default() -> Self { - Local.from_utc_datetime(&NaiveDateTime::default()) - } -} - -impl Default for DateTime { - fn default() -> Self { - FixedOffset::west_opt(0).unwrap().from_utc_datetime(&NaiveDateTime::default()) - } -} - -/// Convert a `DateTime` instance into a `DateTime` instance. -impl From> for DateTime { - /// Convert this `DateTime` instance into a `DateTime` instance. - /// - /// Conversion is done via [`DateTime::with_timezone`]. Note that the converted value returned by - /// this will be created with a fixed timezone offset of 0. - fn from(src: DateTime) -> Self { - src.with_timezone(&FixedOffset::east_opt(0).unwrap()) - } -} - -/// Convert a `DateTime` instance into a `DateTime` instance. -#[cfg(feature = "clock")] -impl From> for DateTime { - /// Convert this `DateTime` instance into a `DateTime` instance. - /// - /// Conversion is performed via [`DateTime::with_timezone`], accounting for the difference in timezones. - fn from(src: DateTime) -> Self { - src.with_timezone(&Local) - } -} - -/// Convert a `DateTime` instance into a `DateTime` instance. -impl From> for DateTime { - /// Convert this `DateTime` instance into a `DateTime` instance. - /// - /// Conversion is performed via [`DateTime::with_timezone`], accounting for the timezone - /// difference. - fn from(src: DateTime) -> Self { - src.with_timezone(&Utc) - } -} - -/// Convert a `DateTime` instance into a `DateTime` instance. -#[cfg(feature = "clock")] -impl From> for DateTime { - /// Convert this `DateTime` instance into a `DateTime` instance. - /// - /// Conversion is performed via [`DateTime::with_timezone`]. Returns the equivalent value in local - /// time. - fn from(src: DateTime) -> Self { - src.with_timezone(&Local) - } -} - -/// Convert a `DateTime` instance into a `DateTime` instance. -#[cfg(feature = "clock")] -impl From> for DateTime { - /// Convert this `DateTime` instance into a `DateTime` instance. - /// - /// Conversion is performed via [`DateTime::with_timezone`], accounting for the difference in - /// timezones. - fn from(src: DateTime) -> Self { - src.with_timezone(&Utc) - } -} - -/// Convert a `DateTime` instance into a `DateTime` instance. -#[cfg(feature = "clock")] -impl From> for DateTime { - /// Convert this `DateTime` instance into a `DateTime` instance. - /// - /// Conversion is performed via [`DateTime::with_timezone`]. - fn from(src: DateTime) -> Self { - src.with_timezone(&src.offset().fix()) - } -} - -/// Maps the local datetime to other datetime with given conversion function. -fn map_local(dt: &DateTime, mut f: F) -> Option> -where - F: FnMut(NaiveDateTime) -> Option, -{ - f(dt.overflowing_naive_local()) - .and_then(|datetime| dt.timezone().from_local_datetime(&datetime).single()) - .filter(|dt| dt >= &DateTime::::MIN_UTC && dt <= &DateTime::::MAX_UTC) -} - -impl DateTime { - /// Parses an RFC 2822 date-and-time string into a `DateTime` value. - /// - /// This parses valid RFC 2822 datetime strings (such as `Tue, 1 Jul 2003 10:52:37 +0200`) - /// and returns a new [`DateTime`] instance with the parsed timezone as the [`FixedOffset`]. - /// - /// RFC 2822 is the internet message standard that specifies the representation of times in HTTP - /// and email headers. It is the 2001 revision of RFC 822, and is itself revised as RFC 5322 in - /// 2008. - /// - /// # Support for the obsolete date format - /// - /// - A 2-digit year is interpreted to be a year in 1950-2049. - /// - The standard allows comments and whitespace between many of the tokens. See [4.3] and - /// [Appendix A.5] - /// - Single letter 'military' time zone names are parsed as a `-0000` offset. - /// They were defined with the wrong sign in RFC 822 and corrected in RFC 2822. But because - /// the meaning is now ambiguous, the standard says they should be considered as `-0000` - /// unless there is out-of-band information confirming their meaning. - /// The exception is `Z`, which remains identical to `+0000`. - /// - /// [4.3]: https://www.rfc-editor.org/rfc/rfc2822#section-4.3 - /// [Appendix A.5]: https://www.rfc-editor.org/rfc/rfc2822#appendix-A.5 - /// - /// # Example - /// - /// ``` - /// # use chrono::{DateTime, FixedOffset, TimeZone}; - /// assert_eq!( - /// DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 GMT").unwrap(), - /// FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap() - /// ); - /// ``` - pub fn parse_from_rfc2822(s: &str) -> ParseResult> { - const ITEMS: &[Item<'static>] = &[Item::Fixed(Fixed::RFC2822)]; - let mut parsed = Parsed::new(); - parse(&mut parsed, s, ITEMS.iter())?; - parsed.to_datetime() - } - - /// Parses an RFC 3339 date-and-time string into a `DateTime` value. - /// - /// Parses all valid RFC 3339 values (as well as the subset of valid ISO 8601 values that are - /// also valid RFC 3339 date-and-time values) and returns a new [`DateTime`] with a - /// [`FixedOffset`] corresponding to the parsed timezone. While RFC 3339 values come in a wide - /// variety of shapes and sizes, `1996-12-19T16:39:57-08:00` is an example of the most commonly - /// encountered variety of RFC 3339 formats. - /// - /// Why isn't this named `parse_from_iso8601`? That's because ISO 8601 allows representing - /// values in a wide range of formats, only some of which represent actual date-and-time - /// instances (rather than periods, ranges, dates, or times). Some valid ISO 8601 values are - /// also simultaneously valid RFC 3339 values, but not all RFC 3339 values are valid ISO 8601 - /// values (or the other way around). - pub fn parse_from_rfc3339(s: &str) -> ParseResult> { - parse_rfc3339(s) - } - - /// Parses a string from a user-specified format into a `DateTime` value. - /// - /// Note that this method *requires a timezone* in the input string. See - /// [`NaiveDateTime::parse_from_str`](./naive/struct.NaiveDateTime.html#method.parse_from_str) - /// for a version that does not require a timezone in the to-be-parsed str. The returned - /// [`DateTime`] value will have a [`FixedOffset`] reflecting the parsed timezone. - /// - /// See the [`format::strftime` module](crate::format::strftime) for supported format - /// sequences. - /// - /// # Example - /// - /// ```rust - /// use chrono::{DateTime, FixedOffset, NaiveDate, TimeZone}; - /// - /// let dt = DateTime::parse_from_str("1983 Apr 13 12:09:14.274 +0000", "%Y %b %d %H:%M:%S%.3f %z"); - /// assert_eq!( - /// dt, - /// Ok(FixedOffset::east_opt(0) - /// .unwrap() - /// .from_local_datetime( - /// &NaiveDate::from_ymd_opt(1983, 4, 13) - /// .unwrap() - /// .and_hms_milli_opt(12, 9, 14, 274) - /// .unwrap() - /// ) - /// .unwrap()) - /// ); - /// ``` - pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult> { - let mut parsed = Parsed::new(); - parse(&mut parsed, s, StrftimeItems::new(fmt))?; - parsed.to_datetime() - } - - /// Parses a string from a user-specified format into a `DateTime` value, and a - /// slice with the remaining portion of the string. - /// - /// Note that this method *requires a timezone* in the input string. See - /// [`NaiveDateTime::parse_and_remainder`] for a version that does not - /// require a timezone in `s`. The returned [`DateTime`] value will have a [`FixedOffset`] - /// reflecting the parsed timezone. - /// - /// See the [`format::strftime` module](./format/strftime/index.html) for supported format - /// sequences. - /// - /// Similar to [`parse_from_str`](#method.parse_from_str). - /// - /// # Example - /// - /// ```rust - /// # use chrono::{DateTime, FixedOffset, TimeZone}; - /// let (datetime, remainder) = DateTime::parse_and_remainder( - /// "2015-02-18 23:16:09 +0200 trailing text", - /// "%Y-%m-%d %H:%M:%S %z", - /// ) - /// .unwrap(); - /// assert_eq!( - /// datetime, - /// FixedOffset::east_opt(2 * 3600).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap() - /// ); - /// assert_eq!(remainder, " trailing text"); - /// ``` - pub fn parse_and_remainder<'a>( - s: &'a str, - fmt: &str, - ) -> ParseResult<(DateTime, &'a str)> { - let mut parsed = Parsed::new(); - let remainder = parse_and_remainder(&mut parsed, s, StrftimeItems::new(fmt))?; - parsed.to_datetime().map(|d| (d, remainder)) - } -} - -impl DateTime -where - Tz::Offset: fmt::Display, -{ - /// Formats the combined date and time with the specified formatting items. - #[cfg(feature = "alloc")] - #[inline] - #[must_use] - pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat - where - I: Iterator + Clone, - B: Borrow>, - { - let local = self.overflowing_naive_local(); - DelayedFormat::new_with_offset(Some(local.date()), Some(local.time()), &self.offset, items) - } - - /// Formats the combined date and time per the specified format string. - /// - /// See the [`crate::format::strftime`] module for the supported escape sequences. - /// - /// # Example - /// ```rust - /// use chrono::prelude::*; - /// - /// let date_time: DateTime = Utc.with_ymd_and_hms(2017, 04, 02, 12, 50, 32).unwrap(); - /// let formatted = format!("{}", date_time.format("%d/%m/%Y %H:%M")); - /// assert_eq!(formatted, "02/04/2017 12:50"); - /// ``` - #[cfg(feature = "alloc")] - #[inline] - #[must_use] - pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat> { - self.format_with_items(StrftimeItems::new(fmt)) - } - - /// Formats the combined date and time with the specified formatting items and locale. - #[cfg(all(feature = "unstable-locales", feature = "alloc"))] - #[inline] - #[must_use] - pub fn format_localized_with_items<'a, I, B>( - &self, - items: I, - locale: Locale, - ) -> DelayedFormat - where - I: Iterator + Clone, - B: Borrow>, - { - let local = self.overflowing_naive_local(); - DelayedFormat::new_with_offset_and_locale( - Some(local.date()), - Some(local.time()), - &self.offset, - items, - locale, - ) - } - - /// Formats the combined date and time per the specified format string and - /// locale. - /// - /// See the [`crate::format::strftime`] module on the supported escape - /// sequences. - #[cfg(all(feature = "unstable-locales", feature = "alloc"))] - #[inline] - #[must_use] - pub fn format_localized<'a>( - &self, - fmt: &'a str, - locale: Locale, - ) -> DelayedFormat> { - self.format_localized_with_items(StrftimeItems::new_with_locale(fmt, locale), locale) - } -} - -impl Datelike for DateTime { - #[inline] - fn year(&self) -> i32 { - self.overflowing_naive_local().year() - } - #[inline] - fn month(&self) -> u32 { - self.overflowing_naive_local().month() - } - #[inline] - fn month0(&self) -> u32 { - self.overflowing_naive_local().month0() - } - #[inline] - fn day(&self) -> u32 { - self.overflowing_naive_local().day() - } - #[inline] - fn day0(&self) -> u32 { - self.overflowing_naive_local().day0() - } - #[inline] - fn ordinal(&self) -> u32 { - self.overflowing_naive_local().ordinal() - } - #[inline] - fn ordinal0(&self) -> u32 { - self.overflowing_naive_local().ordinal0() - } - #[inline] - fn weekday(&self) -> Weekday { - self.overflowing_naive_local().weekday() - } - #[inline] - fn iso_week(&self) -> IsoWeek { - self.overflowing_naive_local().iso_week() - } - - #[inline] - /// Makes a new `DateTime` with the year number changed, while keeping the same month and day. - /// - /// See also the [`NaiveDate::with_year`] method. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The resulting date does not exist (February 29 in a non-leap year). - /// - The local time at the resulting date does not exist or is ambiguous, for example during a - /// daylight saving time transition. - /// - The resulting UTC datetime would be out of range. - /// - The resulting local datetime would be out of range (unless the year remains the same). - fn with_year(&self, year: i32) -> Option> { - map_local(self, |dt| match dt.year() == year { - true => Some(dt), - false => dt.with_year(year), - }) - } - - /// Makes a new `DateTime` with the month number (starting from 1) changed. - /// - /// Don't combine multiple `Datelike::with_*` methods. The intermediate value may not exist. - /// - /// See also the [`NaiveDate::with_month`] method. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The resulting date does not exist (for example `month(4)` when day of the month is 31). - /// - The value for `month` is invalid. - /// - The local time at the resulting date does not exist or is ambiguous, for example during a - /// daylight saving time transition. - #[inline] - fn with_month(&self, month: u32) -> Option> { - map_local(self, |datetime| datetime.with_month(month)) - } - - /// Makes a new `DateTime` with the month number (starting from 0) changed. - /// - /// See also the [`NaiveDate::with_month0`] method. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The resulting date does not exist (for example `month0(3)` when day of the month is 31). - /// - The value for `month0` is invalid. - /// - The local time at the resulting date does not exist or is ambiguous, for example during a - /// daylight saving time transition. - #[inline] - fn with_month0(&self, month0: u32) -> Option> { - map_local(self, |datetime| datetime.with_month0(month0)) - } - - /// Makes a new `DateTime` with the day of month (starting from 1) changed. - /// - /// See also the [`NaiveDate::with_day`] method. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The resulting date does not exist (for example `day(31)` in April). - /// - The value for `day` is invalid. - /// - The local time at the resulting date does not exist or is ambiguous, for example during a - /// daylight saving time transition. - #[inline] - fn with_day(&self, day: u32) -> Option> { - map_local(self, |datetime| datetime.with_day(day)) - } - - /// Makes a new `DateTime` with the day of month (starting from 0) changed. - /// - /// See also the [`NaiveDate::with_day0`] method. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The resulting date does not exist (for example `day(30)` in April). - /// - The value for `day0` is invalid. - /// - The local time at the resulting date does not exist or is ambiguous, for example during a - /// daylight saving time transition. - #[inline] - fn with_day0(&self, day0: u32) -> Option> { - map_local(self, |datetime| datetime.with_day0(day0)) - } - - /// Makes a new `DateTime` with the day of year (starting from 1) changed. - /// - /// See also the [`NaiveDate::with_ordinal`] method. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The resulting date does not exist (`with_ordinal(366)` in a non-leap year). - /// - The value for `ordinal` is invalid. - /// - The local time at the resulting date does not exist or is ambiguous, for example during a - /// daylight saving time transition. - #[inline] - fn with_ordinal(&self, ordinal: u32) -> Option> { - map_local(self, |datetime| datetime.with_ordinal(ordinal)) - } - - /// Makes a new `DateTime` with the day of year (starting from 0) changed. - /// - /// See also the [`NaiveDate::with_ordinal0`] method. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The resulting date does not exist (`with_ordinal0(365)` in a non-leap year). - /// - The value for `ordinal0` is invalid. - /// - The local time at the resulting date does not exist or is ambiguous, for example during a - /// daylight saving time transition. - #[inline] - fn with_ordinal0(&self, ordinal0: u32) -> Option> { - map_local(self, |datetime| datetime.with_ordinal0(ordinal0)) - } -} - -impl Timelike for DateTime { - #[inline] - fn hour(&self) -> u32 { - self.overflowing_naive_local().hour() - } - #[inline] - fn minute(&self) -> u32 { - self.overflowing_naive_local().minute() - } - #[inline] - fn second(&self) -> u32 { - self.overflowing_naive_local().second() - } - #[inline] - fn nanosecond(&self) -> u32 { - self.overflowing_naive_local().nanosecond() - } - - /// Makes a new `DateTime` with the hour number changed. - /// - /// See also the [`NaiveTime::with_hour`] method. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The value for `hour` is invalid. - /// - The local time at the resulting date does not exist or is ambiguous, for example during a - /// daylight saving time transition. - #[inline] - fn with_hour(&self, hour: u32) -> Option> { - map_local(self, |datetime| datetime.with_hour(hour)) - } - - /// Makes a new `DateTime` with the minute number changed. - /// - /// See also the [`NaiveTime::with_minute`] method. - /// - /// # Errors - /// - /// - The value for `minute` is invalid. - /// - The local time at the resulting date does not exist or is ambiguous, for example during a - /// daylight saving time transition. - #[inline] - fn with_minute(&self, min: u32) -> Option> { - map_local(self, |datetime| datetime.with_minute(min)) - } - - /// Makes a new `DateTime` with the second number changed. - /// - /// As with the [`second`](#method.second) method, - /// the input range is restricted to 0 through 59. - /// - /// See also the [`NaiveTime::with_second`] method. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The value for `second` is invalid. - /// - The local time at the resulting date does not exist or is ambiguous, for example during a - /// daylight saving time transition. - #[inline] - fn with_second(&self, sec: u32) -> Option> { - map_local(self, |datetime| datetime.with_second(sec)) - } - - /// Makes a new `DateTime` with nanoseconds since the whole non-leap second changed. - /// - /// Returns `None` when the resulting `NaiveDateTime` would be invalid. - /// As with the [`NaiveDateTime::nanosecond`] method, - /// the input range can exceed 1,000,000,000 for leap seconds. - /// - /// See also the [`NaiveTime::with_nanosecond`] method. - /// - /// # Errors - /// - /// Returns `None` if `nanosecond >= 2,000,000,000`. - #[inline] - fn with_nanosecond(&self, nano: u32) -> Option> { - map_local(self, |datetime| datetime.with_nanosecond(nano)) - } -} - -// We don't store a field with the `Tz` type, so it doesn't need to influence whether `DateTime` can -// be `Copy`. Implement it manually if the two types we do have are `Copy`. -impl Copy for DateTime -where - ::Offset: Copy, - NaiveDateTime: Copy, -{ -} - -impl PartialEq> for DateTime { - fn eq(&self, other: &DateTime) -> bool { - self.datetime == other.datetime - } -} - -impl Eq for DateTime {} - -impl PartialOrd> for DateTime { - /// Compare two DateTimes based on their true time, ignoring time zones - /// - /// # Example - /// - /// ``` - /// use chrono::prelude::*; - /// - /// let earlier = Utc - /// .with_ymd_and_hms(2015, 5, 15, 2, 0, 0) - /// .unwrap() - /// .with_timezone(&FixedOffset::west_opt(1 * 3600).unwrap()); - /// let later = Utc - /// .with_ymd_and_hms(2015, 5, 15, 3, 0, 0) - /// .unwrap() - /// .with_timezone(&FixedOffset::west_opt(5 * 3600).unwrap()); - /// - /// assert_eq!(earlier.to_string(), "2015-05-15 01:00:00 -01:00"); - /// assert_eq!(later.to_string(), "2015-05-14 22:00:00 -05:00"); - /// - /// assert!(later > earlier); - /// ``` - fn partial_cmp(&self, other: &DateTime) -> Option { - self.datetime.partial_cmp(&other.datetime) - } -} - -impl Ord for DateTime { - fn cmp(&self, other: &DateTime) -> Ordering { - self.datetime.cmp(&other.datetime) - } -} - -impl hash::Hash for DateTime { - fn hash(&self, state: &mut H) { - self.datetime.hash(state) - } -} - -/// Add `TimeDelta` to `DateTime`. -/// -/// As a part of Chrono's [leap second handling], the addition assumes that **there is no leap -/// second ever**, except when the `NaiveDateTime` itself represents a leap second in which case -/// the assumption becomes that **there is exactly a single leap second ever**. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using [`DateTime::checked_add_signed`] to get an `Option` instead. -impl Add for DateTime { - type Output = DateTime; - - #[inline] - #[track_caller] - fn add(self, rhs: TimeDelta) -> DateTime { - self.checked_add_signed(rhs).expect("`DateTime + TimeDelta` overflowed") - } -} - -/// Add `std::time::Duration` to `DateTime`. -/// -/// As a part of Chrono's [leap second handling], the addition assumes that **there is no leap -/// second ever**, except when the `NaiveDateTime` itself represents a leap second in which case -/// the assumption becomes that **there is exactly a single leap second ever**. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using [`DateTime::checked_add_signed`] to get an `Option` instead. -impl Add for DateTime { - type Output = DateTime; - - #[inline] - #[track_caller] - fn add(self, rhs: Duration) -> DateTime { - let rhs = TimeDelta::from_std(rhs) - .expect("overflow converting from core::time::Duration to TimeDelta"); - self.checked_add_signed(rhs).expect("`DateTime + TimeDelta` overflowed") - } -} - -/// Add-assign `chrono::Duration` to `DateTime`. -/// -/// As a part of Chrono's [leap second handling], the addition assumes that **there is no leap -/// second ever**, except when the `NaiveDateTime` itself represents a leap second in which case -/// the assumption becomes that **there is exactly a single leap second ever**. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using [`DateTime::checked_add_signed`] to get an `Option` instead. -impl AddAssign for DateTime { - #[inline] - #[track_caller] - fn add_assign(&mut self, rhs: TimeDelta) { - let datetime = - self.datetime.checked_add_signed(rhs).expect("`DateTime + TimeDelta` overflowed"); - let tz = self.timezone(); - *self = tz.from_utc_datetime(&datetime); - } -} - -/// Add-assign `std::time::Duration` to `DateTime`. -/// -/// As a part of Chrono's [leap second handling], the addition assumes that **there is no leap -/// second ever**, except when the `NaiveDateTime` itself represents a leap second in which case -/// the assumption becomes that **there is exactly a single leap second ever**. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using [`DateTime::checked_add_signed`] to get an `Option` instead. -impl AddAssign for DateTime { - #[inline] - #[track_caller] - fn add_assign(&mut self, rhs: Duration) { - let rhs = TimeDelta::from_std(rhs) - .expect("overflow converting from core::time::Duration to TimeDelta"); - *self += rhs; - } -} - -/// Add `FixedOffset` to the datetime value of `DateTime` (offset remains unchanged). -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -impl Add for DateTime { - type Output = DateTime; - - #[inline] - #[track_caller] - fn add(mut self, rhs: FixedOffset) -> DateTime { - self.datetime = - self.naive_utc().checked_add_offset(rhs).expect("`DateTime + FixedOffset` overflowed"); - self - } -} - -/// Add `Months` to `DateTime`. -/// -/// The result will be clamped to valid days in the resulting month, see `checked_add_months` for -/// details. -/// -/// # Panics -/// -/// Panics if: -/// - The resulting date would be out of range. -/// - The local time at the resulting date does not exist or is ambiguous, for example during a -/// daylight saving time transition. -/// -/// Strongly consider using [`DateTime::checked_add_months`] to get an `Option` instead. -impl Add for DateTime { - type Output = DateTime; - - #[track_caller] - fn add(self, rhs: Months) -> Self::Output { - self.checked_add_months(rhs).expect("`DateTime + Months` out of range") - } -} - -/// Subtract `TimeDelta` from `DateTime`. -/// -/// This is the same as the addition with a negated `TimeDelta`. -/// -/// As a part of Chrono's [leap second handling] the subtraction assumes that **there is no leap -/// second ever**, except when the `DateTime` itself represents a leap second in which case -/// the assumption becomes that **there is exactly a single leap second ever**. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using [`DateTime::checked_sub_signed`] to get an `Option` instead. -impl Sub for DateTime { - type Output = DateTime; - - #[inline] - #[track_caller] - fn sub(self, rhs: TimeDelta) -> DateTime { - self.checked_sub_signed(rhs).expect("`DateTime - TimeDelta` overflowed") - } -} - -/// Subtract `std::time::Duration` from `DateTime`. -/// -/// As a part of Chrono's [leap second handling] the subtraction assumes that **there is no leap -/// second ever**, except when the `DateTime` itself represents a leap second in which case -/// the assumption becomes that **there is exactly a single leap second ever**. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using [`DateTime::checked_sub_signed`] to get an `Option` instead. -impl Sub for DateTime { - type Output = DateTime; - - #[inline] - #[track_caller] - fn sub(self, rhs: Duration) -> DateTime { - let rhs = TimeDelta::from_std(rhs) - .expect("overflow converting from core::time::Duration to TimeDelta"); - self.checked_sub_signed(rhs).expect("`DateTime - TimeDelta` overflowed") - } -} - -/// Subtract-assign `TimeDelta` from `DateTime`. -/// -/// This is the same as the addition with a negated `TimeDelta`. -/// -/// As a part of Chrono's [leap second handling], the addition assumes that **there is no leap -/// second ever**, except when the `DateTime` itself represents a leap second in which case -/// the assumption becomes that **there is exactly a single leap second ever**. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using [`DateTime::checked_sub_signed`] to get an `Option` instead. -impl SubAssign for DateTime { - #[inline] - #[track_caller] - fn sub_assign(&mut self, rhs: TimeDelta) { - let datetime = - self.datetime.checked_sub_signed(rhs).expect("`DateTime - TimeDelta` overflowed"); - let tz = self.timezone(); - *self = tz.from_utc_datetime(&datetime) - } -} - -/// Subtract-assign `std::time::Duration` from `DateTime`. -/// -/// As a part of Chrono's [leap second handling], the addition assumes that **there is no leap -/// second ever**, except when the `DateTime` itself represents a leap second in which case -/// the assumption becomes that **there is exactly a single leap second ever**. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using [`DateTime::checked_sub_signed`] to get an `Option` instead. -impl SubAssign for DateTime { - #[inline] - #[track_caller] - fn sub_assign(&mut self, rhs: Duration) { - let rhs = TimeDelta::from_std(rhs) - .expect("overflow converting from core::time::Duration to TimeDelta"); - *self -= rhs; - } -} - -/// Subtract `FixedOffset` from the datetime value of `DateTime` (offset remains unchanged). -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -impl Sub for DateTime { - type Output = DateTime; - - #[inline] - #[track_caller] - fn sub(mut self, rhs: FixedOffset) -> DateTime { - self.datetime = - self.naive_utc().checked_sub_offset(rhs).expect("`DateTime - FixedOffset` overflowed"); - self - } -} - -/// Subtract `Months` from `DateTime`. -/// -/// The result will be clamped to valid days in the resulting month, see -/// [`DateTime::checked_sub_months`] for details. -/// -/// # Panics -/// -/// Panics if: -/// - The resulting date would be out of range. -/// - The local time at the resulting date does not exist or is ambiguous, for example during a -/// daylight saving time transition. -/// -/// Strongly consider using [`DateTime::checked_sub_months`] to get an `Option` instead. -impl Sub for DateTime { - type Output = DateTime; - - #[track_caller] - fn sub(self, rhs: Months) -> Self::Output { - self.checked_sub_months(rhs).expect("`DateTime - Months` out of range") - } -} - -impl Sub> for DateTime { - type Output = TimeDelta; - - #[inline] - fn sub(self, rhs: DateTime) -> TimeDelta { - self.signed_duration_since(rhs) - } -} - -impl Sub<&DateTime> for DateTime { - type Output = TimeDelta; - - #[inline] - fn sub(self, rhs: &DateTime) -> TimeDelta { - self.signed_duration_since(rhs) - } -} - -/// Add `Days` to `NaiveDateTime`. -/// -/// # Panics -/// -/// Panics if: -/// - The resulting date would be out of range. -/// - The local time at the resulting date does not exist or is ambiguous, for example during a -/// daylight saving time transition. -/// -/// Strongly consider using `DateTime::checked_add_days` to get an `Option` instead. -impl Add for DateTime { - type Output = DateTime; - - #[track_caller] - fn add(self, days: Days) -> Self::Output { - self.checked_add_days(days).expect("`DateTime + Days` out of range") - } -} - -/// Subtract `Days` from `DateTime`. -/// -/// # Panics -/// -/// Panics if: -/// - The resulting date would be out of range. -/// - The local time at the resulting date does not exist or is ambiguous, for example during a -/// daylight saving time transition. -/// -/// Strongly consider using `DateTime::checked_sub_days` to get an `Option` instead. -impl Sub for DateTime { - type Output = DateTime; - - #[track_caller] - fn sub(self, days: Days) -> Self::Output { - self.checked_sub_days(days).expect("`DateTime - Days` out of range") - } -} - -impl fmt::Debug for DateTime { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.overflowing_naive_local().fmt(f)?; - self.offset.fmt(f) - } -} - -#[cfg(feature = "defmt")] -impl defmt::Format for DateTime -where - Tz::Offset: defmt::Format, -{ - fn format(&self, fmt: defmt::Formatter) { - defmt::write!(fmt, "{}{}", self.overflowing_naive_local(), self.offset); - } -} - -// `fmt::Debug` is hand implemented for the `rkyv::Archive` variant of `DateTime` because -// deriving a trait recursively does not propagate trait defined associated types with their own -// constraints: -// In our case `<::Offset as Archive>::Archived` -// cannot be formatted using `{:?}` because it doesn't implement `Debug`. -// See below for further discussion: -// * https://github.com/rust-lang/rust/issues/26925 -// * https://github.com/rkyv/rkyv/issues/333 -// * https://github.com/dtolnay/syn/issues/370 -#[cfg(feature = "rkyv-validation")] -impl fmt::Debug for ArchivedDateTime -where - Tz: Archive, - ::Archived: fmt::Debug, - <::Offset as Archive>::Archived: fmt::Debug, - ::Offset: fmt::Debug + Archive, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("ArchivedDateTime") - .field("datetime", &self.datetime) - .field("offset", &self.offset) - .finish() - } -} - -impl fmt::Display for DateTime -where - Tz::Offset: fmt::Display, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.overflowing_naive_local().fmt(f)?; - f.write_char(' ')?; - self.offset.fmt(f) - } -} - -/// Accepts a relaxed form of RFC3339. -/// A space or a 'T' are accepted as the separator between the date and time -/// parts. -/// -/// All of these examples are equivalent: -/// ``` -/// # use chrono::{DateTime, Utc}; -/// "2012-12-12T12:12:12Z".parse::>()?; -/// "2012-12-12 12:12:12Z".parse::>()?; -/// "2012-12-12 12:12:12+0000".parse::>()?; -/// "2012-12-12 12:12:12+00:00".parse::>()?; -/// # Ok::<(), chrono::ParseError>(()) -/// ``` -impl str::FromStr for DateTime { - type Err = ParseError; - - fn from_str(s: &str) -> ParseResult> { - s.parse::>().map(|dt| dt.with_timezone(&Utc)) - } -} - -/// Accepts a relaxed form of RFC3339. -/// A space or a 'T' are accepted as the separator between the date and time -/// parts. -/// -/// All of these examples are equivalent: -/// ``` -/// # use chrono::{DateTime, Local}; -/// "2012-12-12T12:12:12Z".parse::>()?; -/// "2012-12-12 12:12:12Z".parse::>()?; -/// "2012-12-12 12:12:12+0000".parse::>()?; -/// "2012-12-12 12:12:12+00:00".parse::>()?; -/// # Ok::<(), chrono::ParseError>(()) -/// ``` -#[cfg(feature = "clock")] -impl str::FromStr for DateTime { - type Err = ParseError; - - fn from_str(s: &str) -> ParseResult> { - s.parse::>().map(|dt| dt.with_timezone(&Local)) - } -} - -#[cfg(feature = "std")] -impl From for DateTime { - fn from(t: SystemTime) -> DateTime { - let (sec, nsec) = match t.duration_since(UNIX_EPOCH) { - Ok(dur) => (dur.as_secs() as i64, dur.subsec_nanos()), - Err(e) => { - // unlikely but should be handled - let dur = e.duration(); - let (sec, nsec) = (dur.as_secs() as i64, dur.subsec_nanos()); - if nsec == 0 { (-sec, 0) } else { (-sec - 1, 1_000_000_000 - nsec) } - } - }; - Utc.timestamp_opt(sec, nsec).unwrap() - } -} - -#[cfg(feature = "clock")] -impl From for DateTime { - fn from(t: SystemTime) -> DateTime { - DateTime::::from(t).with_timezone(&Local) - } -} - -#[cfg(feature = "std")] -impl From> for SystemTime { - fn from(dt: DateTime) -> SystemTime { - let sec = dt.timestamp(); - let nsec = dt.timestamp_subsec_nanos(); - if sec < 0 { - // unlikely but should be handled - UNIX_EPOCH - Duration::new(-sec as u64, 0) + Duration::new(0, nsec) - } else { - UNIX_EPOCH + Duration::new(sec as u64, nsec) - } - } -} - -#[cfg(all( - target_arch = "wasm32", - feature = "wasmbind", - not(any(target_os = "emscripten", target_os = "wasi", target_os = "linux")) -))] -impl From for DateTime { - fn from(date: js_sys::Date) -> DateTime { - DateTime::::from(&date) - } -} - -#[cfg(all( - target_arch = "wasm32", - feature = "wasmbind", - not(any(target_os = "emscripten", target_os = "wasi", target_os = "linux")) -))] -impl From<&js_sys::Date> for DateTime { - fn from(date: &js_sys::Date) -> DateTime { - Utc.timestamp_millis_opt(date.get_time() as i64).unwrap() - } -} - -#[cfg(all( - target_arch = "wasm32", - feature = "wasmbind", - not(any(target_os = "emscripten", target_os = "wasi", target_os = "linux")) -))] -impl From> for js_sys::Date { - /// Converts a `DateTime` to a JS `Date`. The resulting value may be lossy, - /// any values that have a millisecond timestamp value greater/less than ±8,640,000,000,000,000 - /// (April 20, 271821 BCE ~ September 13, 275760 CE) will become invalid dates in JS. - fn from(date: DateTime) -> js_sys::Date { - let js_millis = wasm_bindgen::JsValue::from_f64(date.timestamp_millis() as f64); - js_sys::Date::new(&js_millis) - } -} - -// Note that implementation of Arbitrary cannot be simply derived for DateTime, due to -// the nontrivial bound ::Offset: Arbitrary. -#[cfg(all(feature = "arbitrary", feature = "std"))] -impl<'a, Tz> arbitrary::Arbitrary<'a> for DateTime -where - Tz: TimeZone, - ::Offset: arbitrary::Arbitrary<'a>, -{ - fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result> { - let datetime = NaiveDateTime::arbitrary(u)?; - let offset = ::Offset::arbitrary(u)?; - Ok(DateTime::from_naive_utc_and_offset(datetime, offset)) - } -} - -/// Number of days between January 1, 1970 and December 31, 1 BCE which we define to be day 0. -/// 4 full leap year cycles until December 31, 1600 4 * 146097 = 584388 -/// 1 day until January 1, 1601 1 -/// 369 years until January 1, 1970 369 * 365 = 134685 -/// of which floor(369 / 4) are leap years floor(369 / 4) = 92 -/// except for 1700, 1800 and 1900 -3 + -/// -------- -/// 719163 -pub(crate) const UNIX_EPOCH_DAY: i64 = 719_163; diff --git a/chrono-0.4.44/src/datetime/serde.rs b/chrono-0.4.44/src/datetime/serde.rs deleted file mode 100644 index bc00d674d0..0000000000 --- a/chrono-0.4.44/src/datetime/serde.rs +++ /dev/null @@ -1,1351 +0,0 @@ -use core::fmt; -use serde::{de, ser}; - -use super::DateTime; -use crate::format::{SecondsFormat, write_rfc3339}; -#[cfg(feature = "clock")] -use crate::offset::Local; -use crate::offset::{FixedOffset, Offset, TimeZone, Utc}; - -#[doc(hidden)] -#[derive(Debug)] -pub struct SecondsTimestampVisitor; - -#[doc(hidden)] -#[derive(Debug)] -pub struct NanoSecondsTimestampVisitor; - -#[doc(hidden)] -#[derive(Debug)] -pub struct MicroSecondsTimestampVisitor; - -#[doc(hidden)] -#[derive(Debug)] -pub struct MilliSecondsTimestampVisitor; - -/// Serialize to an RFC 3339 formatted string -/// -/// As an extension to RFC 3339 this can serialize `DateTime`s outside the range of 0-9999 years -/// using an ISO 8601 syntax (which prepends an `-` or `+`). -/// -/// See [the `serde` module](crate::serde) for alternate serializations. -impl ser::Serialize for DateTime { - fn serialize(&self, serializer: S) -> Result - where - S: ser::Serializer, - { - struct FormatIso8601<'a, Tz: TimeZone> { - inner: &'a DateTime, - } - - impl fmt::Display for FormatIso8601<'_, Tz> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let naive = self.inner.naive_local(); - let offset = self.inner.offset.fix(); - write_rfc3339(f, naive, offset, SecondsFormat::AutoSi, true) - } - } - - serializer.collect_str(&FormatIso8601 { inner: self }) - } -} - -struct DateTimeVisitor; - -impl de::Visitor<'_> for DateTimeVisitor { - type Value = DateTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("an RFC 3339 formatted date and time string") - } - - fn visit_str(self, value: &str) -> Result - where - E: de::Error, - { - value.parse().map_err(E::custom) - } -} - -/// Deserialize an RFC 3339 formatted string into a `DateTime` -/// -/// As an extension to RFC 3339 this can deserialize to `DateTime`s outside the range of 0-9999 -/// years using an ISO 8601 syntax (which prepends an `-` or `+`). -/// -/// See [the `serde` module](crate::serde) for alternate deserialization formats. -impl<'de> de::Deserialize<'de> for DateTime { - fn deserialize(deserializer: D) -> Result - where - D: de::Deserializer<'de>, - { - deserializer.deserialize_str(DateTimeVisitor) - } -} - -/// Deserialize an RFC 3339 formatted string into a `DateTime` -/// -/// If the value contains an offset from UTC that is not zero, the value will be converted to UTC. -/// -/// As an extension to RFC 3339 this can deserialize to `DateTime`s outside the range of 0-9999 -/// years using an ISO 8601 syntax (which prepends an `-` or `+`). -/// -/// See [the `serde` module](crate::serde) for alternate deserialization formats. -impl<'de> de::Deserialize<'de> for DateTime { - fn deserialize(deserializer: D) -> Result - where - D: de::Deserializer<'de>, - { - deserializer.deserialize_str(DateTimeVisitor).map(|dt| dt.with_timezone(&Utc)) - } -} - -/// Deserialize an RFC 3339 formatted string into a `DateTime` -/// -/// The value will remain the same instant in UTC, but the offset will be recalculated to match -/// that of the `Local` platform time zone. -/// -/// As an extension to RFC 3339 this can deserialize to `DateTime`s outside the range of 0-9999 -/// years using an ISO 8601 syntax (which prepends an `-` or `+`). -/// -/// See [the `serde` module](crate::serde) for alternate deserialization formats. -#[cfg(feature = "clock")] -impl<'de> de::Deserialize<'de> for DateTime { - fn deserialize(deserializer: D) -> Result - where - D: de::Deserializer<'de>, - { - deserializer.deserialize_str(DateTimeVisitor).map(|dt| dt.with_timezone(&Local)) - } -} - -/// Ser/de to/from timestamps in nanoseconds -/// -/// Intended for use with `serde`'s `with` attribute. -/// -/// # Example: -/// -/// ```rust -/// # use chrono::{DateTime, Utc, NaiveDate}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::serde::ts_nanoseconds; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_nanoseconds")] -/// time: DateTime, -/// } -/// -/// let time = NaiveDate::from_ymd_opt(2018, 5, 17) -/// .unwrap() -/// .and_hms_nano_opt(02, 04, 59, 918355733) -/// .unwrap() -/// .and_utc(); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_nanoseconds { - use core::fmt; - use serde::{de, ser}; - - use crate::serde::invalid_ts; - use crate::{DateTime, Utc}; - - use super::NanoSecondsTimestampVisitor; - - /// Serialize a UTC datetime into an integer number of nanoseconds since the epoch - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Errors - /// - /// An `i64` with nanosecond precision can span a range of ~584 years. This function returns an - /// error on an out of range `DateTime`. - /// - /// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:44.0 and - /// 2262-04-11T23:47:16.854775804. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, Utc, NaiveDate}; - /// # use serde_derive::Serialize; - /// use chrono::serde::ts_nanoseconds::serialize as to_nano_ts; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_nano_ts")] - /// time: DateTime, - /// } - /// - /// let my_s = S { - /// time: NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_nano_opt(02, 04, 59, 918355733) - /// .unwrap() - /// .and_utc(), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(dt: &DateTime, serializer: S) -> Result - where - S: ser::Serializer, - { - serializer.serialize_i64(dt.timestamp_nanos_opt().ok_or(ser::Error::custom( - "value out of range for a timestamp with nanosecond precision", - ))?) - } - - /// Deserialize a [`DateTime`] from a nanosecond timestamp - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, TimeZone, Utc}; - /// # use serde_derive::Deserialize; - /// use chrono::serde::ts_nanoseconds::deserialize as from_nano_ts; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_nano_ts")] - /// time: DateTime, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?; - /// assert_eq!(my_s, S { time: Utc.timestamp_opt(1526522699, 918355733).unwrap() }); - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// assert_eq!(my_s, S { time: Utc.timestamp_opt(-1, 999_999_999).unwrap() }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(NanoSecondsTimestampVisitor) - } - - impl de::Visitor<'_> for NanoSecondsTimestampVisitor { - type Value = DateTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in nanoseconds") - } - - /// Deserialize a timestamp in nanoseconds since the epoch - fn visit_i64(self, value: i64) -> Result - where - E: de::Error, - { - Ok(DateTime::from_timestamp_nanos(value)) - } - - /// Deserialize a timestamp in nanoseconds since the epoch - fn visit_u64(self, value: u64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp((value / 1_000_000_000) as i64, (value % 1_000_000_000) as u32) - .ok_or_else(|| invalid_ts(value)) - } - } -} - -/// Ser/de to/from optional timestamps in nanoseconds -/// -/// Intended for use with `serde`'s `with` attribute. -/// -/// # Example: -/// -/// ```rust -/// # use chrono::{DateTime, Utc, NaiveDate}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::serde::ts_nanoseconds_option; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_nanoseconds_option")] -/// time: Option>, -/// } -/// -/// let time = Some( -/// NaiveDate::from_ymd_opt(2018, 5, 17) -/// .unwrap() -/// .and_hms_nano_opt(02, 04, 59, 918355733) -/// .unwrap() -/// .and_utc(), -/// ); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_nanoseconds_option { - use core::fmt; - use serde::{de, ser}; - - use crate::{DateTime, Utc}; - - use super::NanoSecondsTimestampVisitor; - - /// Serialize a UTC datetime into an integer number of nanoseconds since the epoch or none - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Errors - /// - /// An `i64` with nanosecond precision can span a range of ~584 years. This function returns an - /// error on an out of range `DateTime`. - /// - /// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:44.0 and - /// 2262-04-11T23:47:16.854775804. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, Utc, NaiveDate}; - /// # use serde_derive::Serialize; - /// use chrono::serde::ts_nanoseconds_option::serialize as to_nano_tsopt; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_nano_tsopt")] - /// time: Option>, - /// } - /// - /// let my_s = S { - /// time: Some( - /// NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_nano_opt(02, 04, 59, 918355733) - /// .unwrap() - /// .and_utc(), - /// ), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(opt: &Option>, serializer: S) -> Result - where - S: ser::Serializer, - { - match *opt { - Some(ref dt) => serializer.serialize_some(&dt.timestamp_nanos_opt().ok_or( - ser::Error::custom("value out of range for a timestamp with nanosecond precision"), - )?), - None => serializer.serialize_none(), - } - } - - /// Deserialize a `DateTime` from a nanosecond timestamp or none - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, TimeZone, Utc}; - /// # use serde_derive::Deserialize; - /// use chrono::serde::ts_nanoseconds_option::deserialize as from_nano_tsopt; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_nano_tsopt")] - /// time: Option>, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?; - /// assert_eq!(my_s, S { time: Utc.timestamp_opt(1526522699, 918355733).single() }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result>, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_option(OptionNanoSecondsTimestampVisitor) - } - - struct OptionNanoSecondsTimestampVisitor; - - impl<'de> de::Visitor<'de> for OptionNanoSecondsTimestampVisitor { - type Value = Option>; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in nanoseconds or none") - } - - /// Deserialize a timestamp in nanoseconds since the epoch - fn visit_some(self, d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(NanoSecondsTimestampVisitor).map(Some) - } - - /// Deserialize a timestamp in nanoseconds since the epoch - fn visit_none(self) -> Result - where - E: de::Error, - { - Ok(None) - } - - /// Deserialize a timestamp in nanoseconds since the epoch - fn visit_unit(self) -> Result - where - E: de::Error, - { - Ok(None) - } - } -} - -/// Ser/de to/from timestamps in microseconds -/// -/// Intended for use with `serde`'s `with` attribute. -/// -/// # Example: -/// -/// ```rust -/// # use chrono::{DateTime, Utc, NaiveDate}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::serde::ts_microseconds; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_microseconds")] -/// time: DateTime, -/// } -/// -/// let time = NaiveDate::from_ymd_opt(2018, 5, 17) -/// .unwrap() -/// .and_hms_micro_opt(02, 04, 59, 918355) -/// .unwrap() -/// .and_utc(); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918355}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_microseconds { - use core::fmt; - use serde::{de, ser}; - - use crate::serde::invalid_ts; - use crate::{DateTime, Utc}; - - use super::MicroSecondsTimestampVisitor; - - /// Serialize a UTC datetime into an integer number of microseconds since the epoch - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, Utc, NaiveDate}; - /// # use serde_derive::Serialize; - /// use chrono::serde::ts_microseconds::serialize as to_micro_ts; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_micro_ts")] - /// time: DateTime, - /// } - /// - /// let my_s = S { - /// time: NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_micro_opt(02, 04, 59, 918355) - /// .unwrap() - /// .and_utc(), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918355}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(dt: &DateTime, serializer: S) -> Result - where - S: ser::Serializer, - { - serializer.serialize_i64(dt.timestamp_micros()) - } - - /// Deserialize a `DateTime` from a microsecond timestamp - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, TimeZone, Utc}; - /// # use serde_derive::Deserialize; - /// use chrono::serde::ts_microseconds::deserialize as from_micro_ts; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_micro_ts")] - /// time: DateTime, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355 }"#)?; - /// assert_eq!(my_s, S { time: Utc.timestamp_opt(1526522699, 918355000).unwrap() }); - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// assert_eq!(my_s, S { time: Utc.timestamp_opt(-1, 999_999_000).unwrap() }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(MicroSecondsTimestampVisitor) - } - - impl de::Visitor<'_> for MicroSecondsTimestampVisitor { - type Value = DateTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in microseconds") - } - - /// Deserialize a timestamp in milliseconds since the epoch - fn visit_i64(self, value: i64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp_micros(value).ok_or_else(|| invalid_ts(value)) - } - - /// Deserialize a timestamp in milliseconds since the epoch - fn visit_u64(self, value: u64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp( - (value / 1_000_000) as i64, - ((value % 1_000_000) * 1_000) as u32, - ) - .ok_or_else(|| invalid_ts(value)) - } - } -} - -/// Ser/de to/from optional timestamps in microseconds -/// -/// Intended for use with `serde`'s `with` attribute. -/// -/// # Example: -/// -/// ```rust -/// # use chrono::{DateTime, Utc, NaiveDate}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::serde::ts_microseconds_option; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_microseconds_option")] -/// time: Option>, -/// } -/// -/// let time = Some( -/// NaiveDate::from_ymd_opt(2018, 5, 17) -/// .unwrap() -/// .and_hms_micro_opt(02, 04, 59, 918355) -/// .unwrap() -/// .and_utc(), -/// ); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918355}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_microseconds_option { - use core::fmt; - use serde::{de, ser}; - - use super::MicroSecondsTimestampVisitor; - use crate::{DateTime, Utc}; - - /// Serialize a UTC datetime into an integer number of microseconds since the epoch or none - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, Utc, NaiveDate}; - /// # use serde_derive::Serialize; - /// use chrono::serde::ts_microseconds_option::serialize as to_micro_tsopt; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_micro_tsopt")] - /// time: Option>, - /// } - /// - /// let my_s = S { - /// time: Some( - /// NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_micro_opt(02, 04, 59, 918355) - /// .unwrap() - /// .and_utc(), - /// ), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918355}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(opt: &Option>, serializer: S) -> Result - where - S: ser::Serializer, - { - match *opt { - Some(ref dt) => serializer.serialize_some(&dt.timestamp_micros()), - None => serializer.serialize_none(), - } - } - - /// Deserialize a `DateTime` from a microsecond timestamp or none - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, TimeZone, Utc}; - /// # use serde_derive::Deserialize; - /// use chrono::serde::ts_microseconds_option::deserialize as from_micro_tsopt; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_micro_tsopt")] - /// time: Option>, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355 }"#)?; - /// assert_eq!(my_s, S { time: Utc.timestamp_opt(1526522699, 918355000).single() }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result>, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_option(OptionMicroSecondsTimestampVisitor) - } - - struct OptionMicroSecondsTimestampVisitor; - - impl<'de> de::Visitor<'de> for OptionMicroSecondsTimestampVisitor { - type Value = Option>; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in microseconds or none") - } - - /// Deserialize a timestamp in microseconds since the epoch - fn visit_some(self, d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(MicroSecondsTimestampVisitor).map(Some) - } - - /// Deserialize a timestamp in microseconds since the epoch - fn visit_none(self) -> Result - where - E: de::Error, - { - Ok(None) - } - - /// Deserialize a timestamp in microseconds since the epoch - fn visit_unit(self) -> Result - where - E: de::Error, - { - Ok(None) - } - } -} - -/// Ser/de to/from timestamps in milliseconds -/// -/// Intended for use with `serde`s `with` attribute. -/// -/// # Example -/// -/// ```rust -/// # use chrono::{DateTime, Utc, NaiveDate}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::serde::ts_milliseconds; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_milliseconds")] -/// time: DateTime, -/// } -/// -/// let time = NaiveDate::from_ymd_opt(2018, 5, 17) -/// .unwrap() -/// .and_hms_milli_opt(02, 04, 59, 918) -/// .unwrap() -/// .and_utc(); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_milliseconds { - use core::fmt; - use serde::{de, ser}; - - use crate::serde::invalid_ts; - use crate::{DateTime, Utc}; - - use super::MilliSecondsTimestampVisitor; - - /// Serialize a UTC datetime into an integer number of milliseconds since the epoch - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, Utc, NaiveDate}; - /// # use serde_derive::Serialize; - /// use chrono::serde::ts_milliseconds::serialize as to_milli_ts; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_milli_ts")] - /// time: DateTime, - /// } - /// - /// let my_s = S { - /// time: NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_milli_opt(02, 04, 59, 918) - /// .unwrap() - /// .and_utc(), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(dt: &DateTime, serializer: S) -> Result - where - S: ser::Serializer, - { - serializer.serialize_i64(dt.timestamp_millis()) - } - - /// Deserialize a `DateTime` from a millisecond timestamp - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, TimeZone, Utc}; - /// # use serde_derive::Deserialize; - /// use chrono::serde::ts_milliseconds::deserialize as from_milli_ts; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_milli_ts")] - /// time: DateTime, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918 }"#)?; - /// assert_eq!(my_s, S { time: Utc.timestamp_opt(1526522699, 918000000).unwrap() }); - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// assert_eq!(my_s, S { time: Utc.timestamp_opt(-1, 999_000_000).unwrap() }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(MilliSecondsTimestampVisitor).map(|dt| dt.with_timezone(&Utc)) - } - - impl de::Visitor<'_> for MilliSecondsTimestampVisitor { - type Value = DateTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in milliseconds") - } - - /// Deserialize a timestamp in milliseconds since the epoch - fn visit_i64(self, value: i64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp_millis(value).ok_or_else(|| invalid_ts(value)) - } - - /// Deserialize a timestamp in milliseconds since the epoch - fn visit_u64(self, value: u64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp((value / 1000) as i64, ((value % 1000) * 1_000_000) as u32) - .ok_or_else(|| invalid_ts(value)) - } - } -} - -/// Ser/de to/from optional timestamps in milliseconds -/// -/// Intended for use with `serde`s `with` attribute. -/// -/// # Example -/// -/// ```rust -/// # use chrono::{DateTime, Utc, NaiveDate}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::serde::ts_milliseconds_option; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_milliseconds_option")] -/// time: Option>, -/// } -/// -/// let time = Some( -/// NaiveDate::from_ymd_opt(2018, 5, 17) -/// .unwrap() -/// .and_hms_milli_opt(02, 04, 59, 918) -/// .unwrap() -/// .and_utc(), -/// ); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_milliseconds_option { - use core::fmt; - use serde::{de, ser}; - - use super::MilliSecondsTimestampVisitor; - use crate::{DateTime, Utc}; - - /// Serialize a UTC datetime into an integer number of milliseconds since the epoch or none - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, Utc, NaiveDate}; - /// # use serde_derive::Serialize; - /// use chrono::serde::ts_milliseconds_option::serialize as to_milli_tsopt; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_milli_tsopt")] - /// time: Option>, - /// } - /// - /// let my_s = S { - /// time: Some( - /// NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_milli_opt(02, 04, 59, 918) - /// .unwrap() - /// .and_utc(), - /// ), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(opt: &Option>, serializer: S) -> Result - where - S: ser::Serializer, - { - match *opt { - Some(ref dt) => serializer.serialize_some(&dt.timestamp_millis()), - None => serializer.serialize_none(), - } - } - - /// Deserialize a `DateTime` from a millisecond timestamp or none - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{TimeZone, DateTime, Utc}; - /// # use serde_derive::Deserialize; - /// use chrono::serde::ts_milliseconds_option::deserialize as from_milli_tsopt; - /// - /// #[derive(Deserialize, PartialEq, Debug)] - /// #[serde(untagged)] - /// enum E { - /// V(T), - /// } - /// - /// #[derive(Deserialize, PartialEq, Debug)] - /// struct S { - /// #[serde(default, deserialize_with = "from_milli_tsopt")] - /// time: Option>, - /// } - /// - /// let my_s: E = serde_json::from_str(r#"{ "time": 1526522699918 }"#)?; - /// assert_eq!(my_s, E::V(S { time: Some(Utc.timestamp_opt(1526522699, 918000000).unwrap()) })); - /// let s: E = serde_json::from_str(r#"{ "time": null }"#)?; - /// assert_eq!(s, E::V(S { time: None })); - /// let t: E = serde_json::from_str(r#"{}"#)?; - /// assert_eq!(t, E::V(S { time: None })); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result>, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_option(OptionMilliSecondsTimestampVisitor) - .map(|opt| opt.map(|dt| dt.with_timezone(&Utc))) - } - - struct OptionMilliSecondsTimestampVisitor; - - impl<'de> de::Visitor<'de> for OptionMilliSecondsTimestampVisitor { - type Value = Option>; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in milliseconds or none") - } - - /// Deserialize a timestamp in milliseconds since the epoch - fn visit_some(self, d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(MilliSecondsTimestampVisitor).map(Some) - } - - /// Deserialize a timestamp in milliseconds since the epoch - fn visit_none(self) -> Result - where - E: de::Error, - { - Ok(None) - } - - /// Deserialize a timestamp in milliseconds since the epoch - fn visit_unit(self) -> Result - where - E: de::Error, - { - Ok(None) - } - } -} - -/// Ser/de to/from timestamps in seconds -/// -/// Intended for use with `serde`'s `with` attribute. -/// -/// # Example: -/// -/// ```rust -/// # use chrono::{TimeZone, DateTime, Utc}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::serde::ts_seconds; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_seconds")] -/// time: DateTime, -/// } -/// -/// let time = Utc.with_ymd_and_hms(2015, 5, 15, 10, 0, 0).unwrap(); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1431684000}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_seconds { - use core::fmt; - use serde::{de, ser}; - - use crate::serde::invalid_ts; - use crate::{DateTime, Utc}; - - use super::SecondsTimestampVisitor; - - /// Serialize a UTC datetime into an integer number of seconds since the epoch - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{TimeZone, DateTime, Utc}; - /// # use serde_derive::Serialize; - /// use chrono::serde::ts_seconds::serialize as to_ts; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_ts")] - /// time: DateTime, - /// } - /// - /// let my_s = S { time: Utc.with_ymd_and_hms(2015, 5, 15, 10, 0, 0).unwrap() }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1431684000}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(dt: &DateTime, serializer: S) -> Result - where - S: ser::Serializer, - { - serializer.serialize_i64(dt.timestamp()) - } - - /// Deserialize a `DateTime` from a seconds timestamp - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, TimeZone, Utc}; - /// # use serde_derive::Deserialize; - /// use chrono::serde::ts_seconds::deserialize as from_ts; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_ts")] - /// time: DateTime, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?; - /// assert_eq!(my_s, S { time: Utc.timestamp_opt(1431684000, 0).unwrap() }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(SecondsTimestampVisitor) - } - - impl de::Visitor<'_> for SecondsTimestampVisitor { - type Value = DateTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in seconds") - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_i64(self, value: i64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp_secs(value).ok_or_else(|| invalid_ts(value)) - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_u64(self, value: u64) -> Result - where - E: de::Error, - { - if value > i64::MAX as u64 { - Err(invalid_ts(value)) - } else { - DateTime::from_timestamp_secs(value as i64).ok_or_else(|| invalid_ts(value)) - } - } - } -} - -/// Ser/de to/from optional timestamps in seconds -/// -/// Intended for use with `serde`'s `with` attribute. -/// -/// # Example: -/// -/// ```rust -/// # use chrono::{TimeZone, DateTime, Utc}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::serde::ts_seconds_option; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_seconds_option")] -/// time: Option>, -/// } -/// -/// let time = Some(Utc.with_ymd_and_hms(2015, 5, 15, 10, 0, 0).unwrap()); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1431684000}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_seconds_option { - use core::fmt; - use serde::{de, ser}; - - use super::SecondsTimestampVisitor; - use crate::{DateTime, Utc}; - - /// Serialize a UTC datetime into an integer number of seconds since the epoch or none - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{TimeZone, DateTime, Utc}; - /// # use serde_derive::Serialize; - /// use chrono::serde::ts_seconds_option::serialize as to_tsopt; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_tsopt")] - /// time: Option>, - /// } - /// - /// let my_s = S { time: Some(Utc.with_ymd_and_hms(2015, 5, 15, 10, 0, 0).unwrap()) }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1431684000}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(opt: &Option>, serializer: S) -> Result - where - S: ser::Serializer, - { - match *opt { - Some(ref dt) => serializer.serialize_some(&dt.timestamp()), - None => serializer.serialize_none(), - } - } - - /// Deserialize a `DateTime` from a seconds timestamp or none - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, TimeZone, Utc}; - /// # use serde_derive::Deserialize; - /// use chrono::serde::ts_seconds_option::deserialize as from_tsopt; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_tsopt")] - /// time: Option>, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?; - /// assert_eq!(my_s, S { time: Utc.timestamp_opt(1431684000, 0).single() }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result>, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_option(OptionSecondsTimestampVisitor) - } - - struct OptionSecondsTimestampVisitor; - - impl<'de> de::Visitor<'de> for OptionSecondsTimestampVisitor { - type Value = Option>; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in seconds or none") - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_some(self, d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(SecondsTimestampVisitor).map(Some) - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_none(self) -> Result - where - E: de::Error, - { - Ok(None) - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_unit(self) -> Result - where - E: de::Error, - { - Ok(None) - } - } -} - -#[cfg(test)] -mod tests { - #[cfg(feature = "clock")] - use crate::Local; - use crate::{DateTime, FixedOffset, TimeZone, Utc}; - use core::fmt; - - #[test] - fn test_serde_serialize() { - assert_eq!( - serde_json::to_string(&Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap()).ok(), - Some(r#""2014-07-24T12:34:06Z""#.to_owned()) - ); - assert_eq!( - serde_json::to_string( - &FixedOffset::east_opt(3660) - .unwrap() - .with_ymd_and_hms(2014, 7, 24, 12, 34, 6) - .unwrap() - ) - .ok(), - Some(r#""2014-07-24T12:34:06+01:01""#.to_owned()) - ); - assert_eq!( - serde_json::to_string( - &FixedOffset::east_opt(3650) - .unwrap() - .with_ymd_and_hms(2014, 7, 24, 12, 34, 6) - .unwrap() - ) - .ok(), - // An offset with seconds is not allowed by RFC 3339, so we round it to the nearest minute. - // In this case `+01:00:50` becomes `+01:01` - Some(r#""2014-07-24T12:34:06+01:01""#.to_owned()) - ); - } - - #[test] - fn test_serde_deserialize() { - // should check against the offset as well (the normal DateTime comparison will ignore them) - fn norm(dt: &Option>) -> Option<(&DateTime, &Tz::Offset)> { - dt.as_ref().map(|dt| (dt, dt.offset())) - } - - let dt: Option> = serde_json::from_str(r#""2014-07-24T12:34:06Z""#).ok(); - assert_eq!(norm(&dt), norm(&Some(Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap()))); - let dt: Option> = serde_json::from_str(r#""2014-07-24T13:57:06+01:23""#).ok(); - assert_eq!(norm(&dt), norm(&Some(Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap()))); - - let dt: Option> = - serde_json::from_str(r#""2014-07-24T12:34:06Z""#).ok(); - assert_eq!( - norm(&dt), - norm(&Some( - FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap() - )) - ); - let dt: Option> = - serde_json::from_str(r#""2014-07-24T13:57:06+01:23""#).ok(); - assert_eq!( - norm(&dt), - norm(&Some( - FixedOffset::east_opt(60 * 60 + 23 * 60) - .unwrap() - .with_ymd_and_hms(2014, 7, 24, 13, 57, 6) - .unwrap() - )) - ); - - // we don't know the exact local offset but we can check that - // the conversion didn't change the instant itself - #[cfg(feature = "clock")] - { - let dt: DateTime = - serde_json::from_str(r#""2014-07-24T12:34:06Z""#).expect("local should parse"); - assert_eq!(dt, Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap()); - - let dt: DateTime = serde_json::from_str(r#""2014-07-24T13:57:06+01:23""#) - .expect("local should parse with offset"); - assert_eq!(dt, Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap()); - } - - assert!(serde_json::from_str::>(r#""2014-07-32T12:34:06Z""#).is_err()); - assert!( - serde_json::from_str::>(r#""2014-07-32T12:34:06Z""#).is_err() - ); - } - - #[test] - fn test_serde_bincode() { - // Bincode is relevant to test separately from JSON because - // it is not self-describing. - use bincode::{deserialize, serialize}; - - let dt = Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap(); - let encoded = serialize(&dt).unwrap(); - let decoded: DateTime = deserialize(&encoded).unwrap(); - assert_eq!(dt, decoded); - assert_eq!(dt.offset(), decoded.offset()); - } - - #[test] - fn test_serde_no_offset_debug() { - use crate::{MappedLocalTime, NaiveDate, NaiveDateTime, Offset}; - use core::fmt::Debug; - - #[derive(Clone)] - struct TestTimeZone; - impl Debug for TestTimeZone { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "TEST") - } - } - impl TimeZone for TestTimeZone { - type Offset = TestTimeZone; - fn from_offset(_state: &TestTimeZone) -> TestTimeZone { - TestTimeZone - } - fn offset_from_local_date(&self, _local: &NaiveDate) -> MappedLocalTime { - MappedLocalTime::Single(TestTimeZone) - } - fn offset_from_local_datetime( - &self, - _local: &NaiveDateTime, - ) -> MappedLocalTime { - MappedLocalTime::Single(TestTimeZone) - } - fn offset_from_utc_date(&self, _utc: &NaiveDate) -> TestTimeZone { - TestTimeZone - } - fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> TestTimeZone { - TestTimeZone - } - } - impl Offset for TestTimeZone { - fn fix(&self) -> FixedOffset { - FixedOffset::east_opt(15 * 60 * 60).unwrap() - } - } - - let tz = TestTimeZone; - assert_eq!(format!("{:?}", &tz), "TEST"); - - let dt = tz.with_ymd_and_hms(2023, 4, 24, 21, 10, 33).unwrap(); - let encoded = serde_json::to_string(&dt).unwrap(); - dbg!(&encoded); - let decoded: DateTime = serde_json::from_str(&encoded).unwrap(); - assert_eq!(dt, decoded); - assert_eq!(dt.offset().fix(), *decoded.offset()); - } -} diff --git a/chrono-0.4.44/src/datetime/tests.rs b/chrono-0.4.44/src/datetime/tests.rs deleted file mode 100644 index 67bee1049e..0000000000 --- a/chrono-0.4.44/src/datetime/tests.rs +++ /dev/null @@ -1,1912 +0,0 @@ -use super::DateTime; -use crate::naive::{NaiveDate, NaiveTime}; -#[cfg(feature = "clock")] -use crate::offset::Local; -use crate::offset::{FixedOffset, Offset, TimeZone, Utc}; -use crate::{Datelike, Days, MappedLocalTime, Months, NaiveDateTime, TimeDelta, Timelike, Weekday}; - -#[derive(Clone)] -struct DstTester; - -impl DstTester { - fn winter_offset() -> FixedOffset { - FixedOffset::east_opt(8 * 60 * 60).unwrap() - } - fn summer_offset() -> FixedOffset { - FixedOffset::east_opt(9 * 60 * 60).unwrap() - } - - const TO_WINTER_MONTH_DAY: (u32, u32) = (4, 15); - const TO_SUMMER_MONTH_DAY: (u32, u32) = (9, 15); - - fn transition_start_local() -> NaiveTime { - NaiveTime::from_hms_opt(2, 0, 0).unwrap() - } -} - -impl TimeZone for DstTester { - type Offset = FixedOffset; - - fn from_offset(_: &Self::Offset) -> Self { - DstTester - } - - fn offset_from_local_date(&self, _: &NaiveDate) -> crate::MappedLocalTime { - unimplemented!() - } - - fn offset_from_local_datetime( - &self, - local: &NaiveDateTime, - ) -> crate::MappedLocalTime { - let local_to_winter_transition_start = NaiveDate::from_ymd_opt( - local.year(), - DstTester::TO_WINTER_MONTH_DAY.0, - DstTester::TO_WINTER_MONTH_DAY.1, - ) - .unwrap() - .and_time(DstTester::transition_start_local()); - - let local_to_winter_transition_end = NaiveDate::from_ymd_opt( - local.year(), - DstTester::TO_WINTER_MONTH_DAY.0, - DstTester::TO_WINTER_MONTH_DAY.1, - ) - .unwrap() - .and_time(DstTester::transition_start_local() - TimeDelta::try_hours(1).unwrap()); - - let local_to_summer_transition_start = NaiveDate::from_ymd_opt( - local.year(), - DstTester::TO_SUMMER_MONTH_DAY.0, - DstTester::TO_SUMMER_MONTH_DAY.1, - ) - .unwrap() - .and_time(DstTester::transition_start_local()); - - let local_to_summer_transition_end = NaiveDate::from_ymd_opt( - local.year(), - DstTester::TO_SUMMER_MONTH_DAY.0, - DstTester::TO_SUMMER_MONTH_DAY.1, - ) - .unwrap() - .and_time(DstTester::transition_start_local() + TimeDelta::try_hours(1).unwrap()); - - if *local < local_to_winter_transition_end || *local >= local_to_summer_transition_end { - MappedLocalTime::Single(DstTester::summer_offset()) - } else if *local >= local_to_winter_transition_start - && *local < local_to_summer_transition_start - { - MappedLocalTime::Single(DstTester::winter_offset()) - } else if *local >= local_to_winter_transition_end - && *local < local_to_winter_transition_start - { - MappedLocalTime::Ambiguous(DstTester::winter_offset(), DstTester::summer_offset()) - } else if *local >= local_to_summer_transition_start - && *local < local_to_summer_transition_end - { - MappedLocalTime::None - } else { - panic!("Unexpected local time {local}") - } - } - - fn offset_from_utc_date(&self, _: &NaiveDate) -> Self::Offset { - unimplemented!() - } - - fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> Self::Offset { - let utc_to_winter_transition = NaiveDate::from_ymd_opt( - utc.year(), - DstTester::TO_WINTER_MONTH_DAY.0, - DstTester::TO_WINTER_MONTH_DAY.1, - ) - .unwrap() - .and_time(DstTester::transition_start_local()) - - DstTester::summer_offset(); - - let utc_to_summer_transition = NaiveDate::from_ymd_opt( - utc.year(), - DstTester::TO_SUMMER_MONTH_DAY.0, - DstTester::TO_SUMMER_MONTH_DAY.1, - ) - .unwrap() - .and_time(DstTester::transition_start_local()) - - DstTester::winter_offset(); - - if *utc < utc_to_winter_transition || *utc >= utc_to_summer_transition { - DstTester::summer_offset() - } else if *utc >= utc_to_winter_transition && *utc < utc_to_summer_transition { - DstTester::winter_offset() - } else { - panic!("Unexpected utc time {utc}") - } - } -} - -#[test] -fn test_datetime_from_timestamp_millis() { - let valid_map = [ - (1662921288000, "2022-09-11 18:34:48.000000000"), - (1662921288123, "2022-09-11 18:34:48.123000000"), - (1662921287890, "2022-09-11 18:34:47.890000000"), - (-2208936075000, "1900-01-01 14:38:45.000000000"), - (0, "1970-01-01 00:00:00.000000000"), - (119731017000, "1973-10-17 18:36:57.000000000"), - (1234567890000, "2009-02-13 23:31:30.000000000"), - (2034061609000, "2034-06-16 09:06:49.000000000"), - ]; - - for (timestamp_millis, _formatted) in valid_map.iter().copied() { - let datetime = DateTime::from_timestamp_millis(timestamp_millis).unwrap(); - assert_eq!(timestamp_millis, datetime.timestamp_millis()); - #[cfg(feature = "alloc")] - assert_eq!(datetime.format("%F %T%.9f").to_string(), _formatted); - } - - let invalid = [i64::MAX, i64::MIN]; - - for timestamp_millis in invalid.iter().copied() { - let datetime = DateTime::from_timestamp_millis(timestamp_millis); - assert!(datetime.is_none()); - } - - // Test that the result of `from_timestamp_millis` compares equal to - // that of `from_timestamp_opt`. - let secs_test = [0, 1, 2, 1000, 1234, 12345678, -1, -2, -1000, -12345678]; - for secs in secs_test.iter().cloned() { - assert_eq!( - DateTime::from_timestamp_millis(secs * 1000), - DateTime::from_timestamp_secs(secs) - ); - } -} - -#[test] -fn test_datetime_from_timestamp_micros() { - let valid_map = [ - (1662921288000000, "2022-09-11 18:34:48.000000000"), - (1662921288123456, "2022-09-11 18:34:48.123456000"), - (1662921287890000, "2022-09-11 18:34:47.890000000"), - (-2208936075000000, "1900-01-01 14:38:45.000000000"), - (0, "1970-01-01 00:00:00.000000000"), - (119731017000000, "1973-10-17 18:36:57.000000000"), - (1234567890000000, "2009-02-13 23:31:30.000000000"), - (2034061609000000, "2034-06-16 09:06:49.000000000"), - ]; - - for (timestamp_micros, _formatted) in valid_map.iter().copied() { - let datetime = DateTime::from_timestamp_micros(timestamp_micros).unwrap(); - assert_eq!(timestamp_micros, datetime.timestamp_micros()); - #[cfg(feature = "alloc")] - assert_eq!(datetime.format("%F %T%.9f").to_string(), _formatted); - } - - let invalid = [i64::MAX, i64::MIN]; - - for timestamp_micros in invalid.iter().copied() { - let datetime = DateTime::from_timestamp_micros(timestamp_micros); - assert!(datetime.is_none()); - } - - // Test that the result of `TimeZone::timestamp_micros` compares equal to - // that of `TimeZone::timestamp_opt`. - let secs_test = [0, 1, 2, 1000, 1234, 12345678, -1, -2, -1000, -12345678]; - for secs in secs_test.iter().copied() { - assert_eq!( - DateTime::from_timestamp_micros(secs * 1_000_000), - DateTime::from_timestamp_secs(secs) - ); - } -} - -#[test] -fn test_datetime_from_timestamp_nanos() { - let valid_map = [ - (1662921288000000000, "2022-09-11 18:34:48.000000000"), - (1662921288123456000, "2022-09-11 18:34:48.123456000"), - (1662921288123456789, "2022-09-11 18:34:48.123456789"), - (1662921287890000000, "2022-09-11 18:34:47.890000000"), - (-2208936075000000000, "1900-01-01 14:38:45.000000000"), - (-5337182663000000000, "1800-11-15 01:15:37.000000000"), - (0, "1970-01-01 00:00:00.000000000"), - (119731017000000000, "1973-10-17 18:36:57.000000000"), - (1234567890000000000, "2009-02-13 23:31:30.000000000"), - (2034061609000000000, "2034-06-16 09:06:49.000000000"), - ]; - - for (timestamp_nanos, _formatted) in valid_map.iter().copied() { - let datetime = DateTime::from_timestamp_nanos(timestamp_nanos); - assert_eq!(timestamp_nanos, datetime.timestamp_nanos_opt().unwrap()); - #[cfg(feature = "alloc")] - assert_eq!(datetime.format("%F %T%.9f").to_string(), _formatted); - } - - const A_BILLION: i64 = 1_000_000_000; - // Maximum datetime in nanoseconds - let maximum = "2262-04-11T23:47:16.854775804UTC"; - let parsed: DateTime = maximum.parse().unwrap(); - let nanos = parsed.timestamp_nanos_opt().unwrap(); - assert_eq!( - Some(DateTime::from_timestamp_nanos(nanos)), - DateTime::from_timestamp(nanos / A_BILLION, (nanos % A_BILLION) as u32) - ); - // Minimum datetime in nanoseconds - let minimum = "1677-09-21T00:12:44.000000000UTC"; - let parsed: DateTime = minimum.parse().unwrap(); - let nanos = parsed.timestamp_nanos_opt().unwrap(); - assert_eq!( - Some(DateTime::from_timestamp_nanos(nanos)), - DateTime::from_timestamp(nanos / A_BILLION, (nanos % A_BILLION) as u32) - ); - - // Test that the result of `TimeZone::timestamp_nanos` compares equal to - // that of `TimeZone::timestamp_opt`. - let secs_test = [0, 1, 2, 1000, 1234, 12345678, -1, -2, -1000, -12345678]; - for secs in secs_test.iter().copied() { - assert_eq!( - Some(DateTime::from_timestamp_nanos(secs * 1_000_000_000)), - DateTime::from_timestamp_secs(secs) - ); - } -} - -#[test] -fn test_datetime_from_timestamp_secs() { - let valid = [-2208936075, 0, 119731017, 1234567890, 2034061609]; - - for timestamp_secs in valid.iter().copied() { - let datetime = DateTime::from_timestamp_secs(timestamp_secs).unwrap(); - assert_eq!(timestamp_secs, datetime.timestamp()); - assert_eq!(DateTime::from_timestamp(timestamp_secs, 0).unwrap(), datetime); - } -} - -#[test] -fn test_datetime_from_timestamp() { - let ymdhms = |y, m, d, h, n, s| { - NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap().and_utc() - }; - assert_eq!(DateTime::from_timestamp_secs(-1), Some(ymdhms(1969, 12, 31, 23, 59, 59))); - assert_eq!(DateTime::from_timestamp_secs(0), Some(ymdhms(1970, 1, 1, 0, 0, 0))); - assert_eq!(DateTime::from_timestamp_secs(1), Some(ymdhms(1970, 1, 1, 0, 0, 1))); - assert_eq!(DateTime::from_timestamp_secs(1_000_000_000), Some(ymdhms(2001, 9, 9, 1, 46, 40))); - assert_eq!(DateTime::from_timestamp_secs(0x7fffffff), Some(ymdhms(2038, 1, 19, 3, 14, 7))); - assert_eq!(DateTime::from_timestamp_secs(i64::MIN), None); - assert_eq!(DateTime::from_timestamp_secs(i64::MAX), None); -} - -#[test] -fn test_datetime_timestamp() { - let to_timestamp = |y, m, d, h, n, s| { - NaiveDate::from_ymd_opt(y, m, d) - .unwrap() - .and_hms_opt(h, n, s) - .unwrap() - .and_utc() - .timestamp() - }; - assert_eq!(to_timestamp(1969, 12, 31, 23, 59, 59), -1); - assert_eq!(to_timestamp(1970, 1, 1, 0, 0, 0), 0); - assert_eq!(to_timestamp(1970, 1, 1, 0, 0, 1), 1); - assert_eq!(to_timestamp(2001, 9, 9, 1, 46, 40), 1_000_000_000); - assert_eq!(to_timestamp(2038, 1, 19, 3, 14, 7), 0x7fffffff); -} - -#[test] -fn test_nanosecond_range() { - const A_BILLION: i64 = 1_000_000_000; - let maximum = "2262-04-11T23:47:16.854775804UTC"; - let parsed: DateTime = maximum.parse().unwrap(); - let nanos = parsed.timestamp_nanos_opt().unwrap(); - assert_eq!( - parsed, - DateTime::::from_timestamp(nanos / A_BILLION, (nanos % A_BILLION) as u32).unwrap() - ); - - let minimum = "1677-09-21T00:12:44.000000000UTC"; - let parsed: DateTime = minimum.parse().unwrap(); - let nanos = parsed.timestamp_nanos_opt().unwrap(); - assert_eq!( - parsed, - DateTime::::from_timestamp(nanos / A_BILLION, (nanos % A_BILLION) as u32).unwrap() - ); - - // Just beyond range - let maximum = "2262-04-11T23:47:16.854775804UTC"; - let parsed: DateTime = maximum.parse().unwrap(); - let beyond_max = parsed + TimeDelta::try_milliseconds(300).unwrap(); - assert!(beyond_max.timestamp_nanos_opt().is_none()); - - // Far beyond range - let maximum = "2262-04-11T23:47:16.854775804UTC"; - let parsed: DateTime = maximum.parse().unwrap(); - let beyond_max = parsed + Days::new(365); - assert!(beyond_max.timestamp_nanos_opt().is_none()); -} - -#[test] -fn test_datetime_add_days() { - let est = FixedOffset::west_opt(5 * 60 * 60).unwrap(); - let kst = FixedOffset::east_opt(9 * 60 * 60).unwrap(); - - assert_eq!( - format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Days::new(5)), - "2014-05-11 07:08:09 -05:00" - ); - assert_eq!( - format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Days::new(5)), - "2014-05-11 07:08:09 +09:00" - ); - - assert_eq!( - format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Days::new(35)), - "2014-06-10 07:08:09 -05:00" - ); - assert_eq!( - format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Days::new(35)), - "2014-06-10 07:08:09 +09:00" - ); - - assert_eq!( - format!("{}", DstTester.with_ymd_and_hms(2014, 4, 6, 7, 8, 9).unwrap() + Days::new(5)), - "2014-04-11 07:08:09 +09:00" - ); - assert_eq!( - format!("{}", DstTester.with_ymd_and_hms(2014, 4, 6, 7, 8, 9).unwrap() + Days::new(10)), - "2014-04-16 07:08:09 +08:00" - ); - - assert_eq!( - format!("{}", DstTester.with_ymd_and_hms(2014, 9, 6, 7, 8, 9).unwrap() + Days::new(5)), - "2014-09-11 07:08:09 +08:00" - ); - assert_eq!( - format!("{}", DstTester.with_ymd_and_hms(2014, 9, 6, 7, 8, 9).unwrap() + Days::new(10)), - "2014-09-16 07:08:09 +09:00" - ); -} - -#[test] -fn test_datetime_sub_days() { - let est = FixedOffset::west_opt(5 * 60 * 60).unwrap(); - let kst = FixedOffset::east_opt(9 * 60 * 60).unwrap(); - - assert_eq!( - format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Days::new(5)), - "2014-05-01 07:08:09 -05:00" - ); - assert_eq!( - format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Days::new(5)), - "2014-05-01 07:08:09 +09:00" - ); - - assert_eq!( - format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Days::new(35)), - "2014-04-01 07:08:09 -05:00" - ); - assert_eq!( - format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Days::new(35)), - "2014-04-01 07:08:09 +09:00" - ); -} - -#[test] -fn test_datetime_add_months() { - let est = FixedOffset::west_opt(5 * 60 * 60).unwrap(); - let kst = FixedOffset::east_opt(9 * 60 * 60).unwrap(); - - assert_eq!( - format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Months::new(1)), - "2014-06-06 07:08:09 -05:00" - ); - assert_eq!( - format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Months::new(1)), - "2014-06-06 07:08:09 +09:00" - ); - - assert_eq!( - format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Months::new(5)), - "2014-10-06 07:08:09 -05:00" - ); - assert_eq!( - format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() + Months::new(5)), - "2014-10-06 07:08:09 +09:00" - ); -} - -#[test] -fn test_datetime_sub_months() { - let est = FixedOffset::west_opt(5 * 60 * 60).unwrap(); - let kst = FixedOffset::east_opt(9 * 60 * 60).unwrap(); - - assert_eq!( - format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Months::new(1)), - "2014-04-06 07:08:09 -05:00" - ); - assert_eq!( - format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Months::new(1)), - "2014-04-06 07:08:09 +09:00" - ); - - assert_eq!( - format!("{}", est.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Months::new(5)), - "2013-12-06 07:08:09 -05:00" - ); - assert_eq!( - format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap() - Months::new(5)), - "2013-12-06 07:08:09 +09:00" - ); -} - -// local helper function to easily create a DateTime -#[allow(clippy::too_many_arguments)] -fn ymdhms( - fixedoffset: &FixedOffset, - year: i32, - month: u32, - day: u32, - hour: u32, - min: u32, - sec: u32, -) -> DateTime { - fixedoffset.with_ymd_and_hms(year, month, day, hour, min, sec).unwrap() -} - -// local helper function to easily create a DateTime -#[allow(clippy::too_many_arguments)] -fn ymdhms_milli( - fixedoffset: &FixedOffset, - year: i32, - month: u32, - day: u32, - hour: u32, - min: u32, - sec: u32, - milli: u32, -) -> DateTime { - fixedoffset - .with_ymd_and_hms(year, month, day, hour, min, sec) - .unwrap() - .with_nanosecond(milli * 1_000_000) - .unwrap() -} - -// local helper function to easily create a DateTime -#[allow(clippy::too_many_arguments)] -#[cfg(feature = "alloc")] -fn ymdhms_micro( - fixedoffset: &FixedOffset, - year: i32, - month: u32, - day: u32, - hour: u32, - min: u32, - sec: u32, - micro: u32, -) -> DateTime { - fixedoffset - .with_ymd_and_hms(year, month, day, hour, min, sec) - .unwrap() - .with_nanosecond(micro * 1000) - .unwrap() -} - -// local helper function to easily create a DateTime -#[allow(clippy::too_many_arguments)] -#[cfg(feature = "alloc")] -fn ymdhms_nano( - fixedoffset: &FixedOffset, - year: i32, - month: u32, - day: u32, - hour: u32, - min: u32, - sec: u32, - nano: u32, -) -> DateTime { - fixedoffset - .with_ymd_and_hms(year, month, day, hour, min, sec) - .unwrap() - .with_nanosecond(nano) - .unwrap() -} - -// local helper function to easily create a DateTime -#[cfg(feature = "alloc")] -fn ymdhms_utc(year: i32, month: u32, day: u32, hour: u32, min: u32, sec: u32) -> DateTime { - Utc.with_ymd_and_hms(year, month, day, hour, min, sec).unwrap() -} - -// local helper function to easily create a DateTime -fn ymdhms_milli_utc( - year: i32, - month: u32, - day: u32, - hour: u32, - min: u32, - sec: u32, - milli: u32, -) -> DateTime { - Utc.with_ymd_and_hms(year, month, day, hour, min, sec) - .unwrap() - .with_nanosecond(milli * 1_000_000) - .unwrap() -} - -#[test] -fn test_datetime_offset() { - let est = FixedOffset::west_opt(5 * 60 * 60).unwrap(); - let edt = FixedOffset::west_opt(4 * 60 * 60).unwrap(); - let kst = FixedOffset::east_opt(9 * 60 * 60).unwrap(); - - assert_eq!( - format!("{}", Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()), - "2014-05-06 07:08:09 UTC" - ); - assert_eq!( - format!("{}", edt.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()), - "2014-05-06 07:08:09 -04:00" - ); - assert_eq!( - format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()), - "2014-05-06 07:08:09 +09:00" - ); - assert_eq!( - format!("{:?}", Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()), - "2014-05-06T07:08:09Z" - ); - assert_eq!( - format!("{:?}", edt.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()), - "2014-05-06T07:08:09-04:00" - ); - assert_eq!( - format!("{:?}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()), - "2014-05-06T07:08:09+09:00" - ); - - // edge cases - assert_eq!( - format!("{:?}", Utc.with_ymd_and_hms(2014, 5, 6, 0, 0, 0).unwrap()), - "2014-05-06T00:00:00Z" - ); - assert_eq!( - format!("{:?}", edt.with_ymd_and_hms(2014, 5, 6, 0, 0, 0).unwrap()), - "2014-05-06T00:00:00-04:00" - ); - assert_eq!( - format!("{:?}", kst.with_ymd_and_hms(2014, 5, 6, 0, 0, 0).unwrap()), - "2014-05-06T00:00:00+09:00" - ); - assert_eq!( - format!("{:?}", Utc.with_ymd_and_hms(2014, 5, 6, 23, 59, 59).unwrap()), - "2014-05-06T23:59:59Z" - ); - assert_eq!( - format!("{:?}", edt.with_ymd_and_hms(2014, 5, 6, 23, 59, 59).unwrap()), - "2014-05-06T23:59:59-04:00" - ); - assert_eq!( - format!("{:?}", kst.with_ymd_and_hms(2014, 5, 6, 23, 59, 59).unwrap()), - "2014-05-06T23:59:59+09:00" - ); - - let dt = Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap(); - assert_eq!(dt, edt.with_ymd_and_hms(2014, 5, 6, 3, 8, 9).unwrap()); - assert_eq!( - dt + TimeDelta::try_seconds(3600 + 60 + 1).unwrap(), - Utc.with_ymd_and_hms(2014, 5, 6, 8, 9, 10).unwrap() - ); - assert_eq!( - dt.signed_duration_since(edt.with_ymd_and_hms(2014, 5, 6, 10, 11, 12).unwrap()), - TimeDelta::try_seconds(-7 * 3600 - 3 * 60 - 3).unwrap() - ); - - assert_eq!(*Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap().offset(), Utc); - assert_eq!(*edt.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap().offset(), edt); - assert!(*edt.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap().offset() != est); -} - -#[test] -#[allow(clippy::needless_borrow, clippy::op_ref)] -fn signed_duration_since_autoref() { - let dt1 = Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap(); - let dt2 = Utc.with_ymd_and_hms(2014, 3, 4, 5, 6, 7).unwrap(); - let diff1 = dt1.signed_duration_since(dt2); // Copy/consume - #[allow(clippy::needless_borrows_for_generic_args)] - let diff2 = dt2.signed_duration_since(&dt1); // Take by reference - assert_eq!(diff1, -diff2); - - let diff1 = dt1 - &dt2; // We can choose to subtract rhs by reference - let diff2 = dt2 - dt1; // Or consume rhs - assert_eq!(diff1, -diff2); -} - -#[test] -fn test_datetime_date_and_time() { - let tz = FixedOffset::east_opt(5 * 60 * 60).unwrap(); - let d = tz.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap(); - assert_eq!(d.time(), NaiveTime::from_hms_opt(7, 8, 9).unwrap()); - assert_eq!(d.date_naive(), NaiveDate::from_ymd_opt(2014, 5, 6).unwrap()); - - let tz = FixedOffset::east_opt(4 * 60 * 60).unwrap(); - let d = tz.with_ymd_and_hms(2016, 5, 4, 3, 2, 1).unwrap(); - assert_eq!(d.time(), NaiveTime::from_hms_opt(3, 2, 1).unwrap()); - assert_eq!(d.date_naive(), NaiveDate::from_ymd_opt(2016, 5, 4).unwrap()); - - let tz = FixedOffset::west_opt(13 * 60 * 60).unwrap(); - let d = tz.with_ymd_and_hms(2017, 8, 9, 12, 34, 56).unwrap(); - assert_eq!(d.time(), NaiveTime::from_hms_opt(12, 34, 56).unwrap()); - assert_eq!(d.date_naive(), NaiveDate::from_ymd_opt(2017, 8, 9).unwrap()); - - let utc_d = Utc.with_ymd_and_hms(2017, 8, 9, 12, 34, 56).unwrap(); - assert!(utc_d < d); -} - -#[test] -#[cfg(feature = "clock")] -fn test_datetime_with_timezone() { - let local_now = Local::now(); - let utc_now = local_now.with_timezone(&Utc); - let local_now2 = utc_now.with_timezone(&Local); - assert_eq!(local_now, local_now2); -} - -#[test] -#[cfg(feature = "alloc")] -fn test_datetime_rfc2822() { - let edt = FixedOffset::east_opt(5 * 60 * 60).unwrap(); - - // timezone 0 - assert_eq!( - Utc.with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap().to_rfc2822(), - "Wed, 18 Feb 2015 23:16:09 +0000" - ); - assert_eq!( - Utc.with_ymd_and_hms(2015, 2, 1, 23, 16, 9).unwrap().to_rfc2822(), - "Sun, 1 Feb 2015 23:16:09 +0000" - ); - // timezone +05 - assert_eq!( - edt.from_local_datetime( - &NaiveDate::from_ymd_opt(2015, 2, 18) - .unwrap() - .and_hms_milli_opt(23, 16, 9, 150) - .unwrap() - ) - .unwrap() - .to_rfc2822(), - "Wed, 18 Feb 2015 23:16:09 +0500" - ); - assert_eq!( - DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:59:60 +0500"), - Ok(edt - .from_local_datetime( - &NaiveDate::from_ymd_opt(2015, 2, 18) - .unwrap() - .and_hms_milli_opt(23, 59, 59, 1_000) - .unwrap() - ) - .unwrap()) - ); - assert!(DateTime::parse_from_rfc2822("31 DEC 262143 23:59 -2359").is_err()); - assert_eq!( - DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00"), - Ok(edt - .from_local_datetime( - &NaiveDate::from_ymd_opt(2015, 2, 18) - .unwrap() - .and_hms_micro_opt(23, 59, 59, 1_234_567) - .unwrap() - ) - .unwrap()) - ); - // seconds 60 - assert_eq!( - edt.from_local_datetime( - &NaiveDate::from_ymd_opt(2015, 2, 18) - .unwrap() - .and_hms_micro_opt(23, 59, 59, 1_234_567) - .unwrap() - ) - .unwrap() - .to_rfc2822(), - "Wed, 18 Feb 2015 23:59:60 +0500" - ); - - assert_eq!( - DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 +0000"), - Ok(FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap()) - ); - assert_eq!( - DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 -0000"), - Ok(FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap()) - ); - assert_eq!( - ymdhms_micro(&edt, 2015, 2, 18, 23, 59, 59, 1_234_567).to_rfc2822(), - "Wed, 18 Feb 2015 23:59:60 +0500" - ); - assert_eq!( - DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:59:58 +0500"), - Ok(ymdhms(&edt, 2015, 2, 18, 23, 59, 58)) - ); - assert_ne!( - DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:59:58 +0500"), - Ok(ymdhms_milli(&edt, 2015, 2, 18, 23, 59, 58, 500)) - ); - - // many varying whitespace intermixed - assert_eq!( - DateTime::parse_from_rfc2822( - "\t\t\tWed,\n\t\t18 \r\n\t\tFeb \u{3000} 2015\r\n\t\t\t23:59:58 \t+0500" - ), - Ok(ymdhms(&edt, 2015, 2, 18, 23, 59, 58)) - ); - // example from RFC 2822 Appendix A.5. - assert_eq!( - DateTime::parse_from_rfc2822( - "Thu,\n\t13\n Feb\n 1969\n 23:32\n -0330 (Newfoundland Time)" - ), - Ok( - ymdhms(&FixedOffset::east_opt(-3 * 60 * 60 - 30 * 60).unwrap(), 1969, 2, 13, 23, 32, 0,) - ) - ); - // example from RFC 2822 Appendix A.5. without trailing " (Newfoundland Time)" - assert_eq!( - DateTime::parse_from_rfc2822( - "Thu,\n\t13\n Feb\n 1969\n 23:32\n -0330" - ), - Ok( - ymdhms(&FixedOffset::east_opt(-3 * 60 * 60 - 30 * 60).unwrap(), 1969, 2, 13, 23, 32, 0,) - ) - ); - - // bad year - assert!(DateTime::parse_from_rfc2822("31 DEC 262143 23:59 -2359").is_err()); - // wrong format - assert!(DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 +00:00").is_err()); - // full name day of week - assert!(DateTime::parse_from_rfc2822("Wednesday, 18 Feb 2015 23:16:09 +0000").is_err()); - // full name day of week - assert!(DateTime::parse_from_rfc2822("Wednesday 18 Feb 2015 23:16:09 +0000").is_err()); - // wrong day of week separator '.' - assert!(DateTime::parse_from_rfc2822("Wed. 18 Feb 2015 23:16:09 +0000").is_err()); - // *trailing* space causes failure - assert!(DateTime::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 +0000 ").is_err()); -} - -#[test] -#[cfg(feature = "alloc")] -fn test_datetime_rfc3339() { - let edt5 = FixedOffset::east_opt(5 * 60 * 60).unwrap(); - let edt0 = FixedOffset::east_opt(0).unwrap(); - - // timezone 0 - assert_eq!( - Utc.with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap().to_rfc3339(), - "2015-02-18T23:16:09+00:00" - ); - // timezone +05 - assert_eq!( - edt5.from_local_datetime( - &NaiveDate::from_ymd_opt(2015, 2, 18) - .unwrap() - .and_hms_milli_opt(23, 16, 9, 150) - .unwrap() - ) - .unwrap() - .to_rfc3339(), - "2015-02-18T23:16:09.150+05:00" - ); - - assert_eq!(ymdhms_utc(2015, 2, 18, 23, 16, 9).to_rfc3339(), "2015-02-18T23:16:09+00:00"); - assert_eq!( - ymdhms_milli(&edt5, 2015, 2, 18, 23, 16, 9, 150).to_rfc3339(), - "2015-02-18T23:16:09.150+05:00" - ); - assert_eq!( - ymdhms_micro(&edt5, 2015, 2, 18, 23, 59, 59, 1_234_567).to_rfc3339(), - "2015-02-18T23:59:60.234567+05:00" - ); - assert_eq!( - DateTime::parse_from_rfc3339("2015-02-18T23:59:59.123+05:00"), - Ok(ymdhms_micro(&edt5, 2015, 2, 18, 23, 59, 59, 123_000)) - ); - assert_eq!( - DateTime::parse_from_rfc3339("2015-02-18T23:59:59.123456+05:00"), - Ok(ymdhms_micro(&edt5, 2015, 2, 18, 23, 59, 59, 123_456)) - ); - assert_eq!( - DateTime::parse_from_rfc3339("2015-02-18T23:59:59.123456789+05:00"), - Ok(ymdhms_nano(&edt5, 2015, 2, 18, 23, 59, 59, 123_456_789)) - ); - assert_eq!( - DateTime::parse_from_rfc3339("2015-02-18T23:16:09Z"), - Ok(ymdhms(&edt0, 2015, 2, 18, 23, 16, 9)) - ); - - assert_eq!( - ymdhms_micro(&edt5, 2015, 2, 18, 23, 59, 59, 1_234_567).to_rfc3339(), - "2015-02-18T23:59:60.234567+05:00" - ); - assert_eq!( - ymdhms_milli(&edt5, 2015, 2, 18, 23, 16, 9, 150).to_rfc3339(), - "2015-02-18T23:16:09.150+05:00" - ); - assert_eq!( - DateTime::parse_from_rfc3339("2015-02-18T00:00:00.234567+05:00"), - Ok(ymdhms_micro(&edt5, 2015, 2, 18, 0, 0, 0, 234_567)) - ); - assert_eq!( - DateTime::parse_from_rfc3339("2015-02-18T23:16:09Z"), - Ok(ymdhms(&edt0, 2015, 2, 18, 23, 16, 9)) - ); - assert_eq!( - DateTime::parse_from_rfc3339("2015-02-18 23:59:60.234567+05:00"), - Ok(ymdhms_micro(&edt5, 2015, 2, 18, 23, 59, 59, 1_234_567)) - ); - assert_eq!(ymdhms_utc(2015, 2, 18, 23, 16, 9).to_rfc3339(), "2015-02-18T23:16:09+00:00"); - - assert!(DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567 +05:00").is_err()); - assert!(DateTime::parse_from_rfc3339("2015-02-18T23:059:60.234567+05:00").is_err()); - assert!(DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00PST").is_err()); - assert!(DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567+PST").is_err()); - assert!(DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567PST").is_err()); - assert!(DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567+0500").is_err()); - assert!(DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00:00").is_err()); - assert!(DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567:+05:00").is_err()); - assert!(DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00 ").is_err()); - assert!(DateTime::parse_from_rfc3339(" 2015-02-18T23:59:60.234567+05:00").is_err()); - assert!(DateTime::parse_from_rfc3339("2015- 02-18T23:59:60.234567+05:00").is_err()); - assert!(DateTime::parse_from_rfc3339("2015-02-18T23:59:60.234567A+05:00").is_err()); -} - -#[test] -#[cfg(feature = "alloc")] -fn test_rfc3339_opts() { - use crate::SecondsFormat::*; - let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap(); - let dt = pst - .from_local_datetime( - &NaiveDate::from_ymd_opt(2018, 1, 11) - .unwrap() - .and_hms_nano_opt(10, 5, 13, 84_660_000) - .unwrap(), - ) - .unwrap(); - assert_eq!(dt.to_rfc3339_opts(Secs, false), "2018-01-11T10:05:13+08:00"); - assert_eq!(dt.to_rfc3339_opts(Secs, true), "2018-01-11T10:05:13+08:00"); - assert_eq!(dt.to_rfc3339_opts(Millis, false), "2018-01-11T10:05:13.084+08:00"); - assert_eq!(dt.to_rfc3339_opts(Micros, false), "2018-01-11T10:05:13.084660+08:00"); - assert_eq!(dt.to_rfc3339_opts(Nanos, false), "2018-01-11T10:05:13.084660000+08:00"); - assert_eq!(dt.to_rfc3339_opts(AutoSi, false), "2018-01-11T10:05:13.084660+08:00"); - - let ut = dt.naive_utc().and_utc(); - assert_eq!(ut.to_rfc3339_opts(Secs, false), "2018-01-11T02:05:13+00:00"); - assert_eq!(ut.to_rfc3339_opts(Secs, true), "2018-01-11T02:05:13Z"); - assert_eq!(ut.to_rfc3339_opts(Millis, false), "2018-01-11T02:05:13.084+00:00"); - assert_eq!(ut.to_rfc3339_opts(Millis, true), "2018-01-11T02:05:13.084Z"); - assert_eq!(ut.to_rfc3339_opts(Micros, true), "2018-01-11T02:05:13.084660Z"); - assert_eq!(ut.to_rfc3339_opts(Nanos, true), "2018-01-11T02:05:13.084660000Z"); - assert_eq!(ut.to_rfc3339_opts(AutoSi, true), "2018-01-11T02:05:13.084660Z"); -} - -#[test] -#[should_panic] -#[cfg(feature = "alloc")] -fn test_rfc3339_opts_nonexhaustive() { - use crate::SecondsFormat; - let dt = Utc.with_ymd_and_hms(1999, 10, 9, 1, 2, 3).unwrap(); - let _ = dt.to_rfc3339_opts(SecondsFormat::__NonExhaustive, true); -} - -#[test] -fn test_datetime_from_str() { - assert_eq!( - "2015-02-18T23:16:9.15Z".parse::>(), - Ok(FixedOffset::east_opt(0) - .unwrap() - .from_local_datetime( - &NaiveDate::from_ymd_opt(2015, 2, 18) - .unwrap() - .and_hms_milli_opt(23, 16, 9, 150) - .unwrap() - ) - .unwrap()) - ); - assert_eq!( - "2015-02-18T23:16:9.15Z".parse::>(), - Ok(Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2015, 2, 18) - .unwrap() - .and_hms_milli_opt(23, 16, 9, 150) - .unwrap() - ) - .unwrap()) - ); - assert_eq!( - "2015-02-18T23:16:9.15 UTC".parse::>(), - Ok(Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2015, 2, 18) - .unwrap() - .and_hms_milli_opt(23, 16, 9, 150) - .unwrap() - ) - .unwrap()) - ); - assert_eq!( - "2015-02-18T23:16:9.15UTC".parse::>(), - Ok(Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2015, 2, 18) - .unwrap() - .and_hms_milli_opt(23, 16, 9, 150) - .unwrap() - ) - .unwrap()) - ); - assert_eq!( - "2015-02-18T23:16:9.15Utc".parse::>(), - Ok(Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2015, 2, 18) - .unwrap() - .and_hms_milli_opt(23, 16, 9, 150) - .unwrap() - ) - .unwrap()) - ); - - assert_eq!( - "2015-2-18T23:16:9.15Z".parse::>(), - Ok(FixedOffset::east_opt(0) - .unwrap() - .from_local_datetime( - &NaiveDate::from_ymd_opt(2015, 2, 18) - .unwrap() - .and_hms_milli_opt(23, 16, 9, 150) - .unwrap() - ) - .unwrap()) - ); - assert_eq!( - "2015-2-18T13:16:9.15-10:00".parse::>(), - Ok(FixedOffset::west_opt(10 * 3600) - .unwrap() - .from_local_datetime( - &NaiveDate::from_ymd_opt(2015, 2, 18) - .unwrap() - .and_hms_milli_opt(13, 16, 9, 150) - .unwrap() - ) - .unwrap()) - ); - assert!("2015-2-18T23:16:9.15".parse::>().is_err()); - - assert_eq!( - "2015-2-18T23:16:9.15Z".parse::>(), - Ok(Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2015, 2, 18) - .unwrap() - .and_hms_milli_opt(23, 16, 9, 150) - .unwrap() - ) - .unwrap()) - ); - assert_eq!( - "2015-2-18T13:16:9.15-10:00".parse::>(), - Ok(Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2015, 2, 18) - .unwrap() - .and_hms_milli_opt(23, 16, 9, 150) - .unwrap() - ) - .unwrap()) - ); - assert!("2015-2-18T23:16:9.15".parse::>().is_err()); - assert!("2015-02-18T23:16:9.15øøø".parse::>().is_err()); - - // no test for `DateTime`, we cannot verify that much. -} - -#[test] -fn test_parse_datetime_utc() { - // valid cases - let valid = [ - "2001-02-03T04:05:06Z", - "2001-02-03T04:05:06+0000", - "2001-02-03T04:05:06-00:00", - "2001-02-03T04:05:06-01:00", - "2012-12-12 12:12:12Z", - "2012-12-12t12:12:12Z", - "2012-12-12T12:12:12Z", - "2012 -12-12T12:12:12Z", - "2012 -12-12T12:12:12Z", - "2012- 12-12T12:12:12Z", - "2012- 12-12T12:12:12Z", - "2012-12-12T 12:12:12Z", - "2012-12-12T12 :12:12Z", - "2012-12-12T12 :12:12Z", - "2012-12-12T12: 12:12Z", - "2012-12-12T12: 12:12Z", - "2012-12-12T12 : 12:12Z", - "2012-12-12T12:12:12Z ", - " 2012-12-12T12:12:12Z", - "2015-02-18T23:16:09.153Z", - "2015-2-18T23:16:09.153Z", - "+2015-2-18T23:16:09.153Z", - "-77-02-18T23:16:09Z", - "+82701-05-6T15:9:60.898989898989Z", - ]; - for &s in &valid { - eprintln!("test_parse_datetime_utc valid {s:?}"); - let d = match s.parse::>() { - Ok(d) => d, - Err(e) => panic!("parsing `{s}` has failed: {e}"), - }; - let s_ = format!("{d:?}"); - // `s` and `s_` may differ, but `s.parse()` and `s_.parse()` must be same - let d_ = match s_.parse::>() { - Ok(d) => d, - Err(e) => { - panic!("`{s}` is parsed into `{d:?}`, but reparsing that has failed: {e}") - } - }; - assert!( - d == d_, - "`{s}` is parsed into `{d:?}`, but reparsed result `{d_:?}` does not match" - ); - } - - // some invalid cases - // since `ParseErrorKind` is private, all we can do is to check if there was an error - let invalid = [ - "", // empty - "Z", // missing data - "15Z", // missing data - "15:8:9Z", // missing date - "15-8-9Z", // missing time or date - "Fri, 09 Aug 2013 23:54:35 GMT", // valid datetime, wrong format - "Sat Jun 30 23:59:60 2012", // valid datetime, wrong format - "1441497364.649", // valid datetime, wrong format - "+1441497364.649", // valid datetime, wrong format - "+1441497364", // valid datetime, wrong format - "+1441497364Z", // valid datetime, wrong format - "2014/02/03 04:05:06Z", // valid datetime, wrong format - "2001-02-03T04:05:0600:00", // valid datetime, timezone too close - "2015-15-15T15:15:15Z", // invalid datetime - "2012-12-12T12:12:12x", // invalid timezone - "2012-123-12T12:12:12Z", // invalid month - "2012-12-77T12:12:12Z", // invalid day - "2012-12-12T26:12:12Z", // invalid hour - "2012-12-12T12:61:12Z", // invalid minute - "2012-12-12T12:12:62Z", // invalid second - "2012-12-12 T12:12:12Z", // space after date - "2012-12-12T12:12:12ZZ", // trailing literal 'Z' - "+802701-12-12T12:12:12Z", // invalid year (out of bounds) - "+ 2012-12-12T12:12:12Z", // invalid space before year - " +82701 - 05 - 6 T 15 : 9 : 60.898989898989 Z", // valid datetime, wrong format - ]; - for &s in &invalid { - eprintln!("test_parse_datetime_utc invalid {s:?}"); - assert!(s.parse::>().is_err()); - } -} - -#[test] -fn test_parse_from_str() { - let edt = FixedOffset::east_opt(570 * 60).unwrap(); - let edt0 = FixedOffset::east_opt(0).unwrap(); - let wdt = FixedOffset::west_opt(10 * 3600).unwrap(); - assert_eq!( - DateTime::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), - Ok(ymdhms(&edt, 2014, 5, 7, 12, 34, 56)) - ); // ignore offset - assert!(DateTime::parse_from_str("20140507000000", "%Y%m%d%H%M%S").is_err()); // no offset - assert!( - DateTime::parse_from_str("Fri, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT") - .is_err() - ); - assert_eq!( - DateTime::parse_from_str("0", "%s").unwrap(), - DateTime::from_timestamp(0, 0).unwrap().fixed_offset() - ); - - assert_eq!( - "2015-02-18T23:16:9.15Z".parse::>(), - Ok(ymdhms_milli(&edt0, 2015, 2, 18, 23, 16, 9, 150)) - ); - assert_eq!( - "2015-02-18T23:16:9.15Z".parse::>(), - Ok(ymdhms_milli_utc(2015, 2, 18, 23, 16, 9, 150)), - ); - assert_eq!( - "2015-02-18T23:16:9.15 UTC".parse::>(), - Ok(ymdhms_milli_utc(2015, 2, 18, 23, 16, 9, 150)) - ); - assert_eq!( - "2015-02-18T23:16:9.15UTC".parse::>(), - Ok(ymdhms_milli_utc(2015, 2, 18, 23, 16, 9, 150)) - ); - - assert_eq!( - "2015-2-18T23:16:9.15Z".parse::>(), - Ok(ymdhms_milli(&edt0, 2015, 2, 18, 23, 16, 9, 150)) - ); - assert_eq!( - "2015-2-18T13:16:9.15-10:00".parse::>(), - Ok(ymdhms_milli(&wdt, 2015, 2, 18, 13, 16, 9, 150)) - ); - assert!("2015-2-18T23:16:9.15".parse::>().is_err()); - - assert_eq!( - "2015-2-18T23:16:9.15Z".parse::>(), - Ok(ymdhms_milli_utc(2015, 2, 18, 23, 16, 9, 150)) - ); - assert_eq!( - "2015-2-18T13:16:9.15-10:00".parse::>(), - Ok(ymdhms_milli_utc(2015, 2, 18, 23, 16, 9, 150)) - ); - assert!("2015-2-18T23:16:9.15".parse::>().is_err()); - - // no test for `DateTime`, we cannot verify that much. -} - -#[test] -fn test_datetime_parse_from_str() { - let dt = ymdhms(&FixedOffset::east_opt(-9 * 60 * 60).unwrap(), 2013, 8, 9, 23, 54, 35); - let parse = DateTime::parse_from_str; - - // timezone variations - - // - // %Z - // - // wrong timezone format - assert!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %Z").is_err()); - // bad timezone data? - assert!(parse("Aug 09 2013 23:54:35 PST", "%b %d %Y %H:%M:%S %Z").is_err()); - // bad timezone data - assert!(parse("Aug 09 2013 23:54:35 XXXXX", "%b %d %Y %H:%M:%S %Z").is_err()); - - // - // %z - // - assert_eq!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -09 00", "%b %d %Y %H:%M:%S %z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -09:00", "%b %d %Y %H:%M:%S %z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -09 : 00", "%b %d %Y %H:%M:%S %z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 --0900", "%b %d %Y %H:%M:%S -%z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 +-0900", "%b %d %Y %H:%M:%S +%z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -09:00 ", "%b %d %Y %H:%M:%S %z "), Ok(dt)); - // trailing newline after timezone - assert!(parse("Aug 09 2013 23:54:35 -09:00\n", "%b %d %Y %H:%M:%S %z").is_err()); - assert_eq!(parse("Aug 09 2013 23:54:35 -09:00\n", "%b %d %Y %H:%M:%S %z "), Ok(dt)); - // trailing colon - assert!(parse("Aug 09 2013 23:54:35 -09:00:", "%b %d %Y %H:%M:%S %z").is_err()); - // trailing colon with space - assert!(parse("Aug 09 2013 23:54:35 -09:00: ", "%b %d %Y %H:%M:%S %z ").is_err()); - // trailing colon, mismatch space - assert!(parse("Aug 09 2013 23:54:35 -09:00:", "%b %d %Y %H:%M:%S %z ").is_err()); - // wrong timezone data - assert!(parse("Aug 09 2013 23:54:35 -09", "%b %d %Y %H:%M:%S %z").is_err()); - assert_eq!(parse("Aug 09 2013 23:54:35 -09::00", "%b %d %Y %H:%M:%S %z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -0900::", "%b %d %Y %H:%M:%S %z::"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -09:00:00", "%b %d %Y %H:%M:%S %z:00"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -09:00:00 ", "%b %d %Y %H:%M:%S %z:00 "), Ok(dt)); - - // - // %:z - // - assert_eq!(parse("Aug 09 2013 23:54:35 -09:00", "%b %d %Y %H:%M:%S %:z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %:z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -09 00", "%b %d %Y %H:%M:%S %:z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -09 : 00", "%b %d %Y %H:%M:%S %:z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -09 : 00:", "%b %d %Y %H:%M:%S %:z:"), Ok(dt)); - // wrong timezone data - assert!(parse("Aug 09 2013 23:54:35 -09", "%b %d %Y %H:%M:%S %:z").is_err()); - assert_eq!(parse("Aug 09 2013 23:54:35 -09::00", "%b %d %Y %H:%M:%S %:z"), Ok(dt)); - // timezone data hs too many colons - assert!(parse("Aug 09 2013 23:54:35 -09:00:", "%b %d %Y %H:%M:%S %:z").is_err()); - // timezone data hs too many colons - assert!(parse("Aug 09 2013 23:54:35 -09:00::", "%b %d %Y %H:%M:%S %:z").is_err()); - assert_eq!(parse("Aug 09 2013 23:54:35 -09:00::", "%b %d %Y %H:%M:%S %:z::"), Ok(dt)); - - // - // %::z - // - assert_eq!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %::z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -09:00", "%b %d %Y %H:%M:%S %::z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -09 : 00", "%b %d %Y %H:%M:%S %::z"), Ok(dt)); - // mismatching colon expectations - assert!(parse("Aug 09 2013 23:54:35 -09:00:00", "%b %d %Y %H:%M:%S %::z").is_err()); - assert_eq!(parse("Aug 09 2013 23:54:35 -09::00", "%b %d %Y %H:%M:%S %::z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -09::00", "%b %d %Y %H:%M:%S %:z"), Ok(dt)); - // wrong timezone data - assert!(parse("Aug 09 2013 23:54:35 -09", "%b %d %Y %H:%M:%S %::z").is_err()); - assert_eq!(parse("Aug 09 2013 23:54:35 -09001234", "%b %d %Y %H:%M:%S %::z1234"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -09:001234", "%b %d %Y %H:%M:%S %::z1234"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -0900 ", "%b %d %Y %H:%M:%S %::z "), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -0900\t\n", "%b %d %Y %H:%M:%S %::z\t\n"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -0900:", "%b %d %Y %H:%M:%S %::z:"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 :-0900:0", "%b %d %Y %H:%M:%S :%::z:0"), Ok(dt)); - // mismatching colons and spaces - assert!(parse("Aug 09 2013 23:54:35 :-0900: ", "%b %d %Y %H:%M:%S :%::z::").is_err()); - // mismatching colons expectations - assert!(parse("Aug 09 2013 23:54:35 -09:00:00", "%b %d %Y %H:%M:%S %::z").is_err()); - assert_eq!(parse("Aug 09 2013 -0900: 23:54:35", "%b %d %Y %::z: %H:%M:%S"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 :-0900:0 23:54:35", "%b %d %Y :%::z:0 %H:%M:%S"), Ok(dt)); - // mismatching colons expectations mid-string - assert!(parse("Aug 09 2013 :-0900: 23:54:35", "%b %d %Y :%::z %H:%M:%S").is_err()); - // mismatching colons expectations, before end - assert!(parse("Aug 09 2013 23:54:35 -09:00:00 ", "%b %d %Y %H:%M:%S %::z ").is_err()); - - // - // %:::z - // - assert_eq!(parse("Aug 09 2013 23:54:35 -09:00", "%b %d %Y %H:%M:%S %:::z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %:::z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -0900 ", "%b %d %Y %H:%M:%S %:::z "), Ok(dt)); - // wrong timezone data - assert!(parse("Aug 09 2013 23:54:35 -09", "%b %d %Y %H:%M:%S %:::z").is_err()); - - // - // %::::z - // - // too many colons - assert!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %::::z").is_err()); - // too many colons - assert!(parse("Aug 09 2013 23:54:35 -09:00", "%b %d %Y %H:%M:%S %::::z").is_err()); - // too many colons - assert!(parse("Aug 09 2013 23:54:35 -09:00:", "%b %d %Y %H:%M:%S %::::z").is_err()); - // too many colons - assert!(parse("Aug 09 2013 23:54:35 -09:00:00", "%b %d %Y %H:%M:%S %::::z").is_err()); - - // - // %#z - // - assert_eq!(parse("Aug 09 2013 23:54:35 -09:00", "%b %d %Y %H:%M:%S %#z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %#z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -09:00 ", "%b %d %Y %H:%M:%S %#z "), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -0900 ", "%b %d %Y %H:%M:%S %#z "), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -09", "%b %d %Y %H:%M:%S %#z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -0900", "%b %d %Y %H:%M:%S %#z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -09:", "%b %d %Y %H:%M:%S %#z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35 -09: ", "%b %d %Y %H:%M:%S %#z "), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35+-09", "%b %d %Y %H:%M:%S+%#z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 23:54:35--09", "%b %d %Y %H:%M:%S-%#z"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 -09:00 23:54:35", "%b %d %Y %#z%H:%M:%S"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 -0900 23:54:35", "%b %d %Y %#z%H:%M:%S"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 -090023:54:35", "%b %d %Y %#z%H:%M:%S"), Ok(dt)); - assert_eq!(parse("Aug 09 2013 -09:0023:54:35", "%b %d %Y %#z%H:%M:%S"), Ok(dt)); - // timezone with partial minutes adjacent hours - assert_ne!(parse("Aug 09 2013 -09023:54:35", "%b %d %Y %#z%H:%M:%S"), Ok(dt)); - // bad timezone data - assert!(parse("Aug 09 2013 23:54:35 -09:00:00", "%b %d %Y %H:%M:%S %#z").is_err()); - // bad timezone data (partial minutes) - assert!(parse("Aug 09 2013 23:54:35 -090", "%b %d %Y %H:%M:%S %#z").is_err()); - // bad timezone data (partial minutes) with trailing space - assert!(parse("Aug 09 2013 23:54:35 -090 ", "%b %d %Y %H:%M:%S %#z ").is_err()); - // bad timezone data (partial minutes) mid-string - assert!(parse("Aug 09 2013 -090 23:54:35", "%b %d %Y %#z %H:%M:%S").is_err()); - // bad timezone data - assert!(parse("Aug 09 2013 -09:00:00 23:54:35", "%b %d %Y %#z %H:%M:%S").is_err()); - // timezone data ambiguous with hours - assert!(parse("Aug 09 2013 -09:00:23:54:35", "%b %d %Y %#z%H:%M:%S").is_err()); -} - -#[test] -fn test_to_string_round_trip() { - let dt = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap(); - let _dt: DateTime = dt.to_string().parse().unwrap(); - - let ndt_fixed = dt.with_timezone(&FixedOffset::east_opt(3600).unwrap()); - let _dt: DateTime = ndt_fixed.to_string().parse().unwrap(); - - let ndt_fixed = dt.with_timezone(&FixedOffset::east_opt(0).unwrap()); - let _dt: DateTime = ndt_fixed.to_string().parse().unwrap(); -} - -#[test] -#[cfg(feature = "clock")] -fn test_to_string_round_trip_with_local() { - let ndt = Local::now(); - let _dt: DateTime = ndt.to_string().parse().unwrap(); -} - -#[test] -#[cfg(feature = "clock")] -fn test_datetime_format_with_local() { - // if we are not around the year boundary, local and UTC date should have the same year - let dt = Local::now().with_month(5).unwrap(); - assert_eq!(dt.format("%Y").to_string(), dt.with_timezone(&Utc).format("%Y").to_string()); -} - -#[test] -fn test_datetime_is_send_and_copy() { - #[derive(Clone)] - struct Tz { - _not_send: *const i32, - } - impl TimeZone for Tz { - type Offset = Off; - - fn from_offset(_: &Self::Offset) -> Self { - unimplemented!() - } - fn offset_from_local_date(&self, _: &NaiveDate) -> crate::MappedLocalTime { - unimplemented!() - } - fn offset_from_local_datetime( - &self, - _: &NaiveDateTime, - ) -> crate::MappedLocalTime { - unimplemented!() - } - fn offset_from_utc_date(&self, _: &NaiveDate) -> Self::Offset { - unimplemented!() - } - fn offset_from_utc_datetime(&self, _: &NaiveDateTime) -> Self::Offset { - unimplemented!() - } - } - - #[derive(Copy, Clone, Debug)] - struct Off(()); - impl Offset for Off { - fn fix(&self) -> FixedOffset { - unimplemented!() - } - } - - fn _assert_send_copy() {} - // `DateTime` is `Send + Copy` if the offset is. - _assert_send_copy::>(); -} - -#[test] -fn test_subsecond_part() { - let datetime = Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2014, 7, 8) - .unwrap() - .and_hms_nano_opt(9, 10, 11, 1234567) - .unwrap(), - ) - .unwrap(); - - assert_eq!(1, datetime.timestamp_subsec_millis()); - assert_eq!(1234, datetime.timestamp_subsec_micros()); - assert_eq!(1234567, datetime.timestamp_subsec_nanos()); -} - -// Some targets, such as `wasm32-wasi`, have a problematic definition of `SystemTime`, such as an -// `i32` (year 2035 problem), or an `u64` (no values before `UNIX-EPOCH`). -// See https://github.com/rust-lang/rust/issues/44394. -#[test] -#[cfg(all(feature = "std", not(all(target_arch = "wasm32", target_os = "wasi"))))] -fn test_from_system_time() { - use std::time::{Duration, SystemTime, UNIX_EPOCH}; - - let nanos = 999_999_000; - - let epoch = Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap(); - - // SystemTime -> DateTime - assert_eq!(DateTime::::from(UNIX_EPOCH), epoch); - assert_eq!( - DateTime::::from(UNIX_EPOCH + Duration::new(999_999_999, nanos)), - Utc.from_local_datetime( - &NaiveDate::from_ymd_opt(2001, 9, 9) - .unwrap() - .and_hms_nano_opt(1, 46, 39, nanos) - .unwrap() - ) - .unwrap() - ); - assert_eq!( - DateTime::::from(UNIX_EPOCH - Duration::new(999_999_999, nanos)), - Utc.from_local_datetime( - &NaiveDate::from_ymd_opt(1938, 4, 24) - .unwrap() - .and_hms_nano_opt(22, 13, 20, 1_000) - .unwrap() - ) - .unwrap() - ); - - // DateTime -> SystemTime - assert_eq!(SystemTime::from(epoch), UNIX_EPOCH); - assert_eq!( - SystemTime::from( - Utc.from_local_datetime( - &NaiveDate::from_ymd_opt(2001, 9, 9) - .unwrap() - .and_hms_nano_opt(1, 46, 39, nanos) - .unwrap() - ) - .unwrap() - ), - UNIX_EPOCH + Duration::new(999_999_999, nanos) - ); - assert_eq!( - SystemTime::from( - Utc.from_local_datetime( - &NaiveDate::from_ymd_opt(1938, 4, 24) - .unwrap() - .and_hms_nano_opt(22, 13, 20, 1_000) - .unwrap() - ) - .unwrap() - ), - UNIX_EPOCH - Duration::new(999_999_999, nanos) - ); - - // DateTime -> SystemTime (via `with_timezone`) - #[cfg(feature = "clock")] - { - assert_eq!(SystemTime::from(epoch.with_timezone(&Local)), UNIX_EPOCH); - } - assert_eq!( - SystemTime::from(epoch.with_timezone(&FixedOffset::east_opt(32400).unwrap())), - UNIX_EPOCH - ); - assert_eq!( - SystemTime::from(epoch.with_timezone(&FixedOffset::west_opt(28800).unwrap())), - UNIX_EPOCH - ); -} - -#[test] -#[allow(deprecated)] -fn test_datetime_from_local() { - // 2000-01-12T02:00:00Z - let naivedatetime_utc = - NaiveDate::from_ymd_opt(2000, 1, 12).unwrap().and_hms_opt(2, 0, 0).unwrap(); - let datetime_utc = DateTime::::from_utc(naivedatetime_utc, Utc); - - // 2000-01-12T10:00:00+8:00:00 - let timezone_east = FixedOffset::east_opt(8 * 60 * 60).unwrap(); - let naivedatetime_east = - NaiveDate::from_ymd_opt(2000, 1, 12).unwrap().and_hms_opt(10, 0, 0).unwrap(); - let datetime_east = DateTime::::from_local(naivedatetime_east, timezone_east); - - // 2000-01-11T19:00:00-7:00:00 - let timezone_west = FixedOffset::west_opt(7 * 60 * 60).unwrap(); - let naivedatetime_west = - NaiveDate::from_ymd_opt(2000, 1, 11).unwrap().and_hms_opt(19, 0, 0).unwrap(); - let datetime_west = DateTime::::from_local(naivedatetime_west, timezone_west); - - assert_eq!(datetime_east, datetime_utc.with_timezone(&timezone_east)); - assert_eq!(datetime_west, datetime_utc.with_timezone(&timezone_west)); -} - -#[test] -#[cfg(feature = "clock")] -fn test_datetime_before_windows_api_limits() { - // dt corresponds to `FILETIME = 147221225472` from issue 651. - // (https://github.com/chronotope/chrono/issues/651) - // This used to fail on Windows for timezones with an offset of -5:00 or greater. - // The API limits years to 1601..=30827. - let dt = NaiveDate::from_ymd_opt(1601, 1, 1).unwrap().and_hms_milli_opt(4, 5, 22, 122).unwrap(); - let local_dt = Local.from_utc_datetime(&dt); - dbg!(local_dt); -} - -#[test] -#[cfg(feature = "clock")] -fn test_years_elapsed() { - // A bit more than 1 year - let one_year_ago = Utc::now().date_naive() - Days::new(400); - // A bit more than 2 years - let two_year_ago = Utc::now().date_naive() - Days::new(750); - - assert_eq!(Utc::now().date_naive().years_since(one_year_ago), Some(1)); - assert_eq!(Utc::now().date_naive().years_since(two_year_ago), Some(2)); - - // If the given DateTime is later than now, the function will always return 0. - let future = Utc::now().date_naive() + Days(100); - assert_eq!(Utc::now().date_naive().years_since(future), None); -} - -#[test] -fn test_datetime_add_assign() { - let naivedatetime = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(); - let datetime = naivedatetime.and_utc(); - let mut datetime_add = datetime; - - datetime_add += TimeDelta::try_seconds(60).unwrap(); - assert_eq!(datetime_add, datetime + TimeDelta::try_seconds(60).unwrap()); - - let timezone = FixedOffset::east_opt(60 * 60).unwrap(); - let datetime = datetime.with_timezone(&timezone); - let datetime_add = datetime_add.with_timezone(&timezone); - - assert_eq!(datetime_add, datetime + TimeDelta::try_seconds(60).unwrap()); - - let timezone = FixedOffset::west_opt(2 * 60 * 60).unwrap(); - let datetime = datetime.with_timezone(&timezone); - let datetime_add = datetime_add.with_timezone(&timezone); - - assert_eq!(datetime_add, datetime + TimeDelta::try_seconds(60).unwrap()); -} - -#[test] -#[cfg(feature = "clock")] -fn test_datetime_add_assign_local() { - let naivedatetime = NaiveDate::from_ymd_opt(2022, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(); - - let datetime = Local.from_utc_datetime(&naivedatetime); - let mut datetime_add = Local.from_utc_datetime(&naivedatetime); - - // ensure we cross a DST transition - for i in 1..=365 { - datetime_add += TimeDelta::try_days(1).unwrap(); - assert_eq!(datetime_add, datetime + TimeDelta::try_days(i).unwrap()) - } -} - -#[test] -fn test_datetime_sub_assign() { - let naivedatetime = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap().and_hms_opt(12, 0, 0).unwrap(); - let datetime = naivedatetime.and_utc(); - let mut datetime_sub = datetime; - - datetime_sub -= TimeDelta::try_minutes(90).unwrap(); - assert_eq!(datetime_sub, datetime - TimeDelta::try_minutes(90).unwrap()); - - let timezone = FixedOffset::east_opt(60 * 60).unwrap(); - let datetime = datetime.with_timezone(&timezone); - let datetime_sub = datetime_sub.with_timezone(&timezone); - - assert_eq!(datetime_sub, datetime - TimeDelta::try_minutes(90).unwrap()); - - let timezone = FixedOffset::west_opt(2 * 60 * 60).unwrap(); - let datetime = datetime.with_timezone(&timezone); - let datetime_sub = datetime_sub.with_timezone(&timezone); - - assert_eq!(datetime_sub, datetime - TimeDelta::try_minutes(90).unwrap()); -} - -#[test] -fn test_min_max_getters() { - let offset_min = FixedOffset::west_opt(2 * 60 * 60).unwrap(); - let beyond_min = offset_min.from_utc_datetime(&NaiveDateTime::MIN); - let offset_max = FixedOffset::east_opt(2 * 60 * 60).unwrap(); - let beyond_max = offset_max.from_utc_datetime(&NaiveDateTime::MAX); - - assert_eq!(format!("{beyond_min:?}"), "-262144-12-31T22:00:00-02:00"); - // RFC 2822 doesn't support years with more than 4 digits. - // assert_eq!(beyond_min.to_rfc2822(), ""); - #[cfg(feature = "alloc")] - assert_eq!(beyond_min.to_rfc3339(), "-262144-12-31T22:00:00-02:00"); - #[cfg(feature = "alloc")] - assert_eq!( - beyond_min.format("%Y-%m-%dT%H:%M:%S%:z").to_string(), - "-262144-12-31T22:00:00-02:00" - ); - assert_eq!(beyond_min.year(), -262144); - assert_eq!(beyond_min.month(), 12); - assert_eq!(beyond_min.month0(), 11); - assert_eq!(beyond_min.day(), 31); - assert_eq!(beyond_min.day0(), 30); - assert_eq!(beyond_min.ordinal(), 366); - assert_eq!(beyond_min.ordinal0(), 365); - assert_eq!(beyond_min.weekday(), Weekday::Wed); - assert_eq!(beyond_min.iso_week().year(), -262143); - assert_eq!(beyond_min.iso_week().week(), 1); - assert_eq!(beyond_min.hour(), 22); - assert_eq!(beyond_min.minute(), 0); - assert_eq!(beyond_min.second(), 0); - assert_eq!(beyond_min.nanosecond(), 0); - - assert_eq!(format!("{beyond_max:?}"), "+262143-01-01T01:59:59.999999999+02:00"); - // RFC 2822 doesn't support years with more than 4 digits. - // assert_eq!(beyond_max.to_rfc2822(), ""); - #[cfg(feature = "alloc")] - assert_eq!(beyond_max.to_rfc3339(), "+262143-01-01T01:59:59.999999999+02:00"); - #[cfg(feature = "alloc")] - assert_eq!( - beyond_max.format("%Y-%m-%dT%H:%M:%S%.9f%:z").to_string(), - "+262143-01-01T01:59:59.999999999+02:00" - ); - assert_eq!(beyond_max.year(), 262143); - assert_eq!(beyond_max.month(), 1); - assert_eq!(beyond_max.month0(), 0); - assert_eq!(beyond_max.day(), 1); - assert_eq!(beyond_max.day0(), 0); - assert_eq!(beyond_max.ordinal(), 1); - assert_eq!(beyond_max.ordinal0(), 0); - assert_eq!(beyond_max.weekday(), Weekday::Tue); - assert_eq!(beyond_max.iso_week().year(), 262143); - assert_eq!(beyond_max.iso_week().week(), 1); - assert_eq!(beyond_max.hour(), 1); - assert_eq!(beyond_max.minute(), 59); - assert_eq!(beyond_max.second(), 59); - assert_eq!(beyond_max.nanosecond(), 999_999_999); -} - -#[test] -fn test_min_max_setters() { - let offset_min = FixedOffset::west_opt(2 * 60 * 60).unwrap(); - let beyond_min = offset_min.from_utc_datetime(&NaiveDateTime::MIN); - let offset_max = FixedOffset::east_opt(2 * 60 * 60).unwrap(); - let beyond_max = offset_max.from_utc_datetime(&NaiveDateTime::MAX); - - assert_eq!(beyond_min.with_year(2020).unwrap().year(), 2020); - assert_eq!(beyond_min.with_year(beyond_min.year()), Some(beyond_min)); - assert_eq!(beyond_min.with_month(beyond_min.month()), Some(beyond_min)); - assert_eq!(beyond_min.with_month(3), None); - assert_eq!(beyond_min.with_month0(beyond_min.month0()), Some(beyond_min)); - assert_eq!(beyond_min.with_month0(3), None); - assert_eq!(beyond_min.with_day(beyond_min.day()), Some(beyond_min)); - assert_eq!(beyond_min.with_day(15), None); - assert_eq!(beyond_min.with_day0(beyond_min.day0()), Some(beyond_min)); - assert_eq!(beyond_min.with_day0(15), None); - assert_eq!(beyond_min.with_ordinal(beyond_min.ordinal()), Some(beyond_min)); - assert_eq!(beyond_min.with_ordinal(200), None); - assert_eq!(beyond_min.with_ordinal0(beyond_min.ordinal0()), Some(beyond_min)); - assert_eq!(beyond_min.with_ordinal0(200), None); - assert_eq!(beyond_min.with_hour(beyond_min.hour()), Some(beyond_min)); - assert_eq!( - beyond_min.with_hour(23), - beyond_min.checked_add_signed(TimeDelta::try_hours(1).unwrap()) - ); - assert_eq!(beyond_min.with_hour(5), None); - assert_eq!(beyond_min.with_minute(0), Some(beyond_min)); - assert_eq!(beyond_min.with_second(0), Some(beyond_min)); - assert_eq!(beyond_min.with_nanosecond(0), Some(beyond_min)); - - assert_eq!(beyond_max.with_year(2020).unwrap().year(), 2020); - assert_eq!(beyond_max.with_year(beyond_max.year()), Some(beyond_max)); - assert_eq!(beyond_max.with_month(beyond_max.month()), Some(beyond_max)); - assert_eq!(beyond_max.with_month(3), None); - assert_eq!(beyond_max.with_month0(beyond_max.month0()), Some(beyond_max)); - assert_eq!(beyond_max.with_month0(3), None); - assert_eq!(beyond_max.with_day(beyond_max.day()), Some(beyond_max)); - assert_eq!(beyond_max.with_day(15), None); - assert_eq!(beyond_max.with_day0(beyond_max.day0()), Some(beyond_max)); - assert_eq!(beyond_max.with_day0(15), None); - assert_eq!(beyond_max.with_ordinal(beyond_max.ordinal()), Some(beyond_max)); - assert_eq!(beyond_max.with_ordinal(200), None); - assert_eq!(beyond_max.with_ordinal0(beyond_max.ordinal0()), Some(beyond_max)); - assert_eq!(beyond_max.with_ordinal0(200), None); - assert_eq!(beyond_max.with_hour(beyond_max.hour()), Some(beyond_max)); - assert_eq!( - beyond_max.with_hour(0), - beyond_max.checked_sub_signed(TimeDelta::try_hours(1).unwrap()) - ); - assert_eq!(beyond_max.with_hour(5), None); - assert_eq!(beyond_max.with_minute(beyond_max.minute()), Some(beyond_max)); - assert_eq!(beyond_max.with_second(beyond_max.second()), Some(beyond_max)); - assert_eq!(beyond_max.with_nanosecond(beyond_max.nanosecond()), Some(beyond_max)); -} - -#[test] -fn test_min_max_add_days() { - let offset_min = FixedOffset::west_opt(2 * 60 * 60).unwrap(); - let beyond_min = offset_min.from_utc_datetime(&NaiveDateTime::MIN); - let offset_max = FixedOffset::east_opt(2 * 60 * 60).unwrap(); - let beyond_max = offset_max.from_utc_datetime(&NaiveDateTime::MAX); - let max_time = NaiveTime::from_hms_nano_opt(23, 59, 59, 999_999_999).unwrap(); - - assert_eq!(beyond_min.checked_add_days(Days::new(0)), Some(beyond_min)); - assert_eq!( - beyond_min.checked_add_days(Days::new(1)), - Some(offset_min.from_utc_datetime(&(NaiveDate::MIN + Days(1)).and_time(NaiveTime::MIN))) - ); - assert_eq!(beyond_min.checked_sub_days(Days::new(0)), Some(beyond_min)); - assert_eq!(beyond_min.checked_sub_days(Days::new(1)), None); - - assert_eq!(beyond_max.checked_add_days(Days::new(0)), Some(beyond_max)); - assert_eq!(beyond_max.checked_add_days(Days::new(1)), None); - assert_eq!(beyond_max.checked_sub_days(Days::new(0)), Some(beyond_max)); - assert_eq!( - beyond_max.checked_sub_days(Days::new(1)), - Some(offset_max.from_utc_datetime(&(NaiveDate::MAX - Days(1)).and_time(max_time))) - ); -} - -#[test] -fn test_min_max_add_months() { - let offset_min = FixedOffset::west_opt(2 * 60 * 60).unwrap(); - let beyond_min = offset_min.from_utc_datetime(&NaiveDateTime::MIN); - let offset_max = FixedOffset::east_opt(2 * 60 * 60).unwrap(); - let beyond_max = offset_max.from_utc_datetime(&NaiveDateTime::MAX); - let max_time = NaiveTime::from_hms_nano_opt(23, 59, 59, 999_999_999).unwrap(); - - assert_eq!(beyond_min.checked_add_months(Months::new(0)), Some(beyond_min)); - assert_eq!( - beyond_min.checked_add_months(Months::new(1)), - Some(offset_min.from_utc_datetime(&(NaiveDate::MIN + Months(1)).and_time(NaiveTime::MIN))) - ); - assert_eq!(beyond_min.checked_sub_months(Months::new(0)), Some(beyond_min)); - assert_eq!(beyond_min.checked_sub_months(Months::new(1)), None); - - assert_eq!(beyond_max.checked_add_months(Months::new(0)), Some(beyond_max)); - assert_eq!(beyond_max.checked_add_months(Months::new(1)), None); - assert_eq!(beyond_max.checked_sub_months(Months::new(0)), Some(beyond_max)); - assert_eq!( - beyond_max.checked_sub_months(Months::new(1)), - Some(offset_max.from_utc_datetime(&(NaiveDate::MAX - Months(1)).and_time(max_time))) - ); -} - -#[test] -#[should_panic] -fn test_local_beyond_min_datetime() { - let min = FixedOffset::west_opt(2 * 60 * 60).unwrap().from_utc_datetime(&NaiveDateTime::MIN); - let _ = min.naive_local(); -} - -#[test] -#[should_panic] -fn test_local_beyond_max_datetime() { - let max = FixedOffset::east_opt(2 * 60 * 60).unwrap().from_utc_datetime(&NaiveDateTime::MAX); - let _ = max.naive_local(); -} - -#[test] -#[cfg(feature = "clock")] -fn test_datetime_sub_assign_local() { - let naivedatetime = NaiveDate::from_ymd_opt(2022, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(); - - let datetime = Local.from_utc_datetime(&naivedatetime); - let mut datetime_sub = Local.from_utc_datetime(&naivedatetime); - - // ensure we cross a DST transition - for i in 1..=365 { - datetime_sub -= TimeDelta::try_days(1).unwrap(); - assert_eq!(datetime_sub, datetime - TimeDelta::try_days(i).unwrap()) - } -} - -#[test] -fn test_core_duration_ops() { - use core::time::Duration; - - let mut utc_dt = Utc.with_ymd_and_hms(2023, 8, 29, 11, 34, 12).unwrap(); - let same = utc_dt + Duration::ZERO; - assert_eq!(utc_dt, same); - - utc_dt += Duration::new(3600, 0); - assert_eq!(utc_dt, Utc.with_ymd_and_hms(2023, 8, 29, 12, 34, 12).unwrap()); -} - -#[test] -#[should_panic] -fn test_core_duration_max() { - use core::time::Duration; - - let mut utc_dt = Utc.with_ymd_and_hms(2023, 8, 29, 11, 34, 12).unwrap(); - utc_dt += Duration::MAX; -} - -#[test] -#[cfg(feature = "clock")] -fn test_datetime_local_from_preserves_offset() { - let naivedatetime = NaiveDate::from_ymd_opt(2023, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(); - - let datetime = Local.from_utc_datetime(&naivedatetime); - let offset = datetime.offset().fix(); - - let datetime_fixed: DateTime = datetime.into(); - assert_eq!(&offset, datetime_fixed.offset()); - assert_eq!(datetime.fixed_offset(), datetime_fixed); -} - -#[test] -fn test_datetime_fixed_offset() { - let naivedatetime = NaiveDate::from_ymd_opt(2023, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(); - - let datetime = Utc.from_utc_datetime(&naivedatetime); - let fixed_utc = FixedOffset::east_opt(0).unwrap(); - assert_eq!(datetime.fixed_offset(), fixed_utc.from_local_datetime(&naivedatetime).unwrap()); - - let fixed_offset = FixedOffset::east_opt(3600).unwrap(); - let datetime_fixed = fixed_offset.from_local_datetime(&naivedatetime).unwrap(); - assert_eq!(datetime_fixed.fixed_offset(), datetime_fixed); -} - -#[test] -fn test_datetime_to_utc() { - let dt = - FixedOffset::east_opt(3600).unwrap().with_ymd_and_hms(2020, 2, 22, 23, 24, 25).unwrap(); - let dt_utc: DateTime = dt.to_utc(); - assert_eq!(dt, dt_utc); -} - -#[test] -fn test_add_sub_months() { - let utc_dt = Utc.with_ymd_and_hms(2018, 9, 5, 23, 58, 0).unwrap(); - assert_eq!(utc_dt + Months::new(15), Utc.with_ymd_and_hms(2019, 12, 5, 23, 58, 0).unwrap()); - - let utc_dt = Utc.with_ymd_and_hms(2020, 1, 31, 23, 58, 0).unwrap(); - assert_eq!(utc_dt + Months::new(1), Utc.with_ymd_and_hms(2020, 2, 29, 23, 58, 0).unwrap()); - assert_eq!(utc_dt + Months::new(2), Utc.with_ymd_and_hms(2020, 3, 31, 23, 58, 0).unwrap()); - - let utc_dt = Utc.with_ymd_and_hms(2018, 9, 5, 23, 58, 0).unwrap(); - assert_eq!(utc_dt - Months::new(15), Utc.with_ymd_and_hms(2017, 6, 5, 23, 58, 0).unwrap()); - - let utc_dt = Utc.with_ymd_and_hms(2020, 3, 31, 23, 58, 0).unwrap(); - assert_eq!(utc_dt - Months::new(1), Utc.with_ymd_and_hms(2020, 2, 29, 23, 58, 0).unwrap()); - assert_eq!(utc_dt - Months::new(2), Utc.with_ymd_and_hms(2020, 1, 31, 23, 58, 0).unwrap()); -} - -#[test] -fn test_auto_conversion() { - let utc_dt = Utc.with_ymd_and_hms(2018, 9, 5, 23, 58, 0).unwrap(); - let cdt_dt = FixedOffset::west_opt(5 * 60 * 60) - .unwrap() - .with_ymd_and_hms(2018, 9, 5, 18, 58, 0) - .unwrap(); - let utc_dt2: DateTime = cdt_dt.into(); - assert_eq!(utc_dt, utc_dt2); -} - -#[test] -#[cfg(feature = "clock")] -#[allow(deprecated)] -fn test_test_deprecated_from_offset() { - let now = Local::now(); - let naive = now.naive_local(); - let utc = now.naive_utc(); - let offset: FixedOffset = *now.offset(); - - assert_eq!(DateTime::::from_local(naive, offset), now); - assert_eq!(DateTime::::from_utc(utc, offset), now); -} - -#[test] -#[cfg(all(feature = "unstable-locales", feature = "alloc"))] -fn locale_decimal_point() { - use crate::Locale::{ar_SY, nl_NL}; - let dt = - Utc.with_ymd_and_hms(2018, 9, 5, 18, 58, 0).unwrap().with_nanosecond(123456780).unwrap(); - - assert_eq!(dt.format_localized("%T%.f", nl_NL).to_string(), "18:58:00,123456780"); - assert_eq!(dt.format_localized("%T%.3f", nl_NL).to_string(), "18:58:00,123"); - assert_eq!(dt.format_localized("%T%.6f", nl_NL).to_string(), "18:58:00,123456"); - assert_eq!(dt.format_localized("%T%.9f", nl_NL).to_string(), "18:58:00,123456780"); - - assert_eq!(dt.format_localized("%T%.f", ar_SY).to_string(), "18:58:00.123456780"); - assert_eq!(dt.format_localized("%T%.3f", ar_SY).to_string(), "18:58:00.123"); - assert_eq!(dt.format_localized("%T%.6f", ar_SY).to_string(), "18:58:00.123456"); - assert_eq!(dt.format_localized("%T%.9f", ar_SY).to_string(), "18:58:00.123456780"); -} - -/// This is an extended test for . -#[test] -fn nano_roundrip() { - const BILLION: i64 = 1_000_000_000; - - for nanos in [ - i64::MIN, - i64::MIN + 1, - i64::MIN + 2, - i64::MIN + BILLION - 1, - i64::MIN + BILLION, - i64::MIN + BILLION + 1, - -BILLION - 1, - -BILLION, - -BILLION + 1, - 0, - BILLION - 1, - BILLION, - BILLION + 1, - i64::MAX - BILLION - 1, - i64::MAX - BILLION, - i64::MAX - BILLION + 1, - i64::MAX - 2, - i64::MAX - 1, - i64::MAX, - ] { - println!("nanos: {nanos}"); - let dt = Utc.timestamp_nanos(nanos); - let nanos2 = dt.timestamp_nanos_opt().expect("value roundtrips"); - assert_eq!(nanos, nanos2); - } -} diff --git a/chrono-0.4.44/src/format/formatting.rs b/chrono-0.4.44/src/format/formatting.rs deleted file mode 100644 index d95c0acbef..0000000000 --- a/chrono-0.4.44/src/format/formatting.rs +++ /dev/null @@ -1,951 +0,0 @@ -// This is a part of Chrono. -// See README.md and LICENSE.txt for details. - -//! Date and time formatting routines. - -#[cfg(all(feature = "alloc", not(feature = "std"), not(test)))] -use alloc::string::{String, ToString}; -#[cfg(feature = "alloc")] -use core::borrow::Borrow; -#[cfg(feature = "alloc")] -use core::fmt::Display; -use core::fmt::{self, Write}; - -#[cfg(feature = "alloc")] -use crate::offset::Offset; -#[cfg(any(feature = "alloc", feature = "serde"))] -use crate::{Datelike, FixedOffset, NaiveDateTime, Timelike}; -#[cfg(feature = "alloc")] -use crate::{NaiveDate, NaiveTime, Weekday}; - -#[cfg(feature = "alloc")] -use super::locales; -#[cfg(any(feature = "alloc", feature = "serde"))] -use super::{Colons, OffsetFormat, OffsetPrecision, Pad}; -#[cfg(feature = "alloc")] -use super::{Fixed, InternalFixed, InternalInternal, Item, Numeric}; -#[cfg(feature = "alloc")] -use locales::*; - -/// A *temporary* object which can be used as an argument to `format!` or others. -/// This is normally constructed via `format` methods of each date and time type. -#[cfg(feature = "alloc")] -#[derive(Debug)] -pub struct DelayedFormat { - /// The date view, if any. - date: Option, - /// The time view, if any. - time: Option, - /// The name and local-to-UTC difference for the offset (timezone), if any. - off: Option<(String, FixedOffset)>, - /// An iterator returning formatting items. - items: I, - /// Locale used for text. - /// ZST if the `unstable-locales` feature is not enabled. - locale: Locale, -} - -#[cfg(feature = "alloc")] -impl<'a, I: Iterator + Clone, B: Borrow>> DelayedFormat { - /// Makes a new `DelayedFormat` value out of local date and time. - #[must_use] - pub fn new(date: Option, time: Option, items: I) -> DelayedFormat { - DelayedFormat { date, time, off: None, items, locale: default_locale() } - } - - /// Makes a new `DelayedFormat` value out of local date and time and UTC offset. - #[must_use] - pub fn new_with_offset( - date: Option, - time: Option, - offset: &Off, - items: I, - ) -> DelayedFormat - where - Off: Offset + Display, - { - let name_and_diff = (offset.to_string(), offset.fix()); - DelayedFormat { date, time, off: Some(name_and_diff), items, locale: default_locale() } - } - - /// Makes a new `DelayedFormat` value out of local date and time and locale. - #[cfg(feature = "unstable-locales")] - #[must_use] - pub fn new_with_locale( - date: Option, - time: Option, - items: I, - locale: Locale, - ) -> DelayedFormat { - DelayedFormat { date, time, off: None, items, locale } - } - - /// Makes a new `DelayedFormat` value out of local date and time, UTC offset and locale. - #[cfg(feature = "unstable-locales")] - #[must_use] - pub fn new_with_offset_and_locale( - date: Option, - time: Option, - offset: &Off, - items: I, - locale: Locale, - ) -> DelayedFormat - where - Off: Offset + Display, - { - let name_and_diff = (offset.to_string(), offset.fix()); - DelayedFormat { date, time, off: Some(name_and_diff), items, locale } - } - - /// Formats `DelayedFormat` into a `core::fmt::Write` instance. - /// # Errors - /// This function returns a `core::fmt::Error` if formatting into the `core::fmt::Write` instance fails. - /// - /// # Example - /// ### Writing to a String - /// ``` - /// let dt = chrono::DateTime::from_timestamp(1643723400, 123456789).unwrap(); - /// let df = dt.format("%Y-%m-%d %H:%M:%S%.9f"); - /// let mut buffer = String::new(); - /// let _ = df.write_to(&mut buffer); - /// ``` - pub fn write_to(&self, w: &mut (impl Write + ?Sized)) -> fmt::Result { - for item in self.items.clone() { - match *item.borrow() { - Item::Literal(s) | Item::Space(s) => w.write_str(s), - #[cfg(feature = "alloc")] - Item::OwnedLiteral(ref s) | Item::OwnedSpace(ref s) => w.write_str(s), - Item::Numeric(ref spec, pad) => self.format_numeric(w, spec, pad), - Item::Fixed(ref spec) => self.format_fixed(w, spec), - Item::Error => Err(fmt::Error), - }?; - } - Ok(()) - } - - #[cfg(feature = "alloc")] - fn format_numeric( - &self, - w: &mut (impl Write + ?Sized), - spec: &Numeric, - pad: Pad, - ) -> fmt::Result { - use self::Numeric::*; - - fn write_one(w: &mut (impl Write + ?Sized), v: u8) -> fmt::Result { - w.write_char((b'0' + v) as char) - } - - fn write_two(w: &mut (impl Write + ?Sized), v: u8, pad: Pad) -> fmt::Result { - let ones = b'0' + v % 10; - match (v / 10, pad) { - (0, Pad::None) => {} - (0, Pad::Space) => w.write_char(' ')?, - (tens, _) => w.write_char((b'0' + tens) as char)?, - } - w.write_char(ones as char) - } - - #[inline] - fn write_year(w: &mut (impl Write + ?Sized), year: i32, pad: Pad) -> fmt::Result { - if (1000..=9999).contains(&year) { - // fast path - write_hundreds(w, (year / 100) as u8)?; - write_hundreds(w, (year % 100) as u8) - } else { - write_n(w, 4, year as i64, pad, !(0..10_000).contains(&year)) - } - } - - fn write_n( - w: &mut (impl Write + ?Sized), - n: usize, - v: i64, - pad: Pad, - always_sign: bool, - ) -> fmt::Result { - if always_sign { - match pad { - Pad::None => write!(w, "{v:+}"), - Pad::Zero => write!(w, "{:+01$}", v, n + 1), - Pad::Space => write!(w, "{:+1$}", v, n + 1), - } - } else { - match pad { - Pad::None => write!(w, "{v}"), - Pad::Zero => write!(w, "{v:0n$}"), - Pad::Space => write!(w, "{v:n$}"), - } - } - } - - match (spec, self.date, self.time) { - (Year, Some(d), _) => write_year(w, d.year(), pad), - (YearDiv100, Some(d), _) => write_two(w, d.year().div_euclid(100) as u8, pad), - (YearMod100, Some(d), _) => write_two(w, d.year().rem_euclid(100) as u8, pad), - (IsoYear, Some(d), _) => write_year(w, d.iso_week().year(), pad), - (IsoYearDiv100, Some(d), _) => { - write_two(w, d.iso_week().year().div_euclid(100) as u8, pad) - } - (IsoYearMod100, Some(d), _) => { - write_two(w, d.iso_week().year().rem_euclid(100) as u8, pad) - } - (Quarter, Some(d), _) => write_one(w, d.quarter() as u8), - (Month, Some(d), _) => write_two(w, d.month() as u8, pad), - (Day, Some(d), _) => write_two(w, d.day() as u8, pad), - (WeekFromSun, Some(d), _) => write_two(w, d.weeks_from(Weekday::Sun) as u8, pad), - (WeekFromMon, Some(d), _) => write_two(w, d.weeks_from(Weekday::Mon) as u8, pad), - (IsoWeek, Some(d), _) => write_two(w, d.iso_week().week() as u8, pad), - (NumDaysFromSun, Some(d), _) => write_one(w, d.weekday().num_days_from_sunday() as u8), - (WeekdayFromMon, Some(d), _) => write_one(w, d.weekday().number_from_monday() as u8), - (Ordinal, Some(d), _) => write_n(w, 3, d.ordinal() as i64, pad, false), - (Hour, _, Some(t)) => write_two(w, t.hour() as u8, pad), - (Hour12, _, Some(t)) => write_two(w, t.hour12().1 as u8, pad), - (Minute, _, Some(t)) => write_two(w, t.minute() as u8, pad), - (Second, _, Some(t)) => { - write_two(w, (t.second() + t.nanosecond() / 1_000_000_000) as u8, pad) - } - (Nanosecond, _, Some(t)) => { - write_n(w, 9, (t.nanosecond() % 1_000_000_000) as i64, pad, false) - } - (Timestamp, Some(d), Some(t)) => { - let offset = self.off.as_ref().map(|(_, o)| i64::from(o.local_minus_utc())); - let timestamp = d.and_time(t).and_utc().timestamp() - offset.unwrap_or(0); - write_n(w, 9, timestamp, pad, false) - } - (Internal(_), _, _) => Ok(()), // for future expansion - _ => Err(fmt::Error), // insufficient arguments for given format - } - } - - #[cfg(feature = "alloc")] - fn format_fixed(&self, w: &mut (impl Write + ?Sized), spec: &Fixed) -> fmt::Result { - use Fixed::*; - use InternalInternal::*; - - match (spec, self.date, self.time, self.off.as_ref()) { - (ShortMonthName, Some(d), _, _) => { - w.write_str(short_months(self.locale)[d.month0() as usize]) - } - (LongMonthName, Some(d), _, _) => { - w.write_str(long_months(self.locale)[d.month0() as usize]) - } - (ShortWeekdayName, Some(d), _, _) => w.write_str( - short_weekdays(self.locale)[d.weekday().num_days_from_sunday() as usize], - ), - (LongWeekdayName, Some(d), _, _) => { - w.write_str(long_weekdays(self.locale)[d.weekday().num_days_from_sunday() as usize]) - } - (LowerAmPm, _, Some(t), _) => { - let ampm = if t.hour12().0 { am_pm(self.locale)[1] } else { am_pm(self.locale)[0] }; - for c in ampm.chars().flat_map(|c| c.to_lowercase()) { - w.write_char(c)? - } - Ok(()) - } - (UpperAmPm, _, Some(t), _) => { - let ampm = if t.hour12().0 { am_pm(self.locale)[1] } else { am_pm(self.locale)[0] }; - w.write_str(ampm) - } - (Nanosecond, _, Some(t), _) => { - let nano = t.nanosecond() % 1_000_000_000; - if nano == 0 { - Ok(()) - } else { - w.write_str(decimal_point(self.locale))?; - if nano % 1_000_000 == 0 { - write!(w, "{:03}", nano / 1_000_000) - } else if nano % 1_000 == 0 { - write!(w, "{:06}", nano / 1_000) - } else { - write!(w, "{nano:09}") - } - } - } - (Nanosecond3, _, Some(t), _) => { - w.write_str(decimal_point(self.locale))?; - write!(w, "{:03}", t.nanosecond() / 1_000_000 % 1000) - } - (Nanosecond6, _, Some(t), _) => { - w.write_str(decimal_point(self.locale))?; - write!(w, "{:06}", t.nanosecond() / 1_000 % 1_000_000) - } - (Nanosecond9, _, Some(t), _) => { - w.write_str(decimal_point(self.locale))?; - write!(w, "{:09}", t.nanosecond() % 1_000_000_000) - } - (Internal(InternalFixed { val: Nanosecond3NoDot }), _, Some(t), _) => { - write!(w, "{:03}", t.nanosecond() / 1_000_000 % 1_000) - } - (Internal(InternalFixed { val: Nanosecond6NoDot }), _, Some(t), _) => { - write!(w, "{:06}", t.nanosecond() / 1_000 % 1_000_000) - } - (Internal(InternalFixed { val: Nanosecond9NoDot }), _, Some(t), _) => { - write!(w, "{:09}", t.nanosecond() % 1_000_000_000) - } - (TimezoneName, _, _, Some((tz_name, _))) => write!(w, "{tz_name}"), - (TimezoneOffset | TimezoneOffsetZ, _, _, Some((_, off))) => { - let offset_format = OffsetFormat { - precision: OffsetPrecision::Minutes, - colons: Colons::Maybe, - allow_zulu: *spec == TimezoneOffsetZ, - padding: Pad::Zero, - }; - offset_format.format(w, *off) - } - (TimezoneOffsetColon | TimezoneOffsetColonZ, _, _, Some((_, off))) => { - let offset_format = OffsetFormat { - precision: OffsetPrecision::Minutes, - colons: Colons::Colon, - allow_zulu: *spec == TimezoneOffsetColonZ, - padding: Pad::Zero, - }; - offset_format.format(w, *off) - } - (TimezoneOffsetDoubleColon, _, _, Some((_, off))) => { - let offset_format = OffsetFormat { - precision: OffsetPrecision::Seconds, - colons: Colons::Colon, - allow_zulu: false, - padding: Pad::Zero, - }; - offset_format.format(w, *off) - } - (TimezoneOffsetTripleColon, _, _, Some((_, off))) => { - let offset_format = OffsetFormat { - precision: OffsetPrecision::Hours, - colons: Colons::None, - allow_zulu: false, - padding: Pad::Zero, - }; - offset_format.format(w, *off) - } - (RFC2822, Some(d), Some(t), Some((_, off))) => { - write_rfc2822(w, crate::NaiveDateTime::new(d, t), *off) - } - (RFC3339, Some(d), Some(t), Some((_, off))) => write_rfc3339( - w, - crate::NaiveDateTime::new(d, t), - *off, - SecondsFormat::AutoSi, - false, - ), - _ => Err(fmt::Error), // insufficient arguments for given format - } - } -} - -#[cfg(feature = "alloc")] -impl<'a, I: Iterator + Clone, B: Borrow>> Display for DelayedFormat { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut result = String::new(); - self.write_to(&mut result)?; - f.pad(&result) - } -} - -/// Tries to format given arguments with given formatting items. -/// Internally used by `DelayedFormat`. -#[cfg(feature = "alloc")] -#[deprecated(since = "0.4.32", note = "Use DelayedFormat::fmt or DelayedFormat::write_to instead")] -pub fn format<'a, I, B>( - w: &mut fmt::Formatter, - date: Option<&NaiveDate>, - time: Option<&NaiveTime>, - off: Option<&(String, FixedOffset)>, - items: I, -) -> fmt::Result -where - I: Iterator + Clone, - B: Borrow>, -{ - DelayedFormat { - date: date.copied(), - time: time.copied(), - off: off.cloned(), - items, - locale: default_locale(), - } - .fmt(w) -} - -/// Formats single formatting item -#[cfg(feature = "alloc")] -#[deprecated(since = "0.4.32", note = "Use DelayedFormat::fmt or DelayedFormat::write_to instead")] -pub fn format_item( - w: &mut fmt::Formatter, - date: Option<&NaiveDate>, - time: Option<&NaiveTime>, - off: Option<&(String, FixedOffset)>, - item: &Item<'_>, -) -> fmt::Result { - DelayedFormat { - date: date.copied(), - time: time.copied(), - off: off.cloned(), - items: [item].into_iter(), - locale: default_locale(), - } - .fmt(w) -} - -#[cfg(any(feature = "alloc", feature = "serde"))] -impl OffsetFormat { - /// Writes an offset from UTC with the format defined by `self`. - fn format(&self, w: &mut (impl Write + ?Sized), off: FixedOffset) -> fmt::Result { - let off = off.local_minus_utc(); - if self.allow_zulu && off == 0 { - w.write_char('Z')?; - return Ok(()); - } - let (sign, off) = if off < 0 { ('-', -off) } else { ('+', off) }; - - let hours; - let mut mins = 0; - let mut secs = 0; - let precision = match self.precision { - OffsetPrecision::Hours => { - // Minutes and seconds are simply truncated - hours = (off / 3600) as u8; - OffsetPrecision::Hours - } - OffsetPrecision::Minutes | OffsetPrecision::OptionalMinutes => { - // Round seconds to the nearest minute. - let minutes = (off + 30) / 60; - mins = (minutes % 60) as u8; - hours = (minutes / 60) as u8; - if self.precision == OffsetPrecision::OptionalMinutes && mins == 0 { - OffsetPrecision::Hours - } else { - OffsetPrecision::Minutes - } - } - OffsetPrecision::Seconds - | OffsetPrecision::OptionalSeconds - | OffsetPrecision::OptionalMinutesAndSeconds => { - let minutes = off / 60; - secs = (off % 60) as u8; - mins = (minutes % 60) as u8; - hours = (minutes / 60) as u8; - if self.precision != OffsetPrecision::Seconds && secs == 0 { - if self.precision == OffsetPrecision::OptionalMinutesAndSeconds && mins == 0 { - OffsetPrecision::Hours - } else { - OffsetPrecision::Minutes - } - } else { - OffsetPrecision::Seconds - } - } - }; - let colons = self.colons == Colons::Colon; - - if hours < 10 { - if self.padding == Pad::Space { - w.write_char(' ')?; - } - w.write_char(sign)?; - if self.padding == Pad::Zero { - w.write_char('0')?; - } - w.write_char((b'0' + hours) as char)?; - } else { - w.write_char(sign)?; - write_hundreds(w, hours)?; - } - if let OffsetPrecision::Minutes | OffsetPrecision::Seconds = precision { - if colons { - w.write_char(':')?; - } - write_hundreds(w, mins)?; - } - if let OffsetPrecision::Seconds = precision { - if colons { - w.write_char(':')?; - } - write_hundreds(w, secs)?; - } - Ok(()) - } -} - -/// Specific formatting options for seconds. This may be extended in the -/// future, so exhaustive matching in external code is not recommended. -/// -/// See the `TimeZone::to_rfc3339_opts` function for usage. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[allow(clippy::manual_non_exhaustive)] -pub enum SecondsFormat { - /// Format whole seconds only, with no decimal point nor subseconds. - Secs, - - /// Use fixed 3 subsecond digits. This corresponds to [Fixed::Nanosecond3]. - Millis, - - /// Use fixed 6 subsecond digits. This corresponds to [Fixed::Nanosecond6]. - Micros, - - /// Use fixed 9 subsecond digits. This corresponds to [Fixed::Nanosecond9]. - Nanos, - - /// Automatically select one of `Secs`, `Millis`, `Micros`, or `Nanos` to display all available - /// non-zero sub-second digits. This corresponds to [Fixed::Nanosecond]. - AutoSi, - - // Do not match against this. - #[doc(hidden)] - __NonExhaustive, -} - -/// Writes the date, time and offset to the string. same as `%Y-%m-%dT%H:%M:%S%.f%:z` -#[inline] -#[cfg(any(feature = "alloc", feature = "serde"))] -pub(crate) fn write_rfc3339( - w: &mut (impl Write + ?Sized), - dt: NaiveDateTime, - off: FixedOffset, - secform: SecondsFormat, - use_z: bool, -) -> fmt::Result { - let year = dt.date().year(); - if (0..=9999).contains(&year) { - write_hundreds(w, (year / 100) as u8)?; - write_hundreds(w, (year % 100) as u8)?; - } else { - // ISO 8601 requires the explicit sign for out-of-range years - write!(w, "{year:+05}")?; - } - w.write_char('-')?; - write_hundreds(w, dt.date().month() as u8)?; - w.write_char('-')?; - write_hundreds(w, dt.date().day() as u8)?; - - w.write_char('T')?; - - let (hour, min, mut sec) = dt.time().hms(); - let mut nano = dt.nanosecond(); - if nano >= 1_000_000_000 { - sec += 1; - nano -= 1_000_000_000; - } - write_hundreds(w, hour as u8)?; - w.write_char(':')?; - write_hundreds(w, min as u8)?; - w.write_char(':')?; - let sec = sec; - write_hundreds(w, sec as u8)?; - - match secform { - SecondsFormat::Secs => {} - SecondsFormat::Millis => write!(w, ".{:03}", nano / 1_000_000)?, - SecondsFormat::Micros => write!(w, ".{:06}", nano / 1000)?, - SecondsFormat::Nanos => write!(w, ".{nano:09}")?, - SecondsFormat::AutoSi => { - if nano == 0 { - } else if nano % 1_000_000 == 0 { - write!(w, ".{:03}", nano / 1_000_000)? - } else if nano % 1_000 == 0 { - write!(w, ".{:06}", nano / 1_000)? - } else { - write!(w, ".{nano:09}")? - } - } - SecondsFormat::__NonExhaustive => unreachable!(), - }; - - OffsetFormat { - precision: OffsetPrecision::Minutes, - colons: Colons::Colon, - allow_zulu: use_z, - padding: Pad::Zero, - } - .format(w, off) -} - -#[cfg(feature = "alloc")] -/// write datetimes like `Tue, 1 Jul 2003 10:52:37 +0200`, same as `%a, %d %b %Y %H:%M:%S %z` -pub(crate) fn write_rfc2822( - w: &mut (impl Write + ?Sized), - dt: NaiveDateTime, - off: FixedOffset, -) -> fmt::Result { - let year = dt.year(); - // RFC2822 is only defined on years 0 through 9999 - if !(0..=9999).contains(&year) { - return Err(fmt::Error); - } - - let english = default_locale(); - - w.write_str(short_weekdays(english)[dt.weekday().num_days_from_sunday() as usize])?; - w.write_str(", ")?; - let day = dt.day(); - if day < 10 { - w.write_char((b'0' + day as u8) as char)?; - } else { - write_hundreds(w, day as u8)?; - } - w.write_char(' ')?; - w.write_str(short_months(english)[dt.month0() as usize])?; - w.write_char(' ')?; - write_hundreds(w, (year / 100) as u8)?; - write_hundreds(w, (year % 100) as u8)?; - w.write_char(' ')?; - - let (hour, min, sec) = dt.time().hms(); - write_hundreds(w, hour as u8)?; - w.write_char(':')?; - write_hundreds(w, min as u8)?; - w.write_char(':')?; - let sec = sec + dt.nanosecond() / 1_000_000_000; - write_hundreds(w, sec as u8)?; - w.write_char(' ')?; - OffsetFormat { - precision: OffsetPrecision::Minutes, - colons: Colons::None, - allow_zulu: false, - padding: Pad::Zero, - } - .format(w, off) -} - -/// Equivalent to `{:02}` formatting for n < 100. -pub(crate) fn write_hundreds(w: &mut (impl Write + ?Sized), n: u8) -> fmt::Result { - if n >= 100 { - return Err(fmt::Error); - } - - let tens = b'0' + n / 10; - let ones = b'0' + n % 10; - w.write_char(tens as char)?; - w.write_char(ones as char) -} - -#[cfg(test)] -#[cfg(feature = "alloc")] -mod tests { - use super::{Colons, OffsetFormat, OffsetPrecision, Pad}; - use crate::FixedOffset; - #[cfg(feature = "alloc")] - use crate::{NaiveDate, NaiveTime, TimeZone, Timelike, Utc}; - - #[cfg(feature = "alloc")] - #[test] - fn test_delayed_write_to() { - let dt = crate::DateTime::from_timestamp(1643723400, 123456789).unwrap(); - let df = dt.format("%Y-%m-%d %H:%M:%S%.9f"); - - let mut dt_str = String::new(); - - df.write_to(&mut dt_str).unwrap(); - assert_eq!(dt_str, "2022-02-01 13:50:00.123456789"); - } - - #[cfg(all(feature = "std", feature = "unstable-locales", feature = "alloc"))] - #[test] - fn test_with_locale_delayed_write_to() { - use crate::DateTime; - use crate::format::locales::Locale; - - let dt = DateTime::from_timestamp(1643723400, 123456789).unwrap(); - let df = dt.format_localized("%A, %B %d, %Y", Locale::ja_JP); - - let mut dt_str = String::new(); - - df.write_to(&mut dt_str).unwrap(); - - assert_eq!(dt_str, "ç«æ›œæ—¥, 2月 01, 2022"); - } - - #[test] - #[cfg(feature = "alloc")] - fn test_date_format() { - let d = NaiveDate::from_ymd_opt(2012, 3, 4).unwrap(); - assert_eq!(d.format("%Y,%C,%y,%G,%g").to_string(), "2012,20,12,2012,12"); - assert_eq!(d.format("%m,%b,%h,%B").to_string(), "03,Mar,Mar,March"); - assert_eq!(d.format("%q").to_string(), "1"); - assert_eq!(d.format("%d,%e").to_string(), "04, 4"); - assert_eq!(d.format("%U,%W,%V").to_string(), "10,09,09"); - assert_eq!(d.format("%a,%A,%w,%u").to_string(), "Sun,Sunday,0,7"); - assert_eq!(d.format("%j").to_string(), "064"); // since 2012 is a leap year - assert_eq!(d.format("%D,%x").to_string(), "03/04/12,03/04/12"); - assert_eq!(d.format("%F").to_string(), "2012-03-04"); - assert_eq!(d.format("%v").to_string(), " 4-Mar-2012"); - assert_eq!(d.format("%t%n%%%n%t").to_string(), "\t\n%\n\t"); - - // non-four-digit years - assert_eq!( - NaiveDate::from_ymd_opt(12345, 1, 1).unwrap().format("%Y").to_string(), - "+12345" - ); - assert_eq!(NaiveDate::from_ymd_opt(1234, 1, 1).unwrap().format("%Y").to_string(), "1234"); - assert_eq!(NaiveDate::from_ymd_opt(123, 1, 1).unwrap().format("%Y").to_string(), "0123"); - assert_eq!(NaiveDate::from_ymd_opt(12, 1, 1).unwrap().format("%Y").to_string(), "0012"); - assert_eq!(NaiveDate::from_ymd_opt(1, 1, 1).unwrap().format("%Y").to_string(), "0001"); - assert_eq!(NaiveDate::from_ymd_opt(0, 1, 1).unwrap().format("%Y").to_string(), "0000"); - assert_eq!(NaiveDate::from_ymd_opt(-1, 1, 1).unwrap().format("%Y").to_string(), "-0001"); - assert_eq!(NaiveDate::from_ymd_opt(-12, 1, 1).unwrap().format("%Y").to_string(), "-0012"); - assert_eq!(NaiveDate::from_ymd_opt(-123, 1, 1).unwrap().format("%Y").to_string(), "-0123"); - assert_eq!(NaiveDate::from_ymd_opt(-1234, 1, 1).unwrap().format("%Y").to_string(), "-1234"); - assert_eq!( - NaiveDate::from_ymd_opt(-12345, 1, 1).unwrap().format("%Y").to_string(), - "-12345" - ); - - // corner cases - assert_eq!( - NaiveDate::from_ymd_opt(2007, 12, 31).unwrap().format("%G,%g,%U,%W,%V").to_string(), - "2008,08,52,53,01" - ); - assert_eq!( - NaiveDate::from_ymd_opt(2010, 1, 3).unwrap().format("%G,%g,%U,%W,%V").to_string(), - "2009,09,01,00,53" - ); - } - - #[test] - #[cfg(feature = "alloc")] - fn test_time_format() { - let t = NaiveTime::from_hms_nano_opt(3, 5, 7, 98765432).unwrap(); - assert_eq!(t.format("%H,%k,%I,%l,%P,%p").to_string(), "03, 3,03, 3,am,AM"); - assert_eq!(t.format("%M").to_string(), "05"); - assert_eq!(t.format("%S,%f,%.f").to_string(), "07,098765432,.098765432"); - assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".098,.098765,.098765432"); - assert_eq!(t.format("%R").to_string(), "03:05"); - assert_eq!(t.format("%T,%X").to_string(), "03:05:07,03:05:07"); - assert_eq!(t.format("%r").to_string(), "03:05:07 AM"); - assert_eq!(t.format("%t%n%%%n%t").to_string(), "\t\n%\n\t"); - - let t = NaiveTime::from_hms_micro_opt(3, 5, 7, 432100).unwrap(); - assert_eq!(t.format("%S,%f,%.f").to_string(), "07,432100000,.432100"); - assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".432,.432100,.432100000"); - - let t = NaiveTime::from_hms_milli_opt(3, 5, 7, 210).unwrap(); - assert_eq!(t.format("%S,%f,%.f").to_string(), "07,210000000,.210"); - assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".210,.210000,.210000000"); - - let t = NaiveTime::from_hms_opt(3, 5, 7).unwrap(); - assert_eq!(t.format("%S,%f,%.f").to_string(), "07,000000000,"); - assert_eq!(t.format("%.3f,%.6f,%.9f").to_string(), ".000,.000000,.000000000"); - - // corner cases - assert_eq!( - NaiveTime::from_hms_opt(13, 57, 9).unwrap().format("%r").to_string(), - "01:57:09 PM" - ); - assert_eq!( - NaiveTime::from_hms_milli_opt(23, 59, 59, 1_000).unwrap().format("%X").to_string(), - "23:59:60" - ); - } - - #[test] - #[cfg(feature = "alloc")] - fn test_datetime_format() { - let dt = - NaiveDate::from_ymd_opt(2010, 9, 8).unwrap().and_hms_milli_opt(7, 6, 54, 321).unwrap(); - assert_eq!(dt.format("%c").to_string(), "Wed Sep 8 07:06:54 2010"); - assert_eq!(dt.format("%s").to_string(), "1283929614"); - assert_eq!(dt.format("%t%n%%%n%t").to_string(), "\t\n%\n\t"); - - // a horror of leap second: coming near to you. - let dt = NaiveDate::from_ymd_opt(2012, 6, 30) - .unwrap() - .and_hms_milli_opt(23, 59, 59, 1_000) - .unwrap(); - assert_eq!(dt.format("%c").to_string(), "Sat Jun 30 23:59:60 2012"); - assert_eq!(dt.format("%s").to_string(), "1341100799"); // not 1341100800, it's intentional. - } - - #[test] - #[cfg(feature = "alloc")] - fn test_datetime_format_alignment() { - let datetime = Utc - .with_ymd_and_hms(2007, 1, 2, 12, 34, 56) - .unwrap() - .with_nanosecond(123456789) - .unwrap(); - - // Item::Literal, odd number of padding bytes. - let percent = datetime.format("%%"); - assert_eq!(" %", format!("{percent:>4}")); - assert_eq!("% ", format!("{percent:<4}")); - assert_eq!(" % ", format!("{percent:^4}")); - - // Item::Numeric, custom non-ASCII padding character - let year = datetime.format("%Y"); - assert_eq!("——2007", format!("{year:—>6}")); - assert_eq!("2007——", format!("{year:—<6}")); - assert_eq!("—2007—", format!("{year:—^6}")); - - // Item::Fixed - let tz = datetime.format("%Z"); - assert_eq!(" UTC", format!("{tz:>5}")); - assert_eq!("UTC ", format!("{tz:<5}")); - assert_eq!(" UTC ", format!("{tz:^5}")); - - // [Item::Numeric, Item::Space, Item::Literal, Item::Space, Item::Numeric] - let ymd = datetime.format("%Y %B %d"); - assert_eq!(" 2007 January 02", format!("{ymd:>17}")); - assert_eq!("2007 January 02 ", format!("{ymd:<17}")); - assert_eq!(" 2007 January 02 ", format!("{ymd:^17}")); - - // Truncated - let time = datetime.format("%T%.6f"); - assert_eq!("12:34:56.1234", format!("{time:.13}")); - } - - #[test] - fn test_offset_formatting() { - fn check_all(precision: OffsetPrecision, expected: [[&str; 7]; 12]) { - fn check( - precision: OffsetPrecision, - colons: Colons, - padding: Pad, - allow_zulu: bool, - offsets: [FixedOffset; 7], - expected: [&str; 7], - ) { - let offset_format = OffsetFormat { precision, colons, allow_zulu, padding }; - for (offset, expected) in offsets.iter().zip(expected.iter()) { - let mut output = String::new(); - offset_format.format(&mut output, *offset).unwrap(); - assert_eq!(&output, expected); - } - } - // +03:45, -03:30, +11:00, -11:00:22, +02:34:26, -12:34:30, +00:00 - let offsets = [ - FixedOffset::east_opt(13_500).unwrap(), - FixedOffset::east_opt(-12_600).unwrap(), - FixedOffset::east_opt(39_600).unwrap(), - FixedOffset::east_opt(-39_622).unwrap(), - FixedOffset::east_opt(9266).unwrap(), - FixedOffset::east_opt(-45270).unwrap(), - FixedOffset::east_opt(0).unwrap(), - ]; - check(precision, Colons::Colon, Pad::Zero, false, offsets, expected[0]); - check(precision, Colons::Colon, Pad::Zero, true, offsets, expected[1]); - check(precision, Colons::Colon, Pad::Space, false, offsets, expected[2]); - check(precision, Colons::Colon, Pad::Space, true, offsets, expected[3]); - check(precision, Colons::Colon, Pad::None, false, offsets, expected[4]); - check(precision, Colons::Colon, Pad::None, true, offsets, expected[5]); - check(precision, Colons::None, Pad::Zero, false, offsets, expected[6]); - check(precision, Colons::None, Pad::Zero, true, offsets, expected[7]); - check(precision, Colons::None, Pad::Space, false, offsets, expected[8]); - check(precision, Colons::None, Pad::Space, true, offsets, expected[9]); - check(precision, Colons::None, Pad::None, false, offsets, expected[10]); - check(precision, Colons::None, Pad::None, true, offsets, expected[11]); - // `Colons::Maybe` should format the same as `Colons::None` - check(precision, Colons::Maybe, Pad::Zero, false, offsets, expected[6]); - check(precision, Colons::Maybe, Pad::Zero, true, offsets, expected[7]); - check(precision, Colons::Maybe, Pad::Space, false, offsets, expected[8]); - check(precision, Colons::Maybe, Pad::Space, true, offsets, expected[9]); - check(precision, Colons::Maybe, Pad::None, false, offsets, expected[10]); - check(precision, Colons::Maybe, Pad::None, true, offsets, expected[11]); - } - check_all( - OffsetPrecision::Hours, - [ - ["+03", "-03", "+11", "-11", "+02", "-12", "+00"], - ["+03", "-03", "+11", "-11", "+02", "-12", "Z"], - [" +3", " -3", "+11", "-11", " +2", "-12", " +0"], - [" +3", " -3", "+11", "-11", " +2", "-12", "Z"], - ["+3", "-3", "+11", "-11", "+2", "-12", "+0"], - ["+3", "-3", "+11", "-11", "+2", "-12", "Z"], - ["+03", "-03", "+11", "-11", "+02", "-12", "+00"], - ["+03", "-03", "+11", "-11", "+02", "-12", "Z"], - [" +3", " -3", "+11", "-11", " +2", "-12", " +0"], - [" +3", " -3", "+11", "-11", " +2", "-12", "Z"], - ["+3", "-3", "+11", "-11", "+2", "-12", "+0"], - ["+3", "-3", "+11", "-11", "+2", "-12", "Z"], - ], - ); - check_all( - OffsetPrecision::Minutes, - [ - ["+03:45", "-03:30", "+11:00", "-11:00", "+02:34", "-12:35", "+00:00"], - ["+03:45", "-03:30", "+11:00", "-11:00", "+02:34", "-12:35", "Z"], - [" +3:45", " -3:30", "+11:00", "-11:00", " +2:34", "-12:35", " +0:00"], - [" +3:45", " -3:30", "+11:00", "-11:00", " +2:34", "-12:35", "Z"], - ["+3:45", "-3:30", "+11:00", "-11:00", "+2:34", "-12:35", "+0:00"], - ["+3:45", "-3:30", "+11:00", "-11:00", "+2:34", "-12:35", "Z"], - ["+0345", "-0330", "+1100", "-1100", "+0234", "-1235", "+0000"], - ["+0345", "-0330", "+1100", "-1100", "+0234", "-1235", "Z"], - [" +345", " -330", "+1100", "-1100", " +234", "-1235", " +000"], - [" +345", " -330", "+1100", "-1100", " +234", "-1235", "Z"], - ["+345", "-330", "+1100", "-1100", "+234", "-1235", "+000"], - ["+345", "-330", "+1100", "-1100", "+234", "-1235", "Z"], - ], - ); - #[rustfmt::skip] - check_all( - OffsetPrecision::Seconds, - [ - ["+03:45:00", "-03:30:00", "+11:00:00", "-11:00:22", "+02:34:26", "-12:34:30", "+00:00:00"], - ["+03:45:00", "-03:30:00", "+11:00:00", "-11:00:22", "+02:34:26", "-12:34:30", "Z"], - [" +3:45:00", " -3:30:00", "+11:00:00", "-11:00:22", " +2:34:26", "-12:34:30", " +0:00:00"], - [" +3:45:00", " -3:30:00", "+11:00:00", "-11:00:22", " +2:34:26", "-12:34:30", "Z"], - ["+3:45:00", "-3:30:00", "+11:00:00", "-11:00:22", "+2:34:26", "-12:34:30", "+0:00:00"], - ["+3:45:00", "-3:30:00", "+11:00:00", "-11:00:22", "+2:34:26", "-12:34:30", "Z"], - ["+034500", "-033000", "+110000", "-110022", "+023426", "-123430", "+000000"], - ["+034500", "-033000", "+110000", "-110022", "+023426", "-123430", "Z"], - [" +34500", " -33000", "+110000", "-110022", " +23426", "-123430", " +00000"], - [" +34500", " -33000", "+110000", "-110022", " +23426", "-123430", "Z"], - ["+34500", "-33000", "+110000", "-110022", "+23426", "-123430", "+00000"], - ["+34500", "-33000", "+110000", "-110022", "+23426", "-123430", "Z"], - ], - ); - check_all( - OffsetPrecision::OptionalMinutes, - [ - ["+03:45", "-03:30", "+11", "-11", "+02:34", "-12:35", "+00"], - ["+03:45", "-03:30", "+11", "-11", "+02:34", "-12:35", "Z"], - [" +3:45", " -3:30", "+11", "-11", " +2:34", "-12:35", " +0"], - [" +3:45", " -3:30", "+11", "-11", " +2:34", "-12:35", "Z"], - ["+3:45", "-3:30", "+11", "-11", "+2:34", "-12:35", "+0"], - ["+3:45", "-3:30", "+11", "-11", "+2:34", "-12:35", "Z"], - ["+0345", "-0330", "+11", "-11", "+0234", "-1235", "+00"], - ["+0345", "-0330", "+11", "-11", "+0234", "-1235", "Z"], - [" +345", " -330", "+11", "-11", " +234", "-1235", " +0"], - [" +345", " -330", "+11", "-11", " +234", "-1235", "Z"], - ["+345", "-330", "+11", "-11", "+234", "-1235", "+0"], - ["+345", "-330", "+11", "-11", "+234", "-1235", "Z"], - ], - ); - check_all( - OffsetPrecision::OptionalSeconds, - [ - ["+03:45", "-03:30", "+11:00", "-11:00:22", "+02:34:26", "-12:34:30", "+00:00"], - ["+03:45", "-03:30", "+11:00", "-11:00:22", "+02:34:26", "-12:34:30", "Z"], - [" +3:45", " -3:30", "+11:00", "-11:00:22", " +2:34:26", "-12:34:30", " +0:00"], - [" +3:45", " -3:30", "+11:00", "-11:00:22", " +2:34:26", "-12:34:30", "Z"], - ["+3:45", "-3:30", "+11:00", "-11:00:22", "+2:34:26", "-12:34:30", "+0:00"], - ["+3:45", "-3:30", "+11:00", "-11:00:22", "+2:34:26", "-12:34:30", "Z"], - ["+0345", "-0330", "+1100", "-110022", "+023426", "-123430", "+0000"], - ["+0345", "-0330", "+1100", "-110022", "+023426", "-123430", "Z"], - [" +345", " -330", "+1100", "-110022", " +23426", "-123430", " +000"], - [" +345", " -330", "+1100", "-110022", " +23426", "-123430", "Z"], - ["+345", "-330", "+1100", "-110022", "+23426", "-123430", "+000"], - ["+345", "-330", "+1100", "-110022", "+23426", "-123430", "Z"], - ], - ); - check_all( - OffsetPrecision::OptionalMinutesAndSeconds, - [ - ["+03:45", "-03:30", "+11", "-11:00:22", "+02:34:26", "-12:34:30", "+00"], - ["+03:45", "-03:30", "+11", "-11:00:22", "+02:34:26", "-12:34:30", "Z"], - [" +3:45", " -3:30", "+11", "-11:00:22", " +2:34:26", "-12:34:30", " +0"], - [" +3:45", " -3:30", "+11", "-11:00:22", " +2:34:26", "-12:34:30", "Z"], - ["+3:45", "-3:30", "+11", "-11:00:22", "+2:34:26", "-12:34:30", "+0"], - ["+3:45", "-3:30", "+11", "-11:00:22", "+2:34:26", "-12:34:30", "Z"], - ["+0345", "-0330", "+11", "-110022", "+023426", "-123430", "+00"], - ["+0345", "-0330", "+11", "-110022", "+023426", "-123430", "Z"], - [" +345", " -330", "+11", "-110022", " +23426", "-123430", " +0"], - [" +345", " -330", "+11", "-110022", " +23426", "-123430", "Z"], - ["+345", "-330", "+11", "-110022", "+23426", "-123430", "+0"], - ["+345", "-330", "+11", "-110022", "+23426", "-123430", "Z"], - ], - ); - } -} diff --git a/chrono-0.4.44/src/format/locales.rs b/chrono-0.4.44/src/format/locales.rs deleted file mode 100644 index c615476c0c..0000000000 --- a/chrono-0.4.44/src/format/locales.rs +++ /dev/null @@ -1,103 +0,0 @@ -#[cfg(feature = "unstable-locales")] -mod localized { - use pure_rust_locales::{Locale, locale_match}; - - pub(crate) const fn default_locale() -> Locale { - Locale::POSIX - } - - pub(crate) const fn short_months(locale: Locale) -> &'static [&'static str] { - locale_match!(locale => LC_TIME::ABMON) - } - - pub(crate) const fn long_months(locale: Locale) -> &'static [&'static str] { - locale_match!(locale => LC_TIME::MON) - } - - pub(crate) const fn short_weekdays(locale: Locale) -> &'static [&'static str] { - locale_match!(locale => LC_TIME::ABDAY) - } - - pub(crate) const fn long_weekdays(locale: Locale) -> &'static [&'static str] { - locale_match!(locale => LC_TIME::DAY) - } - - pub(crate) const fn am_pm(locale: Locale) -> &'static [&'static str] { - locale_match!(locale => LC_TIME::AM_PM) - } - - pub(crate) const fn decimal_point(locale: Locale) -> &'static str { - locale_match!(locale => LC_NUMERIC::DECIMAL_POINT) - } - - pub(crate) const fn d_fmt(locale: Locale) -> &'static str { - locale_match!(locale => LC_TIME::D_FMT) - } - - pub(crate) const fn d_t_fmt(locale: Locale) -> &'static str { - locale_match!(locale => LC_TIME::D_T_FMT) - } - - pub(crate) const fn t_fmt(locale: Locale) -> &'static str { - locale_match!(locale => LC_TIME::T_FMT) - } - - pub(crate) const fn t_fmt_ampm(locale: Locale) -> &'static str { - locale_match!(locale => LC_TIME::T_FMT_AMPM) - } -} - -#[cfg(feature = "unstable-locales")] -pub(crate) use localized::*; -#[cfg(feature = "unstable-locales")] -pub use pure_rust_locales::Locale; - -#[cfg(not(feature = "unstable-locales"))] -mod unlocalized { - #[derive(Copy, Clone, Debug)] - pub(crate) struct Locale; - - pub(crate) const fn default_locale() -> Locale { - Locale - } - - pub(crate) const fn short_months(_locale: Locale) -> &'static [&'static str] { - &["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] - } - - pub(crate) const fn long_months(_locale: Locale) -> &'static [&'static str] { - &[ - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December", - ] - } - - pub(crate) const fn short_weekdays(_locale: Locale) -> &'static [&'static str] { - &["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] - } - - pub(crate) const fn long_weekdays(_locale: Locale) -> &'static [&'static str] { - &["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] - } - - pub(crate) const fn am_pm(_locale: Locale) -> &'static [&'static str] { - &["AM", "PM"] - } - - pub(crate) const fn decimal_point(_locale: Locale) -> &'static str { - "." - } -} - -#[cfg(not(feature = "unstable-locales"))] -pub(crate) use unlocalized::*; diff --git a/chrono-0.4.44/src/format/mod.rs b/chrono-0.4.44/src/format/mod.rs deleted file mode 100644 index ec4beae453..0000000000 --- a/chrono-0.4.44/src/format/mod.rs +++ /dev/null @@ -1,593 +0,0 @@ -// This is a part of Chrono. -// See README.md and LICENSE.txt for details. - -//! Formatting (and parsing) utilities for date and time. -//! -//! This module provides the common types and routines to implement, -//! for example, [`DateTime::format`](../struct.DateTime.html#method.format) or -//! [`DateTime::parse_from_str`](../struct.DateTime.html#method.parse_from_str) methods. -//! For most cases you should use these high-level interfaces. -//! -//! Internally the formatting and parsing shares the same abstract **formatting items**, -//! which are just an [`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) of -//! the [`Item`](./enum.Item.html) type. -//! They are generated from more readable **format strings**; -//! currently Chrono supports a built-in syntax closely resembling -//! C's `strftime` format. The available options can be found [here](./strftime/index.html). -//! -//! # Example -//! ``` -//! # #[cfg(feature = "alloc")] { -//! use chrono::{NaiveDateTime, TimeZone, Utc}; -//! -//! let date_time = Utc.with_ymd_and_hms(2020, 11, 10, 0, 1, 32).unwrap(); -//! -//! let formatted = format!("{}", date_time.format("%Y-%m-%d %H:%M:%S")); -//! assert_eq!(formatted, "2020-11-10 00:01:32"); -//! -//! let parsed = NaiveDateTime::parse_from_str(&formatted, "%Y-%m-%d %H:%M:%S")?.and_utc(); -//! assert_eq!(parsed, date_time); -//! # } -//! # Ok::<(), chrono::ParseError>(()) -//! ``` - -#[cfg(all(feature = "alloc", not(feature = "std"), not(test)))] -use alloc::boxed::Box; -#[cfg(all(feature = "core-error", not(feature = "std")))] -use core::error::Error; -use core::fmt; -use core::str::FromStr; -#[cfg(feature = "std")] -use std::error::Error; - -use crate::{Month, ParseMonthError, ParseWeekdayError, Weekday}; - -mod formatting; -mod parsed; - -// due to the size of parsing routines, they are in separate modules. -mod parse; -pub(crate) mod scan; - -pub mod strftime; - -#[allow(unused)] -// TODO: remove '#[allow(unused)]' once we use this module for parsing or something else that does -// not require `alloc`. -pub(crate) mod locales; - -pub use formatting::SecondsFormat; -pub(crate) use formatting::write_hundreds; -#[cfg(feature = "alloc")] -pub(crate) use formatting::write_rfc2822; -#[cfg(any(feature = "alloc", feature = "serde"))] -pub(crate) use formatting::write_rfc3339; -#[cfg(feature = "alloc")] -#[allow(deprecated)] -pub use formatting::{DelayedFormat, format, format_item}; -#[cfg(feature = "unstable-locales")] -pub use locales::Locale; -pub(crate) use parse::parse_rfc3339; -pub use parse::{parse, parse_and_remainder}; -pub use parsed::Parsed; -pub use strftime::StrftimeItems; - -/// An uninhabited type used for `InternalNumeric` and `InternalFixed` below. -#[derive(Clone, PartialEq, Eq, Hash)] -enum Void {} - -/// Padding characters for numeric items. -#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Pad { - /// No padding. - None, - /// Zero (`0`) padding. - Zero, - /// Space padding. - Space, -} - -/// Numeric item types. -/// They have associated formatting width (FW) and parsing width (PW). -/// -/// The **formatting width** is the minimal width to be formatted. -/// If the number is too short, and the padding is not [`Pad::None`](./enum.Pad.html#variant.None), -/// then it is left-padded. -/// If the number is too long or (in some cases) negative, it is printed as is. -/// -/// The **parsing width** is the maximal width to be scanned. -/// The parser only tries to consume from one to given number of digits (greedily). -/// It also trims the preceding whitespace if any. -/// It cannot parse the negative number, so some date and time cannot be formatted then -/// parsed with the same formatting items. -#[non_exhaustive] -#[derive(Clone, PartialEq, Eq, Debug, Hash)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Numeric { - /// Full Gregorian year (FW=4, PW=∞). - /// May accept years before 1 BCE or after 9999 CE, given an initial sign (+/-). - Year, - /// Gregorian year divided by 100 (century number; FW=PW=2). Implies the non-negative year. - YearDiv100, - /// Gregorian year modulo 100 (FW=PW=2). Cannot be negative. - YearMod100, - /// Year in the ISO week date (FW=4, PW=∞). - /// May accept years before 1 BCE or after 9999 CE, given an initial sign. - IsoYear, - /// Year in the ISO week date, divided by 100 (FW=PW=2). Implies the non-negative year. - IsoYearDiv100, - /// Year in the ISO week date, modulo 100 (FW=PW=2). Cannot be negative. - IsoYearMod100, - /// Quarter (FW=PW=1). - Quarter, - /// Month (FW=PW=2). - Month, - /// Day of the month (FW=PW=2). - Day, - /// Week number, where the week 1 starts at the first Sunday of January (FW=PW=2). - WeekFromSun, - /// Week number, where the week 1 starts at the first Monday of January (FW=PW=2). - WeekFromMon, - /// Week number in the ISO week date (FW=PW=2). - IsoWeek, - /// Day of the week, where Sunday = 0 and Saturday = 6 (FW=PW=1). - NumDaysFromSun, - /// Day of the week, where Monday = 1 and Sunday = 7 (FW=PW=1). - WeekdayFromMon, - /// Day of the year (FW=PW=3). - Ordinal, - /// Hour number in the 24-hour clocks (FW=PW=2). - Hour, - /// Hour number in the 12-hour clocks (FW=PW=2). - Hour12, - /// The number of minutes since the last whole hour (FW=PW=2). - Minute, - /// The number of seconds since the last whole minute (FW=PW=2). - Second, - /// The number of nanoseconds since the last whole second (FW=PW=9). - /// Note that this is *not* left-aligned; - /// see also [`Fixed::Nanosecond`](./enum.Fixed.html#variant.Nanosecond). - Nanosecond, - /// The number of non-leap seconds since the midnight UTC on January 1, 1970 (FW=1, PW=∞). - /// For formatting, it assumes UTC upon the absence of time zone offset. - Timestamp, - - /// Internal uses only. - /// - /// This item exists so that one can add additional internal-only formatting - /// without breaking major compatibility (as enum variants cannot be selectively private). - Internal(InternalNumeric), -} - -/// An opaque type representing numeric item types for internal uses only. -#[derive(Clone, Eq, Hash, PartialEq)] -pub struct InternalNumeric { - _dummy: Void, -} - -impl fmt::Debug for InternalNumeric { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "") - } -} - -#[cfg(feature = "defmt")] -impl defmt::Format for InternalNumeric { - fn format(&self, f: defmt::Formatter) { - defmt::write!(f, "") - } -} - -/// Fixed-format item types. -/// -/// They have their own rules of formatting and parsing. -/// Otherwise noted, they print in the specified cases but parse case-insensitively. -#[non_exhaustive] -#[derive(Clone, PartialEq, Eq, Debug, Hash)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Fixed { - /// Abbreviated month names. - /// - /// Prints a three-letter-long name in the title case, reads the same name in any case. - ShortMonthName, - /// Full month names. - /// - /// Prints a full name in the title case, reads either a short or full name in any case. - LongMonthName, - /// Abbreviated day of the week names. - /// - /// Prints a three-letter-long name in the title case, reads the same name in any case. - ShortWeekdayName, - /// Full day of the week names. - /// - /// Prints a full name in the title case, reads either a short or full name in any case. - LongWeekdayName, - /// AM/PM. - /// - /// Prints in lower case, reads in any case. - LowerAmPm, - /// AM/PM. - /// - /// Prints in upper case, reads in any case. - UpperAmPm, - /// An optional dot plus one or more digits for left-aligned nanoseconds. - /// May print nothing, 3, 6 or 9 digits according to the available accuracy. - /// See also [`Numeric::Nanosecond`](./enum.Numeric.html#variant.Nanosecond). - Nanosecond, - /// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 3. - Nanosecond3, - /// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 6. - Nanosecond6, - /// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 9. - Nanosecond9, - /// Timezone name. - /// - /// It does not support parsing, its use in the parser is an immediate failure. - TimezoneName, - /// Offset from the local time to UTC (`+09:00` or `-04:00` or `+00:00`). - /// - /// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace. - /// The offset is limited from `-24:00` to `+24:00`, - /// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range. - TimezoneOffsetColon, - /// Offset from the local time to UTC with seconds (`+09:00:00` or `-04:00:00` or `+00:00:00`). - /// - /// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace. - /// The offset is limited from `-24:00:00` to `+24:00:00`, - /// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range. - TimezoneOffsetDoubleColon, - /// Offset from the local time to UTC without minutes (`+09` or `-04` or `+00`). - /// - /// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace. - /// The offset is limited from `-24` to `+24`, - /// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range. - TimezoneOffsetTripleColon, - /// Offset from the local time to UTC (`+09:00` or `-04:00` or `Z`). - /// - /// In the parser, the colon can be omitted and/or surrounded with any amount of whitespace, - /// and `Z` can be either in upper case or in lower case. - /// The offset is limited from `-24:00` to `+24:00`, - /// which is the same as [`FixedOffset`](../offset/struct.FixedOffset.html)'s range. - TimezoneOffsetColonZ, - /// Same as [`TimezoneOffsetColon`](#variant.TimezoneOffsetColon) but prints no colon. - /// Parsing allows an optional colon. - TimezoneOffset, - /// Same as [`TimezoneOffsetColonZ`](#variant.TimezoneOffsetColonZ) but prints no colon. - /// Parsing allows an optional colon. - TimezoneOffsetZ, - /// RFC 2822 date and time syntax. Commonly used for email and MIME date and time. - RFC2822, - /// RFC 3339 & ISO 8601 date and time syntax. - RFC3339, - - /// Internal uses only. - /// - /// This item exists so that one can add additional internal-only formatting - /// without breaking major compatibility (as enum variants cannot be selectively private). - Internal(InternalFixed), -} - -/// An opaque type representing fixed-format item types for internal uses only. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct InternalFixed { - val: InternalInternal, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -enum InternalInternal { - /// Same as [`TimezoneOffsetColonZ`](#variant.TimezoneOffsetColonZ), but - /// allows missing minutes (per [ISO 8601][iso8601]). - /// - /// # Panics - /// - /// If you try to use this for printing. - /// - /// [iso8601]: https://en.wikipedia.org/wiki/ISO_8601#Time_offsets_from_UTC - TimezoneOffsetPermissive, - /// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 3 and there is no leading dot. - Nanosecond3NoDot, - /// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 6 and there is no leading dot. - Nanosecond6NoDot, - /// Same as [`Nanosecond`](#variant.Nanosecond) but the accuracy is fixed to 9 and there is no leading dot. - Nanosecond9NoDot, -} - -/// Type for specifying the format of UTC offsets. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct OffsetFormat { - /// See `OffsetPrecision`. - pub precision: OffsetPrecision, - /// Separator between hours, minutes and seconds. - pub colons: Colons, - /// Represent `+00:00` as `Z`. - pub allow_zulu: bool, - /// Pad the hour value to two digits. - pub padding: Pad, -} - -/// The precision of an offset from UTC formatting item. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum OffsetPrecision { - /// Format offset from UTC as only hours. Not recommended, it is not uncommon for timezones to - /// have an offset of 30 minutes, 15 minutes, etc. - /// Any minutes and seconds get truncated. - Hours, - /// Format offset from UTC as hours and minutes. - /// Any seconds will be rounded to the nearest minute. - Minutes, - /// Format offset from UTC as hours, minutes and seconds. - Seconds, - /// Format offset from UTC as hours, and optionally with minutes. - /// Any seconds will be rounded to the nearest minute. - OptionalMinutes, - /// Format offset from UTC as hours and minutes, and optionally seconds. - OptionalSeconds, - /// Format offset from UTC as hours and optionally minutes and seconds. - OptionalMinutesAndSeconds, -} - -/// The separator between hours and minutes in an offset. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Colons { - /// No separator - None, - /// Colon (`:`) as separator - Colon, - /// No separator when formatting, colon allowed when parsing. - Maybe, -} - -/// A single formatting item. This is used for both formatting and parsing. -#[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub enum Item<'a> { - /// A literally printed and parsed text. - Literal(&'a str), - /// Same as `Literal` but with the string owned by the item. - #[cfg(feature = "alloc")] - OwnedLiteral(Box), - /// Whitespace. Prints literally but reads zero or more whitespace. - Space(&'a str), - /// Same as `Space` but with the string owned by the item. - #[cfg(feature = "alloc")] - OwnedSpace(Box), - /// Numeric item. Can be optionally padded to the maximal length (if any) when formatting; - /// the parser simply ignores any padded whitespace and zeroes. - Numeric(Numeric, Pad), - /// Fixed-format item. - Fixed(Fixed), - /// Issues a formatting error. Used to signal an invalid format string. - Error, -} - -#[cfg(feature = "defmt")] -impl<'a> defmt::Format for Item<'a> { - fn format(&self, f: defmt::Formatter) { - match self { - Item::Literal(v) => defmt::write!(f, "Literal {{ {} }}", v), - #[cfg(feature = "alloc")] - Item::OwnedLiteral(_) => {} - Item::Space(v) => defmt::write!(f, "Space {{ {} }}", v), - #[cfg(feature = "alloc")] - Item::OwnedSpace(_) => {} - Item::Numeric(u, v) => defmt::write!(f, "Numeric {{ {}, {} }}", u, v), - Item::Fixed(v) => defmt::write!(f, "Fixed {{ {} }}", v), - Item::Error => defmt::write!(f, "Error"), - } - } -} - -const fn num(numeric: Numeric) -> Item<'static> { - Item::Numeric(numeric, Pad::None) -} - -const fn num0(numeric: Numeric) -> Item<'static> { - Item::Numeric(numeric, Pad::Zero) -} - -const fn nums(numeric: Numeric) -> Item<'static> { - Item::Numeric(numeric, Pad::Space) -} - -const fn fixed(fixed: Fixed) -> Item<'static> { - Item::Fixed(fixed) -} - -const fn internal_fixed(val: InternalInternal) -> Item<'static> { - Item::Fixed(Fixed::Internal(InternalFixed { val })) -} - -impl Item<'_> { - /// Convert items that contain a reference to the format string into an owned variant. - #[cfg(any(feature = "alloc", feature = "std"))] - pub fn to_owned(self) -> Item<'static> { - match self { - Item::Literal(s) => Item::OwnedLiteral(Box::from(s)), - Item::Space(s) => Item::OwnedSpace(Box::from(s)), - Item::Numeric(n, p) => Item::Numeric(n, p), - Item::Fixed(f) => Item::Fixed(f), - Item::OwnedLiteral(l) => Item::OwnedLiteral(l), - Item::OwnedSpace(s) => Item::OwnedSpace(s), - Item::Error => Item::Error, - } - } -} - -/// An error from the `parse` function. -#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct ParseError(ParseErrorKind); - -impl ParseError { - /// The category of parse error - pub const fn kind(&self) -> ParseErrorKind { - self.0 - } -} - -/// The category of parse error -#[allow(clippy::manual_non_exhaustive)] -#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum ParseErrorKind { - /// Given field is out of permitted range. - OutOfRange, - - /// There is no possible date and time value with given set of fields. - /// - /// This does not include the out-of-range conditions, which are trivially invalid. - /// It includes the case that there are one or more fields that are inconsistent to each other. - Impossible, - - /// Given set of fields is not enough to make a requested date and time value. - /// - /// Note that there *may* be a case that given fields constrain the possible values so much - /// that there is a unique possible value. Chrono only tries to be correct for - /// most useful sets of fields however, as such constraint solving can be expensive. - NotEnough, - - /// The input string has some invalid character sequence for given formatting items. - Invalid, - - /// The input string has been prematurely ended. - TooShort, - - /// All formatting items have been read but there is a remaining input. - TooLong, - - /// There was an error on the formatting string, or there were non-supported formatting items. - BadFormat, - - // TODO: Change this to `#[non_exhaustive]` (on the enum) with the next breaking release. - #[doc(hidden)] - __Nonexhaustive, -} - -/// Same as `Result`. -pub type ParseResult = Result; - -impl fmt::Display for ParseError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.0 { - ParseErrorKind::OutOfRange => write!(f, "input is out of range"), - ParseErrorKind::Impossible => write!(f, "no possible date and time matching input"), - ParseErrorKind::NotEnough => write!(f, "input is not enough for unique date and time"), - ParseErrorKind::Invalid => write!(f, "input contains invalid characters"), - ParseErrorKind::TooShort => write!(f, "premature end of input"), - ParseErrorKind::TooLong => write!(f, "trailing input"), - ParseErrorKind::BadFormat => write!(f, "bad or unsupported format string"), - _ => unreachable!(), - } - } -} - -#[cfg(any(feature = "core-error", feature = "std"))] -impl Error for ParseError { - #[allow(deprecated)] - fn description(&self) -> &str { - "parser error, see to_string() for details" - } -} - -// to be used in this module and submodules -pub(crate) const OUT_OF_RANGE: ParseError = ParseError(ParseErrorKind::OutOfRange); -const IMPOSSIBLE: ParseError = ParseError(ParseErrorKind::Impossible); -const NOT_ENOUGH: ParseError = ParseError(ParseErrorKind::NotEnough); -const INVALID: ParseError = ParseError(ParseErrorKind::Invalid); -const TOO_SHORT: ParseError = ParseError(ParseErrorKind::TooShort); -pub(crate) const TOO_LONG: ParseError = ParseError(ParseErrorKind::TooLong); -const BAD_FORMAT: ParseError = ParseError(ParseErrorKind::BadFormat); - -// this implementation is here only because we need some private code from `scan` - -/// Parsing a `str` into a `Weekday` uses the format [`%A`](./format/strftime/index.html). -/// -/// # Example -/// -/// ``` -/// use chrono::Weekday; -/// -/// assert_eq!("Sunday".parse::(), Ok(Weekday::Sun)); -/// assert!("any day".parse::().is_err()); -/// ``` -/// -/// The parsing is case-insensitive. -/// -/// ``` -/// # use chrono::Weekday; -/// assert_eq!("mON".parse::(), Ok(Weekday::Mon)); -/// ``` -/// -/// Only the shortest form (e.g. `sun`) and the longest form (e.g. `sunday`) is accepted. -/// -/// ``` -/// # use chrono::Weekday; -/// assert!("thurs".parse::().is_err()); -/// ``` -impl FromStr for Weekday { - type Err = ParseWeekdayError; - - fn from_str(s: &str) -> Result { - if let Ok(("", w)) = scan::short_or_long_weekday(s) { - Ok(w) - } else { - Err(ParseWeekdayError { _dummy: () }) - } - } -} - -/// Parsing a `str` into a `Month` uses the format [`%B`](./format/strftime/index.html). -/// -/// # Example -/// -/// ``` -/// use chrono::Month; -/// -/// assert_eq!("January".parse::(), Ok(Month::January)); -/// assert!("any day".parse::().is_err()); -/// ``` -/// -/// The parsing is case-insensitive. -/// -/// ``` -/// # use chrono::Month; -/// assert_eq!("fEbruARy".parse::(), Ok(Month::February)); -/// ``` -/// -/// Only the shortest form (e.g. `jan`) and the longest form (e.g. `january`) is accepted. -/// -/// ``` -/// # use chrono::Month; -/// assert!("septem".parse::().is_err()); -/// assert!("Augustin".parse::().is_err()); -/// ``` -impl FromStr for Month { - type Err = ParseMonthError; - - fn from_str(s: &str) -> Result { - if let Ok(("", w)) = scan::short_or_long_month0(s) { - match w { - 0 => Ok(Month::January), - 1 => Ok(Month::February), - 2 => Ok(Month::March), - 3 => Ok(Month::April), - 4 => Ok(Month::May), - 5 => Ok(Month::June), - 6 => Ok(Month::July), - 7 => Ok(Month::August), - 8 => Ok(Month::September), - 9 => Ok(Month::October), - 10 => Ok(Month::November), - 11 => Ok(Month::December), - _ => Err(ParseMonthError { _dummy: () }), - } - } else { - Err(ParseMonthError { _dummy: () }) - } - } -} diff --git a/chrono-0.4.44/src/format/parse.rs b/chrono-0.4.44/src/format/parse.rs deleted file mode 100644 index 18857a286a..0000000000 --- a/chrono-0.4.44/src/format/parse.rs +++ /dev/null @@ -1,1941 +0,0 @@ -// This is a part of Chrono. -// Portions copyright (c) 2015, John Nagle. -// See README.md and LICENSE.txt for details. - -//! Date and time parsing routines. - -use core::borrow::Borrow; -use core::str; - -use super::scan; -use super::{BAD_FORMAT, INVALID, OUT_OF_RANGE, TOO_LONG, TOO_SHORT}; -use super::{Fixed, InternalFixed, InternalInternal, Item, Numeric, Pad, Parsed}; -use super::{ParseError, ParseResult}; -use crate::{DateTime, FixedOffset, MappedLocalTime, NaiveDate, NaiveTime, Weekday}; - -fn set_weekday_with_num_days_from_sunday(p: &mut Parsed, v: i64) -> ParseResult<()> { - p.set_weekday(match v { - 0 => Weekday::Sun, - 1 => Weekday::Mon, - 2 => Weekday::Tue, - 3 => Weekday::Wed, - 4 => Weekday::Thu, - 5 => Weekday::Fri, - 6 => Weekday::Sat, - _ => return Err(OUT_OF_RANGE), - }) -} - -fn set_weekday_with_number_from_monday(p: &mut Parsed, v: i64) -> ParseResult<()> { - p.set_weekday(match v { - 1 => Weekday::Mon, - 2 => Weekday::Tue, - 3 => Weekday::Wed, - 4 => Weekday::Thu, - 5 => Weekday::Fri, - 6 => Weekday::Sat, - 7 => Weekday::Sun, - _ => return Err(OUT_OF_RANGE), - }) -} - -fn parse_rfc2822<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a str, ())> { - macro_rules! try_consume { - ($e:expr) => {{ - let (s_, v) = $e?; - s = s_; - v - }}; - } - - // an adapted RFC 2822 syntax from Section 3.3 and 4.3: - // - // c-char = - // c-escape = "\" - // comment = "(" *(comment / c-char / c-escape) ")" *S - // date-time = [ day-of-week "," ] date 1*S time *S *comment - // day-of-week = *S day-name *S - // day-name = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun" - // date = day month year - // day = *S 1*2DIGIT *S - // month = 1*S month-name 1*S - // month-name = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / - // "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec" - // year = *S 2*DIGIT *S - // time = time-of-day 1*S zone - // time-of-day = hour ":" minute [ ":" second ] - // hour = *S 2DIGIT *S - // minute = *S 2DIGIT *S - // second = *S 2DIGIT *S - // zone = ( "+" / "-" ) 4DIGIT / - // "UT" / "GMT" / ; same as +0000 - // "EST" / "CST" / "MST" / "PST" / ; same as -0500 to -0800 - // "EDT" / "CDT" / "MDT" / "PDT" / ; same as -0400 to -0700 - // 1*(%d65-90 / %d97-122) ; same as -0000 - // - // some notes: - // - // - quoted characters can be in any mixture of lower and upper cases. - // - // - we do not recognize a folding white space (FWS) or comment (CFWS). - // for our purposes, instead, we accept any sequence of Unicode - // white space characters (denoted here to `S`). For comments, we accept - // any text within parentheses while respecting escaped parentheses. - // Any actual RFC 2822 parser is expected to parse FWS and/or CFWS themselves - // and replace it with a single SP (`%x20`); this is legitimate. - // - // - two-digit year < 50 should be interpreted by adding 2000. - // two-digit year >= 50 or three-digit year should be interpreted - // by adding 1900. note that four-or-more-digit years less than 1000 - // are *never* affected by this rule. - // - // - mismatching day-of-week is always an error, which is consistent to - // Chrono's own rules. - // - // - zones can range from `-9959` to `+9959`, but `FixedOffset` does not - // support offsets larger than 24 hours. this is not *that* problematic - // since we do not directly go to a `DateTime` so one can recover - // the offset information from `Parsed` anyway. - - s = s.trim_start(); - - if let Ok((s_, weekday)) = scan::short_weekday(s) { - if !s_.starts_with(',') { - return Err(INVALID); - } - s = &s_[1..]; - parsed.set_weekday(weekday)?; - } - - s = s.trim_start(); - parsed.set_day(try_consume!(scan::number(s, 1, 2)))?; - s = scan::space(s)?; // mandatory - parsed.set_month(1 + i64::from(try_consume!(scan::short_month0(s))))?; - s = scan::space(s)?; // mandatory - - // distinguish two- and three-digit years from four-digit years - let prevlen = s.len(); - let mut year = try_consume!(scan::number(s, 2, usize::MAX)); - let yearlen = prevlen - s.len(); - match (yearlen, year) { - (2, 0..=49) => { - year += 2000; - } // 47 -> 2047, 05 -> 2005 - (2, 50..=99) => { - year += 1900; - } // 79 -> 1979 - (3, _) => { - year += 1900; - } // 112 -> 2012, 009 -> 1909 - (_, _) => {} // 1987 -> 1987, 0654 -> 0654 - } - parsed.set_year(year)?; - - s = scan::space(s)?; // mandatory - parsed.set_hour(try_consume!(scan::number(s, 2, 2)))?; - s = scan::char(s.trim_start(), b':')?.trim_start(); // *S ":" *S - parsed.set_minute(try_consume!(scan::number(s, 2, 2)))?; - if let Ok(s_) = scan::char(s.trim_start(), b':') { - // [ ":" *S 2DIGIT ] - parsed.set_second(try_consume!(scan::number(s_, 2, 2)))?; - } - - s = scan::space(s)?; // mandatory - parsed.set_offset(i64::from(try_consume!(scan::timezone_offset_2822(s))))?; - - // optional comments - while let Ok((s_out, ())) = scan::comment_2822(s) { - s = s_out; - } - - Ok((s, ())) -} - -pub(crate) fn parse_rfc3339(mut s: &str) -> ParseResult> { - macro_rules! try_consume { - ($e:expr) => {{ - let (s_, v) = $e?; - s = s_; - v - }}; - } - - // an adapted RFC 3339 syntax from Section 5.6: - // - // date-fullyear = 4DIGIT - // date-month = 2DIGIT ; 01-12 - // date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year - // time-hour = 2DIGIT ; 00-23 - // time-minute = 2DIGIT ; 00-59 - // time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second rules - // time-secfrac = "." 1*DIGIT - // time-numoffset = ("+" / "-") time-hour ":" time-minute - // time-offset = "Z" / time-numoffset - // partial-time = time-hour ":" time-minute ":" time-second [time-secfrac] - // full-date = date-fullyear "-" date-month "-" date-mday - // full-time = partial-time time-offset - // date-time = full-date "T" full-time - // - // some notes: - // - // - quoted characters can be in any mixture of lower and upper cases. - // - // - it may accept any number of fractional digits for seconds. - // for Chrono, this means that we should skip digits past first 9 digits. - // - // - unlike RFC 2822, the valid offset ranges from -23:59 to +23:59. - // note that this restriction is unique to RFC 3339 and not ISO 8601. - // since this is not a typical Chrono behavior, we check it earlier. - // - // - For readability a full-date and a full-time may be separated by a space character. - - let bytes = s.as_bytes(); - if bytes.len() < 19 { - return Err(TOO_SHORT); - } - - let fixed = <&[u8; 19]>::try_from(&bytes[..19]).unwrap(); // we just checked the length - let year = digit(fixed, 0)? as u16 * 1000 - + digit(fixed, 1)? as u16 * 100 - + digit(fixed, 2)? as u16 * 10 - + digit(fixed, 3)? as u16; - if bytes.get(4) != Some(&b'-') { - return Err(INVALID); - } - - let month = digit(fixed, 5)? * 10 + digit(fixed, 6)?; - if bytes.get(7) != Some(&b'-') { - return Err(INVALID); - } - - let day = digit(fixed, 8)? * 10 + digit(fixed, 9)?; - let date = - NaiveDate::from_ymd_opt(year as i32, month as u32, day as u32).ok_or(OUT_OF_RANGE)?; - - if !matches!(bytes.get(10), Some(&b't' | &b'T' | &b' ')) { - return Err(INVALID); - } - - let hour = digit(fixed, 11)? * 10 + digit(fixed, 12)?; - if bytes.get(13) != Some(&b':') { - return Err(INVALID); - } - - let min = digit(fixed, 14)? * 10 + digit(fixed, 15)?; - if bytes.get(16) != Some(&b':') { - return Err(INVALID); - } - - let sec = digit(fixed, 17)? * 10 + digit(fixed, 18)?; - let (sec, extra_nanos) = match sec { - 60 => (59, 1_000_000_000), // rfc3339 allows leap seconds - _ => (sec, 0), - }; - - let nano = if bytes.get(19) == Some(&b'.') { - let nanosecond = try_consume!(scan::nanosecond(&s[20..])); - extra_nanos + nanosecond - } else { - s = &s[19..]; - extra_nanos - }; - - let time = NaiveTime::from_hms_nano_opt(hour as u32, min as u32, sec as u32, nano) - .ok_or(OUT_OF_RANGE)?; - - // Max for the hours field is `23`, and for the minutes field `59`. - let offset = try_consume!(scan::timezone_offset(s, |s| scan::char(s, b':'), true, false, true)); - if !s.is_empty() { - return Err(TOO_LONG); - } - - let tz = FixedOffset::east_opt(offset).ok_or(OUT_OF_RANGE)?; - Ok(match date.and_time(time).and_local_timezone(tz) { - MappedLocalTime::Single(dt) => dt, - // `FixedOffset::with_ymd_and_hms` doesn't return `MappedLocalTime::Ambiguous` - // and returns `MappedLocalTime::None` on invalid data - MappedLocalTime::Ambiguous(_, _) | MappedLocalTime::None => unreachable!(), - }) -} - -#[inline] -fn digit(bytes: &[u8; 19], index: usize) -> ParseResult { - match bytes[index].is_ascii_digit() { - true => Ok(bytes[index] - b'0'), - false => Err(INVALID), - } -} - -/// Tries to parse given string into `parsed` with given formatting items. -/// Returns `Ok` when the entire string has been parsed (otherwise `parsed` should not be used). -/// There should be no trailing string after parsing; -/// use a stray [`Item::Space`](./enum.Item.html#variant.Space) to trim whitespaces. -/// -/// This particular date and time parser is: -/// -/// - Greedy. It will consume the longest possible prefix. -/// For example, `April` is always consumed entirely when the long month name is requested; -/// it equally accepts `Apr`, but prefers the longer prefix in this case. -/// -/// - Padding-agnostic (for numeric items). -/// The [`Pad`](./enum.Pad.html) field is completely ignored, -/// so one can prepend any number of whitespace then any number of zeroes before numbers. -/// -/// - (Still) obeying the intrinsic parsing width. This allows, for example, parsing `HHMMSS`. -pub fn parse<'a, I, B>(parsed: &mut Parsed, s: &str, items: I) -> ParseResult<()> -where - I: Iterator, - B: Borrow>, -{ - match parse_internal(parsed, s, items) { - Ok("") => Ok(()), - Ok(_) => Err(TOO_LONG), // if there are trailing chars it is an error - Err(e) => Err(e), - } -} - -/// Tries to parse given string into `parsed` with given formatting items. -/// Returns `Ok` with a slice of the unparsed remainder. -/// -/// This particular date and time parser is: -/// -/// - Greedy. It will consume the longest possible prefix. -/// For example, `April` is always consumed entirely when the long month name is requested; -/// it equally accepts `Apr`, but prefers the longer prefix in this case. -/// -/// - Padding-agnostic (for numeric items). -/// The [`Pad`](./enum.Pad.html) field is completely ignored, -/// so one can prepend any number of zeroes before numbers. -/// -/// - (Still) obeying the intrinsic parsing width. This allows, for example, parsing `HHMMSS`. -pub fn parse_and_remainder<'a, 'b, I, B>( - parsed: &mut Parsed, - s: &'b str, - items: I, -) -> ParseResult<&'b str> -where - I: Iterator, - B: Borrow>, -{ - parse_internal(parsed, s, items) -} - -fn parse_internal<'a, 'b, I, B>( - parsed: &mut Parsed, - mut s: &'b str, - items: I, -) -> Result<&'b str, ParseError> -where - I: Iterator, - B: Borrow>, -{ - macro_rules! try_consume { - ($e:expr) => {{ - match $e { - Ok((s_, v)) => { - s = s_; - v - } - Err(e) => return Err(e), - } - }}; - } - - for item in items { - match *item.borrow() { - Item::Literal(prefix) => { - if s.len() < prefix.len() { - return Err(TOO_SHORT); - } - if !s.starts_with(prefix) { - return Err(INVALID); - } - s = &s[prefix.len()..]; - } - - #[cfg(feature = "alloc")] - Item::OwnedLiteral(ref prefix) => { - if s.len() < prefix.len() { - return Err(TOO_SHORT); - } - if !s.starts_with(&prefix[..]) { - return Err(INVALID); - } - s = &s[prefix.len()..]; - } - - Item::Space(_) => { - s = s.trim_start(); - } - - #[cfg(feature = "alloc")] - Item::OwnedSpace(_) => { - s = s.trim_start(); - } - - Item::Numeric(ref spec, ref _pad) => { - use super::Numeric::*; - type Setter = fn(&mut Parsed, i64) -> ParseResult<()>; - - let (width, signed, set): (usize, bool, Setter) = match *spec { - Year => (4, true, Parsed::set_year), - YearDiv100 => (2, false, Parsed::set_year_div_100), - YearMod100 => (2, false, Parsed::set_year_mod_100), - IsoYear => (4, true, Parsed::set_isoyear), - IsoYearDiv100 => (2, false, Parsed::set_isoyear_div_100), - IsoYearMod100 => (2, false, Parsed::set_isoyear_mod_100), - Quarter => (1, false, Parsed::set_quarter), - Month => (2, false, Parsed::set_month), - Day => (2, false, Parsed::set_day), - WeekFromSun => (2, false, Parsed::set_week_from_sun), - WeekFromMon => (2, false, Parsed::set_week_from_mon), - IsoWeek => (2, false, Parsed::set_isoweek), - NumDaysFromSun => (1, false, set_weekday_with_num_days_from_sunday), - WeekdayFromMon => (1, false, set_weekday_with_number_from_monday), - Ordinal => (3, false, Parsed::set_ordinal), - Hour => (2, false, Parsed::set_hour), - Hour12 => (2, false, Parsed::set_hour12), - Minute => (2, false, Parsed::set_minute), - Second => (2, false, Parsed::set_second), - Nanosecond => (9, false, Parsed::set_nanosecond), - Timestamp => (usize::MAX, false, Parsed::set_timestamp), - - // for the future expansion - Internal(ref int) => match int._dummy {}, - }; - - s = s.trim_start(); - let v = if signed { - if s.starts_with('-') { - let v = try_consume!(scan::number(&s[1..], 1, usize::MAX)); - 0i64.checked_sub(v).ok_or(OUT_OF_RANGE)? - } else if s.starts_with('+') { - try_consume!(scan::number(&s[1..], 1, usize::MAX)) - } else { - // if there is no explicit sign, we respect the original `width` - try_consume!(scan::number(s, 1, width)) - } - } else { - try_consume!(scan::number(s, 1, width)) - }; - set(parsed, v)?; - } - - Item::Fixed(ref spec) => { - use super::Fixed::*; - - match spec { - &ShortMonthName => { - let month0 = try_consume!(scan::short_month0(s)); - parsed.set_month(i64::from(month0) + 1)?; - } - - &LongMonthName => { - let month0 = try_consume!(scan::short_or_long_month0(s)); - parsed.set_month(i64::from(month0) + 1)?; - } - - &ShortWeekdayName => { - let weekday = try_consume!(scan::short_weekday(s)); - parsed.set_weekday(weekday)?; - } - - &LongWeekdayName => { - let weekday = try_consume!(scan::short_or_long_weekday(s)); - parsed.set_weekday(weekday)?; - } - - &LowerAmPm | &UpperAmPm => { - if s.len() < 2 { - return Err(TOO_SHORT); - } - let ampm = match (s.as_bytes()[0] | 32, s.as_bytes()[1] | 32) { - (b'a', b'm') => false, - (b'p', b'm') => true, - _ => return Err(INVALID), - }; - parsed.set_ampm(ampm)?; - s = &s[2..]; - } - - &Nanosecond => { - if s.starts_with('.') { - let nano = try_consume!(scan::nanosecond(&s[1..])); - parsed.set_nanosecond(nano as i64)?; - } - } - - &Nanosecond3 => { - if s.starts_with('.') { - let nano = try_consume!(scan::nanosecond_fixed(&s[1..], 3)); - parsed.set_nanosecond(nano)?; - } - } - - &Nanosecond6 => { - if s.starts_with('.') { - let nano = try_consume!(scan::nanosecond_fixed(&s[1..], 6)); - parsed.set_nanosecond(nano)?; - } - } - - &Nanosecond9 => { - if s.starts_with('.') { - let nano = try_consume!(scan::nanosecond_fixed(&s[1..], 9)); - parsed.set_nanosecond(nano)?; - } - } - - &Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot }) => { - if s.len() < 3 { - return Err(TOO_SHORT); - } - let nano = try_consume!(scan::nanosecond_fixed(s, 3)); - parsed.set_nanosecond(nano)?; - } - - &Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot }) => { - if s.len() < 6 { - return Err(TOO_SHORT); - } - let nano = try_consume!(scan::nanosecond_fixed(s, 6)); - parsed.set_nanosecond(nano)?; - } - - &Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot }) => { - if s.len() < 9 { - return Err(TOO_SHORT); - } - let nano = try_consume!(scan::nanosecond_fixed(s, 9)); - parsed.set_nanosecond(nano)?; - } - - &TimezoneName => { - try_consume!(Ok((s.trim_start_matches(|c: char| !c.is_whitespace()), ()))); - } - - &TimezoneOffsetColon - | &TimezoneOffsetDoubleColon - | &TimezoneOffsetTripleColon - | &TimezoneOffset => { - let offset = try_consume!(scan::timezone_offset( - s.trim_start(), - scan::colon_or_space, - false, - false, - true, - )); - parsed.set_offset(i64::from(offset))?; - } - - &TimezoneOffsetColonZ | &TimezoneOffsetZ => { - let offset = try_consume!(scan::timezone_offset( - s.trim_start(), - scan::colon_or_space, - true, - false, - true, - )); - parsed.set_offset(i64::from(offset))?; - } - &Internal(InternalFixed { - val: InternalInternal::TimezoneOffsetPermissive, - }) => { - let offset = try_consume!(scan::timezone_offset( - s.trim_start(), - scan::colon_or_space, - true, - true, - true, - )); - parsed.set_offset(i64::from(offset))?; - } - - &RFC2822 => try_consume!(parse_rfc2822(parsed, s)), - &RFC3339 => { - // Used for the `%+` specifier, which has the description: - // "Same as `%Y-%m-%dT%H:%M:%S%.f%:z` (...) - // This format also supports having a `Z` or `UTC` in place of `%:z`." - // Use the relaxed parser to match this description. - try_consume!(parse_rfc3339_relaxed(parsed, s)) - } - } - } - - Item::Error => { - return Err(BAD_FORMAT); - } - } - } - Ok(s) -} - -/// Accepts a relaxed form of RFC3339. -/// A space or a 'T' are accepted as the separator between the date and time -/// parts. Additional spaces are allowed between each component. -/// -/// All of these examples are equivalent: -/// ``` -/// # use chrono::{DateTime, offset::FixedOffset}; -/// "2012-12-12T12:12:12Z".parse::>()?; -/// "2012-12-12 12:12:12Z".parse::>()?; -/// "2012- 12-12T12: 12:12Z".parse::>()?; -/// # Ok::<(), chrono::ParseError>(()) -/// ``` -impl str::FromStr for DateTime { - type Err = ParseError; - - fn from_str(s: &str) -> ParseResult> { - let mut parsed = Parsed::new(); - let (s, _) = parse_rfc3339_relaxed(&mut parsed, s)?; - if !s.trim_start().is_empty() { - return Err(TOO_LONG); - } - parsed.to_datetime() - } -} - -/// Accepts a relaxed form of RFC3339. -/// -/// Differences with RFC3339: -/// - Values don't require padding to two digits. -/// - Years outside the range 0...=9999 are accepted, but they must include a sign. -/// - `UTC` is accepted as a valid timezone name/offset (for compatibility with the debug format of -/// `DateTime`. -/// - There can be spaces between any of the components. -/// - The colon in the offset may be missing. -fn parse_rfc3339_relaxed<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult<(&'a str, ())> { - const DATE_ITEMS: &[Item<'static>] = &[ - Item::Numeric(Numeric::Year, Pad::Zero), - Item::Space(""), - Item::Literal("-"), - Item::Numeric(Numeric::Month, Pad::Zero), - Item::Space(""), - Item::Literal("-"), - Item::Numeric(Numeric::Day, Pad::Zero), - ]; - const TIME_ITEMS: &[Item<'static>] = &[ - Item::Numeric(Numeric::Hour, Pad::Zero), - Item::Space(""), - Item::Literal(":"), - Item::Numeric(Numeric::Minute, Pad::Zero), - Item::Space(""), - Item::Literal(":"), - Item::Numeric(Numeric::Second, Pad::Zero), - Item::Fixed(Fixed::Nanosecond), - Item::Space(""), - ]; - - s = parse_internal(parsed, s, DATE_ITEMS.iter())?; - - s = match s.as_bytes().first() { - Some(&b't' | &b'T' | &b' ') => &s[1..], - Some(_) => return Err(INVALID), - None => return Err(TOO_SHORT), - }; - - s = parse_internal(parsed, s, TIME_ITEMS.iter())?; - s = s.trim_start(); - let (s, offset) = if s.len() >= 3 && "UTC".as_bytes().eq_ignore_ascii_case(&s.as_bytes()[..3]) { - (&s[3..], 0) - } else { - scan::timezone_offset(s, scan::colon_or_space, true, false, true)? - }; - parsed.set_offset(i64::from(offset))?; - Ok((s, ())) -} - -#[cfg(test)] -mod tests { - use crate::format::*; - use crate::{DateTime, FixedOffset, NaiveDateTime, TimeZone, Timelike, Utc}; - - macro_rules! parsed { - ($($k:ident: $v:expr),*) => (#[allow(unused_mut)] { - let mut expected = Parsed::new(); - $(expected.$k = Some($v);)* - Ok(expected) - }); - } - - #[test] - fn test_parse_whitespace_and_literal() { - use crate::format::Item::{Literal, Space}; - - // empty string - parses("", &[]); - check(" ", &[], Err(TOO_LONG)); - check("a", &[], Err(TOO_LONG)); - check("abc", &[], Err(TOO_LONG)); - check("🤠", &[], Err(TOO_LONG)); - - // whitespaces - parses("", &[Space("")]); - parses(" ", &[Space(" ")]); - parses(" ", &[Space(" ")]); - parses(" ", &[Space(" ")]); - parses(" ", &[Space("")]); - parses(" ", &[Space(" ")]); - parses(" ", &[Space(" ")]); - parses(" ", &[Space(" ")]); - parses("", &[Space(" ")]); - parses(" ", &[Space(" ")]); - parses(" ", &[Space(" ")]); - parses(" ", &[Space(" "), Space(" ")]); - parses(" ", &[Space(" "), Space(" ")]); - parses(" ", &[Space(" "), Space(" ")]); - parses(" ", &[Space(" "), Space(" ")]); - parses(" ", &[Space(" "), Space(" ")]); - parses(" ", &[Space(" "), Space(" "), Space(" ")]); - parses("\t", &[Space("")]); - parses(" \n\r \n", &[Space("")]); - parses("\t", &[Space("\t")]); - parses("\t", &[Space(" ")]); - parses(" ", &[Space("\t")]); - parses("\t\r", &[Space("\t\r")]); - parses("\t\r ", &[Space("\t\r ")]); - parses("\t \r", &[Space("\t \r")]); - parses(" \t\r", &[Space(" \t\r")]); - parses(" \n\r \n", &[Space(" \n\r \n")]); - parses(" \t\n", &[Space(" \t")]); - parses(" \n\t", &[Space(" \t\n")]); - parses("\u{2002}", &[Space("\u{2002}")]); - // most unicode whitespace characters - parses( - "\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{3000}", - &[Space( - "\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{3000}", - )], - ); - // most unicode whitespace characters - parses( - "\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{3000}", - &[ - Space("\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}"), - Space("\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{3000}"), - ], - ); - check("a", &[Space("")], Err(TOO_LONG)); - check("a", &[Space(" ")], Err(TOO_LONG)); - // a Space containing a literal does not match a literal - check("a", &[Space("a")], Err(TOO_LONG)); - check("abc", &[Space("")], Err(TOO_LONG)); - check("abc", &[Space(" ")], Err(TOO_LONG)); - check(" abc", &[Space("")], Err(TOO_LONG)); - check(" abc", &[Space(" ")], Err(TOO_LONG)); - - // `\u{0363}` is combining diacritic mark "COMBINING LATIN SMALL LETTER A" - - // literal - parses("", &[Literal("")]); - check("", &[Literal("a")], Err(TOO_SHORT)); - check(" ", &[Literal("a")], Err(INVALID)); - parses("a", &[Literal("a")]); - parses("+", &[Literal("+")]); - parses("-", &[Literal("-")]); - parses("−", &[Literal("−")]); // MINUS SIGN (U+2212) - parses(" ", &[Literal(" ")]); // a Literal may contain whitespace and match whitespace - check("aa", &[Literal("a")], Err(TOO_LONG)); - check("🤠", &[Literal("a")], Err(INVALID)); - check("A", &[Literal("a")], Err(INVALID)); - check("a", &[Literal("z")], Err(INVALID)); - check("a", &[Literal("🤠")], Err(TOO_SHORT)); - check("a", &[Literal("\u{0363}a")], Err(TOO_SHORT)); - check("\u{0363}a", &[Literal("a")], Err(INVALID)); - parses("\u{0363}a", &[Literal("\u{0363}a")]); - check("a", &[Literal("ab")], Err(TOO_SHORT)); - parses("xy", &[Literal("xy")]); - parses("xy", &[Literal("x"), Literal("y")]); - parses("1", &[Literal("1")]); - parses("1234", &[Literal("1234")]); - parses("+1234", &[Literal("+1234")]); - parses("-1234", &[Literal("-1234")]); - parses("−1234", &[Literal("−1234")]); // MINUS SIGN (U+2212) - parses("PST", &[Literal("PST")]); - parses("🤠", &[Literal("🤠")]); - parses("🤠a", &[Literal("🤠"), Literal("a")]); - parses("🤠a🤠", &[Literal("🤠"), Literal("a🤠")]); - parses("a🤠b", &[Literal("a"), Literal("🤠"), Literal("b")]); - // literals can be together - parses("xy", &[Literal("xy")]); - parses("xyz", &[Literal("xyz")]); - // or literals can be apart - parses("xy", &[Literal("x"), Literal("y")]); - parses("xyz", &[Literal("x"), Literal("yz")]); - parses("xyz", &[Literal("xy"), Literal("z")]); - parses("xyz", &[Literal("x"), Literal("y"), Literal("z")]); - // - check("x y", &[Literal("x"), Literal("y")], Err(INVALID)); - parses("xy", &[Literal("x"), Space(""), Literal("y")]); - parses("x y", &[Literal("x"), Space(""), Literal("y")]); - parses("x y", &[Literal("x"), Space(" "), Literal("y")]); - - // whitespaces + literals - parses("a\n", &[Literal("a"), Space("\n")]); - parses("\tab\n", &[Space("\t"), Literal("ab"), Space("\n")]); - parses( - "ab\tcd\ne", - &[Literal("ab"), Space("\t"), Literal("cd"), Space("\n"), Literal("e")], - ); - parses( - "+1ab\tcd\r\n+,.", - &[Literal("+1ab"), Space("\t"), Literal("cd"), Space("\r\n"), Literal("+,.")], - ); - // whitespace and literals can be intermixed - parses("a\tb", &[Literal("a\tb")]); - parses("a\tb", &[Literal("a"), Space("\t"), Literal("b")]); - } - - #[test] - fn test_parse_numeric() { - use crate::format::Item::{Literal, Space}; - use crate::format::Numeric::*; - - // numeric - check("1987", &[num(Year)], parsed!(year: 1987)); - check("1987 ", &[num(Year)], Err(TOO_LONG)); - check("0x12", &[num(Year)], Err(TOO_LONG)); // `0` is parsed - check("x123", &[num(Year)], Err(INVALID)); - check("o123", &[num(Year)], Err(INVALID)); - check("2015", &[num(Year)], parsed!(year: 2015)); - check("0000", &[num(Year)], parsed!(year: 0)); - check("9999", &[num(Year)], parsed!(year: 9999)); - check(" \t987", &[num(Year)], parsed!(year: 987)); - check(" \t987", &[Space(" \t"), num(Year)], parsed!(year: 987)); - check(" \t987🤠", &[Space(" \t"), num(Year), Literal("🤠")], parsed!(year: 987)); - check("987🤠", &[num(Year), Literal("🤠")], parsed!(year: 987)); - check("5", &[num(Year)], parsed!(year: 5)); - check("5\0", &[num(Year)], Err(TOO_LONG)); - check("\x005", &[num(Year)], Err(INVALID)); - check("", &[num(Year)], Err(TOO_SHORT)); - check("12345", &[num(Year), Literal("5")], parsed!(year: 1234)); - check("12345", &[nums(Year), Literal("5")], parsed!(year: 1234)); - check("12345", &[num0(Year), Literal("5")], parsed!(year: 1234)); - check("12341234", &[num(Year), num(Year)], parsed!(year: 1234)); - check("1234 1234", &[num(Year), num(Year)], parsed!(year: 1234)); - check("1234 1234", &[num(Year), Space(" "), num(Year)], parsed!(year: 1234)); - check("1234 1235", &[num(Year), num(Year)], Err(IMPOSSIBLE)); - check("1234 1234", &[num(Year), Literal("x"), num(Year)], Err(INVALID)); - check("1234x1234", &[num(Year), Literal("x"), num(Year)], parsed!(year: 1234)); - check("1234 x 1234", &[num(Year), Literal("x"), num(Year)], Err(INVALID)); - check("1234xx1234", &[num(Year), Literal("x"), num(Year)], Err(INVALID)); - check("1234xx1234", &[num(Year), Literal("xx"), num(Year)], parsed!(year: 1234)); - check( - "1234 x 1234", - &[num(Year), Space(" "), Literal("x"), Space(" "), num(Year)], - parsed!(year: 1234), - ); - check( - "1234 x 1235", - &[num(Year), Space(" "), Literal("x"), Space(" "), Literal("1235")], - parsed!(year: 1234), - ); - - // signed numeric - check("-42", &[num(Year)], parsed!(year: -42)); - check("+42", &[num(Year)], parsed!(year: 42)); - check("-0042", &[num(Year)], parsed!(year: -42)); - check("+0042", &[num(Year)], parsed!(year: 42)); - check("-42195", &[num(Year)], parsed!(year: -42195)); - check("−42195", &[num(Year)], Err(INVALID)); // MINUS SIGN (U+2212) - check("+42195", &[num(Year)], parsed!(year: 42195)); - check(" -42195", &[num(Year)], parsed!(year: -42195)); - check(" +42195", &[num(Year)], parsed!(year: 42195)); - check(" -42195", &[num(Year)], parsed!(year: -42195)); - check(" +42195", &[num(Year)], parsed!(year: 42195)); - check("-42195 ", &[num(Year)], Err(TOO_LONG)); - check("+42195 ", &[num(Year)], Err(TOO_LONG)); - check(" - 42", &[num(Year)], Err(INVALID)); - check(" + 42", &[num(Year)], Err(INVALID)); - check(" -42195", &[Space(" "), num(Year)], parsed!(year: -42195)); - check(" −42195", &[Space(" "), num(Year)], Err(INVALID)); // MINUS SIGN (U+2212) - check(" +42195", &[Space(" "), num(Year)], parsed!(year: 42195)); - check(" - 42", &[Space(" "), num(Year)], Err(INVALID)); - check(" + 42", &[Space(" "), num(Year)], Err(INVALID)); - check("-", &[num(Year)], Err(TOO_SHORT)); - check("+", &[num(Year)], Err(TOO_SHORT)); - - // unsigned numeric - check("345", &[num(Ordinal)], parsed!(ordinal: 345)); - check("+345", &[num(Ordinal)], Err(INVALID)); - check("-345", &[num(Ordinal)], Err(INVALID)); - check(" 345", &[num(Ordinal)], parsed!(ordinal: 345)); - check("−345", &[num(Ordinal)], Err(INVALID)); // MINUS SIGN (U+2212) - check("345 ", &[num(Ordinal)], Err(TOO_LONG)); - check(" 345", &[Space(" "), num(Ordinal)], parsed!(ordinal: 345)); - check("345 ", &[num(Ordinal), Space(" ")], parsed!(ordinal: 345)); - check("345🤠 ", &[num(Ordinal), Literal("🤠"), Space(" ")], parsed!(ordinal: 345)); - check("345🤠", &[num(Ordinal)], Err(TOO_LONG)); - check("\u{0363}345", &[num(Ordinal)], Err(INVALID)); - check(" +345", &[num(Ordinal)], Err(INVALID)); - check(" -345", &[num(Ordinal)], Err(INVALID)); - check("\t345", &[Space("\t"), num(Ordinal)], parsed!(ordinal: 345)); - check(" +345", &[Space(" "), num(Ordinal)], Err(INVALID)); - check(" -345", &[Space(" "), num(Ordinal)], Err(INVALID)); - - // various numeric fields - check("1234 5678", &[num(Year), num(IsoYear)], parsed!(year: 1234, isoyear: 5678)); - check("1234 5678", &[num(Year), num(IsoYear)], parsed!(year: 1234, isoyear: 5678)); - check( - "12 34 56 78", - &[num(YearDiv100), num(YearMod100), num(IsoYearDiv100), num(IsoYearMod100)], - parsed!(year_div_100: 12, year_mod_100: 34, isoyear_div_100: 56, isoyear_mod_100: 78), - ); - check( - "1 1 2 3 4 5", - &[ - num(Quarter), - num(Month), - num(Day), - num(WeekFromSun), - num(NumDaysFromSun), - num(IsoWeek), - ], - parsed!(quarter: 1, month: 1, day: 2, week_from_sun: 3, weekday: Weekday::Thu, isoweek: 5), - ); - check( - "6 7 89 01", - &[num(WeekFromMon), num(WeekdayFromMon), num(Ordinal), num(Hour12)], - parsed!(week_from_mon: 6, weekday: Weekday::Sun, ordinal: 89, hour_mod_12: 1), - ); - check( - "23 45 6 78901234 567890123", - &[num(Hour), num(Minute), num(Second), num(Nanosecond), num(Timestamp)], - parsed!(hour_div_12: 1, hour_mod_12: 11, minute: 45, second: 6, nanosecond: 78_901_234, timestamp: 567_890_123), - ); - } - - #[test] - fn test_parse_fixed() { - use crate::format::Fixed::*; - use crate::format::Item::{Literal, Space}; - - // fixed: month and weekday names - check("apr", &[fixed(ShortMonthName)], parsed!(month: 4)); - check("Apr", &[fixed(ShortMonthName)], parsed!(month: 4)); - check("APR", &[fixed(ShortMonthName)], parsed!(month: 4)); - check("ApR", &[fixed(ShortMonthName)], parsed!(month: 4)); - check("\u{0363}APR", &[fixed(ShortMonthName)], Err(INVALID)); - check("April", &[fixed(ShortMonthName)], Err(TOO_LONG)); // `Apr` is parsed - check("A", &[fixed(ShortMonthName)], Err(TOO_SHORT)); - check("Sol", &[fixed(ShortMonthName)], Err(INVALID)); - check("Apr", &[fixed(LongMonthName)], parsed!(month: 4)); - check("Apri", &[fixed(LongMonthName)], Err(TOO_LONG)); // `Apr` is parsed - check("April", &[fixed(LongMonthName)], parsed!(month: 4)); - check("Aprill", &[fixed(LongMonthName)], Err(TOO_LONG)); - check("Aprill", &[fixed(LongMonthName), Literal("l")], parsed!(month: 4)); - check("Aprl", &[fixed(LongMonthName), Literal("l")], parsed!(month: 4)); - check("April", &[fixed(LongMonthName), Literal("il")], Err(TOO_SHORT)); // do not backtrack - check("thu", &[fixed(ShortWeekdayName)], parsed!(weekday: Weekday::Thu)); - check("Thu", &[fixed(ShortWeekdayName)], parsed!(weekday: Weekday::Thu)); - check("THU", &[fixed(ShortWeekdayName)], parsed!(weekday: Weekday::Thu)); - check("tHu", &[fixed(ShortWeekdayName)], parsed!(weekday: Weekday::Thu)); - check("Thursday", &[fixed(ShortWeekdayName)], Err(TOO_LONG)); // `Thu` is parsed - check("T", &[fixed(ShortWeekdayName)], Err(TOO_SHORT)); - check("The", &[fixed(ShortWeekdayName)], Err(INVALID)); - check("Nop", &[fixed(ShortWeekdayName)], Err(INVALID)); - check("Thu", &[fixed(LongWeekdayName)], parsed!(weekday: Weekday::Thu)); - check("Thur", &[fixed(LongWeekdayName)], Err(TOO_LONG)); // `Thu` is parsed - check("Thurs", &[fixed(LongWeekdayName)], Err(TOO_LONG)); // `Thu` is parsed - check("Thursday", &[fixed(LongWeekdayName)], parsed!(weekday: Weekday::Thu)); - check("Thursdays", &[fixed(LongWeekdayName)], Err(TOO_LONG)); - check("Thursdays", &[fixed(LongWeekdayName), Literal("s")], parsed!(weekday: Weekday::Thu)); - check("Thus", &[fixed(LongWeekdayName), Literal("s")], parsed!(weekday: Weekday::Thu)); - check("Thursday", &[fixed(LongWeekdayName), Literal("rsday")], Err(TOO_SHORT)); // do not backtrack - - // fixed: am/pm - check("am", &[fixed(LowerAmPm)], parsed!(hour_div_12: 0)); - check("pm", &[fixed(LowerAmPm)], parsed!(hour_div_12: 1)); - check("AM", &[fixed(LowerAmPm)], parsed!(hour_div_12: 0)); - check("PM", &[fixed(LowerAmPm)], parsed!(hour_div_12: 1)); - check("am", &[fixed(UpperAmPm)], parsed!(hour_div_12: 0)); - check("pm", &[fixed(UpperAmPm)], parsed!(hour_div_12: 1)); - check("AM", &[fixed(UpperAmPm)], parsed!(hour_div_12: 0)); - check("PM", &[fixed(UpperAmPm)], parsed!(hour_div_12: 1)); - check("Am", &[fixed(LowerAmPm)], parsed!(hour_div_12: 0)); - check(" Am", &[Space(" "), fixed(LowerAmPm)], parsed!(hour_div_12: 0)); - check("Am🤠", &[fixed(LowerAmPm), Literal("🤠")], parsed!(hour_div_12: 0)); - check("🤠Am", &[Literal("🤠"), fixed(LowerAmPm)], parsed!(hour_div_12: 0)); - check("\u{0363}am", &[fixed(LowerAmPm)], Err(INVALID)); - check("\u{0360}am", &[fixed(LowerAmPm)], Err(INVALID)); - check(" Am", &[fixed(LowerAmPm)], Err(INVALID)); - check("Am ", &[fixed(LowerAmPm)], Err(TOO_LONG)); - check("a.m.", &[fixed(LowerAmPm)], Err(INVALID)); - check("A.M.", &[fixed(LowerAmPm)], Err(INVALID)); - check("ame", &[fixed(LowerAmPm)], Err(TOO_LONG)); // `am` is parsed - check("a", &[fixed(LowerAmPm)], Err(TOO_SHORT)); - check("p", &[fixed(LowerAmPm)], Err(TOO_SHORT)); - check("x", &[fixed(LowerAmPm)], Err(TOO_SHORT)); - check("xx", &[fixed(LowerAmPm)], Err(INVALID)); - check("", &[fixed(LowerAmPm)], Err(TOO_SHORT)); - } - - #[test] - fn test_parse_fixed_nanosecond() { - use crate::format::Fixed::{Nanosecond, Nanosecond3, Nanosecond6, Nanosecond9}; - use crate::format::InternalInternal::*; - use crate::format::Item::Literal; - use crate::format::Numeric::Second; - - // fixed: dot plus nanoseconds - check("", &[fixed(Nanosecond)], parsed!()); // no field set, but not an error - check(".", &[fixed(Nanosecond)], Err(TOO_SHORT)); - check("4", &[fixed(Nanosecond)], Err(TOO_LONG)); // never consumes `4` - check("4", &[fixed(Nanosecond), num(Second)], parsed!(second: 4)); - check(".0", &[fixed(Nanosecond)], parsed!(nanosecond: 0)); - check(".4", &[fixed(Nanosecond)], parsed!(nanosecond: 400_000_000)); - check(".42", &[fixed(Nanosecond)], parsed!(nanosecond: 420_000_000)); - check(".421", &[fixed(Nanosecond)], parsed!(nanosecond: 421_000_000)); - check(".42195", &[fixed(Nanosecond)], parsed!(nanosecond: 421_950_000)); - check(".421951", &[fixed(Nanosecond)], parsed!(nanosecond: 421_951_000)); - check(".4219512", &[fixed(Nanosecond)], parsed!(nanosecond: 421_951_200)); - check(".42195123", &[fixed(Nanosecond)], parsed!(nanosecond: 421_951_230)); - check(".421950803", &[fixed(Nanosecond)], parsed!(nanosecond: 421_950_803)); - check(".4219508035", &[fixed(Nanosecond)], parsed!(nanosecond: 421_950_803)); - check(".42195080354", &[fixed(Nanosecond)], parsed!(nanosecond: 421_950_803)); - check(".421950803547", &[fixed(Nanosecond)], parsed!(nanosecond: 421_950_803)); - check(".000000003", &[fixed(Nanosecond)], parsed!(nanosecond: 3)); - check(".0000000031", &[fixed(Nanosecond)], parsed!(nanosecond: 3)); - check(".0000000035", &[fixed(Nanosecond)], parsed!(nanosecond: 3)); - check(".000000003547", &[fixed(Nanosecond)], parsed!(nanosecond: 3)); - check(".0000000009", &[fixed(Nanosecond)], parsed!(nanosecond: 0)); - check(".000000000547", &[fixed(Nanosecond)], parsed!(nanosecond: 0)); - check(".0000000009999999999999999999999999", &[fixed(Nanosecond)], parsed!(nanosecond: 0)); - check(".4🤠", &[fixed(Nanosecond), Literal("🤠")], parsed!(nanosecond: 400_000_000)); - check(".4x", &[fixed(Nanosecond)], Err(TOO_LONG)); - check(". 4", &[fixed(Nanosecond)], Err(INVALID)); - check(" .4", &[fixed(Nanosecond)], Err(TOO_LONG)); // no automatic trimming - - // fixed-length fractions of a second - check("", &[fixed(Nanosecond3)], parsed!()); // no field set, but not an error - check("4", &[fixed(Nanosecond3)], Err(TOO_LONG)); // never consumes `4` - check(".12", &[fixed(Nanosecond3)], Err(TOO_SHORT)); - check(".123", &[fixed(Nanosecond3)], parsed!(nanosecond: 123_000_000)); - check(".1234", &[fixed(Nanosecond3)], Err(TOO_LONG)); - check(".1234", &[fixed(Nanosecond3), Literal("4")], parsed!(nanosecond: 123_000_000)); - - check("", &[fixed(Nanosecond6)], parsed!()); // no field set, but not an error - check("4", &[fixed(Nanosecond6)], Err(TOO_LONG)); // never consumes `4` - check(".12345", &[fixed(Nanosecond6)], Err(TOO_SHORT)); - check(".123456", &[fixed(Nanosecond6)], parsed!(nanosecond: 123_456_000)); - check(".1234567", &[fixed(Nanosecond6)], Err(TOO_LONG)); - check(".1234567", &[fixed(Nanosecond6), Literal("7")], parsed!(nanosecond: 123_456_000)); - - check("", &[fixed(Nanosecond9)], parsed!()); // no field set, but not an error - check("4", &[fixed(Nanosecond9)], Err(TOO_LONG)); // never consumes `4` - check(".12345678", &[fixed(Nanosecond9)], Err(TOO_SHORT)); - check(".123456789", &[fixed(Nanosecond9)], parsed!(nanosecond: 123_456_789)); - check(".1234567890", &[fixed(Nanosecond9)], Err(TOO_LONG)); - check(".1234567890", &[fixed(Nanosecond9), Literal("0")], parsed!(nanosecond: 123_456_789)); - - // fixed: nanoseconds without the dot - check("", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_SHORT)); - check(".", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_SHORT)); - check("0", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_SHORT)); - check("4", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_SHORT)); - check("42", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_SHORT)); - check("421", &[internal_fixed(Nanosecond3NoDot)], parsed!(nanosecond: 421_000_000)); - check("4210", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_LONG)); - check( - "42143", - &[internal_fixed(Nanosecond3NoDot), num(Second)], - parsed!(nanosecond: 421_000_000, second: 43), - ); - check( - "421🤠", - &[internal_fixed(Nanosecond3NoDot), Literal("🤠")], - parsed!(nanosecond: 421_000_000), - ); - check( - "🤠421", - &[Literal("🤠"), internal_fixed(Nanosecond3NoDot)], - parsed!(nanosecond: 421_000_000), - ); - check("42195", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_LONG)); - check("123456789", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_LONG)); - check("4x", &[internal_fixed(Nanosecond3NoDot)], Err(TOO_SHORT)); - check(" 4", &[internal_fixed(Nanosecond3NoDot)], Err(INVALID)); - check(".421", &[internal_fixed(Nanosecond3NoDot)], Err(INVALID)); - - check("", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_SHORT)); - check(".", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_SHORT)); - check("0", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_SHORT)); - check("1234", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_SHORT)); - check("12345", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_SHORT)); - check("421950", &[internal_fixed(Nanosecond6NoDot)], parsed!(nanosecond: 421_950_000)); - check("000003", &[internal_fixed(Nanosecond6NoDot)], parsed!(nanosecond: 3000)); - check("000000", &[internal_fixed(Nanosecond6NoDot)], parsed!(nanosecond: 0)); - check("1234567", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_LONG)); - check("123456789", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_LONG)); - check("4x", &[internal_fixed(Nanosecond6NoDot)], Err(TOO_SHORT)); - check(" 4", &[internal_fixed(Nanosecond6NoDot)], Err(INVALID)); - check(".42100", &[internal_fixed(Nanosecond6NoDot)], Err(INVALID)); - - check("", &[internal_fixed(Nanosecond9NoDot)], Err(TOO_SHORT)); - check(".", &[internal_fixed(Nanosecond9NoDot)], Err(TOO_SHORT)); - check("42195", &[internal_fixed(Nanosecond9NoDot)], Err(TOO_SHORT)); - check("12345678", &[internal_fixed(Nanosecond9NoDot)], Err(TOO_SHORT)); - check("421950803", &[internal_fixed(Nanosecond9NoDot)], parsed!(nanosecond: 421_950_803)); - check("000000003", &[internal_fixed(Nanosecond9NoDot)], parsed!(nanosecond: 3)); - check( - "42195080354", - &[internal_fixed(Nanosecond9NoDot), num(Second)], - parsed!(nanosecond: 421_950_803, second: 54), - ); // don't skip digits that come after the 9 - check("1234567890", &[internal_fixed(Nanosecond9NoDot)], Err(TOO_LONG)); - check("000000000", &[internal_fixed(Nanosecond9NoDot)], parsed!(nanosecond: 0)); - check("00000000x", &[internal_fixed(Nanosecond9NoDot)], Err(INVALID)); - check(" 4", &[internal_fixed(Nanosecond9NoDot)], Err(INVALID)); - check(".42100000", &[internal_fixed(Nanosecond9NoDot)], Err(INVALID)); - } - - #[test] - fn test_parse_fixed_timezone_offset() { - use crate::format::Fixed::*; - use crate::format::InternalInternal::*; - use crate::format::Item::Literal; - - // TimezoneOffset - check("1", &[fixed(TimezoneOffset)], Err(INVALID)); - check("12", &[fixed(TimezoneOffset)], Err(INVALID)); - check("123", &[fixed(TimezoneOffset)], Err(INVALID)); - check("1234", &[fixed(TimezoneOffset)], Err(INVALID)); - check("12345", &[fixed(TimezoneOffset)], Err(INVALID)); - check("123456", &[fixed(TimezoneOffset)], Err(INVALID)); - check("1234567", &[fixed(TimezoneOffset)], Err(INVALID)); - check("+1", &[fixed(TimezoneOffset)], Err(TOO_SHORT)); - check("+12", &[fixed(TimezoneOffset)], Err(TOO_SHORT)); - check("+123", &[fixed(TimezoneOffset)], Err(TOO_SHORT)); - check("+1234", &[fixed(TimezoneOffset)], parsed!(offset: 45_240)); - check("+12345", &[fixed(TimezoneOffset)], Err(TOO_LONG)); - check("+123456", &[fixed(TimezoneOffset)], Err(TOO_LONG)); - check("+1234567", &[fixed(TimezoneOffset)], Err(TOO_LONG)); - check("+12345678", &[fixed(TimezoneOffset)], Err(TOO_LONG)); - check("+12:", &[fixed(TimezoneOffset)], Err(TOO_SHORT)); - check("+12:3", &[fixed(TimezoneOffset)], Err(TOO_SHORT)); - check("+12:34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240)); - check("-12:34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240)); - check("−12:34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212) - check("+12:34:", &[fixed(TimezoneOffset)], Err(TOO_LONG)); - check("+12:34:5", &[fixed(TimezoneOffset)], Err(TOO_LONG)); - check("+12:34:56", &[fixed(TimezoneOffset)], Err(TOO_LONG)); - check("+12:34:56:", &[fixed(TimezoneOffset)], Err(TOO_LONG)); - check("+12 34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240)); - check("+12 34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240)); - check("12:34", &[fixed(TimezoneOffset)], Err(INVALID)); - check("12:34:56", &[fixed(TimezoneOffset)], Err(INVALID)); - check("+12::34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240)); - check("+12: :34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240)); - check("+12:::34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240)); - check("+12::::34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240)); - check("+12::34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240)); - check("+12:34:56", &[fixed(TimezoneOffset)], Err(TOO_LONG)); - check("+12:3456", &[fixed(TimezoneOffset)], Err(TOO_LONG)); - check("+1234:56", &[fixed(TimezoneOffset)], Err(TOO_LONG)); - check("+1234:567", &[fixed(TimezoneOffset)], Err(TOO_LONG)); - check("+00:00", &[fixed(TimezoneOffset)], parsed!(offset: 0)); - check("-00:00", &[fixed(TimezoneOffset)], parsed!(offset: 0)); - check("−00:00", &[fixed(TimezoneOffset)], parsed!(offset: 0)); // MINUS SIGN (U+2212) - check("+00:01", &[fixed(TimezoneOffset)], parsed!(offset: 60)); - check("-00:01", &[fixed(TimezoneOffset)], parsed!(offset: -60)); - check("+00:30", &[fixed(TimezoneOffset)], parsed!(offset: 1_800)); - check("-00:30", &[fixed(TimezoneOffset)], parsed!(offset: -1_800)); - check("+24:00", &[fixed(TimezoneOffset)], parsed!(offset: 86_400)); - check("-24:00", &[fixed(TimezoneOffset)], parsed!(offset: -86_400)); - check("−24:00", &[fixed(TimezoneOffset)], parsed!(offset: -86_400)); // MINUS SIGN (U+2212) - check("+99:59", &[fixed(TimezoneOffset)], parsed!(offset: 359_940)); - check("-99:59", &[fixed(TimezoneOffset)], parsed!(offset: -359_940)); - check("+00:60", &[fixed(TimezoneOffset)], Err(OUT_OF_RANGE)); - check("+00:99", &[fixed(TimezoneOffset)], Err(OUT_OF_RANGE)); - check("#12:34", &[fixed(TimezoneOffset)], Err(INVALID)); - check("+12:34 ", &[fixed(TimezoneOffset)], Err(TOO_LONG)); - check("+12 34 ", &[fixed(TimezoneOffset)], Err(TOO_LONG)); - check(" +12:34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240)); - check(" -12:34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240)); - check(" −12:34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212) - check(" +12:34", &[fixed(TimezoneOffset)], parsed!(offset: 45_240)); - check(" -12:34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240)); - check("\t -12:34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240)); - check("-12: 34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240)); - check("-12 :34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240)); - check("-12 : 34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240)); - check("-12 : 34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240)); - check("-12 : 34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240)); - check("-12: 34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240)); - check("-12 :34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240)); - check("-12 : 34", &[fixed(TimezoneOffset)], parsed!(offset: -45_240)); - check("12:34 ", &[fixed(TimezoneOffset)], Err(INVALID)); - check(" 12:34", &[fixed(TimezoneOffset)], Err(INVALID)); - check("", &[fixed(TimezoneOffset)], Err(TOO_SHORT)); - check("+", &[fixed(TimezoneOffset)], Err(TOO_SHORT)); - check( - "+12345", - &[fixed(TimezoneOffset), num(Numeric::Day)], - parsed!(offset: 45_240, day: 5), - ); - check( - "+12:345", - &[fixed(TimezoneOffset), num(Numeric::Day)], - parsed!(offset: 45_240, day: 5), - ); - check("+12:34:", &[fixed(TimezoneOffset), Literal(":")], parsed!(offset: 45_240)); - check("Z12:34", &[fixed(TimezoneOffset)], Err(INVALID)); - check("X12:34", &[fixed(TimezoneOffset)], Err(INVALID)); - check("Z+12:34", &[fixed(TimezoneOffset)], Err(INVALID)); - check("X+12:34", &[fixed(TimezoneOffset)], Err(INVALID)); - check("X−12:34", &[fixed(TimezoneOffset)], Err(INVALID)); // MINUS SIGN (U+2212) - check("🤠+12:34", &[fixed(TimezoneOffset)], Err(INVALID)); - check("+12:34🤠", &[fixed(TimezoneOffset)], Err(TOO_LONG)); - check("+12:🤠34", &[fixed(TimezoneOffset)], Err(INVALID)); - check("+1234🤠", &[fixed(TimezoneOffset), Literal("🤠")], parsed!(offset: 45_240)); - check("-1234🤠", &[fixed(TimezoneOffset), Literal("🤠")], parsed!(offset: -45_240)); - check("−1234🤠", &[fixed(TimezoneOffset), Literal("🤠")], parsed!(offset: -45_240)); // MINUS SIGN (U+2212) - check("+12:34🤠", &[fixed(TimezoneOffset), Literal("🤠")], parsed!(offset: 45_240)); - check("-12:34🤠", &[fixed(TimezoneOffset), Literal("🤠")], parsed!(offset: -45_240)); - check("−12:34🤠", &[fixed(TimezoneOffset), Literal("🤠")], parsed!(offset: -45_240)); // MINUS SIGN (U+2212) - check("🤠+12:34", &[Literal("🤠"), fixed(TimezoneOffset)], parsed!(offset: 45_240)); - check("Z", &[fixed(TimezoneOffset)], Err(INVALID)); - check("A", &[fixed(TimezoneOffset)], Err(INVALID)); - check("PST", &[fixed(TimezoneOffset)], Err(INVALID)); - check("#Z", &[fixed(TimezoneOffset)], Err(INVALID)); - check(":Z", &[fixed(TimezoneOffset)], Err(INVALID)); - check("+Z", &[fixed(TimezoneOffset)], Err(TOO_SHORT)); - check("+:Z", &[fixed(TimezoneOffset)], Err(INVALID)); - check("+Z:", &[fixed(TimezoneOffset)], Err(INVALID)); - check("z", &[fixed(TimezoneOffset)], Err(INVALID)); - check(" :Z", &[fixed(TimezoneOffset)], Err(INVALID)); - check(" Z", &[fixed(TimezoneOffset)], Err(INVALID)); - check(" z", &[fixed(TimezoneOffset)], Err(INVALID)); - - // TimezoneOffsetColon - check("1", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("12", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("123", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("1234", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("12345", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("123456", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("1234567", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("12345678", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("+1", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT)); - check("+12", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT)); - check("+123", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT)); - check("+1234", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240)); - check("-1234", &[fixed(TimezoneOffsetColon)], parsed!(offset: -45_240)); - check("−1234", &[fixed(TimezoneOffsetColon)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212) - check("+12345", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG)); - check("+123456", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG)); - check("+1234567", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG)); - check("+12345678", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG)); - check("1:", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("12:", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("12:3", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("12:34", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("12:34:", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("12:34:5", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("12:34:56", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("+1:", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("+12:", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT)); - check("+12:3", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT)); - check("+12:34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240)); - check("-12:34", &[fixed(TimezoneOffsetColon)], parsed!(offset: -45_240)); - check("−12:34", &[fixed(TimezoneOffsetColon)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212) - check("+12:34:", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG)); - check("+12:34:5", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG)); - check("+12:34:56", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG)); - check("+12:34:56:", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG)); - check("+12:34:56:7", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG)); - check("+12:34:56:78", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG)); - check("+12:3456", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG)); - check("+1234:56", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG)); - check("−12:34", &[fixed(TimezoneOffsetColon)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212) - check("−12 : 34", &[fixed(TimezoneOffsetColon)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212) - check("+12 :34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240)); - check("+12: 34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240)); - check("+12 34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240)); - check("+12: 34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240)); - check("+12 :34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240)); - check("+12 : 34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240)); - check("-12 : 34", &[fixed(TimezoneOffsetColon)], parsed!(offset: -45_240)); - check("+12 : 34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240)); - check("+12 : 34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240)); - check("+12 : 34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240)); - check("+12::34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240)); - check("+12: :34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240)); - check("+12:::34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240)); - check("+12::::34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240)); - check("+12::34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240)); - check("#1234", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("#12:34", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("+12:34 ", &[fixed(TimezoneOffsetColon)], Err(TOO_LONG)); - check(" +12:34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240)); - check("\t+12:34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240)); - check("\t\t+12:34", &[fixed(TimezoneOffsetColon)], parsed!(offset: 45_240)); - check("12:34 ", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check(" 12:34", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT)); - check("+", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT)); - check(":", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check( - "+12345", - &[fixed(TimezoneOffsetColon), num(Numeric::Day)], - parsed!(offset: 45_240, day: 5), - ); - check( - "+12:345", - &[fixed(TimezoneOffsetColon), num(Numeric::Day)], - parsed!(offset: 45_240, day: 5), - ); - check("+12:34:", &[fixed(TimezoneOffsetColon), Literal(":")], parsed!(offset: 45_240)); - check("Z", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("A", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("PST", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("#Z", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check(":Z", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("+Z", &[fixed(TimezoneOffsetColon)], Err(TOO_SHORT)); - check("+:Z", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("+Z:", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check("z", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check(" :Z", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check(" Z", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - check(" z", &[fixed(TimezoneOffsetColon)], Err(INVALID)); - // testing `TimezoneOffsetColon` also tests same path as `TimezoneOffsetDoubleColon` - // and `TimezoneOffsetTripleColon` for function `parse_internal`. - // No need for separate tests for `TimezoneOffsetDoubleColon` and - // `TimezoneOffsetTripleColon`. - - // TimezoneOffsetZ - check("1", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("12", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("123", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("1234", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("12345", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("123456", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("1234567", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("12345678", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("+1", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT)); - check("+12", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT)); - check("+123", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT)); - check("+1234", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240)); - check("-1234", &[fixed(TimezoneOffsetZ)], parsed!(offset: -45_240)); - check("−1234", &[fixed(TimezoneOffsetZ)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212) - check("+12345", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG)); - check("+123456", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG)); - check("+1234567", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG)); - check("+12345678", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG)); - check("1:", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("12:", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("12:3", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("12:34", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("12:34:", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("12:34:5", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("12:34:56", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("+1:", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("+12:", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT)); - check("+12:3", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT)); - check("+12:34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240)); - check("-12:34", &[fixed(TimezoneOffsetZ)], parsed!(offset: -45_240)); - check("−12:34", &[fixed(TimezoneOffsetZ)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212) - check("+12:34:", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG)); - check("+12:34:5", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG)); - check("+12:34:56", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG)); - check("+12:34:56:", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG)); - check("+12:34:56:7", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG)); - check("+12:34:56:78", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG)); - check("+12::34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240)); - check("+12:3456", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG)); - check("+1234:56", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG)); - check("+12 34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240)); - check("+12 34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240)); - check("+12: 34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240)); - check("+12 :34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240)); - check("+12 : 34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240)); - check("+12 : 34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240)); - check("+12 : 34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240)); - check("+12 : 34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240)); - check("12:34 ", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check(" 12:34", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("+12:34 ", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG)); - check("+12 34 ", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG)); - check(" +12:34", &[fixed(TimezoneOffsetZ)], parsed!(offset: 45_240)); - check( - "+12345", - &[fixed(TimezoneOffsetZ), num(Numeric::Day)], - parsed!(offset: 45_240, day: 5), - ); - check( - "+12:345", - &[fixed(TimezoneOffsetZ), num(Numeric::Day)], - parsed!(offset: 45_240, day: 5), - ); - check("+12:34:", &[fixed(TimezoneOffsetZ), Literal(":")], parsed!(offset: 45_240)); - check("Z12:34", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG)); - check("X12:34", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("Z", &[fixed(TimezoneOffsetZ)], parsed!(offset: 0)); - check("z", &[fixed(TimezoneOffsetZ)], parsed!(offset: 0)); - check(" Z", &[fixed(TimezoneOffsetZ)], parsed!(offset: 0)); - check(" z", &[fixed(TimezoneOffsetZ)], parsed!(offset: 0)); - check("\u{0363}Z", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("Z ", &[fixed(TimezoneOffsetZ)], Err(TOO_LONG)); - check("A", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("PST", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("#Z", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check(":Z", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check(":z", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("+Z", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT)); - check("-Z", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT)); - check("+A", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT)); - check("+🙃", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("+Z:", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check(" :Z", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check(" +Z", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT)); - check(" -Z", &[fixed(TimezoneOffsetZ)], Err(TOO_SHORT)); - check("+:Z", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("Y", &[fixed(TimezoneOffsetZ)], Err(INVALID)); - check("Zulu", &[fixed(TimezoneOffsetZ), Literal("ulu")], parsed!(offset: 0)); - check("zulu", &[fixed(TimezoneOffsetZ), Literal("ulu")], parsed!(offset: 0)); - check("+1234ulu", &[fixed(TimezoneOffsetZ), Literal("ulu")], parsed!(offset: 45_240)); - check("+12:34ulu", &[fixed(TimezoneOffsetZ), Literal("ulu")], parsed!(offset: 45_240)); - // Testing `TimezoneOffsetZ` also tests same path as `TimezoneOffsetColonZ` - // in function `parse_internal`. - // No need for separate tests for `TimezoneOffsetColonZ`. - - // TimezoneOffsetPermissive - check("1", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("12", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("123", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("1234", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("12345", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("123456", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("1234567", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("12345678", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("+1", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT)); - check("+12", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 43_200)); - check("+123", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT)); - check("+1234", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240)); - check("-1234", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: -45_240)); - check("−1234", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212) - check("+12345", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG)); - check("+123456", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG)); - check("+1234567", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG)); - check("+12345678", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG)); - check("1:", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("12:", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("12:3", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("12:34", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("12:34:", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("12:34:5", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("12:34:56", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("+1:", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("+12:", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 43_200)); - check("+12:3", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT)); - check("+12:34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240)); - check("-12:34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: -45_240)); - check("−12:34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212) - check("+12:34:", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG)); - check("+12:34:5", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG)); - check("+12:34:56", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG)); - check("+12:34:56:", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG)); - check("+12:34:56:7", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG)); - check("+12:34:56:78", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG)); - check("+12 34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240)); - check("+12 34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240)); - check("+12 :34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240)); - check("+12: 34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240)); - check("+12 : 34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240)); - check("+12 :34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240)); - check("+12: 34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240)); - check("+12 : 34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240)); - check("+12::34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240)); - check("+12 ::34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240)); - check("+12: :34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240)); - check("+12:: 34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240)); - check("+12 ::34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240)); - check("+12: :34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240)); - check("+12:: 34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240)); - check("+12:::34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240)); - check("+12::::34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240)); - check("12:34 ", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check(" 12:34", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("+12:34 ", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG)); - check(" +12:34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 45_240)); - check(" -12:34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: -45_240)); - check(" −12:34", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: -45_240)); // MINUS SIGN (U+2212) - check( - "+12345", - &[internal_fixed(TimezoneOffsetPermissive), num(Numeric::Day)], - parsed!(offset: 45_240, day: 5), - ); - check( - "+12:345", - &[internal_fixed(TimezoneOffsetPermissive), num(Numeric::Day)], - parsed!(offset: 45_240, day: 5), - ); - check( - "+12:34:", - &[internal_fixed(TimezoneOffsetPermissive), Literal(":")], - parsed!(offset: 45_240), - ); - check("🤠+12:34", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("+12:34🤠", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG)); - check("+12:🤠34", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check( - "+12:34🤠", - &[internal_fixed(TimezoneOffsetPermissive), Literal("🤠")], - parsed!(offset: 45_240), - ); - check( - "🤠+12:34", - &[Literal("🤠"), internal_fixed(TimezoneOffsetPermissive)], - parsed!(offset: 45_240), - ); - check("Z", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 0)); - check("A", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("PST", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("z", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 0)); - check(" Z", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 0)); - check(" z", &[internal_fixed(TimezoneOffsetPermissive)], parsed!(offset: 0)); - check("Z ", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_LONG)); - check("#Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check(":Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check(":z", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("+Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT)); - check("-Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT)); - check("+A", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT)); - check("+PST", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("+🙃", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("+Z:", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check(" :Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check(" +Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT)); - check(" -Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(TOO_SHORT)); - check("+:Z", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - check("Y", &[internal_fixed(TimezoneOffsetPermissive)], Err(INVALID)); - - // TimezoneName - check("CEST", &[fixed(TimezoneName)], parsed!()); - check("cest", &[fixed(TimezoneName)], parsed!()); // lowercase - check("XXXXXXXX", &[fixed(TimezoneName)], parsed!()); // not a real timezone name - check("!!!!", &[fixed(TimezoneName)], parsed!()); // not a real timezone name! - check("CEST 5", &[fixed(TimezoneName), Literal(" "), num(Numeric::Day)], parsed!(day: 5)); - check("CEST ", &[fixed(TimezoneName)], Err(TOO_LONG)); - check(" CEST", &[fixed(TimezoneName)], Err(TOO_LONG)); - check("CE ST", &[fixed(TimezoneName)], Err(TOO_LONG)); - } - - #[test] - #[rustfmt::skip] - fn test_parse_practical_examples() { - use crate::format::InternalInternal::*; - use crate::format::Item::{Literal, Space}; - use crate::format::Numeric::*; - - // some practical examples - check( - "2015-02-04T14:37:05+09:00", - &[ - num(Year), Literal("-"), num(Month), Literal("-"), num(Day), Literal("T"), - num(Hour), Literal(":"), num(Minute), Literal(":"), num(Second), - fixed(Fixed::TimezoneOffset), - ], - parsed!( - year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2, minute: 37, - second: 5, offset: 32400 - ), - ); - check( - "2015-02-04T14:37:05-09:00", - &[ - num(Year), Literal("-"), num(Month), Literal("-"), num(Day), Literal("T"), - num(Hour), Literal(":"), num(Minute), Literal(":"), num(Second), - fixed(Fixed::TimezoneOffset), - ], - parsed!( - year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2, minute: 37, - second: 5, offset: -32400 - ), - ); - check( - "2015-02-04T14:37:05−09:00", // timezone offset using MINUS SIGN (U+2212) - &[ - num(Year), Literal("-"), num(Month), Literal("-"), num(Day), Literal("T"), - num(Hour), Literal(":"), num(Minute), Literal(":"), num(Second), - fixed(Fixed::TimezoneOffset) - ], - parsed!( - year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2, minute: 37, - second: 5, offset: -32400 - ), - ); - check( - "20150204143705567", - &[ - num(Year), num(Month), num(Day), num(Hour), num(Minute), num(Second), - internal_fixed(Nanosecond3NoDot) - ], - parsed!( - year: 2015, month: 2, day: 4, hour_div_12: 1, hour_mod_12: 2, minute: 37, - second: 5, nanosecond: 567000000 - ), - ); - check( - "Mon, 10 Jun 2013 09:32:37 GMT", - &[ - fixed(Fixed::ShortWeekdayName), Literal(","), Space(" "), num(Day), Space(" "), - fixed(Fixed::ShortMonthName), Space(" "), num(Year), Space(" "), num(Hour), - Literal(":"), num(Minute), Literal(":"), num(Second), Space(" "), Literal("GMT") - ], - parsed!( - year: 2013, month: 6, day: 10, weekday: Weekday::Mon, - hour_div_12: 0, hour_mod_12: 9, minute: 32, second: 37 - ), - ); - check( - "🤠Mon, 10 Jun🤠2013 09:32:37 GMT🤠", - &[ - Literal("🤠"), fixed(Fixed::ShortWeekdayName), Literal(","), Space(" "), num(Day), - Space(" "), fixed(Fixed::ShortMonthName), Literal("🤠"), num(Year), Space(" "), - num(Hour), Literal(":"), num(Minute), Literal(":"), num(Second), Space(" "), - Literal("GMT"), Literal("🤠") - ], - parsed!( - year: 2013, month: 6, day: 10, weekday: Weekday::Mon, - hour_div_12: 0, hour_mod_12: 9, minute: 32, second: 37 - ), - ); - check( - "Sun Aug 02 13:39:15 CEST 2020", - &[ - fixed(Fixed::ShortWeekdayName), Space(" "), fixed(Fixed::ShortMonthName), - Space(" "), num(Day), Space(" "), num(Hour), Literal(":"), num(Minute), - Literal(":"), num(Second), Space(" "), fixed(Fixed::TimezoneName), Space(" "), - num(Year) - ], - parsed!( - year: 2020, month: 8, day: 2, weekday: Weekday::Sun, - hour_div_12: 1, hour_mod_12: 1, minute: 39, second: 15 - ), - ); - check( - "20060102150405", - &[num(Year), num(Month), num(Day), num(Hour), num(Minute), num(Second)], - parsed!( - year: 2006, month: 1, day: 2, hour_div_12: 1, hour_mod_12: 3, minute: 4, second: 5 - ), - ); - check( - "3:14PM", - &[num(Hour12), Literal(":"), num(Minute), fixed(Fixed::LowerAmPm)], - parsed!(hour_div_12: 1, hour_mod_12: 3, minute: 14), - ); - check( - "12345678901234.56789", - &[num(Timestamp), Literal("."), num(Nanosecond)], - parsed!(nanosecond: 56_789, timestamp: 12_345_678_901_234), - ); - check( - "12345678901234.56789", - &[num(Timestamp), fixed(Fixed::Nanosecond)], - parsed!(nanosecond: 567_890_000, timestamp: 12_345_678_901_234), - ); - - // docstring examples from `impl str::FromStr` - check( - "2000-01-02T03:04:05Z", - &[ - num(Year), Literal("-"), num(Month), Literal("-"), num(Day), Literal("T"), - num(Hour), Literal(":"), num(Minute), Literal(":"), num(Second), - internal_fixed(TimezoneOffsetPermissive) - ], - parsed!( - year: 2000, month: 1, day: 2, hour_div_12: 0, hour_mod_12: 3, minute: 4, second: 5, - offset: 0 - ), - ); - check( - "2000-01-02 03:04:05Z", - &[ - num(Year), Literal("-"), num(Month), Literal("-"), num(Day), Space(" "), - num(Hour), Literal(":"), num(Minute), Literal(":"), num(Second), - internal_fixed(TimezoneOffsetPermissive) - ], - parsed!( - year: 2000, month: 1, day: 2, hour_div_12: 0, hour_mod_12: 3, minute: 4, second: 5, - offset: 0 - ), - ); - } - - #[track_caller] - fn parses(s: &str, items: &[Item]) { - let mut parsed = Parsed::new(); - assert!(parse(&mut parsed, s, items.iter()).is_ok()); - } - - #[track_caller] - fn check(s: &str, items: &[Item], expected: ParseResult) { - let mut parsed = Parsed::new(); - let result = parse(&mut parsed, s, items.iter()); - let parsed = result.map(|_| parsed); - assert_eq!(parsed, expected); - } - - #[test] - fn test_rfc2822() { - let ymd_hmsn = |y, m, d, h, n, s, nano, off| { - FixedOffset::east_opt(off * 60 * 60) - .unwrap() - .with_ymd_and_hms(y, m, d, h, n, s) - .unwrap() - .with_nanosecond(nano) - .unwrap() - }; - - // Test data - (input, Ok(expected result) or Err(error code)) - let testdates = [ - ("Tue, 20 Jan 2015 17:35:20 -0800", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8))), // normal case - ("Fri, 2 Jan 2015 17:35:20 -0800", Ok(ymd_hmsn(2015, 1, 2, 17, 35, 20, 0, -8))), // folding whitespace - ("Fri, 02 Jan 2015 17:35:20 -0800", Ok(ymd_hmsn(2015, 1, 2, 17, 35, 20, 0, -8))), // leading zero - ("Tue, 20 Jan 2015 17:35:20 -0800 (UTC)", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8))), // trailing comment - ( - r"Tue, 20 Jan 2015 17:35:20 -0800 ( (UTC ) (\( (a)\(( \t ) ) \\( \) ))", - Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8)), - ), // complex trailing comment - (r"Tue, 20 Jan 2015 17:35:20 -0800 (UTC\)", Err(TOO_LONG)), // incorrect comment, not enough closing parentheses - ( - "Tue, 20 Jan 2015 17:35:20 -0800 (UTC)\t \r\n(Anothercomment)", - Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8)), - ), // multiple comments - ("Tue, 20 Jan 2015 17:35:20 -0800 (UTC) ", Err(TOO_LONG)), // trailing whitespace after comment - ("20 Jan 2015 17:35:20 -0800", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8))), // no day of week - ("20 JAN 2015 17:35:20 -0800", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8))), // upper case month - ("Tue, 20 Jan 2015 17:35 -0800", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 0, 0, -8))), // no second - ("11 Sep 2001 09:45:00 +0000", Ok(ymd_hmsn(2001, 9, 11, 9, 45, 0, 0, 0))), - ("11 Sep 2001 09:45:00 EST", Ok(ymd_hmsn(2001, 9, 11, 9, 45, 0, 0, -5))), - ("11 Sep 2001 09:45:00 GMT", Ok(ymd_hmsn(2001, 9, 11, 9, 45, 0, 0, 0))), - ("30 Feb 2015 17:35:20 -0800", Err(OUT_OF_RANGE)), // bad day of month - ("Tue, 20 Jan 2015", Err(TOO_SHORT)), // omitted fields - ("Tue, 20 Avr 2015 17:35:20 -0800", Err(INVALID)), // bad month name - ("Tue, 20 Jan 2015 25:35:20 -0800", Err(OUT_OF_RANGE)), // bad hour - ("Tue, 20 Jan 2015 7:35:20 -0800", Err(INVALID)), // bad # of digits in hour - ("Tue, 20 Jan 2015 17:65:20 -0800", Err(OUT_OF_RANGE)), // bad minute - ("Tue, 20 Jan 2015 17:35:90 -0800", Err(OUT_OF_RANGE)), // bad second - ("Tue, 20 Jan 2015 17:35:20 -0890", Err(OUT_OF_RANGE)), // bad offset - ("6 Jun 1944 04:00:00Z", Err(INVALID)), // bad offset (zulu not allowed) - // named timezones that have specific timezone offsets - // see https://www.rfc-editor.org/rfc/rfc2822#section-4.3 - ("Tue, 20 Jan 2015 17:35:20 GMT", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, 0))), - ("Tue, 20 Jan 2015 17:35:20 UT", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, 0))), - ("Tue, 20 Jan 2015 17:35:20 ut", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, 0))), - ("Tue, 20 Jan 2015 17:35:20 EDT", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -4))), - ("Tue, 20 Jan 2015 17:35:20 EST", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -5))), - ("Tue, 20 Jan 2015 17:35:20 CDT", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -5))), - ("Tue, 20 Jan 2015 17:35:20 CST", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -6))), - ("Tue, 20 Jan 2015 17:35:20 MDT", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -6))), - ("Tue, 20 Jan 2015 17:35:20 MST", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -7))), - ("Tue, 20 Jan 2015 17:35:20 PDT", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -7))), - ("Tue, 20 Jan 2015 17:35:20 PST", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8))), - ("Tue, 20 Jan 2015 17:35:20 pst", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8))), - // named single-letter military timezones must fallback to +0000 - ("Tue, 20 Jan 2015 17:35:20 Z", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, 0))), - ("Tue, 20 Jan 2015 17:35:20 A", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, 0))), - ("Tue, 20 Jan 2015 17:35:20 a", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, 0))), - ("Tue, 20 Jan 2015 17:35:20 K", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, 0))), - ("Tue, 20 Jan 2015 17:35:20 k", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, 0))), - // named single-letter timezone "J" is specifically not valid - ("Tue, 20 Jan 2015 17:35:20 J", Err(INVALID)), - ("Tue, 20 Jan 2015 17:35:20 -0890", Err(OUT_OF_RANGE)), // bad offset minutes - ("Tue, 20 Jan 2015 17:35:20Z", Err(INVALID)), // bad offset: zulu not allowed - ("Tue, 20 Jan 2015 17:35:20 Zulu", Err(INVALID)), // bad offset: zulu not allowed - ("Tue, 20 Jan 2015 17:35:20 ZULU", Err(INVALID)), // bad offset: zulu not allowed - ("Tue, 20 Jan 2015 17:35:20 −0800", Err(INVALID)), // bad offset: timezone offset using MINUS SIGN (U+2212), not specified for RFC 2822 - ("Tue, 20 Jan 2015 17:35:20 0800", Err(INVALID)), // missing offset sign - ("Tue, 20 Jan 2015 17:35:20 HAS", Err(INVALID)), // bad named timezone - ("Tue, 20 Jan 2015😈17:35:20 -0800", Err(INVALID)), // bad character! - ]; - - fn rfc2822_to_datetime(date: &str) -> ParseResult> { - let mut parsed = Parsed::new(); - parse(&mut parsed, date, [Item::Fixed(Fixed::RFC2822)].iter())?; - parsed.to_datetime() - } - - // Test against test data above - for &(date, checkdate) in testdates.iter() { - #[cfg(feature = "std")] - eprintln!("Test input: {date:?}\n Expect: {checkdate:?}"); - let dt = rfc2822_to_datetime(date); // parse a date - if dt != checkdate { - // check for expected result - panic!( - "Date conversion failed for {date}\nReceived: {dt:?}\nExpected: {checkdate:?}" - ); - } - } - } - - #[test] - fn parse_rfc850() { - static RFC850_FMT: &str = "%A, %d-%b-%y %T GMT"; - - let dt = Utc.with_ymd_and_hms(1994, 11, 6, 8, 49, 37).unwrap(); - - // Check that the format is what we expect - #[cfg(feature = "alloc")] - assert_eq!(dt.format(RFC850_FMT).to_string(), "Sunday, 06-Nov-94 08:49:37 GMT"); - - // Check that it parses correctly - assert_eq!( - NaiveDateTime::parse_from_str("Sunday, 06-Nov-94 08:49:37 GMT", RFC850_FMT), - Ok(dt.naive_utc()) - ); - - // Check that the rest of the weekdays parse correctly (this test originally failed because - // Sunday parsed incorrectly). - let testdates = [ - ( - Utc.with_ymd_and_hms(1994, 11, 7, 8, 49, 37).unwrap(), - "Monday, 07-Nov-94 08:49:37 GMT", - ), - ( - Utc.with_ymd_and_hms(1994, 11, 8, 8, 49, 37).unwrap(), - "Tuesday, 08-Nov-94 08:49:37 GMT", - ), - ( - Utc.with_ymd_and_hms(1994, 11, 9, 8, 49, 37).unwrap(), - "Wednesday, 09-Nov-94 08:49:37 GMT", - ), - ( - Utc.with_ymd_and_hms(1994, 11, 10, 8, 49, 37).unwrap(), - "Thursday, 10-Nov-94 08:49:37 GMT", - ), - ( - Utc.with_ymd_and_hms(1994, 11, 11, 8, 49, 37).unwrap(), - "Friday, 11-Nov-94 08:49:37 GMT", - ), - ( - Utc.with_ymd_and_hms(1994, 11, 12, 8, 49, 37).unwrap(), - "Saturday, 12-Nov-94 08:49:37 GMT", - ), - ]; - - for val in &testdates { - assert_eq!(NaiveDateTime::parse_from_str(val.1, RFC850_FMT), Ok(val.0.naive_utc())); - } - - let test_dates_fail = [ - "Saturday, 12-Nov-94 08:49:37", - "Saturday, 12-Nov-94 08:49:37 Z", - "Saturday, 12-Nov-94 08:49:37 GMTTTT", - "Saturday, 12-Nov-94 08:49:37 gmt", - "Saturday, 12-Nov-94 08:49:37 +08:00", - "Caturday, 12-Nov-94 08:49:37 GMT", - "Saturday, 99-Nov-94 08:49:37 GMT", - "Saturday, 12-Nov-2000 08:49:37 GMT", - "Saturday, 12-Mop-94 08:49:37 GMT", - "Saturday, 12-Nov-94 28:49:37 GMT", - "Saturday, 12-Nov-94 08:99:37 GMT", - "Saturday, 12-Nov-94 08:49:99 GMT", - ]; - - for val in &test_dates_fail { - assert!(NaiveDateTime::parse_from_str(val, RFC850_FMT).is_err()); - } - } - - #[test] - fn test_rfc3339() { - let ymd_hmsn = |y, m, d, h, n, s, nano, off| { - FixedOffset::east_opt(off * 60 * 60) - .unwrap() - .with_ymd_and_hms(y, m, d, h, n, s) - .unwrap() - .with_nanosecond(nano) - .unwrap() - }; - - // Test data - (input, Ok(expected result) or Err(error code)) - let testdates = [ - ("2015-01-20T17:35:20-08:00", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8))), // normal case - ("2015-01-20T17:35:20−08:00", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8))), // normal case with MINUS SIGN (U+2212) - ("1944-06-06T04:04:00Z", Ok(ymd_hmsn(1944, 6, 6, 4, 4, 0, 0, 0))), // D-day - ("2001-09-11T09:45:00-08:00", Ok(ymd_hmsn(2001, 9, 11, 9, 45, 0, 0, -8))), - ("2015-01-20T17:35:20.001-08:00", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 1_000_000, -8))), - ("2015-01-20T17:35:20.001−08:00", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 1_000_000, -8))), // with MINUS SIGN (U+2212) - ("2015-01-20T17:35:20.000031-08:00", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 31_000, -8))), - ("2015-01-20T17:35:20.000000004-08:00", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 4, -8))), - ("2015-01-20T17:35:20.000000004−08:00", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 4, -8))), // with MINUS SIGN (U+2212) - ( - "2015-01-20T17:35:20.000000000452-08:00", - Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8)), - ), // too small - ( - "2015-01-20T17:35:20.000000000452−08:00", - Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8)), - ), // too small with MINUS SIGN (U+2212) - ("2023-11-05T01:30:00-04:00", Ok(ymd_hmsn(2023, 11, 5, 1, 30, 0, 0, -4))), // ambiguous timestamp - ("2015-01-20 17:35:20-08:00", Ok(ymd_hmsn(2015, 1, 20, 17, 35, 20, 0, -8))), // without 'T' - ("2015-01-20_17:35:20-08:00", Err(INVALID)), // wrong date time separator - ("2015/01/20T17:35:20.001-08:00", Err(INVALID)), // wrong separator char YM - ("2015-01/20T17:35:20.001-08:00", Err(INVALID)), // wrong separator char MD - ("2015-01-20T17-35-20.001-08:00", Err(INVALID)), // wrong separator char HM - ("2015-01-20T17-35:20.001-08:00", Err(INVALID)), // wrong separator char MS - ("-01-20T17:35:20-08:00", Err(INVALID)), // missing year - ("99-01-20T17:35:20-08:00", Err(INVALID)), // bad year format - ("99999-01-20T17:35:20-08:00", Err(INVALID)), // bad year value - ("-2000-01-20T17:35:20-08:00", Err(INVALID)), // bad year value - ("2015-00-30T17:35:20-08:00", Err(OUT_OF_RANGE)), // bad month value - ("2015-02-30T17:35:20-08:00", Err(OUT_OF_RANGE)), // bad day of month value - ("2015-01-20T25:35:20-08:00", Err(OUT_OF_RANGE)), // bad hour value - ("2015-01-20T17:65:20-08:00", Err(OUT_OF_RANGE)), // bad minute value - ("2015-01-20T17:35:90-08:00", Err(OUT_OF_RANGE)), // bad second value - ("2015-01-20T17:35:20-24:00", Err(OUT_OF_RANGE)), // bad offset value - ("15-01-20T17:35:20-08:00", Err(INVALID)), // bad year format - ("15-01-20T17:35:20-08:00:00", Err(INVALID)), // bad year format, bad offset format - ("2015-01-20T17:35:2008:00", Err(INVALID)), // missing offset sign - ("2015-01-20T17:35:20 08:00", Err(INVALID)), // missing offset sign - ("2015-01-20T17:35:20Zulu", Err(TOO_LONG)), // bad offset format - ("2015-01-20T17:35:20 Zulu", Err(INVALID)), // bad offset format - ("2015-01-20T17:35:20GMT", Err(INVALID)), // bad offset format - ("2015-01-20T17:35:20 GMT", Err(INVALID)), // bad offset format - ("2015-01-20T17:35:20+GMT", Err(INVALID)), // bad offset format - ("2015-01-20T17:35:20++08:00", Err(INVALID)), // bad offset format - ("2015-01-20T17:35:20--08:00", Err(INVALID)), // bad offset format - ("2015-01-20T17:35:20−−08:00", Err(INVALID)), // bad offset format with MINUS SIGN (U+2212) - ("2015-01-20T17:35:20±08:00", Err(INVALID)), // bad offset sign - ("2015-01-20T17:35:20-08-00", Err(INVALID)), // bad offset separator - ("2015-01-20T17:35:20-08;00", Err(INVALID)), // bad offset separator - ("2015-01-20T17:35:20-0800", Err(INVALID)), // bad offset separator - ("2015-01-20T17:35:20-08:0", Err(TOO_SHORT)), // bad offset minutes - ("2015-01-20T17:35:20-08:AA", Err(INVALID)), // bad offset minutes - ("2015-01-20T17:35:20-08:ZZ", Err(INVALID)), // bad offset minutes - ("2015-01-20T17:35:20.001-08 : 00", Err(INVALID)), // bad offset separator - ("2015-01-20T17:35:20-08:00:00", Err(TOO_LONG)), // bad offset format - ("2015-01-20T17:35:20+08:", Err(TOO_SHORT)), // bad offset format - ("2015-01-20T17:35:20-08:", Err(TOO_SHORT)), // bad offset format - ("2015-01-20T17:35:20−08:", Err(TOO_SHORT)), // bad offset format with MINUS SIGN (U+2212) - ("2015-01-20T17:35:20-08", Err(TOO_SHORT)), // bad offset format - ("2015-01-20T", Err(TOO_SHORT)), // missing HMS - ("2015-01-20T00:00:1", Err(TOO_SHORT)), // missing complete S - ("2015-01-20T00:00:1-08:00", Err(INVALID)), // missing complete S - ]; - - // Test against test data above - for &(date, checkdate) in testdates.iter() { - let dt = DateTime::::parse_from_rfc3339(date); - if dt != checkdate { - // check for expected result - panic!( - "Date conversion failed for {date}\nReceived: {dt:?}\nExpected: {checkdate:?}" - ); - } - } - } - - #[test] - fn test_issue_1010() { - let dt = crate::NaiveDateTime::parse_from_str( - "\u{c}SUN\u{e}\u{3000}\0m@J\u{3000}\0\u{3000}\0m\u{c}!\u{c}\u{b}\u{c}\u{c}\u{c}\u{c}%A\u{c}\u{b}\0SU\u{c}\u{c}", - "\u{c}\u{c}%A\u{c}\u{b}\0SUN\u{c}\u{c}\u{c}SUNN\u{c}\u{c}\u{c}SUN\u{c}\u{c}!\u{c}\u{b}\u{c}\u{c}\u{c}\u{c}%A\u{c}\u{b}%a", - ); - assert_eq!(dt, Err(ParseError(ParseErrorKind::Invalid))); - } -} diff --git a/chrono-0.4.44/src/format/parsed.rs b/chrono-0.4.44/src/format/parsed.rs deleted file mode 100644 index 3746ba4d97..0000000000 --- a/chrono-0.4.44/src/format/parsed.rs +++ /dev/null @@ -1,1913 +0,0 @@ -// This is a part of Chrono. -// See README.md and LICENSE.txt for details. - -//! A collection of parsed date and time items. -//! They can be constructed incrementally while being checked for consistency. - -use super::{IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE, ParseResult}; -use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime}; -use crate::offset::{FixedOffset, MappedLocalTime, Offset, TimeZone}; -use crate::{DateTime, Datelike, TimeDelta, Timelike, Weekday}; - -/// A type to hold parsed fields of date and time that can check all fields are consistent. -/// -/// There are three classes of methods: -/// -/// - `set_*` methods to set fields you have available. They do a basic range check, and if the -/// same field is set more than once it is checked for consistency. -/// -/// - `to_*` methods try to make a concrete date and time value out of set fields. -/// They fully check that all fields are consistent and whether the date/datetime exists. -/// -/// - Methods to inspect the parsed fields. -/// -/// `Parsed` is used internally by all parsing functions in chrono. It is a public type so that it -/// can be used to write custom parsers that reuse the resolving algorithm, or to inspect the -/// results of a string parsed with chrono without converting it to concrete types. -/// -/// # Resolving algorithm -/// -/// Resolving date/time parts is littered with lots of corner cases, which is why common date/time -/// parsers do not implement it correctly. -/// -/// Chrono provides a complete resolution algorithm that checks all fields for consistency via the -/// `Parsed` type. -/// -/// As an easy example, consider RFC 2822. The [RFC 2822 date and time format] has a day of the week -/// part, which should be consistent with the other date parts. But a `strptime`-based parse would -/// happily accept inconsistent input: -/// -/// ```python -/// >>> import time -/// >>> time.strptime('Wed, 31 Dec 2014 04:26:40 +0000', -/// '%a, %d %b %Y %H:%M:%S +0000') -/// time.struct_time(tm_year=2014, tm_mon=12, tm_mday=31, -/// tm_hour=4, tm_min=26, tm_sec=40, -/// tm_wday=2, tm_yday=365, tm_isdst=-1) -/// >>> time.strptime('Thu, 31 Dec 2014 04:26:40 +0000', -/// '%a, %d %b %Y %H:%M:%S +0000') -/// time.struct_time(tm_year=2014, tm_mon=12, tm_mday=31, -/// tm_hour=4, tm_min=26, tm_sec=40, -/// tm_wday=3, tm_yday=365, tm_isdst=-1) -/// ``` -/// -/// [RFC 2822 date and time format]: https://tools.ietf.org/html/rfc2822#section-3.3 -/// -/// # Example -/// -/// Let's see how `Parsed` correctly detects the second RFC 2822 string from before is inconsistent. -/// -/// ``` -/// # #[cfg(feature = "alloc")] { -/// use chrono::format::{ParseErrorKind, Parsed}; -/// use chrono::Weekday; -/// -/// let mut parsed = Parsed::new(); -/// parsed.set_weekday(Weekday::Wed)?; -/// parsed.set_day(31)?; -/// parsed.set_month(12)?; -/// parsed.set_year(2014)?; -/// parsed.set_hour(4)?; -/// parsed.set_minute(26)?; -/// parsed.set_second(40)?; -/// parsed.set_offset(0)?; -/// let dt = parsed.to_datetime()?; -/// assert_eq!(dt.to_rfc2822(), "Wed, 31 Dec 2014 04:26:40 +0000"); -/// -/// let mut parsed = Parsed::new(); -/// parsed.set_weekday(Weekday::Thu)?; // changed to the wrong day -/// parsed.set_day(31)?; -/// parsed.set_month(12)?; -/// parsed.set_year(2014)?; -/// parsed.set_hour(4)?; -/// parsed.set_minute(26)?; -/// parsed.set_second(40)?; -/// parsed.set_offset(0)?; -/// let result = parsed.to_datetime(); -/// -/// assert!(result.is_err()); -/// if let Err(error) = result { -/// assert_eq!(error.kind(), ParseErrorKind::Impossible); -/// } -/// # } -/// # Ok::<(), chrono::ParseError>(()) -/// ``` -/// -/// The same using chrono's built-in parser for RFC 2822 (the [RFC2822 formatting item]) and -/// [`format::parse()`] showing how to inspect a field on failure. -/// -/// [RFC2822 formatting item]: crate::format::Fixed::RFC2822 -/// [`format::parse()`]: crate::format::parse() -/// -/// ``` -/// # #[cfg(feature = "alloc")] { -/// use chrono::format::{parse, Fixed, Item, Parsed}; -/// use chrono::Weekday; -/// -/// let rfc_2822 = [Item::Fixed(Fixed::RFC2822)]; -/// -/// let mut parsed = Parsed::new(); -/// parse(&mut parsed, "Wed, 31 Dec 2014 04:26:40 +0000", rfc_2822.iter())?; -/// let dt = parsed.to_datetime()?; -/// -/// assert_eq!(dt.to_rfc2822(), "Wed, 31 Dec 2014 04:26:40 +0000"); -/// -/// let mut parsed = Parsed::new(); -/// parse(&mut parsed, "Thu, 31 Dec 2014 04:26:40 +0000", rfc_2822.iter())?; -/// let result = parsed.to_datetime(); -/// -/// assert!(result.is_err()); -/// if result.is_err() { -/// // What is the weekday? -/// assert_eq!(parsed.weekday(), Some(Weekday::Thu)); -/// } -/// # } -/// # Ok::<(), chrono::ParseError>(()) -/// ``` -#[allow(clippy::manual_non_exhaustive)] -#[derive(Clone, PartialEq, Eq, Debug, Default, Hash)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Parsed { - #[doc(hidden)] - pub year: Option, - #[doc(hidden)] - pub year_div_100: Option, - #[doc(hidden)] - pub year_mod_100: Option, - #[doc(hidden)] - pub isoyear: Option, - #[doc(hidden)] - pub isoyear_div_100: Option, - #[doc(hidden)] - pub isoyear_mod_100: Option, - #[doc(hidden)] - pub quarter: Option, - #[doc(hidden)] - pub month: Option, - #[doc(hidden)] - pub week_from_sun: Option, - #[doc(hidden)] - pub week_from_mon: Option, - #[doc(hidden)] - pub isoweek: Option, - #[doc(hidden)] - pub weekday: Option, - #[doc(hidden)] - pub ordinal: Option, - #[doc(hidden)] - pub day: Option, - #[doc(hidden)] - pub hour_div_12: Option, - #[doc(hidden)] - pub hour_mod_12: Option, - #[doc(hidden)] - pub minute: Option, - #[doc(hidden)] - pub second: Option, - #[doc(hidden)] - pub nanosecond: Option, - #[doc(hidden)] - pub timestamp: Option, - #[doc(hidden)] - pub offset: Option, - #[doc(hidden)] - _dummy: (), -} - -/// Checks if `old` is either empty or has the same value as `new` (i.e. "consistent"), -/// and if it is empty, set `old` to `new` as well. -#[inline] -fn set_if_consistent(old: &mut Option, new: T) -> ParseResult<()> { - match old { - Some(old) if *old != new => Err(IMPOSSIBLE), - _ => { - *old = Some(new); - Ok(()) - } - } -} - -impl Parsed { - /// Returns the initial value of parsed parts. - #[must_use] - pub fn new() -> Parsed { - Parsed::default() - } - - /// Set the [`year`](Parsed::year) field to the given value. - /// - /// The value can be negative, unlike the [`year_div_100`](Parsed::year_div_100) and - /// [`year_mod_100`](Parsed::year_mod_100) fields. - /// - /// # Errors - /// - /// Returns `OUT_OF_RANGE` if `value` is outside the range of an `i32`. - /// - /// Returns `IMPOSSIBLE` if this field was already set to a different value. - #[inline] - pub fn set_year(&mut self, value: i64) -> ParseResult<()> { - set_if_consistent(&mut self.year, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?) - } - - /// Set the [`year_div_100`](Parsed::year_div_100) field to the given value. - /// - /// # Errors - /// - /// Returns `OUT_OF_RANGE` if `value` is negative or if it is greater than `i32::MAX`. - /// - /// Returns `IMPOSSIBLE` if this field was already set to a different value. - #[inline] - pub fn set_year_div_100(&mut self, value: i64) -> ParseResult<()> { - if !(0..=i32::MAX as i64).contains(&value) { - return Err(OUT_OF_RANGE); - } - set_if_consistent(&mut self.year_div_100, value as i32) - } - - /// Set the [`year_mod_100`](Parsed::year_mod_100) field to the given value. - /// - /// When set it implies that the year is not negative. - /// - /// If this field is set while the [`year_div_100`](Parsed::year_div_100) field is missing (and - /// the full [`year`](Parsed::year) field is also not set), it assumes a default value for the - /// [`year_div_100`](Parsed::year_div_100) field. - /// The default is 19 when `year_mod_100 >= 70` and 20 otherwise. - /// - /// # Errors - /// - /// Returns `OUT_OF_RANGE` if `value` is negative or if it is greater than 99. - /// - /// Returns `IMPOSSIBLE` if this field was already set to a different value. - #[inline] - pub fn set_year_mod_100(&mut self, value: i64) -> ParseResult<()> { - if !(0..100).contains(&value) { - return Err(OUT_OF_RANGE); - } - set_if_consistent(&mut self.year_mod_100, value as i32) - } - - /// Set the [`isoyear`](Parsed::isoyear) field, that is part of an [ISO 8601 week date], to the - /// given value. - /// - /// The value can be negative, unlike the [`isoyear_div_100`](Parsed::isoyear_div_100) and - /// [`isoyear_mod_100`](Parsed::isoyear_mod_100) fields. - /// - /// [ISO 8601 week date]: crate::NaiveDate#week-date - /// - /// # Errors - /// - /// Returns `OUT_OF_RANGE` if `value` is outside the range of an `i32`. - /// - /// Returns `IMPOSSIBLE` if this field was already set to a different value. - #[inline] - pub fn set_isoyear(&mut self, value: i64) -> ParseResult<()> { - set_if_consistent(&mut self.isoyear, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?) - } - - /// Set the [`isoyear_div_100`](Parsed::isoyear_div_100) field, that is part of an - /// [ISO 8601 week date], to the given value. - /// - /// [ISO 8601 week date]: crate::NaiveDate#week-date - /// - /// # Errors - /// - /// Returns `OUT_OF_RANGE` if `value` is negative or if it is greater than `i32::MAX`. - /// - /// Returns `IMPOSSIBLE` if this field was already set to a different value. - #[inline] - pub fn set_isoyear_div_100(&mut self, value: i64) -> ParseResult<()> { - if !(0..=i32::MAX as i64).contains(&value) { - return Err(OUT_OF_RANGE); - } - set_if_consistent(&mut self.isoyear_div_100, value as i32) - } - - /// Set the [`isoyear_mod_100`](Parsed::isoyear_mod_100) field, that is part of an - /// [ISO 8601 week date], to the given value. - /// - /// When set it implies that the year is not negative. - /// - /// If this field is set while the [`isoyear_div_100`](Parsed::isoyear_div_100) field is missing - /// (and the full [`isoyear`](Parsed::isoyear) field is also not set), it assumes a default - /// value for the [`isoyear_div_100`](Parsed::isoyear_div_100) field. - /// The default is 19 when `year_mod_100 >= 70` and 20 otherwise. - /// - /// [ISO 8601 week date]: crate::NaiveDate#week-date - /// - /// # Errors - /// - /// Returns `OUT_OF_RANGE` if `value` is negative or if it is greater than 99. - /// - /// Returns `IMPOSSIBLE` if this field was already set to a different value. - #[inline] - pub fn set_isoyear_mod_100(&mut self, value: i64) -> ParseResult<()> { - if !(0..100).contains(&value) { - return Err(OUT_OF_RANGE); - } - set_if_consistent(&mut self.isoyear_mod_100, value as i32) - } - - /// Set the [`quarter`](Parsed::quarter) field to the given value. - /// - /// Quarter 1 starts in January. - /// - /// # Errors - /// - /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-4. - /// - /// Returns `IMPOSSIBLE` if this field was already set to a different value. - #[inline] - pub fn set_quarter(&mut self, value: i64) -> ParseResult<()> { - if !(1..=4).contains(&value) { - return Err(OUT_OF_RANGE); - } - set_if_consistent(&mut self.quarter, value as u32) - } - - /// Set the [`month`](Parsed::month) field to the given value. - /// - /// # Errors - /// - /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-12. - /// - /// Returns `IMPOSSIBLE` if this field was already set to a different value. - #[inline] - pub fn set_month(&mut self, value: i64) -> ParseResult<()> { - if !(1..=12).contains(&value) { - return Err(OUT_OF_RANGE); - } - set_if_consistent(&mut self.month, value as u32) - } - - /// Set the [`week_from_sun`](Parsed::week_from_sun) week number field to the given value. - /// - /// Week 1 starts at the first Sunday of January. - /// - /// # Errors - /// - /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-53. - /// - /// Returns `IMPOSSIBLE` if this field was already set to a different value. - #[inline] - pub fn set_week_from_sun(&mut self, value: i64) -> ParseResult<()> { - if !(0..=53).contains(&value) { - return Err(OUT_OF_RANGE); - } - set_if_consistent(&mut self.week_from_sun, value as u32) - } - - /// Set the [`week_from_mon`](Parsed::week_from_mon) week number field to the given value. - /// Set the 'week number starting with Monday' field to the given value. - /// - /// Week 1 starts at the first Monday of January. - /// - /// # Errors - /// - /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-53. - /// - /// Returns `IMPOSSIBLE` if this field was already set to a different value. - #[inline] - pub fn set_week_from_mon(&mut self, value: i64) -> ParseResult<()> { - if !(0..=53).contains(&value) { - return Err(OUT_OF_RANGE); - } - set_if_consistent(&mut self.week_from_mon, value as u32) - } - - /// Set the [`isoweek`](Parsed::isoweek) field for an [ISO 8601 week date] to the given value. - /// - /// [ISO 8601 week date]: crate::NaiveDate#week-date - /// - /// # Errors - /// - /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-53. - /// - /// Returns `IMPOSSIBLE` if this field was already set to a different value. - #[inline] - pub fn set_isoweek(&mut self, value: i64) -> ParseResult<()> { - if !(1..=53).contains(&value) { - return Err(OUT_OF_RANGE); - } - set_if_consistent(&mut self.isoweek, value as u32) - } - - /// Set the [`weekday`](Parsed::weekday) field to the given value. - /// - /// # Errors - /// - /// Returns `IMPOSSIBLE` if this field was already set to a different value. - #[inline] - pub fn set_weekday(&mut self, value: Weekday) -> ParseResult<()> { - set_if_consistent(&mut self.weekday, value) - } - - /// Set the [`ordinal`](Parsed::ordinal) (day of the year) field to the given value. - /// - /// # Errors - /// - /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-366. - /// - /// Returns `IMPOSSIBLE` if this field was already set to a different value. - #[inline] - pub fn set_ordinal(&mut self, value: i64) -> ParseResult<()> { - if !(1..=366).contains(&value) { - return Err(OUT_OF_RANGE); - } - set_if_consistent(&mut self.ordinal, value as u32) - } - - /// Set the [`day`](Parsed::day) of the month field to the given value. - /// - /// # Errors - /// - /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-31. - /// - /// Returns `IMPOSSIBLE` if this field was already set to a different value. - #[inline] - pub fn set_day(&mut self, value: i64) -> ParseResult<()> { - if !(1..=31).contains(&value) { - return Err(OUT_OF_RANGE); - } - set_if_consistent(&mut self.day, value as u32) - } - - /// Set the [`hour_div_12`](Parsed::hour_div_12) am/pm field to the given value. - /// - /// `false` indicates AM and `true` indicates PM. - /// - /// # Errors - /// - /// Returns `IMPOSSIBLE` if this field was already set to a different value. - #[inline] - pub fn set_ampm(&mut self, value: bool) -> ParseResult<()> { - set_if_consistent(&mut self.hour_div_12, value as u32) - } - - /// Set the [`hour_mod_12`](Parsed::hour_mod_12) field, for the hour number in 12-hour clocks, - /// to the given value. - /// - /// Value must be in the canonical range of 1-12. - /// It will internally be stored as 0-11 (`value % 12`). - /// - /// # Errors - /// - /// Returns `OUT_OF_RANGE` if `value` is not in the range 1-12. - /// - /// Returns `IMPOSSIBLE` if this field was already set to a different value. - #[inline] - pub fn set_hour12(&mut self, mut value: i64) -> ParseResult<()> { - if !(1..=12).contains(&value) { - return Err(OUT_OF_RANGE); - } - if value == 12 { - value = 0 - } - set_if_consistent(&mut self.hour_mod_12, value as u32) - } - - /// Set the [`hour_div_12`](Parsed::hour_div_12) and [`hour_mod_12`](Parsed::hour_mod_12) - /// fields to the given value for a 24-hour clock. - /// - /// # Errors - /// - /// May return `OUT_OF_RANGE` if `value` is not in the range 0-23. - /// Currently only checks the value is not out of range for a `u32`. - /// - /// Returns `IMPOSSIBLE` one of the fields was already set to a different value. - #[inline] - pub fn set_hour(&mut self, value: i64) -> ParseResult<()> { - let (hour_div_12, hour_mod_12) = match value { - hour @ 0..=11 => (0, hour as u32), - hour @ 12..=23 => (1, hour as u32 - 12), - _ => return Err(OUT_OF_RANGE), - }; - set_if_consistent(&mut self.hour_div_12, hour_div_12)?; - set_if_consistent(&mut self.hour_mod_12, hour_mod_12) - } - - /// Set the [`minute`](Parsed::minute) field to the given value. - /// - /// # Errors - /// - /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-59. - /// - /// Returns `IMPOSSIBLE` if this field was already set to a different value. - #[inline] - pub fn set_minute(&mut self, value: i64) -> ParseResult<()> { - if !(0..=59).contains(&value) { - return Err(OUT_OF_RANGE); - } - set_if_consistent(&mut self.minute, value as u32) - } - - /// Set the [`second`](Parsed::second) field to the given value. - /// - /// The value can be 60 in the case of a leap second. - /// - /// # Errors - /// - /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-60. - /// - /// Returns `IMPOSSIBLE` if this field was already set to a different value. - #[inline] - pub fn set_second(&mut self, value: i64) -> ParseResult<()> { - if !(0..=60).contains(&value) { - return Err(OUT_OF_RANGE); - } - set_if_consistent(&mut self.second, value as u32) - } - - /// Set the [`nanosecond`](Parsed::nanosecond) field to the given value. - /// - /// This is the number of nanoseconds since the whole second. - /// - /// # Errors - /// - /// Returns `OUT_OF_RANGE` if `value` is not in the range 0-999,999,999. - /// - /// Returns `IMPOSSIBLE` if this field was already set to a different value. - #[inline] - pub fn set_nanosecond(&mut self, value: i64) -> ParseResult<()> { - if !(0..=999_999_999).contains(&value) { - return Err(OUT_OF_RANGE); - } - set_if_consistent(&mut self.nanosecond, value as u32) - } - - /// Set the [`timestamp`](Parsed::timestamp) field to the given value. - /// - /// A Unix timestamp is defined as the number of non-leap seconds since midnight UTC on - /// January 1, 1970. - /// - /// # Errors - /// - /// Returns `IMPOSSIBLE` if this field was already set to a different value. - #[inline] - pub fn set_timestamp(&mut self, value: i64) -> ParseResult<()> { - set_if_consistent(&mut self.timestamp, value) - } - - /// Set the [`offset`](Parsed::offset) field to the given value. - /// - /// The offset is in seconds from local time to UTC. - /// - /// # Errors - /// - /// Returns `OUT_OF_RANGE` if `value` is outside the range of an `i32`. - /// - /// Returns `IMPOSSIBLE` if this field was already set to a different value. - #[inline] - pub fn set_offset(&mut self, value: i64) -> ParseResult<()> { - set_if_consistent(&mut self.offset, i32::try_from(value).map_err(|_| OUT_OF_RANGE)?) - } - - /// Returns a parsed naive date out of given fields. - /// - /// This method is able to determine the date from given subset of fields: - /// - /// - Year, month, day. - /// - Year, day of the year (ordinal). - /// - Year, week number counted from Sunday or Monday, day of the week. - /// - ISO week date. - /// - /// Gregorian year and ISO week date year can have their century number (`*_div_100`) omitted, - /// the two-digit year is used to guess the century number then. - /// - /// It checks all given date fields are consistent with each other. - /// - /// # Errors - /// - /// This method returns: - /// - `IMPOSSIBLE` if any of the date fields conflict. - /// - `NOT_ENOUGH` if there are not enough fields set in `Parsed` for a complete date. - /// - `OUT_OF_RANGE` - /// - if any of the date fields of `Parsed` are set to a value beyond their acceptable range. - /// - if the value would be outside the range of a [`NaiveDate`]. - /// - if the date does not exist. - pub fn to_naive_date(&self) -> ParseResult { - fn resolve_year( - y: Option, - q: Option, - r: Option, - ) -> ParseResult> { - match (y, q, r) { - // if there is no further information, simply return the given full year. - // this is a common case, so let's avoid division here. - (y, None, None) => Ok(y), - - // if there is a full year *and* also quotient and/or modulo, - // check if present quotient and/or modulo is consistent to the full year. - // since the presence of those fields means a positive full year, - // we should filter a negative full year first. - (Some(y), q, r @ Some(0..=99)) | (Some(y), q, r @ None) => { - if y < 0 { - return Err(IMPOSSIBLE); - } - let q_ = y / 100; - let r_ = y % 100; - if q.unwrap_or(q_) == q_ && r.unwrap_or(r_) == r_ { - Ok(Some(y)) - } else { - Err(IMPOSSIBLE) - } - } - - // the full year is missing but we have quotient and modulo. - // reconstruct the full year. make sure that the result is always positive. - (None, Some(q), Some(r @ 0..=99)) => { - if q < 0 { - return Err(IMPOSSIBLE); - } - let y = q.checked_mul(100).and_then(|v| v.checked_add(r)); - Ok(Some(y.ok_or(OUT_OF_RANGE)?)) - } - - // we only have modulo. try to interpret a modulo as a conventional two-digit year. - // note: we are affected by Rust issue #18060. avoid multiple range patterns. - (None, None, Some(r @ 0..=99)) => Ok(Some(r + if r < 70 { 2000 } else { 1900 })), - - // otherwise it is an out-of-bound or insufficient condition. - (None, Some(_), None) => Err(NOT_ENOUGH), - (_, _, Some(_)) => Err(OUT_OF_RANGE), - } - } - - let given_year = resolve_year(self.year, self.year_div_100, self.year_mod_100)?; - let given_isoyear = resolve_year(self.isoyear, self.isoyear_div_100, self.isoyear_mod_100)?; - - // verify the normal year-month-day date. - let verify_ymd = |date: NaiveDate| { - let year = date.year(); - let (year_div_100, year_mod_100) = if year >= 0 { - (Some(year / 100), Some(year % 100)) - } else { - (None, None) // they should be empty to be consistent - }; - let month = date.month(); - let day = date.day(); - self.year.unwrap_or(year) == year - && self.year_div_100.or(year_div_100) == year_div_100 - && self.year_mod_100.or(year_mod_100) == year_mod_100 - && self.month.unwrap_or(month) == month - && self.day.unwrap_or(day) == day - }; - - // verify the ISO week date. - let verify_isoweekdate = |date: NaiveDate| { - let week = date.iso_week(); - let isoyear = week.year(); - let isoweek = week.week(); - let weekday = date.weekday(); - let (isoyear_div_100, isoyear_mod_100) = if isoyear >= 0 { - (Some(isoyear / 100), Some(isoyear % 100)) - } else { - (None, None) // they should be empty to be consistent - }; - self.isoyear.unwrap_or(isoyear) == isoyear - && self.isoyear_div_100.or(isoyear_div_100) == isoyear_div_100 - && self.isoyear_mod_100.or(isoyear_mod_100) == isoyear_mod_100 - && self.isoweek.unwrap_or(isoweek) == isoweek - && self.weekday.unwrap_or(weekday) == weekday - }; - - // verify the ordinal and other (non-ISO) week dates. - let verify_ordinal = |date: NaiveDate| { - let ordinal = date.ordinal(); - let week_from_sun = date.weeks_from(Weekday::Sun); - let week_from_mon = date.weeks_from(Weekday::Mon); - self.ordinal.unwrap_or(ordinal) == ordinal - && self.week_from_sun.map_or(week_from_sun, |v| v as i32) == week_from_sun - && self.week_from_mon.map_or(week_from_mon, |v| v as i32) == week_from_mon - }; - - // test several possibilities. - // tries to construct a full `NaiveDate` as much as possible, then verifies that - // it is consistent with other given fields. - let (verified, parsed_date) = match (given_year, given_isoyear, self) { - (Some(year), _, &Parsed { month: Some(month), day: Some(day), .. }) => { - // year, month, day - let date = NaiveDate::from_ymd_opt(year, month, day).ok_or(OUT_OF_RANGE)?; - (verify_isoweekdate(date) && verify_ordinal(date), date) - } - - (Some(year), _, &Parsed { ordinal: Some(ordinal), .. }) => { - // year, day of the year - let date = NaiveDate::from_yo_opt(year, ordinal).ok_or(OUT_OF_RANGE)?; - (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date) - } - - (Some(year), _, &Parsed { week_from_sun: Some(week), weekday: Some(weekday), .. }) => { - // year, week (starting at 1st Sunday), day of the week - let date = resolve_week_date(year, week, weekday, Weekday::Sun)?; - (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date) - } - - (Some(year), _, &Parsed { week_from_mon: Some(week), weekday: Some(weekday), .. }) => { - // year, week (starting at 1st Monday), day of the week - let date = resolve_week_date(year, week, weekday, Weekday::Mon)?; - (verify_ymd(date) && verify_isoweekdate(date) && verify_ordinal(date), date) - } - - (_, Some(isoyear), &Parsed { isoweek: Some(isoweek), weekday: Some(weekday), .. }) => { - // ISO year, week, day of the week - let date = NaiveDate::from_isoywd_opt(isoyear, isoweek, weekday); - let date = date.ok_or(OUT_OF_RANGE)?; - (verify_ymd(date) && verify_ordinal(date), date) - } - - (_, _, _) => return Err(NOT_ENOUGH), - }; - - if !verified { - return Err(IMPOSSIBLE); - } else if let Some(parsed) = self.quarter { - if parsed != parsed_date.quarter() { - return Err(IMPOSSIBLE); - } - } - - Ok(parsed_date) - } - - /// Returns a parsed naive time out of given fields. - /// - /// This method is able to determine the time from given subset of fields: - /// - /// - Hour, minute. (second and nanosecond assumed to be 0) - /// - Hour, minute, second. (nanosecond assumed to be 0) - /// - Hour, minute, second, nanosecond. - /// - /// It is able to handle leap seconds when given second is 60. - /// - /// # Errors - /// - /// This method returns: - /// - `OUT_OF_RANGE` if any of the time fields of `Parsed` are set to a value beyond - /// their acceptable range. - /// - `NOT_ENOUGH` if an hour field is missing, if AM/PM is missing in a 12-hour clock, - /// if minutes are missing, or if seconds are missing while the nanosecond field is present. - pub fn to_naive_time(&self) -> ParseResult { - let hour_div_12 = match self.hour_div_12 { - Some(v @ 0..=1) => v, - Some(_) => return Err(OUT_OF_RANGE), - None => return Err(NOT_ENOUGH), - }; - let hour_mod_12 = match self.hour_mod_12 { - Some(v @ 0..=11) => v, - Some(_) => return Err(OUT_OF_RANGE), - None => return Err(NOT_ENOUGH), - }; - let hour = hour_div_12 * 12 + hour_mod_12; - - let minute = match self.minute { - Some(v @ 0..=59) => v, - Some(_) => return Err(OUT_OF_RANGE), - None => return Err(NOT_ENOUGH), - }; - - // we allow omitting seconds or nanoseconds, but they should be in the range. - let (second, mut nano) = match self.second.unwrap_or(0) { - v @ 0..=59 => (v, 0), - 60 => (59, 1_000_000_000), - _ => return Err(OUT_OF_RANGE), - }; - nano += match self.nanosecond { - Some(v @ 0..=999_999_999) if self.second.is_some() => v, - Some(0..=999_999_999) => return Err(NOT_ENOUGH), // second is missing - Some(_) => return Err(OUT_OF_RANGE), - None => 0, - }; - - NaiveTime::from_hms_nano_opt(hour, minute, second, nano).ok_or(OUT_OF_RANGE) - } - - /// Returns a parsed naive date and time out of given fields, except for the offset field. - /// - /// The offset is assumed to have a given value. It is not compared against the offset field set - /// in the `Parsed` type, so it is allowed to be inconsistent. - /// - /// This method is able to determine the combined date and time from date and time fields or - /// from a single timestamp field. It checks all fields are consistent with each other. - /// - /// # Errors - /// - /// This method returns: - /// - `IMPOSSIBLE` if any of the date fields conflict, or if a timestamp conflicts with any of - /// the other fields. - /// - `NOT_ENOUGH` if there are not enough fields set in `Parsed` for a complete datetime. - /// - `OUT_OF_RANGE` - /// - if any of the date or time fields of `Parsed` are set to a value beyond their acceptable - /// range. - /// - if the value would be outside the range of a [`NaiveDateTime`]. - /// - if the date does not exist. - pub fn to_naive_datetime_with_offset(&self, offset: i32) -> ParseResult { - let date = self.to_naive_date(); - let time = self.to_naive_time(); - if let (Ok(date), Ok(time)) = (date, time) { - let datetime = date.and_time(time); - - // verify the timestamp field if any - // the following is safe, `timestamp` is very limited in range - let timestamp = datetime.and_utc().timestamp() - i64::from(offset); - if let Some(given_timestamp) = self.timestamp { - // if `datetime` represents a leap second, it might be off by one second. - if given_timestamp != timestamp - && !(datetime.nanosecond() >= 1_000_000_000 && given_timestamp == timestamp + 1) - { - return Err(IMPOSSIBLE); - } - } - - Ok(datetime) - } else if let Some(timestamp) = self.timestamp { - use super::ParseError as PE; - use super::ParseErrorKind::{Impossible, OutOfRange}; - - // if date and time is problematic already, there is no point proceeding. - // we at least try to give a correct error though. - match (date, time) { - (Err(PE(OutOfRange)), _) | (_, Err(PE(OutOfRange))) => return Err(OUT_OF_RANGE), - (Err(PE(Impossible)), _) | (_, Err(PE(Impossible))) => return Err(IMPOSSIBLE), - (_, _) => {} // one of them is insufficient - } - - // reconstruct date and time fields from timestamp - let ts = timestamp.checked_add(i64::from(offset)).ok_or(OUT_OF_RANGE)?; - let mut datetime = DateTime::from_timestamp_secs(ts).ok_or(OUT_OF_RANGE)?.naive_utc(); - - // fill year, ordinal, hour, minute and second fields from timestamp. - // if existing fields are consistent, this will allow the full date/time reconstruction. - let mut parsed = self.clone(); - if parsed.second == Some(60) { - // `datetime.second()` cannot be 60, so this is the only case for a leap second. - match datetime.second() { - // it's okay, just do not try to overwrite the existing field. - 59 => {} - // `datetime` is known to be off by one second. - 0 => { - datetime -= TimeDelta::try_seconds(1).unwrap(); - } - // otherwise it is impossible. - _ => return Err(IMPOSSIBLE), - } - // ...and we have the correct candidates for other fields. - } else { - parsed.set_second(i64::from(datetime.second()))?; - } - parsed.set_year(i64::from(datetime.year()))?; - parsed.set_ordinal(i64::from(datetime.ordinal()))?; // more efficient than ymd - parsed.set_hour(i64::from(datetime.hour()))?; - parsed.set_minute(i64::from(datetime.minute()))?; - - // validate other fields (e.g. week) and return - let date = parsed.to_naive_date()?; - let time = parsed.to_naive_time()?; - Ok(date.and_time(time)) - } else { - // reproduce the previous error(s) - date?; - time?; - unreachable!() - } - } - - /// Returns a parsed fixed time zone offset out of given fields. - /// - /// # Errors - /// - /// This method returns: - /// - `OUT_OF_RANGE` if the offset is out of range for a `FixedOffset`. - /// - `NOT_ENOUGH` if the offset field is not set. - pub fn to_fixed_offset(&self) -> ParseResult { - FixedOffset::east_opt(self.offset.ok_or(NOT_ENOUGH)?).ok_or(OUT_OF_RANGE) - } - - /// Returns a parsed timezone-aware date and time out of given fields. - /// - /// This method is able to determine the combined date and time from date, time and offset - /// fields, and/or from a single timestamp field. It checks all fields are consistent with each - /// other. - /// - /// # Errors - /// - /// This method returns: - /// - `IMPOSSIBLE` if any of the date fields conflict, or if a timestamp conflicts with any of - /// the other fields. - /// - `NOT_ENOUGH` if there are not enough fields set in `Parsed` for a complete datetime - /// including offset from UTC. - /// - `OUT_OF_RANGE` - /// - if any of the fields of `Parsed` are set to a value beyond their acceptable - /// range. - /// - if the value would be outside the range of a [`NaiveDateTime`] or [`FixedOffset`]. - /// - if the date does not exist. - pub fn to_datetime(&self) -> ParseResult> { - // If there is no explicit offset, consider a timestamp value as indication of a UTC value. - let offset = match (self.offset, self.timestamp) { - (Some(off), _) => off, - (None, Some(_)) => 0, // UNIX timestamp may assume 0 offset - (None, None) => return Err(NOT_ENOUGH), - }; - let datetime = self.to_naive_datetime_with_offset(offset)?; - let offset = FixedOffset::east_opt(offset).ok_or(OUT_OF_RANGE)?; - - match offset.from_local_datetime(&datetime) { - MappedLocalTime::None => Err(IMPOSSIBLE), - MappedLocalTime::Single(t) => Ok(t), - MappedLocalTime::Ambiguous(..) => Err(NOT_ENOUGH), - } - } - - /// Returns a parsed timezone-aware date and time out of given fields, - /// with an additional [`TimeZone`] used to interpret and validate the local date. - /// - /// This method is able to determine the combined date and time from date and time, and/or from - /// a single timestamp field. It checks all fields are consistent with each other. - /// - /// If the parsed fields include an UTC offset, it also has to be consistent with the offset in - /// the provided `tz` time zone for that datetime. - /// - /// # Errors - /// - /// This method returns: - /// - `IMPOSSIBLE` - /// - if any of the date fields conflict, if a timestamp conflicts with any of the other - /// fields, or if the offset field is set but differs from the offset at that time in the - /// `tz` time zone. - /// - if the local datetime does not exists in the provided time zone (because it falls in a - /// transition due to for example DST). - /// - `NOT_ENOUGH` if there are not enough fields set in `Parsed` for a complete datetime, or if - /// the local time in the provided time zone is ambiguous (because it falls in a transition - /// due to for example DST) while there is no offset field or timestamp field set. - /// - `OUT_OF_RANGE` - /// - if the value would be outside the range of a [`NaiveDateTime`] or [`FixedOffset`]. - /// - if any of the fields of `Parsed` are set to a value beyond their acceptable range. - /// - if the date does not exist. - pub fn to_datetime_with_timezone(&self, tz: &Tz) -> ParseResult> { - // if we have `timestamp` specified, guess an offset from that. - let mut guessed_offset = 0; - if let Some(timestamp) = self.timestamp { - // make a naive `DateTime` from given timestamp and (if any) nanosecond. - // an empty `nanosecond` is always equal to zero, so missing nanosecond is fine. - let nanosecond = self.nanosecond.unwrap_or(0); - let dt = - DateTime::from_timestamp(timestamp, nanosecond).ok_or(OUT_OF_RANGE)?.naive_utc(); - guessed_offset = tz.offset_from_utc_datetime(&dt).fix().local_minus_utc(); - } - - // checks if the given `DateTime` has a consistent `Offset` with given `self.offset`. - let check_offset = |dt: &DateTime| { - if let Some(offset) = self.offset { - dt.offset().fix().local_minus_utc() == offset - } else { - true - } - }; - - // `guessed_offset` should be correct when `self.timestamp` is given. - // it will be 0 otherwise, but this is fine as the algorithm ignores offset for that case. - let datetime = self.to_naive_datetime_with_offset(guessed_offset)?; - match tz.from_local_datetime(&datetime) { - MappedLocalTime::None => Err(IMPOSSIBLE), - MappedLocalTime::Single(t) => { - if check_offset(&t) { - Ok(t) - } else { - Err(IMPOSSIBLE) - } - } - MappedLocalTime::Ambiguous(min, max) => { - // try to disambiguate two possible local dates by offset. - match (check_offset(&min), check_offset(&max)) { - (false, false) => Err(IMPOSSIBLE), - (false, true) => Ok(max), - (true, false) => Ok(min), - (true, true) => Err(NOT_ENOUGH), - } - } - } - } - - /// Get the `year` field if set. - /// - /// See also [`set_year()`](Parsed::set_year). - #[inline] - pub fn year(&self) -> Option { - self.year - } - - /// Get the `year_div_100` field if set. - /// - /// See also [`set_year_div_100()`](Parsed::set_year_div_100). - #[inline] - pub fn year_div_100(&self) -> Option { - self.year_div_100 - } - - /// Get the `year_mod_100` field if set. - /// - /// See also [`set_year_mod_100()`](Parsed::set_year_mod_100). - #[inline] - pub fn year_mod_100(&self) -> Option { - self.year_mod_100 - } - - /// Get the `isoyear` field that is part of an [ISO 8601 week date] if set. - /// - /// See also [`set_isoyear()`](Parsed::set_isoyear). - /// - /// [ISO 8601 week date]: crate::NaiveDate#week-date - #[inline] - pub fn isoyear(&self) -> Option { - self.isoyear - } - - /// Get the `isoyear_div_100` field that is part of an [ISO 8601 week date] if set. - /// - /// See also [`set_isoyear_div_100()`](Parsed::set_isoyear_div_100). - /// - /// [ISO 8601 week date]: crate::NaiveDate#week-date - #[inline] - pub fn isoyear_div_100(&self) -> Option { - self.isoyear_div_100 - } - - /// Get the `isoyear_mod_100` field that is part of an [ISO 8601 week date] if set. - /// - /// See also [`set_isoyear_mod_100()`](Parsed::set_isoyear_mod_100). - /// - /// [ISO 8601 week date]: crate::NaiveDate#week-date - #[inline] - pub fn isoyear_mod_100(&self) -> Option { - self.isoyear_mod_100 - } - - /// Get the `quarter` field if set. - /// - /// See also [`set_quarter()`](Parsed::set_quarter). - #[inline] - pub fn quarter(&self) -> Option { - self.quarter - } - - /// Get the `month` field if set. - /// - /// See also [`set_month()`](Parsed::set_month). - #[inline] - pub fn month(&self) -> Option { - self.month - } - - /// Get the `week_from_sun` field if set. - /// - /// See also [`set_week_from_sun()`](Parsed::set_week_from_sun). - #[inline] - pub fn week_from_sun(&self) -> Option { - self.week_from_sun - } - - /// Get the `week_from_mon` field if set. - /// - /// See also [`set_week_from_mon()`](Parsed::set_week_from_mon). - #[inline] - pub fn week_from_mon(&self) -> Option { - self.week_from_mon - } - - /// Get the `isoweek` field that is part of an [ISO 8601 week date] if set. - /// - /// See also [`set_isoweek()`](Parsed::set_isoweek). - /// - /// [ISO 8601 week date]: crate::NaiveDate#week-date - #[inline] - pub fn isoweek(&self) -> Option { - self.isoweek - } - - /// Get the `weekday` field if set. - /// - /// See also [`set_weekday()`](Parsed::set_weekday). - #[inline] - pub fn weekday(&self) -> Option { - self.weekday - } - - /// Get the `ordinal` (day of the year) field if set. - /// - /// See also [`set_ordinal()`](Parsed::set_ordinal). - #[inline] - pub fn ordinal(&self) -> Option { - self.ordinal - } - - /// Get the `day` of the month field if set. - /// - /// See also [`set_day()`](Parsed::set_day). - #[inline] - pub fn day(&self) -> Option { - self.day - } - - /// Get the `hour_div_12` field (am/pm) if set. - /// - /// 0 indicates AM and 1 indicates PM. - /// - /// See also [`set_ampm()`](Parsed::set_ampm) and [`set_hour()`](Parsed::set_hour). - #[inline] - pub fn hour_div_12(&self) -> Option { - self.hour_div_12 - } - - /// Get the `hour_mod_12` field if set. - /// - /// See also [`set_hour12()`](Parsed::set_hour12) and [`set_hour()`](Parsed::set_hour). - pub fn hour_mod_12(&self) -> Option { - self.hour_mod_12 - } - - /// Get the `minute` field if set. - /// - /// See also [`set_minute()`](Parsed::set_minute). - #[inline] - pub fn minute(&self) -> Option { - self.minute - } - - /// Get the `second` field if set. - /// - /// See also [`set_second()`](Parsed::set_second). - #[inline] - pub fn second(&self) -> Option { - self.second - } - - /// Get the `nanosecond` field if set. - /// - /// See also [`set_nanosecond()`](Parsed::set_nanosecond). - #[inline] - pub fn nanosecond(&self) -> Option { - self.nanosecond - } - - /// Get the `timestamp` field if set. - /// - /// See also [`set_timestamp()`](Parsed::set_timestamp). - #[inline] - pub fn timestamp(&self) -> Option { - self.timestamp - } - - /// Get the `offset` field if set. - /// - /// See also [`set_offset()`](Parsed::set_offset). - #[inline] - pub fn offset(&self) -> Option { - self.offset - } -} - -/// Create a `NaiveDate` when given a year, week, weekday, and the definition at which day of the -/// week a week starts. -/// -/// Returns `IMPOSSIBLE` if `week` is `0` or `53` and the `weekday` falls outside the year. -fn resolve_week_date( - year: i32, - week: u32, - weekday: Weekday, - week_start_day: Weekday, -) -> ParseResult { - if week > 53 { - return Err(OUT_OF_RANGE); - } - - let first_day_of_year = NaiveDate::from_yo_opt(year, 1).ok_or(OUT_OF_RANGE)?; - // Ordinal of the day at which week 1 starts. - let first_week_start = 1 + week_start_day.days_since(first_day_of_year.weekday()) as i32; - // Number of the `weekday`, which is 0 for the first day of the week. - let weekday = weekday.days_since(week_start_day) as i32; - let ordinal = first_week_start + (week as i32 - 1) * 7 + weekday; - if ordinal <= 0 { - return Err(IMPOSSIBLE); - } - first_day_of_year.with_ordinal(ordinal as u32).ok_or(IMPOSSIBLE) -} - -#[cfg(test)] -mod tests { - use super::super::{IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE}; - use super::Parsed; - use crate::Datelike; - use crate::Weekday::*; - use crate::naive::{NaiveDate, NaiveTime}; - use crate::offset::{FixedOffset, TimeZone, Utc}; - - #[test] - fn test_parsed_set_fields() { - // year*, isoyear* - let mut p = Parsed::new(); - assert_eq!(p.set_year(1987), Ok(())); - assert_eq!(p.set_year(1986), Err(IMPOSSIBLE)); - assert_eq!(p.set_year(1988), Err(IMPOSSIBLE)); - assert_eq!(p.set_year(1987), Ok(())); - assert_eq!(p.set_year_div_100(20), Ok(())); // independent to `year` - assert_eq!(p.set_year_div_100(21), Err(IMPOSSIBLE)); - assert_eq!(p.set_year_div_100(19), Err(IMPOSSIBLE)); - assert_eq!(p.set_year_mod_100(37), Ok(())); // ditto - assert_eq!(p.set_year_mod_100(38), Err(IMPOSSIBLE)); - assert_eq!(p.set_year_mod_100(36), Err(IMPOSSIBLE)); - - let mut p = Parsed::new(); - assert_eq!(p.set_year(0), Ok(())); - assert_eq!(p.set_year_div_100(0), Ok(())); - assert_eq!(p.set_year_mod_100(0), Ok(())); - - let mut p = Parsed::new(); - assert_eq!(p.set_year_div_100(-1), Err(OUT_OF_RANGE)); - assert_eq!(p.set_year_mod_100(-1), Err(OUT_OF_RANGE)); - assert_eq!(p.set_year(-1), Ok(())); - assert_eq!(p.set_year(-2), Err(IMPOSSIBLE)); - assert_eq!(p.set_year(0), Err(IMPOSSIBLE)); - - let mut p = Parsed::new(); - assert_eq!(p.set_year_div_100(0x1_0000_0008), Err(OUT_OF_RANGE)); - assert_eq!(p.set_year_div_100(8), Ok(())); - assert_eq!(p.set_year_div_100(0x1_0000_0008), Err(OUT_OF_RANGE)); - - // month, week*, isoweek, ordinal, day, minute, second, nanosecond, offset - let mut p = Parsed::new(); - assert_eq!(p.set_month(7), Ok(())); - assert_eq!(p.set_month(1), Err(IMPOSSIBLE)); - assert_eq!(p.set_month(6), Err(IMPOSSIBLE)); - assert_eq!(p.set_month(8), Err(IMPOSSIBLE)); - assert_eq!(p.set_month(12), Err(IMPOSSIBLE)); - - let mut p = Parsed::new(); - assert_eq!(p.set_month(8), Ok(())); - assert_eq!(p.set_month(0x1_0000_0008), Err(OUT_OF_RANGE)); - - // hour - let mut p = Parsed::new(); - assert_eq!(p.set_hour(12), Ok(())); - assert_eq!(p.set_hour(11), Err(IMPOSSIBLE)); - assert_eq!(p.set_hour(13), Err(IMPOSSIBLE)); - assert_eq!(p.set_hour(12), Ok(())); - assert_eq!(p.set_ampm(false), Err(IMPOSSIBLE)); - assert_eq!(p.set_ampm(true), Ok(())); - assert_eq!(p.set_hour12(12), Ok(())); - assert_eq!(p.set_hour12(0), Err(OUT_OF_RANGE)); // requires canonical representation - assert_eq!(p.set_hour12(1), Err(IMPOSSIBLE)); - assert_eq!(p.set_hour12(11), Err(IMPOSSIBLE)); - - let mut p = Parsed::new(); - assert_eq!(p.set_ampm(true), Ok(())); - assert_eq!(p.set_hour12(7), Ok(())); - assert_eq!(p.set_hour(7), Err(IMPOSSIBLE)); - assert_eq!(p.set_hour(18), Err(IMPOSSIBLE)); - assert_eq!(p.set_hour(19), Ok(())); - - // timestamp - let mut p = Parsed::new(); - assert_eq!(p.set_timestamp(1_234_567_890), Ok(())); - assert_eq!(p.set_timestamp(1_234_567_889), Err(IMPOSSIBLE)); - assert_eq!(p.set_timestamp(1_234_567_891), Err(IMPOSSIBLE)); - } - - #[test] - fn test_parsed_set_range() { - assert_eq!(Parsed::new().set_year(i32::MIN as i64 - 1), Err(OUT_OF_RANGE)); - assert!(Parsed::new().set_year(i32::MIN as i64).is_ok()); - assert!(Parsed::new().set_year(i32::MAX as i64).is_ok()); - assert_eq!(Parsed::new().set_year(i32::MAX as i64 + 1), Err(OUT_OF_RANGE)); - - assert_eq!(Parsed::new().set_year_div_100(-1), Err(OUT_OF_RANGE)); - assert!(Parsed::new().set_year_div_100(0).is_ok()); - assert!(Parsed::new().set_year_div_100(i32::MAX as i64).is_ok()); - assert_eq!(Parsed::new().set_year_div_100(i32::MAX as i64 + 1), Err(OUT_OF_RANGE)); - - assert_eq!(Parsed::new().set_year_mod_100(-1), Err(OUT_OF_RANGE)); - assert!(Parsed::new().set_year_mod_100(0).is_ok()); - assert!(Parsed::new().set_year_mod_100(99).is_ok()); - assert_eq!(Parsed::new().set_year_mod_100(100), Err(OUT_OF_RANGE)); - - assert_eq!(Parsed::new().set_isoyear(i32::MIN as i64 - 1), Err(OUT_OF_RANGE)); - assert!(Parsed::new().set_isoyear(i32::MIN as i64).is_ok()); - assert!(Parsed::new().set_isoyear(i32::MAX as i64).is_ok()); - assert_eq!(Parsed::new().set_isoyear(i32::MAX as i64 + 1), Err(OUT_OF_RANGE)); - - assert_eq!(Parsed::new().set_isoyear_div_100(-1), Err(OUT_OF_RANGE)); - assert!(Parsed::new().set_isoyear_div_100(0).is_ok()); - assert!(Parsed::new().set_isoyear_div_100(99).is_ok()); - assert_eq!(Parsed::new().set_isoyear_div_100(i32::MAX as i64 + 1), Err(OUT_OF_RANGE)); - - assert_eq!(Parsed::new().set_isoyear_mod_100(-1), Err(OUT_OF_RANGE)); - assert!(Parsed::new().set_isoyear_mod_100(0).is_ok()); - assert!(Parsed::new().set_isoyear_mod_100(99).is_ok()); - assert_eq!(Parsed::new().set_isoyear_mod_100(100), Err(OUT_OF_RANGE)); - - assert_eq!(Parsed::new().set_quarter(0), Err(OUT_OF_RANGE)); - assert!(Parsed::new().set_quarter(1).is_ok()); - assert!(Parsed::new().set_quarter(4).is_ok()); - assert_eq!(Parsed::new().set_quarter(5), Err(OUT_OF_RANGE)); - - assert_eq!(Parsed::new().set_month(0), Err(OUT_OF_RANGE)); - assert!(Parsed::new().set_month(1).is_ok()); - assert!(Parsed::new().set_month(12).is_ok()); - assert_eq!(Parsed::new().set_month(13), Err(OUT_OF_RANGE)); - - assert_eq!(Parsed::new().set_week_from_sun(-1), Err(OUT_OF_RANGE)); - assert!(Parsed::new().set_week_from_sun(0).is_ok()); - assert!(Parsed::new().set_week_from_sun(53).is_ok()); - assert_eq!(Parsed::new().set_week_from_sun(54), Err(OUT_OF_RANGE)); - - assert_eq!(Parsed::new().set_week_from_mon(-1), Err(OUT_OF_RANGE)); - assert!(Parsed::new().set_week_from_mon(0).is_ok()); - assert!(Parsed::new().set_week_from_mon(53).is_ok()); - assert_eq!(Parsed::new().set_week_from_mon(54), Err(OUT_OF_RANGE)); - - assert_eq!(Parsed::new().set_isoweek(0), Err(OUT_OF_RANGE)); - assert!(Parsed::new().set_isoweek(1).is_ok()); - assert!(Parsed::new().set_isoweek(53).is_ok()); - assert_eq!(Parsed::new().set_isoweek(54), Err(OUT_OF_RANGE)); - - assert_eq!(Parsed::new().set_ordinal(0), Err(OUT_OF_RANGE)); - assert!(Parsed::new().set_ordinal(1).is_ok()); - assert!(Parsed::new().set_ordinal(366).is_ok()); - assert_eq!(Parsed::new().set_ordinal(367), Err(OUT_OF_RANGE)); - - assert_eq!(Parsed::new().set_day(0), Err(OUT_OF_RANGE)); - assert!(Parsed::new().set_day(1).is_ok()); - assert!(Parsed::new().set_day(31).is_ok()); - assert_eq!(Parsed::new().set_day(32), Err(OUT_OF_RANGE)); - - assert_eq!(Parsed::new().set_hour12(0), Err(OUT_OF_RANGE)); - assert!(Parsed::new().set_hour12(1).is_ok()); - assert!(Parsed::new().set_hour12(12).is_ok()); - assert_eq!(Parsed::new().set_hour12(13), Err(OUT_OF_RANGE)); - - assert_eq!(Parsed::new().set_hour(-1), Err(OUT_OF_RANGE)); - assert!(Parsed::new().set_hour(0).is_ok()); - assert!(Parsed::new().set_hour(23).is_ok()); - assert_eq!(Parsed::new().set_hour(24), Err(OUT_OF_RANGE)); - - assert_eq!(Parsed::new().set_minute(-1), Err(OUT_OF_RANGE)); - assert!(Parsed::new().set_minute(0).is_ok()); - assert!(Parsed::new().set_minute(59).is_ok()); - assert_eq!(Parsed::new().set_minute(60), Err(OUT_OF_RANGE)); - - assert_eq!(Parsed::new().set_second(-1), Err(OUT_OF_RANGE)); - assert!(Parsed::new().set_second(0).is_ok()); - assert!(Parsed::new().set_second(60).is_ok()); - assert_eq!(Parsed::new().set_second(61), Err(OUT_OF_RANGE)); - - assert_eq!(Parsed::new().set_nanosecond(-1), Err(OUT_OF_RANGE)); - assert!(Parsed::new().set_nanosecond(0).is_ok()); - assert!(Parsed::new().set_nanosecond(999_999_999).is_ok()); - assert_eq!(Parsed::new().set_nanosecond(1_000_000_000), Err(OUT_OF_RANGE)); - - assert!(Parsed::new().set_timestamp(i64::MIN).is_ok()); - assert!(Parsed::new().set_timestamp(i64::MAX).is_ok()); - - assert_eq!(Parsed::new().set_offset(i32::MIN as i64 - 1), Err(OUT_OF_RANGE)); - assert!(Parsed::new().set_offset(i32::MIN as i64).is_ok()); - assert!(Parsed::new().set_offset(i32::MAX as i64).is_ok()); - assert_eq!(Parsed::new().set_offset(i32::MAX as i64 + 1), Err(OUT_OF_RANGE)); - } - - #[test] - fn test_parsed_to_naive_date() { - macro_rules! parse { - ($($k:ident: $v:expr),*) => ( - Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_date() - ) - } - - let ymd = |y, m, d| Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap()); - - // ymd: omission of fields - assert_eq!(parse!(), Err(NOT_ENOUGH)); - assert_eq!(parse!(year: 1984), Err(NOT_ENOUGH)); - assert_eq!(parse!(year: 1984, month: 1), Err(NOT_ENOUGH)); - assert_eq!(parse!(year: 1984, month: 1, day: 2), ymd(1984, 1, 2)); - assert_eq!(parse!(year: 1984, day: 2), Err(NOT_ENOUGH)); - assert_eq!(parse!(year_div_100: 19), Err(NOT_ENOUGH)); - assert_eq!(parse!(year_div_100: 19, year_mod_100: 84), Err(NOT_ENOUGH)); - assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 1), Err(NOT_ENOUGH)); - assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 1, day: 2), ymd(1984, 1, 2)); - assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, day: 2), Err(NOT_ENOUGH)); - assert_eq!(parse!(year_div_100: 19, month: 1, day: 2), Err(NOT_ENOUGH)); - assert_eq!(parse!(year_mod_100: 70, month: 1, day: 2), ymd(1970, 1, 2)); - assert_eq!(parse!(year_mod_100: 69, month: 1, day: 2), ymd(2069, 1, 2)); - - // ymd: out-of-range conditions - assert_eq!(parse!(year_div_100: 19, year_mod_100: 84, month: 2, day: 29), ymd(1984, 2, 29)); - assert_eq!( - parse!(year_div_100: 19, year_mod_100: 83, month: 2, day: 29), - Err(OUT_OF_RANGE) - ); - assert_eq!( - parse!(year_div_100: 19, year_mod_100: 83, month: 13, day: 1), - Err(OUT_OF_RANGE) - ); - assert_eq!( - parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 31), - ymd(1983, 12, 31) - ); - assert_eq!( - parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 32), - Err(OUT_OF_RANGE) - ); - assert_eq!( - parse!(year_div_100: 19, year_mod_100: 83, month: 12, day: 0), - Err(OUT_OF_RANGE) - ); - assert_eq!( - parse!(year_div_100: 19, year_mod_100: 100, month: 1, day: 1), - Err(OUT_OF_RANGE) - ); - assert_eq!(parse!(year_div_100: 19, year_mod_100: -1, month: 1, day: 1), Err(OUT_OF_RANGE)); - assert_eq!(parse!(year_div_100: 0, year_mod_100: 0, month: 1, day: 1), ymd(0, 1, 1)); - assert_eq!(parse!(year_div_100: -1, year_mod_100: 42, month: 1, day: 1), Err(IMPOSSIBLE)); - let max_year = NaiveDate::MAX.year(); - assert_eq!( - parse!(year_div_100: max_year / 100, - year_mod_100: max_year % 100, month: 1, day: 1), - ymd(max_year, 1, 1) - ); - assert_eq!( - parse!(year_div_100: (max_year + 1) / 100, - year_mod_100: (max_year + 1) % 100, month: 1, day: 1), - Err(OUT_OF_RANGE) - ); - - // ymd: conflicting inputs - assert_eq!(parse!(year: 1984, year_div_100: 19, month: 1, day: 1), ymd(1984, 1, 1)); - assert_eq!(parse!(year: 1984, year_div_100: 20, month: 1, day: 1), Err(IMPOSSIBLE)); - assert_eq!(parse!(year: 1984, year_mod_100: 84, month: 1, day: 1), ymd(1984, 1, 1)); - assert_eq!(parse!(year: 1984, year_mod_100: 83, month: 1, day: 1), Err(IMPOSSIBLE)); - assert_eq!( - parse!(year: 1984, year_div_100: 19, year_mod_100: 84, month: 1, day: 1), - ymd(1984, 1, 1) - ); - assert_eq!( - parse!(year: 1984, year_div_100: 18, year_mod_100: 94, month: 1, day: 1), - Err(IMPOSSIBLE) - ); - assert_eq!( - parse!(year: 1984, year_div_100: 18, year_mod_100: 184, month: 1, day: 1), - Err(OUT_OF_RANGE) - ); - assert_eq!( - parse!(year: -1, year_div_100: 0, year_mod_100: -1, month: 1, day: 1), - Err(OUT_OF_RANGE) - ); - assert_eq!( - parse!(year: -1, year_div_100: -1, year_mod_100: 99, month: 1, day: 1), - Err(IMPOSSIBLE) - ); - assert_eq!(parse!(year: -1, year_div_100: 0, month: 1, day: 1), Err(IMPOSSIBLE)); - assert_eq!(parse!(year: -1, year_mod_100: 99, month: 1, day: 1), Err(IMPOSSIBLE)); - - // quarters - assert_eq!(parse!(year: 2000, quarter: 1), Err(NOT_ENOUGH)); - assert_eq!(parse!(year: 2000, quarter: 1, month: 1, day: 1), ymd(2000, 1, 1)); - assert_eq!(parse!(year: 2000, quarter: 2, month: 4, day: 1), ymd(2000, 4, 1)); - assert_eq!(parse!(year: 2000, quarter: 3, month: 7, day: 1), ymd(2000, 7, 1)); - assert_eq!(parse!(year: 2000, quarter: 4, month: 10, day: 1), ymd(2000, 10, 1)); - - // quarter: conflicting inputs - assert_eq!(parse!(year: 2000, quarter: 2, month: 3, day: 31), Err(IMPOSSIBLE)); - assert_eq!(parse!(year: 2000, quarter: 4, month: 3, day: 31), Err(IMPOSSIBLE)); - - // weekdates - assert_eq!(parse!(year: 2000, week_from_mon: 0), Err(NOT_ENOUGH)); - assert_eq!(parse!(year: 2000, week_from_sun: 0), Err(NOT_ENOUGH)); - assert_eq!(parse!(year: 2000, weekday: Sun), Err(NOT_ENOUGH)); - assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Fri), Err(IMPOSSIBLE)); - assert_eq!(parse!(year: 2000, week_from_sun: 0, weekday: Fri), Err(IMPOSSIBLE)); - assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Sat), ymd(2000, 1, 1)); - assert_eq!(parse!(year: 2000, week_from_sun: 0, weekday: Sat), ymd(2000, 1, 1)); - assert_eq!(parse!(year: 2000, week_from_mon: 0, weekday: Sun), ymd(2000, 1, 2)); - assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Sun), ymd(2000, 1, 2)); - assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Mon), ymd(2000, 1, 3)); - assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Mon), ymd(2000, 1, 3)); - assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Sat), ymd(2000, 1, 8)); - assert_eq!(parse!(year: 2000, week_from_sun: 1, weekday: Sat), ymd(2000, 1, 8)); - assert_eq!(parse!(year: 2000, week_from_mon: 1, weekday: Sun), ymd(2000, 1, 9)); - assert_eq!(parse!(year: 2000, week_from_sun: 2, weekday: Sun), ymd(2000, 1, 9)); - assert_eq!(parse!(year: 2000, week_from_mon: 2, weekday: Mon), ymd(2000, 1, 10)); - assert_eq!(parse!(year: 2000, week_from_sun: 52, weekday: Sat), ymd(2000, 12, 30)); - assert_eq!(parse!(year: 2000, week_from_sun: 53, weekday: Sun), ymd(2000, 12, 31)); - assert_eq!(parse!(year: 2000, week_from_sun: 53, weekday: Mon), Err(IMPOSSIBLE)); - assert_eq!(parse!(year: 2000, week_from_sun: 0xffffffff, weekday: Mon), Err(OUT_OF_RANGE)); - assert_eq!(parse!(year: 2006, week_from_sun: 0, weekday: Sat), Err(IMPOSSIBLE)); - assert_eq!(parse!(year: 2006, week_from_sun: 1, weekday: Sun), ymd(2006, 1, 1)); - - // weekdates: conflicting inputs - assert_eq!( - parse!(year: 2000, week_from_mon: 1, week_from_sun: 1, weekday: Sat), - ymd(2000, 1, 8) - ); - assert_eq!( - parse!(year: 2000, week_from_mon: 1, week_from_sun: 2, weekday: Sun), - ymd(2000, 1, 9) - ); - assert_eq!( - parse!(year: 2000, week_from_mon: 1, week_from_sun: 1, weekday: Sun), - Err(IMPOSSIBLE) - ); - assert_eq!( - parse!(year: 2000, week_from_mon: 2, week_from_sun: 2, weekday: Sun), - Err(IMPOSSIBLE) - ); - - // ISO weekdates - assert_eq!(parse!(isoyear: 2004, isoweek: 53), Err(NOT_ENOUGH)); - assert_eq!(parse!(isoyear: 2004, isoweek: 53, weekday: Fri), ymd(2004, 12, 31)); - assert_eq!(parse!(isoyear: 2004, isoweek: 53, weekday: Sat), ymd(2005, 1, 1)); - assert_eq!(parse!(isoyear: 2004, isoweek: 0xffffffff, weekday: Sat), Err(OUT_OF_RANGE)); - assert_eq!(parse!(isoyear: 2005, isoweek: 0, weekday: Thu), Err(OUT_OF_RANGE)); - assert_eq!(parse!(isoyear: 2005, isoweek: 5, weekday: Thu), ymd(2005, 2, 3)); - assert_eq!(parse!(isoyear: 2005, weekday: Thu), Err(NOT_ENOUGH)); - - // year and ordinal - assert_eq!(parse!(ordinal: 123), Err(NOT_ENOUGH)); - assert_eq!(parse!(year: 2000, ordinal: 0), Err(OUT_OF_RANGE)); - assert_eq!(parse!(year: 2000, ordinal: 1), ymd(2000, 1, 1)); - assert_eq!(parse!(year: 2000, ordinal: 60), ymd(2000, 2, 29)); - assert_eq!(parse!(year: 2000, ordinal: 61), ymd(2000, 3, 1)); - assert_eq!(parse!(year: 2000, ordinal: 366), ymd(2000, 12, 31)); - assert_eq!(parse!(year: 2000, ordinal: 367), Err(OUT_OF_RANGE)); - assert_eq!(parse!(year: 2000, ordinal: 0xffffffff), Err(OUT_OF_RANGE)); - assert_eq!(parse!(year: 2100, ordinal: 0), Err(OUT_OF_RANGE)); - assert_eq!(parse!(year: 2100, ordinal: 1), ymd(2100, 1, 1)); - assert_eq!(parse!(year: 2100, ordinal: 59), ymd(2100, 2, 28)); - assert_eq!(parse!(year: 2100, ordinal: 60), ymd(2100, 3, 1)); - assert_eq!(parse!(year: 2100, ordinal: 365), ymd(2100, 12, 31)); - assert_eq!(parse!(year: 2100, ordinal: 366), Err(OUT_OF_RANGE)); - assert_eq!(parse!(year: 2100, ordinal: 0xffffffff), Err(OUT_OF_RANGE)); - - // more complex cases - assert_eq!( - parse!(year: 2014, month: 12, day: 31, ordinal: 365, isoyear: 2015, isoweek: 1, - week_from_sun: 52, week_from_mon: 52, weekday: Wed), - ymd(2014, 12, 31) - ); - assert_eq!( - parse!(year: 2014, month: 12, ordinal: 365, isoyear: 2015, isoweek: 1, - week_from_sun: 52, week_from_mon: 52), - ymd(2014, 12, 31) - ); - assert_eq!( - parse!(year: 2014, month: 12, day: 31, ordinal: 365, isoyear: 2014, isoweek: 53, - week_from_sun: 52, week_from_mon: 52, weekday: Wed), - Err(IMPOSSIBLE) - ); // no ISO week date 2014-W53-3 - assert_eq!( - parse!(year: 2012, isoyear: 2015, isoweek: 1, - week_from_sun: 52, week_from_mon: 52), - Err(NOT_ENOUGH) - ); // ambiguous (2014-12-29, 2014-12-30, 2014-12-31) - assert_eq!(parse!(year_div_100: 20, isoyear_mod_100: 15, ordinal: 366), Err(NOT_ENOUGH)); - // technically unique (2014-12-31) but Chrono gives up - } - - #[test] - fn test_parsed_to_naive_time() { - macro_rules! parse { - ($($k:ident: $v:expr),*) => ( - Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_time() - ) - } - - let hms = |h, m, s| Ok(NaiveTime::from_hms_opt(h, m, s).unwrap()); - let hmsn = |h, m, s, n| Ok(NaiveTime::from_hms_nano_opt(h, m, s, n).unwrap()); - - // omission of fields - assert_eq!(parse!(), Err(NOT_ENOUGH)); - assert_eq!(parse!(hour_div_12: 0), Err(NOT_ENOUGH)); - assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1), Err(NOT_ENOUGH)); - assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23), hms(1, 23, 0)); - assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 45), hms(1, 23, 45)); - assert_eq!( - parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 45, - nanosecond: 678_901_234), - hmsn(1, 23, 45, 678_901_234) - ); - assert_eq!(parse!(hour_div_12: 1, hour_mod_12: 11, minute: 45, second: 6), hms(23, 45, 6)); - assert_eq!(parse!(hour_mod_12: 1, minute: 23), Err(NOT_ENOUGH)); - assert_eq!( - parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, nanosecond: 456_789_012), - Err(NOT_ENOUGH) - ); - - // out-of-range conditions - assert_eq!(parse!(hour_div_12: 2, hour_mod_12: 0, minute: 0), Err(OUT_OF_RANGE)); - assert_eq!(parse!(hour_div_12: 1, hour_mod_12: 12, minute: 0), Err(OUT_OF_RANGE)); - assert_eq!(parse!(hour_div_12: 0, hour_mod_12: 1, minute: 60), Err(OUT_OF_RANGE)); - assert_eq!( - parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 61), - Err(OUT_OF_RANGE) - ); - assert_eq!( - parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 34, - nanosecond: 1_000_000_000), - Err(OUT_OF_RANGE) - ); - - // leap seconds - assert_eq!( - parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 60), - hmsn(1, 23, 59, 1_000_000_000) - ); - assert_eq!( - parse!(hour_div_12: 0, hour_mod_12: 1, minute: 23, second: 60, - nanosecond: 999_999_999), - hmsn(1, 23, 59, 1_999_999_999) - ); - } - - #[test] - fn test_parsed_to_naive_datetime_with_offset() { - macro_rules! parse { - (offset = $offset:expr; $($k:ident: $v:expr),*) => ( - Parsed { $($k: Some($v),)* ..Parsed::new() }.to_naive_datetime_with_offset($offset) - ); - ($($k:ident: $v:expr),*) => (parse!(offset = 0; $($k: $v),*)) - } - - let ymdhms = |y, m, d, h, n, s| { - Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap()) - }; - let ymdhmsn = |y, m, d, h, n, s, nano| { - Ok(NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_nano_opt(h, n, s, nano).unwrap()) - }; - - // omission of fields - assert_eq!(parse!(), Err(NOT_ENOUGH)); - assert_eq!( - parse!(year: 2015, month: 1, day: 30, - hour_div_12: 1, hour_mod_12: 2, minute: 38), - ymdhms(2015, 1, 30, 14, 38, 0) - ); - assert_eq!( - parse!(year: 1997, month: 1, day: 30, - hour_div_12: 1, hour_mod_12: 2, minute: 38, second: 5), - ymdhms(1997, 1, 30, 14, 38, 5) - ); - assert_eq!( - parse!(year: 2012, ordinal: 34, hour_div_12: 0, hour_mod_12: 5, - minute: 6, second: 7, nanosecond: 890_123_456), - ymdhmsn(2012, 2, 3, 5, 6, 7, 890_123_456) - ); - assert_eq!(parse!(timestamp: 0), ymdhms(1970, 1, 1, 0, 0, 0)); - assert_eq!(parse!(timestamp: 1, nanosecond: 0), ymdhms(1970, 1, 1, 0, 0, 1)); - assert_eq!(parse!(timestamp: 1, nanosecond: 1), ymdhmsn(1970, 1, 1, 0, 0, 1, 1)); - assert_eq!(parse!(timestamp: 1_420_000_000), ymdhms(2014, 12, 31, 4, 26, 40)); - assert_eq!(parse!(timestamp: -0x1_0000_0000), ymdhms(1833, 11, 24, 17, 31, 44)); - - // full fields - assert_eq!( - parse!(year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31, - ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15, - isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed, - hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40, - nanosecond: 12_345_678, timestamp: 1_420_000_000), - ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678) - ); - assert_eq!( - parse!(year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31, - ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15, - isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed, - hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40, - nanosecond: 12_345_678, timestamp: 1_419_999_999), - Err(IMPOSSIBLE) - ); - assert_eq!( - parse!(offset = 32400; - year: 2014, year_div_100: 20, year_mod_100: 14, month: 12, day: 31, - ordinal: 365, isoyear: 2015, isoyear_div_100: 20, isoyear_mod_100: 15, - isoweek: 1, week_from_sun: 52, week_from_mon: 52, weekday: Wed, - hour_div_12: 0, hour_mod_12: 4, minute: 26, second: 40, - nanosecond: 12_345_678, timestamp: 1_419_967_600), - ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678) - ); - - // more timestamps - let max_days_from_year_1970 = - NaiveDate::MAX.signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()); - let year_0_from_year_1970 = NaiveDate::from_ymd_opt(0, 1, 1) - .unwrap() - .signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()); - let min_days_from_year_1970 = - NaiveDate::MIN.signed_duration_since(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()); - assert_eq!( - parse!(timestamp: min_days_from_year_1970.num_seconds()), - ymdhms(NaiveDate::MIN.year(), 1, 1, 0, 0, 0) - ); - assert_eq!( - parse!(timestamp: year_0_from_year_1970.num_seconds()), - ymdhms(0, 1, 1, 0, 0, 0) - ); - assert_eq!( - parse!(timestamp: max_days_from_year_1970.num_seconds() + 86399), - ymdhms(NaiveDate::MAX.year(), 12, 31, 23, 59, 59) - ); - - // leap seconds #1: partial fields - assert_eq!(parse!(second: 59, timestamp: 1_341_100_798), Err(IMPOSSIBLE)); - assert_eq!(parse!(second: 59, timestamp: 1_341_100_799), ymdhms(2012, 6, 30, 23, 59, 59)); - assert_eq!(parse!(second: 59, timestamp: 1_341_100_800), Err(IMPOSSIBLE)); - assert_eq!( - parse!(second: 60, timestamp: 1_341_100_799), - ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000) - ); - assert_eq!( - parse!(second: 60, timestamp: 1_341_100_800), - ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000) - ); - assert_eq!(parse!(second: 0, timestamp: 1_341_100_800), ymdhms(2012, 7, 1, 0, 0, 0)); - assert_eq!(parse!(second: 1, timestamp: 1_341_100_800), Err(IMPOSSIBLE)); - assert_eq!(parse!(second: 60, timestamp: 1_341_100_801), Err(IMPOSSIBLE)); - - // leap seconds #2: full fields - // we need to have separate tests for them since it uses another control flow. - assert_eq!( - parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, - minute: 59, second: 59, timestamp: 1_341_100_798), - Err(IMPOSSIBLE) - ); - assert_eq!( - parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, - minute: 59, second: 59, timestamp: 1_341_100_799), - ymdhms(2012, 6, 30, 23, 59, 59) - ); - assert_eq!( - parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, - minute: 59, second: 59, timestamp: 1_341_100_800), - Err(IMPOSSIBLE) - ); - assert_eq!( - parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, - minute: 59, second: 60, timestamp: 1_341_100_799), - ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000) - ); - assert_eq!( - parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, - minute: 59, second: 60, timestamp: 1_341_100_800), - ymdhmsn(2012, 6, 30, 23, 59, 59, 1_000_000_000) - ); - assert_eq!( - parse!(year: 2012, ordinal: 183, hour_div_12: 0, hour_mod_12: 0, - minute: 0, second: 0, timestamp: 1_341_100_800), - ymdhms(2012, 7, 1, 0, 0, 0) - ); - assert_eq!( - parse!(year: 2012, ordinal: 183, hour_div_12: 0, hour_mod_12: 0, - minute: 0, second: 1, timestamp: 1_341_100_800), - Err(IMPOSSIBLE) - ); - assert_eq!( - parse!(year: 2012, ordinal: 182, hour_div_12: 1, hour_mod_12: 11, - minute: 59, second: 60, timestamp: 1_341_100_801), - Err(IMPOSSIBLE) - ); - - // error codes - assert_eq!( - parse!(year: 2015, month: 1, day: 20, weekday: Tue, - hour_div_12: 2, hour_mod_12: 1, minute: 35, second: 20), - Err(OUT_OF_RANGE) - ); // `hour_div_12` is out of range - } - - #[test] - fn test_parsed_to_datetime() { - macro_rules! parse { - ($($k:ident: $v:expr),*) => ( - Parsed { $($k: Some($v),)* ..Parsed::new() }.to_datetime() - ) - } - - let ymdhmsn = |y, m, d, h, n, s, nano, off| { - Ok(FixedOffset::east_opt(off) - .unwrap() - .from_local_datetime( - &NaiveDate::from_ymd_opt(y, m, d) - .unwrap() - .and_hms_nano_opt(h, n, s, nano) - .unwrap(), - ) - .unwrap()) - }; - - assert_eq!(parse!(offset: 0), Err(NOT_ENOUGH)); - assert_eq!( - parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4, - minute: 26, second: 40, nanosecond: 12_345_678), - Err(NOT_ENOUGH) - ); - assert_eq!( - parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4, - minute: 26, second: 40, nanosecond: 12_345_678, offset: 0), - ymdhmsn(2014, 12, 31, 4, 26, 40, 12_345_678, 0) - ); - assert_eq!( - parse!(year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1, - minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400), - ymdhmsn(2014, 12, 31, 13, 26, 40, 12_345_678, 32400) - ); - assert_eq!( - parse!(year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 1, - minute: 42, second: 4, nanosecond: 12_345_678, offset: -9876), - ymdhmsn(2014, 12, 31, 1, 42, 4, 12_345_678, -9876) - ); - assert_eq!( - parse!(year: 2015, ordinal: 1, hour_div_12: 0, hour_mod_12: 4, - minute: 26, second: 40, nanosecond: 12_345_678, offset: 86_400), - Err(OUT_OF_RANGE) - ); // `FixedOffset` does not support such huge offset - } - - #[test] - fn test_parsed_to_datetime_with_timezone() { - macro_rules! parse { - ($tz:expr; $($k:ident: $v:expr),*) => ( - Parsed { $($k: Some($v),)* ..Parsed::new() }.to_datetime_with_timezone(&$tz) - ) - } - - // single result from ymdhms - assert_eq!( - parse!(Utc; - year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4, - minute: 26, second: 40, nanosecond: 12_345_678, offset: 0), - Ok(Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2014, 12, 31) - .unwrap() - .and_hms_nano_opt(4, 26, 40, 12_345_678) - .unwrap() - ) - .unwrap()) - ); - assert_eq!( - parse!(Utc; - year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1, - minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400), - Err(IMPOSSIBLE) - ); - assert_eq!( - parse!(FixedOffset::east_opt(32400).unwrap(); - year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4, - minute: 26, second: 40, nanosecond: 12_345_678, offset: 0), - Err(IMPOSSIBLE) - ); - assert_eq!( - parse!(FixedOffset::east_opt(32400).unwrap(); - year: 2014, ordinal: 365, hour_div_12: 1, hour_mod_12: 1, - minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400), - Ok(FixedOffset::east_opt(32400) - .unwrap() - .from_local_datetime( - &NaiveDate::from_ymd_opt(2014, 12, 31) - .unwrap() - .and_hms_nano_opt(13, 26, 40, 12_345_678) - .unwrap() - ) - .unwrap()) - ); - - // single result from timestamp - assert_eq!( - parse!(Utc; timestamp: 1_420_000_000, offset: 0), - Ok(Utc.with_ymd_and_hms(2014, 12, 31, 4, 26, 40).unwrap()) - ); - assert_eq!(parse!(Utc; timestamp: 1_420_000_000, offset: 32400), Err(IMPOSSIBLE)); - assert_eq!( - parse!(FixedOffset::east_opt(32400).unwrap(); timestamp: 1_420_000_000, offset: 0), - Err(IMPOSSIBLE) - ); - assert_eq!( - parse!(FixedOffset::east_opt(32400).unwrap(); timestamp: 1_420_000_000, offset: 32400), - Ok(FixedOffset::east_opt(32400) - .unwrap() - .with_ymd_and_hms(2014, 12, 31, 13, 26, 40) - .unwrap()) - ); - - // TODO test with a variable time zone (for None and Ambiguous cases) - } - - #[test] - fn issue_551() { - use crate::Weekday; - let mut parsed = Parsed::new(); - - parsed.year = Some(2002); - parsed.week_from_mon = Some(22); - parsed.weekday = Some(Weekday::Mon); - assert_eq!(NaiveDate::from_ymd_opt(2002, 6, 3).unwrap(), parsed.to_naive_date().unwrap()); - - parsed.year = Some(2001); - assert_eq!(NaiveDate::from_ymd_opt(2001, 5, 28).unwrap(), parsed.to_naive_date().unwrap()); - } -} diff --git a/chrono-0.4.44/src/format/scan.rs b/chrono-0.4.44/src/format/scan.rs deleted file mode 100644 index 30dbc6746b..0000000000 --- a/chrono-0.4.44/src/format/scan.rs +++ /dev/null @@ -1,432 +0,0 @@ -// This is a part of Chrono. -// See README.md and LICENSE.txt for details. - -/*! - * Various scanning routines for the parser. - */ - -use super::{INVALID, OUT_OF_RANGE, ParseResult, TOO_SHORT}; -use crate::Weekday; - -/// Tries to parse the non-negative number from `min` to `max` digits. -/// -/// The absence of digits at all is an unconditional error. -/// More than `max` digits are consumed up to the first `max` digits. -/// Any number that does not fit in `i64` is an error. -#[inline] -pub(super) fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> { - assert!(min <= max); - - // We are only interested in ascii numbers, so we can work with the `str` as bytes. We stop on - // the first non-numeric byte, which may be another ascii character or beginning of multi-byte - // UTF-8 character. - let bytes = s.as_bytes(); - if bytes.len() < min { - return Err(TOO_SHORT); - } - - let mut n = 0i64; - for (i, c) in bytes.iter().take(max).cloned().enumerate() { - // cloned() = copied() - if !c.is_ascii_digit() { - if i < min { - return Err(INVALID); - } else { - return Ok((&s[i..], n)); - } - } - - n = match n.checked_mul(10).and_then(|n| n.checked_add((c - b'0') as i64)) { - Some(n) => n, - None => return Err(OUT_OF_RANGE), - }; - } - - Ok((&s[core::cmp::min(max, bytes.len())..], n)) -} - -/// Tries to consume at least one digits as a fractional second. -/// Returns the number of whole nanoseconds (0--999,999,999). -pub(super) fn nanosecond(s: &str) -> ParseResult<(&str, u32)> { - // record the number of digits consumed for later scaling. - let origlen = s.len(); - let (s, v) = number(s, 1, 9)?; - let v = u32::try_from(v).expect("999,999,999 should fit u32"); - let consumed = origlen - s.len(); - - // scale the number accordingly. - const SCALE: [u32; 10] = - [0, 100_000_000, 10_000_000, 1_000_000, 100_000, 10_000, 1_000, 100, 10, 1]; - let v = v.checked_mul(SCALE[consumed]).ok_or(OUT_OF_RANGE)?; - - // if there are more than 9 digits, skip next digits. - let s = s.trim_start_matches(|c: char| c.is_ascii_digit()); - - Ok((s, v)) -} - -/// Tries to consume a fixed number of digits as a fractional second. -/// Returns the number of whole nanoseconds (0--999,999,999). -pub(super) fn nanosecond_fixed(s: &str, digits: usize) -> ParseResult<(&str, i64)> { - // record the number of digits consumed for later scaling. - let (s, v) = number(s, digits, digits)?; - - // scale the number accordingly. - static SCALE: [i64; 10] = - [0, 100_000_000, 10_000_000, 1_000_000, 100_000, 10_000, 1_000, 100, 10, 1]; - let v = v.checked_mul(SCALE[digits]).ok_or(OUT_OF_RANGE)?; - - Ok((s, v)) -} - -/// Tries to parse the month index (0 through 11) with the first three ASCII letters. -pub(super) fn short_month0(s: &str) -> ParseResult<(&str, u8)> { - if s.len() < 3 { - return Err(TOO_SHORT); - } - let buf = s.as_bytes(); - let month0 = match (buf[0] | 32, buf[1] | 32, buf[2] | 32) { - (b'j', b'a', b'n') => 0, - (b'f', b'e', b'b') => 1, - (b'm', b'a', b'r') => 2, - (b'a', b'p', b'r') => 3, - (b'm', b'a', b'y') => 4, - (b'j', b'u', b'n') => 5, - (b'j', b'u', b'l') => 6, - (b'a', b'u', b'g') => 7, - (b's', b'e', b'p') => 8, - (b'o', b'c', b't') => 9, - (b'n', b'o', b'v') => 10, - (b'd', b'e', b'c') => 11, - _ => return Err(INVALID), - }; - Ok((&s[3..], month0)) -} - -/// Tries to parse the weekday with the first three ASCII letters. -pub(super) fn short_weekday(s: &str) -> ParseResult<(&str, Weekday)> { - if s.len() < 3 { - return Err(TOO_SHORT); - } - let buf = s.as_bytes(); - let weekday = match (buf[0] | 32, buf[1] | 32, buf[2] | 32) { - (b'm', b'o', b'n') => Weekday::Mon, - (b't', b'u', b'e') => Weekday::Tue, - (b'w', b'e', b'd') => Weekday::Wed, - (b't', b'h', b'u') => Weekday::Thu, - (b'f', b'r', b'i') => Weekday::Fri, - (b's', b'a', b't') => Weekday::Sat, - (b's', b'u', b'n') => Weekday::Sun, - _ => return Err(INVALID), - }; - Ok((&s[3..], weekday)) -} - -/// Tries to parse the month index (0 through 11) with short or long month names. -/// It prefers long month names to short month names when both are possible. -pub(super) fn short_or_long_month0(s: &str) -> ParseResult<(&str, u8)> { - // lowercased month names, minus first three chars - static LONG_MONTH_SUFFIXES: [&[u8]; 12] = [ - b"uary", b"ruary", b"ch", b"il", b"", b"e", b"y", b"ust", b"tember", b"ober", b"ember", - b"ember", - ]; - - let (mut s, month0) = short_month0(s)?; - - // tries to consume the suffix if possible - let suffix = LONG_MONTH_SUFFIXES[month0 as usize]; - if s.len() >= suffix.len() && s.as_bytes()[..suffix.len()].eq_ignore_ascii_case(suffix) { - s = &s[suffix.len()..]; - } - - Ok((s, month0)) -} - -/// Tries to parse the weekday with short or long weekday names. -/// It prefers long weekday names to short weekday names when both are possible. -pub(super) fn short_or_long_weekday(s: &str) -> ParseResult<(&str, Weekday)> { - // lowercased weekday names, minus first three chars - static LONG_WEEKDAY_SUFFIXES: [&[u8]; 7] = - [b"day", b"sday", b"nesday", b"rsday", b"day", b"urday", b"day"]; - - let (mut s, weekday) = short_weekday(s)?; - - // tries to consume the suffix if possible - let suffix = LONG_WEEKDAY_SUFFIXES[weekday.num_days_from_monday() as usize]; - if s.len() >= suffix.len() && s.as_bytes()[..suffix.len()].eq_ignore_ascii_case(suffix) { - s = &s[suffix.len()..]; - } - - Ok((s, weekday)) -} - -/// Tries to consume exactly one given character. -pub(super) fn char(s: &str, c1: u8) -> ParseResult<&str> { - match s.as_bytes().first() { - Some(&c) if c == c1 => Ok(&s[1..]), - Some(_) => Err(INVALID), - None => Err(TOO_SHORT), - } -} - -/// Tries to consume one or more whitespace. -pub(super) fn space(s: &str) -> ParseResult<&str> { - let s_ = s.trim_start(); - if s_.len() < s.len() { - Ok(s_) - } else if s.is_empty() { - Err(TOO_SHORT) - } else { - Err(INVALID) - } -} - -/// Consumes any number (including zero) of colon or spaces. -pub(crate) fn colon_or_space(s: &str) -> ParseResult<&str> { - Ok(s.trim_start_matches(|c: char| c == ':' || c.is_whitespace())) -} - -/// Parse a timezone from `s` and return the offset in seconds. -/// -/// The `consume_colon` function is used to parse a mandatory or optional `:` -/// separator between hours offset and minutes offset. -/// -/// The `allow_missing_minutes` flag allows the timezone minutes offset to be -/// missing from `s`. -/// -/// The `allow_tz_minus_sign` flag allows the timezone offset negative character -/// to also be `−` MINUS SIGN (U+2212) in addition to the typical -/// ASCII-compatible `-` HYPHEN-MINUS (U+2D). -/// This is part of [RFC 3339 & ISO 8601]. -/// -/// [RFC 3339 & ISO 8601]: https://en.wikipedia.org/w/index.php?title=ISO_8601&oldid=1114309368#Time_offsets_from_UTC -pub(crate) fn timezone_offset( - mut s: &str, - mut consume_colon: F, - allow_zulu: bool, - allow_missing_minutes: bool, - allow_tz_minus_sign: bool, -) -> ParseResult<(&str, i32)> -where - F: FnMut(&str) -> ParseResult<&str>, -{ - if allow_zulu { - if let Some(&b'Z' | &b'z') = s.as_bytes().first() { - return Ok((&s[1..], 0)); - } - } - - const fn digits(s: &str) -> ParseResult<(u8, u8)> { - let b = s.as_bytes(); - if b.len() < 2 { Err(TOO_SHORT) } else { Ok((b[0], b[1])) } - } - let negative = match s.chars().next() { - Some('+') => { - // PLUS SIGN (U+2B) - s = &s['+'.len_utf8()..]; - - false - } - Some('-') => { - // HYPHEN-MINUS (U+2D) - s = &s['-'.len_utf8()..]; - - true - } - Some('−') => { - // MINUS SIGN (U+2212) - if !allow_tz_minus_sign { - return Err(INVALID); - } - s = &s['−'.len_utf8()..]; - - true - } - Some(_) => return Err(INVALID), - None => return Err(TOO_SHORT), - }; - - // hours (00--99) - let hours = match digits(s)? { - (h1 @ b'0'..=b'9', h2 @ b'0'..=b'9') => i32::from((h1 - b'0') * 10 + (h2 - b'0')), - _ => return Err(INVALID), - }; - s = &s[2..]; - - // colons (and possibly other separators) - s = consume_colon(s)?; - - // minutes (00--59) - // if the next two items are digits then we have to add minutes - let minutes = if let Ok(ds) = digits(s) { - match ds { - (m1 @ b'0'..=b'5', m2 @ b'0'..=b'9') => i32::from((m1 - b'0') * 10 + (m2 - b'0')), - (b'6'..=b'9', b'0'..=b'9') => return Err(OUT_OF_RANGE), - _ => return Err(INVALID), - } - } else if allow_missing_minutes { - 0 - } else { - return Err(TOO_SHORT); - }; - s = match s.len() { - len if len >= 2 => &s[2..], - 0 => s, - _ => return Err(TOO_SHORT), - }; - - let seconds = hours * 3600 + minutes * 60; - Ok((s, if negative { -seconds } else { seconds })) -} - -/// Same as `timezone_offset` but also allows for RFC 2822 legacy timezones. -/// May return `None` which indicates an insufficient offset data (i.e. `-0000`). -/// See [RFC 2822 Section 4.3]. -/// -/// [RFC 2822 Section 4.3]: https://tools.ietf.org/html/rfc2822#section-4.3 -pub(super) fn timezone_offset_2822(s: &str) -> ParseResult<(&str, i32)> { - // tries to parse legacy time zone names - let upto = s.as_bytes().iter().position(|&c| !c.is_ascii_alphabetic()).unwrap_or(s.len()); - if upto > 0 { - let name = &s.as_bytes()[..upto]; - let s = &s[upto..]; - let offset_hours = |o| Ok((s, o * 3600)); - // RFC 2822 requires support for some named North America timezones, a small subset of all - // named timezones. - if name.eq_ignore_ascii_case(b"gmt") - || name.eq_ignore_ascii_case(b"ut") - || name.eq_ignore_ascii_case(b"z") - { - return offset_hours(0); - } else if name.eq_ignore_ascii_case(b"edt") { - return offset_hours(-4); - } else if name.eq_ignore_ascii_case(b"est") || name.eq_ignore_ascii_case(b"cdt") { - return offset_hours(-5); - } else if name.eq_ignore_ascii_case(b"cst") || name.eq_ignore_ascii_case(b"mdt") { - return offset_hours(-6); - } else if name.eq_ignore_ascii_case(b"mst") || name.eq_ignore_ascii_case(b"pdt") { - return offset_hours(-7); - } else if name.eq_ignore_ascii_case(b"pst") { - return offset_hours(-8); - } else if name.len() == 1 { - if let b'a'..=b'i' | b'k'..=b'y' | b'A'..=b'I' | b'K'..=b'Y' = name[0] { - // recommended by RFC 2822: consume but treat it as -0000 - return Ok((s, 0)); - } - } - Err(INVALID) - } else { - timezone_offset(s, |s| Ok(s), false, false, false) - } -} - -/// Tries to consume an RFC2822 comment including preceding ` `. -/// -/// Returns the remaining string after the closing parenthesis. -pub(super) fn comment_2822(s: &str) -> ParseResult<(&str, ())> { - use CommentState::*; - - let s = s.trim_start(); - - let mut state = Start; - for (i, c) in s.bytes().enumerate() { - state = match (state, c) { - (Start, b'(') => Next(1), - (Next(1), b')') => return Ok((&s[i + 1..], ())), - (Next(depth), b'\\') => Escape(depth), - (Next(depth), b'(') => Next(depth + 1), - (Next(depth), b')') => Next(depth - 1), - (Next(depth), _) | (Escape(depth), _) => Next(depth), - _ => return Err(INVALID), - }; - } - - Err(TOO_SHORT) -} - -enum CommentState { - Start, - Next(usize), - Escape(usize), -} - -#[cfg(test)] -mod tests { - use super::{ - comment_2822, nanosecond, nanosecond_fixed, short_or_long_month0, short_or_long_weekday, - timezone_offset_2822, - }; - use crate::Weekday; - use crate::format::{INVALID, TOO_SHORT}; - - #[test] - fn test_rfc2822_comments() { - let testdata = [ - ("", Err(TOO_SHORT)), - (" ", Err(TOO_SHORT)), - ("x", Err(INVALID)), - ("(", Err(TOO_SHORT)), - ("()", Ok("")), - (" \r\n\t()", Ok("")), - ("() ", Ok(" ")), - ("()z", Ok("z")), - ("(x)", Ok("")), - ("(())", Ok("")), - ("((()))", Ok("")), - ("(x(x(x)x)x)", Ok("")), - ("( x ( x ( x ) x ) x )", Ok("")), - (r"(\)", Err(TOO_SHORT)), - (r"(\()", Ok("")), - (r"(\))", Ok("")), - (r"(\\)", Ok("")), - ("(()())", Ok("")), - ("( x ( x ) x ( x ) x )", Ok("")), - ]; - - for (test_in, expected) in testdata.iter() { - let actual = comment_2822(test_in).map(|(s, _)| s); - assert_eq!( - *expected, actual, - "{test_in:?} expected to produce {expected:?}, but produced {actual:?}." - ); - } - } - - #[test] - fn test_timezone_offset_2822() { - assert_eq!(timezone_offset_2822("cSt").unwrap(), ("", -21600)); - assert_eq!(timezone_offset_2822("pSt").unwrap(), ("", -28800)); - assert_eq!(timezone_offset_2822("mSt").unwrap(), ("", -25200)); - assert_eq!(timezone_offset_2822("-1551").unwrap(), ("", -57060)); - assert_eq!(timezone_offset_2822("Gp"), Err(INVALID)); - } - - #[test] - fn test_short_or_long_month0() { - assert_eq!(short_or_long_month0("JUn").unwrap(), ("", 5)); - assert_eq!(short_or_long_month0("mAy").unwrap(), ("", 4)); - assert_eq!(short_or_long_month0("AuG").unwrap(), ("", 7)); - assert_eq!(short_or_long_month0("Aprâ").unwrap(), ("â", 3)); - assert_eq!(short_or_long_month0("JUl").unwrap(), ("", 6)); - assert_eq!(short_or_long_month0("mAr").unwrap(), ("", 2)); - assert_eq!(short_or_long_month0("Jan").unwrap(), ("", 0)); - } - - #[test] - fn test_short_or_long_weekday() { - assert_eq!(short_or_long_weekday("sAtu").unwrap(), ("u", Weekday::Sat)); - assert_eq!(short_or_long_weekday("thu").unwrap(), ("", Weekday::Thu)); - } - - #[test] - fn test_nanosecond_fixed() { - assert_eq!(nanosecond_fixed("", 0usize).unwrap(), ("", 0)); - assert!(nanosecond_fixed("", 1usize).is_err()); - } - - #[test] - fn test_nanosecond() { - assert_eq!(nanosecond("2Ù").unwrap(), ("Ù", 200000000)); - assert_eq!(nanosecond("8").unwrap(), ("", 800000000)); - } -} diff --git a/chrono-0.4.44/src/format/strftime.rs b/chrono-0.4.44/src/format/strftime.rs deleted file mode 100644 index d93ef814c8..0000000000 --- a/chrono-0.4.44/src/format/strftime.rs +++ /dev/null @@ -1,1217 +0,0 @@ -// This is a part of Chrono. -// See README.md and LICENSE.txt for details. - -/*! -`strftime`/`strptime`-inspired date and time formatting syntax. - -## Specifiers - -The following specifiers are available both to formatting and parsing. - -| Spec. | Example | Description | -|-------|----------|----------------------------------------------------------------------------| -| | | **DATE SPECIFIERS:** | -| `%Y` | `2001` | The full proleptic Gregorian year, zero-padded to 4 digits. chrono supports years from -262144 to 262143. Note: years before 1 BCE or after 9999 CE, require an initial sign (+/-).| -| `%C` | `20` | The proleptic Gregorian year divided by 100, zero-padded to 2 digits. [^1] | -| `%y` | `01` | The proleptic Gregorian year modulo 100, zero-padded to 2 digits. [^1] | -| | | | -| `%q` | `1` | Quarter of year (1-4) | -| `%m` | `07` | Month number (01--12), zero-padded to 2 digits. | -| `%b` | `Jul` | Abbreviated month name. Always 3 letters. | -| `%B` | `July` | Full month name. Also accepts corresponding abbreviation in parsing. | -| `%h` | `Jul` | Same as `%b`. | -| | | | -| `%d` | `08` | Day number (01--31), zero-padded to 2 digits. | -| `%e` | ` 8` | Same as `%d` but space-padded. Same as `%_d`. | -| | | | -| `%a` | `Sun` | Abbreviated weekday name. Always 3 letters. | -| `%A` | `Sunday` | Full weekday name. Also accepts corresponding abbreviation in parsing. | -| `%w` | `0` | Sunday = 0, Monday = 1, ..., Saturday = 6. | -| `%u` | `7` | Monday = 1, Tuesday = 2, ..., Sunday = 7. (ISO 8601) | -| | | | -| `%U` | `28` | Week number starting with Sunday (00--53), zero-padded to 2 digits. [^2] | -| `%W` | `27` | Same as `%U`, but week 1 starts with the first Monday in that year instead.| -| | | | -| `%G` | `2001` | Same as `%Y` but uses the year number in ISO 8601 week date. [^3] | -| `%g` | `01` | Same as `%y` but uses the year number in ISO 8601 week date. [^3] | -| `%V` | `27` | Same as `%U` but uses the week number in ISO 8601 week date (01--53). [^3] | -| | | | -| `%j` | `189` | Day of the year (001--366), zero-padded to 3 digits. | -| | | | -| `%D` | `07/08/01` | Month-day-year format. Same as `%m/%d/%y`. | -| `%x` | `07/08/01` | Locale's date representation (e.g., 12/31/99). | -| `%F` | `2001-07-08` | Year-month-day format (ISO 8601). Same as `%Y-%m-%d`. | -| `%v` | ` 8-Jul-2001` | Day-month-year format. Same as `%e-%b-%Y`. | -| | | | -| | | **TIME SPECIFIERS:** | -| `%H` | `00` | Hour number (00--23), zero-padded to 2 digits. | -| `%k` | ` 0` | Same as `%H` but space-padded. Same as `%_H`. | -| `%I` | `12` | Hour number in 12-hour clocks (01--12), zero-padded to 2 digits. | -| `%l` | `12` | Same as `%I` but space-padded. Same as `%_I`. | -| | | | -| `%P` | `am` | `am` or `pm` in 12-hour clocks. | -| `%p` | `AM` | `AM` or `PM` in 12-hour clocks. | -| | | | -| `%M` | `34` | Minute number (00--59), zero-padded to 2 digits. | -| `%S` | `60` | Second number (00--60), zero-padded to 2 digits. [^4] | -| `%f` | `26490000` | Number of nanoseconds since last whole second. [^7] | -| `%.f` | `.026490`| Decimal fraction of a second. Consumes the leading dot. [^7] | -| `%.3f`| `.026` | Decimal fraction of a second with a fixed length of 3. | -| `%.6f`| `.026490` | Decimal fraction of a second with a fixed length of 6. | -| `%.9f`| `.026490000` | Decimal fraction of a second with a fixed length of 9. | -| `%3f` | `026` | Decimal fraction of a second like `%.3f` but without the leading dot. | -| `%6f` | `026490` | Decimal fraction of a second like `%.6f` but without the leading dot. | -| `%9f` | `026490000` | Decimal fraction of a second like `%.9f` but without the leading dot. | -| | | | -| `%R` | `00:34` | Hour-minute format. Same as `%H:%M`. | -| `%T` | `00:34:60` | Hour-minute-second format. Same as `%H:%M:%S`. | -| `%X` | `00:34:60` | Locale's time representation (e.g., 23:13:48). | -| `%r` | `12:34:60 AM` | Locale's 12 hour clock time. (e.g., 11:11:04 PM). Falls back to `%X` if the locale does not have a 12 hour clock format. | -| | | | -| | | **TIME ZONE SPECIFIERS:** | -| `%Z` | `ACST` | Local time zone name. Skips all non-whitespace characters during parsing. Identical to `%:z` when formatting. [^8] | -| `%z` | `+0930` | Offset from the local time to UTC (with UTC being `+0000`). | -| `%:z` | `+09:30` | Same as `%z` but with a colon. | -|`%::z`|`+09:30:00`| Offset from the local time to UTC with seconds. | -|`%:::z`| `+09` | Offset from the local time to UTC without minutes. | -| `%#z` | `+09` | *Parsing only:* Same as `%z` but allows minutes to be missing or present. | -| | | | -| | | **DATE & TIME SPECIFIERS:** | -|`%c`|`Sun Jul 8 00:34:60 2001`|Locale's date and time (e.g., Thu Mar 3 23:05:25 2005). | -| `%+` | `2001-07-08T00:34:60.026490+09:30` | ISO 8601 / RFC 3339 date & time format. [^5] | -| | | | -| `%s` | `994518299` | UNIX timestamp, the number of seconds since 1970-01-01 00:00 UTC. [^6]| -| | | | -| | | **SPECIAL SPECIFIERS:** | -| `%t` | | Literal tab (`\t`). | -| `%n` | | Literal newline (`\n`). | -| `%%` | | Literal percent sign. | - -It is possible to override the default padding behavior of numeric specifiers `%?`. -This is not allowed for other specifiers and will result in the `BAD_FORMAT` error. - -Modifier | Description --------- | ----------- -`%-?` | Suppresses any padding including spaces and zeroes. (e.g. `%j` = `012`, `%-j` = `12`) -`%_?` | Uses spaces as a padding. (e.g. `%j` = `012`, `%_j` = ` 12`) -`%0?` | Uses zeroes as a padding. (e.g. `%e` = ` 9`, `%0e` = `09`) - -Notes: - -[^1]: `%C`, `%y`: - This is floor division, so 100 BCE (year number -99) will print `-1` and `99` respectively. - For `%y`, values greater or equal to 70 are interpreted as being in the 20th century, - values smaller than 70 in the 21st century. - -[^2]: `%U`: - Week 1 starts with the first Sunday in that year. - It is possible to have week 0 for days before the first Sunday. - -[^3]: `%G`, `%g`, `%V`: - Week 1 is the first week with at least 4 days in that year. - Week 0 does not exist, so this should be used with `%G` or `%g`. - -[^4]: `%S`: - It accounts for leap seconds, so `60` is possible. - -[^5]: `%+`: Same as `%Y-%m-%dT%H:%M:%S%.f%:z`, i.e. 0, 3, 6 or 9 fractional - digits for seconds and colons in the time zone offset. -
-
- This format also supports having a `Z` or `UTC` in place of `%:z`. They - are equivalent to `+00:00`. -
-
- Note that all `T`, `Z`, and `UTC` are parsed case-insensitively. -
-
- The typical `strftime` implementations have different (and locale-dependent) - formats for this specifier. While Chrono's format for `%+` is far more - stable, it is best to avoid this specifier if you want to control the exact - output. - -[^6]: `%s`: - This is not padded and can be negative. - For the purpose of Chrono, it only accounts for non-leap seconds - so it slightly differs from ISO C `strftime` behavior. - -[^7]: `%f`, `%.f`: -
- `%f` and `%.f` are notably different formatting specifiers.
- `%f` counts the number of nanoseconds since the last whole second, while `%.f` is a fraction of a - second.
- Example: 7μs is formatted as `7000` with `%f`, and formatted as `.000007` with `%.f`. - -[^8]: `%Z`: - Since `chrono` is not aware of timezones beyond their offsets, this specifier - **only prints the offset** when used for formatting. The timezone abbreviation - will NOT be printed. See [this issue](https://github.com/chronotope/chrono/issues/960) - for more information. -
-
- Offset will not be populated from the parsed data, nor will it be validated. - Timezone is completely ignored. Similar to the glibc `strptime` treatment of - this format code. -
-
- It is not possible to reliably convert from an abbreviation to an offset, - for example CDT can mean either Central Daylight Time (North America) or - China Daylight Time. -*/ - -#[cfg(feature = "alloc")] -extern crate alloc; - -#[cfg(any(feature = "alloc", feature = "std"))] -use super::{BAD_FORMAT, ParseError}; -use super::{Fixed, InternalInternal, Item, Numeric, Pad}; -#[cfg(feature = "unstable-locales")] -use super::{Locale, locales}; -use super::{fixed, internal_fixed, num, num0, nums}; -#[cfg(all(feature = "alloc", not(feature = "std"), not(test)))] -use alloc::vec::Vec; - -/// Parsing iterator for `strftime`-like format strings. -/// -/// See the [`format::strftime` module](crate::format::strftime) for supported formatting -/// specifiers. -/// -/// `StrftimeItems` is used in combination with more low-level methods such as [`format::parse()`] -/// or [`format_with_items`]. -/// -/// If formatting or parsing date and time values is not performance-critical, the methods -/// [`parse_from_str`] and [`format`] on types such as [`DateTime`](crate::DateTime) are easier to -/// use. -/// -/// [`format`]: crate::DateTime::format -/// [`format_with_items`]: crate::DateTime::format -/// [`parse_from_str`]: crate::DateTime::parse_from_str -/// [`DateTime`]: crate::DateTime -/// [`format::parse()`]: crate::format::parse() -#[derive(Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct StrftimeItems<'a> { - /// Remaining portion of the string. - remainder: &'a str, - /// If the current specifier is composed of multiple formatting items (e.g. `%+`), - /// `queue` stores a slice of `Item`s that have to be returned one by one. - queue: &'static [Item<'static>], - lenient: bool, - #[cfg(feature = "unstable-locales")] - locale_str: &'a str, - #[cfg(feature = "unstable-locales")] - locale: Option, -} - -impl<'a> StrftimeItems<'a> { - /// Creates a new parsing iterator from a `strftime`-like format string. - /// - /// # Errors - /// - /// While iterating [`Item::Error`] will be returned if the format string contains an invalid - /// or unrecognized formatting specifier. - /// - /// # Example - /// - /// ``` - /// use chrono::format::*; - /// - /// let strftime_parser = StrftimeItems::new("%F"); // %F: year-month-day (ISO 8601) - /// - /// const ISO8601_YMD_ITEMS: &[Item<'static>] = &[ - /// Item::Numeric(Numeric::Year, Pad::Zero), - /// Item::Literal("-"), - /// Item::Numeric(Numeric::Month, Pad::Zero), - /// Item::Literal("-"), - /// Item::Numeric(Numeric::Day, Pad::Zero), - /// ]; - /// assert!(strftime_parser.eq(ISO8601_YMD_ITEMS.iter().cloned())); - /// ``` - #[must_use] - pub const fn new(s: &'a str) -> StrftimeItems<'a> { - StrftimeItems { - remainder: s, - queue: &[], - lenient: false, - #[cfg(feature = "unstable-locales")] - locale_str: "", - #[cfg(feature = "unstable-locales")] - locale: None, - } - } - - /// The same as [`StrftimeItems::new`], but returns [`Item::Literal`] instead of [`Item::Error`]. - /// - /// Useful for formatting according to potentially invalid format strings. - /// - /// # Example - /// - /// ``` - /// use chrono::format::*; - /// - /// let strftime_parser = StrftimeItems::new_lenient("%Y-%Q"); // %Y: year, %Q: invalid - /// - /// const ITEMS: &[Item<'static>] = &[ - /// Item::Numeric(Numeric::Year, Pad::Zero), - /// Item::Literal("-"), - /// Item::Literal("%Q"), - /// ]; - /// println!("{:?}", strftime_parser.clone().collect::>()); - /// assert!(strftime_parser.eq(ITEMS.iter().cloned())); - /// ``` - #[must_use] - pub const fn new_lenient(s: &'a str) -> StrftimeItems<'a> { - StrftimeItems { - remainder: s, - queue: &[], - lenient: true, - #[cfg(feature = "unstable-locales")] - locale_str: "", - #[cfg(feature = "unstable-locales")] - locale: None, - } - } - - /// Creates a new parsing iterator from a `strftime`-like format string, with some formatting - /// specifiers adjusted to match [`Locale`]. - /// - /// Note: `StrftimeItems::new_with_locale` only localizes the *format*. You usually want to - /// combine it with other locale-aware methods such as - /// [`DateTime::format_localized_with_items`] to get things like localized month or day names. - /// - /// The `%x` formatting specifier will use the local date format, `%X` the local time format, - /// and `%c` the local format for date and time. - /// `%r` will use the local 12-hour clock format (e.g., 11:11:04 PM). Not all locales have such - /// a format, in which case we fall back to a 24-hour clock (`%X`). - /// - /// See the [`format::strftime` module](crate::format::strftime) for all supported formatting - /// specifiers. - /// - /// [`DateTime::format_localized_with_items`]: crate::DateTime::format_localized_with_items - /// - /// # Errors - /// - /// While iterating [`Item::Error`] will be returned if the format string contains an invalid - /// or unrecognized formatting specifier. - /// - /// # Example - /// - /// ``` - /// # #[cfg(feature = "alloc")] { - /// use chrono::format::{Locale, StrftimeItems}; - /// use chrono::{FixedOffset, TimeZone}; - /// - /// let dt = FixedOffset::east_opt(9 * 60 * 60) - /// .unwrap() - /// .with_ymd_and_hms(2023, 7, 11, 0, 34, 59) - /// .unwrap(); - /// - /// // Note: you usually want to combine `StrftimeItems::new_with_locale` with other - /// // locale-aware methods such as `DateTime::format_localized_with_items`. - /// // We use the regular `format_with_items` to show only how the formatting changes. - /// - /// let fmtr = dt.format_with_items(StrftimeItems::new_with_locale("%x", Locale::en_US)); - /// assert_eq!(fmtr.to_string(), "07/11/2023"); - /// let fmtr = dt.format_with_items(StrftimeItems::new_with_locale("%x", Locale::ko_KR)); - /// assert_eq!(fmtr.to_string(), "2023ë…„ 07ì›” 11ì¼"); - /// let fmtr = dt.format_with_items(StrftimeItems::new_with_locale("%x", Locale::ja_JP)); - /// assert_eq!(fmtr.to_string(), "2023å¹´07月11æ—¥"); - /// # } - /// ``` - #[cfg(feature = "unstable-locales")] - #[must_use] - pub const fn new_with_locale(s: &'a str, locale: Locale) -> StrftimeItems<'a> { - StrftimeItems { - remainder: s, - queue: &[], - lenient: false, - locale_str: "", - locale: Some(locale), - } - } - - /// Parse format string into a `Vec` of formatting [`Item`]'s. - /// - /// If you need to format or parse multiple values with the same format string, it is more - /// efficient to convert it to a `Vec` of formatting [`Item`]'s than to re-parse the format - /// string on every use. - /// - /// The `format_with_items` methods on [`DateTime`], [`NaiveDateTime`], [`NaiveDate`] and - /// [`NaiveTime`] accept the result for formatting. [`format::parse()`] can make use of it for - /// parsing. - /// - /// [`DateTime`]: crate::DateTime::format_with_items - /// [`NaiveDateTime`]: crate::NaiveDateTime::format_with_items - /// [`NaiveDate`]: crate::NaiveDate::format_with_items - /// [`NaiveTime`]: crate::NaiveTime::format_with_items - /// [`format::parse()`]: crate::format::parse() - /// - /// # Errors - /// - /// Returns an error if the format string contains an invalid or unrecognized formatting - /// specifier and the [`StrftimeItems`] wasn't constructed with [`new_lenient`][Self::new_lenient]. - /// - /// # Example - /// - /// ``` - /// use chrono::format::{parse, Parsed, StrftimeItems}; - /// use chrono::NaiveDate; - /// - /// let fmt_items = StrftimeItems::new("%e %b %Y %k.%M").parse()?; - /// let datetime = NaiveDate::from_ymd_opt(2023, 7, 11).unwrap().and_hms_opt(9, 0, 0).unwrap(); - /// - /// // Formatting - /// assert_eq!( - /// datetime.format_with_items(fmt_items.as_slice().iter()).to_string(), - /// "11 Jul 2023 9.00" - /// ); - /// - /// // Parsing - /// let mut parsed = Parsed::new(); - /// parse(&mut parsed, "11 Jul 2023 9.00", fmt_items.as_slice().iter())?; - /// let parsed_dt = parsed.to_naive_datetime_with_offset(0)?; - /// assert_eq!(parsed_dt, datetime); - /// # Ok::<(), chrono::ParseError>(()) - /// ``` - #[cfg(any(feature = "alloc", feature = "std"))] - pub fn parse(self) -> Result>, ParseError> { - self.into_iter() - .map(|item| match item == Item::Error { - false => Ok(item), - true => Err(BAD_FORMAT), - }) - .collect() - } - - /// Parse format string into a `Vec` of [`Item`]'s that contain no references to slices of the - /// format string. - /// - /// A `Vec` created with [`StrftimeItems::parse`] contains references to the format string, - /// binding the lifetime of the `Vec` to that string. [`StrftimeItems::parse_to_owned`] will - /// convert the references to owned types. - /// - /// # Errors - /// - /// Returns an error if the format string contains an invalid or unrecognized formatting - /// specifier and the [`StrftimeItems`] wasn't constructed with [`new_lenient`][Self::new_lenient]. - /// - /// # Example - /// - /// ``` - /// use chrono::format::{Item, ParseError, StrftimeItems}; - /// use chrono::NaiveDate; - /// - /// fn format_items(date_fmt: &str, time_fmt: &str) -> Result>, ParseError> { - /// // `fmt_string` is dropped at the end of this function. - /// let fmt_string = format!("{} {}", date_fmt, time_fmt); - /// StrftimeItems::new(&fmt_string).parse_to_owned() - /// } - /// - /// let fmt_items = format_items("%e %b %Y", "%k.%M")?; - /// let datetime = NaiveDate::from_ymd_opt(2023, 7, 11).unwrap().and_hms_opt(9, 0, 0).unwrap(); - /// - /// assert_eq!( - /// datetime.format_with_items(fmt_items.as_slice().iter()).to_string(), - /// "11 Jul 2023 9.00" - /// ); - /// # Ok::<(), ParseError>(()) - /// ``` - #[cfg(any(feature = "alloc", feature = "std"))] - pub fn parse_to_owned(self) -> Result>, ParseError> { - self.into_iter() - .map(|item| match item == Item::Error { - false => Ok(item.to_owned()), - true => Err(BAD_FORMAT), - }) - .collect() - } - - fn parse_next_item(&mut self, mut remainder: &'a str) -> Option<(&'a str, Item<'a>)> { - use InternalInternal::*; - use Item::{Literal, Space}; - use Numeric::*; - - let (original, mut remainder) = match remainder.chars().next()? { - // the next item is a specifier - '%' => (remainder, &remainder[1..]), - - // the next item is space - c if c.is_whitespace() => { - // `%` is not a whitespace, so `c != '%'` is redundant - let nextspec = - remainder.find(|c: char| !c.is_whitespace()).unwrap_or(remainder.len()); - assert!(nextspec > 0); - let item = Space(&remainder[..nextspec]); - remainder = &remainder[nextspec..]; - return Some((remainder, item)); - } - - // the next item is literal - _ => { - let nextspec = remainder - .find(|c: char| c.is_whitespace() || c == '%') - .unwrap_or(remainder.len()); - assert!(nextspec > 0); - let item = Literal(&remainder[..nextspec]); - remainder = &remainder[nextspec..]; - return Some((remainder, item)); - } - }; - - macro_rules! next { - () => { - match remainder.chars().next() { - Some(x) => { - remainder = &remainder[x.len_utf8()..]; - x - } - None => return Some((remainder, self.error(original, remainder))), // premature end of string - } - }; - } - - let spec = next!(); - let pad_override = match spec { - '-' => Some(Pad::None), - '0' => Some(Pad::Zero), - '_' => Some(Pad::Space), - _ => None, - }; - - let is_alternate = spec == '#'; - let spec = if pad_override.is_some() || is_alternate { next!() } else { spec }; - if is_alternate && !HAVE_ALTERNATES.contains(spec) { - return Some((remainder, self.error(original, remainder))); - } - - macro_rules! queue { - [$head:expr, $($tail:expr),+ $(,)*] => ({ - const QUEUE: &'static [Item<'static>] = &[$($tail),+]; - self.queue = QUEUE; - $head - }) - } - - #[cfg(not(feature = "unstable-locales"))] - macro_rules! queue_from_slice { - ($slice:expr) => {{ - self.queue = &$slice[1..]; - $slice[0].clone() - }}; - } - - let item = match spec { - 'A' => fixed(Fixed::LongWeekdayName), - 'B' => fixed(Fixed::LongMonthName), - 'C' => num0(YearDiv100), - 'D' => { - queue![num0(Month), Literal("/"), num0(Day), Literal("/"), num0(YearMod100)] - } - 'F' => queue![num0(Year), Literal("-"), num0(Month), Literal("-"), num0(Day)], - 'G' => num0(IsoYear), - 'H' => num0(Hour), - 'I' => num0(Hour12), - 'M' => num0(Minute), - 'P' => fixed(Fixed::LowerAmPm), - 'R' => queue![num0(Hour), Literal(":"), num0(Minute)], - 'S' => num0(Second), - 'T' => { - queue![num0(Hour), Literal(":"), num0(Minute), Literal(":"), num0(Second)] - } - 'U' => num0(WeekFromSun), - 'V' => num0(IsoWeek), - 'W' => num0(WeekFromMon), - #[cfg(not(feature = "unstable-locales"))] - 'X' => queue_from_slice!(T_FMT), - #[cfg(feature = "unstable-locales")] - 'X' => self.switch_to_locale_str(locales::t_fmt, T_FMT), - 'Y' => num0(Year), - 'Z' => fixed(Fixed::TimezoneName), - 'a' => fixed(Fixed::ShortWeekdayName), - 'b' | 'h' => fixed(Fixed::ShortMonthName), - #[cfg(not(feature = "unstable-locales"))] - 'c' => queue_from_slice!(D_T_FMT), - #[cfg(feature = "unstable-locales")] - 'c' => self.switch_to_locale_str(locales::d_t_fmt, D_T_FMT), - 'd' => num0(Day), - 'e' => nums(Day), - 'f' => num0(Nanosecond), - 'g' => num0(IsoYearMod100), - 'j' => num0(Ordinal), - 'k' => nums(Hour), - 'l' => nums(Hour12), - 'm' => num0(Month), - 'n' => Space("\n"), - 'p' => fixed(Fixed::UpperAmPm), - 'q' => num(Quarter), - #[cfg(not(feature = "unstable-locales"))] - 'r' => queue_from_slice!(T_FMT_AMPM), - #[cfg(feature = "unstable-locales")] - 'r' => { - if self.locale.is_some() && locales::t_fmt_ampm(self.locale.unwrap()).is_empty() { - // 12-hour clock not supported by this locale. Switch to 24-hour format. - self.switch_to_locale_str(locales::t_fmt, T_FMT) - } else { - self.switch_to_locale_str(locales::t_fmt_ampm, T_FMT_AMPM) - } - } - 's' => num(Timestamp), - 't' => Space("\t"), - 'u' => num(WeekdayFromMon), - 'v' => { - queue![ - nums(Day), - Literal("-"), - fixed(Fixed::ShortMonthName), - Literal("-"), - num0(Year) - ] - } - 'w' => num(NumDaysFromSun), - #[cfg(not(feature = "unstable-locales"))] - 'x' => queue_from_slice!(D_FMT), - #[cfg(feature = "unstable-locales")] - 'x' => self.switch_to_locale_str(locales::d_fmt, D_FMT), - 'y' => num0(YearMod100), - 'z' => { - if is_alternate { - internal_fixed(TimezoneOffsetPermissive) - } else { - fixed(Fixed::TimezoneOffset) - } - } - '+' => fixed(Fixed::RFC3339), - ':' => { - if remainder.starts_with("::z") { - remainder = &remainder[3..]; - fixed(Fixed::TimezoneOffsetTripleColon) - } else if remainder.starts_with(":z") { - remainder = &remainder[2..]; - fixed(Fixed::TimezoneOffsetDoubleColon) - } else if remainder.starts_with('z') { - remainder = &remainder[1..]; - fixed(Fixed::TimezoneOffsetColon) - } else { - self.error(original, remainder) - } - } - '.' => match next!() { - '3' => match next!() { - 'f' => fixed(Fixed::Nanosecond3), - _ => self.error(original, remainder), - }, - '6' => match next!() { - 'f' => fixed(Fixed::Nanosecond6), - _ => self.error(original, remainder), - }, - '9' => match next!() { - 'f' => fixed(Fixed::Nanosecond9), - _ => self.error(original, remainder), - }, - 'f' => fixed(Fixed::Nanosecond), - _ => self.error(original, remainder), - }, - '3' => match next!() { - 'f' => internal_fixed(Nanosecond3NoDot), - _ => self.error(original, remainder), - }, - '6' => match next!() { - 'f' => internal_fixed(Nanosecond6NoDot), - _ => self.error(original, remainder), - }, - '9' => match next!() { - 'f' => internal_fixed(Nanosecond9NoDot), - _ => self.error(original, remainder), - }, - '%' => Literal("%"), - _ => self.error(original, remainder), - }; - - // Adjust `item` if we have any padding modifier. - // Not allowed on non-numeric items or on specifiers composed out of multiple - // formatting items. - if let Some(new_pad) = pad_override { - match item { - Item::Numeric(ref kind, _pad) if self.queue.is_empty() => { - Some((remainder, Item::Numeric(kind.clone(), new_pad))) - } - _ => Some((remainder, self.error(original, remainder))), - } - } else { - Some((remainder, item)) - } - } - - fn error<'b>(&mut self, original: &'b str, remainder: &'b str) -> Item<'b> { - match self.lenient { - false => Item::Error, - true => Item::Literal(&original[..original.len() - remainder.len()]), - } - } - - #[cfg(feature = "unstable-locales")] - fn switch_to_locale_str( - &mut self, - localized_fmt_str: impl Fn(Locale) -> &'static str, - fallback: &'static [Item<'static>], - ) -> Item<'a> { - if let Some(locale) = self.locale { - assert!(self.locale_str.is_empty()); - let (fmt_str, item) = self.parse_next_item(localized_fmt_str(locale)).unwrap(); - self.locale_str = fmt_str; - item - } else { - self.queue = &fallback[1..]; - fallback[0].clone() - } - } -} - -impl<'a> Iterator for StrftimeItems<'a> { - type Item = Item<'a>; - - fn next(&mut self) -> Option> { - // We have items queued to return from a specifier composed of multiple formatting items. - if let Some((item, remainder)) = self.queue.split_first() { - self.queue = remainder; - return Some(item.clone()); - } - - // We are in the middle of parsing the localized formatting string of a specifier. - #[cfg(feature = "unstable-locales")] - if !self.locale_str.is_empty() { - let (remainder, item) = self.parse_next_item(self.locale_str)?; - self.locale_str = remainder; - return Some(item); - } - - // Normal: we are parsing the formatting string. - let (remainder, item) = self.parse_next_item(self.remainder)?; - self.remainder = remainder; - Some(item) - } -} - -static D_FMT: &[Item<'static>] = &[ - num0(Numeric::Month), - Item::Literal("/"), - num0(Numeric::Day), - Item::Literal("/"), - num0(Numeric::YearMod100), -]; -static D_T_FMT: &[Item<'static>] = &[ - fixed(Fixed::ShortWeekdayName), - Item::Space(" "), - fixed(Fixed::ShortMonthName), - Item::Space(" "), - nums(Numeric::Day), - Item::Space(" "), - num0(Numeric::Hour), - Item::Literal(":"), - num0(Numeric::Minute), - Item::Literal(":"), - num0(Numeric::Second), - Item::Space(" "), - num0(Numeric::Year), -]; -static T_FMT: &[Item<'static>] = &[ - num0(Numeric::Hour), - Item::Literal(":"), - num0(Numeric::Minute), - Item::Literal(":"), - num0(Numeric::Second), -]; -static T_FMT_AMPM: &[Item<'static>] = &[ - num0(Numeric::Hour12), - Item::Literal(":"), - num0(Numeric::Minute), - Item::Literal(":"), - num0(Numeric::Second), - Item::Space(" "), - fixed(Fixed::UpperAmPm), -]; - -const HAVE_ALTERNATES: &str = "z"; - -#[cfg(test)] -mod tests { - use super::StrftimeItems; - use crate::format::Item::{self, Literal, Space}; - #[cfg(feature = "unstable-locales")] - use crate::format::Locale; - use crate::format::{Fixed, InternalInternal, Numeric::*}; - use crate::format::{fixed, internal_fixed, num, num0, nums}; - #[cfg(feature = "alloc")] - use crate::{DateTime, FixedOffset, NaiveDate, TimeZone, Timelike, Utc}; - - #[test] - fn test_strftime_items() { - fn parse_and_collect(s: &str) -> Vec> { - // map any error into `[Item::Error]`. useful for easy testing. - eprintln!("test_strftime_items: parse_and_collect({s:?})"); - let items = StrftimeItems::new(s); - let items = items.map(|spec| if spec == Item::Error { None } else { Some(spec) }); - items.collect::>>().unwrap_or_else(|| vec![Item::Error]) - } - - assert_eq!(parse_and_collect(""), []); - assert_eq!(parse_and_collect(" "), [Space(" ")]); - assert_eq!(parse_and_collect(" "), [Space(" ")]); - // ne! - assert_ne!(parse_and_collect(" "), [Space(" "), Space(" ")]); - // eq! - assert_eq!(parse_and_collect(" "), [Space(" ")]); - assert_eq!(parse_and_collect("a"), [Literal("a")]); - assert_eq!(parse_and_collect("ab"), [Literal("ab")]); - assert_eq!(parse_and_collect("😽"), [Literal("😽")]); - assert_eq!(parse_and_collect("a😽"), [Literal("a😽")]); - assert_eq!(parse_and_collect("😽a"), [Literal("😽a")]); - assert_eq!(parse_and_collect(" 😽"), [Space(" "), Literal("😽")]); - assert_eq!(parse_and_collect("😽 "), [Literal("😽"), Space(" ")]); - // ne! - assert_ne!(parse_and_collect("😽😽"), [Literal("😽")]); - assert_ne!(parse_and_collect("😽"), [Literal("😽😽")]); - assert_ne!(parse_and_collect("😽😽"), [Literal("😽😽"), Literal("😽")]); - // eq! - assert_eq!(parse_and_collect("😽😽"), [Literal("😽😽")]); - assert_eq!(parse_and_collect(" \t\n\r "), [Space(" \t\n\r ")]); - assert_eq!(parse_and_collect("hello?"), [Literal("hello?")]); - assert_eq!( - parse_and_collect("a b\t\nc"), - [Literal("a"), Space(" "), Literal("b"), Space("\t\n"), Literal("c")] - ); - assert_eq!(parse_and_collect("100%%"), [Literal("100"), Literal("%")]); - assert_eq!( - parse_and_collect("100%% ok"), - [Literal("100"), Literal("%"), Space(" "), Literal("ok")] - ); - assert_eq!(parse_and_collect("%%PDF-1.0"), [Literal("%"), Literal("PDF-1.0")]); - assert_eq!( - parse_and_collect("%Y-%m-%d"), - [num0(Year), Literal("-"), num0(Month), Literal("-"), num0(Day)] - ); - assert_eq!(parse_and_collect("😽 "), [Literal("😽"), Space(" ")]); - assert_eq!(parse_and_collect("😽😽"), [Literal("😽😽")]); - assert_eq!(parse_and_collect("😽😽😽"), [Literal("😽😽😽")]); - assert_eq!(parse_and_collect("😽😽 😽"), [Literal("😽😽"), Space(" "), Literal("😽")]); - assert_eq!(parse_and_collect("😽😽a 😽"), [Literal("😽😽a"), Space(" "), Literal("😽")]); - assert_eq!(parse_and_collect("😽😽a b😽"), [Literal("😽😽a"), Space(" "), Literal("b😽")]); - assert_eq!( - parse_and_collect("😽😽a b😽c"), - [Literal("😽😽a"), Space(" "), Literal("b😽c")] - ); - assert_eq!(parse_and_collect("😽😽 "), [Literal("😽😽"), Space(" ")]); - assert_eq!(parse_and_collect("😽😽 😽"), [Literal("😽😽"), Space(" "), Literal("😽")]); - assert_eq!(parse_and_collect(" 😽"), [Space(" "), Literal("😽")]); - assert_eq!(parse_and_collect(" 😽 "), [Space(" "), Literal("😽"), Space(" ")]); - assert_eq!( - parse_and_collect(" 😽 😽"), - [Space(" "), Literal("😽"), Space(" "), Literal("😽")] - ); - assert_eq!( - parse_and_collect(" 😽 😽 "), - [Space(" "), Literal("😽"), Space(" "), Literal("😽"), Space(" ")] - ); - assert_eq!( - parse_and_collect(" 😽 😽 "), - [Space(" "), Literal("😽"), Space(" "), Literal("😽"), Space(" ")] - ); - assert_eq!( - parse_and_collect(" 😽 😽😽 "), - [Space(" "), Literal("😽"), Space(" "), Literal("😽😽"), Space(" ")] - ); - assert_eq!(parse_and_collect(" 😽😽"), [Space(" "), Literal("😽😽")]); - assert_eq!(parse_and_collect(" 😽😽 "), [Space(" "), Literal("😽😽"), Space(" ")]); - assert_eq!( - parse_and_collect(" 😽😽 "), - [Space(" "), Literal("😽😽"), Space(" ")] - ); - assert_eq!( - parse_and_collect(" 😽😽 "), - [Space(" "), Literal("😽😽"), Space(" ")] - ); - assert_eq!(parse_and_collect(" 😽😽 "), [Space(" "), Literal("😽😽"), Space(" ")]); - assert_eq!( - parse_and_collect(" 😽 😽😽 "), - [Space(" "), Literal("😽"), Space(" "), Literal("😽😽"), Space(" ")] - ); - assert_eq!( - parse_and_collect(" 😽 😽ã¯ã„😽 ãƒãƒ³ãƒãƒ¼ã‚¬ãƒ¼"), - [ - Space(" "), - Literal("😽"), - Space(" "), - Literal("😽ã¯ã„😽"), - Space(" "), - Literal("ãƒãƒ³ãƒãƒ¼ã‚¬ãƒ¼") - ] - ); - assert_eq!( - parse_and_collect("%%😽%%😽"), - [Literal("%"), Literal("😽"), Literal("%"), Literal("😽")] - ); - assert_eq!(parse_and_collect("%Y--%m"), [num0(Year), Literal("--"), num0(Month)]); - assert_eq!(parse_and_collect("[%F]"), parse_and_collect("[%Y-%m-%d]")); - assert_eq!(parse_and_collect("100%%😽"), [Literal("100"), Literal("%"), Literal("😽")]); - assert_eq!( - parse_and_collect("100%%😽%%a"), - [Literal("100"), Literal("%"), Literal("😽"), Literal("%"), Literal("a")] - ); - assert_eq!(parse_and_collect("😽100%%"), [Literal("😽100"), Literal("%")]); - assert_eq!(parse_and_collect("%m %d"), [num0(Month), Space(" "), num0(Day)]); - assert_eq!(parse_and_collect("%"), [Item::Error]); - assert_eq!(parse_and_collect("%%"), [Literal("%")]); - assert_eq!(parse_and_collect("%%%"), [Item::Error]); - assert_eq!(parse_and_collect("%a"), [fixed(Fixed::ShortWeekdayName)]); - assert_eq!(parse_and_collect("%aa"), [fixed(Fixed::ShortWeekdayName), Literal("a")]); - assert_eq!(parse_and_collect("%%a%"), [Item::Error]); - assert_eq!(parse_and_collect("%😽"), [Item::Error]); - assert_eq!(parse_and_collect("%😽😽"), [Item::Error]); - assert_eq!(parse_and_collect("%%%%"), [Literal("%"), Literal("%")]); - assert_eq!( - parse_and_collect("%%%%ãƒãƒ³ãƒãƒ¼ã‚¬ãƒ¼"), - [Literal("%"), Literal("%"), Literal("ãƒãƒ³ãƒãƒ¼ã‚¬ãƒ¼")] - ); - assert_eq!(parse_and_collect("foo%?"), [Item::Error]); - assert_eq!(parse_and_collect("bar%42"), [Item::Error]); - assert_eq!(parse_and_collect("quux% +"), [Item::Error]); - assert_eq!(parse_and_collect("%.Z"), [Item::Error]); - assert_eq!(parse_and_collect("%:Z"), [Item::Error]); - assert_eq!(parse_and_collect("%-Z"), [Item::Error]); - assert_eq!(parse_and_collect("%0Z"), [Item::Error]); - assert_eq!(parse_and_collect("%_Z"), [Item::Error]); - assert_eq!(parse_and_collect("%.j"), [Item::Error]); - assert_eq!(parse_and_collect("%:j"), [Item::Error]); - assert_eq!(parse_and_collect("%-j"), [num(Ordinal)]); - assert_eq!(parse_and_collect("%0j"), [num0(Ordinal)]); - assert_eq!(parse_and_collect("%_j"), [nums(Ordinal)]); - assert_eq!(parse_and_collect("%.e"), [Item::Error]); - assert_eq!(parse_and_collect("%:e"), [Item::Error]); - assert_eq!(parse_and_collect("%-e"), [num(Day)]); - assert_eq!(parse_and_collect("%0e"), [num0(Day)]); - assert_eq!(parse_and_collect("%_e"), [nums(Day)]); - assert_eq!(parse_and_collect("%z"), [fixed(Fixed::TimezoneOffset)]); - assert_eq!(parse_and_collect("%:z"), [fixed(Fixed::TimezoneOffsetColon)]); - assert_eq!(parse_and_collect("%Z"), [fixed(Fixed::TimezoneName)]); - assert_eq!(parse_and_collect("%ZZZZ"), [fixed(Fixed::TimezoneName), Literal("ZZZ")]); - assert_eq!(parse_and_collect("%Z😽"), [fixed(Fixed::TimezoneName), Literal("😽")]); - assert_eq!( - parse_and_collect("%#z"), - [internal_fixed(InternalInternal::TimezoneOffsetPermissive)] - ); - assert_eq!(parse_and_collect("%#m"), [Item::Error]); - } - - #[test] - #[cfg(feature = "alloc")] - fn test_strftime_docs() { - let dt = FixedOffset::east_opt(34200) - .unwrap() - .from_local_datetime( - &NaiveDate::from_ymd_opt(2001, 7, 8) - .unwrap() - .and_hms_nano_opt(0, 34, 59, 1_026_490_708) - .unwrap(), - ) - .unwrap(); - - // date specifiers - assert_eq!(dt.format("%Y").to_string(), "2001"); - assert_eq!(dt.format("%C").to_string(), "20"); - assert_eq!(dt.format("%y").to_string(), "01"); - assert_eq!(dt.format("%q").to_string(), "3"); - assert_eq!(dt.format("%m").to_string(), "07"); - assert_eq!(dt.format("%b").to_string(), "Jul"); - assert_eq!(dt.format("%B").to_string(), "July"); - assert_eq!(dt.format("%h").to_string(), "Jul"); - assert_eq!(dt.format("%d").to_string(), "08"); - assert_eq!(dt.format("%e").to_string(), " 8"); - assert_eq!(dt.format("%e").to_string(), dt.format("%_d").to_string()); - assert_eq!(dt.format("%a").to_string(), "Sun"); - assert_eq!(dt.format("%A").to_string(), "Sunday"); - assert_eq!(dt.format("%w").to_string(), "0"); - assert_eq!(dt.format("%u").to_string(), "7"); - assert_eq!(dt.format("%U").to_string(), "27"); - assert_eq!(dt.format("%W").to_string(), "27"); - assert_eq!(dt.format("%G").to_string(), "2001"); - assert_eq!(dt.format("%g").to_string(), "01"); - assert_eq!(dt.format("%V").to_string(), "27"); - assert_eq!(dt.format("%j").to_string(), "189"); - assert_eq!(dt.format("%D").to_string(), "07/08/01"); - assert_eq!(dt.format("%x").to_string(), "07/08/01"); - assert_eq!(dt.format("%F").to_string(), "2001-07-08"); - assert_eq!(dt.format("%v").to_string(), " 8-Jul-2001"); - - // time specifiers - assert_eq!(dt.format("%H").to_string(), "00"); - assert_eq!(dt.format("%k").to_string(), " 0"); - assert_eq!(dt.format("%k").to_string(), dt.format("%_H").to_string()); - assert_eq!(dt.format("%I").to_string(), "12"); - assert_eq!(dt.format("%l").to_string(), "12"); - assert_eq!(dt.format("%l").to_string(), dt.format("%_I").to_string()); - assert_eq!(dt.format("%P").to_string(), "am"); - assert_eq!(dt.format("%p").to_string(), "AM"); - assert_eq!(dt.format("%M").to_string(), "34"); - assert_eq!(dt.format("%S").to_string(), "60"); - assert_eq!(dt.format("%f").to_string(), "026490708"); - assert_eq!(dt.format("%.f").to_string(), ".026490708"); - assert_eq!(dt.with_nanosecond(1_026_490_000).unwrap().format("%.f").to_string(), ".026490"); - assert_eq!(dt.format("%.3f").to_string(), ".026"); - assert_eq!(dt.format("%.6f").to_string(), ".026490"); - assert_eq!(dt.format("%.9f").to_string(), ".026490708"); - assert_eq!(dt.format("%3f").to_string(), "026"); - assert_eq!(dt.format("%6f").to_string(), "026490"); - assert_eq!(dt.format("%9f").to_string(), "026490708"); - assert_eq!(dt.format("%R").to_string(), "00:34"); - assert_eq!(dt.format("%T").to_string(), "00:34:60"); - assert_eq!(dt.format("%X").to_string(), "00:34:60"); - assert_eq!(dt.format("%r").to_string(), "12:34:60 AM"); - - // time zone specifiers - //assert_eq!(dt.format("%Z").to_string(), "ACST"); - assert_eq!(dt.format("%z").to_string(), "+0930"); - assert_eq!(dt.format("%:z").to_string(), "+09:30"); - assert_eq!(dt.format("%::z").to_string(), "+09:30:00"); - assert_eq!(dt.format("%:::z").to_string(), "+09"); - - // date & time specifiers - assert_eq!(dt.format("%c").to_string(), "Sun Jul 8 00:34:60 2001"); - assert_eq!(dt.format("%+").to_string(), "2001-07-08T00:34:60.026490708+09:30"); - - assert_eq!( - dt.with_timezone(&Utc).format("%+").to_string(), - "2001-07-07T15:04:60.026490708+00:00" - ); - assert_eq!( - dt.with_timezone(&Utc), - DateTime::parse_from_str("2001-07-07T15:04:60.026490708Z", "%+").unwrap() - ); - assert_eq!( - dt.with_timezone(&Utc), - DateTime::parse_from_str("2001-07-07T15:04:60.026490708UTC", "%+").unwrap() - ); - assert_eq!( - dt.with_timezone(&Utc), - DateTime::parse_from_str("2001-07-07t15:04:60.026490708utc", "%+").unwrap() - ); - - assert_eq!( - dt.with_nanosecond(1_026_490_000).unwrap().format("%+").to_string(), - "2001-07-08T00:34:60.026490+09:30" - ); - assert_eq!(dt.format("%s").to_string(), "994518299"); - - // special specifiers - assert_eq!(dt.format("%t").to_string(), "\t"); - assert_eq!(dt.format("%n").to_string(), "\n"); - assert_eq!(dt.format("%%").to_string(), "%"); - - // complex format specifiers - assert_eq!(dt.format(" %Y%d%m%%%%%t%H%M%S\t").to_string(), " 20010807%%\t003460\t"); - assert_eq!( - dt.format(" %Y%d%m%%%%%t%H:%P:%M%S%:::z\t").to_string(), - " 20010807%%\t00:am:3460+09\t" - ); - } - - #[test] - #[cfg(all(feature = "unstable-locales", feature = "alloc"))] - fn test_strftime_docs_localized() { - let dt = FixedOffset::east_opt(34200) - .unwrap() - .with_ymd_and_hms(2001, 7, 8, 0, 34, 59) - .unwrap() - .with_nanosecond(1_026_490_708) - .unwrap(); - - // date specifiers - assert_eq!(dt.format_localized("%b", Locale::fr_BE).to_string(), "jui"); - assert_eq!(dt.format_localized("%B", Locale::fr_BE).to_string(), "juillet"); - assert_eq!(dt.format_localized("%h", Locale::fr_BE).to_string(), "jui"); - assert_eq!(dt.format_localized("%a", Locale::fr_BE).to_string(), "dim"); - assert_eq!(dt.format_localized("%A", Locale::fr_BE).to_string(), "dimanche"); - assert_eq!(dt.format_localized("%D", Locale::fr_BE).to_string(), "07/08/01"); - assert_eq!(dt.format_localized("%x", Locale::fr_BE).to_string(), "08/07/01"); - assert_eq!(dt.format_localized("%F", Locale::fr_BE).to_string(), "2001-07-08"); - assert_eq!(dt.format_localized("%v", Locale::fr_BE).to_string(), " 8-jui-2001"); - - // time specifiers - assert_eq!(dt.format_localized("%P", Locale::fr_BE).to_string(), ""); - assert_eq!(dt.format_localized("%p", Locale::fr_BE).to_string(), ""); - assert_eq!(dt.format_localized("%R", Locale::fr_BE).to_string(), "00:34"); - assert_eq!(dt.format_localized("%T", Locale::fr_BE).to_string(), "00:34:60"); - assert_eq!(dt.format_localized("%X", Locale::fr_BE).to_string(), "00:34:60"); - assert_eq!(dt.format_localized("%r", Locale::fr_BE).to_string(), "00:34:60"); - - // date & time specifiers - assert_eq!( - dt.format_localized("%c", Locale::fr_BE).to_string(), - "dim 08 jui 2001 00:34:60 +09:30" - ); - - let nd = NaiveDate::from_ymd_opt(2001, 7, 8).unwrap(); - - // date specifiers - assert_eq!(nd.format_localized("%b", Locale::de_DE).to_string(), "Jul"); - assert_eq!(nd.format_localized("%B", Locale::de_DE).to_string(), "Juli"); - assert_eq!(nd.format_localized("%h", Locale::de_DE).to_string(), "Jul"); - assert_eq!(nd.format_localized("%a", Locale::de_DE).to_string(), "So"); - assert_eq!(nd.format_localized("%A", Locale::de_DE).to_string(), "Sonntag"); - assert_eq!(nd.format_localized("%D", Locale::de_DE).to_string(), "07/08/01"); - assert_eq!(nd.format_localized("%x", Locale::de_DE).to_string(), "08.07.2001"); - assert_eq!(nd.format_localized("%F", Locale::de_DE).to_string(), "2001-07-08"); - assert_eq!(nd.format_localized("%v", Locale::de_DE).to_string(), " 8-Jul-2001"); - } - - /// Ensure parsing a timestamp with the parse-only stftime formatter "%#z" does - /// not cause a panic. - /// - /// See . - #[test] - #[cfg(feature = "alloc")] - fn test_parse_only_timezone_offset_permissive_no_panic() { - use crate::NaiveDate; - use crate::{FixedOffset, TimeZone}; - use std::fmt::Write; - - let dt = FixedOffset::east_opt(34200) - .unwrap() - .from_local_datetime( - &NaiveDate::from_ymd_opt(2001, 7, 8) - .unwrap() - .and_hms_nano_opt(0, 34, 59, 1_026_490_708) - .unwrap(), - ) - .unwrap(); - - let mut buf = String::new(); - let _ = write!(buf, "{}", dt.format("%#z")).expect_err("parse-only formatter should fail"); - } - - #[test] - #[cfg(all(feature = "unstable-locales", feature = "alloc"))] - fn test_strftime_localized_korean() { - let dt = FixedOffset::east_opt(34200) - .unwrap() - .with_ymd_and_hms(2001, 7, 8, 0, 34, 59) - .unwrap() - .with_nanosecond(1_026_490_708) - .unwrap(); - - // date specifiers - assert_eq!(dt.format_localized("%b", Locale::ko_KR).to_string(), " 7ì›”"); - assert_eq!(dt.format_localized("%B", Locale::ko_KR).to_string(), "7ì›”"); - assert_eq!(dt.format_localized("%h", Locale::ko_KR).to_string(), " 7ì›”"); - assert_eq!(dt.format_localized("%a", Locale::ko_KR).to_string(), "ì¼"); - assert_eq!(dt.format_localized("%A", Locale::ko_KR).to_string(), "ì¼ìš”ì¼"); - assert_eq!(dt.format_localized("%D", Locale::ko_KR).to_string(), "07/08/01"); - assert_eq!(dt.format_localized("%x", Locale::ko_KR).to_string(), "2001ë…„ 07ì›” 08ì¼"); - assert_eq!(dt.format_localized("%F", Locale::ko_KR).to_string(), "2001-07-08"); - assert_eq!(dt.format_localized("%v", Locale::ko_KR).to_string(), " 8- 7ì›”-2001"); - assert_eq!(dt.format_localized("%r", Locale::ko_KR).to_string(), "오전 12시 34ë¶„ 60ì´ˆ"); - - // date & time specifiers - assert_eq!( - dt.format_localized("%c", Locale::ko_KR).to_string(), - "2001ë…„ 07ì›” 08ì¼ (ì¼) 오전 12시 34ë¶„ 60ì´ˆ" - ); - } - - #[test] - #[cfg(all(feature = "unstable-locales", feature = "alloc"))] - fn test_strftime_localized_japanese() { - let dt = FixedOffset::east_opt(34200) - .unwrap() - .with_ymd_and_hms(2001, 7, 8, 0, 34, 59) - .unwrap() - .with_nanosecond(1_026_490_708) - .unwrap(); - - // date specifiers - assert_eq!(dt.format_localized("%b", Locale::ja_JP).to_string(), " 7月"); - assert_eq!(dt.format_localized("%B", Locale::ja_JP).to_string(), "7月"); - assert_eq!(dt.format_localized("%h", Locale::ja_JP).to_string(), " 7月"); - assert_eq!(dt.format_localized("%a", Locale::ja_JP).to_string(), "æ—¥"); - assert_eq!(dt.format_localized("%A", Locale::ja_JP).to_string(), "日曜日"); - assert_eq!(dt.format_localized("%D", Locale::ja_JP).to_string(), "07/08/01"); - assert_eq!(dt.format_localized("%x", Locale::ja_JP).to_string(), "2001å¹´07月08æ—¥"); - assert_eq!(dt.format_localized("%F", Locale::ja_JP).to_string(), "2001-07-08"); - assert_eq!(dt.format_localized("%v", Locale::ja_JP).to_string(), " 8- 7月-2001"); - assert_eq!(dt.format_localized("%r", Locale::ja_JP).to_string(), "åˆå‰12時34分60ç§’"); - - // date & time specifiers - assert_eq!( - dt.format_localized("%c", Locale::ja_JP).to_string(), - "2001å¹´07月08æ—¥ 00時34分60ç§’" - ); - } - - #[test] - #[cfg(all(feature = "unstable-locales", feature = "alloc"))] - fn test_strftime_localized_time() { - let dt1 = Utc.with_ymd_and_hms(2024, 2, 9, 6, 54, 32).unwrap(); - let dt2 = Utc.with_ymd_and_hms(2024, 2, 9, 18, 54, 32).unwrap(); - // Some of these locales gave issues before pure-rust-locales 0.8.0 with chrono 0.4.27+ - assert_eq!(dt1.format_localized("%X", Locale::nl_NL).to_string(), "06:54:32"); - assert_eq!(dt2.format_localized("%X", Locale::nl_NL).to_string(), "18:54:32"); - assert_eq!(dt1.format_localized("%X", Locale::en_US).to_string(), "06:54:32 AM"); - assert_eq!(dt2.format_localized("%X", Locale::en_US).to_string(), "06:54:32 PM"); - assert_eq!(dt1.format_localized("%X", Locale::hy_AM).to_string(), "06:54:32"); - assert_eq!(dt2.format_localized("%X", Locale::hy_AM).to_string(), "18:54:32"); - assert_eq!(dt1.format_localized("%X", Locale::chr_US).to_string(), "06:54:32 áŒáŽ¾áŽ´"); - assert_eq!(dt2.format_localized("%X", Locale::chr_US).to_string(), "06:54:32 á’Ꭿá±áŽ¢á—á¢"); - } - - #[test] - #[cfg(all(feature = "unstable-locales", target_pointer_width = "64"))] - fn test_type_sizes() { - use core::mem::size_of; - assert_eq!(size_of::(), 24); - assert_eq!(size_of::(), 56); - assert_eq!(size_of::(), 2); - } - - #[test] - #[cfg(all(feature = "unstable-locales", target_pointer_width = "32"))] - fn test_type_sizes() { - use core::mem::size_of; - assert_eq!(size_of::(), 12); - assert_eq!(size_of::(), 28); - assert_eq!(size_of::(), 2); - } - - #[test] - #[cfg(any(feature = "alloc", feature = "std"))] - fn test_strftime_parse() { - let fmt_str = StrftimeItems::new("%Y-%m-%dT%H:%M:%S%z"); - let fmt_items = fmt_str.parse().unwrap(); - let dt = Utc.with_ymd_and_hms(2014, 5, 7, 12, 34, 56).unwrap(); - assert_eq!(&dt.format_with_items(fmt_items.iter()).to_string(), "2014-05-07T12:34:56+0000"); - } - - #[test] - #[cfg(any(feature = "alloc", feature = "std"))] - fn test_strftime_parse_lenient() { - let fmt_str = StrftimeItems::new_lenient("%Y-%m-%dT%H:%M:%S%z%Q%.2f%%%"); - let fmt_items = fmt_str.parse().unwrap(); - let dt = Utc.with_ymd_and_hms(2014, 5, 7, 12, 34, 56).unwrap(); - assert_eq!( - &dt.format_with_items(fmt_items.iter()).to_string(), - "2014-05-07T12:34:56+0000%Q%.2f%%" - ); - } - - /// Regression test for https://github.com/chronotope/chrono/issues/1725 - #[test] - #[cfg(any(feature = "alloc", feature = "std"))] - fn test_finite() { - let mut i = 0; - for item in StrftimeItems::new("%2f") { - println!("{:?}", item); - i += 1; - if i > 10 { - panic!("infinite loop"); - } - } - } -} diff --git a/chrono-0.4.44/src/lib.rs b/chrono-0.4.44/src/lib.rs deleted file mode 100644 index 0e942e02d5..0000000000 --- a/chrono-0.4.44/src/lib.rs +++ /dev/null @@ -1,743 +0,0 @@ -//! # Chrono: Date and Time for Rust -//! -//! Chrono aims to provide all functionality needed to do correct operations on dates and times in -//! the [proleptic Gregorian calendar]: -//! -//! * The [`DateTime`] type is timezone-aware by default, with separate timezone-naive types. -//! * Operations that may produce an invalid or ambiguous date and time return `Option` or -//! [`MappedLocalTime`]. -//! * Configurable parsing and formatting with a `strftime` inspired date and time formatting -//! syntax. -//! * The [`Local`] timezone works with the current timezone of the OS. -//! * Types and operations are implemented to be reasonably efficient. -//! -//! Timezone data is not shipped with chrono by default to limit binary sizes. Use the companion -//! crate [Chrono-TZ] or [`tzfile`] for full timezone support. -//! -//! [proleptic Gregorian calendar]: https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar -//! [Chrono-TZ]: https://crates.io/crates/chrono-tz -//! [`tzfile`]: https://crates.io/crates/tzfile -//! -//! ### Features -//! -//! Chrono supports various runtime environments and operating systems, and has several features -//! that may be enabled or disabled. -//! -//! Default features: -//! -//! - `alloc`: Enable features that depend on allocation (primarily string formatting). -//! - `std`: Enables functionality that depends on the standard library. This is a superset of -//! `alloc` and adds interoperation with standard library types and traits. -//! - `clock`: Enables reading the local timezone (`Local`). This is a superset of `now`. -//! - `now`: Enables reading the system time (`now`). -//! - `wasmbind`: Interface with the JS Date API for the `wasm32` target. -//! -//! Optional features: -//! -//! - `serde`: Enable serialization/deserialization via [serde]. -//! - `rkyv`: Deprecated, use the `rkyv-*` features. -//! - `rkyv-16`: Enable serialization/deserialization via [rkyv], -//! using 16-bit integers for integral `*size` types. -//! - `rkyv-32`: Enable serialization/deserialization via [rkyv], -//! using 32-bit integers for integral `*size` types. -//! - `rkyv-64`: Enable serialization/deserialization via [rkyv], -//! using 64-bit integers for integral `*size` types. -//! - `rkyv-validation`: Enable rkyv validation support using `bytecheck`. -//! - `arbitrary`: Construct arbitrary instances of a type with the Arbitrary crate. -//! - `unstable-locales`: Enable localization. This adds various methods with a `_localized` suffix. -//! The implementation and API may change or even be removed in a patch release. Feedback welcome. -//! - `oldtime`: This feature no longer has any effect; it used to offer compatibility with the -//! `time` 0.1 crate. -//! -//! Note: The `rkyv{,-16,-32,-64}` features are mutually exclusive. -//! -//! See the [cargo docs] for examples of specifying features. -//! -//! [serde]: https://github.com/serde-rs/serde -//! [rkyv]: https://github.com/rkyv/rkyv -//! [cargo docs]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#choosing-features -//! -//! ## Overview -//! -//! ### Time delta / Duration -//! -//! Chrono has a [`TimeDelta`] type to represent the magnitude of a time span. This is an "accurate" -//! duration represented as seconds and nanoseconds, and does not represent "nominal" components -//! such as days or months. -//! -//! The [`TimeDelta`] type was previously named `Duration` (and is still available as a type alias -//! with that name). A notable difference with the similar [`core::time::Duration`] is that it is a -//! signed value instead of unsigned. -//! -//! Chrono currently only supports a small number of operations with [`core::time::Duration`]. -//! You can convert between both types with the [`TimeDelta::from_std`] and [`TimeDelta::to_std`] -//! methods. -//! -//! ### Date and Time -//! -//! Chrono provides a [`DateTime`] type to represent a date and a time in a timezone. -//! -//! For more abstract moment-in-time tracking such as internal timekeeping that is unconcerned with -//! timezones, consider [`std::time::SystemTime`], which tracks your system clock, or -//! [`std::time::Instant`], which is an opaque but monotonically-increasing representation of a -//! moment in time. -//! -//! [`DateTime`] is timezone-aware and must be constructed from a [`TimeZone`] object, which defines -//! how the local date is converted to and back from the UTC date. -//! There are three well-known [`TimeZone`] implementations: -//! -//! * [`Utc`] specifies the UTC time zone. It is most efficient. -//! -//! * [`Local`] specifies the system local time zone. -//! -//! * [`FixedOffset`] specifies an arbitrary, fixed time zone such as UTC+09:00 or UTC-10:30. -//! This often results from the parsed textual date and time. Since it stores the most information -//! and does not depend on the system environment, you would want to normalize other `TimeZone`s -//! into this type. -//! -//! [`DateTime`]s with different [`TimeZone`] types are distinct and do not mix, but can be -//! converted to each other using the [`DateTime::with_timezone`] method. -//! -//! You can get the current date and time in the UTC time zone ([`Utc::now()`]) or in the local time -//! zone ([`Local::now()`]). -//! -//! ``` -//! # #[cfg(feature = "now")] { -//! use chrono::prelude::*; -//! -//! let utc: DateTime = Utc::now(); // e.g. `2014-11-28T12:45:59.324310806Z` -//! # let _ = utc; -//! # } -//! ``` -//! -//! ``` -//! # #[cfg(feature = "clock")] { -//! use chrono::prelude::*; -//! -//! let local: DateTime = Local::now(); // e.g. `2014-11-28T21:45:59.324310806+09:00` -//! # let _ = local; -//! # } -//! ``` -//! -//! Alternatively, you can create your own date and time. This is a bit verbose due to Rust's lack -//! of function and method overloading, but in turn we get a rich combination of initialization -//! methods. -//! -//! ``` -//! use chrono::offset::MappedLocalTime; -//! use chrono::prelude::*; -//! -//! # fn doctest() -> Option<()> { -//! -//! let dt = Utc.with_ymd_and_hms(2014, 7, 8, 9, 10, 11).unwrap(); // `2014-07-08T09:10:11Z` -//! assert_eq!( -//! dt, -//! NaiveDate::from_ymd_opt(2014, 7, 8)? -//! .and_hms_opt(9, 10, 11)? -//! .and_utc() -//! ); -//! -//! // July 8 is 188th day of the year 2014 (`o` for "ordinal") -//! assert_eq!(dt, NaiveDate::from_yo_opt(2014, 189)?.and_hms_opt(9, 10, 11)?.and_utc()); -//! // July 8 is Tuesday in ISO week 28 of the year 2014. -//! assert_eq!( -//! dt, -//! NaiveDate::from_isoywd_opt(2014, 28, Weekday::Tue)?.and_hms_opt(9, 10, 11)?.and_utc() -//! ); -//! -//! let dt = NaiveDate::from_ymd_opt(2014, 7, 8)? -//! .and_hms_milli_opt(9, 10, 11, 12)? -//! .and_utc(); // `2014-07-08T09:10:11.012Z` -//! assert_eq!( -//! dt, -//! NaiveDate::from_ymd_opt(2014, 7, 8)? -//! .and_hms_micro_opt(9, 10, 11, 12_000)? -//! .and_utc() -//! ); -//! assert_eq!( -//! dt, -//! NaiveDate::from_ymd_opt(2014, 7, 8)? -//! .and_hms_nano_opt(9, 10, 11, 12_000_000)? -//! .and_utc() -//! ); -//! -//! // dynamic verification -//! assert_eq!( -//! Utc.with_ymd_and_hms(2014, 7, 8, 21, 15, 33), -//! MappedLocalTime::Single( -//! NaiveDate::from_ymd_opt(2014, 7, 8)?.and_hms_opt(21, 15, 33)?.and_utc() -//! ) -//! ); -//! assert_eq!(Utc.with_ymd_and_hms(2014, 7, 8, 80, 15, 33), MappedLocalTime::None); -//! assert_eq!(Utc.with_ymd_and_hms(2014, 7, 38, 21, 15, 33), MappedLocalTime::None); -//! -//! # #[cfg(feature = "clock")] { -//! // other time zone objects can be used to construct a local datetime. -//! // obviously, `local_dt` is normally different from `dt`, but `fixed_dt` should be identical. -//! let local_dt = Local -//! .from_local_datetime( -//! &NaiveDate::from_ymd_opt(2014, 7, 8).unwrap().and_hms_milli_opt(9, 10, 11, 12).unwrap(), -//! ) -//! .unwrap(); -//! let fixed_dt = FixedOffset::east_opt(9 * 3600) -//! .unwrap() -//! .from_local_datetime( -//! &NaiveDate::from_ymd_opt(2014, 7, 8) -//! .unwrap() -//! .and_hms_milli_opt(18, 10, 11, 12) -//! .unwrap(), -//! ) -//! .unwrap(); -//! assert_eq!(dt, fixed_dt); -//! # let _ = local_dt; -//! # } -//! # Some(()) -//! # } -//! # doctest().unwrap(); -//! ``` -//! -//! Various properties are available to the date and time, and can be altered individually. Most of -//! them are defined in the traits [`Datelike`] and [`Timelike`] which you should `use` before. -//! Addition and subtraction is also supported. -//! The following illustrates most supported operations to the date and time: -//! -//! ```rust -//! use chrono::prelude::*; -//! use chrono::TimeDelta; -//! -//! // assume this returned `2014-11-28T21:45:59.324310806+09:00`: -//! let dt = FixedOffset::east_opt(9 * 3600) -//! .unwrap() -//! .from_local_datetime( -//! &NaiveDate::from_ymd_opt(2014, 11, 28) -//! .unwrap() -//! .and_hms_nano_opt(21, 45, 59, 324310806) -//! .unwrap(), -//! ) -//! .unwrap(); -//! -//! // property accessors -//! assert_eq!((dt.year(), dt.month(), dt.day()), (2014, 11, 28)); -//! assert_eq!((dt.month0(), dt.day0()), (10, 27)); // for unfortunate souls -//! assert_eq!((dt.hour(), dt.minute(), dt.second()), (21, 45, 59)); -//! assert_eq!(dt.weekday(), Weekday::Fri); -//! assert_eq!(dt.weekday().number_from_monday(), 5); // Mon=1, ..., Sun=7 -//! assert_eq!(dt.ordinal(), 332); // the day of year -//! assert_eq!(dt.num_days_from_ce(), 735565); // the number of days from and including Jan 1, 1 -//! -//! // time zone accessor and manipulation -//! assert_eq!(dt.offset().fix().local_minus_utc(), 9 * 3600); -//! assert_eq!(dt.timezone(), FixedOffset::east_opt(9 * 3600).unwrap()); -//! assert_eq!( -//! dt.with_timezone(&Utc), -//! NaiveDate::from_ymd_opt(2014, 11, 28) -//! .unwrap() -//! .and_hms_nano_opt(12, 45, 59, 324310806) -//! .unwrap() -//! .and_utc() -//! ); -//! -//! // a sample of property manipulations (validates dynamically) -//! assert_eq!(dt.with_day(29).unwrap().weekday(), Weekday::Sat); // 2014-11-29 is Saturday -//! assert_eq!(dt.with_day(32), None); -//! assert_eq!(dt.with_year(-300).unwrap().num_days_from_ce(), -109606); // November 29, 301 BCE -//! -//! // arithmetic operations -//! let dt1 = Utc.with_ymd_and_hms(2014, 11, 14, 8, 9, 10).unwrap(); -//! let dt2 = Utc.with_ymd_and_hms(2014, 11, 14, 10, 9, 8).unwrap(); -//! assert_eq!(dt1.signed_duration_since(dt2), TimeDelta::try_seconds(-2 * 3600 + 2).unwrap()); -//! assert_eq!(dt2.signed_duration_since(dt1), TimeDelta::try_seconds(2 * 3600 - 2).unwrap()); -//! assert_eq!( -//! Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap() -//! + TimeDelta::try_seconds(1_000_000_000).unwrap(), -//! Utc.with_ymd_and_hms(2001, 9, 9, 1, 46, 40).unwrap() -//! ); -//! assert_eq!( -//! Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap() -//! - TimeDelta::try_seconds(1_000_000_000).unwrap(), -//! Utc.with_ymd_and_hms(1938, 4, 24, 22, 13, 20).unwrap() -//! ); -//! ``` -//! -//! ### Formatting and Parsing -//! -//! Formatting is done via the [`format`](DateTime::format()) method, which format is equivalent to -//! the familiar `strftime` format. -//! -//! See [`format::strftime`](format::strftime#specifiers) documentation for full syntax and list of -//! specifiers. -//! -//! The default `to_string` method and `{:?}` specifier also give a reasonable representation. -//! Chrono also provides [`to_rfc2822`](DateTime::to_rfc2822) and -//! [`to_rfc3339`](DateTime::to_rfc3339) methods for well-known formats. -//! -//! Chrono now also provides date formatting in almost any language without the help of an -//! additional C library. This functionality is under the feature `unstable-locales`: -//! -//! ```toml -//! chrono = { version = "0.4", features = ["unstable-locales"] } -//! ``` -//! -//! The `unstable-locales` feature requires and implies at least the `alloc` feature. -//! -//! ```rust -//! # #[allow(unused_imports)] -//! use chrono::prelude::*; -//! -//! # #[cfg(all(feature = "unstable-locales", feature = "alloc"))] -//! # fn test() { -//! let dt = Utc.with_ymd_and_hms(2014, 11, 28, 12, 0, 9).unwrap(); -//! assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2014-11-28 12:00:09"); -//! assert_eq!(dt.format("%a %b %e %T %Y").to_string(), "Fri Nov 28 12:00:09 2014"); -//! assert_eq!( -//! dt.format_localized("%A %e %B %Y, %T", Locale::fr_BE).to_string(), -//! "vendredi 28 novembre 2014, 12:00:09" -//! ); -//! -//! assert_eq!(dt.format("%a %b %e %T %Y").to_string(), dt.format("%c").to_string()); -//! assert_eq!(dt.to_string(), "2014-11-28 12:00:09 UTC"); -//! assert_eq!(dt.to_rfc2822(), "Fri, 28 Nov 2014 12:00:09 +0000"); -//! assert_eq!(dt.to_rfc3339(), "2014-11-28T12:00:09+00:00"); -//! assert_eq!(format!("{:?}", dt), "2014-11-28T12:00:09Z"); -//! -//! // Note that milli/nanoseconds are only printed if they are non-zero -//! let dt_nano = NaiveDate::from_ymd_opt(2014, 11, 28) -//! .unwrap() -//! .and_hms_nano_opt(12, 0, 9, 1) -//! .unwrap() -//! .and_utc(); -//! assert_eq!(format!("{:?}", dt_nano), "2014-11-28T12:00:09.000000001Z"); -//! # } -//! # #[cfg(not(all(feature = "unstable-locales", feature = "alloc")))] -//! # fn test() {} -//! # if cfg!(all(feature = "unstable-locales", feature = "alloc")) { -//! # test(); -//! # } -//! ``` -//! -//! Parsing can be done with two methods: -//! -//! 1. The standard [`FromStr`](std::str::FromStr) trait (and [`parse`](str::parse) method on a -//! string) can be used for parsing `DateTime`, `DateTime` and -//! `DateTime` values. This parses what the `{:?}` ([`std::fmt::Debug`] format specifier -//! prints, and requires the offset to be present. -//! -//! 2. [`DateTime::parse_from_str`] parses a date and time with offsets and returns -//! `DateTime`. This should be used when the offset is a part of input and the -//! caller cannot guess that. It *cannot* be used when the offset can be missing. -//! [`DateTime::parse_from_rfc2822`] and [`DateTime::parse_from_rfc3339`] are similar but for -//! well-known formats. -//! -//! More detailed control over the parsing process is available via [`format`](mod@format) module. -//! -//! ```rust -//! use chrono::prelude::*; -//! -//! let dt = Utc.with_ymd_and_hms(2014, 11, 28, 12, 0, 9).unwrap(); -//! let fixed_dt = dt.with_timezone(&FixedOffset::east_opt(9 * 3600).unwrap()); -//! -//! // method 1 -//! assert_eq!("2014-11-28T12:00:09Z".parse::>(), Ok(dt.clone())); -//! assert_eq!("2014-11-28T21:00:09+09:00".parse::>(), Ok(dt.clone())); -//! assert_eq!("2014-11-28T21:00:09+09:00".parse::>(), Ok(fixed_dt.clone())); -//! -//! // method 2 -//! assert_eq!( -//! DateTime::parse_from_str("2014-11-28 21:00:09 +09:00", "%Y-%m-%d %H:%M:%S %z"), -//! Ok(fixed_dt.clone()) -//! ); -//! assert_eq!( -//! DateTime::parse_from_rfc2822("Fri, 28 Nov 2014 21:00:09 +0900"), -//! Ok(fixed_dt.clone()) -//! ); -//! assert_eq!(DateTime::parse_from_rfc3339("2014-11-28T21:00:09+09:00"), Ok(fixed_dt.clone())); -//! -//! // oops, the year is missing! -//! assert!(DateTime::parse_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err()); -//! // oops, the format string does not include the year at all! -//! assert!(DateTime::parse_from_str("Fri Nov 28 12:00:09", "%a %b %e %T").is_err()); -//! // oops, the weekday is incorrect! -//! assert!(DateTime::parse_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err()); -//! ``` -//! -//! Again: See [`format::strftime`](format::strftime#specifiers) documentation for full syntax and -//! list of specifiers. -//! -//! ### Conversion from and to EPOCH timestamps -//! -//! Use [`DateTime::from_timestamp(seconds, nanoseconds)`](DateTime::from_timestamp) -//! to construct a [`DateTime`] from a UNIX timestamp -//! (seconds, nanoseconds that passed since January 1st 1970). -//! -//! Use [`DateTime.timestamp`](DateTime::timestamp) to get the timestamp (in seconds) -//! from a [`DateTime`]. Additionally, you can use -//! [`DateTime.timestamp_subsec_nanos`](DateTime::timestamp_subsec_nanos) -//! to get the number of additional number of nanoseconds. -//! -//! ``` -//! # #[cfg(feature = "alloc")] { -//! // We need the trait in scope to use Utc::timestamp(). -//! use chrono::{DateTime, Utc}; -//! -//! // Construct a datetime from epoch: -//! let dt: DateTime = DateTime::from_timestamp_secs(1_500_000_000).unwrap(); -//! assert_eq!(dt.to_rfc2822(), "Fri, 14 Jul 2017 02:40:00 +0000"); -//! -//! // Get epoch value from a datetime: -//! let dt = DateTime::parse_from_rfc2822("Fri, 14 Jul 2017 02:40:00 +0000").unwrap(); -//! assert_eq!(dt.timestamp(), 1_500_000_000); -//! # } -//! ``` -//! -//! ### Naive date and time -//! -//! Chrono provides naive counterparts to `Date`, (non-existent) `Time` and `DateTime` as -//! [`NaiveDate`], [`NaiveTime`] and [`NaiveDateTime`] respectively. -//! -//! They have almost equivalent interfaces as their timezone-aware twins, but are not associated to -//! time zones obviously and can be quite low-level. They are mostly useful for building blocks for -//! higher-level types. -//! -//! Timezone-aware `DateTime` and `Date` types have two methods returning naive versions: -//! [`naive_local`](DateTime::naive_local) returns a view to the naive local time, -//! and [`naive_utc`](DateTime::naive_utc) returns a view to the naive UTC time. -//! -//! ## Limitations -//! -//! * Only the proleptic Gregorian calendar (i.e. extended to support older dates) is supported. -//! * Date types are limited to about +/- 262,000 years from the common epoch. -//! * Time types are limited to nanosecond accuracy. -//! * Leap seconds can be represented, but Chrono does not fully support them. -//! See [Leap Second Handling](NaiveTime#leap-second-handling). -//! -//! ## Rust version requirements -//! -//! The Minimum Supported Rust Version (MSRV) is currently **Rust 1.61.0**. -//! -//! The MSRV is explicitly tested in CI. It may be bumped in minor releases, but this is not done -//! lightly. -//! -//! ## Relation between chrono and time 0.1 -//! -//! Rust first had a `time` module added to `std` in its 0.7 release. It later moved to -//! `libextra`, and then to a `libtime` library shipped alongside the standard library. In 2014 -//! work on chrono started in order to provide a full-featured date and time library in Rust. -//! Some improvements from chrono made it into the standard library; notably, `chrono::Duration` -//! was included as `std::time::Duration` ([rust#15934]) in 2014. -//! -//! In preparation of Rust 1.0 at the end of 2014 `libtime` was moved out of the Rust distro and -//! into the `time` crate to eventually be redesigned ([rust#18832], [rust#18858]), like the -//! `num` and `rand` crates. Of course chrono kept its dependency on this `time` crate. `time` -//! started re-exporting `std::time::Duration` during this period. Later, the standard library was -//! changed to have a more limited unsigned `Duration` type ([rust#24920], [RFC 1040]), while the -//! `time` crate kept the full functionality with `time::Duration`. `time::Duration` had been a -//! part of chrono's public API. -//! -//! By 2016 `time` 0.1 lived under the `rust-lang-deprecated` organisation and was not actively -//! maintained ([time#136]). chrono absorbed the platform functionality and `Duration` type of the -//! `time` crate in [chrono#478] (the work started in [chrono#286]). In order to preserve -//! compatibility with downstream crates depending on `time` and `chrono` sharing a `Duration` -//! type, chrono kept depending on time 0.1. chrono offered the option to opt out of the `time` -//! dependency by disabling the `oldtime` feature (swapping it out for an effectively similar -//! chrono type). In 2019, @jhpratt took over maintenance on the `time` crate and released what -//! amounts to a new crate as `time` 0.2. -//! -//! [rust#15934]: https://github.com/rust-lang/rust/pull/15934 -//! [rust#18832]: https://github.com/rust-lang/rust/pull/18832#issuecomment-62448221 -//! [rust#18858]: https://github.com/rust-lang/rust/pull/18858 -//! [rust#24920]: https://github.com/rust-lang/rust/pull/24920 -//! [RFC 1040]: https://rust-lang.github.io/rfcs/1040-duration-reform.html -//! [time#136]: https://github.com/time-rs/time/issues/136 -//! [chrono#286]: https://github.com/chronotope/chrono/pull/286 -//! [chrono#478]: https://github.com/chronotope/chrono/pull/478 -//! -//! ## Security advisories -//! -//! In November of 2020 [CVE-2020-26235] and [RUSTSEC-2020-0071] were opened against the `time` crate. -//! @quininer had found that calls to `localtime_r` may be unsound ([chrono#499]). Eventually, almost -//! a year later, this was also made into a security advisory against chrono as [RUSTSEC-2020-0159], -//! which had platform code similar to `time`. -//! -//! On Unix-like systems a process is given a timezone id or description via the `TZ` environment -//! variable. We need this timezone data to calculate the current local time from a value that is -//! in UTC, such as the time from the system clock. `time` 0.1 and chrono used the POSIX function -//! `localtime_r` to do the conversion to local time, which reads the `TZ` variable. -//! -//! Rust assumes the environment to be writable and uses locks to access it from multiple threads. -//! Some other programming languages and libraries use similar locking strategies, but these are -//! typically not shared across languages. More importantly, POSIX declares modifying the -//! environment in a multi-threaded process as unsafe, and `getenv` in libc can't be changed to -//! take a lock because it returns a pointer to the data (see [rust#27970] for more discussion). -//! -//! Since version 4.20 chrono no longer uses `localtime_r`, instead using Rust code to query the -//! timezone (from the `TZ` variable or via `iana-time-zone` as a fallback) and work with data -//! from the system timezone database directly. The code for this was forked from the [tz-rs crate] -//! by @x-hgg-x. As such, chrono now respects the Rust lock when reading the `TZ` environment -//! variable. In general, code should avoid modifying the environment. -//! -//! [CVE-2020-26235]: https://nvd.nist.gov/vuln/detail/CVE-2020-26235 -//! [RUSTSEC-2020-0071]: https://rustsec.org/advisories/RUSTSEC-2020-0071 -//! [chrono#499]: https://github.com/chronotope/chrono/pull/499 -//! [RUSTSEC-2020-0159]: https://rustsec.org/advisories/RUSTSEC-2020-0159.html -//! [rust#27970]: https://github.com/rust-lang/rust/issues/27970 -//! [chrono#677]: https://github.com/chronotope/chrono/pull/677 -//! [tz-rs crate]: https://crates.io/crates/tz-rs -//! -//! ## Removing time 0.1 -//! -//! Because time 0.1 has been unmaintained for years, however, the security advisory mentioned -//! above has not been addressed. While chrono maintainers were careful not to break backwards -//! compatibility with the `time::Duration` type, there has been a long stream of issues from -//! users inquiring about the time 0.1 dependency with the vulnerability. We investigated the -//! potential breakage of removing the time 0.1 dependency in [chrono#1095] using a crater-like -//! experiment and determined that the potential for breaking (public) dependencies is very low. -//! We reached out to those few crates that did still depend on compatibility with time 0.1. -//! -//! As such, for chrono 0.4.30 we have decided to swap out the time 0.1 `Duration` implementation -//! for a local one that will offer a strict superset of the existing API going forward. This -//! will prevent most downstream users from being affected by the security vulnerability in time -//! 0.1 while minimizing the ecosystem impact of semver-incompatible version churn. -//! -//! [chrono#1095]: https://github.com/chronotope/chrono/pull/1095 - -#![doc(html_root_url = "https://docs.rs/chrono/latest/", test(attr(deny(warnings))))] -#![warn(unreachable_pub)] -#![warn(clippy::tests_outside_test_module)] -#![cfg_attr(not(any(feature = "std", test)), no_std)] -#![cfg_attr(docsrs, feature(doc_cfg))] - -#[cfg(feature = "alloc")] -extern crate alloc; - -mod time_delta; -#[doc(no_inline)] -#[cfg(any(feature = "std", feature = "core-error"))] -pub use time_delta::OutOfRangeError; -pub use time_delta::TimeDelta; - -/// Alias of [`TimeDelta`]. -pub type Duration = TimeDelta; - -use core::fmt; - -/// A convenience module appropriate for glob imports (`use chrono::prelude::*;`). -pub mod prelude { - #[allow(deprecated)] - pub use crate::Date; - #[cfg(feature = "clock")] - pub use crate::Local; - #[cfg(all(feature = "unstable-locales", feature = "alloc"))] - pub use crate::Locale; - pub use crate::SubsecRound; - pub use crate::{DateTime, SecondsFormat}; - pub use crate::{Datelike, Month, Timelike, Weekday}; - pub use crate::{FixedOffset, Utc}; - pub use crate::{NaiveDate, NaiveDateTime, NaiveTime}; - pub use crate::{Offset, TimeZone}; -} - -mod date; -#[allow(deprecated)] -pub use date::Date; -#[doc(no_inline)] -#[allow(deprecated)] -pub use date::{MAX_DATE, MIN_DATE}; - -mod datetime; -pub use datetime::DateTime; -#[allow(deprecated)] -#[doc(no_inline)] -pub use datetime::{MAX_DATETIME, MIN_DATETIME}; - -pub mod format; -/// L10n locales. -#[cfg(feature = "unstable-locales")] -pub use format::Locale; -pub use format::{ParseError, ParseResult, SecondsFormat}; - -pub mod naive; -#[doc(inline)] -pub use naive::{Days, NaiveDate, NaiveDateTime, NaiveTime}; -pub use naive::{IsoWeek, NaiveWeek}; - -pub mod offset; -#[cfg(feature = "clock")] -#[doc(inline)] -pub use offset::Local; -#[doc(hidden)] -pub use offset::LocalResult; -pub use offset::MappedLocalTime; -#[doc(inline)] -pub use offset::{FixedOffset, Offset, TimeZone, Utc}; - -pub mod round; -pub use round::{DurationRound, RoundingError, SubsecRound}; - -mod weekday; -#[doc(no_inline)] -pub use weekday::ParseWeekdayError; -pub use weekday::Weekday; - -mod weekday_set; -pub use weekday_set::WeekdaySet; - -mod month; -#[doc(no_inline)] -pub use month::ParseMonthError; -pub use month::{Month, Months}; - -mod traits; -pub use traits::{Datelike, Timelike}; - -#[cfg(feature = "__internal_bench")] -#[doc(hidden)] -pub use naive::__BenchYearFlags; - -/// Serialization/Deserialization with serde -/// -/// The [`DateTime`] type has default implementations for (de)serializing to/from the [RFC 3339] -/// format. This module provides alternatives for serializing to timestamps. -/// -/// The alternatives are for use with serde's [`with` annotation] combined with the module name. -/// Alternatively the individual `serialize` and `deserialize` functions in each module can be used -/// with serde's [`serialize_with`] and [`deserialize_with`] annotations. -/// -/// *Available on crate feature 'serde' only.* -/// -/// [RFC 3339]: https://tools.ietf.org/html/rfc3339 -/// [`with` annotation]: https://serde.rs/field-attrs.html#with -/// [`serialize_with`]: https://serde.rs/field-attrs.html#serialize_with -/// [`deserialize_with`]: https://serde.rs/field-attrs.html#deserialize_with -#[cfg(feature = "serde")] -pub mod serde { - use core::fmt; - use serde::de; - - pub use super::datetime::serde::*; - - /// Create a custom `de::Error` with `SerdeError::InvalidTimestamp`. - pub(crate) fn invalid_ts(value: T) -> E - where - E: de::Error, - T: fmt::Display, - { - E::custom(SerdeError::InvalidTimestamp(value)) - } - - enum SerdeError { - InvalidTimestamp(T), - } - - impl fmt::Display for SerdeError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - SerdeError::InvalidTimestamp(ts) => { - write!(f, "value is not a legal timestamp: {ts}") - } - } - } - } -} - -/// Zero-copy serialization/deserialization with rkyv. -/// -/// This module re-exports the `Archived*` versions of chrono's types. -#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))] -pub mod rkyv { - pub use crate::datetime::ArchivedDateTime; - pub use crate::month::ArchivedMonth; - pub use crate::naive::date::ArchivedNaiveDate; - pub use crate::naive::datetime::ArchivedNaiveDateTime; - pub use crate::naive::isoweek::ArchivedIsoWeek; - pub use crate::naive::time::ArchivedNaiveTime; - pub use crate::offset::fixed::ArchivedFixedOffset; - #[cfg(feature = "clock")] - pub use crate::offset::local::ArchivedLocal; - pub use crate::offset::utc::ArchivedUtc; - pub use crate::time_delta::ArchivedTimeDelta; - pub use crate::weekday::ArchivedWeekday; - - /// Alias of [`ArchivedTimeDelta`] - pub type ArchivedDuration = ArchivedTimeDelta; -} - -/// Out of range error type used in various converting APIs -#[derive(Clone, Copy, Hash, PartialEq, Eq)] -pub struct OutOfRange { - _private: (), -} - -impl OutOfRange { - const fn new() -> OutOfRange { - OutOfRange { _private: () } - } -} - -impl fmt::Display for OutOfRange { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "out of range") - } -} - -impl fmt::Debug for OutOfRange { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "out of range") - } -} - -#[cfg(feature = "defmt")] -impl defmt::Format for OutOfRange { - fn format(&self, fmt: defmt::Formatter) { - defmt::write!(fmt, "out of range"); - } -} - -#[cfg(feature = "std")] -impl std::error::Error for OutOfRange {} - -#[cfg(all(not(feature = "std"), feature = "core-error"))] -impl core::error::Error for OutOfRange {} - -/// Workaround because `?` is not (yet) available in const context. -#[macro_export] -#[doc(hidden)] -macro_rules! try_opt { - ($e:expr) => { - match $e { - Some(v) => v, - None => return None, - } - }; -} - -/// Workaround because `.expect()` is not (yet) available in const context. -pub(crate) const fn expect(opt: Option, msg: &str) -> T { - match opt { - Some(val) => val, - None => panic!("{}", msg), - } -} - -#[cfg(test)] -mod tests { - #[cfg(feature = "clock")] - use crate::{DateTime, FixedOffset, Local, NaiveDate, NaiveDateTime, NaiveTime, Utc}; - - #[test] - #[allow(deprecated)] - #[cfg(feature = "clock")] - fn test_type_sizes() { - use core::mem::size_of; - assert_eq!(size_of::(), 4); - assert_eq!(size_of::>(), 4); - assert_eq!(size_of::(), 8); - assert_eq!(size_of::>(), 12); - assert_eq!(size_of::(), 12); - assert_eq!(size_of::>(), 12); - - assert_eq!(size_of::>(), 12); - assert_eq!(size_of::>(), 16); - assert_eq!(size_of::>(), 16); - assert_eq!(size_of::>>(), 16); - } -} diff --git a/chrono-0.4.44/src/month.rs b/chrono-0.4.44/src/month.rs deleted file mode 100644 index 5e51d7f73c..0000000000 --- a/chrono-0.4.44/src/month.rs +++ /dev/null @@ -1,484 +0,0 @@ -use core::fmt; - -#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))] -use rkyv::{Archive, Deserialize, Serialize}; - -use crate::OutOfRange; -use crate::naive::NaiveDate; - -/// The month of the year. -/// -/// This enum is just a convenience implementation. -/// The month in dates created by DateLike objects does not return this enum. -/// -/// It is possible to convert from a date to a month independently -/// ``` -/// use chrono::prelude::*; -/// let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap(); -/// // `2019-10-28T09:10:11Z` -/// let month = Month::try_from(u8::try_from(date.month()).unwrap()).ok(); -/// assert_eq!(month, Some(Month::October)) -/// ``` -/// Or from a Month to an integer usable by dates -/// ``` -/// # use chrono::prelude::*; -/// let month = Month::January; -/// let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap(); -/// assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28)); -/// ``` -/// Allows mapping from and to month, from 1-January to 12-December. -/// Can be Serialized/Deserialized with serde -// Actual implementation is zero-indexed, API intended as 1-indexed for more intuitive behavior. -#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash, PartialOrd, Ord)] -#[cfg_attr( - any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"), - derive(Archive, Deserialize, Serialize), - archive(compare(PartialEq, PartialOrd)), - archive_attr(derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)) -)] -#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] -#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(arbitrary::Arbitrary))] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Month { - /// January - January = 0, - /// February - February = 1, - /// March - March = 2, - /// April - April = 3, - /// May - May = 4, - /// June - June = 5, - /// July - July = 6, - /// August - August = 7, - /// September - September = 8, - /// October - October = 9, - /// November - November = 10, - /// December - December = 11, -} - -impl Month { - /// The next month. - /// - /// `m`: | `January` | `February` | `...` | `December` - /// ----------- | --------- | ---------- | --- | --------- - /// `m.succ()`: | `February` | `March` | `...` | `January` - #[inline] - #[must_use] - pub const fn succ(&self) -> Month { - match *self { - Month::January => Month::February, - Month::February => Month::March, - Month::March => Month::April, - Month::April => Month::May, - Month::May => Month::June, - Month::June => Month::July, - Month::July => Month::August, - Month::August => Month::September, - Month::September => Month::October, - Month::October => Month::November, - Month::November => Month::December, - Month::December => Month::January, - } - } - - /// The previous month. - /// - /// `m`: | `January` | `February` | `...` | `December` - /// ----------- | --------- | ---------- | --- | --------- - /// `m.pred()`: | `December` | `January` | `...` | `November` - #[inline] - #[must_use] - pub const fn pred(&self) -> Month { - match *self { - Month::January => Month::December, - Month::February => Month::January, - Month::March => Month::February, - Month::April => Month::March, - Month::May => Month::April, - Month::June => Month::May, - Month::July => Month::June, - Month::August => Month::July, - Month::September => Month::August, - Month::October => Month::September, - Month::November => Month::October, - Month::December => Month::November, - } - } - - /// Returns a month-of-year number starting from January = 1. - /// - /// `m`: | `January` | `February` | `...` | `December` - /// -------------------------| --------- | ---------- | --- | ----- - /// `m.number_from_month()`: | 1 | 2 | `...` | 12 - #[inline] - #[must_use] - pub const fn number_from_month(&self) -> u32 { - match *self { - Month::January => 1, - Month::February => 2, - Month::March => 3, - Month::April => 4, - Month::May => 5, - Month::June => 6, - Month::July => 7, - Month::August => 8, - Month::September => 9, - Month::October => 10, - Month::November => 11, - Month::December => 12, - } - } - - /// Get the name of the month - /// - /// ``` - /// use chrono::Month; - /// - /// assert_eq!(Month::January.name(), "January") - /// ``` - #[must_use] - pub const fn name(&self) -> &'static str { - match *self { - Month::January => "January", - Month::February => "February", - Month::March => "March", - Month::April => "April", - Month::May => "May", - Month::June => "June", - Month::July => "July", - Month::August => "August", - Month::September => "September", - Month::October => "October", - Month::November => "November", - Month::December => "December", - } - } - - /// Get the length in days of the month - /// - /// Yields `None` if `year` is out of range for `NaiveDate`. - pub fn num_days(&self, year: i32) -> Option { - Some(match *self { - Month::January => 31, - Month::February => match NaiveDate::from_ymd_opt(year, 2, 1)?.leap_year() { - true => 29, - false => 28, - }, - Month::March => 31, - Month::April => 30, - Month::May => 31, - Month::June => 30, - Month::July => 31, - Month::August => 31, - Month::September => 30, - Month::October => 31, - Month::November => 30, - Month::December => 31, - }) - } -} - -impl TryFrom for Month { - type Error = OutOfRange; - - fn try_from(value: u8) -> Result { - match value { - 1 => Ok(Month::January), - 2 => Ok(Month::February), - 3 => Ok(Month::March), - 4 => Ok(Month::April), - 5 => Ok(Month::May), - 6 => Ok(Month::June), - 7 => Ok(Month::July), - 8 => Ok(Month::August), - 9 => Ok(Month::September), - 10 => Ok(Month::October), - 11 => Ok(Month::November), - 12 => Ok(Month::December), - _ => Err(OutOfRange::new()), - } - } -} - -impl num_traits::FromPrimitive for Month { - /// Returns an `Option` from a i64, assuming a 1-index, January = 1. - /// - /// `Month::from_i64(n: i64)`: | `1` | `2` | ... | `12` - /// ---------------------------| -------------------- | --------------------- | ... | ----- - /// ``: | Some(Month::January) | Some(Month::February) | ... | Some(Month::December) - #[inline] - fn from_u64(n: u64) -> Option { - Self::from_u32(n as u32) - } - - #[inline] - fn from_i64(n: i64) -> Option { - Self::from_u32(n as u32) - } - - #[inline] - fn from_u32(n: u32) -> Option { - match n { - 1 => Some(Month::January), - 2 => Some(Month::February), - 3 => Some(Month::March), - 4 => Some(Month::April), - 5 => Some(Month::May), - 6 => Some(Month::June), - 7 => Some(Month::July), - 8 => Some(Month::August), - 9 => Some(Month::September), - 10 => Some(Month::October), - 11 => Some(Month::November), - 12 => Some(Month::December), - _ => None, - } - } -} - -/// A duration in calendar months -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] -#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(arbitrary::Arbitrary))] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Months(pub(crate) u32); - -impl Months { - /// Construct a new `Months` from a number of months - pub const fn new(num: u32) -> Self { - Self(num) - } - - /// Returns the total number of months in the `Months` instance. - #[inline] - pub const fn as_u32(&self) -> u32 { - self.0 - } -} - -/// An error resulting from reading `` value with `FromStr`. -#[derive(Clone, PartialEq, Eq)] -pub struct ParseMonthError { - pub(crate) _dummy: (), -} - -#[cfg(feature = "std")] -impl std::error::Error for ParseMonthError {} - -#[cfg(all(not(feature = "std"), feature = "core-error"))] -impl core::error::Error for ParseMonthError {} - -impl fmt::Display for ParseMonthError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "ParseMonthError {{ .. }}") - } -} - -impl fmt::Debug for ParseMonthError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "ParseMonthError {{ .. }}") - } -} - -#[cfg(feature = "defmt")] -impl defmt::Format for ParseMonthError { - fn format(&self, fmt: defmt::Formatter) { - defmt::write!(fmt, "ParseMonthError {{ .. }}") - } -} - -#[cfg(feature = "serde")] -mod month_serde { - use super::Month; - use serde::{de, ser}; - - use core::fmt; - - impl ser::Serialize for Month { - fn serialize(&self, serializer: S) -> Result - where - S: ser::Serializer, - { - serializer.collect_str(self.name()) - } - } - - struct MonthVisitor; - - impl de::Visitor<'_> for MonthVisitor { - type Value = Month; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("Month") - } - - fn visit_str(self, value: &str) -> Result - where - E: de::Error, - { - value.parse().map_err(|_| E::custom("short (3-letter) or full month names expected")) - } - } - - impl<'de> de::Deserialize<'de> for Month { - fn deserialize(deserializer: D) -> Result - where - D: de::Deserializer<'de>, - { - deserializer.deserialize_str(MonthVisitor) - } - } -} - -#[cfg(test)] -mod tests { - use super::Month; - use crate::{Datelike, Months, OutOfRange, TimeZone, Utc}; - - #[test] - fn test_month_enum_try_from() { - assert_eq!(Month::try_from(1), Ok(Month::January)); - assert_eq!(Month::try_from(2), Ok(Month::February)); - assert_eq!(Month::try_from(12), Ok(Month::December)); - assert_eq!(Month::try_from(13), Err(OutOfRange::new())); - - let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap(); - assert_eq!(Month::try_from(date.month() as u8), Ok(Month::October)); - - let month = Month::January; - let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap(); - assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28)); - } - - #[test] - fn test_month_enum_primitive_parse() { - use num_traits::FromPrimitive; - - let jan_opt = Month::from_u32(1); - let feb_opt = Month::from_u64(2); - let dec_opt = Month::from_i64(12); - let no_month = Month::from_u32(13); - assert_eq!(jan_opt, Some(Month::January)); - assert_eq!(feb_opt, Some(Month::February)); - assert_eq!(dec_opt, Some(Month::December)); - assert_eq!(no_month, None); - - let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap(); - assert_eq!(Month::from_u32(date.month()), Some(Month::October)); - - let month = Month::January; - let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap(); - assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28)); - } - - #[test] - fn test_month_enum_succ_pred() { - assert_eq!(Month::January.succ(), Month::February); - assert_eq!(Month::December.succ(), Month::January); - assert_eq!(Month::January.pred(), Month::December); - assert_eq!(Month::February.pred(), Month::January); - } - - #[test] - fn test_month_partial_ord() { - assert!(Month::January <= Month::January); - assert!(Month::January < Month::February); - assert!(Month::January < Month::December); - assert!(Month::July >= Month::May); - assert!(Month::September > Month::March); - } - - #[test] - fn test_months_as_u32() { - assert_eq!(Months::new(0).as_u32(), 0); - assert_eq!(Months::new(1).as_u32(), 1); - assert_eq!(Months::new(u32::MAX).as_u32(), u32::MAX); - } - - #[test] - #[cfg(feature = "serde")] - fn test_serde_serialize() { - use Month::*; - use serde_json::to_string; - - let cases: Vec<(Month, &str)> = vec![ - (January, "\"January\""), - (February, "\"February\""), - (March, "\"March\""), - (April, "\"April\""), - (May, "\"May\""), - (June, "\"June\""), - (July, "\"July\""), - (August, "\"August\""), - (September, "\"September\""), - (October, "\"October\""), - (November, "\"November\""), - (December, "\"December\""), - ]; - - for (month, expected_str) in cases { - let string = to_string(&month).unwrap(); - assert_eq!(string, expected_str); - } - } - - #[test] - #[cfg(feature = "serde")] - fn test_serde_deserialize() { - use Month::*; - use serde_json::from_str; - - let cases: Vec<(&str, Month)> = vec![ - ("\"january\"", January), - ("\"jan\"", January), - ("\"FeB\"", February), - ("\"MAR\"", March), - ("\"mar\"", March), - ("\"april\"", April), - ("\"may\"", May), - ("\"june\"", June), - ("\"JULY\"", July), - ("\"august\"", August), - ("\"september\"", September), - ("\"October\"", October), - ("\"November\"", November), - ("\"DECEmbEr\"", December), - ]; - - for (string, expected_month) in cases { - let month = from_str::(string).unwrap(); - assert_eq!(month, expected_month); - } - - let errors: Vec<&str> = - vec!["\"not a month\"", "\"ja\"", "\"Dece\"", "Dec", "\"Augustin\""]; - - for string in errors { - from_str::(string).unwrap_err(); - } - } - - #[test] - #[cfg(feature = "rkyv-validation")] - fn test_rkyv_validation() { - let month = Month::January; - let bytes = rkyv::to_bytes::<_, 1>(&month).unwrap(); - assert_eq!(rkyv::from_bytes::(&bytes).unwrap(), month); - } - - #[test] - fn num_days() { - assert_eq!(Month::January.num_days(2020), Some(31)); - assert_eq!(Month::February.num_days(2020), Some(29)); - assert_eq!(Month::February.num_days(2019), Some(28)); - } -} diff --git a/chrono-0.4.44/src/naive/date/mod.rs b/chrono-0.4.44/src/naive/date/mod.rs deleted file mode 100644 index 4c306b5e7f..0000000000 --- a/chrono-0.4.44/src/naive/date/mod.rs +++ /dev/null @@ -1,2634 +0,0 @@ -// This is a part of Chrono. -// See README.md and LICENSE.txt for details. - -//! ISO 8601 calendar date without timezone. -//! -//! The implementation is optimized for determining year, month, day and day of week. -//! -//! Format of `NaiveDate`: -//! `YYYY_YYYY_YYYY_YYYY_YYYO_OOOO_OOOO_LWWW` -//! `Y`: Year -//! `O`: Ordinal -//! `L`: leap year flag (1 = common year, 0 is leap year) -//! `W`: weekday before the first day of the year -//! `LWWW`: will also be referred to as the year flags (`F`) - -#[cfg(feature = "alloc")] -use core::borrow::Borrow; -use core::iter::FusedIterator; -use core::num::NonZeroI32; -use core::ops::{Add, AddAssign, Sub, SubAssign}; -use core::{fmt, str}; - -#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))] -use rkyv::{Archive, Deserialize, Serialize}; - -/// L10n locales. -#[cfg(all(feature = "unstable-locales", feature = "alloc"))] -use pure_rust_locales::Locale; - -use super::internals::{Mdf, YearFlags}; -use crate::datetime::UNIX_EPOCH_DAY; -#[cfg(feature = "alloc")] -use crate::format::DelayedFormat; -use crate::format::{ - Item, Numeric, Pad, ParseError, ParseResult, Parsed, StrftimeItems, parse, parse_and_remainder, - write_hundreds, -}; -use crate::month::Months; -use crate::naive::{Days, IsoWeek, NaiveDateTime, NaiveTime, NaiveWeek}; -use crate::{Datelike, TimeDelta, Weekday}; -use crate::{expect, try_opt}; - -#[cfg(test)] -mod tests; - -/// ISO 8601 calendar date without timezone. -/// Allows for every [proleptic Gregorian date] from Jan 1, 262145 BCE to Dec 31, 262143 CE. -/// Also supports the conversion from ISO 8601 ordinal and week date. -/// -/// # Calendar Date -/// -/// The ISO 8601 **calendar date** follows the proleptic Gregorian calendar. -/// It is like a normal civil calendar but note some slight differences: -/// -/// * Dates before the Gregorian calendar's inception in 1582 are defined via the extrapolation. -/// Be careful, as historical dates are often noted in the Julian calendar and others -/// and the transition to Gregorian may differ across countries (as late as early 20C). -/// -/// (Some example: Both Shakespeare from Britain and Cervantes from Spain seemingly died -/// on the same calendar date---April 23, 1616---but in the different calendar. -/// Britain used the Julian calendar at that time, so Shakespeare's death is later.) -/// -/// * ISO 8601 calendars have the year 0, which is 1 BCE (a year before 1 CE). -/// If you need a typical BCE/BC and CE/AD notation for year numbers, -/// use the [`Datelike::year_ce`] method. -/// -/// # Week Date -/// -/// The ISO 8601 **week date** is a triple of year number, week number -/// and [day of the week](Weekday) with the following rules: -/// -/// * A week consists of Monday through Sunday, and is always numbered within some year. -/// The week number ranges from 1 to 52 or 53 depending on the year. -/// -/// * The week 1 of given year is defined as the first week containing January 4 of that year, -/// or equivalently, the first week containing four or more days in that year. -/// -/// * The year number in the week date may *not* correspond to the actual Gregorian year. -/// For example, January 3, 2016 (Sunday) was on the last (53rd) week of 2015. -/// -/// Chrono's date types default to the ISO 8601 [calendar date](#calendar-date), but -/// [`Datelike::iso_week`] and [`Datelike::weekday`] methods can be used to get the corresponding -/// week date. -/// -/// # Ordinal Date -/// -/// The ISO 8601 **ordinal date** is a pair of year number and day of the year ("ordinal"). -/// The ordinal number ranges from 1 to 365 or 366 depending on the year. -/// The year number is the same as that of the [calendar date](#calendar-date). -/// -/// This is currently the internal format of Chrono's date types. -/// -/// [proleptic Gregorian date]: crate::NaiveDate#calendar-date -#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone)] -#[cfg_attr( - any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"), - derive(Archive, Deserialize, Serialize), - archive(compare(PartialEq, PartialOrd)), - archive_attr(derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)) -)] -#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] -pub struct NaiveDate { - yof: NonZeroI32, // (year << 13) | of -} - -/// The minimum possible `NaiveDate` (January 1, 262145 BCE). -#[deprecated(since = "0.4.20", note = "Use NaiveDate::MIN instead")] -pub const MIN_DATE: NaiveDate = NaiveDate::MIN; -/// The maximum possible `NaiveDate` (December 31, 262143 CE). -#[deprecated(since = "0.4.20", note = "Use NaiveDate::MAX instead")] -pub const MAX_DATE: NaiveDate = NaiveDate::MAX; - -#[cfg(all(feature = "arbitrary", feature = "std"))] -impl arbitrary::Arbitrary<'_> for NaiveDate { - fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result { - let year = u.int_in_range(MIN_YEAR..=MAX_YEAR)?; - let max_days = YearFlags::from_year(year).ndays(); - let ord = u.int_in_range(1..=max_days)?; - NaiveDate::from_yo_opt(year, ord).ok_or(arbitrary::Error::IncorrectFormat) - } -} - -impl NaiveDate { - pub(crate) fn weeks_from(&self, day: Weekday) -> i32 { - (self.ordinal() as i32 - self.weekday().days_since(day) as i32 + 6) / 7 - } - - /// Makes a new `NaiveDate` from year, ordinal and flags. - /// Does not check whether the flags are correct for the provided year. - const fn from_ordinal_and_flags( - year: i32, - ordinal: u32, - flags: YearFlags, - ) -> Option { - if year < MIN_YEAR || year > MAX_YEAR { - return None; // Out-of-range - } - if ordinal == 0 || ordinal > 366 { - return None; // Invalid - } - debug_assert!(YearFlags::from_year(year).0 == flags.0); - let yof = (year << 13) | (ordinal << 4) as i32 | flags.0 as i32; - match yof & OL_MASK <= MAX_OL { - true => Some(NaiveDate::from_yof(yof)), - false => None, // Does not exist: Ordinal 366 in a common year. - } - } - - /// Makes a new `NaiveDate` from year and packed month-day-flags. - /// Does not check whether the flags are correct for the provided year. - const fn from_mdf(year: i32, mdf: Mdf) -> Option { - if year < MIN_YEAR || year > MAX_YEAR { - return None; // Out-of-range - } - Some(NaiveDate::from_yof((year << 13) | try_opt!(mdf.ordinal_and_flags()))) - } - - /// Makes a new `NaiveDate` from the [calendar date](#calendar-date) - /// (year, month and day). - /// - /// # Panics - /// - /// Panics if the specified calendar day does not exist, on invalid values for `month` or `day`, - /// or if `year` is out of range for `NaiveDate`. - #[deprecated(since = "0.4.23", note = "use `from_ymd_opt()` instead")] - #[must_use] - pub const fn from_ymd(year: i32, month: u32, day: u32) -> NaiveDate { - expect(NaiveDate::from_ymd_opt(year, month, day), "invalid or out-of-range date") - } - - /// Makes a new `NaiveDate` from the [calendar date](#calendar-date) - /// (year, month and day). - /// - /// # Errors - /// - /// Returns `None` if: - /// - The specified calendar day does not exist (for example 2023-04-31). - /// - The value for `month` or `day` is invalid. - /// - `year` is out of range for `NaiveDate`. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDate; - /// - /// let from_ymd_opt = NaiveDate::from_ymd_opt; - /// - /// assert!(from_ymd_opt(2015, 3, 14).is_some()); - /// assert!(from_ymd_opt(2015, 0, 14).is_none()); - /// assert!(from_ymd_opt(2015, 2, 29).is_none()); - /// assert!(from_ymd_opt(-4, 2, 29).is_some()); // 5 BCE is a leap year - /// assert!(from_ymd_opt(400000, 1, 1).is_none()); - /// assert!(from_ymd_opt(-400000, 1, 1).is_none()); - /// ``` - #[must_use] - pub const fn from_ymd_opt(year: i32, month: u32, day: u32) -> Option { - let flags = YearFlags::from_year(year); - - if let Some(mdf) = Mdf::new(month, day, flags) { - NaiveDate::from_mdf(year, mdf) - } else { - None - } - } - - /// Makes a new `NaiveDate` from the [ordinal date](#ordinal-date) - /// (year and day of the year). - /// - /// # Panics - /// - /// Panics if the specified ordinal day does not exist, on invalid values for `ordinal`, or if - /// `year` is out of range for `NaiveDate`. - #[deprecated(since = "0.4.23", note = "use `from_yo_opt()` instead")] - #[must_use] - pub const fn from_yo(year: i32, ordinal: u32) -> NaiveDate { - expect(NaiveDate::from_yo_opt(year, ordinal), "invalid or out-of-range date") - } - - /// Makes a new `NaiveDate` from the [ordinal date](#ordinal-date) - /// (year and day of the year). - /// - /// # Errors - /// - /// Returns `None` if: - /// - The specified ordinal day does not exist (for example 2023-366). - /// - The value for `ordinal` is invalid (for example: `0`, `400`). - /// - `year` is out of range for `NaiveDate`. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDate; - /// - /// let from_yo_opt = NaiveDate::from_yo_opt; - /// - /// assert!(from_yo_opt(2015, 100).is_some()); - /// assert!(from_yo_opt(2015, 0).is_none()); - /// assert!(from_yo_opt(2015, 365).is_some()); - /// assert!(from_yo_opt(2015, 366).is_none()); - /// assert!(from_yo_opt(-4, 366).is_some()); // 5 BCE is a leap year - /// assert!(from_yo_opt(400000, 1).is_none()); - /// assert!(from_yo_opt(-400000, 1).is_none()); - /// ``` - #[must_use] - pub const fn from_yo_opt(year: i32, ordinal: u32) -> Option { - let flags = YearFlags::from_year(year); - NaiveDate::from_ordinal_and_flags(year, ordinal, flags) - } - - /// Makes a new `NaiveDate` from the [ISO week date](#week-date) - /// (year, week number and day of the week). - /// The resulting `NaiveDate` may have a different year from the input year. - /// - /// # Panics - /// - /// Panics if the specified week does not exist in that year, on invalid values for `week`, or - /// if the resulting date is out of range for `NaiveDate`. - #[deprecated(since = "0.4.23", note = "use `from_isoywd_opt()` instead")] - #[must_use] - pub const fn from_isoywd(year: i32, week: u32, weekday: Weekday) -> NaiveDate { - expect(NaiveDate::from_isoywd_opt(year, week, weekday), "invalid or out-of-range date") - } - - /// Makes a new `NaiveDate` from the [ISO week date](#week-date) - /// (year, week number and day of the week). - /// The resulting `NaiveDate` may have a different year from the input year. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The specified week does not exist in that year (for example 2023 week 53). - /// - The value for `week` is invalid (for example: `0`, `60`). - /// - If the resulting date is out of range for `NaiveDate`. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, Weekday}; - /// - /// let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); - /// let from_isoywd_opt = NaiveDate::from_isoywd_opt; - /// - /// assert_eq!(from_isoywd_opt(2015, 0, Weekday::Sun), None); - /// assert_eq!(from_isoywd_opt(2015, 10, Weekday::Sun), Some(from_ymd(2015, 3, 8))); - /// assert_eq!(from_isoywd_opt(2015, 30, Weekday::Mon), Some(from_ymd(2015, 7, 20))); - /// assert_eq!(from_isoywd_opt(2015, 60, Weekday::Mon), None); - /// - /// assert_eq!(from_isoywd_opt(400000, 10, Weekday::Fri), None); - /// assert_eq!(from_isoywd_opt(-400000, 10, Weekday::Sat), None); - /// ``` - /// - /// The year number of ISO week date may differ from that of the calendar date. - /// - /// ``` - /// # use chrono::{NaiveDate, Weekday}; - /// # let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); - /// # let from_isoywd_opt = NaiveDate::from_isoywd_opt; - /// // Mo Tu We Th Fr Sa Su - /// // 2014-W52 22 23 24 25 26 27 28 has 4+ days of new year, - /// // 2015-W01 29 30 31 1 2 3 4 <- so this is the first week - /// assert_eq!(from_isoywd_opt(2014, 52, Weekday::Sun), Some(from_ymd(2014, 12, 28))); - /// assert_eq!(from_isoywd_opt(2014, 53, Weekday::Mon), None); - /// assert_eq!(from_isoywd_opt(2015, 1, Weekday::Mon), Some(from_ymd(2014, 12, 29))); - /// - /// // 2015-W52 21 22 23 24 25 26 27 has 4+ days of old year, - /// // 2015-W53 28 29 30 31 1 2 3 <- so this is the last week - /// // 2016-W01 4 5 6 7 8 9 10 - /// assert_eq!(from_isoywd_opt(2015, 52, Weekday::Sun), Some(from_ymd(2015, 12, 27))); - /// assert_eq!(from_isoywd_opt(2015, 53, Weekday::Sun), Some(from_ymd(2016, 1, 3))); - /// assert_eq!(from_isoywd_opt(2015, 54, Weekday::Mon), None); - /// assert_eq!(from_isoywd_opt(2016, 1, Weekday::Mon), Some(from_ymd(2016, 1, 4))); - /// ``` - #[must_use] - pub const fn from_isoywd_opt(year: i32, week: u32, weekday: Weekday) -> Option { - let flags = YearFlags::from_year(year); - let nweeks = flags.nisoweeks(); - if week == 0 || week > nweeks { - return None; - } - // ordinal = week ordinal - delta - let weekord = week * 7 + weekday as u32; - let delta = flags.isoweek_delta(); - let (year, ordinal, flags) = if weekord <= delta { - // ordinal < 1, previous year - let prevflags = YearFlags::from_year(year - 1); - (year - 1, weekord + prevflags.ndays() - delta, prevflags) - } else { - let ordinal = weekord - delta; - let ndays = flags.ndays(); - if ordinal <= ndays { - // this year - (year, ordinal, flags) - } else { - // ordinal > ndays, next year - let nextflags = YearFlags::from_year(year + 1); - (year + 1, ordinal - ndays, nextflags) - } - }; - NaiveDate::from_ordinal_and_flags(year, ordinal, flags) - } - - /// Makes a new `NaiveDate` from a day's number in the proleptic Gregorian calendar, with - /// January 1, 1 being day 1. - /// - /// # Panics - /// - /// Panics if the date is out of range. - #[deprecated(since = "0.4.23", note = "use `from_num_days_from_ce_opt()` instead")] - #[inline] - #[must_use] - pub const fn from_num_days_from_ce(days: i32) -> NaiveDate { - expect(NaiveDate::from_num_days_from_ce_opt(days), "out-of-range date") - } - - /// Makes a new `NaiveDate` from a day's number in the proleptic Gregorian calendar, with - /// January 1, 1 being day 1. - /// - /// # Errors - /// - /// Returns `None` if the date is out of range. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDate; - /// - /// let from_ndays_opt = NaiveDate::from_num_days_from_ce_opt; - /// let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); - /// - /// assert_eq!(from_ndays_opt(730_000), Some(from_ymd(1999, 9, 3))); - /// assert_eq!(from_ndays_opt(1), Some(from_ymd(1, 1, 1))); - /// assert_eq!(from_ndays_opt(0), Some(from_ymd(0, 12, 31))); - /// assert_eq!(from_ndays_opt(-1), Some(from_ymd(0, 12, 30))); - /// assert_eq!(from_ndays_opt(100_000_000), None); - /// assert_eq!(from_ndays_opt(-100_000_000), None); - /// ``` - #[must_use] - pub const fn from_num_days_from_ce_opt(days: i32) -> Option { - let days = try_opt!(days.checked_add(365)); // make December 31, 1 BCE equal to day 0 - let year_div_400 = days.div_euclid(146_097); - let cycle = days.rem_euclid(146_097); - let (year_mod_400, ordinal) = cycle_to_yo(cycle as u32); - let flags = YearFlags::from_year_mod_400(year_mod_400 as i32); - NaiveDate::from_ordinal_and_flags(year_div_400 * 400 + year_mod_400 as i32, ordinal, flags) - } - - /// Makes a new `NaiveDate` from a day's number in the proleptic Gregorian calendar, with - /// January 1, 1970 being day 0. - /// - /// # Errors - /// - /// Returns `None` if the date is out of range. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDate; - /// - /// let from_ndays_opt = NaiveDate::from_epoch_days; - /// let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); - /// - /// assert_eq!(from_ndays_opt(-719_162), Some(from_ymd(1, 1, 1))); - /// assert_eq!(from_ndays_opt(1), Some(from_ymd(1970, 1, 2))); - /// assert_eq!(from_ndays_opt(0), Some(from_ymd(1970, 1, 1))); - /// assert_eq!(from_ndays_opt(-1), Some(from_ymd(1969, 12, 31))); - /// assert_eq!(from_ndays_opt(13036), Some(from_ymd(2005, 9, 10))); - /// assert_eq!(from_ndays_opt(100_000_000), None); - /// assert_eq!(from_ndays_opt(-100_000_000), None); - /// ``` - #[must_use] - pub const fn from_epoch_days(days: i32) -> Option { - let ce_days = try_opt!(days.checked_add(UNIX_EPOCH_DAY as i32)); - NaiveDate::from_num_days_from_ce_opt(ce_days) - } - - /// Makes a new `NaiveDate` by counting the number of occurrences of a particular day-of-week - /// since the beginning of the given month. For instance, if you want the 2nd Friday of March - /// 2017, you would use `NaiveDate::from_weekday_of_month(2017, 3, Weekday::Fri, 2)`. - /// - /// `n` is 1-indexed. - /// - /// # Panics - /// - /// Panics if the specified day does not exist in that month, on invalid values for `month` or - /// `n`, or if `year` is out of range for `NaiveDate`. - #[deprecated(since = "0.4.23", note = "use `from_weekday_of_month_opt()` instead")] - #[must_use] - pub const fn from_weekday_of_month( - year: i32, - month: u32, - weekday: Weekday, - n: u8, - ) -> NaiveDate { - expect(NaiveDate::from_weekday_of_month_opt(year, month, weekday, n), "out-of-range date") - } - - /// Makes a new `NaiveDate` by counting the number of occurrences of a particular day-of-week - /// since the beginning of the given month. For instance, if you want the 2nd Friday of March - /// 2017, you would use `NaiveDate::from_weekday_of_month(2017, 3, Weekday::Fri, 2)`. - /// - /// `n` is 1-indexed. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The specified day does not exist in that month (for example the 5th Monday of Apr. 2023). - /// - The value for `month` or `n` is invalid. - /// - `year` is out of range for `NaiveDate`. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, Weekday}; - /// assert_eq!( - /// NaiveDate::from_weekday_of_month_opt(2017, 3, Weekday::Fri, 2), - /// NaiveDate::from_ymd_opt(2017, 3, 10) - /// ) - /// ``` - #[must_use] - pub const fn from_weekday_of_month_opt( - year: i32, - month: u32, - weekday: Weekday, - n: u8, - ) -> Option { - if n == 0 { - return None; - } - let first = try_opt!(NaiveDate::from_ymd_opt(year, month, 1)).weekday(); - let first_to_dow = (7 + weekday.number_from_monday() - first.number_from_monday()) % 7; - let day = (n - 1) as u32 * 7 + first_to_dow + 1; - NaiveDate::from_ymd_opt(year, month, day) - } - - /// Parses a string with the specified format string and returns a new `NaiveDate`. - /// See the [`format::strftime` module](crate::format::strftime) - /// on the supported escape sequences. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDate; - /// - /// let parse_from_str = NaiveDate::parse_from_str; - /// - /// assert_eq!( - /// parse_from_str("2015-09-05", "%Y-%m-%d"), - /// Ok(NaiveDate::from_ymd_opt(2015, 9, 5).unwrap()) - /// ); - /// assert_eq!( - /// parse_from_str("5sep2015", "%d%b%Y"), - /// Ok(NaiveDate::from_ymd_opt(2015, 9, 5).unwrap()) - /// ); - /// ``` - /// - /// Time and offset is ignored for the purpose of parsing. - /// - /// ``` - /// # use chrono::NaiveDate; - /// # let parse_from_str = NaiveDate::parse_from_str; - /// assert_eq!( - /// parse_from_str("2014-5-17T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), - /// Ok(NaiveDate::from_ymd_opt(2014, 5, 17).unwrap()) - /// ); - /// ``` - /// - /// Out-of-bound dates or insufficient fields are errors. - /// - /// ``` - /// # use chrono::NaiveDate; - /// # let parse_from_str = NaiveDate::parse_from_str; - /// assert!(parse_from_str("2015/9", "%Y/%m").is_err()); - /// assert!(parse_from_str("2015/9/31", "%Y/%m/%d").is_err()); - /// ``` - /// - /// All parsed fields should be consistent to each other, otherwise it's an error. - /// - /// ``` - /// # use chrono::NaiveDate; - /// # let parse_from_str = NaiveDate::parse_from_str; - /// assert!(parse_from_str("Sat, 09 Aug 2013", "%a, %d %b %Y").is_err()); - /// ``` - pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult { - let mut parsed = Parsed::new(); - parse(&mut parsed, s, StrftimeItems::new(fmt))?; - parsed.to_naive_date() - } - - /// Parses a string from a user-specified format into a new `NaiveDate` value, and a slice with - /// the remaining portion of the string. - /// See the [`format::strftime` module](crate::format::strftime) - /// on the supported escape sequences. - /// - /// Similar to [`parse_from_str`](#method.parse_from_str). - /// - /// # Example - /// - /// ```rust - /// # use chrono::{NaiveDate}; - /// let (date, remainder) = - /// NaiveDate::parse_and_remainder("2015-02-18 trailing text", "%Y-%m-%d").unwrap(); - /// assert_eq!(date, NaiveDate::from_ymd_opt(2015, 2, 18).unwrap()); - /// assert_eq!(remainder, " trailing text"); - /// ``` - pub fn parse_and_remainder<'a>(s: &'a str, fmt: &str) -> ParseResult<(NaiveDate, &'a str)> { - let mut parsed = Parsed::new(); - let remainder = parse_and_remainder(&mut parsed, s, StrftimeItems::new(fmt))?; - parsed.to_naive_date().map(|d| (d, remainder)) - } - - /// Add a duration in [`Months`] to the date - /// - /// Uses the last day of the month if the day does not exist in the resulting month. - /// - /// # Errors - /// - /// Returns `None` if the resulting date would be out of range. - /// - /// # Example - /// - /// ``` - /// # use chrono::{NaiveDate, Months}; - /// assert_eq!( - /// NaiveDate::from_ymd_opt(2022, 2, 20).unwrap().checked_add_months(Months::new(6)), - /// Some(NaiveDate::from_ymd_opt(2022, 8, 20).unwrap()) - /// ); - /// assert_eq!( - /// NaiveDate::from_ymd_opt(2022, 7, 31).unwrap().checked_add_months(Months::new(2)), - /// Some(NaiveDate::from_ymd_opt(2022, 9, 30).unwrap()) - /// ); - /// ``` - #[must_use] - pub const fn checked_add_months(self, months: Months) -> Option { - if months.0 == 0 { - return Some(self); - } - - match months.0 <= i32::MAX as u32 { - true => self.diff_months(months.0 as i32), - false => None, - } - } - - /// Subtract a duration in [`Months`] from the date - /// - /// Uses the last day of the month if the day does not exist in the resulting month. - /// - /// # Errors - /// - /// Returns `None` if the resulting date would be out of range. - /// - /// # Example - /// - /// ``` - /// # use chrono::{NaiveDate, Months}; - /// assert_eq!( - /// NaiveDate::from_ymd_opt(2022, 2, 20).unwrap().checked_sub_months(Months::new(6)), - /// Some(NaiveDate::from_ymd_opt(2021, 8, 20).unwrap()) - /// ); - /// - /// assert_eq!( - /// NaiveDate::from_ymd_opt(2014, 1, 1) - /// .unwrap() - /// .checked_sub_months(Months::new(core::i32::MAX as u32 + 1)), - /// None - /// ); - /// ``` - #[must_use] - pub const fn checked_sub_months(self, months: Months) -> Option { - if months.0 == 0 { - return Some(self); - } - - match months.0 <= i32::MAX as u32 { - true => self.diff_months(-(months.0 as i32)), - false => None, - } - } - - const fn diff_months(self, months: i32) -> Option { - let months = try_opt!((self.year() * 12 + self.month() as i32 - 1).checked_add(months)); - let year = months.div_euclid(12); - let month = months.rem_euclid(12) as u32 + 1; - - // Clamp original day in case new month is shorter - let flags = YearFlags::from_year(year); - let feb_days = if flags.ndays() == 366 { 29 } else { 28 }; - let days = [31, feb_days, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; - let day_max = days[(month - 1) as usize]; - let mut day = self.day(); - if day > day_max { - day = day_max; - }; - - NaiveDate::from_ymd_opt(year, month, day) - } - - /// Add a duration in [`Days`] to the date - /// - /// # Errors - /// - /// Returns `None` if the resulting date would be out of range. - /// - /// # Example - /// - /// ``` - /// # use chrono::{NaiveDate, Days}; - /// assert_eq!( - /// NaiveDate::from_ymd_opt(2022, 2, 20).unwrap().checked_add_days(Days::new(9)), - /// Some(NaiveDate::from_ymd_opt(2022, 3, 1).unwrap()) - /// ); - /// assert_eq!( - /// NaiveDate::from_ymd_opt(2022, 7, 31).unwrap().checked_add_days(Days::new(2)), - /// Some(NaiveDate::from_ymd_opt(2022, 8, 2).unwrap()) - /// ); - /// assert_eq!( - /// NaiveDate::from_ymd_opt(2022, 7, 31).unwrap().checked_add_days(Days::new(1000000000000)), - /// None - /// ); - /// ``` - #[must_use] - pub const fn checked_add_days(self, days: Days) -> Option { - match days.0 <= i32::MAX as u64 { - true => self.add_days(days.0 as i32), - false => None, - } - } - - /// Subtract a duration in [`Days`] from the date - /// - /// # Errors - /// - /// Returns `None` if the resulting date would be out of range. - /// - /// # Example - /// - /// ``` - /// # use chrono::{NaiveDate, Days}; - /// assert_eq!( - /// NaiveDate::from_ymd_opt(2022, 2, 20).unwrap().checked_sub_days(Days::new(6)), - /// Some(NaiveDate::from_ymd_opt(2022, 2, 14).unwrap()) - /// ); - /// assert_eq!( - /// NaiveDate::from_ymd_opt(2022, 2, 20).unwrap().checked_sub_days(Days::new(1000000000000)), - /// None - /// ); - /// ``` - #[must_use] - pub const fn checked_sub_days(self, days: Days) -> Option { - match days.0 <= i32::MAX as u64 { - true => self.add_days(-(days.0 as i32)), - false => None, - } - } - - /// Add a duration of `i32` days to the date. - pub(crate) const fn add_days(self, days: i32) -> Option { - // Fast path if the result is within the same year. - // Also `DateTime::checked_(add|sub)_days` relies on this path, because if the value remains - // within the year it doesn't do a check if the year is in range. - // This way `DateTime:checked_(add|sub)_days(Days::new(0))` can be a no-op on dates were the - // local datetime is beyond `NaiveDate::{MIN, MAX}. - const ORDINAL_MASK: i32 = 0b1_1111_1111_0000; - if let Some(ordinal) = ((self.yof() & ORDINAL_MASK) >> 4).checked_add(days) { - if ordinal > 0 && ordinal <= (365 + self.leap_year() as i32) { - let year_and_flags = self.yof() & !ORDINAL_MASK; - return Some(NaiveDate::from_yof(year_and_flags | (ordinal << 4))); - } - } - // do the full check - let year = self.year(); - let (mut year_div_400, year_mod_400) = div_mod_floor(year, 400); - let cycle = yo_to_cycle(year_mod_400 as u32, self.ordinal()); - let cycle = try_opt!((cycle as i32).checked_add(days)); - let (cycle_div_400y, cycle) = div_mod_floor(cycle, 146_097); - year_div_400 += cycle_div_400y; - - let (year_mod_400, ordinal) = cycle_to_yo(cycle as u32); - let flags = YearFlags::from_year_mod_400(year_mod_400 as i32); - NaiveDate::from_ordinal_and_flags(year_div_400 * 400 + year_mod_400 as i32, ordinal, flags) - } - - /// Makes a new `NaiveDateTime` from the current date and given `NaiveTime`. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, NaiveDateTime, NaiveTime}; - /// - /// let d = NaiveDate::from_ymd_opt(2015, 6, 3).unwrap(); - /// let t = NaiveTime::from_hms_milli_opt(12, 34, 56, 789).unwrap(); - /// - /// let dt: NaiveDateTime = d.and_time(t); - /// assert_eq!(dt.date(), d); - /// assert_eq!(dt.time(), t); - /// ``` - #[inline] - #[must_use] - pub const fn and_time(&self, time: NaiveTime) -> NaiveDateTime { - NaiveDateTime::new(*self, time) - } - - /// Makes a new `NaiveDateTime` from the current date, hour, minute and second. - /// - /// No [leap second](./struct.NaiveTime.html#leap-second-handling) is allowed here; - /// use `NaiveDate::and_hms_*` methods with a subsecond parameter instead. - /// - /// # Panics - /// - /// Panics on invalid hour, minute and/or second. - #[deprecated(since = "0.4.23", note = "use `and_hms_opt()` instead")] - #[inline] - #[must_use] - pub const fn and_hms(&self, hour: u32, min: u32, sec: u32) -> NaiveDateTime { - expect(self.and_hms_opt(hour, min, sec), "invalid time") - } - - /// Makes a new `NaiveDateTime` from the current date, hour, minute and second. - /// - /// No [leap second](./struct.NaiveTime.html#leap-second-handling) is allowed here; - /// use `NaiveDate::and_hms_*_opt` methods with a subsecond parameter instead. - /// - /// # Errors - /// - /// Returns `None` on invalid hour, minute and/or second. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDate; - /// - /// let d = NaiveDate::from_ymd_opt(2015, 6, 3).unwrap(); - /// assert!(d.and_hms_opt(12, 34, 56).is_some()); - /// assert!(d.and_hms_opt(12, 34, 60).is_none()); // use `and_hms_milli_opt` instead - /// assert!(d.and_hms_opt(12, 60, 56).is_none()); - /// assert!(d.and_hms_opt(24, 34, 56).is_none()); - /// ``` - #[inline] - #[must_use] - pub const fn and_hms_opt(&self, hour: u32, min: u32, sec: u32) -> Option { - let time = try_opt!(NaiveTime::from_hms_opt(hour, min, sec)); - Some(self.and_time(time)) - } - - /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and millisecond. - /// - /// The millisecond part is allowed to exceed 1,000 in order to represent a [leap second]( - /// ./struct.NaiveTime.html#leap-second-handling), but only when `sec == 59`. - /// - /// # Panics - /// - /// Panics on invalid hour, minute, second and/or millisecond. - #[deprecated(since = "0.4.23", note = "use `and_hms_milli_opt()` instead")] - #[inline] - #[must_use] - pub const fn and_hms_milli(&self, hour: u32, min: u32, sec: u32, milli: u32) -> NaiveDateTime { - expect(self.and_hms_milli_opt(hour, min, sec, milli), "invalid time") - } - - /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and millisecond. - /// - /// The millisecond part is allowed to exceed 1,000 in order to represent a [leap second]( - /// ./struct.NaiveTime.html#leap-second-handling), but only when `sec == 59`. - /// - /// # Errors - /// - /// Returns `None` on invalid hour, minute, second and/or millisecond. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDate; - /// - /// let d = NaiveDate::from_ymd_opt(2015, 6, 3).unwrap(); - /// assert!(d.and_hms_milli_opt(12, 34, 56, 789).is_some()); - /// assert!(d.and_hms_milli_opt(12, 34, 59, 1_789).is_some()); // leap second - /// assert!(d.and_hms_milli_opt(12, 34, 59, 2_789).is_none()); - /// assert!(d.and_hms_milli_opt(12, 34, 60, 789).is_none()); - /// assert!(d.and_hms_milli_opt(12, 60, 56, 789).is_none()); - /// assert!(d.and_hms_milli_opt(24, 34, 56, 789).is_none()); - /// ``` - #[inline] - #[must_use] - pub const fn and_hms_milli_opt( - &self, - hour: u32, - min: u32, - sec: u32, - milli: u32, - ) -> Option { - let time = try_opt!(NaiveTime::from_hms_milli_opt(hour, min, sec, milli)); - Some(self.and_time(time)) - } - - /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and microsecond. - /// - /// The microsecond part is allowed to exceed 1,000,000 in order to represent a [leap second]( - /// ./struct.NaiveTime.html#leap-second-handling), but only when `sec == 59`. - /// - /// # Panics - /// - /// Panics on invalid hour, minute, second and/or microsecond. - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate, NaiveDateTime, Timelike, Weekday}; - /// - /// let d = NaiveDate::from_ymd_opt(2015, 6, 3).unwrap(); - /// - /// let dt: NaiveDateTime = d.and_hms_micro_opt(12, 34, 56, 789_012).unwrap(); - /// assert_eq!(dt.year(), 2015); - /// assert_eq!(dt.weekday(), Weekday::Wed); - /// assert_eq!(dt.second(), 56); - /// assert_eq!(dt.nanosecond(), 789_012_000); - /// ``` - #[deprecated(since = "0.4.23", note = "use `and_hms_micro_opt()` instead")] - #[inline] - #[must_use] - pub const fn and_hms_micro(&self, hour: u32, min: u32, sec: u32, micro: u32) -> NaiveDateTime { - expect(self.and_hms_micro_opt(hour, min, sec, micro), "invalid time") - } - - /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and microsecond. - /// - /// The microsecond part is allowed to exceed 1,000,000 in order to represent a [leap second]( - /// ./struct.NaiveTime.html#leap-second-handling), but only when `sec == 59`. - /// - /// # Errors - /// - /// Returns `None` on invalid hour, minute, second and/or microsecond. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDate; - /// - /// let d = NaiveDate::from_ymd_opt(2015, 6, 3).unwrap(); - /// assert!(d.and_hms_micro_opt(12, 34, 56, 789_012).is_some()); - /// assert!(d.and_hms_micro_opt(12, 34, 59, 1_789_012).is_some()); // leap second - /// assert!(d.and_hms_micro_opt(12, 34, 59, 2_789_012).is_none()); - /// assert!(d.and_hms_micro_opt(12, 34, 60, 789_012).is_none()); - /// assert!(d.and_hms_micro_opt(12, 60, 56, 789_012).is_none()); - /// assert!(d.and_hms_micro_opt(24, 34, 56, 789_012).is_none()); - /// ``` - #[inline] - #[must_use] - pub const fn and_hms_micro_opt( - &self, - hour: u32, - min: u32, - sec: u32, - micro: u32, - ) -> Option { - let time = try_opt!(NaiveTime::from_hms_micro_opt(hour, min, sec, micro)); - Some(self.and_time(time)) - } - - /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and nanosecond. - /// - /// The nanosecond part is allowed to exceed 1,000,000,000 in order to represent a [leap second]( - /// ./struct.NaiveTime.html#leap-second-handling), but only when `sec == 59`. - /// - /// # Panics - /// - /// Panics on invalid hour, minute, second and/or nanosecond. - #[deprecated(since = "0.4.23", note = "use `and_hms_nano_opt()` instead")] - #[inline] - #[must_use] - pub const fn and_hms_nano(&self, hour: u32, min: u32, sec: u32, nano: u32) -> NaiveDateTime { - expect(self.and_hms_nano_opt(hour, min, sec, nano), "invalid time") - } - - /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and nanosecond. - /// - /// The nanosecond part is allowed to exceed 1,000,000,000 in order to represent a [leap second]( - /// ./struct.NaiveTime.html#leap-second-handling), but only when `sec == 59`. - /// - /// # Errors - /// - /// Returns `None` on invalid hour, minute, second and/or nanosecond. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDate; - /// - /// let d = NaiveDate::from_ymd_opt(2015, 6, 3).unwrap(); - /// assert!(d.and_hms_nano_opt(12, 34, 56, 789_012_345).is_some()); - /// assert!(d.and_hms_nano_opt(12, 34, 59, 1_789_012_345).is_some()); // leap second - /// assert!(d.and_hms_nano_opt(12, 34, 59, 2_789_012_345).is_none()); - /// assert!(d.and_hms_nano_opt(12, 34, 60, 789_012_345).is_none()); - /// assert!(d.and_hms_nano_opt(12, 60, 56, 789_012_345).is_none()); - /// assert!(d.and_hms_nano_opt(24, 34, 56, 789_012_345).is_none()); - /// ``` - #[inline] - #[must_use] - pub const fn and_hms_nano_opt( - &self, - hour: u32, - min: u32, - sec: u32, - nano: u32, - ) -> Option { - let time = try_opt!(NaiveTime::from_hms_nano_opt(hour, min, sec, nano)); - Some(self.and_time(time)) - } - - /// Returns the packed month-day-flags. - #[inline] - const fn mdf(&self) -> Mdf { - Mdf::from_ol((self.yof() & OL_MASK) >> 3, self.year_flags()) - } - - /// Makes a new `NaiveDate` with the packed month-day-flags changed. - /// - /// Returns `None` when the resulting `NaiveDate` would be invalid. - #[inline] - const fn with_mdf(&self, mdf: Mdf) -> Option { - debug_assert!(self.year_flags().0 == mdf.year_flags().0); - match mdf.ordinal() { - Some(ordinal) => { - Some(NaiveDate::from_yof((self.yof() & !ORDINAL_MASK) | (ordinal << 4) as i32)) - } - None => None, // Non-existing date - } - } - - /// Makes a new `NaiveDate` for the next calendar date. - /// - /// # Panics - /// - /// Panics when `self` is the last representable date. - #[deprecated(since = "0.4.23", note = "use `succ_opt()` instead")] - #[inline] - #[must_use] - pub const fn succ(&self) -> NaiveDate { - expect(self.succ_opt(), "out of bound") - } - - /// Makes a new `NaiveDate` for the next calendar date. - /// - /// # Errors - /// - /// Returns `None` when `self` is the last representable date. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDate; - /// - /// assert_eq!( - /// NaiveDate::from_ymd_opt(2015, 6, 3).unwrap().succ_opt(), - /// Some(NaiveDate::from_ymd_opt(2015, 6, 4).unwrap()) - /// ); - /// assert_eq!(NaiveDate::MAX.succ_opt(), None); - /// ``` - #[inline] - #[must_use] - pub const fn succ_opt(&self) -> Option { - let new_ol = (self.yof() & OL_MASK) + (1 << 4); - match new_ol <= MAX_OL { - true => Some(NaiveDate::from_yof(self.yof() & !OL_MASK | new_ol)), - false => NaiveDate::from_yo_opt(self.year() + 1, 1), - } - } - - /// Makes a new `NaiveDate` for the previous calendar date. - /// - /// # Panics - /// - /// Panics when `self` is the first representable date. - #[deprecated(since = "0.4.23", note = "use `pred_opt()` instead")] - #[inline] - #[must_use] - pub const fn pred(&self) -> NaiveDate { - expect(self.pred_opt(), "out of bound") - } - - /// Makes a new `NaiveDate` for the previous calendar date. - /// - /// # Errors - /// - /// Returns `None` when `self` is the first representable date. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDate; - /// - /// assert_eq!( - /// NaiveDate::from_ymd_opt(2015, 6, 3).unwrap().pred_opt(), - /// Some(NaiveDate::from_ymd_opt(2015, 6, 2).unwrap()) - /// ); - /// assert_eq!(NaiveDate::MIN.pred_opt(), None); - /// ``` - #[inline] - #[must_use] - pub const fn pred_opt(&self) -> Option { - let new_shifted_ordinal = (self.yof() & ORDINAL_MASK) - (1 << 4); - match new_shifted_ordinal > 0 { - true => Some(NaiveDate::from_yof(self.yof() & !ORDINAL_MASK | new_shifted_ordinal)), - false => NaiveDate::from_ymd_opt(self.year() - 1, 12, 31), - } - } - - /// Adds the number of whole days in the given `TimeDelta` to the current date. - /// - /// # Errors - /// - /// Returns `None` if the resulting date would be out of range. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, TimeDelta}; - /// - /// let d = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap(); - /// assert_eq!( - /// d.checked_add_signed(TimeDelta::try_days(40).unwrap()), - /// Some(NaiveDate::from_ymd_opt(2015, 10, 15).unwrap()) - /// ); - /// assert_eq!( - /// d.checked_add_signed(TimeDelta::try_days(-40).unwrap()), - /// Some(NaiveDate::from_ymd_opt(2015, 7, 27).unwrap()) - /// ); - /// assert_eq!(d.checked_add_signed(TimeDelta::try_days(1_000_000_000).unwrap()), None); - /// assert_eq!(d.checked_add_signed(TimeDelta::try_days(-1_000_000_000).unwrap()), None); - /// assert_eq!(NaiveDate::MAX.checked_add_signed(TimeDelta::try_days(1).unwrap()), None); - /// ``` - #[must_use] - pub const fn checked_add_signed(self, rhs: TimeDelta) -> Option { - let days = rhs.num_days(); - if days < i32::MIN as i64 || days > i32::MAX as i64 { - return None; - } - self.add_days(days as i32) - } - - /// Subtracts the number of whole days in the given `TimeDelta` from the current date. - /// - /// # Errors - /// - /// Returns `None` if the resulting date would be out of range. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, TimeDelta}; - /// - /// let d = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap(); - /// assert_eq!( - /// d.checked_sub_signed(TimeDelta::try_days(40).unwrap()), - /// Some(NaiveDate::from_ymd_opt(2015, 7, 27).unwrap()) - /// ); - /// assert_eq!( - /// d.checked_sub_signed(TimeDelta::try_days(-40).unwrap()), - /// Some(NaiveDate::from_ymd_opt(2015, 10, 15).unwrap()) - /// ); - /// assert_eq!(d.checked_sub_signed(TimeDelta::try_days(1_000_000_000).unwrap()), None); - /// assert_eq!(d.checked_sub_signed(TimeDelta::try_days(-1_000_000_000).unwrap()), None); - /// assert_eq!(NaiveDate::MIN.checked_sub_signed(TimeDelta::try_days(1).unwrap()), None); - /// ``` - #[must_use] - pub const fn checked_sub_signed(self, rhs: TimeDelta) -> Option { - let days = -rhs.num_days(); - if days < i32::MIN as i64 || days > i32::MAX as i64 { - return None; - } - self.add_days(days as i32) - } - - /// Subtracts another `NaiveDate` from the current date. - /// Returns a `TimeDelta` of integral numbers. - /// - /// This does not overflow or underflow at all, - /// as all possible output fits in the range of `TimeDelta`. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, TimeDelta}; - /// - /// let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); - /// let since = NaiveDate::signed_duration_since; - /// - /// assert_eq!(since(from_ymd(2014, 1, 1), from_ymd(2014, 1, 1)), TimeDelta::zero()); - /// assert_eq!( - /// since(from_ymd(2014, 1, 1), from_ymd(2013, 12, 31)), - /// TimeDelta::try_days(1).unwrap() - /// ); - /// assert_eq!(since(from_ymd(2014, 1, 1), from_ymd(2014, 1, 2)), TimeDelta::try_days(-1).unwrap()); - /// assert_eq!( - /// since(from_ymd(2014, 1, 1), from_ymd(2013, 9, 23)), - /// TimeDelta::try_days(100).unwrap() - /// ); - /// assert_eq!( - /// since(from_ymd(2014, 1, 1), from_ymd(2013, 1, 1)), - /// TimeDelta::try_days(365).unwrap() - /// ); - /// assert_eq!( - /// since(from_ymd(2014, 1, 1), from_ymd(2010, 1, 1)), - /// TimeDelta::try_days(365 * 4 + 1).unwrap() - /// ); - /// assert_eq!( - /// since(from_ymd(2014, 1, 1), from_ymd(1614, 1, 1)), - /// TimeDelta::try_days(365 * 400 + 97).unwrap() - /// ); - /// ``` - #[must_use] - pub const fn signed_duration_since(self, rhs: Self) -> TimeDelta { - let year1 = self.year(); - let year2 = rhs.year(); - let (year1_div_400, year1_mod_400) = div_mod_floor(year1, 400); - let (year2_div_400, year2_mod_400) = div_mod_floor(year2, 400); - let cycle1 = yo_to_cycle(year1_mod_400 as u32, self.ordinal()) as i64; - let cycle2 = yo_to_cycle(year2_mod_400 as u32, rhs.ordinal()) as i64; - let days = (year1_div_400 as i64 - year2_div_400 as i64) * 146_097 + (cycle1 - cycle2); - // The range of `TimeDelta` is ca. 585 million years, the range of `NaiveDate` ca. 525.000 - // years. - expect(TimeDelta::try_days(days), "always in range") - } - - /// Returns the absolute difference between two `NaiveDate`s measured as the number of days. - /// - /// This is always an integer, non-negative number, similar to `abs_diff` in `std`. - /// - /// # Example - /// - /// ``` - /// # use chrono::{Days, NaiveDate}; - /// # - /// let date1: NaiveDate = "2020-01-01".parse().unwrap(); - /// let date2: NaiveDate = "2020-01-31".parse().unwrap(); - /// assert_eq!(date2.abs_diff(date1), Days::new(30)); - /// assert_eq!(date1.abs_diff(date2), Days::new(30)); - /// ``` - pub const fn abs_diff(self, rhs: Self) -> Days { - Days::new(i32::abs_diff(self.num_days_from_ce(), rhs.num_days_from_ce()) as u64) - } - - /// Returns the number of whole years from the given `base` until `self`. - /// - /// # Errors - /// - /// Returns `None` if `base > self`. - /// - /// # Example - /// - /// ``` - /// # use chrono::{NaiveDate}; - /// # - /// let base: NaiveDate = "2025-01-01".parse().unwrap(); - /// let date: NaiveDate = "2030-01-01".parse().unwrap(); - /// - /// assert_eq!(date.years_since(base), Some(5)) - /// ``` - #[must_use] - pub const fn years_since(&self, base: Self) -> Option { - let mut years = self.year() - base.year(); - // Comparing tuples is not (yet) possible in const context. Instead we combine month and - // day into one `u32` for easy comparison. - if ((self.month() << 5) | self.day()) < ((base.month() << 5) | base.day()) { - years -= 1; - } - - match years >= 0 { - true => Some(years as u32), - false => None, - } - } - - /// Formats the date with the specified formatting items. - /// Otherwise it is the same as the ordinary `format` method. - /// - /// The `Iterator` of items should be `Clone`able, - /// since the resulting `DelayedFormat` value may be formatted multiple times. - /// - /// # Example - /// - /// ``` - /// use chrono::format::strftime::StrftimeItems; - /// use chrono::NaiveDate; - /// - /// let fmt = StrftimeItems::new("%Y-%m-%d"); - /// let d = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap(); - /// assert_eq!(d.format_with_items(fmt.clone()).to_string(), "2015-09-05"); - /// assert_eq!(d.format("%Y-%m-%d").to_string(), "2015-09-05"); - /// ``` - /// - /// The resulting `DelayedFormat` can be formatted directly via the `Display` trait. - /// - /// ``` - /// # use chrono::NaiveDate; - /// # use chrono::format::strftime::StrftimeItems; - /// # let fmt = StrftimeItems::new("%Y-%m-%d").clone(); - /// # let d = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap(); - /// assert_eq!(format!("{}", d.format_with_items(fmt)), "2015-09-05"); - /// ``` - #[cfg(feature = "alloc")] - #[inline] - #[must_use] - pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat - where - I: Iterator + Clone, - B: Borrow>, - { - DelayedFormat::new(Some(*self), None, items) - } - - /// Formats the date with the specified format string. - /// See the [`format::strftime` module](crate::format::strftime) - /// on the supported escape sequences. - /// - /// This returns a `DelayedFormat`, - /// which gets converted to a string only when actual formatting happens. - /// You may use the `to_string` method to get a `String`, - /// or just feed it into `print!` and other formatting macros. - /// (In this way it avoids the redundant memory allocation.) - /// - /// # Panics - /// - /// Converting or formatting the returned `DelayedFormat` panics if the format string is wrong. - /// Because of this delayed failure, you are recommended to immediately use the `DelayedFormat` - /// value. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDate; - /// - /// let d = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap(); - /// assert_eq!(d.format("%Y-%m-%d").to_string(), "2015-09-05"); - /// assert_eq!(d.format("%A, %-d %B, %C%y").to_string(), "Saturday, 5 September, 2015"); - /// ``` - /// - /// The resulting `DelayedFormat` can be formatted directly via the `Display` trait. - /// - /// ``` - /// # use chrono::NaiveDate; - /// # let d = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap(); - /// assert_eq!(format!("{}", d.format("%Y-%m-%d")), "2015-09-05"); - /// assert_eq!(format!("{}", d.format("%A, %-d %B, %C%y")), "Saturday, 5 September, 2015"); - /// ``` - #[cfg(feature = "alloc")] - #[inline] - #[must_use] - pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat> { - self.format_with_items(StrftimeItems::new(fmt)) - } - - /// Formats the date with the specified formatting items and locale. - #[cfg(all(feature = "unstable-locales", feature = "alloc"))] - #[inline] - #[must_use] - pub fn format_localized_with_items<'a, I, B>( - &self, - items: I, - locale: Locale, - ) -> DelayedFormat - where - I: Iterator + Clone, - B: Borrow>, - { - DelayedFormat::new_with_locale(Some(*self), None, items, locale) - } - - /// Formats the date with the specified format string and locale. - /// - /// See the [`crate::format::strftime`] module on the supported escape - /// sequences. - #[cfg(all(feature = "unstable-locales", feature = "alloc"))] - #[inline] - #[must_use] - pub fn format_localized<'a>( - &self, - fmt: &'a str, - locale: Locale, - ) -> DelayedFormat> { - self.format_localized_with_items(StrftimeItems::new_with_locale(fmt, locale), locale) - } - - /// Returns an iterator that steps by days across all representable dates. - /// - /// # Example - /// - /// ``` - /// # use chrono::NaiveDate; - /// - /// let expected = [ - /// NaiveDate::from_ymd_opt(2016, 2, 27).unwrap(), - /// NaiveDate::from_ymd_opt(2016, 2, 28).unwrap(), - /// NaiveDate::from_ymd_opt(2016, 2, 29).unwrap(), - /// NaiveDate::from_ymd_opt(2016, 3, 1).unwrap(), - /// ]; - /// - /// let mut count = 0; - /// for (idx, d) in NaiveDate::from_ymd_opt(2016, 2, 27).unwrap().iter_days().take(4).enumerate() { - /// assert_eq!(d, expected[idx]); - /// count += 1; - /// } - /// assert_eq!(count, 4); - /// - /// for d in NaiveDate::from_ymd_opt(2016, 3, 1).unwrap().iter_days().rev().take(4) { - /// count -= 1; - /// assert_eq!(d, expected[count]); - /// } - /// ``` - #[inline] - pub const fn iter_days(&self) -> NaiveDateDaysIterator { - NaiveDateDaysIterator { value: *self } - } - - /// Returns an iterator that steps by weeks across all representable dates. - /// - /// # Example - /// - /// ``` - /// # use chrono::NaiveDate; - /// - /// let expected = [ - /// NaiveDate::from_ymd_opt(2016, 2, 27).unwrap(), - /// NaiveDate::from_ymd_opt(2016, 3, 5).unwrap(), - /// NaiveDate::from_ymd_opt(2016, 3, 12).unwrap(), - /// NaiveDate::from_ymd_opt(2016, 3, 19).unwrap(), - /// ]; - /// - /// let mut count = 0; - /// for (idx, d) in NaiveDate::from_ymd_opt(2016, 2, 27).unwrap().iter_weeks().take(4).enumerate() { - /// assert_eq!(d, expected[idx]); - /// count += 1; - /// } - /// assert_eq!(count, 4); - /// - /// for d in NaiveDate::from_ymd_opt(2016, 3, 19).unwrap().iter_weeks().rev().take(4) { - /// count -= 1; - /// assert_eq!(d, expected[count]); - /// } - /// ``` - #[inline] - pub const fn iter_weeks(&self) -> NaiveDateWeeksIterator { - NaiveDateWeeksIterator { value: *self } - } - - /// Returns the [`NaiveWeek`] that the date belongs to, starting with the [`Weekday`] - /// specified. - #[inline] - pub const fn week(&self, start: Weekday) -> NaiveWeek { - NaiveWeek::new(*self, start) - } - - /// Returns `true` if this is a leap year. - /// - /// ``` - /// # use chrono::NaiveDate; - /// assert_eq!(NaiveDate::from_ymd_opt(2000, 1, 1).unwrap().leap_year(), true); - /// assert_eq!(NaiveDate::from_ymd_opt(2001, 1, 1).unwrap().leap_year(), false); - /// assert_eq!(NaiveDate::from_ymd_opt(2002, 1, 1).unwrap().leap_year(), false); - /// assert_eq!(NaiveDate::from_ymd_opt(2003, 1, 1).unwrap().leap_year(), false); - /// assert_eq!(NaiveDate::from_ymd_opt(2004, 1, 1).unwrap().leap_year(), true); - /// assert_eq!(NaiveDate::from_ymd_opt(2100, 1, 1).unwrap().leap_year(), false); - /// ``` - pub const fn leap_year(&self) -> bool { - self.yof() & (0b1000) == 0 - } - - // This duplicates `Datelike::year()`, because trait methods can't be const yet. - #[inline] - const fn year(&self) -> i32 { - self.yof() >> 13 - } - - /// Returns the day of year starting from 1. - // This duplicates `Datelike::ordinal()`, because trait methods can't be const yet. - #[inline] - const fn ordinal(&self) -> u32 { - ((self.yof() & ORDINAL_MASK) >> 4) as u32 - } - - // This duplicates `Datelike::month()`, because trait methods can't be const yet. - #[inline] - const fn month(&self) -> u32 { - self.mdf().month() - } - - // This duplicates `Datelike::day()`, because trait methods can't be const yet. - #[inline] - const fn day(&self) -> u32 { - self.mdf().day() - } - - /// Returns the day of week. - // This duplicates `Datelike::weekday()`, because trait methods can't be const yet. - #[inline] - pub(super) const fn weekday(&self) -> Weekday { - match (((self.yof() & ORDINAL_MASK) >> 4) + (self.yof() & WEEKDAY_FLAGS_MASK)) % 7 { - 0 => Weekday::Mon, - 1 => Weekday::Tue, - 2 => Weekday::Wed, - 3 => Weekday::Thu, - 4 => Weekday::Fri, - 5 => Weekday::Sat, - _ => Weekday::Sun, - } - } - - #[inline] - const fn year_flags(&self) -> YearFlags { - YearFlags((self.yof() & YEAR_FLAGS_MASK) as u8) - } - - /// Counts the days in the proleptic Gregorian calendar, with January 1, Year 1 (CE) as day 1. - // This duplicates `Datelike::num_days_from_ce()`, because trait methods can't be const yet. - pub(crate) const fn num_days_from_ce(&self) -> i32 { - // we know this wouldn't overflow since year is limited to 1/2^13 of i32's full range. - let mut year = self.year() - 1; - let mut ndays = 0; - if year < 0 { - let excess = 1 + (-year) / 400; - year += excess * 400; - ndays -= excess * 146_097; - } - let div_100 = year / 100; - ndays += ((year * 1461) >> 2) - div_100 + (div_100 >> 2); - ndays + self.ordinal() as i32 - } - - /// Counts the days in the proleptic Gregorian calendar, with January 1, Year 1970 as day 0. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDate; - /// - /// let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); - /// - /// assert_eq!(from_ymd(1, 1, 1).to_epoch_days(), -719162); - /// assert_eq!(from_ymd(1970, 1, 1).to_epoch_days(), 0); - /// assert_eq!(from_ymd(2005, 9, 10).to_epoch_days(), 13036); - /// ``` - pub const fn to_epoch_days(&self) -> i32 { - self.num_days_from_ce() - UNIX_EPOCH_DAY as i32 - } - - /// Create a new `NaiveDate` from a raw year-ordinal-flags `i32`. - /// - /// In a valid value an ordinal is never `0`, and neither are the year flags. This method - /// doesn't do any validation in release builds. - #[inline] - const fn from_yof(yof: i32) -> NaiveDate { - // The following are the invariants our ordinal and flags should uphold for a valid - // `NaiveDate`. - debug_assert!(((yof & OL_MASK) >> 3) > 1); - debug_assert!(((yof & OL_MASK) >> 3) <= MAX_OL); - debug_assert!((yof & 0b111) != 000); - NaiveDate { yof: unsafe { NonZeroI32::new_unchecked(yof) } } - } - - /// Get the raw year-ordinal-flags `i32`. - #[inline] - const fn yof(&self) -> i32 { - self.yof.get() - } - - /// The minimum possible `NaiveDate` (January 1, 262144 BCE). - pub const MIN: NaiveDate = NaiveDate::from_yof((MIN_YEAR << 13) | (1 << 4) | 0o12 /* D */); - /// The maximum possible `NaiveDate` (December 31, 262142 CE). - pub const MAX: NaiveDate = - NaiveDate::from_yof((MAX_YEAR << 13) | (365 << 4) | 0o16 /* G */); - - /// One day before the minimum possible `NaiveDate` (December 31, 262145 BCE). - pub(crate) const BEFORE_MIN: NaiveDate = - NaiveDate::from_yof(((MIN_YEAR - 1) << 13) | (366 << 4) | 0o07 /* FE */); - /// One day after the maximum possible `NaiveDate` (January 1, 262143 CE). - pub(crate) const AFTER_MAX: NaiveDate = - NaiveDate::from_yof(((MAX_YEAR + 1) << 13) | (1 << 4) | 0o17 /* F */); -} - -impl Datelike for NaiveDate { - /// Returns the year number in the [calendar date](#calendar-date). - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate}; - /// - /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().year(), 2015); - /// assert_eq!(NaiveDate::from_ymd_opt(-308, 3, 14).unwrap().year(), -308); // 309 BCE - /// ``` - #[inline] - fn year(&self) -> i32 { - self.year() - } - - /// Returns the month number starting from 1. - /// - /// The return value ranges from 1 to 12. - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate}; - /// - /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().month(), 9); - /// assert_eq!(NaiveDate::from_ymd_opt(-308, 3, 14).unwrap().month(), 3); - /// ``` - #[inline] - fn month(&self) -> u32 { - self.month() - } - - /// Returns the month number starting from 0. - /// - /// The return value ranges from 0 to 11. - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate}; - /// - /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().month0(), 8); - /// assert_eq!(NaiveDate::from_ymd_opt(-308, 3, 14).unwrap().month0(), 2); - /// ``` - #[inline] - fn month0(&self) -> u32 { - self.month() - 1 - } - - /// Returns the day of month starting from 1. - /// - /// The return value ranges from 1 to 31. (The last day of month differs by months.) - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate}; - /// - /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().day(), 8); - /// assert_eq!(NaiveDate::from_ymd_opt(-308, 3, 14).unwrap().day(), 14); - /// ``` - /// - /// Combined with [`NaiveDate::pred_opt`](#method.pred_opt), - /// one can determine the number of days in a particular month. - /// (Note that this panics when `year` is out of range.) - /// - /// ``` - /// use chrono::{Datelike, NaiveDate}; - /// - /// fn ndays_in_month(year: i32, month: u32) -> u32 { - /// // the first day of the next month... - /// let (y, m) = if month == 12 { (year + 1, 1) } else { (year, month + 1) }; - /// let d = NaiveDate::from_ymd_opt(y, m, 1).unwrap(); - /// - /// // ...is preceded by the last day of the original month - /// d.pred_opt().unwrap().day() - /// } - /// - /// assert_eq!(ndays_in_month(2015, 8), 31); - /// assert_eq!(ndays_in_month(2015, 9), 30); - /// assert_eq!(ndays_in_month(2015, 12), 31); - /// assert_eq!(ndays_in_month(2016, 2), 29); - /// assert_eq!(ndays_in_month(2017, 2), 28); - /// ``` - #[inline] - fn day(&self) -> u32 { - self.day() - } - - /// Returns the day of month starting from 0. - /// - /// The return value ranges from 0 to 30. (The last day of month differs by months.) - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate}; - /// - /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().day0(), 7); - /// assert_eq!(NaiveDate::from_ymd_opt(-308, 3, 14).unwrap().day0(), 13); - /// ``` - #[inline] - fn day0(&self) -> u32 { - self.mdf().day() - 1 - } - - /// Returns the day of year starting from 1. - /// - /// The return value ranges from 1 to 366. (The last day of year differs by years.) - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate}; - /// - /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().ordinal(), 251); - /// assert_eq!(NaiveDate::from_ymd_opt(-308, 3, 14).unwrap().ordinal(), 74); - /// ``` - /// - /// Combined with [`NaiveDate::pred_opt`](#method.pred_opt), - /// one can determine the number of days in a particular year. - /// (Note that this panics when `year` is out of range.) - /// - /// ``` - /// use chrono::{Datelike, NaiveDate}; - /// - /// fn ndays_in_year(year: i32) -> u32 { - /// // the first day of the next year... - /// let d = NaiveDate::from_ymd_opt(year + 1, 1, 1).unwrap(); - /// - /// // ...is preceded by the last day of the original year - /// d.pred_opt().unwrap().ordinal() - /// } - /// - /// assert_eq!(ndays_in_year(2015), 365); - /// assert_eq!(ndays_in_year(2016), 366); - /// assert_eq!(ndays_in_year(2017), 365); - /// assert_eq!(ndays_in_year(2000), 366); - /// assert_eq!(ndays_in_year(2100), 365); - /// ``` - #[inline] - fn ordinal(&self) -> u32 { - ((self.yof() & ORDINAL_MASK) >> 4) as u32 - } - - /// Returns the day of year starting from 0. - /// - /// The return value ranges from 0 to 365. (The last day of year differs by years.) - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate}; - /// - /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().ordinal0(), 250); - /// assert_eq!(NaiveDate::from_ymd_opt(-308, 3, 14).unwrap().ordinal0(), 73); - /// ``` - #[inline] - fn ordinal0(&self) -> u32 { - self.ordinal() - 1 - } - - /// Returns the day of week. - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate, Weekday}; - /// - /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().weekday(), Weekday::Tue); - /// assert_eq!(NaiveDate::from_ymd_opt(-308, 3, 14).unwrap().weekday(), Weekday::Fri); - /// ``` - #[inline] - fn weekday(&self) -> Weekday { - self.weekday() - } - - #[inline] - fn iso_week(&self) -> IsoWeek { - IsoWeek::from_yof(self.year(), self.ordinal(), self.year_flags()) - } - - /// Makes a new `NaiveDate` with the year number changed, while keeping the same month and day. - /// - /// This method assumes you want to work on the date as a year-month-day value. Don't use it if - /// you want the ordinal to stay the same after changing the year, of if you want the week and - /// weekday values to stay the same. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The resulting date does not exist (February 29 in a non-leap year). - /// - The year is out of range for a `NaiveDate`. - /// - /// # Examples - /// - /// ``` - /// use chrono::{Datelike, NaiveDate}; - /// - /// assert_eq!( - /// NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().with_year(2016), - /// Some(NaiveDate::from_ymd_opt(2016, 9, 8).unwrap()) - /// ); - /// assert_eq!( - /// NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().with_year(-308), - /// Some(NaiveDate::from_ymd_opt(-308, 9, 8).unwrap()) - /// ); - /// ``` - /// - /// A leap day (February 29) is a case where this method can return `None`. - /// - /// ``` - /// # use chrono::{NaiveDate, Datelike}; - /// assert!(NaiveDate::from_ymd_opt(2016, 2, 29).unwrap().with_year(2015).is_none()); - /// assert!(NaiveDate::from_ymd_opt(2016, 2, 29).unwrap().with_year(2020).is_some()); - /// ``` - /// - /// Don't use `with_year` if you want the ordinal date to stay the same: - /// - /// ``` - /// # use chrono::{Datelike, NaiveDate}; - /// assert_ne!( - /// NaiveDate::from_yo_opt(2020, 100).unwrap().with_year(2023).unwrap(), - /// NaiveDate::from_yo_opt(2023, 100).unwrap() // result is 2023-101 - /// ); - /// ``` - #[inline] - fn with_year(&self, year: i32) -> Option { - // we need to operate with `mdf` since we should keep the month and day number as is - let mdf = self.mdf(); - - // adjust the flags as needed - let flags = YearFlags::from_year(year); - let mdf = mdf.with_flags(flags); - - NaiveDate::from_mdf(year, mdf) - } - - /// Makes a new `NaiveDate` with the month number (starting from 1) changed. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The resulting date does not exist (for example `month(4)` when day of the month is 31). - /// - The value for `month` is invalid. - /// - /// # Examples - /// - /// ``` - /// use chrono::{Datelike, NaiveDate}; - /// - /// assert_eq!( - /// NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().with_month(10), - /// Some(NaiveDate::from_ymd_opt(2015, 10, 8).unwrap()) - /// ); - /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().with_month(13), None); // No month 13 - /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 30).unwrap().with_month(2), None); // No Feb 30 - /// ``` - /// - /// Don't combine multiple `Datelike::with_*` methods. The intermediate value may not exist. - /// - /// ``` - /// use chrono::{Datelike, NaiveDate}; - /// - /// fn with_year_month(date: NaiveDate, year: i32, month: u32) -> Option { - /// date.with_year(year)?.with_month(month) - /// } - /// let d = NaiveDate::from_ymd_opt(2020, 2, 29).unwrap(); - /// assert!(with_year_month(d, 2019, 1).is_none()); // fails because of invalid intermediate value - /// - /// // Correct version: - /// fn with_year_month_fixed(date: NaiveDate, year: i32, month: u32) -> Option { - /// NaiveDate::from_ymd_opt(year, month, date.day()) - /// } - /// let d = NaiveDate::from_ymd_opt(2020, 2, 29).unwrap(); - /// assert_eq!(with_year_month_fixed(d, 2019, 1), NaiveDate::from_ymd_opt(2019, 1, 29)); - /// ``` - #[inline] - fn with_month(&self, month: u32) -> Option { - self.with_mdf(self.mdf().with_month(month)?) - } - - /// Makes a new `NaiveDate` with the month number (starting from 0) changed. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The resulting date does not exist (for example `month0(3)` when day of the month is 31). - /// - The value for `month0` is invalid. - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate}; - /// - /// assert_eq!( - /// NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().with_month0(9), - /// Some(NaiveDate::from_ymd_opt(2015, 10, 8).unwrap()) - /// ); - /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().with_month0(12), None); // No month 12 - /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 30).unwrap().with_month0(1), None); // No Feb 30 - /// ``` - #[inline] - fn with_month0(&self, month0: u32) -> Option { - let month = month0.checked_add(1)?; - self.with_mdf(self.mdf().with_month(month)?) - } - - /// Makes a new `NaiveDate` with the day of month (starting from 1) changed. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The resulting date does not exist (for example `day(31)` in April). - /// - The value for `day` is invalid. - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate}; - /// - /// assert_eq!( - /// NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().with_day(30), - /// Some(NaiveDate::from_ymd_opt(2015, 9, 30).unwrap()) - /// ); - /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().with_day(31), None); - /// // no September 31 - /// ``` - #[inline] - fn with_day(&self, day: u32) -> Option { - self.with_mdf(self.mdf().with_day(day)?) - } - - /// Makes a new `NaiveDate` with the day of month (starting from 0) changed. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The resulting date does not exist (for example `day(30)` in April). - /// - The value for `day0` is invalid. - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate}; - /// - /// assert_eq!( - /// NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().with_day0(29), - /// Some(NaiveDate::from_ymd_opt(2015, 9, 30).unwrap()) - /// ); - /// assert_eq!(NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().with_day0(30), None); - /// // no September 31 - /// ``` - #[inline] - fn with_day0(&self, day0: u32) -> Option { - let day = day0.checked_add(1)?; - self.with_mdf(self.mdf().with_day(day)?) - } - - /// Makes a new `NaiveDate` with the day of year (starting from 1) changed. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The resulting date does not exist (`with_ordinal(366)` in a non-leap year). - /// - The value for `ordinal` is invalid. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, Datelike}; - /// - /// assert_eq!(NaiveDate::from_ymd_opt(2015, 1, 1).unwrap().with_ordinal(60), - /// Some(NaiveDate::from_ymd_opt(2015, 3, 1).unwrap())); - /// assert_eq!(NaiveDate::from_ymd_opt(2015, 1, 1).unwrap().with_ordinal(366), - /// None); // 2015 had only 365 days - /// - /// assert_eq!(NaiveDate::from_ymd_opt(2016, 1, 1).unwrap().with_ordinal(60), - /// Some(NaiveDate::from_ymd_opt(2016, 2, 29).unwrap())); - /// assert_eq!(NaiveDate::from_ymd_opt(2016, 1, 1).unwrap().with_ordinal(366), - /// Some(NaiveDate::from_ymd_opt(2016, 12, 31).unwrap())); - /// ``` - #[inline] - fn with_ordinal(&self, ordinal: u32) -> Option { - if ordinal == 0 || ordinal > 366 { - return None; - } - let yof = (self.yof() & !ORDINAL_MASK) | (ordinal << 4) as i32; - match yof & OL_MASK <= MAX_OL { - true => Some(NaiveDate::from_yof(yof)), - false => None, // Does not exist: Ordinal 366 in a common year. - } - } - - /// Makes a new `NaiveDate` with the day of year (starting from 0) changed. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The resulting date does not exist (`with_ordinal0(365)` in a non-leap year). - /// - The value for `ordinal0` is invalid. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, Datelike}; - /// - /// assert_eq!(NaiveDate::from_ymd_opt(2015, 1, 1).unwrap().with_ordinal0(59), - /// Some(NaiveDate::from_ymd_opt(2015, 3, 1).unwrap())); - /// assert_eq!(NaiveDate::from_ymd_opt(2015, 1, 1).unwrap().with_ordinal0(365), - /// None); // 2015 had only 365 days - /// - /// assert_eq!(NaiveDate::from_ymd_opt(2016, 1, 1).unwrap().with_ordinal0(59), - /// Some(NaiveDate::from_ymd_opt(2016, 2, 29).unwrap())); - /// assert_eq!(NaiveDate::from_ymd_opt(2016, 1, 1).unwrap().with_ordinal0(365), - /// Some(NaiveDate::from_ymd_opt(2016, 12, 31).unwrap())); - /// ``` - #[inline] - fn with_ordinal0(&self, ordinal0: u32) -> Option { - let ordinal = ordinal0.checked_add(1)?; - self.with_ordinal(ordinal) - } -} - -/// Add `TimeDelta` to `NaiveDate`. -/// -/// This discards the fractional days in `TimeDelta`, rounding to the closest integral number of -/// days towards `TimeDelta::zero()`. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using [`NaiveDate::checked_add_signed`] to get an `Option` instead. -/// -/// # Example -/// -/// ``` -/// use chrono::{NaiveDate, TimeDelta}; -/// -/// let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); -/// -/// assert_eq!(from_ymd(2014, 1, 1) + TimeDelta::zero(), from_ymd(2014, 1, 1)); -/// assert_eq!(from_ymd(2014, 1, 1) + TimeDelta::try_seconds(86399).unwrap(), from_ymd(2014, 1, 1)); -/// assert_eq!( -/// from_ymd(2014, 1, 1) + TimeDelta::try_seconds(-86399).unwrap(), -/// from_ymd(2014, 1, 1) -/// ); -/// assert_eq!(from_ymd(2014, 1, 1) + TimeDelta::try_days(1).unwrap(), from_ymd(2014, 1, 2)); -/// assert_eq!(from_ymd(2014, 1, 1) + TimeDelta::try_days(-1).unwrap(), from_ymd(2013, 12, 31)); -/// assert_eq!(from_ymd(2014, 1, 1) + TimeDelta::try_days(364).unwrap(), from_ymd(2014, 12, 31)); -/// assert_eq!( -/// from_ymd(2014, 1, 1) + TimeDelta::try_days(365 * 4 + 1).unwrap(), -/// from_ymd(2018, 1, 1) -/// ); -/// assert_eq!( -/// from_ymd(2014, 1, 1) + TimeDelta::try_days(365 * 400 + 97).unwrap(), -/// from_ymd(2414, 1, 1) -/// ); -/// ``` -/// -/// [`NaiveDate::checked_add_signed`]: crate::NaiveDate::checked_add_signed -impl Add for NaiveDate { - type Output = NaiveDate; - - #[inline] - #[track_caller] - fn add(self, rhs: TimeDelta) -> NaiveDate { - self.checked_add_signed(rhs).expect("`NaiveDate + TimeDelta` overflowed") - } -} - -/// Add-assign of `TimeDelta` to `NaiveDate`. -/// -/// This discards the fractional days in `TimeDelta`, rounding to the closest integral number of days -/// towards `TimeDelta::zero()`. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using [`NaiveDate::checked_add_signed`] to get an `Option` instead. -impl AddAssign for NaiveDate { - #[inline] - #[track_caller] - fn add_assign(&mut self, rhs: TimeDelta) { - *self = self.add(rhs); - } -} - -/// Add `Months` to `NaiveDate`. -/// -/// The result will be clamped to valid days in the resulting month, see `checked_add_months` for -/// details. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using `NaiveDate::checked_add_months` to get an `Option` instead. -/// -/// # Example -/// -/// ``` -/// use chrono::{Months, NaiveDate}; -/// -/// let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); -/// -/// assert_eq!(from_ymd(2014, 1, 1) + Months::new(1), from_ymd(2014, 2, 1)); -/// assert_eq!(from_ymd(2014, 1, 1) + Months::new(11), from_ymd(2014, 12, 1)); -/// assert_eq!(from_ymd(2014, 1, 1) + Months::new(12), from_ymd(2015, 1, 1)); -/// assert_eq!(from_ymd(2014, 1, 1) + Months::new(13), from_ymd(2015, 2, 1)); -/// assert_eq!(from_ymd(2014, 1, 31) + Months::new(1), from_ymd(2014, 2, 28)); -/// assert_eq!(from_ymd(2020, 1, 31) + Months::new(1), from_ymd(2020, 2, 29)); -/// ``` -impl Add for NaiveDate { - type Output = NaiveDate; - - #[track_caller] - fn add(self, months: Months) -> Self::Output { - self.checked_add_months(months).expect("`NaiveDate + Months` out of range") - } -} - -/// Subtract `Months` from `NaiveDate`. -/// -/// The result will be clamped to valid days in the resulting month, see `checked_sub_months` for -/// details. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using `NaiveDate::checked_sub_months` to get an `Option` instead. -/// -/// # Example -/// -/// ``` -/// use chrono::{Months, NaiveDate}; -/// -/// let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); -/// -/// assert_eq!(from_ymd(2014, 1, 1) - Months::new(11), from_ymd(2013, 2, 1)); -/// assert_eq!(from_ymd(2014, 1, 1) - Months::new(12), from_ymd(2013, 1, 1)); -/// assert_eq!(from_ymd(2014, 1, 1) - Months::new(13), from_ymd(2012, 12, 1)); -/// ``` -impl Sub for NaiveDate { - type Output = NaiveDate; - - #[track_caller] - fn sub(self, months: Months) -> Self::Output { - self.checked_sub_months(months).expect("`NaiveDate - Months` out of range") - } -} - -/// Add `Days` to `NaiveDate`. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using `NaiveDate::checked_add_days` to get an `Option` instead. -impl Add for NaiveDate { - type Output = NaiveDate; - - #[track_caller] - fn add(self, days: Days) -> Self::Output { - self.checked_add_days(days).expect("`NaiveDate + Days` out of range") - } -} - -/// Subtract `Days` from `NaiveDate`. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using `NaiveDate::checked_sub_days` to get an `Option` instead. -impl Sub for NaiveDate { - type Output = NaiveDate; - - #[track_caller] - fn sub(self, days: Days) -> Self::Output { - self.checked_sub_days(days).expect("`NaiveDate - Days` out of range") - } -} - -/// Subtract `TimeDelta` from `NaiveDate`. -/// -/// This discards the fractional days in `TimeDelta`, rounding to the closest integral number of -/// days towards `TimeDelta::zero()`. -/// It is the same as the addition with a negated `TimeDelta`. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using [`NaiveDate::checked_sub_signed`] to get an `Option` instead. -/// -/// # Example -/// -/// ``` -/// use chrono::{NaiveDate, TimeDelta}; -/// -/// let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); -/// -/// assert_eq!(from_ymd(2014, 1, 1) - TimeDelta::zero(), from_ymd(2014, 1, 1)); -/// assert_eq!(from_ymd(2014, 1, 1) - TimeDelta::try_seconds(86399).unwrap(), from_ymd(2014, 1, 1)); -/// assert_eq!( -/// from_ymd(2014, 1, 1) - TimeDelta::try_seconds(-86399).unwrap(), -/// from_ymd(2014, 1, 1) -/// ); -/// assert_eq!(from_ymd(2014, 1, 1) - TimeDelta::try_days(1).unwrap(), from_ymd(2013, 12, 31)); -/// assert_eq!(from_ymd(2014, 1, 1) - TimeDelta::try_days(-1).unwrap(), from_ymd(2014, 1, 2)); -/// assert_eq!(from_ymd(2014, 1, 1) - TimeDelta::try_days(364).unwrap(), from_ymd(2013, 1, 2)); -/// assert_eq!( -/// from_ymd(2014, 1, 1) - TimeDelta::try_days(365 * 4 + 1).unwrap(), -/// from_ymd(2010, 1, 1) -/// ); -/// assert_eq!( -/// from_ymd(2014, 1, 1) - TimeDelta::try_days(365 * 400 + 97).unwrap(), -/// from_ymd(1614, 1, 1) -/// ); -/// ``` -/// -/// [`NaiveDate::checked_sub_signed`]: crate::NaiveDate::checked_sub_signed -impl Sub for NaiveDate { - type Output = NaiveDate; - - #[inline] - #[track_caller] - fn sub(self, rhs: TimeDelta) -> NaiveDate { - self.checked_sub_signed(rhs).expect("`NaiveDate - TimeDelta` overflowed") - } -} - -/// Subtract-assign `TimeDelta` from `NaiveDate`. -/// -/// This discards the fractional days in `TimeDelta`, rounding to the closest integral number of -/// days towards `TimeDelta::zero()`. -/// It is the same as the addition with a negated `TimeDelta`. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using [`NaiveDate::checked_sub_signed`] to get an `Option` instead. -impl SubAssign for NaiveDate { - #[inline] - #[track_caller] - fn sub_assign(&mut self, rhs: TimeDelta) { - *self = self.sub(rhs); - } -} - -/// Subtracts another `NaiveDate` from the current date. -/// Returns a `TimeDelta` of integral numbers. -/// -/// This does not overflow or underflow at all, -/// as all possible output fits in the range of `TimeDelta`. -/// -/// The implementation is a wrapper around -/// [`NaiveDate::signed_duration_since`](#method.signed_duration_since). -/// -/// # Example -/// -/// ``` -/// use chrono::{NaiveDate, TimeDelta}; -/// -/// let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); -/// -/// assert_eq!(from_ymd(2014, 1, 1) - from_ymd(2014, 1, 1), TimeDelta::zero()); -/// assert_eq!(from_ymd(2014, 1, 1) - from_ymd(2013, 12, 31), TimeDelta::try_days(1).unwrap()); -/// assert_eq!(from_ymd(2014, 1, 1) - from_ymd(2014, 1, 2), TimeDelta::try_days(-1).unwrap()); -/// assert_eq!(from_ymd(2014, 1, 1) - from_ymd(2013, 9, 23), TimeDelta::try_days(100).unwrap()); -/// assert_eq!(from_ymd(2014, 1, 1) - from_ymd(2013, 1, 1), TimeDelta::try_days(365).unwrap()); -/// assert_eq!( -/// from_ymd(2014, 1, 1) - from_ymd(2010, 1, 1), -/// TimeDelta::try_days(365 * 4 + 1).unwrap() -/// ); -/// assert_eq!( -/// from_ymd(2014, 1, 1) - from_ymd(1614, 1, 1), -/// TimeDelta::try_days(365 * 400 + 97).unwrap() -/// ); -/// ``` -impl Sub for NaiveDate { - type Output = TimeDelta; - - #[inline] - fn sub(self, rhs: NaiveDate) -> TimeDelta { - self.signed_duration_since(rhs) - } -} - -impl From for NaiveDate { - fn from(naive_datetime: NaiveDateTime) -> Self { - naive_datetime.date() - } -} - -/// Iterator over `NaiveDate` with a step size of one day. -#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Eq, Ord)] -pub struct NaiveDateDaysIterator { - value: NaiveDate, -} - -impl Iterator for NaiveDateDaysIterator { - type Item = NaiveDate; - - fn next(&mut self) -> Option { - // We return the current value, and have no way to return `NaiveDate::MAX`. - let current = self.value; - // This can't panic because current is < NaiveDate::MAX: - self.value = current.succ_opt()?; - Some(current) - } - - fn size_hint(&self) -> (usize, Option) { - let exact_size = NaiveDate::MAX.signed_duration_since(self.value).num_days(); - (exact_size as usize, Some(exact_size as usize)) - } -} - -impl ExactSizeIterator for NaiveDateDaysIterator {} - -impl DoubleEndedIterator for NaiveDateDaysIterator { - fn next_back(&mut self) -> Option { - // We return the current value, and have no way to return `NaiveDate::MIN`. - let current = self.value; - self.value = current.pred_opt()?; - Some(current) - } -} - -impl FusedIterator for NaiveDateDaysIterator {} - -/// Iterator over `NaiveDate` with a step size of one week. -#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Eq, Ord)] -pub struct NaiveDateWeeksIterator { - value: NaiveDate, -} - -impl Iterator for NaiveDateWeeksIterator { - type Item = NaiveDate; - - fn next(&mut self) -> Option { - let current = self.value; - self.value = current.checked_add_days(Days::new(7))?; - Some(current) - } - - fn size_hint(&self) -> (usize, Option) { - let exact_size = NaiveDate::MAX.signed_duration_since(self.value).num_weeks(); - (exact_size as usize, Some(exact_size as usize)) - } -} - -impl ExactSizeIterator for NaiveDateWeeksIterator {} - -impl DoubleEndedIterator for NaiveDateWeeksIterator { - fn next_back(&mut self) -> Option { - let current = self.value; - self.value = current.checked_sub_days(Days::new(7))?; - Some(current) - } -} - -impl FusedIterator for NaiveDateWeeksIterator {} - -/// The `Debug` output of the naive date `d` is the same as -/// [`d.format("%Y-%m-%d")`](crate::format::strftime). -/// -/// The string printed can be readily parsed via the `parse` method on `str`. -/// -/// # Example -/// -/// ``` -/// use chrono::NaiveDate; -/// -/// assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(2015, 9, 5).unwrap()), "2015-09-05"); -/// assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(0, 1, 1).unwrap()), "0000-01-01"); -/// assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(9999, 12, 31).unwrap()), "9999-12-31"); -/// ``` -/// -/// ISO 8601 requires an explicit sign for years before 1 BCE or after 9999 CE. -/// -/// ``` -/// # use chrono::NaiveDate; -/// assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(-1, 1, 1).unwrap()), "-0001-01-01"); -/// assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(10000, 12, 31).unwrap()), "+10000-12-31"); -/// ``` -impl fmt::Debug for NaiveDate { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use core::fmt::Write; - - let year = self.year(); - let mdf = self.mdf(); - if (0..=9999).contains(&year) { - write_hundreds(f, (year / 100) as u8)?; - write_hundreds(f, (year % 100) as u8)?; - } else { - // ISO 8601 requires the explicit sign for out-of-range years - write!(f, "{year:+05}")?; - } - - f.write_char('-')?; - write_hundreds(f, mdf.month() as u8)?; - f.write_char('-')?; - write_hundreds(f, mdf.day() as u8) - } -} - -#[cfg(feature = "defmt")] -impl defmt::Format for NaiveDate { - fn format(&self, fmt: defmt::Formatter) { - let year = self.year(); - let mdf = self.mdf(); - if (0..=9999).contains(&year) { - defmt::write!(fmt, "{:02}{:02}", year / 100, year % 100); - } else { - // ISO 8601 requires the explicit sign for out-of-range years - let sign = ['+', '-'][(year < 0) as usize]; - defmt::write!(fmt, "{}{:05}", sign, year.abs()); - } - - defmt::write!(fmt, "-{:02}-{:02}", mdf.month(), mdf.day()); - } -} - -/// The `Display` output of the naive date `d` is the same as -/// [`d.format("%Y-%m-%d")`](crate::format::strftime). -/// -/// The string printed can be readily parsed via the `parse` method on `str`. -/// -/// # Example -/// -/// ``` -/// use chrono::NaiveDate; -/// -/// assert_eq!(format!("{}", NaiveDate::from_ymd_opt(2015, 9, 5).unwrap()), "2015-09-05"); -/// assert_eq!(format!("{}", NaiveDate::from_ymd_opt(0, 1, 1).unwrap()), "0000-01-01"); -/// assert_eq!(format!("{}", NaiveDate::from_ymd_opt(9999, 12, 31).unwrap()), "9999-12-31"); -/// ``` -/// -/// ISO 8601 requires an explicit sign for years before 1 BCE or after 9999 CE. -/// -/// ``` -/// # use chrono::NaiveDate; -/// assert_eq!(format!("{}", NaiveDate::from_ymd_opt(-1, 1, 1).unwrap()), "-0001-01-01"); -/// assert_eq!(format!("{}", NaiveDate::from_ymd_opt(10000, 12, 31).unwrap()), "+10000-12-31"); -/// ``` -impl fmt::Display for NaiveDate { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(self, f) - } -} - -/// Parsing a `str` into a `NaiveDate` uses the same format, -/// [`%Y-%m-%d`](crate::format::strftime), as in `Debug` and `Display`. -/// -/// # Example -/// -/// ``` -/// use chrono::NaiveDate; -/// -/// let d = NaiveDate::from_ymd_opt(2015, 9, 18).unwrap(); -/// assert_eq!("2015-09-18".parse::(), Ok(d)); -/// -/// let d = NaiveDate::from_ymd_opt(12345, 6, 7).unwrap(); -/// assert_eq!("+12345-6-7".parse::(), Ok(d)); -/// -/// assert!("foo".parse::().is_err()); -/// ``` -impl str::FromStr for NaiveDate { - type Err = ParseError; - - fn from_str(s: &str) -> ParseResult { - const ITEMS: &[Item<'static>] = &[ - Item::Numeric(Numeric::Year, Pad::Zero), - Item::Space(""), - Item::Literal("-"), - Item::Numeric(Numeric::Month, Pad::Zero), - Item::Space(""), - Item::Literal("-"), - Item::Numeric(Numeric::Day, Pad::Zero), - Item::Space(""), - ]; - - let mut parsed = Parsed::new(); - parse(&mut parsed, s, ITEMS.iter())?; - parsed.to_naive_date() - } -} - -/// The default value for a NaiveDate is 1st of January 1970. -/// -/// # Example -/// -/// ```rust -/// use chrono::NaiveDate; -/// -/// let default_date = NaiveDate::default(); -/// assert_eq!(default_date, NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()); -/// ``` -impl Default for NaiveDate { - fn default() -> Self { - NaiveDate::from_ymd_opt(1970, 1, 1).unwrap() - } -} - -const fn cycle_to_yo(cycle: u32) -> (u32, u32) { - let mut year_mod_400 = cycle / 365; - let mut ordinal0 = cycle % 365; - let delta = YEAR_DELTAS[year_mod_400 as usize] as u32; - if ordinal0 < delta { - year_mod_400 -= 1; - ordinal0 += 365 - YEAR_DELTAS[year_mod_400 as usize] as u32; - } else { - ordinal0 -= delta; - } - (year_mod_400, ordinal0 + 1) -} - -const fn yo_to_cycle(year_mod_400: u32, ordinal: u32) -> u32 { - year_mod_400 * 365 + YEAR_DELTAS[year_mod_400 as usize] as u32 + ordinal - 1 -} - -const fn div_mod_floor(val: i32, div: i32) -> (i32, i32) { - (val.div_euclid(div), val.rem_euclid(div)) -} - -/// MAX_YEAR is one year less than the type is capable of representing. Internally we may sometimes -/// use the headroom, notably to handle cases where the offset of a `DateTime` constructed with -/// `NaiveDate::MAX` pushes it beyond the valid, representable range. -pub(super) const MAX_YEAR: i32 = (i32::MAX >> 13) - 1; - -/// MIN_YEAR is one year more than the type is capable of representing. Internally we may sometimes -/// use the headroom, notably to handle cases where the offset of a `DateTime` constructed with -/// `NaiveDate::MIN` pushes it beyond the valid, representable range. -pub(super) const MIN_YEAR: i32 = (i32::MIN >> 13) + 1; - -const ORDINAL_MASK: i32 = 0b1_1111_1111_0000; - -const LEAP_YEAR_MASK: i32 = 0b1000; - -// OL: ordinal and leap year flag. -// With only these parts of the date an ordinal 366 in a common year would be encoded as -// `((366 << 1) | 1) << 3`, and in a leap year as `((366 << 1) | 0) << 3`, which is less. -// This allows for efficiently checking the ordinal exists depending on whether this is a leap year. -const OL_MASK: i32 = ORDINAL_MASK | LEAP_YEAR_MASK; -const MAX_OL: i32 = 366 << 4; - -// Weekday of the last day in the preceding year. -// Allows for quick day of week calculation from the 1-based ordinal. -const WEEKDAY_FLAGS_MASK: i32 = 0b111; - -const YEAR_FLAGS_MASK: i32 = LEAP_YEAR_MASK | WEEKDAY_FLAGS_MASK; - -const YEAR_DELTAS: &[u8; 401] = &[ - 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, - 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, - 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, - 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, // 100 - 25, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, - 30, 31, 31, 31, 31, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 36, - 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39, 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, - 42, 43, 43, 43, 43, 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 48, 48, 48, - 48, 49, 49, 49, // 200 - 49, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51, 52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, - 54, 55, 55, 55, 55, 56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, 60, 60, 60, - 60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63, 64, 64, 64, 64, 65, 65, 65, 65, 66, 66, 66, - 66, 67, 67, 67, 67, 68, 68, 68, 68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 72, 72, 72, - 72, 73, 73, 73, // 300 - 73, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 76, 76, 76, 76, 77, 77, 77, 77, 78, 78, 78, - 78, 79, 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 82, 82, 82, 82, 83, 83, 83, 83, 84, 84, 84, - 84, 85, 85, 85, 85, 86, 86, 86, 86, 87, 87, 87, 87, 88, 88, 88, 88, 89, 89, 89, 89, 90, 90, 90, - 90, 91, 91, 91, 91, 92, 92, 92, 92, 93, 93, 93, 93, 94, 94, 94, 94, 95, 95, 95, 95, 96, 96, 96, - 96, 97, 97, 97, 97, // 400+1 -]; - -#[cfg(feature = "serde")] -mod serde { - use super::NaiveDate; - use core::fmt; - use serde::{de, ser}; - - // TODO not very optimized for space (binary formats would want something better) - - impl ser::Serialize for NaiveDate { - fn serialize(&self, serializer: S) -> Result - where - S: ser::Serializer, - { - struct FormatWrapped<'a, D: 'a> { - inner: &'a D, - } - - impl fmt::Display for FormatWrapped<'_, D> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.inner.fmt(f) - } - } - - serializer.collect_str(&FormatWrapped { inner: &self }) - } - } - - struct NaiveDateVisitor; - - impl de::Visitor<'_> for NaiveDateVisitor { - type Value = NaiveDate; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a formatted date string") - } - - fn visit_str(self, value: &str) -> Result - where - E: de::Error, - { - value.parse().map_err(E::custom) - } - } - - impl<'de> de::Deserialize<'de> for NaiveDate { - fn deserialize(deserializer: D) -> Result - where - D: de::Deserializer<'de>, - { - deserializer.deserialize_str(NaiveDateVisitor) - } - } - - #[cfg(test)] - mod tests { - use crate::NaiveDate; - - #[test] - fn test_serde_serialize() { - assert_eq!( - serde_json::to_string(&NaiveDate::from_ymd_opt(2014, 7, 24).unwrap()).ok(), - Some(r#""2014-07-24""#.into()) - ); - assert_eq!( - serde_json::to_string(&NaiveDate::from_ymd_opt(0, 1, 1).unwrap()).ok(), - Some(r#""0000-01-01""#.into()) - ); - assert_eq!( - serde_json::to_string(&NaiveDate::from_ymd_opt(-1, 12, 31).unwrap()).ok(), - Some(r#""-0001-12-31""#.into()) - ); - assert_eq!( - serde_json::to_string(&NaiveDate::MIN).ok(), - Some(r#""-262143-01-01""#.into()) - ); - assert_eq!( - serde_json::to_string(&NaiveDate::MAX).ok(), - Some(r#""+262142-12-31""#.into()) - ); - } - - #[test] - fn test_serde_deserialize() { - let from_str = serde_json::from_str::; - - assert_eq!( - from_str(r#""2016-07-08""#).ok(), - Some(NaiveDate::from_ymd_opt(2016, 7, 8).unwrap()) - ); - assert_eq!( - from_str(r#""2016-7-8""#).ok(), - Some(NaiveDate::from_ymd_opt(2016, 7, 8).unwrap()) - ); - assert_eq!(from_str(r#""+002016-07-08""#).ok(), NaiveDate::from_ymd_opt(2016, 7, 8)); - assert_eq!( - from_str(r#""0000-01-01""#).ok(), - Some(NaiveDate::from_ymd_opt(0, 1, 1).unwrap()) - ); - assert_eq!( - from_str(r#""0-1-1""#).ok(), - Some(NaiveDate::from_ymd_opt(0, 1, 1).unwrap()) - ); - assert_eq!( - from_str(r#""-0001-12-31""#).ok(), - Some(NaiveDate::from_ymd_opt(-1, 12, 31).unwrap()) - ); - assert_eq!(from_str(r#""-262143-01-01""#).ok(), Some(NaiveDate::MIN)); - assert_eq!(from_str(r#""+262142-12-31""#).ok(), Some(NaiveDate::MAX)); - - // bad formats - assert!(from_str(r#""""#).is_err()); - assert!(from_str(r#""20001231""#).is_err()); - assert!(from_str(r#""2000-00-00""#).is_err()); - assert!(from_str(r#""2000-02-30""#).is_err()); - assert!(from_str(r#""2001-02-29""#).is_err()); - assert!(from_str(r#""2002-002-28""#).is_err()); - assert!(from_str(r#""yyyy-mm-dd""#).is_err()); - assert!(from_str(r#"0"#).is_err()); - assert!(from_str(r#"20.01"#).is_err()); - let min = i32::MIN.to_string(); - assert!(from_str(&min).is_err()); - let max = i32::MAX.to_string(); - assert!(from_str(&max).is_err()); - let min = i64::MIN.to_string(); - assert!(from_str(&min).is_err()); - let max = i64::MAX.to_string(); - assert!(from_str(&max).is_err()); - assert!(from_str(r#"{}"#).is_err()); - } - - #[test] - fn test_serde_bincode() { - // Bincode is relevant to test separately from JSON because - // it is not self-describing. - use bincode::{deserialize, serialize}; - - let d = NaiveDate::from_ymd_opt(2014, 7, 24).unwrap(); - let encoded = serialize(&d).unwrap(); - let decoded: NaiveDate = deserialize(&encoded).unwrap(); - assert_eq!(d, decoded); - } - } -} diff --git a/chrono-0.4.44/src/naive/date/tests.rs b/chrono-0.4.44/src/naive/date/tests.rs deleted file mode 100644 index 0b47ca439e..0000000000 --- a/chrono-0.4.44/src/naive/date/tests.rs +++ /dev/null @@ -1,920 +0,0 @@ -use super::{Days, MAX_YEAR, MIN_YEAR, Months, NaiveDate}; -use crate::naive::internals::{A, AG, B, BA, C, CB, D, DC, E, ED, F, FE, G, GF, YearFlags}; -use crate::{Datelike, TimeDelta, Weekday}; - -// as it is hard to verify year flags in `NaiveDate::MIN` and `NaiveDate::MAX`, -// we use a separate run-time test. -#[test] -fn test_date_bounds() { - let calculated_min = NaiveDate::from_ymd_opt(MIN_YEAR, 1, 1).unwrap(); - let calculated_max = NaiveDate::from_ymd_opt(MAX_YEAR, 12, 31).unwrap(); - assert!( - NaiveDate::MIN == calculated_min, - "`NaiveDate::MIN` should have year flag {:?}", - calculated_min.year_flags() - ); - assert!( - NaiveDate::MAX == calculated_max, - "`NaiveDate::MAX` should have year flag {:?} and ordinal {}", - calculated_max.year_flags(), - calculated_max.ordinal() - ); - - // let's also check that the entire range do not exceed 2^44 seconds - // (sometimes used for bounding `TimeDelta` against overflow) - let maxsecs = NaiveDate::MAX.signed_duration_since(NaiveDate::MIN).num_seconds(); - let maxsecs = maxsecs + 86401; // also take care of DateTime - assert!( - maxsecs < (1 << MAX_BITS), - "The entire `NaiveDate` range somehow exceeds 2^{MAX_BITS} seconds" - ); - - const BEFORE_MIN: NaiveDate = NaiveDate::BEFORE_MIN; - assert_eq!(BEFORE_MIN.year_flags(), YearFlags::from_year(BEFORE_MIN.year())); - assert_eq!((BEFORE_MIN.month(), BEFORE_MIN.day()), (12, 31)); - - const AFTER_MAX: NaiveDate = NaiveDate::AFTER_MAX; - assert_eq!(AFTER_MAX.year_flags(), YearFlags::from_year(AFTER_MAX.year())); - assert_eq!((AFTER_MAX.month(), AFTER_MAX.day()), (1, 1)); -} - -#[test] -fn diff_months() { - // identity - assert_eq!( - NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_add_months(Months::new(0)), - Some(NaiveDate::from_ymd_opt(2022, 8, 3).unwrap()) - ); - - // add with months exceeding `i32::MAX` - assert_eq!( - NaiveDate::from_ymd_opt(2022, 8, 3) - .unwrap() - .checked_add_months(Months::new(i32::MAX as u32 + 1)), - None - ); - - // sub with months exceeding `i32::MIN` - assert_eq!( - NaiveDate::from_ymd_opt(2022, 8, 3) - .unwrap() - .checked_sub_months(Months::new(i32::MIN.unsigned_abs() + 1)), - None - ); - - // add overflowing year - assert_eq!(NaiveDate::MAX.checked_add_months(Months::new(1)), None); - - // add underflowing year - assert_eq!(NaiveDate::MIN.checked_sub_months(Months::new(1)), None); - - // sub crossing year 0 boundary - assert_eq!( - NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_sub_months(Months::new(2050 * 12)), - Some(NaiveDate::from_ymd_opt(-28, 8, 3).unwrap()) - ); - - // add crossing year boundary - assert_eq!( - NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_add_months(Months::new(6)), - Some(NaiveDate::from_ymd_opt(2023, 2, 3).unwrap()) - ); - - // sub crossing year boundary - assert_eq!( - NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_sub_months(Months::new(10)), - Some(NaiveDate::from_ymd_opt(2021, 10, 3).unwrap()) - ); - - // add clamping day, non-leap year - assert_eq!( - NaiveDate::from_ymd_opt(2022, 1, 29).unwrap().checked_add_months(Months::new(1)), - Some(NaiveDate::from_ymd_opt(2022, 2, 28).unwrap()) - ); - - // add to leap day - assert_eq!( - NaiveDate::from_ymd_opt(2022, 10, 29).unwrap().checked_add_months(Months::new(16)), - Some(NaiveDate::from_ymd_opt(2024, 2, 29).unwrap()) - ); - - // add into december - assert_eq!( - NaiveDate::from_ymd_opt(2022, 10, 31).unwrap().checked_add_months(Months::new(2)), - Some(NaiveDate::from_ymd_opt(2022, 12, 31).unwrap()) - ); - - // sub into december - assert_eq!( - NaiveDate::from_ymd_opt(2022, 10, 31).unwrap().checked_sub_months(Months::new(10)), - Some(NaiveDate::from_ymd_opt(2021, 12, 31).unwrap()) - ); - - // add into january - assert_eq!( - NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_add_months(Months::new(5)), - Some(NaiveDate::from_ymd_opt(2023, 1, 3).unwrap()) - ); - - // sub into january - assert_eq!( - NaiveDate::from_ymd_opt(2022, 8, 3).unwrap().checked_sub_months(Months::new(7)), - Some(NaiveDate::from_ymd_opt(2022, 1, 3).unwrap()) - ); -} - -#[test] -fn test_readme_doomsday() { - for y in NaiveDate::MIN.year()..=NaiveDate::MAX.year() { - // even months - let d4 = NaiveDate::from_ymd_opt(y, 4, 4).unwrap(); - let d6 = NaiveDate::from_ymd_opt(y, 6, 6).unwrap(); - let d8 = NaiveDate::from_ymd_opt(y, 8, 8).unwrap(); - let d10 = NaiveDate::from_ymd_opt(y, 10, 10).unwrap(); - let d12 = NaiveDate::from_ymd_opt(y, 12, 12).unwrap(); - - // nine to five, seven-eleven - let d59 = NaiveDate::from_ymd_opt(y, 5, 9).unwrap(); - let d95 = NaiveDate::from_ymd_opt(y, 9, 5).unwrap(); - let d711 = NaiveDate::from_ymd_opt(y, 7, 11).unwrap(); - let d117 = NaiveDate::from_ymd_opt(y, 11, 7).unwrap(); - - // "March 0" - let d30 = NaiveDate::from_ymd_opt(y, 3, 1).unwrap().pred_opt().unwrap(); - - let weekday = d30.weekday(); - let other_dates = [d4, d6, d8, d10, d12, d59, d95, d711, d117]; - assert!(other_dates.iter().all(|d| d.weekday() == weekday)); - } -} - -#[test] -fn test_date_from_ymd() { - let from_ymd = NaiveDate::from_ymd_opt; - - assert!(from_ymd(2012, 0, 1).is_none()); - assert!(from_ymd(2012, 1, 1).is_some()); - assert!(from_ymd(2012, 2, 29).is_some()); - assert!(from_ymd(2014, 2, 29).is_none()); - assert!(from_ymd(2014, 3, 0).is_none()); - assert!(from_ymd(2014, 3, 1).is_some()); - assert!(from_ymd(2014, 3, 31).is_some()); - assert!(from_ymd(2014, 3, 32).is_none()); - assert!(from_ymd(2014, 12, 31).is_some()); - assert!(from_ymd(2014, 13, 1).is_none()); -} - -#[test] -fn test_date_from_yo() { - let from_yo = NaiveDate::from_yo_opt; - let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); - - assert_eq!(from_yo(2012, 0), None); - assert_eq!(from_yo(2012, 1), Some(ymd(2012, 1, 1))); - assert_eq!(from_yo(2012, 2), Some(ymd(2012, 1, 2))); - assert_eq!(from_yo(2012, 32), Some(ymd(2012, 2, 1))); - assert_eq!(from_yo(2012, 60), Some(ymd(2012, 2, 29))); - assert_eq!(from_yo(2012, 61), Some(ymd(2012, 3, 1))); - assert_eq!(from_yo(2012, 100), Some(ymd(2012, 4, 9))); - assert_eq!(from_yo(2012, 200), Some(ymd(2012, 7, 18))); - assert_eq!(from_yo(2012, 300), Some(ymd(2012, 10, 26))); - assert_eq!(from_yo(2012, 366), Some(ymd(2012, 12, 31))); - assert_eq!(from_yo(2012, 367), None); - assert_eq!(from_yo(2012, (1 << 28) | 60), None); - - assert_eq!(from_yo(2014, 0), None); - assert_eq!(from_yo(2014, 1), Some(ymd(2014, 1, 1))); - assert_eq!(from_yo(2014, 2), Some(ymd(2014, 1, 2))); - assert_eq!(from_yo(2014, 32), Some(ymd(2014, 2, 1))); - assert_eq!(from_yo(2014, 59), Some(ymd(2014, 2, 28))); - assert_eq!(from_yo(2014, 60), Some(ymd(2014, 3, 1))); - assert_eq!(from_yo(2014, 100), Some(ymd(2014, 4, 10))); - assert_eq!(from_yo(2014, 200), Some(ymd(2014, 7, 19))); - assert_eq!(from_yo(2014, 300), Some(ymd(2014, 10, 27))); - assert_eq!(from_yo(2014, 365), Some(ymd(2014, 12, 31))); - assert_eq!(from_yo(2014, 366), None); -} - -#[test] -fn test_date_from_isoywd() { - let from_isoywd = NaiveDate::from_isoywd_opt; - let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); - - assert_eq!(from_isoywd(2004, 0, Weekday::Sun), None); - assert_eq!(from_isoywd(2004, 1, Weekday::Mon), Some(ymd(2003, 12, 29))); - assert_eq!(from_isoywd(2004, 1, Weekday::Sun), Some(ymd(2004, 1, 4))); - assert_eq!(from_isoywd(2004, 2, Weekday::Mon), Some(ymd(2004, 1, 5))); - assert_eq!(from_isoywd(2004, 2, Weekday::Sun), Some(ymd(2004, 1, 11))); - assert_eq!(from_isoywd(2004, 52, Weekday::Mon), Some(ymd(2004, 12, 20))); - assert_eq!(from_isoywd(2004, 52, Weekday::Sun), Some(ymd(2004, 12, 26))); - assert_eq!(from_isoywd(2004, 53, Weekday::Mon), Some(ymd(2004, 12, 27))); - assert_eq!(from_isoywd(2004, 53, Weekday::Sun), Some(ymd(2005, 1, 2))); - assert_eq!(from_isoywd(2004, 54, Weekday::Mon), None); - - assert_eq!(from_isoywd(2011, 0, Weekday::Sun), None); - assert_eq!(from_isoywd(2011, 1, Weekday::Mon), Some(ymd(2011, 1, 3))); - assert_eq!(from_isoywd(2011, 1, Weekday::Sun), Some(ymd(2011, 1, 9))); - assert_eq!(from_isoywd(2011, 2, Weekday::Mon), Some(ymd(2011, 1, 10))); - assert_eq!(from_isoywd(2011, 2, Weekday::Sun), Some(ymd(2011, 1, 16))); - - assert_eq!(from_isoywd(2018, 51, Weekday::Mon), Some(ymd(2018, 12, 17))); - assert_eq!(from_isoywd(2018, 51, Weekday::Sun), Some(ymd(2018, 12, 23))); - assert_eq!(from_isoywd(2018, 52, Weekday::Mon), Some(ymd(2018, 12, 24))); - assert_eq!(from_isoywd(2018, 52, Weekday::Sun), Some(ymd(2018, 12, 30))); - assert_eq!(from_isoywd(2018, 53, Weekday::Mon), None); -} - -#[test] -fn test_date_from_isoywd_and_iso_week() { - for year in 2000..2401 { - for week in 1..54 { - for &weekday in [ - Weekday::Mon, - Weekday::Tue, - Weekday::Wed, - Weekday::Thu, - Weekday::Fri, - Weekday::Sat, - Weekday::Sun, - ] - .iter() - { - let d = NaiveDate::from_isoywd_opt(year, week, weekday); - if let Some(d) = d { - assert_eq!(d.weekday(), weekday); - let w = d.iso_week(); - assert_eq!(w.year(), year); - assert_eq!(w.week(), week); - } - } - } - } - - for year in 2000..2401 { - for month in 1..13 { - for day in 1..32 { - let d = NaiveDate::from_ymd_opt(year, month, day); - if let Some(d) = d { - let w = d.iso_week(); - let d_ = NaiveDate::from_isoywd_opt(w.year(), w.week(), d.weekday()); - assert_eq!(d, d_.unwrap()); - } - } - } - } -} - -#[test] -fn test_date_from_num_days_from_ce() { - let from_ndays_from_ce = NaiveDate::from_num_days_from_ce_opt; - assert_eq!(from_ndays_from_ce(1), Some(NaiveDate::from_ymd_opt(1, 1, 1).unwrap())); - assert_eq!(from_ndays_from_ce(2), Some(NaiveDate::from_ymd_opt(1, 1, 2).unwrap())); - assert_eq!(from_ndays_from_ce(31), Some(NaiveDate::from_ymd_opt(1, 1, 31).unwrap())); - assert_eq!(from_ndays_from_ce(32), Some(NaiveDate::from_ymd_opt(1, 2, 1).unwrap())); - assert_eq!(from_ndays_from_ce(59), Some(NaiveDate::from_ymd_opt(1, 2, 28).unwrap())); - assert_eq!(from_ndays_from_ce(60), Some(NaiveDate::from_ymd_opt(1, 3, 1).unwrap())); - assert_eq!(from_ndays_from_ce(365), Some(NaiveDate::from_ymd_opt(1, 12, 31).unwrap())); - assert_eq!(from_ndays_from_ce(365 + 1), Some(NaiveDate::from_ymd_opt(2, 1, 1).unwrap())); - assert_eq!(from_ndays_from_ce(365 * 2 + 1), Some(NaiveDate::from_ymd_opt(3, 1, 1).unwrap())); - assert_eq!(from_ndays_from_ce(365 * 3 + 1), Some(NaiveDate::from_ymd_opt(4, 1, 1).unwrap())); - assert_eq!(from_ndays_from_ce(365 * 4 + 2), Some(NaiveDate::from_ymd_opt(5, 1, 1).unwrap())); - assert_eq!(from_ndays_from_ce(146097 + 1), Some(NaiveDate::from_ymd_opt(401, 1, 1).unwrap())); - assert_eq!( - from_ndays_from_ce(146097 * 5 + 1), - Some(NaiveDate::from_ymd_opt(2001, 1, 1).unwrap()) - ); - assert_eq!(from_ndays_from_ce(719163), Some(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap())); - assert_eq!(from_ndays_from_ce(0), Some(NaiveDate::from_ymd_opt(0, 12, 31).unwrap())); // 1 BCE - assert_eq!(from_ndays_from_ce(-365), Some(NaiveDate::from_ymd_opt(0, 1, 1).unwrap())); - assert_eq!(from_ndays_from_ce(-366), Some(NaiveDate::from_ymd_opt(-1, 12, 31).unwrap())); // 2 BCE - - for days in (-9999..10001).map(|x| x * 100) { - assert_eq!(from_ndays_from_ce(days).map(|d| d.num_days_from_ce()), Some(days)); - } - - assert_eq!(from_ndays_from_ce(NaiveDate::MIN.num_days_from_ce()), Some(NaiveDate::MIN)); - assert_eq!(from_ndays_from_ce(NaiveDate::MIN.num_days_from_ce() - 1), None); - assert_eq!(from_ndays_from_ce(NaiveDate::MAX.num_days_from_ce()), Some(NaiveDate::MAX)); - assert_eq!(from_ndays_from_ce(NaiveDate::MAX.num_days_from_ce() + 1), None); - - assert_eq!(from_ndays_from_ce(i32::MIN), None); - assert_eq!(from_ndays_from_ce(i32::MAX), None); -} - -#[test] -fn test_date_from_epoch_days() { - let from_epoch_days = NaiveDate::from_epoch_days; - assert_eq!(from_epoch_days(-719_162), Some(NaiveDate::from_ymd_opt(1, 1, 1).unwrap())); - assert_eq!(from_epoch_days(0), Some(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap())); - assert_eq!(from_epoch_days(1), Some(NaiveDate::from_ymd_opt(1970, 1, 2).unwrap())); - assert_eq!(from_epoch_days(2), Some(NaiveDate::from_ymd_opt(1970, 1, 3).unwrap())); - assert_eq!(from_epoch_days(30), Some(NaiveDate::from_ymd_opt(1970, 1, 31).unwrap())); - assert_eq!(from_epoch_days(31), Some(NaiveDate::from_ymd_opt(1970, 2, 1).unwrap())); - assert_eq!(from_epoch_days(58), Some(NaiveDate::from_ymd_opt(1970, 2, 28).unwrap())); - assert_eq!(from_epoch_days(59), Some(NaiveDate::from_ymd_opt(1970, 3, 1).unwrap())); - assert_eq!(from_epoch_days(364), Some(NaiveDate::from_ymd_opt(1970, 12, 31).unwrap())); - assert_eq!(from_epoch_days(365), Some(NaiveDate::from_ymd_opt(1971, 1, 1).unwrap())); - assert_eq!(from_epoch_days(365 * 2), Some(NaiveDate::from_ymd_opt(1972, 1, 1).unwrap())); - assert_eq!(from_epoch_days(365 * 3 + 1), Some(NaiveDate::from_ymd_opt(1973, 1, 1).unwrap())); - assert_eq!(from_epoch_days(365 * 4 + 1), Some(NaiveDate::from_ymd_opt(1974, 1, 1).unwrap())); - assert_eq!(from_epoch_days(13036), Some(NaiveDate::from_ymd_opt(2005, 9, 10).unwrap())); - assert_eq!(from_epoch_days(-365), Some(NaiveDate::from_ymd_opt(1969, 1, 1).unwrap())); - assert_eq!(from_epoch_days(-366), Some(NaiveDate::from_ymd_opt(1968, 12, 31).unwrap())); - - for days in (-9999..10001).map(|x| x * 100) { - assert_eq!(from_epoch_days(days).map(|d| d.to_epoch_days()), Some(days)); - } - - assert_eq!(from_epoch_days(NaiveDate::MIN.to_epoch_days()), Some(NaiveDate::MIN)); - assert_eq!(from_epoch_days(NaiveDate::MIN.to_epoch_days() - 1), None); - assert_eq!(from_epoch_days(NaiveDate::MAX.to_epoch_days()), Some(NaiveDate::MAX)); - assert_eq!(from_epoch_days(NaiveDate::MAX.to_epoch_days() + 1), None); - - assert_eq!(from_epoch_days(i32::MIN), None); - assert_eq!(from_epoch_days(i32::MAX), None); -} - -#[test] -fn test_date_from_weekday_of_month_opt() { - let ymwd = NaiveDate::from_weekday_of_month_opt; - assert_eq!(ymwd(2018, 8, Weekday::Tue, 0), None); - assert_eq!(ymwd(2018, 8, Weekday::Wed, 1), Some(NaiveDate::from_ymd_opt(2018, 8, 1).unwrap())); - assert_eq!(ymwd(2018, 8, Weekday::Thu, 1), Some(NaiveDate::from_ymd_opt(2018, 8, 2).unwrap())); - assert_eq!(ymwd(2018, 8, Weekday::Sun, 1), Some(NaiveDate::from_ymd_opt(2018, 8, 5).unwrap())); - assert_eq!(ymwd(2018, 8, Weekday::Mon, 1), Some(NaiveDate::from_ymd_opt(2018, 8, 6).unwrap())); - assert_eq!(ymwd(2018, 8, Weekday::Tue, 1), Some(NaiveDate::from_ymd_opt(2018, 8, 7).unwrap())); - assert_eq!(ymwd(2018, 8, Weekday::Wed, 2), Some(NaiveDate::from_ymd_opt(2018, 8, 8).unwrap())); - assert_eq!(ymwd(2018, 8, Weekday::Sun, 2), Some(NaiveDate::from_ymd_opt(2018, 8, 12).unwrap())); - assert_eq!(ymwd(2018, 8, Weekday::Thu, 3), Some(NaiveDate::from_ymd_opt(2018, 8, 16).unwrap())); - assert_eq!(ymwd(2018, 8, Weekday::Thu, 4), Some(NaiveDate::from_ymd_opt(2018, 8, 23).unwrap())); - assert_eq!(ymwd(2018, 8, Weekday::Thu, 5), Some(NaiveDate::from_ymd_opt(2018, 8, 30).unwrap())); - assert_eq!(ymwd(2018, 8, Weekday::Fri, 5), Some(NaiveDate::from_ymd_opt(2018, 8, 31).unwrap())); - assert_eq!(ymwd(2018, 8, Weekday::Sat, 5), None); -} - -#[test] -fn test_date_fields() { - fn check(year: i32, month: u32, day: u32, ordinal: u32) { - let d1 = NaiveDate::from_ymd_opt(year, month, day).unwrap(); - assert_eq!(d1.year(), year); - assert_eq!(d1.month(), month); - assert_eq!(d1.day(), day); - assert_eq!(d1.ordinal(), ordinal); - - let d2 = NaiveDate::from_yo_opt(year, ordinal).unwrap(); - assert_eq!(d2.year(), year); - assert_eq!(d2.month(), month); - assert_eq!(d2.day(), day); - assert_eq!(d2.ordinal(), ordinal); - - assert_eq!(d1, d2); - } - - check(2012, 1, 1, 1); - check(2012, 1, 2, 2); - check(2012, 2, 1, 32); - check(2012, 2, 29, 60); - check(2012, 3, 1, 61); - check(2012, 4, 9, 100); - check(2012, 7, 18, 200); - check(2012, 10, 26, 300); - check(2012, 12, 31, 366); - - check(2014, 1, 1, 1); - check(2014, 1, 2, 2); - check(2014, 2, 1, 32); - check(2014, 2, 28, 59); - check(2014, 3, 1, 60); - check(2014, 4, 10, 100); - check(2014, 7, 19, 200); - check(2014, 10, 27, 300); - check(2014, 12, 31, 365); -} - -#[test] -fn test_date_weekday() { - assert_eq!(NaiveDate::from_ymd_opt(1582, 10, 15).unwrap().weekday(), Weekday::Fri); - // May 20, 1875 = ISO 8601 reference date - assert_eq!(NaiveDate::from_ymd_opt(1875, 5, 20).unwrap().weekday(), Weekday::Thu); - assert_eq!(NaiveDate::from_ymd_opt(2000, 1, 1).unwrap().weekday(), Weekday::Sat); -} - -#[test] -fn test_date_with_fields() { - let d = NaiveDate::from_ymd_opt(2000, 2, 29).unwrap(); - assert_eq!(d.with_year(-400), Some(NaiveDate::from_ymd_opt(-400, 2, 29).unwrap())); - assert_eq!(d.with_year(-100), None); - assert_eq!(d.with_year(1600), Some(NaiveDate::from_ymd_opt(1600, 2, 29).unwrap())); - assert_eq!(d.with_year(1900), None); - assert_eq!(d.with_year(2000), Some(NaiveDate::from_ymd_opt(2000, 2, 29).unwrap())); - assert_eq!(d.with_year(2001), None); - assert_eq!(d.with_year(2004), Some(NaiveDate::from_ymd_opt(2004, 2, 29).unwrap())); - assert_eq!(d.with_year(i32::MAX), None); - - let d = NaiveDate::from_ymd_opt(2000, 4, 30).unwrap(); - assert_eq!(d.with_month(0), None); - assert_eq!(d.with_month(1), Some(NaiveDate::from_ymd_opt(2000, 1, 30).unwrap())); - assert_eq!(d.with_month(2), None); - assert_eq!(d.with_month(3), Some(NaiveDate::from_ymd_opt(2000, 3, 30).unwrap())); - assert_eq!(d.with_month(4), Some(NaiveDate::from_ymd_opt(2000, 4, 30).unwrap())); - assert_eq!(d.with_month(12), Some(NaiveDate::from_ymd_opt(2000, 12, 30).unwrap())); - assert_eq!(d.with_month(13), None); - assert_eq!(d.with_month(u32::MAX), None); - - let d = NaiveDate::from_ymd_opt(2000, 2, 8).unwrap(); - assert_eq!(d.with_day(0), None); - assert_eq!(d.with_day(1), Some(NaiveDate::from_ymd_opt(2000, 2, 1).unwrap())); - assert_eq!(d.with_day(29), Some(NaiveDate::from_ymd_opt(2000, 2, 29).unwrap())); - assert_eq!(d.with_day(30), None); - assert_eq!(d.with_day(u32::MAX), None); -} - -#[test] -fn test_date_with_ordinal() { - let d = NaiveDate::from_ymd_opt(2000, 5, 5).unwrap(); - assert_eq!(d.with_ordinal(0), None); - assert_eq!(d.with_ordinal(1), Some(NaiveDate::from_ymd_opt(2000, 1, 1).unwrap())); - assert_eq!(d.with_ordinal(60), Some(NaiveDate::from_ymd_opt(2000, 2, 29).unwrap())); - assert_eq!(d.with_ordinal(61), Some(NaiveDate::from_ymd_opt(2000, 3, 1).unwrap())); - assert_eq!(d.with_ordinal(366), Some(NaiveDate::from_ymd_opt(2000, 12, 31).unwrap())); - assert_eq!(d.with_ordinal(367), None); - assert_eq!(d.with_ordinal((1 << 28) | 60), None); - let d = NaiveDate::from_ymd_opt(1999, 5, 5).unwrap(); - assert_eq!(d.with_ordinal(366), None); - assert_eq!(d.with_ordinal(u32::MAX), None); -} - -#[test] -fn test_date_num_days_from_ce() { - assert_eq!(NaiveDate::from_ymd_opt(1, 1, 1).unwrap().num_days_from_ce(), 1); - - for year in -9999..10001 { - assert_eq!( - NaiveDate::from_ymd_opt(year, 1, 1).unwrap().num_days_from_ce(), - NaiveDate::from_ymd_opt(year - 1, 12, 31).unwrap().num_days_from_ce() + 1 - ); - } -} - -#[test] -fn test_date_to_epoch_days() { - assert_eq!(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().to_epoch_days(), 0); - - for year in -9999..10001 { - assert_eq!( - NaiveDate::from_ymd_opt(year, 1, 1).unwrap().to_epoch_days(), - NaiveDate::from_ymd_opt(year - 1, 12, 31).unwrap().to_epoch_days() + 1 - ); - } -} - -#[test] -fn test_date_succ() { - let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); - assert_eq!(ymd(2014, 5, 6).succ_opt(), Some(ymd(2014, 5, 7))); - assert_eq!(ymd(2014, 5, 31).succ_opt(), Some(ymd(2014, 6, 1))); - assert_eq!(ymd(2014, 12, 31).succ_opt(), Some(ymd(2015, 1, 1))); - assert_eq!(ymd(2016, 2, 28).succ_opt(), Some(ymd(2016, 2, 29))); - assert_eq!(ymd(NaiveDate::MAX.year(), 12, 31).succ_opt(), None); -} - -#[test] -fn test_date_pred() { - let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); - assert_eq!(ymd(2016, 3, 1).pred_opt(), Some(ymd(2016, 2, 29))); - assert_eq!(ymd(2015, 1, 1).pred_opt(), Some(ymd(2014, 12, 31))); - assert_eq!(ymd(2014, 6, 1).pred_opt(), Some(ymd(2014, 5, 31))); - assert_eq!(ymd(2014, 5, 7).pred_opt(), Some(ymd(2014, 5, 6))); - assert_eq!(ymd(NaiveDate::MIN.year(), 1, 1).pred_opt(), None); -} - -#[test] -fn test_date_checked_add_signed() { - fn check(lhs: Option, delta: TimeDelta, rhs: Option) { - assert_eq!(lhs.unwrap().checked_add_signed(delta), rhs); - assert_eq!(lhs.unwrap().checked_sub_signed(-delta), rhs); - } - let ymd = NaiveDate::from_ymd_opt; - - check(ymd(2014, 1, 1), TimeDelta::zero(), ymd(2014, 1, 1)); - check(ymd(2014, 1, 1), TimeDelta::try_seconds(86399).unwrap(), ymd(2014, 1, 1)); - // always round towards zero - check(ymd(2014, 1, 1), TimeDelta::try_seconds(-86399).unwrap(), ymd(2014, 1, 1)); - check(ymd(2014, 1, 1), TimeDelta::try_days(1).unwrap(), ymd(2014, 1, 2)); - check(ymd(2014, 1, 1), TimeDelta::try_days(-1).unwrap(), ymd(2013, 12, 31)); - check(ymd(2014, 1, 1), TimeDelta::try_days(364).unwrap(), ymd(2014, 12, 31)); - check(ymd(2014, 1, 1), TimeDelta::try_days(365 * 4 + 1).unwrap(), ymd(2018, 1, 1)); - check(ymd(2014, 1, 1), TimeDelta::try_days(365 * 400 + 97).unwrap(), ymd(2414, 1, 1)); - - check(ymd(-7, 1, 1), TimeDelta::try_days(365 * 12 + 3).unwrap(), ymd(5, 1, 1)); - - // overflow check - check( - ymd(0, 1, 1), - TimeDelta::try_days(MAX_DAYS_FROM_YEAR_0 as i64).unwrap(), - ymd(MAX_YEAR, 12, 31), - ); - check(ymd(0, 1, 1), TimeDelta::try_days(MAX_DAYS_FROM_YEAR_0 as i64 + 1).unwrap(), None); - check(ymd(0, 1, 1), TimeDelta::MAX, None); - check( - ymd(0, 1, 1), - TimeDelta::try_days(MIN_DAYS_FROM_YEAR_0 as i64).unwrap(), - ymd(MIN_YEAR, 1, 1), - ); - check(ymd(0, 1, 1), TimeDelta::try_days(MIN_DAYS_FROM_YEAR_0 as i64 - 1).unwrap(), None); - check(ymd(0, 1, 1), TimeDelta::MIN, None); -} - -#[test] -fn test_date_signed_duration_since() { - fn check(lhs: Option, rhs: Option, delta: TimeDelta) { - assert_eq!(lhs.unwrap().signed_duration_since(rhs.unwrap()), delta); - assert_eq!(rhs.unwrap().signed_duration_since(lhs.unwrap()), -delta); - } - let ymd = NaiveDate::from_ymd_opt; - - check(ymd(2014, 1, 1), ymd(2014, 1, 1), TimeDelta::zero()); - check(ymd(2014, 1, 2), ymd(2014, 1, 1), TimeDelta::try_days(1).unwrap()); - check(ymd(2014, 12, 31), ymd(2014, 1, 1), TimeDelta::try_days(364).unwrap()); - check(ymd(2015, 1, 3), ymd(2014, 1, 1), TimeDelta::try_days(365 + 2).unwrap()); - check(ymd(2018, 1, 1), ymd(2014, 1, 1), TimeDelta::try_days(365 * 4 + 1).unwrap()); - check(ymd(2414, 1, 1), ymd(2014, 1, 1), TimeDelta::try_days(365 * 400 + 97).unwrap()); - - check( - ymd(MAX_YEAR, 12, 31), - ymd(0, 1, 1), - TimeDelta::try_days(MAX_DAYS_FROM_YEAR_0 as i64).unwrap(), - ); - check( - ymd(MIN_YEAR, 1, 1), - ymd(0, 1, 1), - TimeDelta::try_days(MIN_DAYS_FROM_YEAR_0 as i64).unwrap(), - ); -} - -#[test] -fn test_date_add_days() { - fn check(lhs: Option, days: Days, rhs: Option) { - assert_eq!(lhs.unwrap().checked_add_days(days), rhs); - } - let ymd = NaiveDate::from_ymd_opt; - - check(ymd(2014, 1, 1), Days::new(0), ymd(2014, 1, 1)); - // always round towards zero - check(ymd(2014, 1, 1), Days::new(1), ymd(2014, 1, 2)); - check(ymd(2014, 1, 1), Days::new(364), ymd(2014, 12, 31)); - check(ymd(2014, 1, 1), Days::new(365 * 4 + 1), ymd(2018, 1, 1)); - check(ymd(2014, 1, 1), Days::new(365 * 400 + 97), ymd(2414, 1, 1)); - - check(ymd(-7, 1, 1), Days::new(365 * 12 + 3), ymd(5, 1, 1)); - - // overflow check - check(ymd(0, 1, 1), Days::new(MAX_DAYS_FROM_YEAR_0.try_into().unwrap()), ymd(MAX_YEAR, 12, 31)); - check(ymd(0, 1, 1), Days::new(u64::try_from(MAX_DAYS_FROM_YEAR_0).unwrap() + 1), None); -} - -#[test] -fn test_date_sub_days() { - fn check(lhs: Option, days: Days, rhs: Option) { - assert_eq!(lhs.unwrap().checked_sub_days(days), rhs); - } - let ymd = NaiveDate::from_ymd_opt; - - check(ymd(2014, 1, 1), Days::new(0), ymd(2014, 1, 1)); - check(ymd(2014, 1, 2), Days::new(1), ymd(2014, 1, 1)); - check(ymd(2014, 12, 31), Days::new(364), ymd(2014, 1, 1)); - check(ymd(2015, 1, 3), Days::new(365 + 2), ymd(2014, 1, 1)); - check(ymd(2018, 1, 1), Days::new(365 * 4 + 1), ymd(2014, 1, 1)); - check(ymd(2414, 1, 1), Days::new(365 * 400 + 97), ymd(2014, 1, 1)); - - check(ymd(MAX_YEAR, 12, 31), Days::new(MAX_DAYS_FROM_YEAR_0.try_into().unwrap()), ymd(0, 1, 1)); - check( - ymd(0, 1, 1), - Days::new((-MIN_DAYS_FROM_YEAR_0).try_into().unwrap()), - ymd(MIN_YEAR, 1, 1), - ); -} - -#[test] -fn test_date_addassignment() { - let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); - let mut date = ymd(2016, 10, 1); - date += TimeDelta::try_days(10).unwrap(); - assert_eq!(date, ymd(2016, 10, 11)); - date += TimeDelta::try_days(30).unwrap(); - assert_eq!(date, ymd(2016, 11, 10)); -} - -#[test] -fn test_date_subassignment() { - let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); - let mut date = ymd(2016, 10, 11); - date -= TimeDelta::try_days(10).unwrap(); - assert_eq!(date, ymd(2016, 10, 1)); - date -= TimeDelta::try_days(2).unwrap(); - assert_eq!(date, ymd(2016, 9, 29)); -} - -#[test] -fn test_date_fmt() { - assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(2012, 3, 4).unwrap()), "2012-03-04"); - assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(0, 3, 4).unwrap()), "0000-03-04"); - assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(-307, 3, 4).unwrap()), "-0307-03-04"); - assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(12345, 3, 4).unwrap()), "+12345-03-04"); - - assert_eq!(NaiveDate::from_ymd_opt(2012, 3, 4).unwrap().to_string(), "2012-03-04"); - assert_eq!(NaiveDate::from_ymd_opt(0, 3, 4).unwrap().to_string(), "0000-03-04"); - assert_eq!(NaiveDate::from_ymd_opt(-307, 3, 4).unwrap().to_string(), "-0307-03-04"); - assert_eq!(NaiveDate::from_ymd_opt(12345, 3, 4).unwrap().to_string(), "+12345-03-04"); - - // the format specifier should have no effect on `NaiveTime` - assert_eq!(format!("{:+30?}", NaiveDate::from_ymd_opt(1234, 5, 6).unwrap()), "1234-05-06"); - assert_eq!(format!("{:30?}", NaiveDate::from_ymd_opt(12345, 6, 7).unwrap()), "+12345-06-07"); -} - -#[test] -fn test_date_from_str() { - // valid cases - let valid = [ - "-0000000123456-1-2", - " -123456 - 1 - 2 ", - "-12345-1-2", - "-1234-12-31", - "-7-6-5", - "350-2-28", - "360-02-29", - "0360-02-29", - "2015-2 -18", - "2015-02-18", - "+70-2-18", - "+70000-2-18", - "+00007-2-18", - ]; - for &s in &valid { - eprintln!("test_date_from_str valid {s:?}"); - let d = match s.parse::() { - Ok(d) => d, - Err(e) => panic!("parsing `{s}` has failed: {e}"), - }; - eprintln!("d {d:?} (NaiveDate)"); - let s_ = format!("{d:?}"); - eprintln!("s_ {s_:?}"); - // `s` and `s_` may differ, but `s.parse()` and `s_.parse()` must be same - let d_ = match s_.parse::() { - Ok(d) => d, - Err(e) => { - panic!("`{s}` is parsed into `{d:?}`, but reparsing that has failed: {e}") - } - }; - eprintln!("d_ {d_:?} (NaiveDate)"); - assert!( - d == d_, - "`{s}` is parsed into `{d:?}`, but reparsed result \ - `{d_:?}` does not match" - ); - } - - // some invalid cases - // since `ParseErrorKind` is private, all we can do is to check if there was an error - let invalid = [ - "", // empty - "x", // invalid - "Fri, 09 Aug 2013 GMT", // valid date, wrong format - "Sat Jun 30 2012", // valid date, wrong format - "1441497364.649", // valid datetime, wrong format - "+1441497364.649", // valid datetime, wrong format - "+1441497364", // valid datetime, wrong format - "2014/02/03", // valid date, wrong format - "2014", // datetime missing data - "2014-01", // datetime missing data - "2014-01-00", // invalid day - "2014-11-32", // invalid day - "2014-13-01", // invalid month - "2014-13-57", // invalid month, day - "9999999-9-9", // invalid year (out of bounds) - ]; - for &s in &invalid { - eprintln!("test_date_from_str invalid {s:?}"); - assert!(s.parse::().is_err()); - } -} - -#[test] -fn test_date_parse_from_str() { - let ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); - assert_eq!( - NaiveDate::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), - Ok(ymd(2014, 5, 7)) - ); // ignore time and offset - assert_eq!( - NaiveDate::parse_from_str("2015-W06-1=2015-033 Q1", "%G-W%V-%u = %Y-%j Q%q"), - Ok(ymd(2015, 2, 2)) - ); - assert_eq!(NaiveDate::parse_from_str("Fri, 09 Aug 13", "%a, %d %b %y"), Ok(ymd(2013, 8, 9))); - assert!(NaiveDate::parse_from_str("Sat, 09 Aug 2013", "%a, %d %b %Y").is_err()); - assert!(NaiveDate::parse_from_str("2014-57", "%Y-%m-%d").is_err()); - assert!(NaiveDate::parse_from_str("2014", "%Y").is_err()); // insufficient - - assert!(NaiveDate::parse_from_str("2014-5-7 Q3", "%Y-%m-%d Q%q").is_err()); // mismatched quarter - - assert_eq!( - NaiveDate::parse_from_str("2020-01-0", "%Y-%W-%w").ok(), - NaiveDate::from_ymd_opt(2020, 1, 12), - ); - - assert_eq!( - NaiveDate::parse_from_str("2019-01-0", "%Y-%W-%w").ok(), - NaiveDate::from_ymd_opt(2019, 1, 13), - ); -} - -#[test] -fn test_day_iterator_limit() { - assert_eq!(NaiveDate::from_ymd_opt(MAX_YEAR, 12, 29).unwrap().iter_days().take(4).count(), 2); - assert_eq!( - NaiveDate::from_ymd_opt(MIN_YEAR, 1, 3).unwrap().iter_days().rev().take(4).count(), - 2 - ); -} - -#[test] -fn test_week_iterator_limit() { - assert_eq!(NaiveDate::from_ymd_opt(MAX_YEAR, 12, 12).unwrap().iter_weeks().take(4).count(), 2); - assert_eq!( - NaiveDate::from_ymd_opt(MIN_YEAR, 1, 15).unwrap().iter_weeks().rev().take(4).count(), - 2 - ); -} - -#[test] -fn test_weeks_from() { - // tests per: https://github.com/chronotope/chrono/issues/961 - // these internally use `weeks_from` via the parsing infrastructure - assert_eq!( - NaiveDate::parse_from_str("2020-01-0", "%Y-%W-%w").ok(), - NaiveDate::from_ymd_opt(2020, 1, 12), - ); - assert_eq!( - NaiveDate::parse_from_str("2019-01-0", "%Y-%W-%w").ok(), - NaiveDate::from_ymd_opt(2019, 1, 13), - ); - - // direct tests - for (y, starts_on) in &[ - (2019, Weekday::Tue), - (2020, Weekday::Wed), - (2021, Weekday::Fri), - (2022, Weekday::Sat), - (2023, Weekday::Sun), - (2024, Weekday::Mon), - (2025, Weekday::Wed), - (2026, Weekday::Thu), - ] { - for day in &[ - Weekday::Mon, - Weekday::Tue, - Weekday::Wed, - Weekday::Thu, - Weekday::Fri, - Weekday::Sat, - Weekday::Sun, - ] { - assert_eq!( - NaiveDate::from_ymd_opt(*y, 1, 1).map(|d| d.weeks_from(*day)), - Some(if day == starts_on { 1 } else { 0 }) - ); - - // last day must always be in week 52 or 53 - assert!( - [52, 53].contains(&NaiveDate::from_ymd_opt(*y, 12, 31).unwrap().weeks_from(*day)), - ); - } - } - - let base = NaiveDate::from_ymd_opt(2019, 1, 1).unwrap(); - - // 400 years covers all year types - for day in &[ - Weekday::Mon, - Weekday::Tue, - Weekday::Wed, - Weekday::Thu, - Weekday::Fri, - Weekday::Sat, - Weekday::Sun, - ] { - // must always be below 54 - for dplus in 1..(400 * 366) { - assert!((base + Days::new(dplus)).weeks_from(*day) < 54) - } - } -} - -#[test] -fn test_with_0_overflow() { - let dt = NaiveDate::from_ymd_opt(2023, 4, 18).unwrap(); - assert!(dt.with_month0(4294967295).is_none()); - assert!(dt.with_day0(4294967295).is_none()); - assert!(dt.with_ordinal0(4294967295).is_none()); -} - -#[test] -fn test_leap_year() { - for year in 0..=MAX_YEAR { - let date = NaiveDate::from_ymd_opt(year, 1, 1).unwrap(); - let is_leap = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); - assert_eq!(date.leap_year(), is_leap); - assert_eq!(date.leap_year(), date.with_ordinal(366).is_some()); - } -} - -#[test] -fn test_date_yearflags() { - for (year, year_flags, _) in YEAR_FLAGS { - assert_eq!(NaiveDate::from_yo_opt(year, 1).unwrap().year_flags(), year_flags); - } -} - -#[test] -fn test_weekday_with_yearflags() { - for (year, year_flags, first_weekday) in YEAR_FLAGS { - let first_day_of_year = NaiveDate::from_yo_opt(year, 1).unwrap(); - dbg!(year); - assert_eq!(first_day_of_year.year_flags(), year_flags); - assert_eq!(first_day_of_year.weekday(), first_weekday); - - let mut prev = first_day_of_year.weekday(); - for ordinal in 2u32..=year_flags.ndays() { - let date = NaiveDate::from_yo_opt(year, ordinal).unwrap(); - let expected = prev.succ(); - assert_eq!(date.weekday(), expected); - prev = expected; - } - } -} - -#[test] -fn test_isoweekdate_with_yearflags() { - for (year, year_flags, _) in YEAR_FLAGS { - // January 4 should be in the first week - let jan4 = NaiveDate::from_ymd_opt(year, 1, 4).unwrap(); - let iso_week = jan4.iso_week(); - assert_eq!(jan4.year_flags(), year_flags); - assert_eq!(iso_week.week(), 1); - } -} - -#[test] -fn test_date_to_mdf_to_date() { - for (year, year_flags, _) in YEAR_FLAGS { - for ordinal in 1..=year_flags.ndays() { - let date = NaiveDate::from_yo_opt(year, ordinal).unwrap(); - assert_eq!(date, NaiveDate::from_mdf(date.year(), date.mdf()).unwrap()); - } - } -} - -// Used for testing some methods with all combinations of `YearFlags`. -// (year, flags, first weekday of year) -const YEAR_FLAGS: [(i32, YearFlags, Weekday); 14] = [ - (2006, A, Weekday::Sun), - (2005, B, Weekday::Sat), - (2010, C, Weekday::Fri), - (2009, D, Weekday::Thu), - (2003, E, Weekday::Wed), - (2002, F, Weekday::Tue), - (2001, G, Weekday::Mon), - (2012, AG, Weekday::Sun), - (2000, BA, Weekday::Sat), - (2016, CB, Weekday::Fri), - (2004, DC, Weekday::Thu), - (2020, ED, Weekday::Wed), - (2008, FE, Weekday::Tue), - (2024, GF, Weekday::Mon), -]; - -#[test] -#[cfg(feature = "rkyv-validation")] -fn test_rkyv_validation() { - let date_min = NaiveDate::MIN; - let bytes = rkyv::to_bytes::<_, 4>(&date_min).unwrap(); - assert_eq!(rkyv::from_bytes::(&bytes).unwrap(), date_min); - - let date_max = NaiveDate::MAX; - let bytes = rkyv::to_bytes::<_, 4>(&date_max).unwrap(); - assert_eq!(rkyv::from_bytes::(&bytes).unwrap(), date_max); -} - -// MAX_YEAR-12-31 minus 0000-01-01 -// = (MAX_YEAR-12-31 minus 0000-12-31) + (0000-12-31 - 0000-01-01) -// = MAX_YEAR * 365 + (# of leap years from 0001 to MAX_YEAR) + 365 -// = (MAX_YEAR + 1) * 365 + (# of leap years from 0001 to MAX_YEAR) -const MAX_DAYS_FROM_YEAR_0: i32 = - (MAX_YEAR + 1) * 365 + MAX_YEAR / 4 - MAX_YEAR / 100 + MAX_YEAR / 400; - -// MIN_YEAR-01-01 minus 0000-01-01 -// = MIN_YEAR * 365 + (# of leap years from MIN_YEAR to 0000) -const MIN_DAYS_FROM_YEAR_0: i32 = MIN_YEAR * 365 + MIN_YEAR / 4 - MIN_YEAR / 100 + MIN_YEAR / 400; - -// only used for testing, but duplicated in naive::datetime -const MAX_BITS: usize = 44; diff --git a/chrono-0.4.44/src/naive/datetime/mod.rs b/chrono-0.4.44/src/naive/datetime/mod.rs deleted file mode 100644 index 0723c4866f..0000000000 --- a/chrono-0.4.44/src/naive/datetime/mod.rs +++ /dev/null @@ -1,2171 +0,0 @@ -// This is a part of Chrono. -// See README.md and LICENSE.txt for details. - -//! ISO 8601 date and time without timezone. - -#[cfg(feature = "alloc")] -use core::borrow::Borrow; -use core::fmt::Write; -use core::ops::{Add, AddAssign, Sub, SubAssign}; -use core::time::Duration; -use core::{fmt, str}; - -#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))] -use rkyv::{Archive, Deserialize, Serialize}; - -#[cfg(feature = "alloc")] -use crate::format::DelayedFormat; -use crate::format::{Fixed, Item, Numeric, Pad}; -use crate::format::{ParseError, ParseResult, Parsed, StrftimeItems, parse, parse_and_remainder}; -use crate::naive::{Days, IsoWeek, NaiveDate, NaiveTime}; -use crate::offset::Utc; -use crate::time_delta::NANOS_PER_SEC; -use crate::{ - DateTime, Datelike, FixedOffset, MappedLocalTime, Months, TimeDelta, TimeZone, Timelike, - Weekday, expect, try_opt, -}; - -/// Tools to help serializing/deserializing `NaiveDateTime`s -#[cfg(feature = "serde")] -pub(crate) mod serde; - -#[cfg(test)] -mod tests; - -/// The minimum possible `NaiveDateTime`. -#[deprecated(since = "0.4.20", note = "Use NaiveDateTime::MIN instead")] -pub const MIN_DATETIME: NaiveDateTime = NaiveDateTime::MIN; -/// The maximum possible `NaiveDateTime`. -#[deprecated(since = "0.4.20", note = "Use NaiveDateTime::MAX instead")] -pub const MAX_DATETIME: NaiveDateTime = NaiveDateTime::MAX; - -/// ISO 8601 combined date and time without timezone. -/// -/// # Example -/// -/// `NaiveDateTime` is commonly created from [`NaiveDate`]. -/// -/// ``` -/// use chrono::{NaiveDate, NaiveDateTime}; -/// -/// let dt: NaiveDateTime = -/// NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_opt(9, 10, 11).unwrap(); -/// # let _ = dt; -/// ``` -/// -/// You can use typical [date-like](Datelike) and [time-like](Timelike) methods, -/// provided that relevant traits are in the scope. -/// -/// ``` -/// # use chrono::{NaiveDate, NaiveDateTime}; -/// # let dt: NaiveDateTime = NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_opt(9, 10, 11).unwrap(); -/// use chrono::{Datelike, Timelike, Weekday}; -/// -/// assert_eq!(dt.weekday(), Weekday::Fri); -/// assert_eq!(dt.num_seconds_from_midnight(), 33011); -/// ``` -#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone)] -#[cfg_attr( - any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"), - derive(Archive, Deserialize, Serialize), - archive(compare(PartialEq, PartialOrd)), - archive_attr(derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)) -)] -#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] -#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(arbitrary::Arbitrary))] -pub struct NaiveDateTime { - date: NaiveDate, - time: NaiveTime, -} - -impl NaiveDateTime { - /// Makes a new `NaiveDateTime` from date and time components. - /// Equivalent to [`date.and_time(time)`](./struct.NaiveDate.html#method.and_time) - /// and many other helper constructors on `NaiveDate`. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, NaiveDateTime, NaiveTime}; - /// - /// let d = NaiveDate::from_ymd_opt(2015, 6, 3).unwrap(); - /// let t = NaiveTime::from_hms_milli_opt(12, 34, 56, 789).unwrap(); - /// - /// let dt = NaiveDateTime::new(d, t); - /// assert_eq!(dt.date(), d); - /// assert_eq!(dt.time(), t); - /// ``` - #[inline] - pub const fn new(date: NaiveDate, time: NaiveTime) -> NaiveDateTime { - NaiveDateTime { date, time } - } - - /// Makes a new `NaiveDateTime` corresponding to a UTC date and time, - /// from the number of non-leap seconds - /// since the midnight UTC on January 1, 1970 (aka "UNIX timestamp") - /// and the number of nanoseconds since the last whole non-leap second. - /// - /// For a non-naive version of this function see [`TimeZone::timestamp`]. - /// - /// The nanosecond part can exceed 1,000,000,000 in order to represent a - /// [leap second](NaiveTime#leap-second-handling), but only when `secs % 60 == 59`. - /// (The true "UNIX timestamp" cannot represent a leap second unambiguously.) - /// - /// # Panics - /// - /// Panics if the number of seconds would be out of range for a `NaiveDateTime` (more than - /// ca. 262,000 years away from common era), and panics on an invalid nanosecond (2 seconds or - /// more). - #[deprecated(since = "0.4.23", note = "use `DateTime::from_timestamp` instead")] - #[inline] - #[must_use] - pub const fn from_timestamp(secs: i64, nsecs: u32) -> NaiveDateTime { - let datetime = - expect(DateTime::from_timestamp(secs, nsecs), "invalid or out-of-range datetime"); - datetime.naive_utc() - } - - /// Creates a new [NaiveDateTime] from milliseconds since the UNIX epoch. - /// - /// The UNIX epoch starts on midnight, January 1, 1970, UTC. - /// - /// # Errors - /// - /// Returns `None` if the number of milliseconds would be out of range for a `NaiveDateTime` - /// (more than ca. 262,000 years away from common era) - #[deprecated(since = "0.4.35", note = "use `DateTime::from_timestamp_millis` instead")] - #[inline] - #[must_use] - pub const fn from_timestamp_millis(millis: i64) -> Option { - Some(try_opt!(DateTime::from_timestamp_millis(millis)).naive_utc()) - } - - /// Creates a new [NaiveDateTime] from microseconds since the UNIX epoch. - /// - /// The UNIX epoch starts on midnight, January 1, 1970, UTC. - /// - /// # Errors - /// - /// Returns `None` if the number of microseconds would be out of range for a `NaiveDateTime` - /// (more than ca. 262,000 years away from common era) - #[deprecated(since = "0.4.35", note = "use `DateTime::from_timestamp_micros` instead")] - #[inline] - #[must_use] - pub const fn from_timestamp_micros(micros: i64) -> Option { - let secs = micros.div_euclid(1_000_000); - let nsecs = micros.rem_euclid(1_000_000) as u32 * 1000; - Some(try_opt!(DateTime::::from_timestamp(secs, nsecs)).naive_utc()) - } - - /// Creates a new [NaiveDateTime] from nanoseconds since the UNIX epoch. - /// - /// The UNIX epoch starts on midnight, January 1, 1970, UTC. - /// - /// # Errors - /// - /// Returns `None` if the number of nanoseconds would be out of range for a `NaiveDateTime` - /// (more than ca. 262,000 years away from common era) - #[deprecated(since = "0.4.35", note = "use `DateTime::from_timestamp_nanos` instead")] - #[inline] - #[must_use] - pub const fn from_timestamp_nanos(nanos: i64) -> Option { - let secs = nanos.div_euclid(NANOS_PER_SEC as i64); - let nsecs = nanos.rem_euclid(NANOS_PER_SEC as i64) as u32; - Some(try_opt!(DateTime::from_timestamp(secs, nsecs)).naive_utc()) - } - - /// Makes a new `NaiveDateTime` corresponding to a UTC date and time, - /// from the number of non-leap seconds - /// since the midnight UTC on January 1, 1970 (aka "UNIX timestamp") - /// and the number of nanoseconds since the last whole non-leap second. - /// - /// The nanosecond part can exceed 1,000,000,000 in order to represent a - /// [leap second](NaiveTime#leap-second-handling), but only when `secs % 60 == 59`. - /// (The true "UNIX timestamp" cannot represent a leap second unambiguously.) - /// - /// # Errors - /// - /// Returns `None` if the number of seconds would be out of range for a `NaiveDateTime` (more - /// than ca. 262,000 years away from common era), and panics on an invalid nanosecond - /// (2 seconds or more). - #[deprecated(since = "0.4.35", note = "use `DateTime::from_timestamp` instead")] - #[inline] - #[must_use] - pub const fn from_timestamp_opt(secs: i64, nsecs: u32) -> Option { - Some(try_opt!(DateTime::from_timestamp(secs, nsecs)).naive_utc()) - } - - /// Parses a string with the specified format string and returns a new `NaiveDateTime`. - /// See the [`format::strftime` module](crate::format::strftime) - /// on the supported escape sequences. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, NaiveDateTime}; - /// - /// let parse_from_str = NaiveDateTime::parse_from_str; - /// - /// assert_eq!( - /// parse_from_str("2015-09-05 23:56:04", "%Y-%m-%d %H:%M:%S"), - /// Ok(NaiveDate::from_ymd_opt(2015, 9, 5).unwrap().and_hms_opt(23, 56, 4).unwrap()) - /// ); - /// assert_eq!( - /// parse_from_str("5sep2015pm012345.6789", "%d%b%Y%p%I%M%S%.f"), - /// Ok(NaiveDate::from_ymd_opt(2015, 9, 5) - /// .unwrap() - /// .and_hms_micro_opt(13, 23, 45, 678_900) - /// .unwrap()) - /// ); - /// ``` - /// - /// Offset is ignored for the purpose of parsing. - /// - /// ``` - /// # use chrono::{NaiveDateTime, NaiveDate}; - /// # let parse_from_str = NaiveDateTime::parse_from_str; - /// assert_eq!( - /// parse_from_str("2014-5-17T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), - /// Ok(NaiveDate::from_ymd_opt(2014, 5, 17).unwrap().and_hms_opt(12, 34, 56).unwrap()) - /// ); - /// ``` - /// - /// [Leap seconds](./struct.NaiveTime.html#leap-second-handling) are correctly handled by - /// treating any time of the form `hh:mm:60` as a leap second. - /// (This equally applies to the formatting, so the round trip is possible.) - /// - /// ``` - /// # use chrono::{NaiveDateTime, NaiveDate}; - /// # let parse_from_str = NaiveDateTime::parse_from_str; - /// assert_eq!( - /// parse_from_str("2015-07-01 08:59:60.123", "%Y-%m-%d %H:%M:%S%.f"), - /// Ok(NaiveDate::from_ymd_opt(2015, 7, 1) - /// .unwrap() - /// .and_hms_milli_opt(8, 59, 59, 1_123) - /// .unwrap()) - /// ); - /// ``` - /// - /// Missing seconds are assumed to be zero, - /// but out-of-bound times or insufficient fields are errors otherwise. - /// - /// ``` - /// # use chrono::{NaiveDateTime, NaiveDate}; - /// # let parse_from_str = NaiveDateTime::parse_from_str; - /// assert_eq!( - /// parse_from_str("94/9/4 7:15", "%y/%m/%d %H:%M"), - /// Ok(NaiveDate::from_ymd_opt(1994, 9, 4).unwrap().and_hms_opt(7, 15, 0).unwrap()) - /// ); - /// - /// assert!(parse_from_str("04m33s", "%Mm%Ss").is_err()); - /// assert!(parse_from_str("94/9/4 12", "%y/%m/%d %H").is_err()); - /// assert!(parse_from_str("94/9/4 17:60", "%y/%m/%d %H:%M").is_err()); - /// assert!(parse_from_str("94/9/4 24:00:00", "%y/%m/%d %H:%M:%S").is_err()); - /// ``` - /// - /// All parsed fields should be consistent to each other, otherwise it's an error. - /// - /// ``` - /// # use chrono::NaiveDateTime; - /// # let parse_from_str = NaiveDateTime::parse_from_str; - /// let fmt = "%Y-%m-%d %H:%M:%S = UNIX timestamp %s"; - /// assert!(parse_from_str("2001-09-09 01:46:39 = UNIX timestamp 999999999", fmt).is_ok()); - /// assert!(parse_from_str("1970-01-01 00:00:00 = UNIX timestamp 1", fmt).is_err()); - /// ``` - /// - /// Years before 1 BCE or after 9999 CE, require an initial sign - /// - ///``` - /// # use chrono::NaiveDateTime; - /// # let parse_from_str = NaiveDateTime::parse_from_str; - /// let fmt = "%Y-%m-%d %H:%M:%S"; - /// assert!(parse_from_str("10000-09-09 01:46:39", fmt).is_err()); - /// assert!(parse_from_str("+10000-09-09 01:46:39", fmt).is_ok()); - /// ``` - pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult { - let mut parsed = Parsed::new(); - parse(&mut parsed, s, StrftimeItems::new(fmt))?; - parsed.to_naive_datetime_with_offset(0) // no offset adjustment - } - - /// Parses a string with the specified format string and returns a new `NaiveDateTime`, and a - /// slice with the remaining portion of the string. - /// See the [`format::strftime` module](crate::format::strftime) - /// on the supported escape sequences. - /// - /// Similar to [`parse_from_str`](#method.parse_from_str). - /// - /// # Example - /// - /// ```rust - /// # use chrono::{NaiveDate, NaiveDateTime}; - /// let (datetime, remainder) = NaiveDateTime::parse_and_remainder( - /// "2015-02-18 23:16:09 trailing text", - /// "%Y-%m-%d %H:%M:%S", - /// ) - /// .unwrap(); - /// assert_eq!( - /// datetime, - /// NaiveDate::from_ymd_opt(2015, 2, 18).unwrap().and_hms_opt(23, 16, 9).unwrap() - /// ); - /// assert_eq!(remainder, " trailing text"); - /// ``` - pub fn parse_and_remainder<'a>(s: &'a str, fmt: &str) -> ParseResult<(NaiveDateTime, &'a str)> { - let mut parsed = Parsed::new(); - let remainder = parse_and_remainder(&mut parsed, s, StrftimeItems::new(fmt))?; - parsed.to_naive_datetime_with_offset(0).map(|d| (d, remainder)) // no offset adjustment - } - - /// Retrieves a date component. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDate; - /// - /// let dt = NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_opt(9, 10, 11).unwrap(); - /// assert_eq!(dt.date(), NaiveDate::from_ymd_opt(2016, 7, 8).unwrap()); - /// ``` - #[inline] - pub const fn date(&self) -> NaiveDate { - self.date - } - - /// Retrieves a time component. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, NaiveTime}; - /// - /// let dt = NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_opt(9, 10, 11).unwrap(); - /// assert_eq!(dt.time(), NaiveTime::from_hms_opt(9, 10, 11).unwrap()); - /// ``` - #[inline] - pub const fn time(&self) -> NaiveTime { - self.time - } - - /// Returns the number of non-leap seconds since the midnight on January 1, 1970. - /// - /// Note that this does *not* account for the timezone! - /// The true "UNIX timestamp" would count seconds since the midnight *UTC* on the epoch. - #[deprecated(since = "0.4.35", note = "use `.and_utc().timestamp()` instead")] - #[inline] - #[must_use] - pub const fn timestamp(&self) -> i64 { - self.and_utc().timestamp() - } - - /// Returns the number of non-leap *milliseconds* since midnight on January 1, 1970. - /// - /// Note that this does *not* account for the timezone! - /// The true "UNIX timestamp" would count seconds since the midnight *UTC* on the epoch. - #[deprecated(since = "0.4.35", note = "use `.and_utc().timestamp_millis()` instead")] - #[inline] - #[must_use] - pub const fn timestamp_millis(&self) -> i64 { - self.and_utc().timestamp_millis() - } - - /// Returns the number of non-leap *microseconds* since midnight on January 1, 1970. - /// - /// Note that this does *not* account for the timezone! - /// The true "UNIX timestamp" would count seconds since the midnight *UTC* on the epoch. - #[deprecated(since = "0.4.35", note = "use `.and_utc().timestamp_micros()` instead")] - #[inline] - #[must_use] - pub const fn timestamp_micros(&self) -> i64 { - self.and_utc().timestamp_micros() - } - - /// Returns the number of non-leap *nanoseconds* since midnight on January 1, 1970. - /// - /// Note that this does *not* account for the timezone! - /// The true "UNIX timestamp" would count seconds since the midnight *UTC* on the epoch. - /// - /// # Panics - /// - /// An `i64` with nanosecond precision can span a range of ~584 years. This function panics on - /// an out of range `NaiveDateTime`. - /// - /// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:43.145224192 - /// and 2262-04-11T23:47:16.854775807. - #[deprecated(since = "0.4.31", note = "use `.and_utc().timestamp_nanos_opt()` instead")] - #[inline] - #[must_use] - #[allow(deprecated)] - pub const fn timestamp_nanos(&self) -> i64 { - self.and_utc().timestamp_nanos() - } - - /// Returns the number of non-leap *nanoseconds* since midnight on January 1, 1970. - /// - /// Note that this does *not* account for the timezone! - /// The true "UNIX timestamp" would count seconds since the midnight *UTC* on the epoch. - /// - /// # Errors - /// - /// An `i64` with nanosecond precision can span a range of ~584 years. This function returns - /// `None` on an out of range `NaiveDateTime`. - /// - /// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:43.145224192 - /// and 2262-04-11T23:47:16.854775807. - #[deprecated(since = "0.4.35", note = "use `.and_utc().timestamp_nanos_opt()` instead")] - #[inline] - #[must_use] - pub const fn timestamp_nanos_opt(&self) -> Option { - self.and_utc().timestamp_nanos_opt() - } - - /// Returns the number of milliseconds since the last whole non-leap second. - /// - /// The return value ranges from 0 to 999, - /// or for [leap seconds](./struct.NaiveTime.html#leap-second-handling), to 1,999. - #[deprecated(since = "0.4.35", note = "use `.and_utc().timestamp_subsec_millis()` instead")] - #[inline] - #[must_use] - pub const fn timestamp_subsec_millis(&self) -> u32 { - self.and_utc().timestamp_subsec_millis() - } - - /// Returns the number of microseconds since the last whole non-leap second. - /// - /// The return value ranges from 0 to 999,999, - /// or for [leap seconds](./struct.NaiveTime.html#leap-second-handling), to 1,999,999. - #[deprecated(since = "0.4.35", note = "use `.and_utc().timestamp_subsec_micros()` instead")] - #[inline] - #[must_use] - pub const fn timestamp_subsec_micros(&self) -> u32 { - self.and_utc().timestamp_subsec_micros() - } - - /// Returns the number of nanoseconds since the last whole non-leap second. - /// - /// The return value ranges from 0 to 999,999,999, - /// or for [leap seconds](./struct.NaiveTime.html#leap-second-handling), to 1,999,999,999. - #[deprecated(since = "0.4.36", note = "use `.and_utc().timestamp_subsec_nanos()` instead")] - pub const fn timestamp_subsec_nanos(&self) -> u32 { - self.and_utc().timestamp_subsec_nanos() - } - - /// Adds given `TimeDelta` to the current date and time. - /// - /// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling), - /// the addition assumes that **there is no leap second ever**, - /// except when the `NaiveDateTime` itself represents a leap second - /// in which case the assumption becomes that **there is exactly a single leap second ever**. - /// - /// # Errors - /// - /// Returns `None` if the resulting date would be out of range. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, TimeDelta}; - /// - /// let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); - /// - /// let d = from_ymd(2016, 7, 8); - /// let hms = |h, m, s| d.and_hms_opt(h, m, s).unwrap(); - /// assert_eq!(hms(3, 5, 7).checked_add_signed(TimeDelta::zero()), Some(hms(3, 5, 7))); - /// assert_eq!( - /// hms(3, 5, 7).checked_add_signed(TimeDelta::try_seconds(1).unwrap()), - /// Some(hms(3, 5, 8)) - /// ); - /// assert_eq!( - /// hms(3, 5, 7).checked_add_signed(TimeDelta::try_seconds(-1).unwrap()), - /// Some(hms(3, 5, 6)) - /// ); - /// assert_eq!( - /// hms(3, 5, 7).checked_add_signed(TimeDelta::try_seconds(3600 + 60).unwrap()), - /// Some(hms(4, 6, 7)) - /// ); - /// assert_eq!( - /// hms(3, 5, 7).checked_add_signed(TimeDelta::try_seconds(86_400).unwrap()), - /// Some(from_ymd(2016, 7, 9).and_hms_opt(3, 5, 7).unwrap()) - /// ); - /// - /// let hmsm = |h, m, s, milli| d.and_hms_milli_opt(h, m, s, milli).unwrap(); - /// assert_eq!( - /// hmsm(3, 5, 7, 980).checked_add_signed(TimeDelta::try_milliseconds(450).unwrap()), - /// Some(hmsm(3, 5, 8, 430)) - /// ); - /// ``` - /// - /// Overflow returns `None`. - /// - /// ``` - /// # use chrono::{TimeDelta, NaiveDate}; - /// # let hms = |h, m, s| NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_opt(h, m, s).unwrap(); - /// assert_eq!(hms(3, 5, 7).checked_add_signed(TimeDelta::try_days(1_000_000_000).unwrap()), None); - /// ``` - /// - /// Leap seconds are handled, - /// but the addition assumes that it is the only leap second happened. - /// - /// ``` - /// # use chrono::{TimeDelta, NaiveDate}; - /// # let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); - /// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli_opt(h, m, s, milli).unwrap(); - /// let leap = hmsm(3, 5, 59, 1_300); - /// assert_eq!(leap.checked_add_signed(TimeDelta::zero()), - /// Some(hmsm(3, 5, 59, 1_300))); - /// assert_eq!(leap.checked_add_signed(TimeDelta::try_milliseconds(-500).unwrap()), - /// Some(hmsm(3, 5, 59, 800))); - /// assert_eq!(leap.checked_add_signed(TimeDelta::try_milliseconds(500).unwrap()), - /// Some(hmsm(3, 5, 59, 1_800))); - /// assert_eq!(leap.checked_add_signed(TimeDelta::try_milliseconds(800).unwrap()), - /// Some(hmsm(3, 6, 0, 100))); - /// assert_eq!(leap.checked_add_signed(TimeDelta::try_seconds(10).unwrap()), - /// Some(hmsm(3, 6, 9, 300))); - /// assert_eq!(leap.checked_add_signed(TimeDelta::try_seconds(-10).unwrap()), - /// Some(hmsm(3, 5, 50, 300))); - /// assert_eq!(leap.checked_add_signed(TimeDelta::try_days(1).unwrap()), - /// Some(from_ymd(2016, 7, 9).and_hms_milli_opt(3, 5, 59, 300).unwrap())); - /// ``` - #[must_use] - pub const fn checked_add_signed(self, rhs: TimeDelta) -> Option { - let (time, remainder) = self.time.overflowing_add_signed(rhs); - let remainder = try_opt!(TimeDelta::try_seconds(remainder)); - let date = try_opt!(self.date.checked_add_signed(remainder)); - Some(NaiveDateTime { date, time }) - } - - /// Adds given `Months` to the current date and time. - /// - /// Uses the last day of the month if the day does not exist in the resulting month. - /// - /// # Errors - /// - /// Returns `None` if the resulting date would be out of range. - /// - /// # Example - /// - /// ``` - /// use chrono::{Months, NaiveDate}; - /// - /// assert_eq!( - /// NaiveDate::from_ymd_opt(2014, 1, 1) - /// .unwrap() - /// .and_hms_opt(1, 0, 0) - /// .unwrap() - /// .checked_add_months(Months::new(1)), - /// Some(NaiveDate::from_ymd_opt(2014, 2, 1).unwrap().and_hms_opt(1, 0, 0).unwrap()) - /// ); - /// - /// assert_eq!( - /// NaiveDate::from_ymd_opt(2014, 1, 1) - /// .unwrap() - /// .and_hms_opt(1, 0, 0) - /// .unwrap() - /// .checked_add_months(Months::new(core::i32::MAX as u32 + 1)), - /// None - /// ); - /// ``` - #[must_use] - pub const fn checked_add_months(self, rhs: Months) -> Option { - Some(Self { date: try_opt!(self.date.checked_add_months(rhs)), time: self.time }) - } - - /// Adds given `FixedOffset` to the current datetime. - /// Returns `None` if the result would be outside the valid range for [`NaiveDateTime`]. - /// - /// This method is similar to [`checked_add_signed`](#method.checked_add_offset), but preserves - /// leap seconds. - #[must_use] - pub const fn checked_add_offset(self, rhs: FixedOffset) -> Option { - let (time, days) = self.time.overflowing_add_offset(rhs); - let date = match days { - -1 => try_opt!(self.date.pred_opt()), - 1 => try_opt!(self.date.succ_opt()), - _ => self.date, - }; - Some(NaiveDateTime { date, time }) - } - - /// Subtracts given `FixedOffset` from the current datetime. - /// Returns `None` if the result would be outside the valid range for [`NaiveDateTime`]. - /// - /// This method is similar to [`checked_sub_signed`](#method.checked_sub_signed), but preserves - /// leap seconds. - pub const fn checked_sub_offset(self, rhs: FixedOffset) -> Option { - let (time, days) = self.time.overflowing_sub_offset(rhs); - let date = match days { - -1 => try_opt!(self.date.pred_opt()), - 1 => try_opt!(self.date.succ_opt()), - _ => self.date, - }; - Some(NaiveDateTime { date, time }) - } - - /// Adds given `FixedOffset` to the current datetime. - /// The resulting value may be outside the valid range of [`NaiveDateTime`]. - /// - /// This can be useful for intermediate values, but the resulting out-of-range `NaiveDate` - /// should not be exposed to library users. - #[must_use] - pub(crate) fn overflowing_add_offset(self, rhs: FixedOffset) -> NaiveDateTime { - let (time, days) = self.time.overflowing_add_offset(rhs); - let date = match days { - -1 => self.date.pred_opt().unwrap_or(NaiveDate::BEFORE_MIN), - 1 => self.date.succ_opt().unwrap_or(NaiveDate::AFTER_MAX), - _ => self.date, - }; - NaiveDateTime { date, time } - } - - /// Subtracts given `FixedOffset` from the current datetime. - /// The resulting value may be outside the valid range of [`NaiveDateTime`]. - /// - /// This can be useful for intermediate values, but the resulting out-of-range `NaiveDate` - /// should not be exposed to library users. - #[must_use] - #[allow(unused)] // currently only used in `Local` but not on all platforms - pub(crate) fn overflowing_sub_offset(self, rhs: FixedOffset) -> NaiveDateTime { - let (time, days) = self.time.overflowing_sub_offset(rhs); - let date = match days { - -1 => self.date.pred_opt().unwrap_or(NaiveDate::BEFORE_MIN), - 1 => self.date.succ_opt().unwrap_or(NaiveDate::AFTER_MAX), - _ => self.date, - }; - NaiveDateTime { date, time } - } - - /// Subtracts given `TimeDelta` from the current date and time. - /// - /// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling), - /// the subtraction assumes that **there is no leap second ever**, - /// except when the `NaiveDateTime` itself represents a leap second - /// in which case the assumption becomes that **there is exactly a single leap second ever**. - /// - /// # Errors - /// - /// Returns `None` if the resulting date would be out of range. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, TimeDelta}; - /// - /// let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); - /// - /// let d = from_ymd(2016, 7, 8); - /// let hms = |h, m, s| d.and_hms_opt(h, m, s).unwrap(); - /// assert_eq!(hms(3, 5, 7).checked_sub_signed(TimeDelta::zero()), Some(hms(3, 5, 7))); - /// assert_eq!( - /// hms(3, 5, 7).checked_sub_signed(TimeDelta::try_seconds(1).unwrap()), - /// Some(hms(3, 5, 6)) - /// ); - /// assert_eq!( - /// hms(3, 5, 7).checked_sub_signed(TimeDelta::try_seconds(-1).unwrap()), - /// Some(hms(3, 5, 8)) - /// ); - /// assert_eq!( - /// hms(3, 5, 7).checked_sub_signed(TimeDelta::try_seconds(3600 + 60).unwrap()), - /// Some(hms(2, 4, 7)) - /// ); - /// assert_eq!( - /// hms(3, 5, 7).checked_sub_signed(TimeDelta::try_seconds(86_400).unwrap()), - /// Some(from_ymd(2016, 7, 7).and_hms_opt(3, 5, 7).unwrap()) - /// ); - /// - /// let hmsm = |h, m, s, milli| d.and_hms_milli_opt(h, m, s, milli).unwrap(); - /// assert_eq!( - /// hmsm(3, 5, 7, 450).checked_sub_signed(TimeDelta::try_milliseconds(670).unwrap()), - /// Some(hmsm(3, 5, 6, 780)) - /// ); - /// ``` - /// - /// Overflow returns `None`. - /// - /// ``` - /// # use chrono::{TimeDelta, NaiveDate}; - /// # let hms = |h, m, s| NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_opt(h, m, s).unwrap(); - /// assert_eq!(hms(3, 5, 7).checked_sub_signed(TimeDelta::try_days(1_000_000_000).unwrap()), None); - /// ``` - /// - /// Leap seconds are handled, - /// but the subtraction assumes that it is the only leap second happened. - /// - /// ``` - /// # use chrono::{TimeDelta, NaiveDate}; - /// # let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); - /// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli_opt(h, m, s, milli).unwrap(); - /// let leap = hmsm(3, 5, 59, 1_300); - /// assert_eq!(leap.checked_sub_signed(TimeDelta::zero()), - /// Some(hmsm(3, 5, 59, 1_300))); - /// assert_eq!(leap.checked_sub_signed(TimeDelta::try_milliseconds(200).unwrap()), - /// Some(hmsm(3, 5, 59, 1_100))); - /// assert_eq!(leap.checked_sub_signed(TimeDelta::try_milliseconds(500).unwrap()), - /// Some(hmsm(3, 5, 59, 800))); - /// assert_eq!(leap.checked_sub_signed(TimeDelta::try_seconds(60).unwrap()), - /// Some(hmsm(3, 5, 0, 300))); - /// assert_eq!(leap.checked_sub_signed(TimeDelta::try_days(1).unwrap()), - /// Some(from_ymd(2016, 7, 7).and_hms_milli_opt(3, 6, 0, 300).unwrap())); - /// ``` - #[must_use] - pub const fn checked_sub_signed(self, rhs: TimeDelta) -> Option { - let (time, remainder) = self.time.overflowing_sub_signed(rhs); - let remainder = try_opt!(TimeDelta::try_seconds(remainder)); - let date = try_opt!(self.date.checked_sub_signed(remainder)); - Some(NaiveDateTime { date, time }) - } - - /// Subtracts given `Months` from the current date and time. - /// - /// Uses the last day of the month if the day does not exist in the resulting month. - /// - /// # Errors - /// - /// Returns `None` if the resulting date would be out of range. - /// - /// # Example - /// - /// ``` - /// use chrono::{Months, NaiveDate}; - /// - /// assert_eq!( - /// NaiveDate::from_ymd_opt(2014, 1, 1) - /// .unwrap() - /// .and_hms_opt(1, 0, 0) - /// .unwrap() - /// .checked_sub_months(Months::new(1)), - /// Some(NaiveDate::from_ymd_opt(2013, 12, 1).unwrap().and_hms_opt(1, 0, 0).unwrap()) - /// ); - /// - /// assert_eq!( - /// NaiveDate::from_ymd_opt(2014, 1, 1) - /// .unwrap() - /// .and_hms_opt(1, 0, 0) - /// .unwrap() - /// .checked_sub_months(Months::new(core::i32::MAX as u32 + 1)), - /// None - /// ); - /// ``` - #[must_use] - pub const fn checked_sub_months(self, rhs: Months) -> Option { - Some(Self { date: try_opt!(self.date.checked_sub_months(rhs)), time: self.time }) - } - - /// Add a duration in [`Days`] to the date part of the `NaiveDateTime` - /// - /// Returns `None` if the resulting date would be out of range. - #[must_use] - pub const fn checked_add_days(self, days: Days) -> Option { - Some(Self { date: try_opt!(self.date.checked_add_days(days)), ..self }) - } - - /// Subtract a duration in [`Days`] from the date part of the `NaiveDateTime` - /// - /// Returns `None` if the resulting date would be out of range. - #[must_use] - pub const fn checked_sub_days(self, days: Days) -> Option { - Some(Self { date: try_opt!(self.date.checked_sub_days(days)), ..self }) - } - - /// Subtracts another `NaiveDateTime` from the current date and time. - /// This does not overflow or underflow at all. - /// - /// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling), - /// the subtraction assumes that **there is no leap second ever**, - /// except when any of the `NaiveDateTime`s themselves represents a leap second - /// in which case the assumption becomes that - /// **there are exactly one (or two) leap second(s) ever**. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, TimeDelta}; - /// - /// let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); - /// - /// let d = from_ymd(2016, 7, 8); - /// assert_eq!( - /// d.and_hms_opt(3, 5, 7).unwrap().signed_duration_since(d.and_hms_opt(2, 4, 6).unwrap()), - /// TimeDelta::try_seconds(3600 + 60 + 1).unwrap() - /// ); - /// - /// // July 8 is 190th day in the year 2016 - /// let d0 = from_ymd(2016, 1, 1); - /// assert_eq!( - /// d.and_hms_milli_opt(0, 7, 6, 500) - /// .unwrap() - /// .signed_duration_since(d0.and_hms_opt(0, 0, 0).unwrap()), - /// TimeDelta::try_seconds(189 * 86_400 + 7 * 60 + 6).unwrap() - /// + TimeDelta::try_milliseconds(500).unwrap() - /// ); - /// ``` - /// - /// Leap seconds are handled, but the subtraction assumes that - /// there were no other leap seconds happened. - /// - /// ``` - /// # use chrono::{TimeDelta, NaiveDate}; - /// # let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); - /// let leap = from_ymd(2015, 6, 30).and_hms_milli_opt(23, 59, 59, 1_500).unwrap(); - /// assert_eq!( - /// leap.signed_duration_since(from_ymd(2015, 6, 30).and_hms_opt(23, 0, 0).unwrap()), - /// TimeDelta::try_seconds(3600).unwrap() + TimeDelta::try_milliseconds(500).unwrap() - /// ); - /// assert_eq!( - /// from_ymd(2015, 7, 1).and_hms_opt(1, 0, 0).unwrap().signed_duration_since(leap), - /// TimeDelta::try_seconds(3600).unwrap() - TimeDelta::try_milliseconds(500).unwrap() - /// ); - /// ``` - #[must_use] - pub const fn signed_duration_since(self, rhs: NaiveDateTime) -> TimeDelta { - expect( - self.date - .signed_duration_since(rhs.date) - .checked_add(&self.time.signed_duration_since(rhs.time)), - "always in range", - ) - } - - /// Formats the combined date and time with the specified formatting items. - /// Otherwise it is the same as the ordinary [`format`](#method.format) method. - /// - /// The `Iterator` of items should be `Clone`able, - /// since the resulting `DelayedFormat` value may be formatted multiple times. - /// - /// # Example - /// - /// ``` - /// use chrono::format::strftime::StrftimeItems; - /// use chrono::NaiveDate; - /// - /// let fmt = StrftimeItems::new("%Y-%m-%d %H:%M:%S"); - /// let dt = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap().and_hms_opt(23, 56, 4).unwrap(); - /// assert_eq!(dt.format_with_items(fmt.clone()).to_string(), "2015-09-05 23:56:04"); - /// assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2015-09-05 23:56:04"); - /// ``` - /// - /// The resulting `DelayedFormat` can be formatted directly via the `Display` trait. - /// - /// ``` - /// # use chrono::NaiveDate; - /// # use chrono::format::strftime::StrftimeItems; - /// # let fmt = StrftimeItems::new("%Y-%m-%d %H:%M:%S").clone(); - /// # let dt = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap().and_hms_opt(23, 56, 4).unwrap(); - /// assert_eq!(format!("{}", dt.format_with_items(fmt)), "2015-09-05 23:56:04"); - /// ``` - #[cfg(feature = "alloc")] - #[inline] - #[must_use] - pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat - where - I: Iterator + Clone, - B: Borrow>, - { - DelayedFormat::new(Some(self.date), Some(self.time), items) - } - - /// Formats the combined date and time with the specified format string. - /// See the [`format::strftime` module](crate::format::strftime) - /// on the supported escape sequences. - /// - /// This returns a `DelayedFormat`, - /// which gets converted to a string only when actual formatting happens. - /// You may use the `to_string` method to get a `String`, - /// or just feed it into `print!` and other formatting macros. - /// (In this way it avoids the redundant memory allocation.) - /// - /// A wrong format string does *not* issue an error immediately. - /// Rather, converting or formatting the `DelayedFormat` fails. - /// You are recommended to immediately use `DelayedFormat` for this reason. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveDate; - /// - /// let dt = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap().and_hms_opt(23, 56, 4).unwrap(); - /// assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2015-09-05 23:56:04"); - /// assert_eq!(dt.format("around %l %p on %b %-d").to_string(), "around 11 PM on Sep 5"); - /// ``` - /// - /// The resulting `DelayedFormat` can be formatted directly via the `Display` trait. - /// - /// ``` - /// # use chrono::NaiveDate; - /// # let dt = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap().and_hms_opt(23, 56, 4).unwrap(); - /// assert_eq!(format!("{}", dt.format("%Y-%m-%d %H:%M:%S")), "2015-09-05 23:56:04"); - /// assert_eq!(format!("{}", dt.format("around %l %p on %b %-d")), "around 11 PM on Sep 5"); - /// ``` - #[cfg(feature = "alloc")] - #[inline] - #[must_use] - pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat> { - self.format_with_items(StrftimeItems::new(fmt)) - } - - /// Converts the `NaiveDateTime` into a timezone-aware `DateTime` with the provided - /// time zone. - /// - /// # Example - /// - /// ``` - /// use chrono::{FixedOffset, NaiveDate}; - /// let hour = 3600; - /// let tz = FixedOffset::east_opt(5 * hour).unwrap(); - /// let dt = NaiveDate::from_ymd_opt(2015, 9, 5) - /// .unwrap() - /// .and_hms_opt(23, 56, 4) - /// .unwrap() - /// .and_local_timezone(tz) - /// .unwrap(); - /// assert_eq!(dt.timezone(), tz); - /// ``` - #[must_use] - pub fn and_local_timezone(&self, tz: Tz) -> MappedLocalTime> { - tz.from_local_datetime(self) - } - - /// Converts the `NaiveDateTime` into the timezone-aware `DateTime`. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, Utc}; - /// let dt = - /// NaiveDate::from_ymd_opt(2023, 1, 30).unwrap().and_hms_opt(19, 32, 33).unwrap().and_utc(); - /// assert_eq!(dt.timezone(), Utc); - /// ``` - #[must_use] - pub const fn and_utc(&self) -> DateTime { - DateTime::from_naive_utc_and_offset(*self, Utc) - } - - /// The minimum possible `NaiveDateTime`. - pub const MIN: Self = Self { date: NaiveDate::MIN, time: NaiveTime::MIN }; - - /// The maximum possible `NaiveDateTime`. - pub const MAX: Self = Self { date: NaiveDate::MAX, time: NaiveTime::MAX }; - - /// The datetime of the Unix Epoch, 1970-01-01 00:00:00. - /// - /// Note that while this may look like the UNIX epoch, it is missing the - /// time zone. The actual UNIX epoch cannot be expressed by this type, - /// however it is available as [`DateTime::UNIX_EPOCH`]. - #[deprecated(since = "0.4.41", note = "use `DateTime::UNIX_EPOCH` instead")] - pub const UNIX_EPOCH: Self = DateTime::UNIX_EPOCH.naive_utc(); -} - -impl From for NaiveDateTime { - /// Converts a `NaiveDate` to a `NaiveDateTime` of the same date but at midnight. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, NaiveDateTime}; - /// - /// let nd = NaiveDate::from_ymd_opt(2016, 5, 28).unwrap(); - /// let ndt = NaiveDate::from_ymd_opt(2016, 5, 28).unwrap().and_hms_opt(0, 0, 0).unwrap(); - /// assert_eq!(ndt, NaiveDateTime::from(nd)); - fn from(date: NaiveDate) -> Self { - date.and_hms_opt(0, 0, 0).unwrap() - } -} - -impl Datelike for NaiveDateTime { - /// Returns the year number in the [calendar date](./struct.NaiveDate.html#calendar-date). - /// - /// See also the [`NaiveDate::year`](./struct.NaiveDate.html#method.year) method. - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate, NaiveDateTime}; - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2015, 9, 25).unwrap().and_hms_opt(12, 34, 56).unwrap(); - /// assert_eq!(dt.year(), 2015); - /// ``` - #[inline] - fn year(&self) -> i32 { - self.date.year() - } - - /// Returns the month number starting from 1. - /// - /// The return value ranges from 1 to 12. - /// - /// See also the [`NaiveDate::month`](./struct.NaiveDate.html#method.month) method. - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate, NaiveDateTime}; - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2015, 9, 25).unwrap().and_hms_opt(12, 34, 56).unwrap(); - /// assert_eq!(dt.month(), 9); - /// ``` - #[inline] - fn month(&self) -> u32 { - self.date.month() - } - - /// Returns the month number starting from 0. - /// - /// The return value ranges from 0 to 11. - /// - /// See also the [`NaiveDate::month0`] method. - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate, NaiveDateTime}; - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2015, 9, 25).unwrap().and_hms_opt(12, 34, 56).unwrap(); - /// assert_eq!(dt.month0(), 8); - /// ``` - #[inline] - fn month0(&self) -> u32 { - self.date.month0() - } - - /// Returns the day of month starting from 1. - /// - /// The return value ranges from 1 to 31. (The last day of month differs by months.) - /// - /// See also the [`NaiveDate::day`](./struct.NaiveDate.html#method.day) method. - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate, NaiveDateTime}; - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2015, 9, 25).unwrap().and_hms_opt(12, 34, 56).unwrap(); - /// assert_eq!(dt.day(), 25); - /// ``` - #[inline] - fn day(&self) -> u32 { - self.date.day() - } - - /// Returns the day of month starting from 0. - /// - /// The return value ranges from 0 to 30. (The last day of month differs by months.) - /// - /// See also the [`NaiveDate::day0`] method. - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate, NaiveDateTime}; - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2015, 9, 25).unwrap().and_hms_opt(12, 34, 56).unwrap(); - /// assert_eq!(dt.day0(), 24); - /// ``` - #[inline] - fn day0(&self) -> u32 { - self.date.day0() - } - - /// Returns the day of year starting from 1. - /// - /// The return value ranges from 1 to 366. (The last day of year differs by years.) - /// - /// See also the [`NaiveDate::ordinal`](./struct.NaiveDate.html#method.ordinal) method. - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate, NaiveDateTime}; - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2015, 9, 25).unwrap().and_hms_opt(12, 34, 56).unwrap(); - /// assert_eq!(dt.ordinal(), 268); - /// ``` - #[inline] - fn ordinal(&self) -> u32 { - self.date.ordinal() - } - - /// Returns the day of year starting from 0. - /// - /// The return value ranges from 0 to 365. (The last day of year differs by years.) - /// - /// See also the [`NaiveDate::ordinal0`] method. - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate, NaiveDateTime}; - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2015, 9, 25).unwrap().and_hms_opt(12, 34, 56).unwrap(); - /// assert_eq!(dt.ordinal0(), 267); - /// ``` - #[inline] - fn ordinal0(&self) -> u32 { - self.date.ordinal0() - } - - /// Returns the day of week. - /// - /// See also the [`NaiveDate::weekday`](./struct.NaiveDate.html#method.weekday) method. - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate, NaiveDateTime, Weekday}; - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2015, 9, 25).unwrap().and_hms_opt(12, 34, 56).unwrap(); - /// assert_eq!(dt.weekday(), Weekday::Fri); - /// ``` - #[inline] - fn weekday(&self) -> Weekday { - self.date.weekday() - } - - #[inline] - fn iso_week(&self) -> IsoWeek { - self.date.iso_week() - } - - /// Makes a new `NaiveDateTime` with the year number changed, while keeping the same month and - /// day. - /// - /// See also the [`NaiveDate::with_year`] method. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The resulting date does not exist (February 29 in a non-leap year). - /// - The year is out of range for a `NaiveDate`. - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate, NaiveDateTime}; - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2015, 9, 25).unwrap().and_hms_opt(12, 34, 56).unwrap(); - /// assert_eq!( - /// dt.with_year(2016), - /// Some(NaiveDate::from_ymd_opt(2016, 9, 25).unwrap().and_hms_opt(12, 34, 56).unwrap()) - /// ); - /// assert_eq!( - /// dt.with_year(-308), - /// Some(NaiveDate::from_ymd_opt(-308, 9, 25).unwrap().and_hms_opt(12, 34, 56).unwrap()) - /// ); - /// ``` - #[inline] - fn with_year(&self, year: i32) -> Option { - self.date.with_year(year).map(|d| NaiveDateTime { date: d, ..*self }) - } - - /// Makes a new `NaiveDateTime` with the month number (starting from 1) changed. - /// - /// Don't combine multiple `Datelike::with_*` methods. The intermediate value may not exist. - /// - /// See also the [`NaiveDate::with_month`] method. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The resulting date does not exist (for example `month(4)` when day of the month is 31). - /// - The value for `month` is invalid. - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate, NaiveDateTime}; - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2015, 9, 30).unwrap().and_hms_opt(12, 34, 56).unwrap(); - /// assert_eq!( - /// dt.with_month(10), - /// Some(NaiveDate::from_ymd_opt(2015, 10, 30).unwrap().and_hms_opt(12, 34, 56).unwrap()) - /// ); - /// assert_eq!(dt.with_month(13), None); // No month 13 - /// assert_eq!(dt.with_month(2), None); // No February 30 - /// ``` - #[inline] - fn with_month(&self, month: u32) -> Option { - self.date.with_month(month).map(|d| NaiveDateTime { date: d, ..*self }) - } - - /// Makes a new `NaiveDateTime` with the month number (starting from 0) changed. - /// - /// See also the [`NaiveDate::with_month0`] method. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The resulting date does not exist (for example `month0(3)` when day of the month is 31). - /// - The value for `month0` is invalid. - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate, NaiveDateTime}; - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2015, 9, 30).unwrap().and_hms_opt(12, 34, 56).unwrap(); - /// assert_eq!( - /// dt.with_month0(9), - /// Some(NaiveDate::from_ymd_opt(2015, 10, 30).unwrap().and_hms_opt(12, 34, 56).unwrap()) - /// ); - /// assert_eq!(dt.with_month0(12), None); // No month 13 - /// assert_eq!(dt.with_month0(1), None); // No February 30 - /// ``` - #[inline] - fn with_month0(&self, month0: u32) -> Option { - self.date.with_month0(month0).map(|d| NaiveDateTime { date: d, ..*self }) - } - - /// Makes a new `NaiveDateTime` with the day of month (starting from 1) changed. - /// - /// See also the [`NaiveDate::with_day`] method. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The resulting date does not exist (for example `day(31)` in April). - /// - The value for `day` is invalid. - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate, NaiveDateTime}; - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_opt(12, 34, 56).unwrap(); - /// assert_eq!( - /// dt.with_day(30), - /// Some(NaiveDate::from_ymd_opt(2015, 9, 30).unwrap().and_hms_opt(12, 34, 56).unwrap()) - /// ); - /// assert_eq!(dt.with_day(31), None); // no September 31 - /// ``` - #[inline] - fn with_day(&self, day: u32) -> Option { - self.date.with_day(day).map(|d| NaiveDateTime { date: d, ..*self }) - } - - /// Makes a new `NaiveDateTime` with the day of month (starting from 0) changed. - /// - /// See also the [`NaiveDate::with_day0`] method. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The resulting date does not exist (for example `day(30)` in April). - /// - The value for `day0` is invalid. - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate, NaiveDateTime}; - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_opt(12, 34, 56).unwrap(); - /// assert_eq!( - /// dt.with_day0(29), - /// Some(NaiveDate::from_ymd_opt(2015, 9, 30).unwrap().and_hms_opt(12, 34, 56).unwrap()) - /// ); - /// assert_eq!(dt.with_day0(30), None); // no September 31 - /// ``` - #[inline] - fn with_day0(&self, day0: u32) -> Option { - self.date.with_day0(day0).map(|d| NaiveDateTime { date: d, ..*self }) - } - - /// Makes a new `NaiveDateTime` with the day of year (starting from 1) changed. - /// - /// See also the [`NaiveDate::with_ordinal`] method. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The resulting date does not exist (`with_ordinal(366)` in a non-leap year). - /// - The value for `ordinal` is invalid. - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate, NaiveDateTime}; - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_opt(12, 34, 56).unwrap(); - /// assert_eq!( - /// dt.with_ordinal(60), - /// Some(NaiveDate::from_ymd_opt(2015, 3, 1).unwrap().and_hms_opt(12, 34, 56).unwrap()) - /// ); - /// assert_eq!(dt.with_ordinal(366), None); // 2015 had only 365 days - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2016, 9, 8).unwrap().and_hms_opt(12, 34, 56).unwrap(); - /// assert_eq!( - /// dt.with_ordinal(60), - /// Some(NaiveDate::from_ymd_opt(2016, 2, 29).unwrap().and_hms_opt(12, 34, 56).unwrap()) - /// ); - /// assert_eq!( - /// dt.with_ordinal(366), - /// Some(NaiveDate::from_ymd_opt(2016, 12, 31).unwrap().and_hms_opt(12, 34, 56).unwrap()) - /// ); - /// ``` - #[inline] - fn with_ordinal(&self, ordinal: u32) -> Option { - self.date.with_ordinal(ordinal).map(|d| NaiveDateTime { date: d, ..*self }) - } - - /// Makes a new `NaiveDateTime` with the day of year (starting from 0) changed. - /// - /// See also the [`NaiveDate::with_ordinal0`] method. - /// - /// # Errors - /// - /// Returns `None` if: - /// - The resulting date does not exist (`with_ordinal0(365)` in a non-leap year). - /// - The value for `ordinal0` is invalid. - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate, NaiveDateTime}; - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_opt(12, 34, 56).unwrap(); - /// assert_eq!( - /// dt.with_ordinal0(59), - /// Some(NaiveDate::from_ymd_opt(2015, 3, 1).unwrap().and_hms_opt(12, 34, 56).unwrap()) - /// ); - /// assert_eq!(dt.with_ordinal0(365), None); // 2015 had only 365 days - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2016, 9, 8).unwrap().and_hms_opt(12, 34, 56).unwrap(); - /// assert_eq!( - /// dt.with_ordinal0(59), - /// Some(NaiveDate::from_ymd_opt(2016, 2, 29).unwrap().and_hms_opt(12, 34, 56).unwrap()) - /// ); - /// assert_eq!( - /// dt.with_ordinal0(365), - /// Some(NaiveDate::from_ymd_opt(2016, 12, 31).unwrap().and_hms_opt(12, 34, 56).unwrap()) - /// ); - /// ``` - #[inline] - fn with_ordinal0(&self, ordinal0: u32) -> Option { - self.date.with_ordinal0(ordinal0).map(|d| NaiveDateTime { date: d, ..*self }) - } -} - -impl Timelike for NaiveDateTime { - /// Returns the hour number from 0 to 23. - /// - /// See also the [`NaiveTime::hour`] method. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_milli_opt(12, 34, 56, 789).unwrap(); - /// assert_eq!(dt.hour(), 12); - /// ``` - #[inline] - fn hour(&self) -> u32 { - self.time.hour() - } - - /// Returns the minute number from 0 to 59. - /// - /// See also the [`NaiveTime::minute`] method. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_milli_opt(12, 34, 56, 789).unwrap(); - /// assert_eq!(dt.minute(), 34); - /// ``` - #[inline] - fn minute(&self) -> u32 { - self.time.minute() - } - - /// Returns the second number from 0 to 59. - /// - /// See also the [`NaiveTime::second`] method. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_milli_opt(12, 34, 56, 789).unwrap(); - /// assert_eq!(dt.second(), 56); - /// ``` - #[inline] - fn second(&self) -> u32 { - self.time.second() - } - - /// Returns the number of nanoseconds since the whole non-leap second. - /// The range from 1,000,000,000 to 1,999,999,999 represents - /// the [leap second](./struct.NaiveTime.html#leap-second-handling). - /// - /// See also the [`NaiveTime#method.nanosecond`] method. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_milli_opt(12, 34, 56, 789).unwrap(); - /// assert_eq!(dt.nanosecond(), 789_000_000); - /// ``` - #[inline] - fn nanosecond(&self) -> u32 { - self.time.nanosecond() - } - - /// Makes a new `NaiveDateTime` with the hour number changed. - /// - /// See also the [`NaiveTime::with_hour`] method. - /// - /// # Errors - /// - /// Returns `None` if the value for `hour` is invalid. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_milli_opt(12, 34, 56, 789).unwrap(); - /// assert_eq!( - /// dt.with_hour(7), - /// Some( - /// NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_milli_opt(7, 34, 56, 789).unwrap() - /// ) - /// ); - /// assert_eq!(dt.with_hour(24), None); - /// ``` - #[inline] - fn with_hour(&self, hour: u32) -> Option { - self.time.with_hour(hour).map(|t| NaiveDateTime { time: t, ..*self }) - } - - /// Makes a new `NaiveDateTime` with the minute number changed. - /// - /// See also the [`NaiveTime::with_minute`] method. - /// - /// # Errors - /// - /// Returns `None` if the value for `minute` is invalid. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_milli_opt(12, 34, 56, 789).unwrap(); - /// assert_eq!( - /// dt.with_minute(45), - /// Some( - /// NaiveDate::from_ymd_opt(2015, 9, 8) - /// .unwrap() - /// .and_hms_milli_opt(12, 45, 56, 789) - /// .unwrap() - /// ) - /// ); - /// assert_eq!(dt.with_minute(60), None); - /// ``` - #[inline] - fn with_minute(&self, min: u32) -> Option { - self.time.with_minute(min).map(|t| NaiveDateTime { time: t, ..*self }) - } - - /// Makes a new `NaiveDateTime` with the second number changed. - /// - /// As with the [`second`](#method.second) method, - /// the input range is restricted to 0 through 59. - /// - /// See also the [`NaiveTime::with_second`] method. - /// - /// # Errors - /// - /// Returns `None` if the value for `second` is invalid. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_milli_opt(12, 34, 56, 789).unwrap(); - /// assert_eq!( - /// dt.with_second(17), - /// Some( - /// NaiveDate::from_ymd_opt(2015, 9, 8) - /// .unwrap() - /// .and_hms_milli_opt(12, 34, 17, 789) - /// .unwrap() - /// ) - /// ); - /// assert_eq!(dt.with_second(60), None); - /// ``` - #[inline] - fn with_second(&self, sec: u32) -> Option { - self.time.with_second(sec).map(|t| NaiveDateTime { time: t, ..*self }) - } - - /// Makes a new `NaiveDateTime` with nanoseconds since the whole non-leap second changed. - /// - /// Returns `None` when the resulting `NaiveDateTime` would be invalid. - /// As with the [`NaiveDateTime::nanosecond`] method, - /// the input range can exceed 1,000,000,000 for leap seconds. - /// - /// See also the [`NaiveTime::with_nanosecond`] method. - /// - /// # Errors - /// - /// Returns `None` if `nanosecond >= 2,000,000,000`. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveDate, NaiveDateTime, Timelike}; - /// - /// let dt: NaiveDateTime = - /// NaiveDate::from_ymd_opt(2015, 9, 8).unwrap().and_hms_milli_opt(12, 34, 59, 789).unwrap(); - /// assert_eq!( - /// dt.with_nanosecond(333_333_333), - /// Some( - /// NaiveDate::from_ymd_opt(2015, 9, 8) - /// .unwrap() - /// .and_hms_nano_opt(12, 34, 59, 333_333_333) - /// .unwrap() - /// ) - /// ); - /// assert_eq!( - /// dt.with_nanosecond(1_333_333_333), // leap second - /// Some( - /// NaiveDate::from_ymd_opt(2015, 9, 8) - /// .unwrap() - /// .and_hms_nano_opt(12, 34, 59, 1_333_333_333) - /// .unwrap() - /// ) - /// ); - /// assert_eq!(dt.with_nanosecond(2_000_000_000), None); - /// ``` - #[inline] - fn with_nanosecond(&self, nano: u32) -> Option { - self.time.with_nanosecond(nano).map(|t| NaiveDateTime { time: t, ..*self }) - } -} - -/// Add `TimeDelta` to `NaiveDateTime`. -/// -/// As a part of Chrono's [leap second handling], the addition assumes that **there is no leap -/// second ever**, except when the `NaiveDateTime` itself represents a leap second in which case -/// the assumption becomes that **there is exactly a single leap second ever**. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using [`NaiveDateTime::checked_add_signed`] to get an `Option` instead. -/// -/// # Example -/// -/// ``` -/// use chrono::{NaiveDate, TimeDelta}; -/// -/// let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); -/// -/// let d = from_ymd(2016, 7, 8); -/// let hms = |h, m, s| d.and_hms_opt(h, m, s).unwrap(); -/// assert_eq!(hms(3, 5, 7) + TimeDelta::zero(), hms(3, 5, 7)); -/// assert_eq!(hms(3, 5, 7) + TimeDelta::try_seconds(1).unwrap(), hms(3, 5, 8)); -/// assert_eq!(hms(3, 5, 7) + TimeDelta::try_seconds(-1).unwrap(), hms(3, 5, 6)); -/// assert_eq!(hms(3, 5, 7) + TimeDelta::try_seconds(3600 + 60).unwrap(), hms(4, 6, 7)); -/// assert_eq!( -/// hms(3, 5, 7) + TimeDelta::try_seconds(86_400).unwrap(), -/// from_ymd(2016, 7, 9).and_hms_opt(3, 5, 7).unwrap() -/// ); -/// assert_eq!( -/// hms(3, 5, 7) + TimeDelta::try_days(365).unwrap(), -/// from_ymd(2017, 7, 8).and_hms_opt(3, 5, 7).unwrap() -/// ); -/// -/// let hmsm = |h, m, s, milli| d.and_hms_milli_opt(h, m, s, milli).unwrap(); -/// assert_eq!(hmsm(3, 5, 7, 980) + TimeDelta::try_milliseconds(450).unwrap(), hmsm(3, 5, 8, 430)); -/// ``` -/// -/// Leap seconds are handled, -/// but the addition assumes that it is the only leap second happened. -/// -/// ``` -/// # use chrono::{TimeDelta, NaiveDate}; -/// # let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); -/// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli_opt(h, m, s, milli).unwrap(); -/// let leap = hmsm(3, 5, 59, 1_300); -/// assert_eq!(leap + TimeDelta::zero(), hmsm(3, 5, 59, 1_300)); -/// assert_eq!(leap + TimeDelta::try_milliseconds(-500).unwrap(), hmsm(3, 5, 59, 800)); -/// assert_eq!(leap + TimeDelta::try_milliseconds(500).unwrap(), hmsm(3, 5, 59, 1_800)); -/// assert_eq!(leap + TimeDelta::try_milliseconds(800).unwrap(), hmsm(3, 6, 0, 100)); -/// assert_eq!(leap + TimeDelta::try_seconds(10).unwrap(), hmsm(3, 6, 9, 300)); -/// assert_eq!(leap + TimeDelta::try_seconds(-10).unwrap(), hmsm(3, 5, 50, 300)); -/// assert_eq!(leap + TimeDelta::try_days(1).unwrap(), -/// from_ymd(2016, 7, 9).and_hms_milli_opt(3, 5, 59, 300).unwrap()); -/// ``` -/// -/// [leap second handling]: crate::NaiveTime#leap-second-handling -impl Add for NaiveDateTime { - type Output = NaiveDateTime; - - #[inline] - #[track_caller] - fn add(self, rhs: TimeDelta) -> NaiveDateTime { - self.checked_add_signed(rhs).expect("`NaiveDateTime + TimeDelta` overflowed") - } -} - -/// Add `std::time::Duration` to `NaiveDateTime`. -/// -/// As a part of Chrono's [leap second handling], the addition assumes that **there is no leap -/// second ever**, except when the `NaiveDateTime` itself represents a leap second in which case -/// the assumption becomes that **there is exactly a single leap second ever**. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using [`NaiveDateTime::checked_add_signed`] to get an `Option` instead. -impl Add for NaiveDateTime { - type Output = NaiveDateTime; - - #[inline] - #[track_caller] - fn add(self, rhs: Duration) -> NaiveDateTime { - let rhs = TimeDelta::from_std(rhs) - .expect("overflow converting from core::time::Duration to TimeDelta"); - self.checked_add_signed(rhs).expect("`NaiveDateTime + TimeDelta` overflowed") - } -} - -/// Add-assign `TimeDelta` to `NaiveDateTime`. -/// -/// As a part of Chrono's [leap second handling], the addition assumes that **there is no leap -/// second ever**, except when the `NaiveDateTime` itself represents a leap second in which case -/// the assumption becomes that **there is exactly a single leap second ever**. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using [`NaiveDateTime::checked_add_signed`] to get an `Option` instead. -impl AddAssign for NaiveDateTime { - #[inline] - #[track_caller] - fn add_assign(&mut self, rhs: TimeDelta) { - *self = self.add(rhs); - } -} - -/// Add-assign `std::time::Duration` to `NaiveDateTime`. -/// -/// As a part of Chrono's [leap second handling], the addition assumes that **there is no leap -/// second ever**, except when the `NaiveDateTime` itself represents a leap second in which case -/// the assumption becomes that **there is exactly a single leap second ever**. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using [`NaiveDateTime::checked_add_signed`] to get an `Option` instead. -impl AddAssign for NaiveDateTime { - #[inline] - #[track_caller] - fn add_assign(&mut self, rhs: Duration) { - *self = self.add(rhs); - } -} - -/// Add `FixedOffset` to `NaiveDateTime`. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using `checked_add_offset` to get an `Option` instead. -impl Add for NaiveDateTime { - type Output = NaiveDateTime; - - #[inline] - #[track_caller] - fn add(self, rhs: FixedOffset) -> NaiveDateTime { - self.checked_add_offset(rhs).expect("`NaiveDateTime + FixedOffset` out of range") - } -} - -/// Add `Months` to `NaiveDateTime`. -/// -/// The result will be clamped to valid days in the resulting month, see `checked_add_months` for -/// details. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using `checked_add_months` to get an `Option` instead. -/// -/// # Example -/// -/// ``` -/// use chrono::{Months, NaiveDate}; -/// -/// assert_eq!( -/// NaiveDate::from_ymd_opt(2014, 1, 1).unwrap().and_hms_opt(1, 0, 0).unwrap() + Months::new(1), -/// NaiveDate::from_ymd_opt(2014, 2, 1).unwrap().and_hms_opt(1, 0, 0).unwrap() -/// ); -/// assert_eq!( -/// NaiveDate::from_ymd_opt(2014, 1, 1).unwrap().and_hms_opt(0, 2, 0).unwrap() -/// + Months::new(11), -/// NaiveDate::from_ymd_opt(2014, 12, 1).unwrap().and_hms_opt(0, 2, 0).unwrap() -/// ); -/// assert_eq!( -/// NaiveDate::from_ymd_opt(2014, 1, 1).unwrap().and_hms_opt(0, 0, 3).unwrap() -/// + Months::new(12), -/// NaiveDate::from_ymd_opt(2015, 1, 1).unwrap().and_hms_opt(0, 0, 3).unwrap() -/// ); -/// assert_eq!( -/// NaiveDate::from_ymd_opt(2014, 1, 1).unwrap().and_hms_opt(0, 0, 4).unwrap() -/// + Months::new(13), -/// NaiveDate::from_ymd_opt(2015, 2, 1).unwrap().and_hms_opt(0, 0, 4).unwrap() -/// ); -/// assert_eq!( -/// NaiveDate::from_ymd_opt(2014, 1, 31).unwrap().and_hms_opt(0, 5, 0).unwrap() -/// + Months::new(1), -/// NaiveDate::from_ymd_opt(2014, 2, 28).unwrap().and_hms_opt(0, 5, 0).unwrap() -/// ); -/// assert_eq!( -/// NaiveDate::from_ymd_opt(2020, 1, 31).unwrap().and_hms_opt(6, 0, 0).unwrap() -/// + Months::new(1), -/// NaiveDate::from_ymd_opt(2020, 2, 29).unwrap().and_hms_opt(6, 0, 0).unwrap() -/// ); -/// ``` -impl Add for NaiveDateTime { - type Output = NaiveDateTime; - - #[track_caller] - fn add(self, rhs: Months) -> Self::Output { - self.checked_add_months(rhs).expect("`NaiveDateTime + Months` out of range") - } -} - -/// Subtract `TimeDelta` from `NaiveDateTime`. -/// -/// This is the same as the addition with a negated `TimeDelta`. -/// -/// As a part of Chrono's [leap second handling] the subtraction assumes that **there is no leap -/// second ever**, except when the `NaiveDateTime` itself represents a leap second in which case -/// the assumption becomes that **there is exactly a single leap second ever**. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using [`NaiveDateTime::checked_sub_signed`] to get an `Option` instead. -/// -/// # Example -/// -/// ``` -/// use chrono::{NaiveDate, TimeDelta}; -/// -/// let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); -/// -/// let d = from_ymd(2016, 7, 8); -/// let hms = |h, m, s| d.and_hms_opt(h, m, s).unwrap(); -/// assert_eq!(hms(3, 5, 7) - TimeDelta::zero(), hms(3, 5, 7)); -/// assert_eq!(hms(3, 5, 7) - TimeDelta::try_seconds(1).unwrap(), hms(3, 5, 6)); -/// assert_eq!(hms(3, 5, 7) - TimeDelta::try_seconds(-1).unwrap(), hms(3, 5, 8)); -/// assert_eq!(hms(3, 5, 7) - TimeDelta::try_seconds(3600 + 60).unwrap(), hms(2, 4, 7)); -/// assert_eq!( -/// hms(3, 5, 7) - TimeDelta::try_seconds(86_400).unwrap(), -/// from_ymd(2016, 7, 7).and_hms_opt(3, 5, 7).unwrap() -/// ); -/// assert_eq!( -/// hms(3, 5, 7) - TimeDelta::try_days(365).unwrap(), -/// from_ymd(2015, 7, 9).and_hms_opt(3, 5, 7).unwrap() -/// ); -/// -/// let hmsm = |h, m, s, milli| d.and_hms_milli_opt(h, m, s, milli).unwrap(); -/// assert_eq!(hmsm(3, 5, 7, 450) - TimeDelta::try_milliseconds(670).unwrap(), hmsm(3, 5, 6, 780)); -/// ``` -/// -/// Leap seconds are handled, -/// but the subtraction assumes that it is the only leap second happened. -/// -/// ``` -/// # use chrono::{TimeDelta, NaiveDate}; -/// # let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); -/// # let hmsm = |h, m, s, milli| from_ymd(2016, 7, 8).and_hms_milli_opt(h, m, s, milli).unwrap(); -/// let leap = hmsm(3, 5, 59, 1_300); -/// assert_eq!(leap - TimeDelta::zero(), hmsm(3, 5, 59, 1_300)); -/// assert_eq!(leap - TimeDelta::try_milliseconds(200).unwrap(), hmsm(3, 5, 59, 1_100)); -/// assert_eq!(leap - TimeDelta::try_milliseconds(500).unwrap(), hmsm(3, 5, 59, 800)); -/// assert_eq!(leap - TimeDelta::try_seconds(60).unwrap(), hmsm(3, 5, 0, 300)); -/// assert_eq!(leap - TimeDelta::try_days(1).unwrap(), -/// from_ymd(2016, 7, 7).and_hms_milli_opt(3, 6, 0, 300).unwrap()); -/// ``` -/// -/// [leap second handling]: crate::NaiveTime#leap-second-handling -impl Sub for NaiveDateTime { - type Output = NaiveDateTime; - - #[inline] - #[track_caller] - fn sub(self, rhs: TimeDelta) -> NaiveDateTime { - self.checked_sub_signed(rhs).expect("`NaiveDateTime - TimeDelta` overflowed") - } -} - -/// Subtract `std::time::Duration` from `NaiveDateTime`. -/// -/// As a part of Chrono's [leap second handling] the subtraction assumes that **there is no leap -/// second ever**, except when the `NaiveDateTime` itself represents a leap second in which case -/// the assumption becomes that **there is exactly a single leap second ever**. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using [`NaiveDateTime::checked_sub_signed`] to get an `Option` instead. -impl Sub for NaiveDateTime { - type Output = NaiveDateTime; - - #[inline] - #[track_caller] - fn sub(self, rhs: Duration) -> NaiveDateTime { - let rhs = TimeDelta::from_std(rhs) - .expect("overflow converting from core::time::Duration to TimeDelta"); - self.checked_sub_signed(rhs).expect("`NaiveDateTime - TimeDelta` overflowed") - } -} - -/// Subtract-assign `TimeDelta` from `NaiveDateTime`. -/// -/// This is the same as the addition with a negated `TimeDelta`. -/// -/// As a part of Chrono's [leap second handling], the addition assumes that **there is no leap -/// second ever**, except when the `NaiveDateTime` itself represents a leap second in which case -/// the assumption becomes that **there is exactly a single leap second ever**. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using [`NaiveDateTime::checked_sub_signed`] to get an `Option` instead. -impl SubAssign for NaiveDateTime { - #[inline] - #[track_caller] - fn sub_assign(&mut self, rhs: TimeDelta) { - *self = self.sub(rhs); - } -} - -/// Subtract-assign `std::time::Duration` from `NaiveDateTime`. -/// -/// As a part of Chrono's [leap second handling], the addition assumes that **there is no leap -/// second ever**, except when the `NaiveDateTime` itself represents a leap second in which case -/// the assumption becomes that **there is exactly a single leap second ever**. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using [`NaiveDateTime::checked_sub_signed`] to get an `Option` instead. -impl SubAssign for NaiveDateTime { - #[inline] - #[track_caller] - fn sub_assign(&mut self, rhs: Duration) { - *self = self.sub(rhs); - } -} - -/// Subtract `FixedOffset` from `NaiveDateTime`. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using `checked_sub_offset` to get an `Option` instead. -impl Sub for NaiveDateTime { - type Output = NaiveDateTime; - - #[inline] - #[track_caller] - fn sub(self, rhs: FixedOffset) -> NaiveDateTime { - self.checked_sub_offset(rhs).expect("`NaiveDateTime - FixedOffset` out of range") - } -} - -/// Subtract `Months` from `NaiveDateTime`. -/// -/// The result will be clamped to valid days in the resulting month, see -/// [`NaiveDateTime::checked_sub_months`] for details. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using [`NaiveDateTime::checked_sub_months`] to get an `Option` instead. -/// -/// # Example -/// -/// ``` -/// use chrono::{Months, NaiveDate}; -/// -/// assert_eq!( -/// NaiveDate::from_ymd_opt(2014, 01, 01).unwrap().and_hms_opt(01, 00, 00).unwrap() -/// - Months::new(11), -/// NaiveDate::from_ymd_opt(2013, 02, 01).unwrap().and_hms_opt(01, 00, 00).unwrap() -/// ); -/// assert_eq!( -/// NaiveDate::from_ymd_opt(2014, 01, 01).unwrap().and_hms_opt(00, 02, 00).unwrap() -/// - Months::new(12), -/// NaiveDate::from_ymd_opt(2013, 01, 01).unwrap().and_hms_opt(00, 02, 00).unwrap() -/// ); -/// assert_eq!( -/// NaiveDate::from_ymd_opt(2014, 01, 01).unwrap().and_hms_opt(00, 00, 03).unwrap() -/// - Months::new(13), -/// NaiveDate::from_ymd_opt(2012, 12, 01).unwrap().and_hms_opt(00, 00, 03).unwrap() -/// ); -/// ``` -impl Sub for NaiveDateTime { - type Output = NaiveDateTime; - - #[track_caller] - fn sub(self, rhs: Months) -> Self::Output { - self.checked_sub_months(rhs).expect("`NaiveDateTime - Months` out of range") - } -} - -/// Subtracts another `NaiveDateTime` from the current date and time. -/// This does not overflow or underflow at all. -/// -/// As a part of Chrono's [leap second handling](./struct.NaiveTime.html#leap-second-handling), -/// the subtraction assumes that **there is no leap second ever**, -/// except when any of the `NaiveDateTime`s themselves represents a leap second -/// in which case the assumption becomes that -/// **there are exactly one (or two) leap second(s) ever**. -/// -/// The implementation is a wrapper around [`NaiveDateTime::signed_duration_since`]. -/// -/// # Example -/// -/// ``` -/// use chrono::{NaiveDate, TimeDelta}; -/// -/// let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); -/// -/// let d = from_ymd(2016, 7, 8); -/// assert_eq!( -/// d.and_hms_opt(3, 5, 7).unwrap() - d.and_hms_opt(2, 4, 6).unwrap(), -/// TimeDelta::try_seconds(3600 + 60 + 1).unwrap() -/// ); -/// -/// // July 8 is 190th day in the year 2016 -/// let d0 = from_ymd(2016, 1, 1); -/// assert_eq!( -/// d.and_hms_milli_opt(0, 7, 6, 500).unwrap() - d0.and_hms_opt(0, 0, 0).unwrap(), -/// TimeDelta::try_seconds(189 * 86_400 + 7 * 60 + 6).unwrap() -/// + TimeDelta::try_milliseconds(500).unwrap() -/// ); -/// ``` -/// -/// Leap seconds are handled, but the subtraction assumes that no other leap -/// seconds happened. -/// -/// ``` -/// # use chrono::{TimeDelta, NaiveDate}; -/// # let from_ymd = |y, m, d| NaiveDate::from_ymd_opt(y, m, d).unwrap(); -/// let leap = from_ymd(2015, 6, 30).and_hms_milli_opt(23, 59, 59, 1_500).unwrap(); -/// assert_eq!( -/// leap - from_ymd(2015, 6, 30).and_hms_opt(23, 0, 0).unwrap(), -/// TimeDelta::try_seconds(3600).unwrap() + TimeDelta::try_milliseconds(500).unwrap() -/// ); -/// assert_eq!( -/// from_ymd(2015, 7, 1).and_hms_opt(1, 0, 0).unwrap() - leap, -/// TimeDelta::try_seconds(3600).unwrap() - TimeDelta::try_milliseconds(500).unwrap() -/// ); -/// ``` -impl Sub for NaiveDateTime { - type Output = TimeDelta; - - #[inline] - fn sub(self, rhs: NaiveDateTime) -> TimeDelta { - self.signed_duration_since(rhs) - } -} - -/// Add `Days` to `NaiveDateTime`. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using `checked_add_days` to get an `Option` instead. -impl Add for NaiveDateTime { - type Output = NaiveDateTime; - - #[track_caller] - fn add(self, days: Days) -> Self::Output { - self.checked_add_days(days).expect("`NaiveDateTime + Days` out of range") - } -} - -/// Subtract `Days` from `NaiveDateTime`. -/// -/// # Panics -/// -/// Panics if the resulting date would be out of range. -/// Consider using `checked_sub_days` to get an `Option` instead. -impl Sub for NaiveDateTime { - type Output = NaiveDateTime; - - #[track_caller] - fn sub(self, days: Days) -> Self::Output { - self.checked_sub_days(days).expect("`NaiveDateTime - Days` out of range") - } -} - -/// The `Debug` output of the naive date and time `dt` is the same as -/// [`dt.format("%Y-%m-%dT%H:%M:%S%.f")`](crate::format::strftime). -/// -/// The string printed can be readily parsed via the `parse` method on `str`. -/// -/// It should be noted that, for leap seconds not on the minute boundary, -/// it may print a representation not distinguishable from non-leap seconds. -/// This doesn't matter in practice, since such leap seconds never happened. -/// (By the time of the first leap second on 1972-06-30, -/// every time zone offset around the world has standardized to the 5-minute alignment.) -/// -/// # Example -/// -/// ``` -/// use chrono::NaiveDate; -/// -/// let dt = NaiveDate::from_ymd_opt(2016, 11, 15).unwrap().and_hms_opt(7, 39, 24).unwrap(); -/// assert_eq!(format!("{:?}", dt), "2016-11-15T07:39:24"); -/// ``` -/// -/// Leap seconds may also be used. -/// -/// ``` -/// # use chrono::NaiveDate; -/// let dt = -/// NaiveDate::from_ymd_opt(2015, 6, 30).unwrap().and_hms_milli_opt(23, 59, 59, 1_500).unwrap(); -/// assert_eq!(format!("{:?}", dt), "2015-06-30T23:59:60.500"); -/// ``` -impl fmt::Debug for NaiveDateTime { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.date.fmt(f)?; - f.write_char('T')?; - self.time.fmt(f) - } -} - -#[cfg(feature = "defmt")] -impl defmt::Format for NaiveDateTime { - fn format(&self, fmt: defmt::Formatter) { - defmt::write!(fmt, "{}T{}", self.date, self.time); - } -} - -/// The `Display` output of the naive date and time `dt` is the same as -/// [`dt.format("%Y-%m-%d %H:%M:%S%.f")`](crate::format::strftime). -/// -/// It should be noted that, for leap seconds not on the minute boundary, -/// it may print a representation not distinguishable from non-leap seconds. -/// This doesn't matter in practice, since such leap seconds never happened. -/// (By the time of the first leap second on 1972-06-30, -/// every time zone offset around the world has standardized to the 5-minute alignment.) -/// -/// # Example -/// -/// ``` -/// use chrono::NaiveDate; -/// -/// let dt = NaiveDate::from_ymd_opt(2016, 11, 15).unwrap().and_hms_opt(7, 39, 24).unwrap(); -/// assert_eq!(format!("{}", dt), "2016-11-15 07:39:24"); -/// ``` -/// -/// Leap seconds may also be used. -/// -/// ``` -/// # use chrono::NaiveDate; -/// let dt = -/// NaiveDate::from_ymd_opt(2015, 6, 30).unwrap().and_hms_milli_opt(23, 59, 59, 1_500).unwrap(); -/// assert_eq!(format!("{}", dt), "2015-06-30 23:59:60.500"); -/// ``` -impl fmt::Display for NaiveDateTime { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.date.fmt(f)?; - f.write_char(' ')?; - self.time.fmt(f) - } -} - -/// Parsing a `str` into a `NaiveDateTime` uses the same format, -/// [`%Y-%m-%dT%H:%M:%S%.f`](crate::format::strftime), as in `Debug`. -/// -/// # Example -/// -/// ``` -/// use chrono::{NaiveDateTime, NaiveDate}; -/// -/// let dt = NaiveDate::from_ymd_opt(2015, 9, 18).unwrap().and_hms_opt(23, 56, 4).unwrap(); -/// assert_eq!("2015-09-18T23:56:04".parse::(), Ok(dt)); -/// -/// let dt = NaiveDate::from_ymd_opt(12345, 6, 7).unwrap().and_hms_milli_opt(7, 59, 59, 1_500).unwrap(); // leap second -/// assert_eq!("+12345-6-7T7:59:60.5".parse::(), Ok(dt)); -/// -/// assert!("foo".parse::().is_err()); -/// ``` -impl str::FromStr for NaiveDateTime { - type Err = ParseError; - - fn from_str(s: &str) -> ParseResult { - const ITEMS: &[Item<'static>] = &[ - Item::Numeric(Numeric::Year, Pad::Zero), - Item::Space(""), - Item::Literal("-"), - Item::Numeric(Numeric::Month, Pad::Zero), - Item::Space(""), - Item::Literal("-"), - Item::Numeric(Numeric::Day, Pad::Zero), - Item::Space(""), - Item::Literal("T"), // XXX shouldn't this be case-insensitive? - Item::Numeric(Numeric::Hour, Pad::Zero), - Item::Space(""), - Item::Literal(":"), - Item::Numeric(Numeric::Minute, Pad::Zero), - Item::Space(""), - Item::Literal(":"), - Item::Numeric(Numeric::Second, Pad::Zero), - Item::Fixed(Fixed::Nanosecond), - Item::Space(""), - ]; - - let mut parsed = Parsed::new(); - parse(&mut parsed, s, ITEMS.iter())?; - parsed.to_naive_datetime_with_offset(0) - } -} - -/// The default value for a NaiveDateTime is 1st of January 1970 at 00:00:00. -/// -/// Note that while this may look like the UNIX epoch, it is missing the -/// time zone. The actual UNIX epoch cannot be expressed by this type, -/// however it is available as [`DateTime::UNIX_EPOCH`]. -impl Default for NaiveDateTime { - fn default() -> Self { - DateTime::UNIX_EPOCH.naive_local() - } -} diff --git a/chrono-0.4.44/src/naive/datetime/serde.rs b/chrono-0.4.44/src/naive/datetime/serde.rs deleted file mode 100644 index 6ebdefaee5..0000000000 --- a/chrono-0.4.44/src/naive/datetime/serde.rs +++ /dev/null @@ -1,1304 +0,0 @@ -use core::fmt; -use serde::{de, ser}; - -use super::NaiveDateTime; - -/// Serialize a `NaiveDateTime` as an ISO 8601 string -/// -/// See [the `naive::serde` module](crate::naive::serde) for alternate serialization formats. -impl ser::Serialize for NaiveDateTime { - fn serialize(&self, serializer: S) -> Result - where - S: ser::Serializer, - { - struct FormatWrapped<'a, D: 'a> { - inner: &'a D, - } - - impl fmt::Display for FormatWrapped<'_, D> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.inner.fmt(f) - } - } - - serializer.collect_str(&FormatWrapped { inner: &self }) - } -} - -struct NaiveDateTimeVisitor; - -impl de::Visitor<'_> for NaiveDateTimeVisitor { - type Value = NaiveDateTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a formatted date and time string") - } - - fn visit_str(self, value: &str) -> Result - where - E: de::Error, - { - value.parse().map_err(E::custom) - } -} - -impl<'de> de::Deserialize<'de> for NaiveDateTime { - fn deserialize(deserializer: D) -> Result - where - D: de::Deserializer<'de>, - { - deserializer.deserialize_str(NaiveDateTimeVisitor) - } -} - -/// Used to serialize/deserialize from nanosecond-precision timestamps -/// -/// # Example: -/// -/// ```rust -/// # use chrono::{NaiveDate, NaiveDateTime}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::naive::serde::ts_nanoseconds; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_nanoseconds")] -/// time: NaiveDateTime, -/// } -/// -/// let time = NaiveDate::from_ymd_opt(2018, 5, 17) -/// .unwrap() -/// .and_hms_nano_opt(02, 04, 59, 918355733) -/// .unwrap(); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_nanoseconds { - use core::fmt; - use serde::{de, ser}; - - use crate::serde::invalid_ts; - use crate::{DateTime, NaiveDateTime}; - - /// Serialize a datetime into an integer number of nanoseconds since the epoch - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Errors - /// - /// An `i64` with nanosecond precision can span a range of ~584 years. This function returns an - /// error on an out of range `DateTime`. - /// - /// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:44.0 and - /// 2262-04-11T23:47:16.854775804. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{NaiveDate, NaiveDateTime}; - /// # use serde_derive::Serialize; - /// use chrono::naive::serde::ts_nanoseconds::serialize as to_nano_ts; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_nano_ts")] - /// time: NaiveDateTime, - /// } - /// - /// let my_s = S { - /// time: NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_nano_opt(02, 04, 59, 918355733) - /// .unwrap(), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(dt: &NaiveDateTime, serializer: S) -> Result - where - S: ser::Serializer, - { - serializer.serialize_i64(dt.and_utc().timestamp_nanos_opt().ok_or(ser::Error::custom( - "value out of range for a timestamp with nanosecond precision", - ))?) - } - - /// Deserialize a `NaiveDateTime` from a nanoseconds timestamp - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, NaiveDateTime}; - /// # use serde_derive::Deserialize; - /// use chrono::naive::serde::ts_nanoseconds::deserialize as from_nano_ts; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_nano_ts")] - /// time: NaiveDateTime, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?; - /// let expected = DateTime::from_timestamp(1526522699, 918355733).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: expected }); - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// let expected = DateTime::from_timestamp(-1, 999_999_999).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: expected }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(NanoSecondsTimestampVisitor) - } - - pub(super) struct NanoSecondsTimestampVisitor; - - impl de::Visitor<'_> for NanoSecondsTimestampVisitor { - type Value = NaiveDateTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp") - } - - fn visit_i64(self, value: i64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp( - value.div_euclid(1_000_000_000), - (value.rem_euclid(1_000_000_000)) as u32, - ) - .map(|dt| dt.naive_utc()) - .ok_or_else(|| invalid_ts(value)) - } - - fn visit_u64(self, value: u64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp((value / 1_000_000_000) as i64, (value % 1_000_000_000) as u32) - .map(|dt| dt.naive_utc()) - .ok_or_else(|| invalid_ts(value)) - } - } -} - -/// Ser/de to/from optional timestamps in nanoseconds -/// -/// Intended for use with `serde`'s `with` attribute. -/// -/// # Example: -/// -/// ```rust -/// # use chrono::naive::{NaiveDate, NaiveDateTime}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::naive::serde::ts_nanoseconds_option; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_nanoseconds_option")] -/// time: Option, -/// } -/// -/// let time = Some( -/// NaiveDate::from_ymd_opt(2018, 5, 17) -/// .unwrap() -/// .and_hms_nano_opt(02, 04, 59, 918355733) -/// .unwrap(), -/// ); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_nanoseconds_option { - use core::fmt; - use serde::{de, ser}; - - use super::ts_nanoseconds::NanoSecondsTimestampVisitor; - use crate::NaiveDateTime; - - /// Serialize a datetime into an integer number of nanoseconds since the epoch or none - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Errors - /// - /// An `i64` with nanosecond precision can span a range of ~584 years. This function returns an - /// error on an out of range `DateTime`. - /// - /// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:44.0 and - /// 2262-04-11T23:47:16.854775804. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::naive::{NaiveDate, NaiveDateTime}; - /// # use serde_derive::Serialize; - /// use chrono::naive::serde::ts_nanoseconds_option::serialize as to_nano_tsopt; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_nano_tsopt")] - /// time: Option, - /// } - /// - /// let my_s = S { - /// time: Some( - /// NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_nano_opt(02, 04, 59, 918355733) - /// .unwrap(), - /// ), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(opt: &Option, serializer: S) -> Result - where - S: ser::Serializer, - { - match *opt { - Some(ref dt) => serializer.serialize_some(&dt.and_utc().timestamp_nanos_opt().ok_or( - ser::Error::custom("value out of range for a timestamp with nanosecond precision"), - )?), - None => serializer.serialize_none(), - } - } - - /// Deserialize a `NaiveDateTime` from a nanosecond timestamp or none - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, NaiveDateTime}; - /// # use serde_derive::Deserialize; - /// use chrono::naive::serde::ts_nanoseconds_option::deserialize as from_nano_tsopt; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_nano_tsopt")] - /// time: Option, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?; - /// let expected = DateTime::from_timestamp(1526522699, 918355733).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: Some(expected) }); - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// let expected = DateTime::from_timestamp(-1, 999_999_999).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: Some(expected) }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_option(OptionNanoSecondsTimestampVisitor) - } - - struct OptionNanoSecondsTimestampVisitor; - - impl<'de> de::Visitor<'de> for OptionNanoSecondsTimestampVisitor { - type Value = Option; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in nanoseconds or none") - } - - /// Deserialize a timestamp in nanoseconds since the epoch - fn visit_some(self, d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(NanoSecondsTimestampVisitor).map(Some) - } - - /// Deserialize a timestamp in nanoseconds since the epoch - fn visit_none(self) -> Result - where - E: de::Error, - { - Ok(None) - } - - /// Deserialize a timestamp in nanoseconds since the epoch - fn visit_unit(self) -> Result - where - E: de::Error, - { - Ok(None) - } - } -} - -/// Used to serialize/deserialize from microsecond-precision timestamps -/// -/// # Example: -/// -/// ```rust -/// # use chrono::{NaiveDate, NaiveDateTime}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::naive::serde::ts_microseconds; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_microseconds")] -/// time: NaiveDateTime, -/// } -/// -/// let time = NaiveDate::from_ymd_opt(2018, 5, 17) -/// .unwrap() -/// .and_hms_micro_opt(02, 04, 59, 918355) -/// .unwrap(); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918355}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_microseconds { - use core::fmt; - use serde::{de, ser}; - - use crate::serde::invalid_ts; - use crate::{DateTime, NaiveDateTime}; - - /// Serialize a datetime into an integer number of microseconds since the epoch - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{NaiveDate, NaiveDateTime}; - /// # use serde_derive::Serialize; - /// use chrono::naive::serde::ts_microseconds::serialize as to_micro_ts; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_micro_ts")] - /// time: NaiveDateTime, - /// } - /// - /// let my_s = S { - /// time: NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_micro_opt(02, 04, 59, 918355) - /// .unwrap(), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918355}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(dt: &NaiveDateTime, serializer: S) -> Result - where - S: ser::Serializer, - { - serializer.serialize_i64(dt.and_utc().timestamp_micros()) - } - - /// Deserialize a `NaiveDateTime` from a microseconds timestamp - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, NaiveDateTime}; - /// # use serde_derive::Deserialize; - /// use chrono::naive::serde::ts_microseconds::deserialize as from_micro_ts; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_micro_ts")] - /// time: NaiveDateTime, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355 }"#)?; - /// let expected = DateTime::from_timestamp(1526522699, 918355000).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: expected }); - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// let expected = DateTime::from_timestamp(-1, 999_999_000).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: expected }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(MicroSecondsTimestampVisitor) - } - - pub(super) struct MicroSecondsTimestampVisitor; - - impl de::Visitor<'_> for MicroSecondsTimestampVisitor { - type Value = NaiveDateTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp") - } - - fn visit_i64(self, value: i64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp_micros(value) - .map(|dt| dt.naive_utc()) - .ok_or_else(|| invalid_ts(value)) - } - - fn visit_u64(self, value: u64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp( - (value / 1_000_000) as i64, - ((value % 1_000_000) * 1_000) as u32, - ) - .map(|dt| dt.naive_utc()) - .ok_or_else(|| invalid_ts(value)) - } - } -} - -/// Ser/de to/from optional timestamps in microseconds -/// -/// Intended for use with `serde`'s `with` attribute. -/// -/// # Example: -/// -/// ```rust -/// # use chrono::naive::{NaiveDate, NaiveDateTime}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::naive::serde::ts_microseconds_option; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_microseconds_option")] -/// time: Option, -/// } -/// -/// let time = Some( -/// NaiveDate::from_ymd_opt(2018, 5, 17) -/// .unwrap() -/// .and_hms_micro_opt(02, 04, 59, 918355) -/// .unwrap(), -/// ); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918355}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_microseconds_option { - use core::fmt; - use serde::{de, ser}; - - use super::ts_microseconds::MicroSecondsTimestampVisitor; - use crate::NaiveDateTime; - - /// Serialize a datetime into an integer number of microseconds since the epoch or none - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::naive::{NaiveDate, NaiveDateTime}; - /// # use serde_derive::Serialize; - /// use chrono::naive::serde::ts_microseconds_option::serialize as to_micro_tsopt; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_micro_tsopt")] - /// time: Option, - /// } - /// - /// let my_s = S { - /// time: Some( - /// NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_micro_opt(02, 04, 59, 918355) - /// .unwrap(), - /// ), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918355}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(opt: &Option, serializer: S) -> Result - where - S: ser::Serializer, - { - match *opt { - Some(ref dt) => serializer.serialize_some(&dt.and_utc().timestamp_micros()), - None => serializer.serialize_none(), - } - } - - /// Deserialize a `NaiveDateTime` from a nanosecond timestamp or none - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, NaiveDateTime}; - /// # use serde_derive::Deserialize; - /// use chrono::naive::serde::ts_microseconds_option::deserialize as from_micro_tsopt; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_micro_tsopt")] - /// time: Option, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355 }"#)?; - /// let expected = DateTime::from_timestamp(1526522699, 918355000).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: Some(expected) }); - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// let expected = DateTime::from_timestamp(-1, 999_999_000).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: Some(expected) }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_option(OptionMicroSecondsTimestampVisitor) - } - - struct OptionMicroSecondsTimestampVisitor; - - impl<'de> de::Visitor<'de> for OptionMicroSecondsTimestampVisitor { - type Value = Option; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in microseconds or none") - } - - /// Deserialize a timestamp in microseconds since the epoch - fn visit_some(self, d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(MicroSecondsTimestampVisitor).map(Some) - } - - /// Deserialize a timestamp in microseconds since the epoch - fn visit_none(self) -> Result - where - E: de::Error, - { - Ok(None) - } - - /// Deserialize a timestamp in microseconds since the epoch - fn visit_unit(self) -> Result - where - E: de::Error, - { - Ok(None) - } - } -} - -/// Used to serialize/deserialize from millisecond-precision timestamps -/// -/// # Example: -/// -/// ```rust -/// # use chrono::{NaiveDate, NaiveDateTime}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::naive::serde::ts_milliseconds; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_milliseconds")] -/// time: NaiveDateTime, -/// } -/// -/// let time = -/// NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_milli_opt(02, 04, 59, 918).unwrap(); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_milliseconds { - use core::fmt; - use serde::{de, ser}; - - use crate::serde::invalid_ts; - use crate::{DateTime, NaiveDateTime}; - - /// Serialize a datetime into an integer number of milliseconds since the epoch - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{NaiveDate, NaiveDateTime}; - /// # use serde_derive::Serialize; - /// use chrono::naive::serde::ts_milliseconds::serialize as to_milli_ts; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_milli_ts")] - /// time: NaiveDateTime, - /// } - /// - /// let my_s = S { - /// time: NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_milli_opt(02, 04, 59, 918) - /// .unwrap(), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(dt: &NaiveDateTime, serializer: S) -> Result - where - S: ser::Serializer, - { - serializer.serialize_i64(dt.and_utc().timestamp_millis()) - } - - /// Deserialize a `NaiveDateTime` from a milliseconds timestamp - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, NaiveDateTime}; - /// # use serde_derive::Deserialize; - /// use chrono::naive::serde::ts_milliseconds::deserialize as from_milli_ts; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_milli_ts")] - /// time: NaiveDateTime, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918 }"#)?; - /// let expected = DateTime::from_timestamp(1526522699, 918000000).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: expected }); - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// let expected = DateTime::from_timestamp(-1, 999_000_000).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: expected }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(MilliSecondsTimestampVisitor) - } - - pub(super) struct MilliSecondsTimestampVisitor; - - impl de::Visitor<'_> for MilliSecondsTimestampVisitor { - type Value = NaiveDateTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp") - } - - fn visit_i64(self, value: i64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp_millis(value) - .map(|dt| dt.naive_utc()) - .ok_or_else(|| invalid_ts(value)) - } - - fn visit_u64(self, value: u64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp((value / 1000) as i64, ((value % 1000) * 1_000_000) as u32) - .map(|dt| dt.naive_utc()) - .ok_or_else(|| invalid_ts(value)) - } - } -} - -/// Ser/de to/from optional timestamps in milliseconds -/// -/// Intended for use with `serde`'s `with` attribute. -/// -/// # Example: -/// -/// ```rust -/// # use chrono::naive::{NaiveDate, NaiveDateTime}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::naive::serde::ts_milliseconds_option; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_milliseconds_option")] -/// time: Option, -/// } -/// -/// let time = Some( -/// NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_milli_opt(02, 04, 59, 918).unwrap(), -/// ); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699918}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_milliseconds_option { - use core::fmt; - use serde::{de, ser}; - - use super::ts_milliseconds::MilliSecondsTimestampVisitor; - use crate::NaiveDateTime; - - /// Serialize a datetime into an integer number of milliseconds since the epoch or none - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::naive::{NaiveDate, NaiveDateTime}; - /// # use serde_derive::Serialize; - /// use chrono::naive::serde::ts_milliseconds_option::serialize as to_milli_tsopt; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_milli_tsopt")] - /// time: Option, - /// } - /// - /// let my_s = S { - /// time: Some( - /// NaiveDate::from_ymd_opt(2018, 5, 17) - /// .unwrap() - /// .and_hms_milli_opt(02, 04, 59, 918) - /// .unwrap(), - /// ), - /// }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699918}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(opt: &Option, serializer: S) -> Result - where - S: ser::Serializer, - { - match *opt { - Some(ref dt) => serializer.serialize_some(&dt.and_utc().timestamp_millis()), - None => serializer.serialize_none(), - } - } - - /// Deserialize a `NaiveDateTime` from a millisecond timestamp or none - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, NaiveDateTime}; - /// # use serde_derive::Deserialize; - /// use chrono::naive::serde::ts_milliseconds_option::deserialize as from_milli_tsopt; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_milli_tsopt")] - /// time: Option, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918 }"#)?; - /// let expected = DateTime::from_timestamp(1526522699, 918000000).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: Some(expected) }); - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": -1 }"#)?; - /// let expected = DateTime::from_timestamp(-1, 999_000_000).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: Some(expected) }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_option(OptionMilliSecondsTimestampVisitor) - } - - struct OptionMilliSecondsTimestampVisitor; - - impl<'de> de::Visitor<'de> for OptionMilliSecondsTimestampVisitor { - type Value = Option; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in milliseconds or none") - } - - /// Deserialize a timestamp in milliseconds since the epoch - fn visit_some(self, d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(MilliSecondsTimestampVisitor).map(Some) - } - - /// Deserialize a timestamp in milliseconds since the epoch - fn visit_none(self) -> Result - where - E: de::Error, - { - Ok(None) - } - - /// Deserialize a timestamp in milliseconds since the epoch - fn visit_unit(self) -> Result - where - E: de::Error, - { - Ok(None) - } - } -} - -/// Used to serialize/deserialize from second-precision timestamps -/// -/// # Example: -/// -/// ```rust -/// # use chrono::{NaiveDate, NaiveDateTime}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::naive::serde::ts_seconds; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_seconds")] -/// time: NaiveDateTime, -/// } -/// -/// let time = NaiveDate::from_ymd_opt(2015, 5, 15).unwrap().and_hms_opt(10, 0, 0).unwrap(); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1431684000}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_seconds { - use core::fmt; - use serde::{de, ser}; - - use crate::serde::invalid_ts; - use crate::{DateTime, NaiveDateTime}; - - /// Serialize a datetime into an integer number of seconds since the epoch - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{NaiveDate, NaiveDateTime}; - /// # use serde_derive::Serialize; - /// use chrono::naive::serde::ts_seconds::serialize as to_ts; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_ts")] - /// time: NaiveDateTime, - /// } - /// - /// let my_s = - /// S { time: NaiveDate::from_ymd_opt(2015, 5, 15).unwrap().and_hms_opt(10, 0, 0).unwrap() }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1431684000}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(dt: &NaiveDateTime, serializer: S) -> Result - where - S: ser::Serializer, - { - serializer.serialize_i64(dt.and_utc().timestamp()) - } - - /// Deserialize a `NaiveDateTime` from a seconds timestamp - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, NaiveDateTime}; - /// # use serde_derive::Deserialize; - /// use chrono::naive::serde::ts_seconds::deserialize as from_ts; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_ts")] - /// time: NaiveDateTime, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?; - /// let expected = DateTime::from_timestamp_secs(1431684000).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: expected }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(SecondsTimestampVisitor) - } - - pub(super) struct SecondsTimestampVisitor; - - impl de::Visitor<'_> for SecondsTimestampVisitor { - type Value = NaiveDateTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp") - } - - fn visit_i64(self, value: i64) -> Result - where - E: de::Error, - { - DateTime::from_timestamp_secs(value) - .map(|dt| dt.naive_utc()) - .ok_or_else(|| invalid_ts(value)) - } - - fn visit_u64(self, value: u64) -> Result - where - E: de::Error, - { - if value > i64::MAX as u64 { - Err(invalid_ts(value)) - } else { - DateTime::from_timestamp_secs(value as i64) - .map(|dt| dt.naive_utc()) - .ok_or_else(|| invalid_ts(value)) - } - } - } -} - -/// Ser/de to/from optional timestamps in seconds -/// -/// Intended for use with `serde`'s `with` attribute. -/// -/// # Example: -/// -/// ```rust -/// # use chrono::naive::{NaiveDate, NaiveDateTime}; -/// # use serde_derive::{Deserialize, Serialize}; -/// use chrono::naive::serde::ts_seconds_option; -/// #[derive(Deserialize, Serialize)] -/// struct S { -/// #[serde(with = "ts_seconds_option")] -/// time: Option, -/// } -/// -/// let time = Some(NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_opt(02, 04, 59).unwrap()); -/// let my_s = S { time: time.clone() }; -/// -/// let as_string = serde_json::to_string(&my_s)?; -/// assert_eq!(as_string, r#"{"time":1526522699}"#); -/// let my_s: S = serde_json::from_str(&as_string)?; -/// assert_eq!(my_s.time, time); -/// # Ok::<(), serde_json::Error>(()) -/// ``` -pub mod ts_seconds_option { - use core::fmt; - use serde::{de, ser}; - - use super::ts_seconds::SecondsTimestampVisitor; - use crate::NaiveDateTime; - - /// Serialize a datetime into an integer number of seconds since the epoch or none - /// - /// Intended for use with `serde`s `serialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::naive::{NaiveDate, NaiveDateTime}; - /// # use serde_derive::Serialize; - /// use chrono::naive::serde::ts_seconds_option::serialize as to_tsopt; - /// #[derive(Serialize)] - /// struct S { - /// #[serde(serialize_with = "to_tsopt")] - /// time: Option, - /// } - /// - /// let expected = NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_opt(02, 04, 59).unwrap(); - /// let my_s = S { time: Some(expected) }; - /// let as_string = serde_json::to_string(&my_s)?; - /// assert_eq!(as_string, r#"{"time":1526522699}"#); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn serialize(opt: &Option, serializer: S) -> Result - where - S: ser::Serializer, - { - match *opt { - Some(ref dt) => serializer.serialize_some(&dt.and_utc().timestamp()), - None => serializer.serialize_none(), - } - } - - /// Deserialize a `NaiveDateTime` from a second timestamp or none - /// - /// Intended for use with `serde`s `deserialize_with` attribute. - /// - /// # Example: - /// - /// ```rust - /// # use chrono::{DateTime, NaiveDateTime}; - /// # use serde_derive::Deserialize; - /// use chrono::naive::serde::ts_seconds_option::deserialize as from_tsopt; - /// #[derive(Debug, PartialEq, Deserialize)] - /// struct S { - /// #[serde(deserialize_with = "from_tsopt")] - /// time: Option, - /// } - /// - /// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?; - /// let expected = DateTime::from_timestamp_secs(1431684000).unwrap().naive_utc(); - /// assert_eq!(my_s, S { time: Some(expected) }); - /// # Ok::<(), serde_json::Error>(()) - /// ``` - pub fn deserialize<'de, D>(d: D) -> Result, D::Error> - where - D: de::Deserializer<'de>, - { - d.deserialize_option(OptionSecondsTimestampVisitor) - } - - struct OptionSecondsTimestampVisitor; - - impl<'de> de::Visitor<'de> for OptionSecondsTimestampVisitor { - type Value = Option; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a unix timestamp in seconds or none") - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_some(self, d: D) -> Result - where - D: de::Deserializer<'de>, - { - d.deserialize_i64(SecondsTimestampVisitor).map(Some) - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_none(self) -> Result - where - E: de::Error, - { - Ok(None) - } - - /// Deserialize a timestamp in seconds since the epoch - fn visit_unit(self) -> Result - where - E: de::Error, - { - Ok(None) - } - } -} - -#[cfg(test)] -mod tests { - use crate::serde::ts_nanoseconds_option; - use crate::{DateTime, NaiveDate, NaiveDateTime, TimeZone, Utc}; - - use bincode::{deserialize, serialize}; - use serde_derive::{Deserialize, Serialize}; - - #[test] - fn test_serde_serialize() { - assert_eq!( - serde_json::to_string( - &NaiveDate::from_ymd_opt(2016, 7, 8) - .unwrap() - .and_hms_milli_opt(9, 10, 48, 90) - .unwrap() - ) - .ok(), - Some(r#""2016-07-08T09:10:48.090""#.into()) - ); - assert_eq!( - serde_json::to_string( - &NaiveDate::from_ymd_opt(2014, 7, 24).unwrap().and_hms_opt(12, 34, 6).unwrap() - ) - .ok(), - Some(r#""2014-07-24T12:34:06""#.into()) - ); - assert_eq!( - serde_json::to_string( - &NaiveDate::from_ymd_opt(0, 1, 1) - .unwrap() - .and_hms_milli_opt(0, 0, 59, 1_000) - .unwrap() - ) - .ok(), - Some(r#""0000-01-01T00:00:60""#.into()) - ); - assert_eq!( - serde_json::to_string( - &NaiveDate::from_ymd_opt(-1, 12, 31) - .unwrap() - .and_hms_nano_opt(23, 59, 59, 7) - .unwrap() - ) - .ok(), - Some(r#""-0001-12-31T23:59:59.000000007""#.into()) - ); - assert_eq!( - serde_json::to_string(&NaiveDate::MIN.and_hms_opt(0, 0, 0).unwrap()).ok(), - Some(r#""-262143-01-01T00:00:00""#.into()) - ); - assert_eq!( - serde_json::to_string( - &NaiveDate::MAX.and_hms_nano_opt(23, 59, 59, 1_999_999_999).unwrap() - ) - .ok(), - Some(r#""+262142-12-31T23:59:60.999999999""#.into()) - ); - } - - #[test] - fn test_serde_deserialize() { - let from_str = serde_json::from_str::; - - assert_eq!( - from_str(r#""2016-07-08T09:10:48.090""#).ok(), - Some( - NaiveDate::from_ymd_opt(2016, 7, 8) - .unwrap() - .and_hms_milli_opt(9, 10, 48, 90) - .unwrap() - ) - ); - assert_eq!( - from_str(r#""2016-7-8T9:10:48.09""#).ok(), - Some( - NaiveDate::from_ymd_opt(2016, 7, 8) - .unwrap() - .and_hms_milli_opt(9, 10, 48, 90) - .unwrap() - ) - ); - assert_eq!( - from_str(r#""2014-07-24T12:34:06""#).ok(), - Some(NaiveDate::from_ymd_opt(2014, 7, 24).unwrap().and_hms_opt(12, 34, 6).unwrap()) - ); - assert_eq!( - from_str(r#""0000-01-01T00:00:60""#).ok(), - Some( - NaiveDate::from_ymd_opt(0, 1, 1) - .unwrap() - .and_hms_milli_opt(0, 0, 59, 1_000) - .unwrap() - ) - ); - assert_eq!( - from_str(r#""0-1-1T0:0:60""#).ok(), - Some( - NaiveDate::from_ymd_opt(0, 1, 1) - .unwrap() - .and_hms_milli_opt(0, 0, 59, 1_000) - .unwrap() - ) - ); - assert_eq!( - from_str(r#""-0001-12-31T23:59:59.000000007""#).ok(), - Some( - NaiveDate::from_ymd_opt(-1, 12, 31) - .unwrap() - .and_hms_nano_opt(23, 59, 59, 7) - .unwrap() - ) - ); - assert_eq!( - from_str(r#""-262143-01-01T00:00:00""#).ok(), - Some(NaiveDate::MIN.and_hms_opt(0, 0, 0).unwrap()) - ); - assert_eq!( - from_str(r#""+262142-12-31T23:59:60.999999999""#).ok(), - Some(NaiveDate::MAX.and_hms_nano_opt(23, 59, 59, 1_999_999_999).unwrap()) - ); - assert_eq!( - from_str(r#""+262142-12-31T23:59:60.9999999999997""#).ok(), // excess digits are ignored - Some(NaiveDate::MAX.and_hms_nano_opt(23, 59, 59, 1_999_999_999).unwrap()) - ); - - // bad formats - assert!(from_str(r#""""#).is_err()); - assert!(from_str(r#""2016-07-08""#).is_err()); - assert!(from_str(r#""09:10:48.090""#).is_err()); - assert!(from_str(r#""20160708T091048.090""#).is_err()); - assert!(from_str(r#""2000-00-00T00:00:00""#).is_err()); - assert!(from_str(r#""2000-02-30T00:00:00""#).is_err()); - assert!(from_str(r#""2001-02-29T00:00:00""#).is_err()); - assert!(from_str(r#""2002-02-28T24:00:00""#).is_err()); - assert!(from_str(r#""2002-02-28T23:60:00""#).is_err()); - assert!(from_str(r#""2002-02-28T23:59:61""#).is_err()); - assert!(from_str(r#""2016-07-08T09:10:48,090""#).is_err()); - assert!(from_str(r#""2016-07-08 09:10:48.090""#).is_err()); - assert!(from_str(r#""2016-007-08T09:10:48.090""#).is_err()); - assert!(from_str(r#""yyyy-mm-ddThh:mm:ss.fffffffff""#).is_err()); - assert!(from_str(r#"20160708000000"#).is_err()); - assert!(from_str(r#"{}"#).is_err()); - // pre-0.3.0 rustc-serialize format is now invalid - assert!(from_str(r#"{"date":{"ymdf":20},"time":{"secs":0,"frac":0}}"#).is_err()); - assert!(from_str(r#"null"#).is_err()); - } - - // Bincode is relevant to test separately from JSON because - // it is not self-describing. - #[test] - fn test_serde_bincode() { - let dt = - NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_milli_opt(9, 10, 48, 90).unwrap(); - let encoded = serialize(&dt).unwrap(); - let decoded: NaiveDateTime = deserialize(&encoded).unwrap(); - assert_eq!(dt, decoded); - } - - #[test] - fn test_serde_bincode_optional() { - #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] - struct Test { - one: Option, - #[serde(with = "ts_nanoseconds_option")] - two: Option>, - } - - let expected = - Test { one: Some(1), two: Some(Utc.with_ymd_and_hms(1970, 1, 1, 0, 1, 1).unwrap()) }; - let bytes: Vec = serialize(&expected).unwrap(); - let actual = deserialize::(&(bytes)).unwrap(); - - assert_eq!(expected, actual); - } -} diff --git a/chrono-0.4.44/src/naive/datetime/tests.rs b/chrono-0.4.44/src/naive/datetime/tests.rs deleted file mode 100644 index d6148d6799..0000000000 --- a/chrono-0.4.44/src/naive/datetime/tests.rs +++ /dev/null @@ -1,405 +0,0 @@ -use super::NaiveDateTime; -use crate::{Datelike, FixedOffset, MappedLocalTime, NaiveDate, TimeDelta, Utc}; - -#[test] -fn test_datetime_add() { - fn check( - (y, m, d, h, n, s): (i32, u32, u32, u32, u32, u32), - rhs: TimeDelta, - result: Option<(i32, u32, u32, u32, u32, u32)>, - ) { - let lhs = NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap(); - let sum = result.map(|(y, m, d, h, n, s)| { - NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap() - }); - assert_eq!(lhs.checked_add_signed(rhs), sum); - assert_eq!(lhs.checked_sub_signed(-rhs), sum); - } - let seconds = |s| TimeDelta::try_seconds(s).unwrap(); - - check((2014, 5, 6, 7, 8, 9), seconds(3600 + 60 + 1), Some((2014, 5, 6, 8, 9, 10))); - check((2014, 5, 6, 7, 8, 9), seconds(-(3600 + 60 + 1)), Some((2014, 5, 6, 6, 7, 8))); - check((2014, 5, 6, 7, 8, 9), seconds(86399), Some((2014, 5, 7, 7, 8, 8))); - check((2014, 5, 6, 7, 8, 9), seconds(86_400 * 10), Some((2014, 5, 16, 7, 8, 9))); - check((2014, 5, 6, 7, 8, 9), seconds(-86_400 * 10), Some((2014, 4, 26, 7, 8, 9))); - check((2014, 5, 6, 7, 8, 9), seconds(86_400 * 10), Some((2014, 5, 16, 7, 8, 9))); - - // overflow check - // assumes that we have correct values for MAX/MIN_DAYS_FROM_YEAR_0 from `naive::date`. - // (they are private constants, but the equivalence is tested in that module.) - let max_days_from_year_0 = - NaiveDate::MAX.signed_duration_since(NaiveDate::from_ymd_opt(0, 1, 1).unwrap()); - check((0, 1, 1, 0, 0, 0), max_days_from_year_0, Some((NaiveDate::MAX.year(), 12, 31, 0, 0, 0))); - check( - (0, 1, 1, 0, 0, 0), - max_days_from_year_0 + seconds(86399), - Some((NaiveDate::MAX.year(), 12, 31, 23, 59, 59)), - ); - check((0, 1, 1, 0, 0, 0), max_days_from_year_0 + seconds(86_400), None); - check((0, 1, 1, 0, 0, 0), TimeDelta::MAX, None); - - let min_days_from_year_0 = - NaiveDate::MIN.signed_duration_since(NaiveDate::from_ymd_opt(0, 1, 1).unwrap()); - check((0, 1, 1, 0, 0, 0), min_days_from_year_0, Some((NaiveDate::MIN.year(), 1, 1, 0, 0, 0))); - check((0, 1, 1, 0, 0, 0), min_days_from_year_0 - seconds(1), None); - check((0, 1, 1, 0, 0, 0), TimeDelta::MIN, None); -} - -#[test] -fn test_datetime_sub() { - let ymdhms = - |y, m, d, h, n, s| NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap(); - let since = NaiveDateTime::signed_duration_since; - assert_eq!(since(ymdhms(2014, 5, 6, 7, 8, 9), ymdhms(2014, 5, 6, 7, 8, 9)), TimeDelta::zero()); - assert_eq!( - since(ymdhms(2014, 5, 6, 7, 8, 10), ymdhms(2014, 5, 6, 7, 8, 9)), - TimeDelta::try_seconds(1).unwrap() - ); - assert_eq!( - since(ymdhms(2014, 5, 6, 7, 8, 9), ymdhms(2014, 5, 6, 7, 8, 10)), - TimeDelta::try_seconds(-1).unwrap() - ); - assert_eq!( - since(ymdhms(2014, 5, 7, 7, 8, 9), ymdhms(2014, 5, 6, 7, 8, 10)), - TimeDelta::try_seconds(86399).unwrap() - ); - assert_eq!( - since(ymdhms(2001, 9, 9, 1, 46, 39), ymdhms(1970, 1, 1, 0, 0, 0)), - TimeDelta::try_seconds(999_999_999).unwrap() - ); -} - -#[test] -fn test_datetime_addassignment() { - let ymdhms = - |y, m, d, h, n, s| NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap(); - let mut date = ymdhms(2016, 10, 1, 10, 10, 10); - date += TimeDelta::try_minutes(10_000_000).unwrap(); - assert_eq!(date, ymdhms(2035, 10, 6, 20, 50, 10)); - date += TimeDelta::try_days(10).unwrap(); - assert_eq!(date, ymdhms(2035, 10, 16, 20, 50, 10)); -} - -#[test] -fn test_datetime_subassignment() { - let ymdhms = - |y, m, d, h, n, s| NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap(); - let mut date = ymdhms(2016, 10, 1, 10, 10, 10); - date -= TimeDelta::try_minutes(10_000_000).unwrap(); - assert_eq!(date, ymdhms(1997, 9, 26, 23, 30, 10)); - date -= TimeDelta::try_days(10).unwrap(); - assert_eq!(date, ymdhms(1997, 9, 16, 23, 30, 10)); -} - -#[test] -fn test_core_duration_ops() { - use core::time::Duration; - - let mut dt = NaiveDate::from_ymd_opt(2023, 8, 29).unwrap().and_hms_opt(11, 34, 12).unwrap(); - let same = dt + Duration::ZERO; - assert_eq!(dt, same); - - dt += Duration::new(3600, 0); - assert_eq!(dt, NaiveDate::from_ymd_opt(2023, 8, 29).unwrap().and_hms_opt(12, 34, 12).unwrap()); -} - -#[test] -#[should_panic] -fn test_core_duration_max() { - use core::time::Duration; - - let mut utc_dt = NaiveDate::from_ymd_opt(2023, 8, 29).unwrap().and_hms_opt(11, 34, 12).unwrap(); - utc_dt += Duration::MAX; -} - -#[test] -fn test_datetime_from_str() { - // valid cases - let valid = [ - "2001-02-03T04:05:06", - "2012-12-12T12:12:12", - "2015-02-18T23:16:09.153", - "2015-2-18T23:16:09.153", - "-77-02-18T23:16:09", - "+82701-05-6T15:9:60.898989898989", - " +82701 - 05 - 6 T 15 : 9 : 60.898989898989 ", - ]; - for &s in &valid { - eprintln!("test_parse_naivedatetime valid {s:?}"); - let d = match s.parse::() { - Ok(d) => d, - Err(e) => panic!("parsing `{s}` has failed: {e}"), - }; - let s_ = format!("{d:?}"); - // `s` and `s_` may differ, but `s.parse()` and `s_.parse()` must be same - let d_ = match s_.parse::() { - Ok(d) => d, - Err(e) => { - panic!("`{s}` is parsed into `{d:?}`, but reparsing that has failed: {e}") - } - }; - assert!( - d == d_, - "`{s}` is parsed into `{d:?}`, but reparsed result \ - `{d_:?}` does not match" - ); - } - - // some invalid cases - // since `ParseErrorKind` is private, all we can do is to check if there was an error - let invalid = [ - "", // empty - "x", // invalid / missing data - "15", // missing data - "15:8:9", // looks like a time (invalid date) - "15-8-9", // looks like a date (invalid) - "Fri, 09 Aug 2013 23:54:35 GMT", // valid date, wrong format - "Sat Jun 30 23:59:60 2012", // valid date, wrong format - "1441497364.649", // valid date, wrong format - "+1441497364.649", // valid date, wrong format - "+1441497364", // valid date, wrong format - "2014/02/03 04:05:06", // valid date, wrong format - "2015-15-15T15:15:15", // invalid date - "2012-12-12T12:12:12x", // bad timezone / trailing literal - "2012-12-12T12:12:12+00:00", // unexpected timezone / trailing literal - "2012-12-12T12:12:12 +00:00", // unexpected timezone / trailing literal - "2012-12-12T12:12:12 GMT", // unexpected timezone / trailing literal - "2012-123-12T12:12:12", // invalid month - "2012-12-12t12:12:12", // bad divider 't' - "2012-12-12 12:12:12", // missing divider 'T' - "2012-12-12T12:12:12Z", // trailing char 'Z' - "+ 82701-123-12T12:12:12", // strange year, invalid month - "+802701-123-12T12:12:12", // out-of-bound year, invalid month - ]; - for &s in &invalid { - eprintln!("test_datetime_from_str invalid {s:?}"); - assert!(s.parse::().is_err()); - } -} - -#[test] -fn test_datetime_parse_from_str() { - let ymdhms = - |y, m, d, h, n, s| NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap(); - let ymdhmsn = |y, m, d, h, n, s, nano| { - NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_nano_opt(h, n, s, nano).unwrap() - }; - assert_eq!( - NaiveDateTime::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), - Ok(ymdhms(2014, 5, 7, 12, 34, 56)) - ); // ignore offset - assert_eq!( - NaiveDateTime::parse_from_str("2015-W06-1 000000", "%G-W%V-%u%H%M%S"), - Ok(ymdhms(2015, 2, 2, 0, 0, 0)) - ); - assert_eq!( - NaiveDateTime::parse_from_str("Fri, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT"), - Ok(ymdhms(2013, 8, 9, 23, 54, 35)) - ); - assert!( - NaiveDateTime::parse_from_str("Sat, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT") - .is_err() - ); - assert!(NaiveDateTime::parse_from_str("2014-5-7 Q2 12:3456", "%Y-%m-%d Q%q %H:%M:%S").is_err()); - assert!(NaiveDateTime::parse_from_str("12:34:56", "%H:%M:%S").is_err()); // insufficient - assert_eq!( - NaiveDateTime::parse_from_str("1441497364", "%s"), - Ok(ymdhms(2015, 9, 5, 23, 56, 4)) - ); - assert_eq!( - NaiveDateTime::parse_from_str("1283929614.1234", "%s.%f"), - Ok(ymdhmsn(2010, 9, 8, 7, 6, 54, 1234)) - ); - assert_eq!( - NaiveDateTime::parse_from_str("1441497364.649", "%s%.3f"), - Ok(ymdhmsn(2015, 9, 5, 23, 56, 4, 649000000)) - ); - assert_eq!( - NaiveDateTime::parse_from_str("1497854303.087654", "%s%.6f"), - Ok(ymdhmsn(2017, 6, 19, 6, 38, 23, 87654000)) - ); - assert_eq!( - NaiveDateTime::parse_from_str("1437742189.918273645", "%s%.9f"), - Ok(ymdhmsn(2015, 7, 24, 12, 49, 49, 918273645)) - ); -} - -#[test] -fn test_datetime_parse_from_str_with_spaces() { - let parse_from_str = NaiveDateTime::parse_from_str; - let dt = NaiveDate::from_ymd_opt(2013, 8, 9).unwrap().and_hms_opt(23, 54, 35).unwrap(); - // with varying spaces - should succeed - assert_eq!(parse_from_str(" Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt)); - assert_eq!(parse_from_str("Aug 09 2013 23:54:35 ", "%b %d %Y %H:%M:%S "), Ok(dt)); - assert_eq!(parse_from_str(" Aug 09 2013 23:54:35 ", " %b %d %Y %H:%M:%S "), Ok(dt)); - assert_eq!(parse_from_str(" Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt)); - assert_eq!(parse_from_str(" Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt)); - assert_eq!(parse_from_str("\n\tAug 09 2013 23:54:35 ", "\n\t%b %d %Y %H:%M:%S "), Ok(dt)); - assert_eq!(parse_from_str("\tAug 09 2013 23:54:35\t", "\t%b %d %Y %H:%M:%S\t"), Ok(dt)); - assert_eq!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S"), Ok(dt)); - assert_eq!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S"), Ok(dt)); - assert_eq!(parse_from_str("Aug 09 2013\t23:54:35", "%b %d %Y\t%H:%M:%S"), Ok(dt)); - assert_eq!(parse_from_str("Aug 09 2013\t\t23:54:35", "%b %d %Y\t\t%H:%M:%S"), Ok(dt)); - assert_eq!(parse_from_str("Aug 09 2013 23:54:35 ", "%b %d %Y %H:%M:%S\n"), Ok(dt)); - assert_eq!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y\t%H:%M:%S"), Ok(dt)); - assert_eq!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S "), Ok(dt)); - assert_eq!(parse_from_str("Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt)); - assert_eq!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S\n"), Ok(dt)); - // with varying spaces - should fail - // leading space in data - assert!(parse_from_str(" Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S").is_err()); - // trailing space in data - assert!(parse_from_str("Aug 09 2013 23:54:35 ", "%b %d %Y %H:%M:%S").is_err()); - // trailing tab in data - assert!(parse_from_str("Aug 09 2013 23:54:35\t", "%b %d %Y %H:%M:%S").is_err()); - // mismatched newlines - assert!(parse_from_str("\nAug 09 2013 23:54:35", "%b %d %Y %H:%M:%S\n").is_err()); - // trailing literal in data - assert!(parse_from_str("Aug 09 2013 23:54:35 !!!", "%b %d %Y %H:%M:%S ").is_err()); -} - -#[test] -fn test_datetime_add_sub_invariant() { - // issue #37 - let base = NaiveDate::from_ymd_opt(2000, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(); - let t = -946684799990000; - let time = base + TimeDelta::microseconds(t); - assert_eq!(t, time.signed_duration_since(base).num_microseconds().unwrap()); -} - -#[test] -fn test_and_local_timezone() { - let ndt = NaiveDate::from_ymd_opt(2022, 6, 15).unwrap().and_hms_opt(18, 59, 36).unwrap(); - let dt_utc = ndt.and_utc(); - assert_eq!(dt_utc.naive_local(), ndt); - assert_eq!(dt_utc.timezone(), Utc); - - let offset_tz = FixedOffset::west_opt(4 * 3600).unwrap(); - let dt_offset = ndt.and_local_timezone(offset_tz).unwrap(); - assert_eq!(dt_offset.naive_local(), ndt); - assert_eq!(dt_offset.timezone(), offset_tz); -} - -#[test] -fn test_and_utc() { - let ndt = NaiveDate::from_ymd_opt(2023, 1, 30).unwrap().and_hms_opt(19, 32, 33).unwrap(); - let dt_utc = ndt.and_utc(); - assert_eq!(dt_utc.naive_local(), ndt); - assert_eq!(dt_utc.timezone(), Utc); -} - -#[test] -fn test_checked_add_offset() { - let ymdhmsm = |y, m, d, h, mn, s, mi| { - NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_milli_opt(h, mn, s, mi) - }; - - let positive_offset = FixedOffset::east_opt(2 * 60 * 60).unwrap(); - // regular date - let dt = ymdhmsm(2023, 5, 5, 20, 10, 0, 0).unwrap(); - assert_eq!(dt.checked_add_offset(positive_offset), ymdhmsm(2023, 5, 5, 22, 10, 0, 0)); - // leap second is preserved - let dt = ymdhmsm(2023, 6, 30, 23, 59, 59, 1_000).unwrap(); - assert_eq!(dt.checked_add_offset(positive_offset), ymdhmsm(2023, 7, 1, 1, 59, 59, 1_000)); - // out of range - assert!(NaiveDateTime::MAX.checked_add_offset(positive_offset).is_none()); - - let negative_offset = FixedOffset::west_opt(2 * 60 * 60).unwrap(); - // regular date - let dt = ymdhmsm(2023, 5, 5, 20, 10, 0, 0).unwrap(); - assert_eq!(dt.checked_add_offset(negative_offset), ymdhmsm(2023, 5, 5, 18, 10, 0, 0)); - // leap second is preserved - let dt = ymdhmsm(2023, 6, 30, 23, 59, 59, 1_000).unwrap(); - assert_eq!(dt.checked_add_offset(negative_offset), ymdhmsm(2023, 6, 30, 21, 59, 59, 1_000)); - // out of range - assert!(NaiveDateTime::MIN.checked_add_offset(negative_offset).is_none()); -} - -#[test] -fn test_checked_sub_offset() { - let ymdhmsm = |y, m, d, h, mn, s, mi| { - NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_milli_opt(h, mn, s, mi) - }; - - let positive_offset = FixedOffset::east_opt(2 * 60 * 60).unwrap(); - // regular date - let dt = ymdhmsm(2023, 5, 5, 20, 10, 0, 0).unwrap(); - assert_eq!(dt.checked_sub_offset(positive_offset), ymdhmsm(2023, 5, 5, 18, 10, 0, 0)); - // leap second is preserved - let dt = ymdhmsm(2023, 6, 30, 23, 59, 59, 1_000).unwrap(); - assert_eq!(dt.checked_sub_offset(positive_offset), ymdhmsm(2023, 6, 30, 21, 59, 59, 1_000)); - // out of range - assert!(NaiveDateTime::MIN.checked_sub_offset(positive_offset).is_none()); - - let negative_offset = FixedOffset::west_opt(2 * 60 * 60).unwrap(); - // regular date - let dt = ymdhmsm(2023, 5, 5, 20, 10, 0, 0).unwrap(); - assert_eq!(dt.checked_sub_offset(negative_offset), ymdhmsm(2023, 5, 5, 22, 10, 0, 0)); - // leap second is preserved - let dt = ymdhmsm(2023, 6, 30, 23, 59, 59, 1_000).unwrap(); - assert_eq!(dt.checked_sub_offset(negative_offset), ymdhmsm(2023, 7, 1, 1, 59, 59, 1_000)); - // out of range - assert!(NaiveDateTime::MAX.checked_sub_offset(negative_offset).is_none()); - - assert_eq!(dt.checked_add_offset(positive_offset), Some(dt + positive_offset)); - assert_eq!(dt.checked_sub_offset(positive_offset), Some(dt - positive_offset)); -} - -#[test] -fn test_overflowing_add_offset() { - let ymdhmsm = |y, m, d, h, mn, s, mi| { - NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_milli_opt(h, mn, s, mi).unwrap() - }; - let positive_offset = FixedOffset::east_opt(2 * 60 * 60).unwrap(); - // regular date - let dt = ymdhmsm(2023, 5, 5, 20, 10, 0, 0); - assert_eq!(dt.overflowing_add_offset(positive_offset), ymdhmsm(2023, 5, 5, 22, 10, 0, 0)); - // leap second is preserved - let dt = ymdhmsm(2023, 6, 30, 23, 59, 59, 1_000); - assert_eq!(dt.overflowing_add_offset(positive_offset), ymdhmsm(2023, 7, 1, 1, 59, 59, 1_000)); - // out of range - assert!(NaiveDateTime::MAX.overflowing_add_offset(positive_offset) > NaiveDateTime::MAX); - - let negative_offset = FixedOffset::west_opt(2 * 60 * 60).unwrap(); - // regular date - let dt = ymdhmsm(2023, 5, 5, 20, 10, 0, 0); - assert_eq!(dt.overflowing_add_offset(negative_offset), ymdhmsm(2023, 5, 5, 18, 10, 0, 0)); - // leap second is preserved - let dt = ymdhmsm(2023, 6, 30, 23, 59, 59, 1_000); - assert_eq!(dt.overflowing_add_offset(negative_offset), ymdhmsm(2023, 6, 30, 21, 59, 59, 1_000)); - // out of range - assert!(NaiveDateTime::MIN.overflowing_add_offset(negative_offset) < NaiveDateTime::MIN); -} - -#[test] -fn test_and_timezone_min_max_dates() { - for offset_hour in -23..=23 { - dbg!(offset_hour); - let offset = FixedOffset::east_opt(offset_hour * 60 * 60).unwrap(); - - let local_max = NaiveDateTime::MAX.and_local_timezone(offset); - if offset_hour >= 0 { - assert_eq!(local_max.unwrap().naive_local(), NaiveDateTime::MAX); - } else { - assert_eq!(local_max, MappedLocalTime::None); - } - let local_min = NaiveDateTime::MIN.and_local_timezone(offset); - if offset_hour <= 0 { - assert_eq!(local_min.unwrap().naive_local(), NaiveDateTime::MIN); - } else { - assert_eq!(local_min, MappedLocalTime::None); - } - } -} - -#[test] -#[cfg(feature = "rkyv-validation")] -fn test_rkyv_validation() { - let dt_min = NaiveDateTime::MIN; - let bytes = rkyv::to_bytes::<_, 12>(&dt_min).unwrap(); - assert_eq!(rkyv::from_bytes::(&bytes).unwrap(), dt_min); - - let dt_max = NaiveDateTime::MAX; - let bytes = rkyv::to_bytes::<_, 12>(&dt_max).unwrap(); - assert_eq!(rkyv::from_bytes::(&bytes).unwrap(), dt_max); -} diff --git a/chrono-0.4.44/src/naive/internals.rs b/chrono-0.4.44/src/naive/internals.rs deleted file mode 100644 index 547dc46ad5..0000000000 --- a/chrono-0.4.44/src/naive/internals.rs +++ /dev/null @@ -1,591 +0,0 @@ -//! Internal helper types for working with dates. - -#![cfg_attr(feature = "__internal_bench", allow(missing_docs))] - -use core::fmt; - -/// Year flags (aka the dominical letter). -/// -/// `YearFlags` are used as the last four bits of `NaiveDate`, `Mdf` and `IsoWeek`. -/// -/// There are 14 possible classes of year in the Gregorian calendar: -/// common and leap years starting with Monday through Sunday. -/// -/// The `YearFlags` stores this information into 4 bits `LWWW`. `L` is the leap year flag, with `1` -/// for the common year (this simplifies validating an ordinal in `NaiveDate`). `WWW` is a non-zero -/// `Weekday` of the last day in the preceding year. -#[allow(unreachable_pub)] // public as an alias for benchmarks only -#[derive(PartialEq, Eq, Copy, Clone, Hash)] -pub struct YearFlags(pub(super) u8); - -// Weekday of the last day in the preceding year. -// Allows for quick day of week calculation from the 1-based ordinal. -const YEAR_STARTS_AFTER_MONDAY: u8 = 7; // non-zero to allow use with `NonZero*`. -const YEAR_STARTS_AFTER_THUESDAY: u8 = 1; -const YEAR_STARTS_AFTER_WEDNESDAY: u8 = 2; -const YEAR_STARTS_AFTER_THURSDAY: u8 = 3; -const YEAR_STARTS_AFTER_FRIDAY: u8 = 4; -const YEAR_STARTS_AFTER_SATURDAY: u8 = 5; -const YEAR_STARTS_AFTER_SUNDAY: u8 = 6; - -const COMMON_YEAR: u8 = 1 << 3; -const LEAP_YEAR: u8 = 0 << 3; - -pub(super) const A: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_SATURDAY); -pub(super) const AG: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_SATURDAY); -pub(super) const B: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_FRIDAY); -pub(super) const BA: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_FRIDAY); -pub(super) const C: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_THURSDAY); -pub(super) const CB: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_THURSDAY); -pub(super) const D: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_WEDNESDAY); -pub(super) const DC: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_WEDNESDAY); -pub(super) const E: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_THUESDAY); -pub(super) const ED: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_THUESDAY); -pub(super) const F: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_MONDAY); -pub(super) const FE: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_MONDAY); -pub(super) const G: YearFlags = YearFlags(COMMON_YEAR | YEAR_STARTS_AFTER_SUNDAY); -pub(super) const GF: YearFlags = YearFlags(LEAP_YEAR | YEAR_STARTS_AFTER_SUNDAY); - -const YEAR_TO_FLAGS: &[YearFlags; 400] = &[ - BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, - G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, - F, E, DC, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, - E, DC, B, A, G, FE, D, C, B, AG, F, E, D, // 100 - C, B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, - B, A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, - A, G, FE, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, - G, FE, D, C, B, AG, F, E, D, CB, A, G, F, // 200 - E, D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, - D, C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, - C, B, AG, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, - B, AG, F, E, D, CB, A, G, F, ED, C, B, A, // 300 - G, F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, - F, E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, - E, D, CB, A, G, F, ED, C, B, A, GF, E, D, C, BA, G, F, E, DC, B, A, G, FE, D, C, B, AG, F, E, - D, CB, A, G, F, ED, C, B, A, GF, E, D, C, // 400 -]; - -impl YearFlags { - #[allow(unreachable_pub)] // public as an alias for benchmarks only - #[doc(hidden)] // for benchmarks only - #[inline] - #[must_use] - pub const fn from_year(year: i32) -> YearFlags { - let year = year.rem_euclid(400); - YearFlags::from_year_mod_400(year) - } - - #[inline] - pub(super) const fn from_year_mod_400(year: i32) -> YearFlags { - YEAR_TO_FLAGS[year as usize] - } - - #[inline] - pub(super) const fn ndays(&self) -> u32 { - let YearFlags(flags) = *self; - 366 - (flags >> 3) as u32 - } - - #[inline] - pub(super) const fn isoweek_delta(&self) -> u32 { - let YearFlags(flags) = *self; - let mut delta = (flags & 0b0111) as u32; - if delta < 3 { - delta += 7; - } - delta - } - - #[inline] - pub(super) const fn nisoweeks(&self) -> u32 { - let YearFlags(flags) = *self; - 52 + ((0b0000_0100_0000_0110 >> flags as usize) & 1) - } -} - -impl fmt::Debug for YearFlags { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let YearFlags(flags) = *self; - match flags { - 0o15 => "A".fmt(f), - 0o05 => "AG".fmt(f), - 0o14 => "B".fmt(f), - 0o04 => "BA".fmt(f), - 0o13 => "C".fmt(f), - 0o03 => "CB".fmt(f), - 0o12 => "D".fmt(f), - 0o02 => "DC".fmt(f), - 0o11 => "E".fmt(f), - 0o01 => "ED".fmt(f), - 0o10 => "F?".fmt(f), - 0o00 => "FE?".fmt(f), // non-canonical - 0o17 => "F".fmt(f), - 0o07 => "FE".fmt(f), - 0o16 => "G".fmt(f), - 0o06 => "GF".fmt(f), - _ => write!(f, "YearFlags({flags})"), - } - } -} - -// OL: (ordinal << 1) | leap year flag -const MAX_OL: u32 = 366 << 1; // `(366 << 1) | 1` would be day 366 in a non-leap year -const MAX_MDL: u32 = (12 << 6) | (31 << 1) | 1; - -// The next table are adjustment values to convert a date encoded as month-day-leapyear to -// ordinal-leapyear. OL = MDL - adjustment. -// Dates that do not exist are encoded as `XX`. -const XX: i8 = 0; -const MDL_TO_OL: &[i8; MAX_MDL as usize + 1] = &[ - XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, - XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, - XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, XX, // 0 - XX, XX, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 1 - XX, XX, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, XX, XX, XX, XX, XX, // 2 - XX, XX, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, - 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, - 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, // 3 - XX, XX, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, - 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, - 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, XX, XX, // 4 - XX, XX, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, - 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, - 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, // 5 - XX, XX, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, - 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, - 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, XX, XX, // 6 - XX, XX, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, - 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, - 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, // 7 - XX, XX, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, - 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, - 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, // 8 - XX, XX, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, - 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, - 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, XX, XX, // 9 - XX, XX, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, - 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, - 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, // 10 - XX, XX, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, - 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, - 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, XX, XX, // 11 - XX, XX, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, - 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, - 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, - 100, // 12 -]; - -const OL_TO_MDL: &[u8; MAX_OL as usize + 1] = &[ - 0, 0, // 0 - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 1 - 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 66, 66, 66, 66, 66, 66, // 2 - 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, - 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, - 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, 74, 72, // 3 - 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, - 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, - 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, 76, 74, // 4 - 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, - 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, - 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, 80, 78, // 5 - 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, - 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, - 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, 82, 80, // 6 - 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, - 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, - 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, 86, 84, // 7 - 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, - 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, - 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, 88, 86, // 8 - 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, - 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, - 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, 90, 88, // 9 - 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, - 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, - 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, 94, 92, // 10 - 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, - 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, - 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, 96, 94, // 11 - 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, - 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, - 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, 98, 100, - 98, // 12 -]; - -/// Month, day of month and year flags: `(month << 9) | (day << 4) | flags` -/// `M_MMMD_DDDD_LFFF` -/// -/// The whole bits except for the least 3 bits are referred as `Mdl` (month, day of month, and leap -/// year flag), which is an index to the `MDL_TO_OL` lookup table. -/// -/// The conversion between the packed calendar date (`Mdf`) and the ordinal date (`NaiveDate`) is -/// based on the moderately-sized lookup table (~1.5KB) and the packed representation is chosen for -/// efficient lookup. -/// -/// The methods of `Mdf` validate their inputs as late as possible. Dates that can't exist, like -/// February 30, can still be represented. This allows the validation to be combined with the final -/// table lookup, which is good for performance. -#[derive(PartialEq, PartialOrd, Copy, Clone)] -pub(super) struct Mdf(u32); - -impl Mdf { - /// Makes a new `Mdf` value from month, day and `YearFlags`. - /// - /// This method doesn't fully validate the range of the `month` and `day` parameters, only as - /// much as what can't be deferred until later. The year `flags` are trusted to be correct. - /// - /// # Errors - /// - /// Returns `None` if `month > 12` or `day > 31`. - #[inline] - pub(super) const fn new(month: u32, day: u32, YearFlags(flags): YearFlags) -> Option { - match month <= 12 && day <= 31 { - true => Some(Mdf((month << 9) | (day << 4) | flags as u32)), - false => None, - } - } - - /// Makes a new `Mdf` value from an `i32` with an ordinal and a leap year flag, and year - /// `flags`. - /// - /// The `ol` is trusted to be valid, and the `flags` are trusted to match it. - #[inline] - pub(super) const fn from_ol(ol: i32, YearFlags(flags): YearFlags) -> Mdf { - debug_assert!(ol > 1 && ol <= MAX_OL as i32); - // Array is indexed from `[2..=MAX_OL]`, with a `0` index having a meaningless value. - Mdf(((ol as u32 + OL_TO_MDL[ol as usize] as u32) << 3) | flags as u32) - } - - /// Returns the month of this `Mdf`. - #[inline] - pub(super) const fn month(&self) -> u32 { - let Mdf(mdf) = *self; - mdf >> 9 - } - - /// Replaces the month of this `Mdf`, keeping the day and flags. - /// - /// # Errors - /// - /// Returns `None` if `month > 12`. - #[inline] - pub(super) const fn with_month(&self, month: u32) -> Option { - if month > 12 { - return None; - } - - let Mdf(mdf) = *self; - Some(Mdf((mdf & 0b1_1111_1111) | (month << 9))) - } - - /// Returns the day of this `Mdf`. - #[inline] - pub(super) const fn day(&self) -> u32 { - let Mdf(mdf) = *self; - (mdf >> 4) & 0b1_1111 - } - - /// Replaces the day of this `Mdf`, keeping the month and flags. - /// - /// # Errors - /// - /// Returns `None` if `day > 31`. - #[inline] - pub(super) const fn with_day(&self, day: u32) -> Option { - if day > 31 { - return None; - } - - let Mdf(mdf) = *self; - Some(Mdf((mdf & !0b1_1111_0000) | (day << 4))) - } - - /// Replaces the flags of this `Mdf`, keeping the month and day. - #[inline] - pub(super) const fn with_flags(&self, YearFlags(flags): YearFlags) -> Mdf { - let Mdf(mdf) = *self; - Mdf((mdf & !0b1111) | flags as u32) - } - - /// Returns the ordinal that corresponds to this `Mdf`. - /// - /// This does a table lookup to calculate the corresponding ordinal. It will return an error if - /// the `Mdl` turns out not to be a valid date. - /// - /// # Errors - /// - /// Returns `None` if `month == 0` or `day == 0`, or if a the given day does not exist in the - /// given month. - #[inline] - pub(super) const fn ordinal(&self) -> Option { - let mdl = self.0 >> 3; - match MDL_TO_OL[mdl as usize] { - XX => None, - v => Some((mdl - v as u8 as u32) >> 1), - } - } - - /// Returns the year flags of this `Mdf`. - #[inline] - pub(super) const fn year_flags(&self) -> YearFlags { - YearFlags((self.0 & 0b1111) as u8) - } - - /// Returns the ordinal that corresponds to this `Mdf`, encoded as a value including year flags. - /// - /// This does a table lookup to calculate the corresponding ordinal. It will return an error if - /// the `Mdl` turns out not to be a valid date. - /// - /// # Errors - /// - /// Returns `None` if `month == 0` or `day == 0`, or if a the given day does not exist in the - /// given month. - #[inline] - pub(super) const fn ordinal_and_flags(&self) -> Option { - let mdl = self.0 >> 3; - match MDL_TO_OL[mdl as usize] { - XX => None, - v => Some(self.0 as i32 - ((v as i32) << 3)), - } - } - - #[cfg(test)] - fn valid(&self) -> bool { - let mdl = self.0 >> 3; - MDL_TO_OL[mdl as usize] > 0 - } -} - -impl fmt::Debug for Mdf { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let Mdf(mdf) = *self; - write!( - f, - "Mdf(({} << 9) | ({} << 4) | {:#04o} /*{:?}*/)", - mdf >> 9, - (mdf >> 4) & 0b1_1111, - mdf & 0b1111, - YearFlags((mdf & 0b1111) as u8) - ) - } -} - -#[cfg(test)] -mod tests { - use super::Mdf; - use super::{A, AG, B, BA, C, CB, D, DC, E, ED, F, FE, G, GF, YearFlags}; - - const NONLEAP_FLAGS: [YearFlags; 7] = [A, B, C, D, E, F, G]; - const LEAP_FLAGS: [YearFlags; 7] = [AG, BA, CB, DC, ED, FE, GF]; - const FLAGS: [YearFlags; 14] = [A, B, C, D, E, F, G, AG, BA, CB, DC, ED, FE, GF]; - - #[test] - fn test_year_flags_ndays_from_year() { - assert_eq!(YearFlags::from_year(2014).ndays(), 365); - assert_eq!(YearFlags::from_year(2012).ndays(), 366); - assert_eq!(YearFlags::from_year(2000).ndays(), 366); - assert_eq!(YearFlags::from_year(1900).ndays(), 365); - assert_eq!(YearFlags::from_year(1600).ndays(), 366); - assert_eq!(YearFlags::from_year(1).ndays(), 365); - assert_eq!(YearFlags::from_year(0).ndays(), 366); // 1 BCE (proleptic Gregorian) - assert_eq!(YearFlags::from_year(-1).ndays(), 365); // 2 BCE - assert_eq!(YearFlags::from_year(-4).ndays(), 366); // 5 BCE - assert_eq!(YearFlags::from_year(-99).ndays(), 365); // 100 BCE - assert_eq!(YearFlags::from_year(-100).ndays(), 365); // 101 BCE - assert_eq!(YearFlags::from_year(-399).ndays(), 365); // 400 BCE - assert_eq!(YearFlags::from_year(-400).ndays(), 366); // 401 BCE - } - - #[test] - fn test_year_flags_nisoweeks() { - assert_eq!(A.nisoweeks(), 52); - assert_eq!(B.nisoweeks(), 52); - assert_eq!(C.nisoweeks(), 52); - assert_eq!(D.nisoweeks(), 53); - assert_eq!(E.nisoweeks(), 52); - assert_eq!(F.nisoweeks(), 52); - assert_eq!(G.nisoweeks(), 52); - assert_eq!(AG.nisoweeks(), 52); - assert_eq!(BA.nisoweeks(), 52); - assert_eq!(CB.nisoweeks(), 52); - assert_eq!(DC.nisoweeks(), 53); - assert_eq!(ED.nisoweeks(), 53); - assert_eq!(FE.nisoweeks(), 52); - assert_eq!(GF.nisoweeks(), 52); - } - - #[test] - fn test_mdf_valid() { - fn check(expected: bool, flags: YearFlags, month1: u32, day1: u32, month2: u32, day2: u32) { - for month in month1..=month2 { - for day in day1..=day2 { - let mdf = match Mdf::new(month, day, flags) { - Some(mdf) => mdf, - None if !expected => continue, - None => panic!("Mdf::new({month}, {day}, {flags:?}) returned None"), - }; - - assert!( - mdf.valid() == expected, - "month {} day {} = {:?} should be {} for dominical year {:?}", - month, - day, - mdf, - if expected { "valid" } else { "invalid" }, - flags - ); - } - } - } - - for &flags in NONLEAP_FLAGS.iter() { - check(false, flags, 0, 0, 0, 1024); - check(false, flags, 0, 0, 16, 0); - check(true, flags, 1, 1, 1, 31); - check(false, flags, 1, 32, 1, 1024); - check(true, flags, 2, 1, 2, 28); - check(false, flags, 2, 29, 2, 1024); - check(true, flags, 3, 1, 3, 31); - check(false, flags, 3, 32, 3, 1024); - check(true, flags, 4, 1, 4, 30); - check(false, flags, 4, 31, 4, 1024); - check(true, flags, 5, 1, 5, 31); - check(false, flags, 5, 32, 5, 1024); - check(true, flags, 6, 1, 6, 30); - check(false, flags, 6, 31, 6, 1024); - check(true, flags, 7, 1, 7, 31); - check(false, flags, 7, 32, 7, 1024); - check(true, flags, 8, 1, 8, 31); - check(false, flags, 8, 32, 8, 1024); - check(true, flags, 9, 1, 9, 30); - check(false, flags, 9, 31, 9, 1024); - check(true, flags, 10, 1, 10, 31); - check(false, flags, 10, 32, 10, 1024); - check(true, flags, 11, 1, 11, 30); - check(false, flags, 11, 31, 11, 1024); - check(true, flags, 12, 1, 12, 31); - check(false, flags, 12, 32, 12, 1024); - check(false, flags, 13, 0, 16, 1024); - check(false, flags, u32::MAX, 0, u32::MAX, 1024); - check(false, flags, 0, u32::MAX, 16, u32::MAX); - check(false, flags, u32::MAX, u32::MAX, u32::MAX, u32::MAX); - } - - for &flags in LEAP_FLAGS.iter() { - check(false, flags, 0, 0, 0, 1024); - check(false, flags, 0, 0, 16, 0); - check(true, flags, 1, 1, 1, 31); - check(false, flags, 1, 32, 1, 1024); - check(true, flags, 2, 1, 2, 29); - check(false, flags, 2, 30, 2, 1024); - check(true, flags, 3, 1, 3, 31); - check(false, flags, 3, 32, 3, 1024); - check(true, flags, 4, 1, 4, 30); - check(false, flags, 4, 31, 4, 1024); - check(true, flags, 5, 1, 5, 31); - check(false, flags, 5, 32, 5, 1024); - check(true, flags, 6, 1, 6, 30); - check(false, flags, 6, 31, 6, 1024); - check(true, flags, 7, 1, 7, 31); - check(false, flags, 7, 32, 7, 1024); - check(true, flags, 8, 1, 8, 31); - check(false, flags, 8, 32, 8, 1024); - check(true, flags, 9, 1, 9, 30); - check(false, flags, 9, 31, 9, 1024); - check(true, flags, 10, 1, 10, 31); - check(false, flags, 10, 32, 10, 1024); - check(true, flags, 11, 1, 11, 30); - check(false, flags, 11, 31, 11, 1024); - check(true, flags, 12, 1, 12, 31); - check(false, flags, 12, 32, 12, 1024); - check(false, flags, 13, 0, 16, 1024); - check(false, flags, u32::MAX, 0, u32::MAX, 1024); - check(false, flags, 0, u32::MAX, 16, u32::MAX); - check(false, flags, u32::MAX, u32::MAX, u32::MAX, u32::MAX); - } - } - - #[test] - fn test_mdf_fields() { - for &flags in FLAGS.iter() { - for month in 1u32..=12 { - for day in 1u32..31 { - let mdf = match Mdf::new(month, day, flags) { - Some(mdf) => mdf, - None => continue, - }; - - if mdf.valid() { - assert_eq!(mdf.month(), month); - assert_eq!(mdf.day(), day); - } - } - } - } - } - - #[test] - fn test_mdf_with_fields() { - fn check(flags: YearFlags, month: u32, day: u32) { - let mdf = Mdf::new(month, day, flags).unwrap(); - - for month in 0u32..=16 { - let mdf = match mdf.with_month(month) { - Some(mdf) => mdf, - None if month > 12 => continue, - None => panic!("failed to create Mdf with month {month}"), - }; - - if mdf.valid() { - assert_eq!(mdf.month(), month); - assert_eq!(mdf.day(), day); - } - } - - for day in 0u32..=1024 { - let mdf = match mdf.with_day(day) { - Some(mdf) => mdf, - None if day > 31 => continue, - None => panic!("failed to create Mdf with month {month}"), - }; - - if mdf.valid() { - assert_eq!(mdf.month(), month); - assert_eq!(mdf.day(), day); - } - } - } - - for &flags in NONLEAP_FLAGS.iter() { - check(flags, 1, 1); - check(flags, 1, 31); - check(flags, 2, 1); - check(flags, 2, 28); - check(flags, 2, 29); - check(flags, 12, 31); - } - for &flags in LEAP_FLAGS.iter() { - check(flags, 1, 1); - check(flags, 1, 31); - check(flags, 2, 1); - check(flags, 2, 29); - check(flags, 2, 30); - check(flags, 12, 31); - } - } - - #[test] - fn test_mdf_new_range() { - let flags = YearFlags::from_year(2023); - assert!(Mdf::new(13, 1, flags).is_none()); - assert!(Mdf::new(1, 32, flags).is_none()); - } -} diff --git a/chrono-0.4.44/src/naive/isoweek.rs b/chrono-0.4.44/src/naive/isoweek.rs deleted file mode 100644 index 25fcbc138e..0000000000 --- a/chrono-0.4.44/src/naive/isoweek.rs +++ /dev/null @@ -1,248 +0,0 @@ -// This is a part of Chrono. -// See README.md and LICENSE.txt for details. - -//! ISO 8601 week. - -use core::fmt; - -use super::internals::YearFlags; - -#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))] -use rkyv::{Archive, Deserialize, Serialize}; - -/// ISO 8601 week. -/// -/// This type, combined with [`Weekday`](../enum.Weekday.html), -/// constitutes the ISO 8601 [week date](./struct.NaiveDate.html#week-date). -/// One can retrieve this type from the existing [`Datelike`](../trait.Datelike.html) types -/// via the [`Datelike::iso_week`](../trait.Datelike.html#tymethod.iso_week) method. -#[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Hash)] -#[cfg_attr( - any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"), - derive(Archive, Deserialize, Serialize), - archive(compare(PartialEq, PartialOrd)), - archive_attr(derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)) -)] -#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] -pub struct IsoWeek { - // Note that this allows for larger year range than `NaiveDate`. - // This is crucial because we have an edge case for the first and last week supported, - // which year number might not match the calendar year number. - ywf: i32, // (year << 10) | (week << 4) | flag -} - -impl IsoWeek { - /// Returns the corresponding `IsoWeek` from the year and the `Of` internal value. - // - // Internal use only. We don't expose the public constructor for `IsoWeek` for now - // because the year range for the week date and the calendar date do not match, and - // it is confusing to have a date that is out of range in one and not in another. - // Currently we sidestep this issue by making `IsoWeek` fully dependent of `Datelike`. - pub(super) fn from_yof(year: i32, ordinal: u32, year_flags: YearFlags) -> Self { - let rawweek = (ordinal + year_flags.isoweek_delta()) / 7; - let (year, week) = if rawweek < 1 { - // previous year - let prevlastweek = YearFlags::from_year(year - 1).nisoweeks(); - (year - 1, prevlastweek) - } else { - let lastweek = year_flags.nisoweeks(); - if rawweek > lastweek { - // next year - (year + 1, 1) - } else { - (year, rawweek) - } - }; - let flags = YearFlags::from_year(year); - IsoWeek { ywf: (year << 10) | (week << 4) as i32 | i32::from(flags.0) } - } - - /// Returns the year number for this ISO week. - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate, Weekday}; - /// - /// let d = NaiveDate::from_isoywd_opt(2015, 1, Weekday::Mon).unwrap(); - /// assert_eq!(d.iso_week().year(), 2015); - /// ``` - /// - /// This year number might not match the calendar year number. - /// Continuing the example... - /// - /// ``` - /// # use chrono::{NaiveDate, Datelike, Weekday}; - /// # let d = NaiveDate::from_isoywd_opt(2015, 1, Weekday::Mon).unwrap(); - /// assert_eq!(d.year(), 2014); - /// assert_eq!(d, NaiveDate::from_ymd_opt(2014, 12, 29).unwrap()); - /// ``` - #[inline] - pub const fn year(&self) -> i32 { - self.ywf >> 10 - } - - /// Returns the ISO week number starting from 1. - /// - /// The return value ranges from 1 to 53. (The last week of year differs by years.) - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate, Weekday}; - /// - /// let d = NaiveDate::from_isoywd_opt(2015, 15, Weekday::Mon).unwrap(); - /// assert_eq!(d.iso_week().week(), 15); - /// ``` - #[inline] - pub const fn week(&self) -> u32 { - ((self.ywf >> 4) & 0x3f) as u32 - } - - /// Returns the ISO week number starting from 0. - /// - /// The return value ranges from 0 to 52. (The last week of year differs by years.) - /// - /// # Example - /// - /// ``` - /// use chrono::{Datelike, NaiveDate, Weekday}; - /// - /// let d = NaiveDate::from_isoywd_opt(2015, 15, Weekday::Mon).unwrap(); - /// assert_eq!(d.iso_week().week0(), 14); - /// ``` - #[inline] - pub const fn week0(&self) -> u32 { - ((self.ywf >> 4) & 0x3f) as u32 - 1 - } -} - -/// The `Debug` output of the ISO week `w` is the same as -/// [`d.format("%G-W%V")`](../format/strftime/index.html) -/// where `d` is any `NaiveDate` value in that week. -/// -/// # Example -/// -/// ``` -/// use chrono::{Datelike, NaiveDate}; -/// -/// assert_eq!( -/// format!("{:?}", NaiveDate::from_ymd_opt(2015, 9, 5).unwrap().iso_week()), -/// "2015-W36" -/// ); -/// assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(0, 1, 3).unwrap().iso_week()), "0000-W01"); -/// assert_eq!( -/// format!("{:?}", NaiveDate::from_ymd_opt(9999, 12, 31).unwrap().iso_week()), -/// "9999-W52" -/// ); -/// ``` -/// -/// ISO 8601 requires an explicit sign for years before 1 BCE or after 9999 CE. -/// -/// ``` -/// # use chrono::{NaiveDate, Datelike}; -/// assert_eq!(format!("{:?}", NaiveDate::from_ymd_opt(0, 1, 2).unwrap().iso_week()), "-0001-W52"); -/// assert_eq!( -/// format!("{:?}", NaiveDate::from_ymd_opt(10000, 12, 31).unwrap().iso_week()), -/// "+10000-W52" -/// ); -/// ``` -impl fmt::Debug for IsoWeek { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let year = self.year(); - let week = self.week(); - if (0..=9999).contains(&year) { - write!(f, "{year:04}-W{week:02}") - } else { - // ISO 8601 requires the explicit sign for out-of-range years - write!(f, "{year:+05}-W{week:02}") - } - } -} - -#[cfg(feature = "defmt")] -impl defmt::Format for IsoWeek { - fn format(&self, fmt: defmt::Formatter) { - let year = self.year(); - let week = self.week(); - if (0..=9999).contains(&year) { - defmt::write!(fmt, "{:04}-W{:02}", year, week) - } else { - // ISO 8601 requires the explicit sign for out-of-range years - let sign = ['+', '-'][(year < 0) as usize]; - defmt::write!(fmt, "{}{:05}-W{:02}", sign, year.abs(), week) - } - } -} - -#[cfg(test)] -mod tests { - #[cfg(feature = "rkyv-validation")] - use super::IsoWeek; - use crate::Datelike; - use crate::naive::date::{self, NaiveDate}; - - #[test] - fn test_iso_week_extremes() { - let minweek = NaiveDate::MIN.iso_week(); - let maxweek = NaiveDate::MAX.iso_week(); - - assert_eq!(minweek.year(), date::MIN_YEAR); - assert_eq!(minweek.week(), 1); - assert_eq!(minweek.week0(), 0); - #[cfg(feature = "alloc")] - assert_eq!(format!("{minweek:?}"), NaiveDate::MIN.format("%G-W%V").to_string()); - - assert_eq!(maxweek.year(), date::MAX_YEAR + 1); - assert_eq!(maxweek.week(), 1); - assert_eq!(maxweek.week0(), 0); - #[cfg(feature = "alloc")] - assert_eq!(format!("{maxweek:?}"), NaiveDate::MAX.format("%G-W%V").to_string()); - } - - #[test] - fn test_iso_week_equivalence_for_first_week() { - let monday = NaiveDate::from_ymd_opt(2024, 12, 30).unwrap(); - let friday = NaiveDate::from_ymd_opt(2025, 1, 3).unwrap(); - - assert_eq!(monday.iso_week(), friday.iso_week()); - } - - #[test] - fn test_iso_week_equivalence_for_last_week() { - let monday = NaiveDate::from_ymd_opt(2026, 12, 28).unwrap(); - let friday = NaiveDate::from_ymd_opt(2027, 1, 1).unwrap(); - - assert_eq!(monday.iso_week(), friday.iso_week()); - } - - #[test] - fn test_iso_week_ordering_for_first_week() { - let monday = NaiveDate::from_ymd_opt(2024, 12, 30).unwrap(); - let friday = NaiveDate::from_ymd_opt(2025, 1, 3).unwrap(); - - assert!(monday.iso_week() >= friday.iso_week()); - assert!(monday.iso_week() <= friday.iso_week()); - } - - #[test] - fn test_iso_week_ordering_for_last_week() { - let monday = NaiveDate::from_ymd_opt(2026, 12, 28).unwrap(); - let friday = NaiveDate::from_ymd_opt(2027, 1, 1).unwrap(); - - assert!(monday.iso_week() >= friday.iso_week()); - assert!(monday.iso_week() <= friday.iso_week()); - } - - #[test] - #[cfg(feature = "rkyv-validation")] - fn test_rkyv_validation() { - let minweek = NaiveDate::MIN.iso_week(); - let bytes = rkyv::to_bytes::<_, 4>(&minweek).unwrap(); - assert_eq!(rkyv::from_bytes::(&bytes).unwrap(), minweek); - - let maxweek = NaiveDate::MAX.iso_week(); - let bytes = rkyv::to_bytes::<_, 4>(&maxweek).unwrap(); - assert_eq!(rkyv::from_bytes::(&bytes).unwrap(), maxweek); - } -} diff --git a/chrono-0.4.44/src/naive/mod.rs b/chrono-0.4.44/src/naive/mod.rs deleted file mode 100644 index 4cc205595c..0000000000 --- a/chrono-0.4.44/src/naive/mod.rs +++ /dev/null @@ -1,340 +0,0 @@ -//! Date and time types unconcerned with timezones. -//! -//! They are primarily building blocks for other types -//! (e.g. [`TimeZone`](../offset/trait.TimeZone.html)), -//! but can be also used for the simpler date and time handling. - -use core::hash::{Hash, Hasher}; -use core::ops::RangeInclusive; - -use crate::Weekday; -use crate::expect; - -pub(crate) mod date; -pub(crate) mod datetime; -mod internals; -pub(crate) mod isoweek; -pub(crate) mod time; - -#[allow(deprecated)] -pub use self::date::{MAX_DATE, MIN_DATE}; -pub use self::date::{NaiveDate, NaiveDateDaysIterator, NaiveDateWeeksIterator}; -#[allow(deprecated)] -pub use self::datetime::{MAX_DATETIME, MIN_DATETIME, NaiveDateTime}; -pub use self::isoweek::IsoWeek; -pub use self::time::NaiveTime; - -#[cfg(feature = "__internal_bench")] -#[doc(hidden)] -pub use self::internals::YearFlags as __BenchYearFlags; - -/// A week represented by a [`NaiveDate`] and a [`Weekday`] which is the first -/// day of the week. -#[derive(Clone, Copy, Debug, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct NaiveWeek { - date: NaiveDate, - start: Weekday, -} - -impl NaiveWeek { - /// Create a new `NaiveWeek` - pub(crate) const fn new(date: NaiveDate, start: Weekday) -> Self { - Self { date, start } - } - - /// Returns a date representing the first day of the week. - /// - /// # Panics - /// - /// Panics if the first day of the week happens to fall just out of range of `NaiveDate` - /// (more than ca. 262,000 years away from common era). - /// - /// # Examples - /// - /// ``` - /// use chrono::{NaiveDate, Weekday}; - /// - /// let date = NaiveDate::from_ymd_opt(2022, 4, 18).unwrap(); - /// let week = date.week(Weekday::Mon); - /// assert!(week.first_day() <= date); - /// ``` - #[inline] - #[must_use] - #[track_caller] - pub const fn first_day(&self) -> NaiveDate { - expect(self.checked_first_day(), "first weekday out of range for `NaiveDate`") - } - - /// Returns a date representing the first day of the week or - /// `None` if the date is out of `NaiveDate`'s range - /// (more than ca. 262,000 years away from common era). - /// - /// # Examples - /// - /// ``` - /// use chrono::{NaiveDate, Weekday}; - /// - /// let date = NaiveDate::MIN; - /// let week = date.week(Weekday::Mon); - /// if let Some(first_day) = week.checked_first_day() { - /// assert!(first_day == date); - /// } else { - /// // error handling code - /// return; - /// }; - /// ``` - #[inline] - #[must_use] - pub const fn checked_first_day(&self) -> Option { - let start = self.start.num_days_from_monday() as i32; - let ref_day = self.date.weekday().num_days_from_monday() as i32; - // Calculate the number of days to subtract from `self.date`. - // Do not construct an intermediate date beyond `self.date`, because that may be out of - // range if `date` is close to `NaiveDate::MAX`. - let days = start - ref_day - if start > ref_day { 7 } else { 0 }; - self.date.add_days(days) - } - - /// Returns a date representing the last day of the week. - /// - /// # Panics - /// - /// Panics if the last day of the week happens to fall just out of range of `NaiveDate` - /// (more than ca. 262,000 years away from common era). - /// - /// # Examples - /// - /// ``` - /// use chrono::{NaiveDate, Weekday}; - /// - /// let date = NaiveDate::from_ymd_opt(2022, 4, 18).unwrap(); - /// let week = date.week(Weekday::Mon); - /// assert!(week.last_day() >= date); - /// ``` - #[inline] - #[must_use] - #[track_caller] - pub const fn last_day(&self) -> NaiveDate { - expect(self.checked_last_day(), "last weekday out of range for `NaiveDate`") - } - - /// Returns a date representing the last day of the week or - /// `None` if the date is out of `NaiveDate`'s range - /// (more than ca. 262,000 years away from common era). - /// - /// # Examples - /// - /// ``` - /// use chrono::{NaiveDate, Weekday}; - /// - /// let date = NaiveDate::MAX; - /// let week = date.week(Weekday::Mon); - /// if let Some(last_day) = week.checked_last_day() { - /// assert!(last_day == date); - /// } else { - /// // error handling code - /// return; - /// }; - /// ``` - #[inline] - #[must_use] - pub const fn checked_last_day(&self) -> Option { - let end = self.start.pred().num_days_from_monday() as i32; - let ref_day = self.date.weekday().num_days_from_monday() as i32; - // Calculate the number of days to add to `self.date`. - // Do not construct an intermediate date before `self.date` (like with `first_day()`), - // because that may be out of range if `date` is close to `NaiveDate::MIN`. - let days = end - ref_day + if end < ref_day { 7 } else { 0 }; - self.date.add_days(days) - } - - /// Returns a [`RangeInclusive`] representing the whole week bounded by - /// [first_day](NaiveWeek::first_day) and [last_day](NaiveWeek::last_day) functions. - /// - /// # Panics - /// - /// Panics if the either the first or last day of the week happens to fall just out of range of - /// `NaiveDate` (more than ca. 262,000 years away from common era). - /// - /// # Examples - /// - /// ``` - /// use chrono::{NaiveDate, Weekday}; - /// - /// let date = NaiveDate::from_ymd_opt(2022, 4, 18).unwrap(); - /// let week = date.week(Weekday::Mon); - /// let days = week.days(); - /// assert!(days.contains(&date)); - /// ``` - #[inline] - #[must_use] - #[track_caller] - pub const fn days(&self) -> RangeInclusive { - // `expect` doesn't work because `RangeInclusive` is not `Copy` - match self.checked_days() { - Some(val) => val, - None => panic!("{}", "first or last weekday is out of range for `NaiveDate`"), - } - } - - /// Returns an [`Option>`] representing the whole week bounded by - /// [checked_first_day](NaiveWeek::checked_first_day) and - /// [checked_last_day](NaiveWeek::checked_last_day) functions. - /// - /// Returns `None` if either of the boundaries are out of `NaiveDate`'s range - /// (more than ca. 262,000 years away from common era). - /// - /// - /// # Examples - /// - /// ``` - /// use chrono::{NaiveDate, Weekday}; - /// - /// let date = NaiveDate::MAX; - /// let week = date.week(Weekday::Mon); - /// let _days = match week.checked_days() { - /// Some(d) => d, - /// None => { - /// // error handling code - /// return; - /// } - /// }; - /// ``` - #[inline] - #[must_use] - pub const fn checked_days(&self) -> Option> { - match (self.checked_first_day(), self.checked_last_day()) { - (Some(first), Some(last)) => Some(first..=last), - (_, _) => None, - } - } -} - -impl PartialEq for NaiveWeek { - fn eq(&self, other: &Self) -> bool { - self.first_day() == other.first_day() - } -} - -impl Hash for NaiveWeek { - fn hash(&self, state: &mut H) { - self.first_day().hash(state); - } -} - -/// A duration in calendar days. -/// -/// This is useful because when using `TimeDelta` it is possible that adding `TimeDelta::days(1)` -/// doesn't increment the day value as expected due to it being a fixed number of seconds. This -/// difference applies only when dealing with `DateTime` data types and in other cases -/// `TimeDelta::days(n)` and `Days::new(n)` are equivalent. -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Days(pub(crate) u64); - -impl Days { - /// Construct a new `Days` from a number of days - pub const fn new(num: u64) -> Self { - Self(num) - } -} - -/// Serialization/Deserialization of `NaiveDateTime` in alternate formats -/// -/// The various modules in here are intended to be used with serde's [`with` annotation] to -/// serialize as something other than the default ISO 8601 format. -/// -/// [`with` annotation]: https://serde.rs/field-attrs.html#with -#[cfg(feature = "serde")] -pub mod serde { - pub use super::datetime::serde::*; -} - -#[cfg(test)] -mod test { - use crate::{NaiveDate, NaiveWeek, Weekday}; - use std::hash::{DefaultHasher, Hash, Hasher}; - #[test] - fn test_naiveweek() { - let date = NaiveDate::from_ymd_opt(2022, 5, 18).unwrap(); - let asserts = [ - (Weekday::Mon, "Mon 2022-05-16", "Sun 2022-05-22"), - (Weekday::Tue, "Tue 2022-05-17", "Mon 2022-05-23"), - (Weekday::Wed, "Wed 2022-05-18", "Tue 2022-05-24"), - (Weekday::Thu, "Thu 2022-05-12", "Wed 2022-05-18"), - (Weekday::Fri, "Fri 2022-05-13", "Thu 2022-05-19"), - (Weekday::Sat, "Sat 2022-05-14", "Fri 2022-05-20"), - (Weekday::Sun, "Sun 2022-05-15", "Sat 2022-05-21"), - ]; - for (start, first_day, last_day) in asserts { - let week = date.week(start); - let days = week.days(); - assert_eq!(Ok(week.first_day()), NaiveDate::parse_from_str(first_day, "%a %Y-%m-%d")); - assert_eq!(Ok(week.last_day()), NaiveDate::parse_from_str(last_day, "%a %Y-%m-%d")); - assert!(days.contains(&date)); - } - } - - #[test] - fn test_naiveweek_min_max() { - let date_max = NaiveDate::MAX; - assert!(date_max.week(Weekday::Mon).first_day() <= date_max); - let date_min = NaiveDate::MIN; - assert!(date_min.week(Weekday::Mon).last_day() >= date_min); - } - - #[test] - fn test_naiveweek_checked_no_panic() { - let date_max = NaiveDate::MAX; - if let Some(last) = date_max.week(Weekday::Mon).checked_last_day() { - assert!(last == date_max); - } - let date_min = NaiveDate::MIN; - if let Some(first) = date_min.week(Weekday::Mon).checked_first_day() { - assert!(first == date_min); - } - let _ = date_min.week(Weekday::Mon).checked_days(); - let _ = date_max.week(Weekday::Mon).checked_days(); - } - - #[test] - fn test_naiveweek_eq() { - let a = - NaiveWeek { date: NaiveDate::from_ymd_opt(2025, 4, 3).unwrap(), start: Weekday::Mon }; - let b = - NaiveWeek { date: NaiveDate::from_ymd_opt(2025, 4, 4).unwrap(), start: Weekday::Mon }; - assert_eq!(a, b); - - let c = - NaiveWeek { date: NaiveDate::from_ymd_opt(2025, 4, 3).unwrap(), start: Weekday::Sun }; - assert_ne!(a, c); - assert_ne!(b, c); - } - - #[test] - fn test_naiveweek_hash() { - let a = - NaiveWeek { date: NaiveDate::from_ymd_opt(2025, 4, 3).unwrap(), start: Weekday::Mon }; - let b = - NaiveWeek { date: NaiveDate::from_ymd_opt(2025, 4, 4).unwrap(), start: Weekday::Mon }; - let c = - NaiveWeek { date: NaiveDate::from_ymd_opt(2025, 4, 3).unwrap(), start: Weekday::Sun }; - - let mut hasher = DefaultHasher::default(); - a.hash(&mut hasher); - let a_hash = hasher.finish(); - - hasher = DefaultHasher::default(); - b.hash(&mut hasher); - let b_hash = hasher.finish(); - - hasher = DefaultHasher::default(); - c.hash(&mut hasher); - let c_hash = hasher.finish(); - - assert_eq!(a_hash, b_hash); - assert_ne!(b_hash, c_hash); - assert_ne!(a_hash, c_hash); - } -} diff --git a/chrono-0.4.44/src/naive/time/mod.rs b/chrono-0.4.44/src/naive/time/mod.rs deleted file mode 100644 index 5a35aa3844..0000000000 --- a/chrono-0.4.44/src/naive/time/mod.rs +++ /dev/null @@ -1,1668 +0,0 @@ -// This is a part of Chrono. -// See README.md and LICENSE.txt for details. - -//! ISO 8601 time without timezone. - -#[cfg(feature = "alloc")] -use core::borrow::Borrow; -use core::ops::{Add, AddAssign, Sub, SubAssign}; -use core::time::Duration; -use core::{fmt, str}; - -#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))] -use rkyv::{Archive, Deserialize, Serialize}; - -#[cfg(feature = "alloc")] -use crate::format::DelayedFormat; -use crate::format::{ - Fixed, Item, Numeric, Pad, ParseError, ParseResult, Parsed, StrftimeItems, parse, - parse_and_remainder, write_hundreds, -}; -use crate::{FixedOffset, TimeDelta, Timelike}; -use crate::{expect, try_opt}; - -#[cfg(feature = "serde")] -mod serde; - -#[cfg(test)] -mod tests; - -/// ISO 8601 time without timezone. -/// Allows for the nanosecond precision and optional leap second representation. -/// -/// # Leap Second Handling -/// -/// Since 1960s, the manmade atomic clock has been so accurate that -/// it is much more accurate than Earth's own motion. -/// It became desirable to define the civil time in terms of the atomic clock, -/// but that risks the desynchronization of the civil time from Earth. -/// To account for this, the designers of the Coordinated Universal Time (UTC) -/// made that the UTC should be kept within 0.9 seconds of the observed Earth-bound time. -/// When the mean solar day is longer than the ideal (86,400 seconds), -/// the error slowly accumulates and it is necessary to add a **leap second** -/// to slow the UTC down a bit. -/// (We may also remove a second to speed the UTC up a bit, but it never happened.) -/// The leap second, if any, follows 23:59:59 of June 30 or December 31 in the UTC. -/// -/// Fast forward to the 21st century, -/// we have seen 26 leap seconds from January 1972 to December 2015. -/// Yes, 26 seconds. Probably you can read this paragraph within 26 seconds. -/// But those 26 seconds, and possibly more in the future, are never predictable, -/// and whether to add a leap second or not is known only before 6 months. -/// Internet-based clocks (via NTP) do account for known leap seconds, -/// but the system API normally doesn't (and often can't, with no network connection) -/// and there is no reliable way to retrieve leap second information. -/// -/// Chrono does not try to accurately implement leap seconds; it is impossible. -/// Rather, **it allows for leap seconds but behaves as if there are *no other* leap seconds.** -/// Various operations will ignore any possible leap second(s) -/// except when any of the operands were actually leap seconds. -/// -/// If you cannot tolerate this behavior, -/// you must use a separate `TimeZone` for the International Atomic Time (TAI). -/// TAI is like UTC but has no leap seconds, and thus slightly differs from UTC. -/// Chrono does not yet provide such implementation, but it is planned. -/// -/// ## Representing Leap Seconds -/// -/// The leap second is indicated via fractional seconds more than 1 second. -/// This makes possible to treat a leap second as the prior non-leap second -/// if you don't care about sub-second accuracy. -/// You should use the proper formatting to get the raw leap second. -/// -/// All methods accepting fractional seconds will accept such values. -/// -/// ``` -/// use chrono::{NaiveDate, NaiveTime}; -/// -/// let t = NaiveTime::from_hms_milli_opt(8, 59, 59, 1_000).unwrap(); -/// -/// let dt1 = NaiveDate::from_ymd_opt(2015, 7, 1) -/// .unwrap() -/// .and_hms_micro_opt(8, 59, 59, 1_000_000) -/// .unwrap(); -/// -/// let dt2 = NaiveDate::from_ymd_opt(2015, 6, 30) -/// .unwrap() -/// .and_hms_nano_opt(23, 59, 59, 1_000_000_000) -/// .unwrap() -/// .and_utc(); -/// # let _ = (t, dt1, dt2); -/// ``` -/// -/// Note that the leap second can happen anytime given an appropriate time zone; -/// 2015-07-01 01:23:60 would be a proper leap second if UTC+01:24 had existed. -/// Practically speaking, though, by the time of the first leap second on 1972-06-30, -/// every time zone offset around the world has standardized to the 5-minute alignment. -/// -/// ## Date And Time Arithmetic -/// -/// As a concrete example, let's assume that `03:00:60` and `04:00:60` are leap seconds. -/// In reality, of course, leap seconds are separated by at least 6 months. -/// We will also use some intuitive concise notations for the explanation. -/// -/// `Time + TimeDelta` -/// (short for [`NaiveTime::overflowing_add_signed`](#method.overflowing_add_signed)): -/// -/// - `03:00:00 + 1s = 03:00:01`. -/// - `03:00:59 + 60s = 03:01:59`. -/// - `03:00:59 + 61s = 03:02:00`. -/// - `03:00:59 + 1s = 03:01:00`. -/// - `03:00:60 + 1s = 03:01:00`. -/// Note that the sum is identical to the previous. -/// - `03:00:60 + 60s = 03:01:59`. -/// - `03:00:60 + 61s = 03:02:00`. -/// - `03:00:60.1 + 0.8s = 03:00:60.9`. -/// -/// `Time - TimeDelta` -/// (short for [`NaiveTime::overflowing_sub_signed`](#method.overflowing_sub_signed)): -/// -/// - `03:00:00 - 1s = 02:59:59`. -/// - `03:01:00 - 1s = 03:00:59`. -/// - `03:01:00 - 60s = 03:00:00`. -/// - `03:00:60 - 60s = 03:00:00`. -/// Note that the result is identical to the previous. -/// - `03:00:60.7 - 0.4s = 03:00:60.3`. -/// - `03:00:60.7 - 0.9s = 03:00:59.8`. -/// -/// `Time - Time` -/// (short for [`NaiveTime::signed_duration_since`](#method.signed_duration_since)): -/// -/// - `04:00:00 - 03:00:00 = 3600s`. -/// - `03:01:00 - 03:00:00 = 60s`. -/// - `03:00:60 - 03:00:00 = 60s`. -/// Note that the difference is identical to the previous. -/// - `03:00:60.6 - 03:00:59.4 = 1.2s`. -/// - `03:01:00 - 03:00:59.8 = 0.2s`. -/// - `03:01:00 - 03:00:60.5 = 0.5s`. -/// Note that the difference is larger than the previous, -/// even though the leap second clearly follows the previous whole second. -/// - `04:00:60.9 - 03:00:60.1 = -/// (04:00:60.9 - 04:00:00) + (04:00:00 - 03:01:00) + (03:01:00 - 03:00:60.1) = -/// 60.9s + 3540s + 0.9s = 3601.8s`. -/// -/// In general, -/// -/// - `Time + TimeDelta` unconditionally equals to `TimeDelta + Time`. -/// -/// - `Time - TimeDelta` unconditionally equals to `Time + (-TimeDelta)`. -/// -/// - `Time1 - Time2` unconditionally equals to `-(Time2 - Time1)`. -/// -/// - Associativity does not generally hold, because -/// `(Time + TimeDelta1) - TimeDelta2` no longer equals to `Time + (TimeDelta1 - TimeDelta2)` -/// for two positive durations. -/// -/// - As a special case, `(Time + TimeDelta) - TimeDelta` also does not equal to `Time`. -/// -/// - If you can assume that all durations have the same sign, however, -/// then the associativity holds: -/// `(Time + TimeDelta1) + TimeDelta2` equals to `Time + (TimeDelta1 + TimeDelta2)` -/// for two positive durations. -/// -/// ## Reading And Writing Leap Seconds -/// -/// The "typical" leap seconds on the minute boundary are -/// correctly handled both in the formatting and parsing. -/// The leap second in the human-readable representation -/// will be represented as the second part being 60, as required by ISO 8601. -/// -/// ``` -/// use chrono::NaiveDate; -/// -/// let dt = NaiveDate::from_ymd_opt(2015, 6, 30) -/// .unwrap() -/// .and_hms_milli_opt(23, 59, 59, 1_000) -/// .unwrap() -/// .and_utc(); -/// assert_eq!(format!("{:?}", dt), "2015-06-30T23:59:60Z"); -/// ``` -/// -/// There are hypothetical leap seconds not on the minute boundary nevertheless supported by Chrono. -/// They are allowed for the sake of completeness and consistency; there were several "exotic" time -/// zone offsets with fractional minutes prior to UTC after all. -/// For such cases the human-readable representation is ambiguous and would be read back to the next -/// non-leap second. -/// -/// A `NaiveTime` with a leap second that is not on a minute boundary can only be created from a -/// [`DateTime`](crate::DateTime) with fractional minutes as offset, or using -/// [`Timelike::with_nanosecond()`]. -/// -/// ``` -/// use chrono::{FixedOffset, NaiveDate, TimeZone}; -/// -/// let paramaribo_pre1945 = FixedOffset::east_opt(-13236).unwrap(); // -03:40:36 -/// let leap_sec_2015 = -/// NaiveDate::from_ymd_opt(2015, 6, 30).unwrap().and_hms_milli_opt(23, 59, 59, 1_000).unwrap(); -/// let dt1 = paramaribo_pre1945.from_utc_datetime(&leap_sec_2015); -/// assert_eq!(format!("{:?}", dt1), "2015-06-30T20:19:24-03:40:36"); -/// assert_eq!(format!("{:?}", dt1.time()), "20:19:24"); -/// -/// let next_sec = NaiveDate::from_ymd_opt(2015, 7, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(); -/// let dt2 = paramaribo_pre1945.from_utc_datetime(&next_sec); -/// assert_eq!(format!("{:?}", dt2), "2015-06-30T20:19:24-03:40:36"); -/// assert_eq!(format!("{:?}", dt2.time()), "20:19:24"); -/// -/// assert!(dt1.time() != dt2.time()); -/// assert!(dt1.time().to_string() == dt2.time().to_string()); -/// ``` -/// -/// Since Chrono alone cannot determine any existence of leap seconds, -/// **there is absolutely no guarantee that the leap second read has actually happened**. -#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone)] -#[cfg_attr( - any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"), - derive(Archive, Deserialize, Serialize), - archive(compare(PartialEq, PartialOrd)), - archive_attr(derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)) -)] -#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] -pub struct NaiveTime { - secs: u32, - frac: u32, -} - -#[cfg(feature = "arbitrary")] -impl arbitrary::Arbitrary<'_> for NaiveTime { - fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result { - let mins = u.int_in_range(0..=1439)?; - let mut secs = u.int_in_range(0..=60)?; - let mut nano = u.int_in_range(0..=999_999_999)?; - if secs == 60 { - secs = 59; - nano += 1_000_000_000; - } - let time = NaiveTime::from_num_seconds_from_midnight_opt(mins * 60 + secs, nano) - .expect("Could not generate a valid chrono::NaiveTime. It looks like implementation of Arbitrary for NaiveTime is erroneous."); - Ok(time) - } -} - -impl NaiveTime { - /// Makes a new `NaiveTime` from hour, minute and second. - /// - /// No [leap second](#leap-second-handling) is allowed here; - /// use `NaiveTime::from_hms_*` methods with a subsecond parameter instead. - /// - /// # Panics - /// - /// Panics on invalid hour, minute and/or second. - #[deprecated(since = "0.4.23", note = "use `from_hms_opt()` instead")] - #[inline] - #[must_use] - pub const fn from_hms(hour: u32, min: u32, sec: u32) -> NaiveTime { - expect(NaiveTime::from_hms_opt(hour, min, sec), "invalid time") - } - - /// Makes a new `NaiveTime` from hour, minute and second. - /// - /// The millisecond part is allowed to exceed 1,000,000,000 in order to represent a - /// [leap second](#leap-second-handling), but only when `sec == 59`. - /// - /// # Errors - /// - /// Returns `None` on invalid hour, minute and/or second. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveTime; - /// - /// let from_hms_opt = NaiveTime::from_hms_opt; - /// - /// assert!(from_hms_opt(0, 0, 0).is_some()); - /// assert!(from_hms_opt(23, 59, 59).is_some()); - /// assert!(from_hms_opt(24, 0, 0).is_none()); - /// assert!(from_hms_opt(23, 60, 0).is_none()); - /// assert!(from_hms_opt(23, 59, 60).is_none()); - /// ``` - #[inline] - #[must_use] - pub const fn from_hms_opt(hour: u32, min: u32, sec: u32) -> Option { - NaiveTime::from_hms_nano_opt(hour, min, sec, 0) - } - - /// Makes a new `NaiveTime` from hour, minute, second and millisecond. - /// - /// The millisecond part can exceed 1,000 - /// in order to represent the [leap second](#leap-second-handling). - /// - /// # Panics - /// - /// Panics on invalid hour, minute, second and/or millisecond. - #[deprecated(since = "0.4.23", note = "use `from_hms_milli_opt()` instead")] - #[inline] - #[must_use] - pub const fn from_hms_milli(hour: u32, min: u32, sec: u32, milli: u32) -> NaiveTime { - expect(NaiveTime::from_hms_milli_opt(hour, min, sec, milli), "invalid time") - } - - /// Makes a new `NaiveTime` from hour, minute, second and millisecond. - /// - /// The millisecond part is allowed to exceed 1,000,000,000 in order to represent a - /// [leap second](#leap-second-handling), but only when `sec == 59`. - /// - /// # Errors - /// - /// Returns `None` on invalid hour, minute, second and/or millisecond. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveTime; - /// - /// let from_hmsm_opt = NaiveTime::from_hms_milli_opt; - /// - /// assert!(from_hmsm_opt(0, 0, 0, 0).is_some()); - /// assert!(from_hmsm_opt(23, 59, 59, 999).is_some()); - /// assert!(from_hmsm_opt(23, 59, 59, 1_999).is_some()); // a leap second after 23:59:59 - /// assert!(from_hmsm_opt(24, 0, 0, 0).is_none()); - /// assert!(from_hmsm_opt(23, 60, 0, 0).is_none()); - /// assert!(from_hmsm_opt(23, 59, 60, 0).is_none()); - /// assert!(from_hmsm_opt(23, 59, 59, 2_000).is_none()); - /// ``` - #[inline] - #[must_use] - pub const fn from_hms_milli_opt( - hour: u32, - min: u32, - sec: u32, - milli: u32, - ) -> Option { - let nano = try_opt!(milli.checked_mul(1_000_000)); - NaiveTime::from_hms_nano_opt(hour, min, sec, nano) - } - - /// Makes a new `NaiveTime` from hour, minute, second and microsecond. - /// - /// The microsecond part is allowed to exceed 1,000,000,000 in order to represent a - /// [leap second](#leap-second-handling), but only when `sec == 59`. - /// - /// # Panics - /// - /// Panics on invalid hour, minute, second and/or microsecond. - #[deprecated(since = "0.4.23", note = "use `from_hms_micro_opt()` instead")] - #[inline] - #[must_use] - pub const fn from_hms_micro(hour: u32, min: u32, sec: u32, micro: u32) -> NaiveTime { - expect(NaiveTime::from_hms_micro_opt(hour, min, sec, micro), "invalid time") - } - - /// Makes a new `NaiveTime` from hour, minute, second and microsecond. - /// - /// The microsecond part is allowed to exceed 1,000,000,000 in order to represent a - /// [leap second](#leap-second-handling), but only when `sec == 59`. - /// - /// # Errors - /// - /// Returns `None` on invalid hour, minute, second and/or microsecond. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveTime; - /// - /// let from_hmsu_opt = NaiveTime::from_hms_micro_opt; - /// - /// assert!(from_hmsu_opt(0, 0, 0, 0).is_some()); - /// assert!(from_hmsu_opt(23, 59, 59, 999_999).is_some()); - /// assert!(from_hmsu_opt(23, 59, 59, 1_999_999).is_some()); // a leap second after 23:59:59 - /// assert!(from_hmsu_opt(24, 0, 0, 0).is_none()); - /// assert!(from_hmsu_opt(23, 60, 0, 0).is_none()); - /// assert!(from_hmsu_opt(23, 59, 60, 0).is_none()); - /// assert!(from_hmsu_opt(23, 59, 59, 2_000_000).is_none()); - /// ``` - #[inline] - #[must_use] - pub const fn from_hms_micro_opt( - hour: u32, - min: u32, - sec: u32, - micro: u32, - ) -> Option { - let nano = try_opt!(micro.checked_mul(1_000)); - NaiveTime::from_hms_nano_opt(hour, min, sec, nano) - } - - /// Makes a new `NaiveTime` from hour, minute, second and nanosecond. - /// - /// The nanosecond part is allowed to exceed 1,000,000,000 in order to represent a - /// [leap second](#leap-second-handling), but only when `sec == 59`. - /// - /// # Panics - /// - /// Panics on invalid hour, minute, second and/or nanosecond. - #[deprecated(since = "0.4.23", note = "use `from_hms_nano_opt()` instead")] - #[inline] - #[must_use] - pub const fn from_hms_nano(hour: u32, min: u32, sec: u32, nano: u32) -> NaiveTime { - expect(NaiveTime::from_hms_nano_opt(hour, min, sec, nano), "invalid time") - } - - /// Makes a new `NaiveTime` from hour, minute, second and nanosecond. - /// - /// The nanosecond part is allowed to exceed 1,000,000,000 in order to represent a - /// [leap second](#leap-second-handling), but only when `sec == 59`. - /// - /// # Errors - /// - /// Returns `None` on invalid hour, minute, second and/or nanosecond. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveTime; - /// - /// let from_hmsn_opt = NaiveTime::from_hms_nano_opt; - /// - /// assert!(from_hmsn_opt(0, 0, 0, 0).is_some()); - /// assert!(from_hmsn_opt(23, 59, 59, 999_999_999).is_some()); - /// assert!(from_hmsn_opt(23, 59, 59, 1_999_999_999).is_some()); // a leap second after 23:59:59 - /// assert!(from_hmsn_opt(24, 0, 0, 0).is_none()); - /// assert!(from_hmsn_opt(23, 60, 0, 0).is_none()); - /// assert!(from_hmsn_opt(23, 59, 60, 0).is_none()); - /// assert!(from_hmsn_opt(23, 59, 59, 2_000_000_000).is_none()); - /// ``` - #[inline] - #[must_use] - pub const fn from_hms_nano_opt(hour: u32, min: u32, sec: u32, nano: u32) -> Option { - if (hour >= 24 || min >= 60 || sec >= 60) - || (nano >= 1_000_000_000 && sec != 59) - || nano >= 2_000_000_000 - { - return None; - } - let secs = hour * 3600 + min * 60 + sec; - Some(NaiveTime { secs, frac: nano }) - } - - /// Makes a new `NaiveTime` from the number of seconds since midnight and nanosecond. - /// - /// The nanosecond part is allowed to exceed 1,000,000,000 in order to represent a - /// [leap second](#leap-second-handling), but only when `secs % 60 == 59`. - /// - /// # Panics - /// - /// Panics on invalid number of seconds and/or nanosecond. - #[deprecated(since = "0.4.23", note = "use `from_num_seconds_from_midnight_opt()` instead")] - #[inline] - #[must_use] - pub const fn from_num_seconds_from_midnight(secs: u32, nano: u32) -> NaiveTime { - expect(NaiveTime::from_num_seconds_from_midnight_opt(secs, nano), "invalid time") - } - - /// Makes a new `NaiveTime` from the number of seconds since midnight and nanosecond. - /// - /// The nanosecond part is allowed to exceed 1,000,000,000 in order to represent a - /// [leap second](#leap-second-handling), but only when `secs % 60 == 59`. - /// - /// # Errors - /// - /// Returns `None` on invalid number of seconds and/or nanosecond. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveTime; - /// - /// let from_nsecs_opt = NaiveTime::from_num_seconds_from_midnight_opt; - /// - /// assert!(from_nsecs_opt(0, 0).is_some()); - /// assert!(from_nsecs_opt(86399, 999_999_999).is_some()); - /// assert!(from_nsecs_opt(86399, 1_999_999_999).is_some()); // a leap second after 23:59:59 - /// assert!(from_nsecs_opt(86_400, 0).is_none()); - /// assert!(from_nsecs_opt(86399, 2_000_000_000).is_none()); - /// ``` - #[inline] - #[must_use] - pub const fn from_num_seconds_from_midnight_opt(secs: u32, nano: u32) -> Option { - if secs >= 86_400 || nano >= 2_000_000_000 || (nano >= 1_000_000_000 && secs % 60 != 59) { - return None; - } - Some(NaiveTime { secs, frac: nano }) - } - - /// Parses a string with the specified format string and returns a new `NaiveTime`. - /// See the [`format::strftime` module](crate::format::strftime) - /// on the supported escape sequences. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveTime; - /// - /// let parse_from_str = NaiveTime::parse_from_str; - /// - /// assert_eq!( - /// parse_from_str("23:56:04", "%H:%M:%S"), - /// Ok(NaiveTime::from_hms_opt(23, 56, 4).unwrap()) - /// ); - /// assert_eq!( - /// parse_from_str("pm012345.6789", "%p%I%M%S%.f"), - /// Ok(NaiveTime::from_hms_micro_opt(13, 23, 45, 678_900).unwrap()) - /// ); - /// ``` - /// - /// Date and offset is ignored for the purpose of parsing. - /// - /// ``` - /// # use chrono::NaiveTime; - /// # let parse_from_str = NaiveTime::parse_from_str; - /// assert_eq!( - /// parse_from_str("2014-5-17T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), - /// Ok(NaiveTime::from_hms_opt(12, 34, 56).unwrap()) - /// ); - /// ``` - /// - /// [Leap seconds](#leap-second-handling) are correctly handled by - /// treating any time of the form `hh:mm:60` as a leap second. - /// (This equally applies to the formatting, so the round trip is possible.) - /// - /// ``` - /// # use chrono::NaiveTime; - /// # let parse_from_str = NaiveTime::parse_from_str; - /// assert_eq!( - /// parse_from_str("08:59:60.123", "%H:%M:%S%.f"), - /// Ok(NaiveTime::from_hms_milli_opt(8, 59, 59, 1_123).unwrap()) - /// ); - /// ``` - /// - /// Missing seconds are assumed to be zero, - /// but out-of-bound times or insufficient fields are errors otherwise. - /// - /// ``` - /// # use chrono::NaiveTime; - /// # let parse_from_str = NaiveTime::parse_from_str; - /// assert_eq!(parse_from_str("7:15", "%H:%M"), Ok(NaiveTime::from_hms_opt(7, 15, 0).unwrap())); - /// - /// assert!(parse_from_str("04m33s", "%Mm%Ss").is_err()); - /// assert!(parse_from_str("12", "%H").is_err()); - /// assert!(parse_from_str("17:60", "%H:%M").is_err()); - /// assert!(parse_from_str("24:00:00", "%H:%M:%S").is_err()); - /// ``` - /// - /// All parsed fields should be consistent to each other, otherwise it's an error. - /// Here `%H` is for 24-hour clocks, unlike `%I`, - /// and thus can be independently determined without AM/PM. - /// - /// ``` - /// # use chrono::NaiveTime; - /// # let parse_from_str = NaiveTime::parse_from_str; - /// assert!(parse_from_str("13:07 AM", "%H:%M %p").is_err()); - /// ``` - pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult { - let mut parsed = Parsed::new(); - parse(&mut parsed, s, StrftimeItems::new(fmt))?; - parsed.to_naive_time() - } - - /// Parses a string from a user-specified format into a new `NaiveTime` value, and a slice with - /// the remaining portion of the string. - /// See the [`format::strftime` module](crate::format::strftime) - /// on the supported escape sequences. - /// - /// Similar to [`parse_from_str`](#method.parse_from_str). - /// - /// # Example - /// - /// ```rust - /// # use chrono::{NaiveTime}; - /// let (time, remainder) = - /// NaiveTime::parse_and_remainder("3h4m33s trailing text", "%-Hh%-Mm%-Ss").unwrap(); - /// assert_eq!(time, NaiveTime::from_hms_opt(3, 4, 33).unwrap()); - /// assert_eq!(remainder, " trailing text"); - /// ``` - pub fn parse_and_remainder<'a>(s: &'a str, fmt: &str) -> ParseResult<(NaiveTime, &'a str)> { - let mut parsed = Parsed::new(); - let remainder = parse_and_remainder(&mut parsed, s, StrftimeItems::new(fmt))?; - parsed.to_naive_time().map(|t| (t, remainder)) - } - - /// Adds given `TimeDelta` to the current time, and also returns the number of *seconds* - /// in the integral number of days ignored from the addition. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveTime, TimeDelta}; - /// - /// let from_hms = |h, m, s| NaiveTime::from_hms_opt(h, m, s).unwrap(); - /// - /// assert_eq!( - /// from_hms(3, 4, 5).overflowing_add_signed(TimeDelta::try_hours(11).unwrap()), - /// (from_hms(14, 4, 5), 0) - /// ); - /// assert_eq!( - /// from_hms(3, 4, 5).overflowing_add_signed(TimeDelta::try_hours(23).unwrap()), - /// (from_hms(2, 4, 5), 86_400) - /// ); - /// assert_eq!( - /// from_hms(3, 4, 5).overflowing_add_signed(TimeDelta::try_hours(-7).unwrap()), - /// (from_hms(20, 4, 5), -86_400) - /// ); - /// ``` - #[must_use] - pub const fn overflowing_add_signed(&self, rhs: TimeDelta) -> (NaiveTime, i64) { - let mut secs = self.secs as i64; - let mut frac = self.frac as i32; - let secs_to_add = rhs.num_seconds(); - let frac_to_add = rhs.subsec_nanos(); - - // Check if `self` is a leap second and adding `rhs` would escape that leap second. - // If that is the case, update `frac` and `secs` to involve no leap second. - // If it stays within the leap second or the second before, and only adds a fractional - // second, just do that and return (this way the rest of the code can ignore leap seconds). - if frac >= 1_000_000_000 { - // check below is adjusted to not overflow an i32: `frac + frac_to_add >= 2_000_000_000` - if secs_to_add > 0 || (frac_to_add > 0 && frac >= 2_000_000_000 - frac_to_add) { - frac -= 1_000_000_000; - } else if secs_to_add < 0 { - frac -= 1_000_000_000; - secs += 1; - } else { - return (NaiveTime { secs: self.secs, frac: (frac + frac_to_add) as u32 }, 0); - } - } - - let mut secs = secs + secs_to_add; - frac += frac_to_add; - - if frac < 0 { - frac += 1_000_000_000; - secs -= 1; - } else if frac >= 1_000_000_000 { - frac -= 1_000_000_000; - secs += 1; - } - - let secs_in_day = secs.rem_euclid(86_400); - let remaining = secs - secs_in_day; - (NaiveTime { secs: secs_in_day as u32, frac: frac as u32 }, remaining) - } - - /// Subtracts given `TimeDelta` from the current time, and also returns the number of *seconds* - /// in the integral number of days ignored from the subtraction. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveTime, TimeDelta}; - /// - /// let from_hms = |h, m, s| NaiveTime::from_hms_opt(h, m, s).unwrap(); - /// - /// assert_eq!( - /// from_hms(3, 4, 5).overflowing_sub_signed(TimeDelta::try_hours(2).unwrap()), - /// (from_hms(1, 4, 5), 0) - /// ); - /// assert_eq!( - /// from_hms(3, 4, 5).overflowing_sub_signed(TimeDelta::try_hours(17).unwrap()), - /// (from_hms(10, 4, 5), 86_400) - /// ); - /// assert_eq!( - /// from_hms(3, 4, 5).overflowing_sub_signed(TimeDelta::try_hours(-22).unwrap()), - /// (from_hms(1, 4, 5), -86_400) - /// ); - /// ``` - #[inline] - #[must_use] - pub const fn overflowing_sub_signed(&self, rhs: TimeDelta) -> (NaiveTime, i64) { - let (time, rhs) = self.overflowing_add_signed(rhs.neg()); - (time, -rhs) // safe to negate, rhs is within +/- (2^63 / 1000) - } - - /// Subtracts another `NaiveTime` from the current time. - /// Returns a `TimeDelta` within +/- 1 day. - /// This does not overflow or underflow at all. - /// - /// As a part of Chrono's [leap second handling](#leap-second-handling), - /// the subtraction assumes that **there is no leap second ever**, - /// except when any of the `NaiveTime`s themselves represents a leap second - /// in which case the assumption becomes that - /// **there are exactly one (or two) leap second(s) ever**. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveTime, TimeDelta}; - /// - /// let from_hmsm = |h, m, s, milli| NaiveTime::from_hms_milli_opt(h, m, s, milli).unwrap(); - /// let since = NaiveTime::signed_duration_since; - /// - /// assert_eq!(since(from_hmsm(3, 5, 7, 900), from_hmsm(3, 5, 7, 900)), TimeDelta::zero()); - /// assert_eq!( - /// since(from_hmsm(3, 5, 7, 900), from_hmsm(3, 5, 7, 875)), - /// TimeDelta::try_milliseconds(25).unwrap() - /// ); - /// assert_eq!( - /// since(from_hmsm(3, 5, 7, 900), from_hmsm(3, 5, 6, 925)), - /// TimeDelta::try_milliseconds(975).unwrap() - /// ); - /// assert_eq!( - /// since(from_hmsm(3, 5, 7, 900), from_hmsm(3, 5, 0, 900)), - /// TimeDelta::try_seconds(7).unwrap() - /// ); - /// assert_eq!( - /// since(from_hmsm(3, 5, 7, 900), from_hmsm(3, 0, 7, 900)), - /// TimeDelta::try_seconds(5 * 60).unwrap() - /// ); - /// assert_eq!( - /// since(from_hmsm(3, 5, 7, 900), from_hmsm(0, 5, 7, 900)), - /// TimeDelta::try_seconds(3 * 3600).unwrap() - /// ); - /// assert_eq!( - /// since(from_hmsm(3, 5, 7, 900), from_hmsm(4, 5, 7, 900)), - /// TimeDelta::try_seconds(-3600).unwrap() - /// ); - /// assert_eq!( - /// since(from_hmsm(3, 5, 7, 900), from_hmsm(2, 4, 6, 800)), - /// TimeDelta::try_seconds(3600 + 60 + 1).unwrap() + TimeDelta::try_milliseconds(100).unwrap() - /// ); - /// ``` - /// - /// Leap seconds are handled, but the subtraction assumes that - /// there were no other leap seconds happened. - /// - /// ``` - /// # use chrono::{TimeDelta, NaiveTime}; - /// # let from_hmsm = |h, m, s, milli| { NaiveTime::from_hms_milli_opt(h, m, s, milli).unwrap() }; - /// # let since = NaiveTime::signed_duration_since; - /// assert_eq!(since(from_hmsm(3, 0, 59, 1_000), from_hmsm(3, 0, 59, 0)), - /// TimeDelta::try_seconds(1).unwrap()); - /// assert_eq!(since(from_hmsm(3, 0, 59, 1_500), from_hmsm(3, 0, 59, 0)), - /// TimeDelta::try_milliseconds(1500).unwrap()); - /// assert_eq!(since(from_hmsm(3, 0, 59, 1_000), from_hmsm(3, 0, 0, 0)), - /// TimeDelta::try_seconds(60).unwrap()); - /// assert_eq!(since(from_hmsm(3, 0, 0, 0), from_hmsm(2, 59, 59, 1_000)), - /// TimeDelta::try_seconds(1).unwrap()); - /// assert_eq!(since(from_hmsm(3, 0, 59, 1_000), from_hmsm(2, 59, 59, 1_000)), - /// TimeDelta::try_seconds(61).unwrap()); - /// ``` - #[must_use] - pub const fn signed_duration_since(self, rhs: NaiveTime) -> TimeDelta { - // | | :leap| | | | | | | :leap| | - // | | : | | | | | | | : | | - // ----+----+-----*---+----+----+----+----+----+----+-------*-+----+---- - // | `rhs` | | `self` - // |======================================>| | - // | | `self.secs - rhs.secs` |`self.frac` - // |====>| | |======>| - // `rhs.frac`|========================================>| - // | | | `self - rhs` | | - - let mut secs = self.secs as i64 - rhs.secs as i64; - let frac = self.frac as i64 - rhs.frac as i64; - - // `secs` may contain a leap second yet to be counted - if self.secs > rhs.secs && rhs.frac >= 1_000_000_000 { - secs += 1; - } else if self.secs < rhs.secs && self.frac >= 1_000_000_000 { - secs -= 1; - } - - let secs_from_frac = frac.div_euclid(1_000_000_000); - let frac = frac.rem_euclid(1_000_000_000) as u32; - - expect(TimeDelta::new(secs + secs_from_frac, frac), "must be in range") - } - - /// Adds given `FixedOffset` to the current time, and returns the number of days that should be - /// added to a date as a result of the offset (either `-1`, `0`, or `1` because the offset is - /// always less than 24h). - /// - /// This method is similar to [`overflowing_add_signed`](#method.overflowing_add_signed), but - /// preserves leap seconds. - pub(super) const fn overflowing_add_offset(&self, offset: FixedOffset) -> (NaiveTime, i32) { - let secs = self.secs as i32 + offset.local_minus_utc(); - let days = secs.div_euclid(86_400); - let secs = secs.rem_euclid(86_400); - (NaiveTime { secs: secs as u32, frac: self.frac }, days) - } - - /// Subtracts given `FixedOffset` from the current time, and returns the number of days that - /// should be added to a date as a result of the offset (either `-1`, `0`, or `1` because the - /// offset is always less than 24h). - /// - /// This method is similar to [`overflowing_sub_signed`](#method.overflowing_sub_signed), but - /// preserves leap seconds. - pub(super) const fn overflowing_sub_offset(&self, offset: FixedOffset) -> (NaiveTime, i32) { - let secs = self.secs as i32 - offset.local_minus_utc(); - let days = secs.div_euclid(86_400); - let secs = secs.rem_euclid(86_400); - (NaiveTime { secs: secs as u32, frac: self.frac }, days) - } - - /// Formats the time with the specified formatting items. - /// Otherwise it is the same as the ordinary [`format`](#method.format) method. - /// - /// The `Iterator` of items should be `Clone`able, - /// since the resulting `DelayedFormat` value may be formatted multiple times. - /// - /// # Example - /// - /// ``` - /// use chrono::format::strftime::StrftimeItems; - /// use chrono::NaiveTime; - /// - /// let fmt = StrftimeItems::new("%H:%M:%S"); - /// let t = NaiveTime::from_hms_opt(23, 56, 4).unwrap(); - /// assert_eq!(t.format_with_items(fmt.clone()).to_string(), "23:56:04"); - /// assert_eq!(t.format("%H:%M:%S").to_string(), "23:56:04"); - /// ``` - /// - /// The resulting `DelayedFormat` can be formatted directly via the `Display` trait. - /// - /// ``` - /// # use chrono::NaiveTime; - /// # use chrono::format::strftime::StrftimeItems; - /// # let fmt = StrftimeItems::new("%H:%M:%S").clone(); - /// # let t = NaiveTime::from_hms_opt(23, 56, 4).unwrap(); - /// assert_eq!(format!("{}", t.format_with_items(fmt)), "23:56:04"); - /// ``` - #[cfg(feature = "alloc")] - #[inline] - #[must_use] - pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat - where - I: Iterator + Clone, - B: Borrow>, - { - DelayedFormat::new(None, Some(*self), items) - } - - /// Formats the time with the specified format string. - /// See the [`format::strftime` module](crate::format::strftime) - /// on the supported escape sequences. - /// - /// This returns a `DelayedFormat`, - /// which gets converted to a string only when actual formatting happens. - /// You may use the `to_string` method to get a `String`, - /// or just feed it into `print!` and other formatting macros. - /// (In this way it avoids the redundant memory allocation.) - /// - /// A wrong format string does *not* issue an error immediately. - /// Rather, converting or formatting the `DelayedFormat` fails. - /// You are recommended to immediately use `DelayedFormat` for this reason. - /// - /// # Example - /// - /// ``` - /// use chrono::NaiveTime; - /// - /// let t = NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap(); - /// assert_eq!(t.format("%H:%M:%S").to_string(), "23:56:04"); - /// assert_eq!(t.format("%H:%M:%S%.6f").to_string(), "23:56:04.012345"); - /// assert_eq!(t.format("%-I:%M %p").to_string(), "11:56 PM"); - /// ``` - /// - /// The resulting `DelayedFormat` can be formatted directly via the `Display` trait. - /// - /// ``` - /// # use chrono::NaiveTime; - /// # let t = NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap(); - /// assert_eq!(format!("{}", t.format("%H:%M:%S")), "23:56:04"); - /// assert_eq!(format!("{}", t.format("%H:%M:%S%.6f")), "23:56:04.012345"); - /// assert_eq!(format!("{}", t.format("%-I:%M %p")), "11:56 PM"); - /// ``` - #[cfg(feature = "alloc")] - #[inline] - #[must_use] - pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat> { - self.format_with_items(StrftimeItems::new(fmt)) - } - - /// Returns a triple of the hour, minute and second numbers. - pub(crate) fn hms(&self) -> (u32, u32, u32) { - let sec = self.secs % 60; - let mins = self.secs / 60; - let min = mins % 60; - let hour = mins / 60; - (hour, min, sec) - } - - /// Returns the number of non-leap seconds past the last midnight. - // This duplicates `Timelike::num_seconds_from_midnight()`, because trait methods can't be const - // yet. - #[inline] - pub(crate) const fn num_seconds_from_midnight(&self) -> u32 { - self.secs - } - - /// Returns the number of nanoseconds since the whole non-leap second. - // This duplicates `Timelike::nanosecond()`, because trait methods can't be const yet. - #[inline] - pub(crate) const fn nanosecond(&self) -> u32 { - self.frac - } - - /// The earliest possible `NaiveTime` - pub const MIN: Self = Self { secs: 0, frac: 0 }; - pub(super) const MAX: Self = Self { secs: 23 * 3600 + 59 * 60 + 59, frac: 999_999_999 }; -} - -impl Timelike for NaiveTime { - /// Returns the hour number from 0 to 23. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveTime, Timelike}; - /// - /// assert_eq!(NaiveTime::from_hms_opt(0, 0, 0).unwrap().hour(), 0); - /// assert_eq!(NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap().hour(), 23); - /// ``` - #[inline] - fn hour(&self) -> u32 { - self.hms().0 - } - - /// Returns the minute number from 0 to 59. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveTime, Timelike}; - /// - /// assert_eq!(NaiveTime::from_hms_opt(0, 0, 0).unwrap().minute(), 0); - /// assert_eq!(NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap().minute(), 56); - /// ``` - #[inline] - fn minute(&self) -> u32 { - self.hms().1 - } - - /// Returns the second number from 0 to 59. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveTime, Timelike}; - /// - /// assert_eq!(NaiveTime::from_hms_opt(0, 0, 0).unwrap().second(), 0); - /// assert_eq!(NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap().second(), 4); - /// ``` - /// - /// This method never returns 60 even when it is a leap second. - /// ([Why?](#leap-second-handling)) - /// Use the proper [formatting method](#method.format) to get a human-readable representation. - /// - /// ``` - /// # #[cfg(feature = "alloc")] { - /// # use chrono::{NaiveTime, Timelike}; - /// let leap = NaiveTime::from_hms_milli_opt(23, 59, 59, 1_000).unwrap(); - /// assert_eq!(leap.second(), 59); - /// assert_eq!(leap.format("%H:%M:%S").to_string(), "23:59:60"); - /// # } - /// ``` - #[inline] - fn second(&self) -> u32 { - self.hms().2 - } - - /// Returns the number of nanoseconds since the whole non-leap second. - /// The range from 1,000,000,000 to 1,999,999,999 represents - /// the [leap second](#leap-second-handling). - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveTime, Timelike}; - /// - /// assert_eq!(NaiveTime::from_hms_opt(0, 0, 0).unwrap().nanosecond(), 0); - /// assert_eq!( - /// NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap().nanosecond(), - /// 12_345_678 - /// ); - /// ``` - /// - /// Leap seconds may have seemingly out-of-range return values. - /// You can reduce the range with `time.nanosecond() % 1_000_000_000`, or - /// use the proper [formatting method](#method.format) to get a human-readable representation. - /// - /// ``` - /// # #[cfg(feature = "alloc")] { - /// # use chrono::{NaiveTime, Timelike}; - /// let leap = NaiveTime::from_hms_milli_opt(23, 59, 59, 1_000).unwrap(); - /// assert_eq!(leap.nanosecond(), 1_000_000_000); - /// assert_eq!(leap.format("%H:%M:%S%.9f").to_string(), "23:59:60.000000000"); - /// # } - /// ``` - #[inline] - fn nanosecond(&self) -> u32 { - self.frac - } - - /// Makes a new `NaiveTime` with the hour number changed. - /// - /// # Errors - /// - /// Returns `None` if the value for `hour` is invalid. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveTime, Timelike}; - /// - /// let dt = NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap(); - /// assert_eq!(dt.with_hour(7), Some(NaiveTime::from_hms_nano_opt(7, 56, 4, 12_345_678).unwrap())); - /// assert_eq!(dt.with_hour(24), None); - /// ``` - #[inline] - fn with_hour(&self, hour: u32) -> Option { - if hour >= 24 { - return None; - } - let secs = hour * 3600 + self.secs % 3600; - Some(NaiveTime { secs, ..*self }) - } - - /// Makes a new `NaiveTime` with the minute number changed. - /// - /// # Errors - /// - /// Returns `None` if the value for `minute` is invalid. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveTime, Timelike}; - /// - /// let dt = NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap(); - /// assert_eq!( - /// dt.with_minute(45), - /// Some(NaiveTime::from_hms_nano_opt(23, 45, 4, 12_345_678).unwrap()) - /// ); - /// assert_eq!(dt.with_minute(60), None); - /// ``` - #[inline] - fn with_minute(&self, min: u32) -> Option { - if min >= 60 { - return None; - } - let secs = self.secs / 3600 * 3600 + min * 60 + self.secs % 60; - Some(NaiveTime { secs, ..*self }) - } - - /// Makes a new `NaiveTime` with the second number changed. - /// - /// As with the [`second`](#method.second) method, - /// the input range is restricted to 0 through 59. - /// - /// # Errors - /// - /// Returns `None` if the value for `second` is invalid. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveTime, Timelike}; - /// - /// let dt = NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap(); - /// assert_eq!( - /// dt.with_second(17), - /// Some(NaiveTime::from_hms_nano_opt(23, 56, 17, 12_345_678).unwrap()) - /// ); - /// assert_eq!(dt.with_second(60), None); - /// ``` - #[inline] - fn with_second(&self, sec: u32) -> Option { - if sec >= 60 { - return None; - } - let secs = self.secs / 60 * 60 + sec; - Some(NaiveTime { secs, ..*self }) - } - - /// Makes a new `NaiveTime` with nanoseconds since the whole non-leap second changed. - /// - /// As with the [`nanosecond`](#method.nanosecond) method, - /// the input range can exceed 1,000,000,000 for leap seconds. - /// - /// # Errors - /// - /// Returns `None` if `nanosecond >= 2,000,000,000`. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveTime, Timelike}; - /// - /// let dt = NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap(); - /// assert_eq!( - /// dt.with_nanosecond(333_333_333), - /// Some(NaiveTime::from_hms_nano_opt(23, 56, 4, 333_333_333).unwrap()) - /// ); - /// assert_eq!(dt.with_nanosecond(2_000_000_000), None); - /// ``` - /// - /// Leap seconds can theoretically follow *any* whole second. - /// The following would be a proper leap second at the time zone offset of UTC-00:03:57 - /// (there are several historical examples comparable to this "non-sense" offset), - /// and therefore is allowed. - /// - /// ``` - /// # use chrono::{NaiveTime, Timelike}; - /// let dt = NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap(); - /// let strange_leap_second = dt.with_nanosecond(1_333_333_333).unwrap(); - /// assert_eq!(strange_leap_second.nanosecond(), 1_333_333_333); - /// ``` - #[inline] - fn with_nanosecond(&self, nano: u32) -> Option { - if nano >= 2_000_000_000 { - return None; - } - Some(NaiveTime { frac: nano, ..*self }) - } - - /// Returns the number of non-leap seconds past the last midnight. - /// - /// # Example - /// - /// ``` - /// use chrono::{NaiveTime, Timelike}; - /// - /// assert_eq!(NaiveTime::from_hms_opt(1, 2, 3).unwrap().num_seconds_from_midnight(), 3723); - /// assert_eq!( - /// NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap().num_seconds_from_midnight(), - /// 86164 - /// ); - /// assert_eq!( - /// NaiveTime::from_hms_milli_opt(23, 59, 59, 1_000).unwrap().num_seconds_from_midnight(), - /// 86399 - /// ); - /// ``` - #[inline] - fn num_seconds_from_midnight(&self) -> u32 { - self.secs // do not repeat the calculation! - } -} - -/// Add `TimeDelta` to `NaiveTime`. -/// -/// This wraps around and never overflows or underflows. -/// In particular the addition ignores integral number of days. -/// -/// As a part of Chrono's [leap second handling], the addition assumes that **there is no leap -/// second ever**, except when the `NaiveTime` itself represents a leap second in which case the -/// assumption becomes that **there is exactly a single leap second ever**. -/// -/// # Example -/// -/// ``` -/// use chrono::{NaiveTime, TimeDelta}; -/// -/// let from_hmsm = |h, m, s, milli| NaiveTime::from_hms_milli_opt(h, m, s, milli).unwrap(); -/// -/// assert_eq!(from_hmsm(3, 5, 7, 0) + TimeDelta::zero(), from_hmsm(3, 5, 7, 0)); -/// assert_eq!(from_hmsm(3, 5, 7, 0) + TimeDelta::try_seconds(1).unwrap(), from_hmsm(3, 5, 8, 0)); -/// assert_eq!(from_hmsm(3, 5, 7, 0) + TimeDelta::try_seconds(-1).unwrap(), from_hmsm(3, 5, 6, 0)); -/// assert_eq!( -/// from_hmsm(3, 5, 7, 0) + TimeDelta::try_seconds(60 + 4).unwrap(), -/// from_hmsm(3, 6, 11, 0) -/// ); -/// assert_eq!( -/// from_hmsm(3, 5, 7, 0) + TimeDelta::try_seconds(7 * 60 * 60 - 6 * 60).unwrap(), -/// from_hmsm(9, 59, 7, 0) -/// ); -/// assert_eq!( -/// from_hmsm(3, 5, 7, 0) + TimeDelta::try_milliseconds(80).unwrap(), -/// from_hmsm(3, 5, 7, 80) -/// ); -/// assert_eq!( -/// from_hmsm(3, 5, 7, 950) + TimeDelta::try_milliseconds(280).unwrap(), -/// from_hmsm(3, 5, 8, 230) -/// ); -/// assert_eq!( -/// from_hmsm(3, 5, 7, 950) + TimeDelta::try_milliseconds(-980).unwrap(), -/// from_hmsm(3, 5, 6, 970) -/// ); -/// ``` -/// -/// The addition wraps around. -/// -/// ``` -/// # use chrono::{TimeDelta, NaiveTime}; -/// # let from_hmsm = |h, m, s, milli| { NaiveTime::from_hms_milli_opt(h, m, s, milli).unwrap() }; -/// assert_eq!(from_hmsm(3, 5, 7, 0) + TimeDelta::try_seconds(22*60*60).unwrap(), from_hmsm(1, 5, 7, 0)); -/// assert_eq!(from_hmsm(3, 5, 7, 0) + TimeDelta::try_seconds(-8*60*60).unwrap(), from_hmsm(19, 5, 7, 0)); -/// assert_eq!(from_hmsm(3, 5, 7, 0) + TimeDelta::try_days(800).unwrap(), from_hmsm(3, 5, 7, 0)); -/// ``` -/// -/// Leap seconds are handled, but the addition assumes that it is the only leap second happened. -/// -/// ``` -/// # use chrono::{TimeDelta, NaiveTime}; -/// # let from_hmsm = |h, m, s, milli| { NaiveTime::from_hms_milli_opt(h, m, s, milli).unwrap() }; -/// let leap = from_hmsm(3, 5, 59, 1_300); -/// assert_eq!(leap + TimeDelta::zero(), from_hmsm(3, 5, 59, 1_300)); -/// assert_eq!(leap + TimeDelta::try_milliseconds(-500).unwrap(), from_hmsm(3, 5, 59, 800)); -/// assert_eq!(leap + TimeDelta::try_milliseconds(500).unwrap(), from_hmsm(3, 5, 59, 1_800)); -/// assert_eq!(leap + TimeDelta::try_milliseconds(800).unwrap(), from_hmsm(3, 6, 0, 100)); -/// assert_eq!(leap + TimeDelta::try_seconds(10).unwrap(), from_hmsm(3, 6, 9, 300)); -/// assert_eq!(leap + TimeDelta::try_seconds(-10).unwrap(), from_hmsm(3, 5, 50, 300)); -/// assert_eq!(leap + TimeDelta::try_days(1).unwrap(), from_hmsm(3, 5, 59, 300)); -/// ``` -/// -/// [leap second handling]: crate::NaiveTime#leap-second-handling -impl Add for NaiveTime { - type Output = NaiveTime; - - #[inline] - fn add(self, rhs: TimeDelta) -> NaiveTime { - self.overflowing_add_signed(rhs).0 - } -} - -/// Add-assign `TimeDelta` to `NaiveTime`. -/// -/// This wraps around and never overflows or underflows. -/// In particular the addition ignores integral number of days. -impl AddAssign for NaiveTime { - #[inline] - fn add_assign(&mut self, rhs: TimeDelta) { - *self = self.add(rhs); - } -} - -/// Add `std::time::Duration` to `NaiveTime`. -/// -/// This wraps around and never overflows or underflows. -/// In particular the addition ignores integral number of days. -impl Add for NaiveTime { - type Output = NaiveTime; - - #[inline] - fn add(self, rhs: Duration) -> NaiveTime { - // We don't care about values beyond `24 * 60 * 60`, so we can take a modulus and avoid - // overflow during the conversion to `TimeDelta`. - // But we limit to double that just in case `self` is a leap-second. - let secs = rhs.as_secs() % (2 * 24 * 60 * 60); - let d = TimeDelta::new(secs as i64, rhs.subsec_nanos()).unwrap(); - self.overflowing_add_signed(d).0 - } -} - -/// Add-assign `std::time::Duration` to `NaiveTime`. -/// -/// This wraps around and never overflows or underflows. -/// In particular the addition ignores integral number of days. -impl AddAssign for NaiveTime { - #[inline] - fn add_assign(&mut self, rhs: Duration) { - *self = *self + rhs; - } -} - -/// Add `FixedOffset` to `NaiveTime`. -/// -/// This wraps around and never overflows or underflows. -/// In particular the addition ignores integral number of days. -impl Add for NaiveTime { - type Output = NaiveTime; - - #[inline] - fn add(self, rhs: FixedOffset) -> NaiveTime { - self.overflowing_add_offset(rhs).0 - } -} - -/// Subtract `TimeDelta` from `NaiveTime`. -/// -/// This wraps around and never overflows or underflows. -/// In particular the subtraction ignores integral number of days. -/// This is the same as addition with a negated `TimeDelta`. -/// -/// As a part of Chrono's [leap second handling], the subtraction assumes that **there is no leap -/// second ever**, except when the `NaiveTime` itself represents a leap second in which case the -/// assumption becomes that **there is exactly a single leap second ever**. -/// -/// # Example -/// -/// ``` -/// use chrono::{NaiveTime, TimeDelta}; -/// -/// let from_hmsm = |h, m, s, milli| NaiveTime::from_hms_milli_opt(h, m, s, milli).unwrap(); -/// -/// assert_eq!(from_hmsm(3, 5, 7, 0) - TimeDelta::zero(), from_hmsm(3, 5, 7, 0)); -/// assert_eq!(from_hmsm(3, 5, 7, 0) - TimeDelta::try_seconds(1).unwrap(), from_hmsm(3, 5, 6, 0)); -/// assert_eq!( -/// from_hmsm(3, 5, 7, 0) - TimeDelta::try_seconds(60 + 5).unwrap(), -/// from_hmsm(3, 4, 2, 0) -/// ); -/// assert_eq!( -/// from_hmsm(3, 5, 7, 0) - TimeDelta::try_seconds(2 * 60 * 60 + 6 * 60).unwrap(), -/// from_hmsm(0, 59, 7, 0) -/// ); -/// assert_eq!( -/// from_hmsm(3, 5, 7, 0) - TimeDelta::try_milliseconds(80).unwrap(), -/// from_hmsm(3, 5, 6, 920) -/// ); -/// assert_eq!( -/// from_hmsm(3, 5, 7, 950) - TimeDelta::try_milliseconds(280).unwrap(), -/// from_hmsm(3, 5, 7, 670) -/// ); -/// ``` -/// -/// The subtraction wraps around. -/// -/// ``` -/// # use chrono::{TimeDelta, NaiveTime}; -/// # let from_hmsm = |h, m, s, milli| { NaiveTime::from_hms_milli_opt(h, m, s, milli).unwrap() }; -/// assert_eq!(from_hmsm(3, 5, 7, 0) - TimeDelta::try_seconds(8*60*60).unwrap(), from_hmsm(19, 5, 7, 0)); -/// assert_eq!(from_hmsm(3, 5, 7, 0) - TimeDelta::try_days(800).unwrap(), from_hmsm(3, 5, 7, 0)); -/// ``` -/// -/// Leap seconds are handled, but the subtraction assumes that it is the only leap second happened. -/// -/// ``` -/// # use chrono::{TimeDelta, NaiveTime}; -/// # let from_hmsm = |h, m, s, milli| { NaiveTime::from_hms_milli_opt(h, m, s, milli).unwrap() }; -/// let leap = from_hmsm(3, 5, 59, 1_300); -/// assert_eq!(leap - TimeDelta::zero(), from_hmsm(3, 5, 59, 1_300)); -/// assert_eq!(leap - TimeDelta::try_milliseconds(200).unwrap(), from_hmsm(3, 5, 59, 1_100)); -/// assert_eq!(leap - TimeDelta::try_milliseconds(500).unwrap(), from_hmsm(3, 5, 59, 800)); -/// assert_eq!(leap - TimeDelta::try_seconds(60).unwrap(), from_hmsm(3, 5, 0, 300)); -/// assert_eq!(leap - TimeDelta::try_days(1).unwrap(), from_hmsm(3, 6, 0, 300)); -/// ``` -/// -/// [leap second handling]: crate::NaiveTime#leap-second-handling -impl Sub for NaiveTime { - type Output = NaiveTime; - - #[inline] - fn sub(self, rhs: TimeDelta) -> NaiveTime { - self.overflowing_sub_signed(rhs).0 - } -} - -/// Subtract-assign `TimeDelta` from `NaiveTime`. -/// -/// This wraps around and never overflows or underflows. -/// In particular the subtraction ignores integral number of days. -impl SubAssign for NaiveTime { - #[inline] - fn sub_assign(&mut self, rhs: TimeDelta) { - *self = self.sub(rhs); - } -} - -/// Subtract `std::time::Duration` from `NaiveTime`. -/// -/// This wraps around and never overflows or underflows. -/// In particular the subtraction ignores integral number of days. -impl Sub for NaiveTime { - type Output = NaiveTime; - - #[inline] - fn sub(self, rhs: Duration) -> NaiveTime { - // We don't care about values beyond `24 * 60 * 60`, so we can take a modulus and avoid - // overflow during the conversion to `TimeDelta`. - // But we limit to double that just in case `self` is a leap-second. - let secs = rhs.as_secs() % (2 * 24 * 60 * 60); - let d = TimeDelta::new(secs as i64, rhs.subsec_nanos()).unwrap(); - self.overflowing_sub_signed(d).0 - } -} - -/// Subtract-assign `std::time::Duration` from `NaiveTime`. -/// -/// This wraps around and never overflows or underflows. -/// In particular the subtraction ignores integral number of days. -impl SubAssign for NaiveTime { - #[inline] - fn sub_assign(&mut self, rhs: Duration) { - *self = *self - rhs; - } -} - -/// Subtract `FixedOffset` from `NaiveTime`. -/// -/// This wraps around and never overflows or underflows. -/// In particular the subtraction ignores integral number of days. -impl Sub for NaiveTime { - type Output = NaiveTime; - - #[inline] - fn sub(self, rhs: FixedOffset) -> NaiveTime { - self.overflowing_sub_offset(rhs).0 - } -} - -/// Subtracts another `NaiveTime` from the current time. -/// Returns a `TimeDelta` within +/- 1 day. -/// This does not overflow or underflow at all. -/// -/// As a part of Chrono's [leap second handling](#leap-second-handling), -/// the subtraction assumes that **there is no leap second ever**, -/// except when any of the `NaiveTime`s themselves represents a leap second -/// in which case the assumption becomes that -/// **there are exactly one (or two) leap second(s) ever**. -/// -/// The implementation is a wrapper around -/// [`NaiveTime::signed_duration_since`](#method.signed_duration_since). -/// -/// # Example -/// -/// ``` -/// use chrono::{NaiveTime, TimeDelta}; -/// -/// let from_hmsm = |h, m, s, milli| NaiveTime::from_hms_milli_opt(h, m, s, milli).unwrap(); -/// -/// assert_eq!(from_hmsm(3, 5, 7, 900) - from_hmsm(3, 5, 7, 900), TimeDelta::zero()); -/// assert_eq!( -/// from_hmsm(3, 5, 7, 900) - from_hmsm(3, 5, 7, 875), -/// TimeDelta::try_milliseconds(25).unwrap() -/// ); -/// assert_eq!( -/// from_hmsm(3, 5, 7, 900) - from_hmsm(3, 5, 6, 925), -/// TimeDelta::try_milliseconds(975).unwrap() -/// ); -/// assert_eq!( -/// from_hmsm(3, 5, 7, 900) - from_hmsm(3, 5, 0, 900), -/// TimeDelta::try_seconds(7).unwrap() -/// ); -/// assert_eq!( -/// from_hmsm(3, 5, 7, 900) - from_hmsm(3, 0, 7, 900), -/// TimeDelta::try_seconds(5 * 60).unwrap() -/// ); -/// assert_eq!( -/// from_hmsm(3, 5, 7, 900) - from_hmsm(0, 5, 7, 900), -/// TimeDelta::try_seconds(3 * 3600).unwrap() -/// ); -/// assert_eq!( -/// from_hmsm(3, 5, 7, 900) - from_hmsm(4, 5, 7, 900), -/// TimeDelta::try_seconds(-3600).unwrap() -/// ); -/// assert_eq!( -/// from_hmsm(3, 5, 7, 900) - from_hmsm(2, 4, 6, 800), -/// TimeDelta::try_seconds(3600 + 60 + 1).unwrap() + TimeDelta::try_milliseconds(100).unwrap() -/// ); -/// ``` -/// -/// Leap seconds are handled, but the subtraction assumes that -/// there were no other leap seconds happened. -/// -/// ``` -/// # use chrono::{TimeDelta, NaiveTime}; -/// # let from_hmsm = |h, m, s, milli| { NaiveTime::from_hms_milli_opt(h, m, s, milli).unwrap() }; -/// assert_eq!(from_hmsm(3, 0, 59, 1_000) - from_hmsm(3, 0, 59, 0), TimeDelta::try_seconds(1).unwrap()); -/// assert_eq!(from_hmsm(3, 0, 59, 1_500) - from_hmsm(3, 0, 59, 0), -/// TimeDelta::try_milliseconds(1500).unwrap()); -/// assert_eq!(from_hmsm(3, 0, 59, 1_000) - from_hmsm(3, 0, 0, 0), TimeDelta::try_seconds(60).unwrap()); -/// assert_eq!(from_hmsm(3, 0, 0, 0) - from_hmsm(2, 59, 59, 1_000), TimeDelta::try_seconds(1).unwrap()); -/// assert_eq!(from_hmsm(3, 0, 59, 1_000) - from_hmsm(2, 59, 59, 1_000), -/// TimeDelta::try_seconds(61).unwrap()); -/// ``` -impl Sub for NaiveTime { - type Output = TimeDelta; - - #[inline] - fn sub(self, rhs: NaiveTime) -> TimeDelta { - self.signed_duration_since(rhs) - } -} - -/// The `Debug` output of the naive time `t` is the same as -/// [`t.format("%H:%M:%S%.f")`](crate::format::strftime). -/// -/// The string printed can be readily parsed via the `parse` method on `str`. -/// -/// It should be noted that, for leap seconds not on the minute boundary, -/// it may print a representation not distinguishable from non-leap seconds. -/// This doesn't matter in practice, since such leap seconds never happened. -/// (By the time of the first leap second on 1972-06-30, -/// every time zone offset around the world has standardized to the 5-minute alignment.) -/// -/// # Example -/// -/// ``` -/// use chrono::NaiveTime; -/// -/// assert_eq!(format!("{:?}", NaiveTime::from_hms_opt(23, 56, 4).unwrap()), "23:56:04"); -/// assert_eq!( -/// format!("{:?}", NaiveTime::from_hms_milli_opt(23, 56, 4, 12).unwrap()), -/// "23:56:04.012" -/// ); -/// assert_eq!( -/// format!("{:?}", NaiveTime::from_hms_micro_opt(23, 56, 4, 1234).unwrap()), -/// "23:56:04.001234" -/// ); -/// assert_eq!( -/// format!("{:?}", NaiveTime::from_hms_nano_opt(23, 56, 4, 123456).unwrap()), -/// "23:56:04.000123456" -/// ); -/// ``` -/// -/// Leap seconds may also be used. -/// -/// ``` -/// # use chrono::NaiveTime; -/// assert_eq!( -/// format!("{:?}", NaiveTime::from_hms_milli_opt(6, 59, 59, 1_500).unwrap()), -/// "06:59:60.500" -/// ); -/// ``` -impl fmt::Debug for NaiveTime { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let (hour, min, sec) = self.hms(); - let (sec, nano) = if self.frac >= 1_000_000_000 { - (sec + 1, self.frac - 1_000_000_000) - } else { - (sec, self.frac) - }; - - use core::fmt::Write; - write_hundreds(f, hour as u8)?; - f.write_char(':')?; - write_hundreds(f, min as u8)?; - f.write_char(':')?; - write_hundreds(f, sec as u8)?; - - if nano == 0 { - Ok(()) - } else if nano % 1_000_000 == 0 { - write!(f, ".{:03}", nano / 1_000_000) - } else if nano % 1_000 == 0 { - write!(f, ".{:06}", nano / 1_000) - } else { - write!(f, ".{nano:09}") - } - } -} - -#[cfg(feature = "defmt")] -impl defmt::Format for NaiveTime { - fn format(&self, fmt: defmt::Formatter) { - let (hour, min, sec) = self.hms(); - let (sec, nano) = if self.frac >= 1_000_000_000 { - (sec + 1, self.frac - 1_000_000_000) - } else { - (sec, self.frac) - }; - - let (hour, min, sec) = (hour as u8, min as u8, sec as u8); - defmt::write!(fmt, "{:02}:{:02}:{:02}", hour, min, sec); - - if nano == 0 { - return; - } else if nano % 1_000_000 == 0 { - defmt::write!(fmt, ".{:03}", nano / 1_000_000); - } else if nano % 1_000 == 0 { - defmt::write!(fmt, ".{:06}", nano / 1_000); - } else { - defmt::write!(fmt, ".{:09}", nano); - } - } -} - -/// The `Display` output of the naive time `t` is the same as -/// [`t.format("%H:%M:%S%.f")`](crate::format::strftime). -/// -/// The string printed can be readily parsed via the `parse` method on `str`. -/// -/// It should be noted that, for leap seconds not on the minute boundary, -/// it may print a representation not distinguishable from non-leap seconds. -/// This doesn't matter in practice, since such leap seconds never happened. -/// (By the time of the first leap second on 1972-06-30, -/// every time zone offset around the world has standardized to the 5-minute alignment.) -/// -/// # Example -/// -/// ``` -/// use chrono::NaiveTime; -/// -/// assert_eq!(format!("{}", NaiveTime::from_hms_opt(23, 56, 4).unwrap()), "23:56:04"); -/// assert_eq!( -/// format!("{}", NaiveTime::from_hms_milli_opt(23, 56, 4, 12).unwrap()), -/// "23:56:04.012" -/// ); -/// assert_eq!( -/// format!("{}", NaiveTime::from_hms_micro_opt(23, 56, 4, 1234).unwrap()), -/// "23:56:04.001234" -/// ); -/// assert_eq!( -/// format!("{}", NaiveTime::from_hms_nano_opt(23, 56, 4, 123456).unwrap()), -/// "23:56:04.000123456" -/// ); -/// ``` -/// -/// Leap seconds may also be used. -/// -/// ``` -/// # use chrono::NaiveTime; -/// assert_eq!( -/// format!("{}", NaiveTime::from_hms_milli_opt(6, 59, 59, 1_500).unwrap()), -/// "06:59:60.500" -/// ); -/// ``` -impl fmt::Display for NaiveTime { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(self, f) - } -} - -/// Parsing a `str` into a `NaiveTime` uses the same format, -/// [`%H:%M:%S%.f`](crate::format::strftime), as in `Debug` and `Display`. -/// -/// # Example -/// -/// ``` -/// use chrono::NaiveTime; -/// -/// let t = NaiveTime::from_hms_opt(23, 56, 4).unwrap(); -/// assert_eq!("23:56:04".parse::(), Ok(t)); -/// -/// let t = NaiveTime::from_hms_nano_opt(23, 56, 4, 12_345_678).unwrap(); -/// assert_eq!("23:56:4.012345678".parse::(), Ok(t)); -/// -/// let t = NaiveTime::from_hms_nano_opt(23, 59, 59, 1_234_567_890).unwrap(); // leap second -/// assert_eq!("23:59:60.23456789".parse::(), Ok(t)); -/// -/// // Seconds are optional -/// let t = NaiveTime::from_hms_opt(23, 56, 0).unwrap(); -/// assert_eq!("23:56".parse::(), Ok(t)); -/// -/// assert!("foo".parse::().is_err()); -/// ``` -impl str::FromStr for NaiveTime { - type Err = ParseError; - - fn from_str(s: &str) -> ParseResult { - const HOUR_AND_MINUTE: &[Item<'static>] = &[ - Item::Numeric(Numeric::Hour, Pad::Zero), - Item::Space(""), - Item::Literal(":"), - Item::Numeric(Numeric::Minute, Pad::Zero), - ]; - const SECOND_AND_NANOS: &[Item<'static>] = &[ - Item::Space(""), - Item::Literal(":"), - Item::Numeric(Numeric::Second, Pad::Zero), - Item::Fixed(Fixed::Nanosecond), - Item::Space(""), - ]; - const TRAILING_WHITESPACE: [Item<'static>; 1] = [Item::Space("")]; - - let mut parsed = Parsed::new(); - let s = parse_and_remainder(&mut parsed, s, HOUR_AND_MINUTE.iter())?; - // Seconds are optional, don't fail if parsing them doesn't succeed. - let s = parse_and_remainder(&mut parsed, s, SECOND_AND_NANOS.iter()).unwrap_or(s); - parse(&mut parsed, s, TRAILING_WHITESPACE.iter())?; - parsed.to_naive_time() - } -} - -/// The default value for a NaiveTime is midnight, 00:00:00 exactly. -/// -/// # Example -/// -/// ```rust -/// use chrono::NaiveTime; -/// -/// let default_time = NaiveTime::default(); -/// assert_eq!(default_time, NaiveTime::from_hms_opt(0, 0, 0).unwrap()); -/// ``` -impl Default for NaiveTime { - fn default() -> Self { - NaiveTime::from_hms_opt(0, 0, 0).unwrap() - } -} diff --git a/chrono-0.4.44/src/naive/time/serde.rs b/chrono-0.4.44/src/naive/time/serde.rs deleted file mode 100644 index f9b1dde215..0000000000 --- a/chrono-0.4.44/src/naive/time/serde.rs +++ /dev/null @@ -1,143 +0,0 @@ -use super::NaiveTime; -use core::fmt; -use serde::{de, ser}; - -// TODO not very optimized for space (binary formats would want something better) -// TODO round-trip for general leap seconds (not just those with second = 60) - -impl ser::Serialize for NaiveTime { - fn serialize(&self, serializer: S) -> Result - where - S: ser::Serializer, - { - serializer.collect_str(&self) - } -} - -struct NaiveTimeVisitor; - -impl de::Visitor<'_> for NaiveTimeVisitor { - type Value = NaiveTime; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a formatted time string") - } - - fn visit_str(self, value: &str) -> Result - where - E: de::Error, - { - value.parse().map_err(E::custom) - } -} - -impl<'de> de::Deserialize<'de> for NaiveTime { - fn deserialize(deserializer: D) -> Result - where - D: de::Deserializer<'de>, - { - deserializer.deserialize_str(NaiveTimeVisitor) - } -} - -#[cfg(test)] -mod tests { - use crate::NaiveTime; - - #[test] - fn test_serde_serialize() { - assert_eq!( - serde_json::to_string(&NaiveTime::from_hms_opt(0, 0, 0).unwrap()).ok(), - Some(r#""00:00:00""#.into()) - ); - assert_eq!( - serde_json::to_string(&NaiveTime::from_hms_milli_opt(0, 0, 0, 950).unwrap()).ok(), - Some(r#""00:00:00.950""#.into()) - ); - assert_eq!( - serde_json::to_string(&NaiveTime::from_hms_milli_opt(0, 0, 59, 1_000).unwrap()).ok(), - Some(r#""00:00:60""#.into()) - ); - assert_eq!( - serde_json::to_string(&NaiveTime::from_hms_opt(0, 1, 2).unwrap()).ok(), - Some(r#""00:01:02""#.into()) - ); - assert_eq!( - serde_json::to_string(&NaiveTime::from_hms_nano_opt(3, 5, 7, 98765432).unwrap()).ok(), - Some(r#""03:05:07.098765432""#.into()) - ); - assert_eq!( - serde_json::to_string(&NaiveTime::from_hms_opt(7, 8, 9).unwrap()).ok(), - Some(r#""07:08:09""#.into()) - ); - assert_eq!( - serde_json::to_string(&NaiveTime::from_hms_micro_opt(12, 34, 56, 789).unwrap()).ok(), - Some(r#""12:34:56.000789""#.into()) - ); - let leap = NaiveTime::from_hms_nano_opt(23, 59, 59, 1_999_999_999).unwrap(); - assert_eq!(serde_json::to_string(&leap).ok(), Some(r#""23:59:60.999999999""#.into())); - } - - #[test] - fn test_serde_deserialize() { - let from_str = serde_json::from_str::; - - assert_eq!(from_str(r#""00:00:00""#).ok(), Some(NaiveTime::from_hms_opt(0, 0, 0).unwrap())); - assert_eq!(from_str(r#""0:0:0""#).ok(), Some(NaiveTime::from_hms_opt(0, 0, 0).unwrap())); - assert_eq!( - from_str(r#""00:00:00.950""#).ok(), - Some(NaiveTime::from_hms_milli_opt(0, 0, 0, 950).unwrap()) - ); - assert_eq!( - from_str(r#""0:0:0.95""#).ok(), - Some(NaiveTime::from_hms_milli_opt(0, 0, 0, 950).unwrap()) - ); - assert_eq!( - from_str(r#""00:00:60""#).ok(), - Some(NaiveTime::from_hms_milli_opt(0, 0, 59, 1_000).unwrap()) - ); - assert_eq!(from_str(r#""00:01:02""#).ok(), Some(NaiveTime::from_hms_opt(0, 1, 2).unwrap())); - assert_eq!( - from_str(r#""03:05:07.098765432""#).ok(), - Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 98765432).unwrap()) - ); - assert_eq!(from_str(r#""07:08:09""#).ok(), Some(NaiveTime::from_hms_opt(7, 8, 9).unwrap())); - assert_eq!( - from_str(r#""12:34:56.000789""#).ok(), - Some(NaiveTime::from_hms_micro_opt(12, 34, 56, 789).unwrap()) - ); - assert_eq!( - from_str(r#""23:59:60.999999999""#).ok(), - Some(NaiveTime::from_hms_nano_opt(23, 59, 59, 1_999_999_999).unwrap()) - ); - assert_eq!( - from_str(r#""23:59:60.9999999999997""#).ok(), // excess digits are ignored - Some(NaiveTime::from_hms_nano_opt(23, 59, 59, 1_999_999_999).unwrap()) - ); - - // bad formats - assert!(from_str(r#""""#).is_err()); - assert!(from_str(r#""000000""#).is_err()); - assert!(from_str(r#""00:00:61""#).is_err()); - assert!(from_str(r#""00:60:00""#).is_err()); - assert!(from_str(r#""24:00:00""#).is_err()); - assert!(from_str(r#""23:59:59,1""#).is_err()); - assert!(from_str(r#""012:34:56""#).is_err()); - assert!(from_str(r#""hh:mm:ss""#).is_err()); - assert!(from_str(r#"0"#).is_err()); - assert!(from_str(r#"86399"#).is_err()); - assert!(from_str(r#"{}"#).is_err()); - } - - #[test] - fn test_serde_bincode() { - // Bincode is relevant to test separately from JSON because - // it is not self-describing. - use bincode::{deserialize, serialize}; - - let t = NaiveTime::from_hms_nano_opt(3, 5, 7, 98765432).unwrap(); - let encoded = serialize(&t).unwrap(); - let decoded: NaiveTime = deserialize(&encoded).unwrap(); - assert_eq!(t, decoded); - } -} diff --git a/chrono-0.4.44/src/naive/time/tests.rs b/chrono-0.4.44/src/naive/time/tests.rs deleted file mode 100644 index d1df20cf1e..0000000000 --- a/chrono-0.4.44/src/naive/time/tests.rs +++ /dev/null @@ -1,390 +0,0 @@ -use super::NaiveTime; -use crate::{FixedOffset, TimeDelta, Timelike}; - -#[test] -fn test_time_from_hms_milli() { - assert_eq!( - NaiveTime::from_hms_milli_opt(3, 5, 7, 0), - Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 0).unwrap()) - ); - assert_eq!( - NaiveTime::from_hms_milli_opt(3, 5, 7, 777), - Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 777_000_000).unwrap()) - ); - assert_eq!( - NaiveTime::from_hms_milli_opt(3, 5, 59, 1_999), - Some(NaiveTime::from_hms_nano_opt(3, 5, 59, 1_999_000_000).unwrap()) - ); - assert_eq!(NaiveTime::from_hms_milli_opt(3, 5, 59, 2_000), None); - assert_eq!(NaiveTime::from_hms_milli_opt(3, 5, 59, 5_000), None); // overflow check - assert_eq!(NaiveTime::from_hms_milli_opt(3, 5, 59, u32::MAX), None); -} - -#[test] -fn test_time_from_hms_micro() { - assert_eq!( - NaiveTime::from_hms_micro_opt(3, 5, 7, 0), - Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 0).unwrap()) - ); - assert_eq!( - NaiveTime::from_hms_micro_opt(3, 5, 7, 333), - Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 333_000).unwrap()) - ); - assert_eq!( - NaiveTime::from_hms_micro_opt(3, 5, 7, 777_777), - Some(NaiveTime::from_hms_nano_opt(3, 5, 7, 777_777_000).unwrap()) - ); - assert_eq!( - NaiveTime::from_hms_micro_opt(3, 5, 59, 1_999_999), - Some(NaiveTime::from_hms_nano_opt(3, 5, 59, 1_999_999_000).unwrap()) - ); - assert_eq!(NaiveTime::from_hms_micro_opt(3, 5, 59, 2_000_000), None); - assert_eq!(NaiveTime::from_hms_micro_opt(3, 5, 59, 5_000_000), None); // overflow check - assert_eq!(NaiveTime::from_hms_micro_opt(3, 5, 59, u32::MAX), None); -} - -#[test] -fn test_time_hms() { - assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().hour(), 3); - assert_eq!( - NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_hour(0), - Some(NaiveTime::from_hms_opt(0, 5, 7).unwrap()) - ); - assert_eq!( - NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_hour(23), - Some(NaiveTime::from_hms_opt(23, 5, 7).unwrap()) - ); - assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_hour(24), None); - assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_hour(u32::MAX), None); - - assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().minute(), 5); - assert_eq!( - NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_minute(0), - Some(NaiveTime::from_hms_opt(3, 0, 7).unwrap()) - ); - assert_eq!( - NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_minute(59), - Some(NaiveTime::from_hms_opt(3, 59, 7).unwrap()) - ); - assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_minute(60), None); - assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_minute(u32::MAX), None); - - assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().second(), 7); - assert_eq!( - NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_second(0), - Some(NaiveTime::from_hms_opt(3, 5, 0).unwrap()) - ); - assert_eq!( - NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_second(59), - Some(NaiveTime::from_hms_opt(3, 5, 59).unwrap()) - ); - assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_second(60), None); - assert_eq!(NaiveTime::from_hms_opt(3, 5, 7).unwrap().with_second(u32::MAX), None); -} - -#[test] -fn test_time_add() { - macro_rules! check { - ($lhs:expr, $rhs:expr, $sum:expr) => {{ - assert_eq!($lhs + $rhs, $sum); - //assert_eq!($rhs + $lhs, $sum); - }}; - } - - let hmsm = |h, m, s, ms| NaiveTime::from_hms_milli_opt(h, m, s, ms).unwrap(); - - check!(hmsm(3, 5, 59, 900), TimeDelta::zero(), hmsm(3, 5, 59, 900)); - check!(hmsm(3, 5, 59, 900), TimeDelta::try_milliseconds(100).unwrap(), hmsm(3, 6, 0, 0)); - check!(hmsm(3, 5, 59, 1_300), TimeDelta::try_milliseconds(-1800).unwrap(), hmsm(3, 5, 58, 500)); - check!(hmsm(3, 5, 59, 1_300), TimeDelta::try_milliseconds(-800).unwrap(), hmsm(3, 5, 59, 500)); - check!( - hmsm(3, 5, 59, 1_300), - TimeDelta::try_milliseconds(-100).unwrap(), - hmsm(3, 5, 59, 1_200) - ); - check!(hmsm(3, 5, 59, 1_300), TimeDelta::try_milliseconds(100).unwrap(), hmsm(3, 5, 59, 1_400)); - check!(hmsm(3, 5, 59, 1_300), TimeDelta::try_milliseconds(800).unwrap(), hmsm(3, 6, 0, 100)); - check!(hmsm(3, 5, 59, 1_300), TimeDelta::try_milliseconds(1800).unwrap(), hmsm(3, 6, 1, 100)); - check!(hmsm(3, 5, 59, 900), TimeDelta::try_seconds(86399).unwrap(), hmsm(3, 5, 58, 900)); // overwrap - check!(hmsm(3, 5, 59, 900), TimeDelta::try_seconds(-86399).unwrap(), hmsm(3, 6, 0, 900)); - check!(hmsm(3, 5, 59, 900), TimeDelta::try_days(12345).unwrap(), hmsm(3, 5, 59, 900)); - check!(hmsm(3, 5, 59, 1_300), TimeDelta::try_days(1).unwrap(), hmsm(3, 5, 59, 300)); - check!(hmsm(3, 5, 59, 1_300), TimeDelta::try_days(-1).unwrap(), hmsm(3, 6, 0, 300)); - - // regression tests for #37 - check!(hmsm(0, 0, 0, 0), TimeDelta::try_milliseconds(-990).unwrap(), hmsm(23, 59, 59, 10)); - check!(hmsm(0, 0, 0, 0), TimeDelta::try_milliseconds(-9990).unwrap(), hmsm(23, 59, 50, 10)); -} - -#[test] -fn test_time_overflowing_add() { - let hmsm = |h, m, s, ms| NaiveTime::from_hms_milli_opt(h, m, s, ms).unwrap(); - - assert_eq!( - hmsm(3, 4, 5, 678).overflowing_add_signed(TimeDelta::try_hours(11).unwrap()), - (hmsm(14, 4, 5, 678), 0) - ); - assert_eq!( - hmsm(3, 4, 5, 678).overflowing_add_signed(TimeDelta::try_hours(23).unwrap()), - (hmsm(2, 4, 5, 678), 86_400) - ); - assert_eq!( - hmsm(3, 4, 5, 678).overflowing_add_signed(TimeDelta::try_hours(-7).unwrap()), - (hmsm(20, 4, 5, 678), -86_400) - ); - - // overflowing_add_signed with leap seconds may be counter-intuitive - assert_eq!( - hmsm(3, 4, 59, 1_678).overflowing_add_signed(TimeDelta::try_days(1).unwrap()), - (hmsm(3, 4, 59, 678), 86_400) - ); - assert_eq!( - hmsm(3, 4, 59, 1_678).overflowing_add_signed(TimeDelta::try_days(-1).unwrap()), - (hmsm(3, 5, 0, 678), -86_400) - ); -} - -#[test] -fn test_time_addassignment() { - let hms = |h, m, s| NaiveTime::from_hms_opt(h, m, s).unwrap(); - let mut time = hms(12, 12, 12); - time += TimeDelta::try_hours(10).unwrap(); - assert_eq!(time, hms(22, 12, 12)); - time += TimeDelta::try_hours(10).unwrap(); - assert_eq!(time, hms(8, 12, 12)); -} - -#[test] -fn test_time_subassignment() { - let hms = |h, m, s| NaiveTime::from_hms_opt(h, m, s).unwrap(); - let mut time = hms(12, 12, 12); - time -= TimeDelta::try_hours(10).unwrap(); - assert_eq!(time, hms(2, 12, 12)); - time -= TimeDelta::try_hours(10).unwrap(); - assert_eq!(time, hms(16, 12, 12)); -} - -#[test] -fn test_time_sub() { - macro_rules! check { - ($lhs:expr, $rhs:expr, $diff:expr) => {{ - // `time1 - time2 = duration` is equivalent to `time2 - time1 = -duration` - assert_eq!($lhs.signed_duration_since($rhs), $diff); - assert_eq!($rhs.signed_duration_since($lhs), -$diff); - }}; - } - - let hmsm = |h, m, s, ms| NaiveTime::from_hms_milli_opt(h, m, s, ms).unwrap(); - - check!(hmsm(3, 5, 7, 900), hmsm(3, 5, 7, 900), TimeDelta::zero()); - check!(hmsm(3, 5, 7, 900), hmsm(3, 5, 7, 600), TimeDelta::try_milliseconds(300).unwrap()); - check!(hmsm(3, 5, 7, 200), hmsm(2, 4, 6, 200), TimeDelta::try_seconds(3600 + 60 + 1).unwrap()); - check!( - hmsm(3, 5, 7, 200), - hmsm(2, 4, 6, 300), - TimeDelta::try_seconds(3600 + 60).unwrap() + TimeDelta::try_milliseconds(900).unwrap() - ); - - // treats the leap second as if it coincides with the prior non-leap second, - // as required by `time1 - time2 = duration` and `time2 - time1 = -duration` equivalence. - check!(hmsm(3, 6, 0, 200), hmsm(3, 5, 59, 1_800), TimeDelta::try_milliseconds(400).unwrap()); - //check!(hmsm(3, 5, 7, 1_200), hmsm(3, 5, 6, 1_800), TimeDelta::try_milliseconds(1400).unwrap()); - //check!(hmsm(3, 5, 7, 1_200), hmsm(3, 5, 6, 800), TimeDelta::try_milliseconds(1400).unwrap()); - - // additional equality: `time1 + duration = time2` is equivalent to - // `time2 - time1 = duration` IF AND ONLY IF `time2` represents a non-leap second. - assert_eq!(hmsm(3, 5, 6, 800) + TimeDelta::try_milliseconds(400).unwrap(), hmsm(3, 5, 7, 200)); - //assert_eq!(hmsm(3, 5, 6, 1_800) + TimeDelta::try_milliseconds(400).unwrap(), hmsm(3, 5, 7, 200)); -} - -#[test] -fn test_core_duration_ops() { - use core::time::Duration; - - let mut t = NaiveTime::from_hms_opt(11, 34, 23).unwrap(); - let same = t + Duration::ZERO; - assert_eq!(t, same); - - t += Duration::new(3600, 0); - assert_eq!(t, NaiveTime::from_hms_opt(12, 34, 23).unwrap()); - - t -= Duration::new(7200, 0); - assert_eq!(t, NaiveTime::from_hms_opt(10, 34, 23).unwrap()); -} - -#[test] -fn test_time_fmt() { - assert_eq!( - format!("{}", NaiveTime::from_hms_milli_opt(23, 59, 59, 999).unwrap()), - "23:59:59.999" - ); - assert_eq!( - format!("{}", NaiveTime::from_hms_milli_opt(23, 59, 59, 1_000).unwrap()), - "23:59:60" - ); - assert_eq!( - format!("{}", NaiveTime::from_hms_milli_opt(23, 59, 59, 1_001).unwrap()), - "23:59:60.001" - ); - assert_eq!( - format!("{}", NaiveTime::from_hms_micro_opt(0, 0, 0, 43210).unwrap()), - "00:00:00.043210" - ); - assert_eq!( - format!("{}", NaiveTime::from_hms_nano_opt(0, 0, 0, 6543210).unwrap()), - "00:00:00.006543210" - ); - - // the format specifier should have no effect on `NaiveTime` - assert_eq!( - format!("{:30}", NaiveTime::from_hms_milli_opt(3, 5, 7, 9).unwrap()), - "03:05:07.009" - ); -} - -#[test] -fn test_time_from_str() { - // valid cases - let valid = [ - "0:0:0", - "0:0:0.0000000", - "0:0:0.0000003", - " 4 : 3 : 2.1 ", - " 09:08:07 ", - " 09:08 ", - " 9:8:07 ", - "01:02:03", - "4:3:2.1", - "9:8:7", - "09:8:7", - "9:08:7", - "9:8:07", - "09:08:7", - "09:8:07", - "09:08:7", - "9:08:07", - "09:08:07", - "9:8:07.123", - "9:08:7.123", - "09:8:7.123", - "09:08:7.123", - "9:08:07.123", - "09:8:07.123", - "09:08:07.123", - "09:08:07.123", - "09:08:07.1234", - "09:08:07.12345", - "09:08:07.123456", - "09:08:07.1234567", - "09:08:07.12345678", - "09:08:07.123456789", - "09:08:07.1234567891", - "09:08:07.12345678912", - "23:59:60.373929310237", - ]; - for &s in &valid { - eprintln!("test_time_parse_from_str valid {s:?}"); - let d = match s.parse::() { - Ok(d) => d, - Err(e) => panic!("parsing `{s}` has failed: {e}"), - }; - let s_ = format!("{d:?}"); - // `s` and `s_` may differ, but `s.parse()` and `s_.parse()` must be same - let d_ = match s_.parse::() { - Ok(d) => d, - Err(e) => { - panic!("`{s}` is parsed into `{d:?}`, but reparsing that has failed: {e}") - } - }; - assert!( - d == d_, - "`{s}` is parsed into `{d:?}`, but reparsed result \ - `{d_:?}` does not match" - ); - } - - // some invalid cases - // since `ParseErrorKind` is private, all we can do is to check if there was an error - let invalid = [ - "", // empty - "x", // invalid - "15", // missing data - "15:8:", // trailing colon - "15:8:x", // invalid data - "15:8:9x", // invalid data - "23:59:61", // invalid second (out of bounds) - "23:54:35 GMT", // invalid (timezone non-sensical for NaiveTime) - "23:54:35 +0000", // invalid (timezone non-sensical for NaiveTime) - "1441497364.649", // valid datetime, not a NaiveTime - "+1441497364.649", // valid datetime, not a NaiveTime - "+1441497364", // valid datetime, not a NaiveTime - "001:02:03", // invalid hour - "01:002:03", // invalid minute - "01:02:003", // invalid second - "12:34:56.x", // invalid fraction - "12:34:56. 0", // invalid fraction format - "09:08:00000000007", // invalid second / invalid fraction format - ]; - for &s in &invalid { - eprintln!("test_time_parse_from_str invalid {s:?}"); - assert!(s.parse::().is_err()); - } -} - -#[test] -fn test_time_parse_from_str() { - let hms = |h, m, s| NaiveTime::from_hms_opt(h, m, s).unwrap(); - assert_eq!( - NaiveTime::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), - Ok(hms(12, 34, 56)) - ); // ignore date and offset - assert_eq!(NaiveTime::parse_from_str("PM 12:59", "%P %H:%M"), Ok(hms(12, 59, 0))); - assert_eq!(NaiveTime::parse_from_str("12:59 \n\t PM", "%H:%M \n\t %P"), Ok(hms(12, 59, 0))); - assert_eq!(NaiveTime::parse_from_str("\t\t12:59\tPM\t", "\t\t%H:%M\t%P\t"), Ok(hms(12, 59, 0))); - assert_eq!( - NaiveTime::parse_from_str("\t\t1259\t\tPM\t", "\t\t%H%M\t\t%P\t"), - Ok(hms(12, 59, 0)) - ); - assert!(NaiveTime::parse_from_str("12:59 PM", "%H:%M\t%P").is_ok()); - assert!(NaiveTime::parse_from_str("\t\t12:59 PM\t", "\t\t%H:%M\t%P\t").is_ok()); - assert!(NaiveTime::parse_from_str("12:59 PM", "%H:%M %P").is_ok()); - assert!(NaiveTime::parse_from_str("12:3456", "%H:%M:%S").is_err()); -} - -#[test] -fn test_overflowing_offset() { - let hmsm = |h, m, s, n| NaiveTime::from_hms_milli_opt(h, m, s, n).unwrap(); - - let positive_offset = FixedOffset::east_opt(4 * 60 * 60).unwrap(); - // regular time - let t = hmsm(5, 6, 7, 890); - assert_eq!(t.overflowing_add_offset(positive_offset), (hmsm(9, 6, 7, 890), 0)); - assert_eq!(t.overflowing_sub_offset(positive_offset), (hmsm(1, 6, 7, 890), 0)); - // leap second is preserved, and wrap to next day - let t = hmsm(23, 59, 59, 1_000); - assert_eq!(t.overflowing_add_offset(positive_offset), (hmsm(3, 59, 59, 1_000), 1)); - assert_eq!(t.overflowing_sub_offset(positive_offset), (hmsm(19, 59, 59, 1_000), 0)); - // wrap to previous day - let t = hmsm(1, 2, 3, 456); - assert_eq!(t.overflowing_sub_offset(positive_offset), (hmsm(21, 2, 3, 456), -1)); - // an odd offset - let negative_offset = FixedOffset::west_opt(((2 * 60) + 3) * 60 + 4).unwrap(); - let t = hmsm(5, 6, 7, 890); - assert_eq!(t.overflowing_add_offset(negative_offset), (hmsm(3, 3, 3, 890), 0)); - assert_eq!(t.overflowing_sub_offset(negative_offset), (hmsm(7, 9, 11, 890), 0)); - - assert_eq!(t.overflowing_add_offset(positive_offset).0, t + positive_offset); - assert_eq!(t.overflowing_sub_offset(positive_offset).0, t - positive_offset); -} - -#[test] -#[cfg(feature = "rkyv-validation")] -fn test_rkyv_validation() { - let t_min = NaiveTime::MIN; - let bytes = rkyv::to_bytes::<_, 8>(&t_min).unwrap(); - assert_eq!(rkyv::from_bytes::(&bytes).unwrap(), t_min); - - let t_max = NaiveTime::MAX; - let bytes = rkyv::to_bytes::<_, 8>(&t_max).unwrap(); - assert_eq!(rkyv::from_bytes::(&bytes).unwrap(), t_max); -} diff --git a/chrono-0.4.44/src/offset/fixed.rs b/chrono-0.4.44/src/offset/fixed.rs deleted file mode 100644 index e810c854fd..0000000000 --- a/chrono-0.4.44/src/offset/fixed.rs +++ /dev/null @@ -1,253 +0,0 @@ -// This is a part of Chrono. -// See README.md and LICENSE.txt for details. - -//! The time zone which has a fixed offset from UTC. - -use core::fmt; -use core::str::FromStr; - -#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))] -use rkyv::{Archive, Deserialize, Serialize}; - -use super::{MappedLocalTime, Offset, TimeZone}; -use crate::format::{OUT_OF_RANGE, ParseError, scan}; -use crate::naive::{NaiveDate, NaiveDateTime}; - -/// The time zone with fixed offset, from UTC-23:59:59 to UTC+23:59:59. -/// -/// Using the [`TimeZone`](./trait.TimeZone.html) methods -/// on a `FixedOffset` struct is the preferred way to construct -/// `DateTime` instances. See the [`east_opt`](#method.east_opt) and -/// [`west_opt`](#method.west_opt) methods for examples. -#[derive(PartialEq, Eq, Hash, Copy, Clone)] -#[cfg_attr( - any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"), - derive(Archive, Deserialize, Serialize), - archive(compare(PartialEq)), - archive_attr(derive(Clone, Copy, PartialEq, Eq, Hash, Debug)) -)] -#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] -pub struct FixedOffset { - local_minus_utc: i32, -} - -impl FixedOffset { - /// Makes a new `FixedOffset` for the Eastern Hemisphere with given timezone difference. - /// The negative `secs` means the Western Hemisphere. - /// - /// Panics on the out-of-bound `secs`. - #[deprecated(since = "0.4.23", note = "use `east_opt()` instead")] - #[must_use] - pub fn east(secs: i32) -> FixedOffset { - FixedOffset::east_opt(secs).expect("FixedOffset::east out of bounds") - } - - /// Makes a new `FixedOffset` for the Eastern Hemisphere with given timezone difference. - /// The negative `secs` means the Western Hemisphere. - /// - /// Returns `None` on the out-of-bound `secs`. - /// - /// # Example - /// - /// ``` - /// # #[cfg(feature = "alloc")] { - /// use chrono::{FixedOffset, TimeZone}; - /// let hour = 3600; - /// let datetime = - /// FixedOffset::east_opt(5 * hour).unwrap().with_ymd_and_hms(2016, 11, 08, 0, 0, 0).unwrap(); - /// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00+05:00") - /// # } - /// ``` - #[must_use] - pub const fn east_opt(secs: i32) -> Option { - if -86_400 < secs && secs < 86_400 { - Some(FixedOffset { local_minus_utc: secs }) - } else { - None - } - } - - /// Makes a new `FixedOffset` for the Western Hemisphere with given timezone difference. - /// The negative `secs` means the Eastern Hemisphere. - /// - /// Panics on the out-of-bound `secs`. - #[deprecated(since = "0.4.23", note = "use `west_opt()` instead")] - #[must_use] - pub fn west(secs: i32) -> FixedOffset { - FixedOffset::west_opt(secs).expect("FixedOffset::west out of bounds") - } - - /// Makes a new `FixedOffset` for the Western Hemisphere with given timezone difference. - /// The negative `secs` means the Eastern Hemisphere. - /// - /// Returns `None` on the out-of-bound `secs`. - /// - /// # Example - /// - /// ``` - /// # #[cfg(feature = "alloc")] { - /// use chrono::{FixedOffset, TimeZone}; - /// let hour = 3600; - /// let datetime = - /// FixedOffset::west_opt(5 * hour).unwrap().with_ymd_and_hms(2016, 11, 08, 0, 0, 0).unwrap(); - /// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00-05:00") - /// # } - /// ``` - #[must_use] - pub const fn west_opt(secs: i32) -> Option { - if -86_400 < secs && secs < 86_400 { - Some(FixedOffset { local_minus_utc: -secs }) - } else { - None - } - } - - /// Returns the number of seconds to add to convert from UTC to the local time. - #[inline] - pub const fn local_minus_utc(&self) -> i32 { - self.local_minus_utc - } - - /// Returns the number of seconds to add to convert from the local time to UTC. - #[inline] - pub const fn utc_minus_local(&self) -> i32 { - -self.local_minus_utc - } -} - -/// Parsing a `str` into a `FixedOffset` uses the format [`%z`](crate::format::strftime). -impl FromStr for FixedOffset { - type Err = ParseError; - fn from_str(s: &str) -> Result { - let (_, offset) = scan::timezone_offset(s, scan::colon_or_space, false, false, true)?; - Self::east_opt(offset).ok_or(OUT_OF_RANGE) - } -} - -impl TimeZone for FixedOffset { - type Offset = FixedOffset; - - fn from_offset(offset: &FixedOffset) -> FixedOffset { - *offset - } - - fn offset_from_local_date(&self, _local: &NaiveDate) -> MappedLocalTime { - MappedLocalTime::Single(*self) - } - fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> MappedLocalTime { - MappedLocalTime::Single(*self) - } - - fn offset_from_utc_date(&self, _utc: &NaiveDate) -> FixedOffset { - *self - } - fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> FixedOffset { - *self - } -} - -impl Offset for FixedOffset { - fn fix(&self) -> FixedOffset { - *self - } -} - -impl fmt::Debug for FixedOffset { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let offset = self.local_minus_utc; - let (sign, offset) = if offset < 0 { ('-', -offset) } else { ('+', offset) }; - let sec = offset.rem_euclid(60); - let mins = offset.div_euclid(60); - let min = mins.rem_euclid(60); - let hour = mins.div_euclid(60); - if sec == 0 { - write!(f, "{sign}{hour:02}:{min:02}") - } else { - write!(f, "{sign}{hour:02}:{min:02}:{sec:02}") - } - } -} - -impl fmt::Display for FixedOffset { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(self, f) - } -} - -#[cfg(feature = "defmt")] -impl defmt::Format for FixedOffset { - fn format(&self, f: defmt::Formatter) { - let offset = self.local_minus_utc; - let (sign, offset) = if offset < 0 { ('-', -offset) } else { ('+', offset) }; - let sec = offset.rem_euclid(60); - let mins = offset.div_euclid(60); - let min = mins.rem_euclid(60); - let hour = mins.div_euclid(60); - if sec == 0 { - defmt::write!(f, "{}{:02}:{:02}", sign, hour, min) - } else { - defmt::write!(f, "{}{:02}:{:02}:{:02}", sign, hour, min, sec) - } - } -} - -#[cfg(all(feature = "arbitrary", feature = "std"))] -impl arbitrary::Arbitrary<'_> for FixedOffset { - fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result { - let secs = u.int_in_range(-86_399..=86_399)?; - let fixed_offset = FixedOffset::east_opt(secs) - .expect("Could not generate a valid chrono::FixedOffset. It looks like implementation of Arbitrary for FixedOffset is erroneous."); - Ok(fixed_offset) - } -} - -#[cfg(test)] -mod tests { - use super::FixedOffset; - use crate::offset::TimeZone; - use std::str::FromStr; - - #[test] - fn test_date_extreme_offset() { - // starting from 0.3 we don't have an offset exceeding one day. - // this makes everything easier! - let offset = FixedOffset::east_opt(86399).unwrap(); - assert_eq!( - format!("{:?}", offset.with_ymd_and_hms(2012, 2, 29, 5, 6, 7).unwrap()), - "2012-02-29T05:06:07+23:59:59" - ); - let offset = FixedOffset::east_opt(-86399).unwrap(); - assert_eq!( - format!("{:?}", offset.with_ymd_and_hms(2012, 2, 29, 5, 6, 7).unwrap()), - "2012-02-29T05:06:07-23:59:59" - ); - let offset = FixedOffset::west_opt(86399).unwrap(); - assert_eq!( - format!("{:?}", offset.with_ymd_and_hms(2012, 3, 4, 5, 6, 7).unwrap()), - "2012-03-04T05:06:07-23:59:59" - ); - let offset = FixedOffset::west_opt(-86399).unwrap(); - assert_eq!( - format!("{:?}", offset.with_ymd_and_hms(2012, 3, 4, 5, 6, 7).unwrap()), - "2012-03-04T05:06:07+23:59:59" - ); - } - - #[test] - fn test_parse_offset() { - let offset = FixedOffset::from_str("-0500").unwrap(); - assert_eq!(offset.local_minus_utc, -5 * 3600); - let offset = FixedOffset::from_str("-08:00").unwrap(); - assert_eq!(offset.local_minus_utc, -8 * 3600); - let offset = FixedOffset::from_str("+06:30").unwrap(); - assert_eq!(offset.local_minus_utc, (6 * 3600) + 1800); - } - - #[test] - #[cfg(feature = "rkyv-validation")] - fn test_rkyv_validation() { - let offset = FixedOffset::from_str("-0500").unwrap(); - let bytes = rkyv::to_bytes::<_, 4>(&offset).unwrap(); - assert_eq!(rkyv::from_bytes::(&bytes).unwrap(), offset); - } -} diff --git a/chrono-0.4.44/src/offset/local/mod.rs b/chrono-0.4.44/src/offset/local/mod.rs deleted file mode 100644 index 5741d24103..0000000000 --- a/chrono-0.4.44/src/offset/local/mod.rs +++ /dev/null @@ -1,543 +0,0 @@ -// This is a part of Chrono. -// See README.md and LICENSE.txt for details. - -//! The local (system) time zone. - -#[cfg(windows)] -use std::cmp::Ordering; - -#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))] -use rkyv::{Archive, Deserialize, Serialize}; - -use super::fixed::FixedOffset; -use super::{MappedLocalTime, TimeZone}; -#[allow(deprecated)] -use crate::Date; -use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime}; -use crate::{DateTime, Utc}; - -#[cfg(unix)] -#[path = "unix.rs"] -mod inner; - -#[cfg(windows)] -#[path = "windows.rs"] -mod inner; - -#[cfg(all(windows, feature = "clock"))] -#[allow(unreachable_pub)] -mod win_bindings; - -#[cfg(all(any(target_os = "android", target_env = "ohos", test), feature = "clock"))] -mod tz_data; - -#[cfg(all( - not(unix), - not(windows), - not(all( - target_arch = "wasm32", - feature = "wasmbind", - not(any(target_os = "emscripten", target_os = "wasi")) - )) -))] -mod inner { - use crate::{FixedOffset, MappedLocalTime, NaiveDateTime}; - - pub(super) fn offset_from_utc_datetime( - _utc_time: &NaiveDateTime, - ) -> MappedLocalTime { - MappedLocalTime::Single(FixedOffset::east_opt(0).unwrap()) - } - - pub(super) fn offset_from_local_datetime( - _local_time: &NaiveDateTime, - ) -> MappedLocalTime { - MappedLocalTime::Single(FixedOffset::east_opt(0).unwrap()) - } -} - -#[cfg(all( - target_arch = "wasm32", - feature = "wasmbind", - not(any(target_os = "emscripten", target_os = "wasi", target_os = "linux")) -))] -mod inner { - use crate::{Datelike, FixedOffset, MappedLocalTime, NaiveDateTime, Timelike}; - - pub(super) fn offset_from_utc_datetime(utc: &NaiveDateTime) -> MappedLocalTime { - let offset = js_sys::Date::from(utc.and_utc()).get_timezone_offset(); - MappedLocalTime::Single(FixedOffset::west_opt((offset as i32) * 60).unwrap()) - } - - pub(super) fn offset_from_local_datetime( - local: &NaiveDateTime, - ) -> MappedLocalTime { - let mut year = local.year(); - if year < 100 { - // The API in `js_sys` does not let us create a `Date` with negative years. - // And values for years from `0` to `99` map to the years `1900` to `1999`. - // Shift the value by a multiple of 400 years until it is `>= 100`. - let shift_cycles = (year - 100).div_euclid(400); - year -= shift_cycles * 400; - } - let js_date = js_sys::Date::new_with_year_month_day_hr_min_sec( - year as u32, - local.month0() as i32, - local.day() as i32, - local.hour() as i32, - local.minute() as i32, - local.second() as i32, - // ignore milliseconds, our representation of leap seconds may be problematic - ); - let offset = js_date.get_timezone_offset(); - // We always get a result, even if this time does not exist or is ambiguous. - MappedLocalTime::Single(FixedOffset::west_opt((offset as i32) * 60).unwrap()) - } -} - -#[cfg(unix)] -mod tz_info; - -/// The local timescale. -/// -/// Using the [`TimeZone`](./trait.TimeZone.html) methods -/// on the Local struct is the preferred way to construct `DateTime` -/// instances. -/// -/// # Example -/// -/// ``` -/// use chrono::{DateTime, Local, TimeZone}; -/// -/// let dt1: DateTime = Local::now(); -/// let dt2: DateTime = Local.timestamp_opt(0, 0).unwrap(); -/// assert!(dt1 >= dt2); -/// ``` -#[derive(Copy, Clone, Debug)] -#[cfg_attr( - any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"), - derive(Archive, Deserialize, Serialize), - archive(compare(PartialEq)), - archive_attr(derive(Clone, Copy, Debug)) -)] -#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] -#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Local; - -impl Local { - /// Returns a `Date` which corresponds to the current date. - #[deprecated(since = "0.4.23", note = "use `Local::now()` instead")] - #[allow(deprecated)] - #[must_use] - pub fn today() -> Date { - Local::now().date() - } - - /// Returns a `DateTime` which corresponds to the current date, time and offset from - /// UTC. - /// - /// See also the similar [`Utc::now()`] which returns `DateTime`, i.e. without the local - /// offset. - /// - /// # Example - /// - /// ``` - /// # #![allow(unused_variables)] - /// # use chrono::{DateTime, FixedOffset, Local}; - /// // Current local time - /// let now = Local::now(); - /// - /// // Current local date - /// let today = now.date_naive(); - /// - /// // Current local time, converted to `DateTime` - /// let now_fixed_offset = Local::now().fixed_offset(); - /// // or - /// let now_fixed_offset: DateTime = Local::now().into(); - /// - /// // Current time in some timezone (let's use +05:00) - /// // Note that it is usually more efficient to use `Utc::now` for this use case. - /// let offset = FixedOffset::east_opt(5 * 60 * 60).unwrap(); - /// let now_with_offset = Local::now().with_timezone(&offset); - /// ``` - pub fn now() -> DateTime { - Utc::now().with_timezone(&Local) - } -} - -impl TimeZone for Local { - type Offset = FixedOffset; - - fn from_offset(_offset: &FixedOffset) -> Local { - Local - } - - #[allow(deprecated)] - fn offset_from_local_date(&self, local: &NaiveDate) -> MappedLocalTime { - // Get the offset at local midnight. - self.offset_from_local_datetime(&local.and_time(NaiveTime::MIN)) - } - - fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> MappedLocalTime { - inner::offset_from_local_datetime(local) - } - - #[allow(deprecated)] - fn offset_from_utc_date(&self, utc: &NaiveDate) -> FixedOffset { - // Get the offset at midnight. - self.offset_from_utc_datetime(&utc.and_time(NaiveTime::MIN)) - } - - fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> FixedOffset { - inner::offset_from_utc_datetime(utc).unwrap() - } -} - -#[cfg(windows)] -#[derive(Copy, Clone, Eq, PartialEq)] -struct Transition { - transition_utc: NaiveDateTime, - offset_before: FixedOffset, - offset_after: FixedOffset, -} - -#[cfg(windows)] -impl Transition { - fn new( - transition_local: NaiveDateTime, - offset_before: FixedOffset, - offset_after: FixedOffset, - ) -> Transition { - // It is no problem if the transition time in UTC falls a couple of hours inside the buffer - // space around the `NaiveDateTime` range (although it is very theoretical to have a - // transition at midnight around `NaiveDate::(MIN|MAX)`. - let transition_utc = transition_local.overflowing_sub_offset(offset_before); - Transition { transition_utc, offset_before, offset_after } - } -} - -#[cfg(windows)] -impl PartialOrd for Transition { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -#[cfg(windows)] -impl Ord for Transition { - fn cmp(&self, other: &Self) -> Ordering { - self.transition_utc.cmp(&other.transition_utc) - } -} - -// Calculate the time in UTC given a local time and transitions. -// `transitions` must be sorted. -#[cfg(windows)] -fn lookup_with_dst_transitions( - transitions: &[Transition], - dt: NaiveDateTime, -) -> MappedLocalTime { - for t in transitions.iter() { - // A transition can result in the wall clock time going forward (creating a gap) or going - // backward (creating a fold). We are interested in the earliest and latest wall time of the - // transition, as this are the times between which `dt` does may not exist or is ambiguous. - // - // It is no problem if the transition times falls a couple of hours inside the buffer - // space around the `NaiveDateTime` range (although it is very theoretical to have a - // transition at midnight around `NaiveDate::(MIN|MAX)`. - let (offset_min, offset_max) = - match t.offset_after.local_minus_utc() > t.offset_before.local_minus_utc() { - true => (t.offset_before, t.offset_after), - false => (t.offset_after, t.offset_before), - }; - let wall_earliest = t.transition_utc.overflowing_add_offset(offset_min); - let wall_latest = t.transition_utc.overflowing_add_offset(offset_max); - - if dt < wall_earliest { - return MappedLocalTime::Single(t.offset_before); - } else if dt <= wall_latest { - return match t.offset_after.local_minus_utc().cmp(&t.offset_before.local_minus_utc()) { - Ordering::Equal => MappedLocalTime::Single(t.offset_before), - Ordering::Less => MappedLocalTime::Ambiguous(t.offset_before, t.offset_after), - Ordering::Greater => { - if dt == wall_earliest { - MappedLocalTime::Single(t.offset_before) - } else if dt == wall_latest { - MappedLocalTime::Single(t.offset_after) - } else { - MappedLocalTime::None - } - } - }; - } - } - MappedLocalTime::Single(transitions.last().unwrap().offset_after) -} - -#[cfg(test)] -mod tests { - use super::Local; - use crate::offset::TimeZone; - #[cfg(windows)] - use crate::offset::local::{Transition, lookup_with_dst_transitions}; - use crate::{Datelike, Days, Utc}; - #[cfg(windows)] - use crate::{FixedOffset, MappedLocalTime, NaiveDate, NaiveDateTime}; - - #[test] - fn verify_correct_offsets() { - let now = Local::now(); - let from_local = Local.from_local_datetime(&now.naive_local()).unwrap(); - let from_utc = Local.from_utc_datetime(&now.naive_utc()); - - assert_eq!(now.offset().local_minus_utc(), from_local.offset().local_minus_utc()); - assert_eq!(now.offset().local_minus_utc(), from_utc.offset().local_minus_utc()); - - assert_eq!(now, from_local); - assert_eq!(now, from_utc); - } - - #[test] - fn verify_correct_offsets_distant_past() { - let distant_past = Local::now() - Days::new(365 * 500); - let from_local = Local.from_local_datetime(&distant_past.naive_local()).unwrap(); - let from_utc = Local.from_utc_datetime(&distant_past.naive_utc()); - - assert_eq!(distant_past.offset().local_minus_utc(), from_local.offset().local_minus_utc()); - assert_eq!(distant_past.offset().local_minus_utc(), from_utc.offset().local_minus_utc()); - - assert_eq!(distant_past, from_local); - assert_eq!(distant_past, from_utc); - } - - #[test] - fn verify_correct_offsets_distant_future() { - let distant_future = Local::now() + Days::new(365 * 35000); - let from_local = Local.from_local_datetime(&distant_future.naive_local()).unwrap(); - let from_utc = Local.from_utc_datetime(&distant_future.naive_utc()); - - assert_eq!( - distant_future.offset().local_minus_utc(), - from_local.offset().local_minus_utc() - ); - assert_eq!(distant_future.offset().local_minus_utc(), from_utc.offset().local_minus_utc()); - - assert_eq!(distant_future, from_local); - assert_eq!(distant_future, from_utc); - } - - #[test] - fn test_local_date_sanity_check() { - // issue #27 - assert_eq!(Local.with_ymd_and_hms(2999, 12, 28, 0, 0, 0).unwrap().day(), 28); - } - - #[test] - fn test_leap_second() { - // issue #123 - let today = Utc::now().date_naive(); - - if let Some(dt) = today.and_hms_milli_opt(15, 2, 59, 1000) { - let timestr = dt.time().to_string(); - // the OS API may or may not support the leap second, - // but there are only two sensible options. - assert!( - timestr == "15:02:60" || timestr == "15:03:00", - "unexpected timestr {timestr:?}" - ); - } - - if let Some(dt) = today.and_hms_milli_opt(15, 2, 3, 1234) { - let timestr = dt.time().to_string(); - assert!( - timestr == "15:02:03.234" || timestr == "15:02:04.234", - "unexpected timestr {timestr:?}" - ); - } - } - - #[test] - #[cfg(windows)] - fn test_lookup_with_dst_transitions() { - let ymdhms = |y, m, d, h, n, s| { - NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap() - }; - - #[track_caller] - #[allow(clippy::too_many_arguments)] - fn compare_lookup( - transitions: &[Transition], - y: i32, - m: u32, - d: u32, - h: u32, - n: u32, - s: u32, - result: MappedLocalTime, - ) { - let dt = NaiveDate::from_ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap(); - assert_eq!(lookup_with_dst_transitions(transitions, dt), result); - } - - // dst transition before std transition - // dst offset > std offset - let std = FixedOffset::east_opt(3 * 60 * 60).unwrap(); - let dst = FixedOffset::east_opt(4 * 60 * 60).unwrap(); - let transitions = [ - Transition::new(ymdhms(2023, 3, 26, 2, 0, 0), std, dst), - Transition::new(ymdhms(2023, 10, 29, 3, 0, 0), dst, std), - ]; - compare_lookup(&transitions, 2023, 3, 26, 1, 0, 0, MappedLocalTime::Single(std)); - compare_lookup(&transitions, 2023, 3, 26, 2, 0, 0, MappedLocalTime::Single(std)); - compare_lookup(&transitions, 2023, 3, 26, 2, 30, 0, MappedLocalTime::None); - compare_lookup(&transitions, 2023, 3, 26, 3, 0, 0, MappedLocalTime::Single(dst)); - compare_lookup(&transitions, 2023, 3, 26, 4, 0, 0, MappedLocalTime::Single(dst)); - - compare_lookup(&transitions, 2023, 10, 29, 1, 0, 0, MappedLocalTime::Single(dst)); - compare_lookup(&transitions, 2023, 10, 29, 2, 0, 0, MappedLocalTime::Ambiguous(dst, std)); - compare_lookup(&transitions, 2023, 10, 29, 2, 30, 0, MappedLocalTime::Ambiguous(dst, std)); - compare_lookup(&transitions, 2023, 10, 29, 3, 0, 0, MappedLocalTime::Ambiguous(dst, std)); - compare_lookup(&transitions, 2023, 10, 29, 4, 0, 0, MappedLocalTime::Single(std)); - - // std transition before dst transition - // dst offset > std offset - let std = FixedOffset::east_opt(-5 * 60 * 60).unwrap(); - let dst = FixedOffset::east_opt(-4 * 60 * 60).unwrap(); - let transitions = [ - Transition::new(ymdhms(2023, 3, 24, 3, 0, 0), dst, std), - Transition::new(ymdhms(2023, 10, 27, 2, 0, 0), std, dst), - ]; - compare_lookup(&transitions, 2023, 3, 24, 1, 0, 0, MappedLocalTime::Single(dst)); - compare_lookup(&transitions, 2023, 3, 24, 2, 0, 0, MappedLocalTime::Ambiguous(dst, std)); - compare_lookup(&transitions, 2023, 3, 24, 2, 30, 0, MappedLocalTime::Ambiguous(dst, std)); - compare_lookup(&transitions, 2023, 3, 24, 3, 0, 0, MappedLocalTime::Ambiguous(dst, std)); - compare_lookup(&transitions, 2023, 3, 24, 4, 0, 0, MappedLocalTime::Single(std)); - - compare_lookup(&transitions, 2023, 10, 27, 1, 0, 0, MappedLocalTime::Single(std)); - compare_lookup(&transitions, 2023, 10, 27, 2, 0, 0, MappedLocalTime::Single(std)); - compare_lookup(&transitions, 2023, 10, 27, 2, 30, 0, MappedLocalTime::None); - compare_lookup(&transitions, 2023, 10, 27, 3, 0, 0, MappedLocalTime::Single(dst)); - compare_lookup(&transitions, 2023, 10, 27, 4, 0, 0, MappedLocalTime::Single(dst)); - - // dst transition before std transition - // dst offset < std offset - let std = FixedOffset::east_opt(3 * 60 * 60).unwrap(); - let dst = FixedOffset::east_opt((2 * 60 + 30) * 60).unwrap(); - let transitions = [ - Transition::new(ymdhms(2023, 3, 26, 2, 30, 0), std, dst), - Transition::new(ymdhms(2023, 10, 29, 2, 0, 0), dst, std), - ]; - compare_lookup(&transitions, 2023, 3, 26, 1, 0, 0, MappedLocalTime::Single(std)); - compare_lookup(&transitions, 2023, 3, 26, 2, 0, 0, MappedLocalTime::Ambiguous(std, dst)); - compare_lookup(&transitions, 2023, 3, 26, 2, 15, 0, MappedLocalTime::Ambiguous(std, dst)); - compare_lookup(&transitions, 2023, 3, 26, 2, 30, 0, MappedLocalTime::Ambiguous(std, dst)); - compare_lookup(&transitions, 2023, 3, 26, 3, 0, 0, MappedLocalTime::Single(dst)); - - compare_lookup(&transitions, 2023, 10, 29, 1, 0, 0, MappedLocalTime::Single(dst)); - compare_lookup(&transitions, 2023, 10, 29, 2, 0, 0, MappedLocalTime::Single(dst)); - compare_lookup(&transitions, 2023, 10, 29, 2, 15, 0, MappedLocalTime::None); - compare_lookup(&transitions, 2023, 10, 29, 2, 30, 0, MappedLocalTime::Single(std)); - compare_lookup(&transitions, 2023, 10, 29, 3, 0, 0, MappedLocalTime::Single(std)); - - // std transition before dst transition - // dst offset < std offset - let std = FixedOffset::east_opt(-(4 * 60 + 30) * 60).unwrap(); - let dst = FixedOffset::east_opt(-5 * 60 * 60).unwrap(); - let transitions = [ - Transition::new(ymdhms(2023, 3, 24, 2, 0, 0), dst, std), - Transition::new(ymdhms(2023, 10, 27, 2, 30, 0), std, dst), - ]; - compare_lookup(&transitions, 2023, 3, 24, 1, 0, 0, MappedLocalTime::Single(dst)); - compare_lookup(&transitions, 2023, 3, 24, 2, 0, 0, MappedLocalTime::Single(dst)); - compare_lookup(&transitions, 2023, 3, 24, 2, 15, 0, MappedLocalTime::None); - compare_lookup(&transitions, 2023, 3, 24, 2, 30, 0, MappedLocalTime::Single(std)); - compare_lookup(&transitions, 2023, 3, 24, 3, 0, 0, MappedLocalTime::Single(std)); - - compare_lookup(&transitions, 2023, 10, 27, 1, 0, 0, MappedLocalTime::Single(std)); - compare_lookup(&transitions, 2023, 10, 27, 2, 0, 0, MappedLocalTime::Ambiguous(std, dst)); - compare_lookup(&transitions, 2023, 10, 27, 2, 15, 0, MappedLocalTime::Ambiguous(std, dst)); - compare_lookup(&transitions, 2023, 10, 27, 2, 30, 0, MappedLocalTime::Ambiguous(std, dst)); - compare_lookup(&transitions, 2023, 10, 27, 3, 0, 0, MappedLocalTime::Single(dst)); - - // offset stays the same - let std = FixedOffset::east_opt(3 * 60 * 60).unwrap(); - let transitions = [ - Transition::new(ymdhms(2023, 3, 26, 2, 0, 0), std, std), - Transition::new(ymdhms(2023, 10, 29, 3, 0, 0), std, std), - ]; - compare_lookup(&transitions, 2023, 3, 26, 2, 0, 0, MappedLocalTime::Single(std)); - compare_lookup(&transitions, 2023, 10, 29, 3, 0, 0, MappedLocalTime::Single(std)); - - // single transition - let std = FixedOffset::east_opt(3 * 60 * 60).unwrap(); - let dst = FixedOffset::east_opt(4 * 60 * 60).unwrap(); - let transitions = [Transition::new(ymdhms(2023, 3, 26, 2, 0, 0), std, dst)]; - compare_lookup(&transitions, 2023, 3, 26, 1, 0, 0, MappedLocalTime::Single(std)); - compare_lookup(&transitions, 2023, 3, 26, 2, 0, 0, MappedLocalTime::Single(std)); - compare_lookup(&transitions, 2023, 3, 26, 2, 30, 0, MappedLocalTime::None); - compare_lookup(&transitions, 2023, 3, 26, 3, 0, 0, MappedLocalTime::Single(dst)); - compare_lookup(&transitions, 2023, 3, 26, 4, 0, 0, MappedLocalTime::Single(dst)); - } - - #[test] - #[cfg(windows)] - fn test_lookup_with_dst_transitions_limits() { - // Transition beyond UTC year end doesn't panic in year of `NaiveDate::MAX` - let std = FixedOffset::east_opt(3 * 60 * 60).unwrap(); - let dst = FixedOffset::east_opt(4 * 60 * 60).unwrap(); - let transitions = [ - Transition::new(NaiveDateTime::MAX.with_month(7).unwrap(), std, dst), - Transition::new(NaiveDateTime::MAX, dst, std), - ]; - assert_eq!( - lookup_with_dst_transitions(&transitions, NaiveDateTime::MAX.with_month(3).unwrap()), - MappedLocalTime::Single(std) - ); - assert_eq!( - lookup_with_dst_transitions(&transitions, NaiveDateTime::MAX.with_month(8).unwrap()), - MappedLocalTime::Single(dst) - ); - // Doesn't panic with `NaiveDateTime::MAX` as argument (which would be out of range when - // converted to UTC). - assert_eq!( - lookup_with_dst_transitions(&transitions, NaiveDateTime::MAX), - MappedLocalTime::Ambiguous(dst, std) - ); - - // Transition before UTC year end doesn't panic in year of `NaiveDate::MIN` - let std = FixedOffset::west_opt(3 * 60 * 60).unwrap(); - let dst = FixedOffset::west_opt(4 * 60 * 60).unwrap(); - let transitions = [ - Transition::new(NaiveDateTime::MIN, std, dst), - Transition::new(NaiveDateTime::MIN.with_month(6).unwrap(), dst, std), - ]; - assert_eq!( - lookup_with_dst_transitions(&transitions, NaiveDateTime::MIN.with_month(3).unwrap()), - MappedLocalTime::Single(dst) - ); - assert_eq!( - lookup_with_dst_transitions(&transitions, NaiveDateTime::MIN.with_month(8).unwrap()), - MappedLocalTime::Single(std) - ); - // Doesn't panic with `NaiveDateTime::MIN` as argument (which would be out of range when - // converted to UTC). - assert_eq!( - lookup_with_dst_transitions(&transitions, NaiveDateTime::MIN), - MappedLocalTime::Ambiguous(std, dst) - ); - } - - #[test] - #[cfg(feature = "rkyv-validation")] - fn test_rkyv_validation() { - let local = Local; - // Local is a ZST and serializes to 0 bytes - let bytes = rkyv::to_bytes::<_, 0>(&local).unwrap(); - assert_eq!(bytes.len(), 0); - - // but is deserialized to an archived variant without a - // wrapping object - assert_eq!(rkyv::from_bytes::(&bytes).unwrap(), super::ArchivedLocal); - } -} diff --git a/chrono-0.4.44/src/offset/local/tz_data.rs b/chrono-0.4.44/src/offset/local/tz_data.rs deleted file mode 100644 index 3533e56d56..0000000000 --- a/chrono-0.4.44/src/offset/local/tz_data.rs +++ /dev/null @@ -1,267 +0,0 @@ -//! Rust parser of ZoneInfoDb(`tzdata`) on Android and OpenHarmony -//! -//! Ported from: https://android.googlesource.com/platform/prebuilts/fullsdk/sources/+/refs/heads/androidx-appcompat-release/android-34/com/android/i18n/timezone/ZoneInfoDb.java -use std::{ - ffi::CStr, - fmt::Debug, - fs::File, - io::{Error, ErrorKind, Read, Result, Seek, SeekFrom}, -}; - -/// Get timezone data from the `tzdata` file of HarmonyOS NEXT. -#[cfg(target_env = "ohos")] -pub(crate) fn for_zone(tz_string: &str) -> Result>> { - let mut file = File::open("/system/etc/zoneinfo/tzdata")?; - find_tz_data::(&mut file, tz_string.as_bytes()) -} - -/// Get timezone data from the `tzdata` file of Android. -#[cfg(target_os = "android")] -pub(crate) fn for_zone(tz_string: &str) -> Result>> { - let mut file = open_android_tz_data_file()?; - find_tz_data::(&mut file, tz_string.as_bytes()) -} - -/// Open the `tzdata` file of Android from the environment variables. -#[cfg(target_os = "android")] -fn open_android_tz_data_file() -> Result { - for (env_var, path) in - [("ANDROID_DATA", "/misc/zoneinfo"), ("ANDROID_ROOT", "/usr/share/zoneinfo")] - { - if let Ok(env_value) = std::env::var(env_var) { - if let Ok(file) = File::open(format!("{}{}/tzdata", env_value, path)) { - return Ok(file); - } - } - } - Err(Error::from(ErrorKind::NotFound)) -} - -/// Get timezone data from the `tzdata` file reader -#[cfg(any(test, target_env = "ohos", target_os = "android"))] -fn find_tz_data( - mut reader: impl Read + Seek, - tz_name: &[u8], -) -> Result>> { - let header = TzDataHeader::new(&mut reader)?; - let index = TzDataIndexes::new::(&mut reader, &header)?; - Ok(if let Some(entry) = index.find_timezone(tz_name) { - Some(index.find_tzdata(reader, &header, entry)?) - } else { - None - }) -} - -/// Header of the `tzdata` file. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -struct TzDataHeader { - version: [u8; 5], - index_offset: u32, - data_offset: u32, - zonetab_offset: u32, -} - -impl TzDataHeader { - /// Parse the header of the `tzdata` file. - fn new(mut data: impl Read) -> Result { - let version = { - let mut magic = [0; TZDATA_VERSION_LEN]; - data.read_exact(&mut magic)?; - if !magic.starts_with(b"tzdata") || magic[TZDATA_VERSION_LEN - 1] != 0 { - return Err(Error::new(ErrorKind::Other, "invalid tzdata header magic")); - } - let mut version = [0; 5]; - version.copy_from_slice(&magic[6..11]); - version - }; - - let mut offset = [0; 4]; - data.read_exact(&mut offset)?; - let index_offset = u32::from_be_bytes(offset); - data.read_exact(&mut offset)?; - let data_offset = u32::from_be_bytes(offset); - data.read_exact(&mut offset)?; - let zonetab_offset = u32::from_be_bytes(offset); - - Ok(Self { version, index_offset, data_offset, zonetab_offset }) - } -} - -/// Indexes of the `tzdata` file. -struct TzDataIndexes { - indexes: Vec, -} - -impl TzDataIndexes { - /// Create a new `TzDataIndexes` from the `tzdata` file reader. - fn new(mut reader: impl Read, header: &TzDataHeader) -> Result { - let mut buf = vec![0; header.data_offset.saturating_sub(header.index_offset) as usize]; - reader.read_exact(&mut buf)?; - // replace chunks with array_chunks when it's stable - Ok(TzDataIndexes { - indexes: buf - .chunks(ENTRY_LEN) - .filter_map(|chunk| { - from_bytes_until_nul(&chunk[..TZ_NAME_LEN]).map(|name| { - let name = name.to_bytes().to_vec().into_boxed_slice(); - let offset = u32::from_be_bytes( - chunk[TZ_NAME_LEN..TZ_NAME_LEN + 4].try_into().unwrap(), - ); - let length = u32::from_be_bytes( - chunk[TZ_NAME_LEN + 4..TZ_NAME_LEN + 8].try_into().unwrap(), - ); - TzDataIndex { name, offset, length } - }) - }) - .collect(), - }) - } - - /// Find a timezone by name. - fn find_timezone(&self, timezone: &[u8]) -> Option<&TzDataIndex> { - // timezones in tzdata are sorted by name. - self.indexes.binary_search_by_key(&timezone, |x| &x.name).map(|x| &self.indexes[x]).ok() - } - - /// Retrieve a chunk of timezone data by the index. - fn find_tzdata( - &self, - mut reader: impl Read + Seek, - header: &TzDataHeader, - index: &TzDataIndex, - ) -> Result> { - reader.seek(SeekFrom::Start(index.offset as u64 + header.data_offset as u64))?; - let mut buffer = vec![0; index.length as usize]; - reader.read_exact(&mut buffer)?; - Ok(buffer) - } -} - -/// Index entry of the `tzdata` file. -struct TzDataIndex { - name: Box<[u8]>, - offset: u32, - length: u32, -} - -/// TODO: Change this `CStr::from_bytes_until_nul` once MSRV was bumped above 1.72.0 -fn from_bytes_until_nul(bytes: &[u8]) -> Option<&CStr> { - let nul_pos = bytes.iter().position(|&b| b == 0)?; - // SAFETY: - // 1. nul_pos + 1 <= bytes.len() - // 2. We know there is a nul byte at nul_pos, so this slice (ending at the nul byte) is a well-formed C string. - Some(unsafe { CStr::from_bytes_with_nul_unchecked(&bytes[..=nul_pos]) }) -} - -/// Ohos tzdata index entry size: `name + offset + length` -#[cfg(any(test, target_env = "ohos"))] -const OHOS_ENTRY_LEN: usize = TZ_NAME_LEN + 2 * size_of::(); -/// Android tzdata index entry size: `name + offset + length + raw_utc_offset(legacy)`: -/// [reference](https://android.googlesource.com/platform/prebuilts/fullsdk/sources/+/refs/heads/androidx-appcompat-release/android-34/com/android/i18n/timezone/ZoneInfoDb.java#271) -#[cfg(any(test, target_os = "android"))] -const ANDROID_ENTRY_LEN: usize = TZ_NAME_LEN + 3 * size_of::(); -/// The database reserves 40 bytes for each id. -const TZ_NAME_LEN: usize = 40; -/// Size of the version string in the header of `tzdata` file. -/// e.g. `tzdata2024b\0` -const TZDATA_VERSION_LEN: usize = 12; - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_ohos_tzdata_header_and_index() { - let file = File::open("./tests/ohos/tzdata").unwrap(); - let header = TzDataHeader::new(&file).unwrap(); - assert_eq!(header.version, *b"2024a"); - assert_eq!(header.index_offset, 24); - assert_eq!(header.data_offset, 21240); - assert_eq!(header.zonetab_offset, 272428); - - let iter = TzDataIndexes::new::(&file, &header).unwrap(); - assert_eq!(iter.indexes.len(), 442); - assert!(iter.find_timezone(b"Asia/Shanghai").is_some()); - assert!(iter.find_timezone(b"Pacific/Noumea").is_some()); - } - - #[test] - fn test_ohos_tzdata_loading() { - let file = File::open("./tests/ohos/tzdata").unwrap(); - let header = TzDataHeader::new(&file).unwrap(); - let iter = TzDataIndexes::new::(&file, &header).unwrap(); - let timezone = iter.find_timezone(b"Asia/Shanghai").unwrap(); - let tzdata = iter.find_tzdata(&file, &header, timezone).unwrap(); - assert_eq!(tzdata.len(), 393); - } - - #[test] - fn test_invalid_tzdata_header() { - TzDataHeader::new(&b"tzdaaa2024aaaaaaaaaaaaaaa\0"[..]).unwrap_err(); - } - - #[test] - fn test_android_tzdata_header_and_index() { - let file = File::open("./tests/android/tzdata").unwrap(); - let header = TzDataHeader::new(&file).unwrap(); - assert_eq!(header.version, *b"2021a"); - assert_eq!(header.index_offset, 24); - assert_eq!(header.data_offset, 30860); - assert_eq!(header.zonetab_offset, 491837); - - let iter = TzDataIndexes::new::(&file, &header).unwrap(); - assert_eq!(iter.indexes.len(), 593); - assert!(iter.find_timezone(b"Asia/Shanghai").is_some()); - assert!(iter.find_timezone(b"Pacific/Noumea").is_some()); - } - - #[test] - fn test_android_tzdata_loading() { - let file = File::open("./tests/android/tzdata").unwrap(); - let header = TzDataHeader::new(&file).unwrap(); - let iter = TzDataIndexes::new::(&file, &header).unwrap(); - let timezone = iter.find_timezone(b"Asia/Shanghai").unwrap(); - let tzdata = iter.find_tzdata(&file, &header, timezone).unwrap(); - assert_eq!(tzdata.len(), 573); - } - - #[test] - fn test_ohos_tzdata_find() { - let file = File::open("./tests/ohos/tzdata").unwrap(); - let tzdata = find_tz_data::(file, b"Asia/Shanghai").unwrap().unwrap(); - assert_eq!(tzdata.len(), 393); - } - - #[test] - fn test_ohos_tzdata_find_missing() { - let file = File::open("./tests/ohos/tzdata").unwrap(); - assert!(find_tz_data::(file, b"Asia/Sjasdfai").unwrap().is_none()); - } - - #[test] - fn test_android_tzdata_find() { - let file = File::open("./tests/android/tzdata").unwrap(); - let tzdata = find_tz_data::(file, b"Asia/Shanghai").unwrap().unwrap(); - assert_eq!(tzdata.len(), 573); - } - - #[test] - fn test_android_tzdata_find_missing() { - let file = File::open("./tests/android/tzdata").unwrap(); - assert!(find_tz_data::(file, b"Asia/S000000i").unwrap().is_none()); - } - - #[cfg(target_env = "ohos")] - #[test] - fn test_ohos_machine_tz_data_loading() { - let tzdata = for_zone(b"Asia/Shanghai").unwrap().unwrap(); - assert!(!tzdata.is_empty()); - } - - #[cfg(target_os = "android")] - #[test] - fn test_android_machine_tz_data_loading() { - let tzdata = for_zone(b"Asia/Shanghai").unwrap().unwrap(); - assert!(!tzdata.is_empty()); - } -} diff --git a/chrono-0.4.44/src/offset/local/tz_info/mod.rs b/chrono-0.4.44/src/offset/local/tz_info/mod.rs deleted file mode 100644 index a8e53010b3..0000000000 --- a/chrono-0.4.44/src/offset/local/tz_info/mod.rs +++ /dev/null @@ -1,116 +0,0 @@ -#![deny(missing_docs)] -#![allow(dead_code)] -#![warn(unreachable_pub)] - -use std::num::ParseIntError; -use std::str::Utf8Error; -use std::time::SystemTimeError; -use std::{error, fmt, io}; - -mod timezone; -pub(crate) use timezone::TimeZone; - -mod parser; -mod rule; - -/// Unified error type for everything in the crate -#[derive(Debug)] -pub(crate) enum Error { - /// Date time error - DateTime(&'static str), - /// Local time type search error - FindLocalTimeType(&'static str), - /// Local time type error - LocalTimeType(&'static str), - /// Invalid slice for integer conversion - InvalidSlice(&'static str), - /// Invalid Tzif file - InvalidTzFile(&'static str), - /// Invalid TZ string - InvalidTzString(&'static str), - /// I/O error - Io(io::Error), - /// Out of range error - OutOfRange(&'static str), - /// Integer parsing error - ParseInt(ParseIntError), - /// Date time projection error - ProjectDateTime(&'static str), - /// System time error - SystemTime(SystemTimeError), - /// Time zone error - TimeZone(&'static str), - /// Transition rule error - TransitionRule(&'static str), - /// Unsupported Tzif file - UnsupportedTzFile(&'static str), - /// Unsupported TZ string - UnsupportedTzString(&'static str), - /// UTF-8 error - Utf8(Utf8Error), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use Error::*; - match self { - DateTime(error) => write!(f, "invalid date time: {error}"), - FindLocalTimeType(error) => error.fmt(f), - LocalTimeType(error) => write!(f, "invalid local time type: {error}"), - InvalidSlice(error) => error.fmt(f), - InvalidTzString(error) => write!(f, "invalid TZ string: {error}"), - InvalidTzFile(error) => error.fmt(f), - Io(error) => error.fmt(f), - OutOfRange(error) => error.fmt(f), - ParseInt(error) => error.fmt(f), - ProjectDateTime(error) => error.fmt(f), - SystemTime(error) => error.fmt(f), - TransitionRule(error) => write!(f, "invalid transition rule: {error}"), - TimeZone(error) => write!(f, "invalid time zone: {error}"), - UnsupportedTzFile(error) => error.fmt(f), - UnsupportedTzString(error) => write!(f, "unsupported TZ string: {error}"), - Utf8(error) => error.fmt(f), - } - } -} - -impl error::Error for Error {} - -impl From for Error { - fn from(error: io::Error) -> Self { - Error::Io(error) - } -} - -impl From for Error { - fn from(error: ParseIntError) -> Self { - Error::ParseInt(error) - } -} - -impl From for Error { - fn from(error: SystemTimeError) -> Self { - Error::SystemTime(error) - } -} - -impl From for Error { - fn from(error: Utf8Error) -> Self { - Error::Utf8(error) - } -} - -/// Number of hours in one day -const HOURS_PER_DAY: i64 = 24; -/// Number of seconds in one hour -const SECONDS_PER_HOUR: i64 = 3600; -/// Number of seconds in one day -const SECONDS_PER_DAY: i64 = SECONDS_PER_HOUR * HOURS_PER_DAY; -/// Number of days in one week -const DAYS_PER_WEEK: i64 = 7; - -/// Month days in a normal year -const DAY_IN_MONTHS_NORMAL_YEAR: [i64; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; -/// Cumulated month days in a normal year -const CUMUL_DAY_IN_MONTHS_NORMAL_YEAR: [i64; 12] = - [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; diff --git a/chrono-0.4.44/src/offset/local/tz_info/parser.rs b/chrono-0.4.44/src/offset/local/tz_info/parser.rs deleted file mode 100644 index 84c99063c1..0000000000 --- a/chrono-0.4.44/src/offset/local/tz_info/parser.rs +++ /dev/null @@ -1,348 +0,0 @@ -use std::io::{self, ErrorKind}; -use std::iter; -use std::num::ParseIntError; -use std::str::{self, FromStr}; - -use super::Error; -use super::rule::TransitionRule; -use super::timezone::{LeapSecond, LocalTimeType, TimeZone, Transition}; - -pub(super) fn parse(bytes: &[u8]) -> Result { - let mut cursor = Cursor::new(bytes); - let state = State::new(&mut cursor, true)?; - let (state, footer) = match state.header.version { - Version::V1 => match cursor.is_empty() { - true => (state, None), - false => { - return Err(Error::InvalidTzFile("remaining data after end of TZif v1 data block")); - } - }, - Version::V2 | Version::V3 => { - let state = State::new(&mut cursor, false)?; - (state, Some(cursor.remaining())) - } - }; - - let mut transitions = Vec::with_capacity(state.header.transition_count); - for (arr_time, &local_time_type_index) in - state.transition_times.chunks_exact(state.time_size).zip(state.transition_types) - { - let unix_leap_time = - state.parse_time(&arr_time[0..state.time_size], state.header.version)?; - let local_time_type_index = local_time_type_index as usize; - transitions.push(Transition::new(unix_leap_time, local_time_type_index)); - } - - let mut local_time_types = Vec::with_capacity(state.header.type_count); - for arr in state.local_time_types.chunks_exact(6) { - let ut_offset = read_be_i32(&arr[..4])?; - - let is_dst = match arr[4] { - 0 => false, - 1 => true, - _ => return Err(Error::InvalidTzFile("invalid DST indicator")), - }; - - let char_index = arr[5] as usize; - if char_index >= state.header.char_count { - return Err(Error::InvalidTzFile("invalid time zone name char index")); - } - - let position = match state.names[char_index..].iter().position(|&c| c == b'\0') { - Some(position) => position, - None => return Err(Error::InvalidTzFile("invalid time zone name char index")), - }; - - let name = &state.names[char_index..char_index + position]; - let name = if !name.is_empty() { Some(name) } else { None }; - local_time_types.push(LocalTimeType::new(ut_offset, is_dst, name)?); - } - - let mut leap_seconds = Vec::with_capacity(state.header.leap_count); - for arr in state.leap_seconds.chunks_exact(state.time_size + 4) { - let unix_leap_time = state.parse_time(&arr[0..state.time_size], state.header.version)?; - let correction = read_be_i32(&arr[state.time_size..state.time_size + 4])?; - leap_seconds.push(LeapSecond::new(unix_leap_time, correction)); - } - - let std_walls_iter = state.std_walls.iter().copied().chain(iter::repeat(0)); - let ut_locals_iter = state.ut_locals.iter().copied().chain(iter::repeat(0)); - if std_walls_iter.zip(ut_locals_iter).take(state.header.type_count).any(|pair| pair == (0, 1)) { - return Err(Error::InvalidTzFile( - "invalid couple of standard/wall and UT/local indicators", - )); - } - - let extra_rule = match footer { - Some(footer) => { - let footer = str::from_utf8(footer)?; - if !(footer.starts_with('\n') && footer.ends_with('\n')) { - return Err(Error::InvalidTzFile("invalid footer")); - } - - let tz_string = footer.trim_matches(|c: char| c.is_ascii_whitespace()); - if tz_string.starts_with(':') || tz_string.contains('\0') { - return Err(Error::InvalidTzFile("invalid footer")); - } - - match tz_string.is_empty() { - true => None, - false => Some(TransitionRule::from_tz_string( - tz_string.as_bytes(), - state.header.version == Version::V3, - )?), - } - } - None => None, - }; - - TimeZone::new(transitions, local_time_types, leap_seconds, extra_rule) -} - -/// TZif data blocks -struct State<'a> { - header: Header, - /// Time size in bytes - time_size: usize, - /// Transition times data block - transition_times: &'a [u8], - /// Transition types data block - transition_types: &'a [u8], - /// Local time types data block - local_time_types: &'a [u8], - /// Time zone names data block - names: &'a [u8], - /// Leap seconds data block - leap_seconds: &'a [u8], - /// UT/local indicators data block - std_walls: &'a [u8], - /// Standard/wall indicators data block - ut_locals: &'a [u8], -} - -impl<'a> State<'a> { - /// Read TZif data blocks - fn new(cursor: &mut Cursor<'a>, first: bool) -> Result { - let header = Header::new(cursor)?; - let time_size = match first { - true => 4, // We always parse V1 first - false => 8, - }; - - Ok(Self { - time_size, - transition_times: cursor.read_exact(header.transition_count * time_size)?, - transition_types: cursor.read_exact(header.transition_count)?, - local_time_types: cursor.read_exact(header.type_count * 6)?, - names: cursor.read_exact(header.char_count)?, - leap_seconds: cursor.read_exact(header.leap_count * (time_size + 4))?, - std_walls: cursor.read_exact(header.std_wall_count)?, - ut_locals: cursor.read_exact(header.ut_local_count)?, - header, - }) - } - - /// Parse time values - fn parse_time(&self, arr: &[u8], version: Version) -> Result { - match version { - Version::V1 => Ok(read_be_i32(&arr[..4])?.into()), - Version::V2 | Version::V3 => read_be_i64(arr), - } - } -} - -/// TZif header -#[derive(Debug)] -struct Header { - /// TZif version - version: Version, - /// Number of UT/local indicators - ut_local_count: usize, - /// Number of standard/wall indicators - std_wall_count: usize, - /// Number of leap-second records - leap_count: usize, - /// Number of transition times - transition_count: usize, - /// Number of local time type records - type_count: usize, - /// Number of time zone names bytes - char_count: usize, -} - -impl Header { - fn new(cursor: &mut Cursor) -> Result { - let magic = cursor.read_exact(4)?; - if magic != *b"TZif" { - return Err(Error::InvalidTzFile("invalid magic number")); - } - - let version = match cursor.read_exact(1)? { - [0x00] => Version::V1, - [0x32] => Version::V2, - [0x33] => Version::V3, - _ => return Err(Error::UnsupportedTzFile("unsupported TZif version")), - }; - - cursor.read_exact(15)?; - let ut_local_count = cursor.read_be_u32()?; - let std_wall_count = cursor.read_be_u32()?; - let leap_count = cursor.read_be_u32()?; - let transition_count = cursor.read_be_u32()?; - let type_count = cursor.read_be_u32()?; - let char_count = cursor.read_be_u32()?; - - if !(type_count != 0 - && char_count != 0 - && (ut_local_count == 0 || ut_local_count == type_count) - && (std_wall_count == 0 || std_wall_count == type_count)) - { - return Err(Error::InvalidTzFile("invalid header")); - } - - Ok(Self { - version, - ut_local_count: ut_local_count as usize, - std_wall_count: std_wall_count as usize, - leap_count: leap_count as usize, - transition_count: transition_count as usize, - type_count: type_count as usize, - char_count: char_count as usize, - }) - } -} - -/// A `Cursor` contains a slice of a buffer and a read count. -#[derive(Debug, Eq, PartialEq)] -pub(crate) struct Cursor<'a> { - /// Slice representing the remaining data to be read - remaining: &'a [u8], - /// Number of already read bytes - read_count: usize, -} - -impl<'a> Cursor<'a> { - /// Construct a new `Cursor` from remaining data - pub(crate) const fn new(remaining: &'a [u8]) -> Self { - Self { remaining, read_count: 0 } - } - - pub(crate) fn peek(&self) -> Option<&u8> { - self.remaining().first() - } - - /// Returns remaining data - pub(crate) const fn remaining(&self) -> &'a [u8] { - self.remaining - } - - /// Returns `true` if data is remaining - pub(crate) const fn is_empty(&self) -> bool { - self.remaining.is_empty() - } - - pub(crate) fn read_be_u32(&mut self) -> Result { - let mut buf = [0; 4]; - buf.copy_from_slice(self.read_exact(4)?); - Ok(u32::from_be_bytes(buf)) - } - - #[cfg(target_env = "ohos")] - pub(crate) fn seek_after(&mut self, offset: usize) -> Result { - if offset < self.read_count { - return Err(io::Error::from(ErrorKind::UnexpectedEof)); - } - match self.remaining.get((offset - self.read_count)..) { - Some(remaining) => { - self.remaining = remaining; - self.read_count = offset; - Ok(offset) - } - _ => Err(io::Error::from(ErrorKind::UnexpectedEof)), - } - } - - /// Read exactly `count` bytes, reducing remaining data and incrementing read count - pub(crate) fn read_exact(&mut self, count: usize) -> Result<&'a [u8], io::Error> { - match (self.remaining.get(..count), self.remaining.get(count..)) { - (Some(result), Some(remaining)) => { - self.remaining = remaining; - self.read_count += count; - Ok(result) - } - _ => Err(io::Error::from(ErrorKind::UnexpectedEof)), - } - } - - /// Read bytes and compare them to the provided tag - pub(crate) fn read_tag(&mut self, tag: &[u8]) -> Result<(), io::Error> { - if self.read_exact(tag.len())? == tag { - Ok(()) - } else { - Err(io::Error::from(ErrorKind::InvalidData)) - } - } - - /// Read bytes if the remaining data is prefixed by the provided tag - pub(crate) fn read_optional_tag(&mut self, tag: &[u8]) -> Result { - if self.remaining.starts_with(tag) { - self.read_exact(tag.len())?; - Ok(true) - } else { - Ok(false) - } - } - - /// Read bytes as long as the provided predicate is true - pub(crate) fn read_while bool>(&mut self, f: F) -> Result<&'a [u8], io::Error> { - match self.remaining.iter().position(|x| !f(x)) { - None => self.read_exact(self.remaining.len()), - Some(position) => self.read_exact(position), - } - } - - // Parse an integer out of the ASCII digits - pub(crate) fn read_int>(&mut self) -> Result { - let bytes = self.read_while(u8::is_ascii_digit)?; - Ok(str::from_utf8(bytes)?.parse()?) - } - - /// Read bytes until the provided predicate is true - pub(crate) fn read_until bool>(&mut self, f: F) -> Result<&'a [u8], io::Error> { - match self.remaining.iter().position(f) { - None => self.read_exact(self.remaining.len()), - Some(position) => self.read_exact(position), - } - } -} - -pub(crate) fn read_be_i32(bytes: &[u8]) -> Result { - if bytes.len() != 4 { - return Err(Error::InvalidSlice("too short for i32")); - } - - let mut buf = [0; 4]; - buf.copy_from_slice(bytes); - Ok(i32::from_be_bytes(buf)) -} - -pub(crate) fn read_be_i64(bytes: &[u8]) -> Result { - if bytes.len() != 8 { - return Err(Error::InvalidSlice("too short for i64")); - } - - let mut buf = [0; 8]; - buf.copy_from_slice(bytes); - Ok(i64::from_be_bytes(buf)) -} - -/// TZif version -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -enum Version { - /// Version 1 - V1, - /// Version 2 - V2, - /// Version 3 - V3, -} diff --git a/chrono-0.4.44/src/offset/local/tz_info/rule.rs b/chrono-0.4.44/src/offset/local/tz_info/rule.rs deleted file mode 100644 index d428395db0..0000000000 --- a/chrono-0.4.44/src/offset/local/tz_info/rule.rs +++ /dev/null @@ -1,1037 +0,0 @@ -use super::parser::Cursor; -use super::timezone::{LocalTimeType, SECONDS_PER_WEEK}; -use super::{ - CUMUL_DAY_IN_MONTHS_NORMAL_YEAR, DAY_IN_MONTHS_NORMAL_YEAR, DAYS_PER_WEEK, Error, - SECONDS_PER_DAY, -}; -use crate::{Datelike, NaiveDateTime}; -use std::cmp::Ordering; - -/// Transition rule -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub(super) enum TransitionRule { - /// Fixed local time type - Fixed(LocalTimeType), - /// Alternate local time types - Alternate(AlternateTime), -} - -impl TransitionRule { - /// Parse a POSIX TZ string containing a time zone description, as described in [the POSIX documentation of the `TZ` environment variable](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html). - /// - /// TZ string extensions from [RFC 8536](https://datatracker.ietf.org/doc/html/rfc8536#section-3.3.1) may be used. - pub(super) fn from_tz_string( - tz_string: &[u8], - use_string_extensions: bool, - ) -> Result { - let mut cursor = Cursor::new(tz_string); - - let std_time_zone = Some(parse_name(&mut cursor)?); - let std_offset = parse_offset(&mut cursor)?; - - if cursor.is_empty() { - return Ok(LocalTimeType::new(-std_offset, false, std_time_zone)?.into()); - } - - let dst_time_zone = Some(parse_name(&mut cursor)?); - - let dst_offset = match cursor.peek() { - Some(&b',') => std_offset - 3600, - Some(_) => parse_offset(&mut cursor)?, - None => { - return Err(Error::UnsupportedTzString("DST start and end rules must be provided")); - } - }; - - if cursor.is_empty() { - return Err(Error::UnsupportedTzString("DST start and end rules must be provided")); - } - - cursor.read_tag(b",")?; - let (dst_start, dst_start_time) = RuleDay::parse(&mut cursor, use_string_extensions)?; - - cursor.read_tag(b",")?; - let (dst_end, dst_end_time) = RuleDay::parse(&mut cursor, use_string_extensions)?; - - if !cursor.is_empty() { - return Err(Error::InvalidTzString("remaining data after parsing TZ string")); - } - - Ok(AlternateTime::new( - LocalTimeType::new(-std_offset, false, std_time_zone)?, - LocalTimeType::new(-dst_offset, true, dst_time_zone)?, - dst_start, - dst_start_time, - dst_end, - dst_end_time, - )? - .into()) - } - - /// Find the local time type associated to the transition rule at the specified Unix time in seconds - pub(super) fn find_local_time_type(&self, unix_time: i64) -> Result<&LocalTimeType, Error> { - match self { - TransitionRule::Fixed(local_time_type) => Ok(local_time_type), - TransitionRule::Alternate(alternate_time) => { - alternate_time.find_local_time_type(unix_time) - } - } - } - - /// Find the local time type associated to the transition rule at the specified Unix time in seconds - pub(super) fn find_local_time_type_from_local( - &self, - local_time: NaiveDateTime, - ) -> Result, Error> { - match self { - TransitionRule::Fixed(local_time_type) => { - Ok(crate::MappedLocalTime::Single(*local_time_type)) - } - TransitionRule::Alternate(alternate_time) => { - alternate_time.find_local_time_type_from_local(local_time) - } - } - } -} - -impl From for TransitionRule { - fn from(inner: LocalTimeType) -> Self { - TransitionRule::Fixed(inner) - } -} - -impl From for TransitionRule { - fn from(inner: AlternateTime) -> Self { - TransitionRule::Alternate(inner) - } -} - -/// Transition rule representing alternate local time types -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub(super) struct AlternateTime { - /// Local time type for standard time - pub(super) std: LocalTimeType, - /// Local time type for Daylight Saving Time - pub(super) dst: LocalTimeType, - /// Start day of Daylight Saving Time - dst_start: RuleDay, - /// Local start day time of Daylight Saving Time, in seconds - dst_start_time: i32, - /// End day of Daylight Saving Time - dst_end: RuleDay, - /// Local end day time of Daylight Saving Time, in seconds - dst_end_time: i32, -} - -impl AlternateTime { - /// Construct a transition rule representing alternate local time types - const fn new( - std: LocalTimeType, - dst: LocalTimeType, - dst_start: RuleDay, - dst_start_time: i32, - dst_end: RuleDay, - dst_end_time: i32, - ) -> Result { - // Overflow is not possible - if !((dst_start_time as i64).abs() < SECONDS_PER_WEEK - && (dst_end_time as i64).abs() < SECONDS_PER_WEEK) - { - return Err(Error::TransitionRule("invalid DST start or end time")); - } - - Ok(Self { std, dst, dst_start, dst_start_time, dst_end, dst_end_time }) - } - - /// Find the local time type associated to the alternate transition rule at the specified Unix time in seconds - fn find_local_time_type(&self, unix_time: i64) -> Result<&LocalTimeType, Error> { - // Overflow is not possible - let dst_start_time_in_utc = self.dst_start_time as i64 - self.std.ut_offset as i64; - let dst_end_time_in_utc = self.dst_end_time as i64 - self.dst.ut_offset as i64; - - let current_year = match UtcDateTime::from_timespec(unix_time) { - Ok(dt) => dt.year, - Err(error) => return Err(error), - }; - - // Check if the current year is valid for the following computations - if !(i32::MIN + 2..=i32::MAX - 2).contains(¤t_year) { - return Err(Error::OutOfRange("out of range date time")); - } - - let current_year_dst_start_unix_time = - self.dst_start.unix_time(current_year, dst_start_time_in_utc); - let current_year_dst_end_unix_time = - self.dst_end.unix_time(current_year, dst_end_time_in_utc); - - // Check DST start/end Unix times for previous/current/next years to support for transition day times outside of [0h, 24h] range - let is_dst = - match Ord::cmp(¤t_year_dst_start_unix_time, ¤t_year_dst_end_unix_time) { - Ordering::Less | Ordering::Equal => { - if unix_time < current_year_dst_start_unix_time { - let previous_year_dst_end_unix_time = - self.dst_end.unix_time(current_year - 1, dst_end_time_in_utc); - if unix_time < previous_year_dst_end_unix_time { - let previous_year_dst_start_unix_time = - self.dst_start.unix_time(current_year - 1, dst_start_time_in_utc); - previous_year_dst_start_unix_time <= unix_time - } else { - false - } - } else if unix_time < current_year_dst_end_unix_time { - true - } else { - let next_year_dst_start_unix_time = - self.dst_start.unix_time(current_year + 1, dst_start_time_in_utc); - if next_year_dst_start_unix_time <= unix_time { - let next_year_dst_end_unix_time = - self.dst_end.unix_time(current_year + 1, dst_end_time_in_utc); - unix_time < next_year_dst_end_unix_time - } else { - false - } - } - } - Ordering::Greater => { - if unix_time < current_year_dst_end_unix_time { - let previous_year_dst_start_unix_time = - self.dst_start.unix_time(current_year - 1, dst_start_time_in_utc); - if unix_time < previous_year_dst_start_unix_time { - let previous_year_dst_end_unix_time = - self.dst_end.unix_time(current_year - 1, dst_end_time_in_utc); - unix_time < previous_year_dst_end_unix_time - } else { - true - } - } else if unix_time < current_year_dst_start_unix_time { - false - } else { - let next_year_dst_end_unix_time = - self.dst_end.unix_time(current_year + 1, dst_end_time_in_utc); - if next_year_dst_end_unix_time <= unix_time { - let next_year_dst_start_unix_time = - self.dst_start.unix_time(current_year + 1, dst_start_time_in_utc); - next_year_dst_start_unix_time <= unix_time - } else { - true - } - } - } - }; - - if is_dst { Ok(&self.dst) } else { Ok(&self.std) } - } - - fn find_local_time_type_from_local( - &self, - local_time: NaiveDateTime, - ) -> Result, Error> { - // Year must be between i32::MIN + 2 and i32::MAX - 2, year in NaiveDate is always smaller. - let current_year = local_time.year(); - let local_time = local_time.and_utc().timestamp(); - - let dst_start_transition_start = - self.dst_start.unix_time(current_year, 0) + i64::from(self.dst_start_time); - let dst_start_transition_end = self.dst_start.unix_time(current_year, 0) - + i64::from(self.dst_start_time) - + i64::from(self.dst.ut_offset) - - i64::from(self.std.ut_offset); - - let dst_end_transition_start = - self.dst_end.unix_time(current_year, 0) + i64::from(self.dst_end_time); - let dst_end_transition_end = self.dst_end.unix_time(current_year, 0) - + i64::from(self.dst_end_time) - + i64::from(self.std.ut_offset) - - i64::from(self.dst.ut_offset); - - match self.std.ut_offset.cmp(&self.dst.ut_offset) { - Ordering::Equal => Ok(crate::MappedLocalTime::Single(self.std)), - Ordering::Less => { - if self.dst_start.transition_date(current_year).0 - < self.dst_end.transition_date(current_year).0 - { - // northern hemisphere - // For the DST END transition, the `start` happens at a later timestamp than the `end`. - if local_time <= dst_start_transition_start { - Ok(crate::MappedLocalTime::Single(self.std)) - } else if local_time > dst_start_transition_start - && local_time < dst_start_transition_end - { - Ok(crate::MappedLocalTime::None) - } else if local_time >= dst_start_transition_end - && local_time < dst_end_transition_end - { - Ok(crate::MappedLocalTime::Single(self.dst)) - } else if local_time >= dst_end_transition_end - && local_time <= dst_end_transition_start - { - Ok(crate::MappedLocalTime::Ambiguous(self.std, self.dst)) - } else { - Ok(crate::MappedLocalTime::Single(self.std)) - } - } else { - // southern hemisphere regular DST - // For the DST END transition, the `start` happens at a later timestamp than the `end`. - if local_time < dst_end_transition_end { - Ok(crate::MappedLocalTime::Single(self.dst)) - } else if local_time >= dst_end_transition_end - && local_time <= dst_end_transition_start - { - Ok(crate::MappedLocalTime::Ambiguous(self.std, self.dst)) - } else if local_time > dst_end_transition_end - && local_time < dst_start_transition_start - { - Ok(crate::MappedLocalTime::Single(self.std)) - } else if local_time >= dst_start_transition_start - && local_time < dst_start_transition_end - { - Ok(crate::MappedLocalTime::None) - } else { - Ok(crate::MappedLocalTime::Single(self.dst)) - } - } - } - Ordering::Greater => { - if self.dst_start.transition_date(current_year).0 - < self.dst_end.transition_date(current_year).0 - { - // southern hemisphere reverse DST - // For the DST END transition, the `start` happens at a later timestamp than the `end`. - if local_time < dst_start_transition_end { - Ok(crate::MappedLocalTime::Single(self.std)) - } else if local_time >= dst_start_transition_end - && local_time <= dst_start_transition_start - { - Ok(crate::MappedLocalTime::Ambiguous(self.dst, self.std)) - } else if local_time > dst_start_transition_start - && local_time < dst_end_transition_start - { - Ok(crate::MappedLocalTime::Single(self.dst)) - } else if local_time >= dst_end_transition_start - && local_time < dst_end_transition_end - { - Ok(crate::MappedLocalTime::None) - } else { - Ok(crate::MappedLocalTime::Single(self.std)) - } - } else { - // northern hemisphere reverse DST - // For the DST END transition, the `start` happens at a later timestamp than the `end`. - if local_time <= dst_end_transition_start { - Ok(crate::MappedLocalTime::Single(self.dst)) - } else if local_time > dst_end_transition_start - && local_time < dst_end_transition_end - { - Ok(crate::MappedLocalTime::None) - } else if local_time >= dst_end_transition_end - && local_time < dst_start_transition_end - { - Ok(crate::MappedLocalTime::Single(self.std)) - } else if local_time >= dst_start_transition_end - && local_time <= dst_start_transition_start - { - Ok(crate::MappedLocalTime::Ambiguous(self.dst, self.std)) - } else { - Ok(crate::MappedLocalTime::Single(self.dst)) - } - } - } - } - } -} - -/// Parse time zone name -fn parse_name<'a>(cursor: &mut Cursor<'a>) -> Result<&'a [u8], Error> { - match cursor.peek() { - Some(b'<') => {} - _ => return Ok(cursor.read_while(u8::is_ascii_alphabetic)?), - } - - cursor.read_exact(1)?; - let unquoted = cursor.read_until(|&x| x == b'>')?; - cursor.read_exact(1)?; - Ok(unquoted) -} - -/// Parse time zone offset -fn parse_offset(cursor: &mut Cursor) -> Result { - let (sign, hour, minute, second) = parse_signed_hhmmss(cursor)?; - - if !(0..=24).contains(&hour) { - return Err(Error::InvalidTzString("invalid offset hour")); - } - if !(0..=59).contains(&minute) { - return Err(Error::InvalidTzString("invalid offset minute")); - } - if !(0..=59).contains(&second) { - return Err(Error::InvalidTzString("invalid offset second")); - } - - Ok(sign * (hour * 3600 + minute * 60 + second)) -} - -/// Parse transition rule time -fn parse_rule_time(cursor: &mut Cursor) -> Result { - let (hour, minute, second) = parse_hhmmss(cursor)?; - - if !(0..=24).contains(&hour) { - return Err(Error::InvalidTzString("invalid day time hour")); - } - if !(0..=59).contains(&minute) { - return Err(Error::InvalidTzString("invalid day time minute")); - } - if !(0..=59).contains(&second) { - return Err(Error::InvalidTzString("invalid day time second")); - } - - Ok(hour * 3600 + minute * 60 + second) -} - -/// Parse transition rule time with TZ string extensions -fn parse_rule_time_extended(cursor: &mut Cursor) -> Result { - let (sign, hour, minute, second) = parse_signed_hhmmss(cursor)?; - - if !(-167..=167).contains(&hour) { - return Err(Error::InvalidTzString("invalid day time hour")); - } - if !(0..=59).contains(&minute) { - return Err(Error::InvalidTzString("invalid day time minute")); - } - if !(0..=59).contains(&second) { - return Err(Error::InvalidTzString("invalid day time second")); - } - - Ok(sign * (hour * 3600 + minute * 60 + second)) -} - -/// Parse hours, minutes and seconds -fn parse_hhmmss(cursor: &mut Cursor) -> Result<(i32, i32, i32), Error> { - let hour = cursor.read_int()?; - - let mut minute = 0; - let mut second = 0; - - if cursor.read_optional_tag(b":")? { - minute = cursor.read_int()?; - - if cursor.read_optional_tag(b":")? { - second = cursor.read_int()?; - } - } - - Ok((hour, minute, second)) -} - -/// Parse signed hours, minutes and seconds -fn parse_signed_hhmmss(cursor: &mut Cursor) -> Result<(i32, i32, i32, i32), Error> { - let mut sign = 1; - if let Some(&c) = cursor.peek() { - if c == b'+' || c == b'-' { - cursor.read_exact(1)?; - if c == b'-' { - sign = -1; - } - } - } - - let (hour, minute, second) = parse_hhmmss(cursor)?; - Ok((sign, hour, minute, second)) -} - -/// Transition rule day -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -enum RuleDay { - /// Julian day in `[1, 365]`, without taking occasional Feb 29 into account, which is not referenceable - Julian1WithoutLeap(u16), - /// Zero-based Julian day in `[0, 365]`, taking occasional Feb 29 into account - Julian0WithLeap(u16), - /// Day represented by a month, a month week and a week day - MonthWeekday { - /// Month in `[1, 12]` - month: u8, - /// Week of the month in `[1, 5]`, with `5` representing the last week of the month - week: u8, - /// Day of the week in `[0, 6]` from Sunday - week_day: u8, - }, -} - -impl RuleDay { - /// Parse transition rule - fn parse(cursor: &mut Cursor, use_string_extensions: bool) -> Result<(Self, i32), Error> { - let date = match cursor.peek() { - Some(b'M') => { - cursor.read_exact(1)?; - let month = cursor.read_int()?; - cursor.read_tag(b".")?; - let week = cursor.read_int()?; - cursor.read_tag(b".")?; - let week_day = cursor.read_int()?; - RuleDay::month_weekday(month, week, week_day)? - } - Some(b'J') => { - cursor.read_exact(1)?; - RuleDay::julian_1(cursor.read_int()?)? - } - _ => RuleDay::julian_0(cursor.read_int()?)?, - }; - - Ok(( - date, - match (cursor.read_optional_tag(b"/")?, use_string_extensions) { - (false, _) => 2 * 3600, - (true, true) => parse_rule_time_extended(cursor)?, - (true, false) => parse_rule_time(cursor)?, - }, - )) - } - - /// Construct a transition rule day represented by a Julian day in `[1, 365]`, without taking occasional Feb 29 into account, which is not referenceable - fn julian_1(julian_day_1: u16) -> Result { - if !(1..=365).contains(&julian_day_1) { - return Err(Error::TransitionRule("invalid rule day julian day")); - } - - Ok(RuleDay::Julian1WithoutLeap(julian_day_1)) - } - - /// Construct a transition rule day represented by a zero-based Julian day in `[0, 365]`, taking occasional Feb 29 into account - const fn julian_0(julian_day_0: u16) -> Result { - if julian_day_0 > 365 { - return Err(Error::TransitionRule("invalid rule day julian day")); - } - - Ok(RuleDay::Julian0WithLeap(julian_day_0)) - } - - /// Construct a transition rule day represented by a month, a month week and a week day - fn month_weekday(month: u8, week: u8, week_day: u8) -> Result { - if !(1..=12).contains(&month) { - return Err(Error::TransitionRule("invalid rule day month")); - } - - if !(1..=5).contains(&week) { - return Err(Error::TransitionRule("invalid rule day week")); - } - - if week_day > 6 { - return Err(Error::TransitionRule("invalid rule day week day")); - } - - Ok(RuleDay::MonthWeekday { month, week, week_day }) - } - - /// Get the transition date for the provided year - /// - /// ## Outputs - /// - /// * `month`: Month in `[1, 12]` - /// * `month_day`: Day of the month in `[1, 31]` - fn transition_date(&self, year: i32) -> (usize, i64) { - match *self { - RuleDay::Julian1WithoutLeap(year_day) => { - let year_day = year_day as i64; - - let month = match CUMUL_DAY_IN_MONTHS_NORMAL_YEAR.binary_search(&(year_day - 1)) { - Ok(x) => x + 1, - Err(x) => x, - }; - - let month_day = year_day - CUMUL_DAY_IN_MONTHS_NORMAL_YEAR[month - 1]; - - (month, month_day) - } - RuleDay::Julian0WithLeap(year_day) => { - let leap = is_leap_year(year) as i64; - - let cumul_day_in_months = [ - 0, - 31, - 59 + leap, - 90 + leap, - 120 + leap, - 151 + leap, - 181 + leap, - 212 + leap, - 243 + leap, - 273 + leap, - 304 + leap, - 334 + leap, - ]; - - let year_day = year_day as i64; - - let month = match cumul_day_in_months.binary_search(&year_day) { - Ok(x) => x + 1, - Err(x) => x, - }; - - let month_day = 1 + year_day - cumul_day_in_months[month - 1]; - - (month, month_day) - } - RuleDay::MonthWeekday { month: rule_month, week, week_day } => { - let leap = is_leap_year(year) as i64; - - let month = rule_month as usize; - - let mut day_in_month = DAY_IN_MONTHS_NORMAL_YEAR[month - 1]; - if month == 2 { - day_in_month += leap; - } - - let week_day_of_first_month_day = - (4 + days_since_unix_epoch(year, month, 1)).rem_euclid(DAYS_PER_WEEK); - let first_week_day_occurrence_in_month = - 1 + (week_day as i64 - week_day_of_first_month_day).rem_euclid(DAYS_PER_WEEK); - - let mut month_day = - first_week_day_occurrence_in_month + (week as i64 - 1) * DAYS_PER_WEEK; - if month_day > day_in_month { - month_day -= DAYS_PER_WEEK - } - - (month, month_day) - } - } - } - - /// Returns the UTC Unix time in seconds associated to the transition date for the provided year - fn unix_time(&self, year: i32, day_time_in_utc: i64) -> i64 { - let (month, month_day) = self.transition_date(year); - days_since_unix_epoch(year, month, month_day) * SECONDS_PER_DAY + day_time_in_utc - } -} - -/// UTC date time exprimed in the [proleptic gregorian calendar](https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar) -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] -pub(crate) struct UtcDateTime { - /// Year - pub(crate) year: i32, - /// Month in `[1, 12]` - pub(crate) month: u8, - /// Day of the month in `[1, 31]` - pub(crate) month_day: u8, - /// Hours since midnight in `[0, 23]` - pub(crate) hour: u8, - /// Minutes in `[0, 59]` - pub(crate) minute: u8, - /// Seconds in `[0, 60]`, with a possible leap second - pub(crate) second: u8, -} - -impl UtcDateTime { - /// Construct a UTC date time from a Unix time in seconds and nanoseconds - pub(crate) fn from_timespec(unix_time: i64) -> Result { - let seconds = match unix_time.checked_sub(UNIX_OFFSET_SECS) { - Some(seconds) => seconds, - None => return Err(Error::OutOfRange("out of range operation")), - }; - - let mut remaining_days = seconds / SECONDS_PER_DAY; - let mut remaining_seconds = seconds % SECONDS_PER_DAY; - if remaining_seconds < 0 { - remaining_seconds += SECONDS_PER_DAY; - remaining_days -= 1; - } - - let mut cycles_400_years = remaining_days / DAYS_PER_400_YEARS; - remaining_days %= DAYS_PER_400_YEARS; - if remaining_days < 0 { - remaining_days += DAYS_PER_400_YEARS; - cycles_400_years -= 1; - } - - let cycles_100_years = Ord::min(remaining_days / DAYS_PER_100_YEARS, 3); - remaining_days -= cycles_100_years * DAYS_PER_100_YEARS; - - let cycles_4_years = Ord::min(remaining_days / DAYS_PER_4_YEARS, 24); - remaining_days -= cycles_4_years * DAYS_PER_4_YEARS; - - let remaining_years = Ord::min(remaining_days / DAYS_PER_NORMAL_YEAR, 3); - remaining_days -= remaining_years * DAYS_PER_NORMAL_YEAR; - - let mut year = OFFSET_YEAR - + remaining_years - + cycles_4_years * 4 - + cycles_100_years * 100 - + cycles_400_years * 400; - - let mut month = 0; - while month < DAY_IN_MONTHS_LEAP_YEAR_FROM_MARCH.len() { - let days = DAY_IN_MONTHS_LEAP_YEAR_FROM_MARCH[month]; - if remaining_days < days { - break; - } - remaining_days -= days; - month += 1; - } - month += 2; - - if month >= MONTHS_PER_YEAR as usize { - month -= MONTHS_PER_YEAR as usize; - year += 1; - } - month += 1; - - let month_day = 1 + remaining_days; - - let hour = remaining_seconds / SECONDS_PER_HOUR; - let minute = (remaining_seconds / SECONDS_PER_MINUTE) % MINUTES_PER_HOUR; - let second = remaining_seconds % SECONDS_PER_MINUTE; - - let year = match year >= i32::MIN as i64 && year <= i32::MAX as i64 { - true => year as i32, - false => return Err(Error::OutOfRange("i64 is out of range for i32")), - }; - - Ok(Self { - year, - month: month as u8, - month_day: month_day as u8, - hour: hour as u8, - minute: minute as u8, - second: second as u8, - }) - } -} - -/// Number of nanoseconds in one second -const NANOSECONDS_PER_SECOND: u32 = 1_000_000_000; -/// Number of seconds in one minute -const SECONDS_PER_MINUTE: i64 = 60; -/// Number of seconds in one hour -const SECONDS_PER_HOUR: i64 = 3600; -/// Number of minutes in one hour -const MINUTES_PER_HOUR: i64 = 60; -/// Number of months in one year -const MONTHS_PER_YEAR: i64 = 12; -/// Number of days in a normal year -const DAYS_PER_NORMAL_YEAR: i64 = 365; -/// Number of days in 4 years (including 1 leap year) -const DAYS_PER_4_YEARS: i64 = DAYS_PER_NORMAL_YEAR * 4 + 1; -/// Number of days in 100 years (including 24 leap years) -const DAYS_PER_100_YEARS: i64 = DAYS_PER_NORMAL_YEAR * 100 + 24; -/// Number of days in 400 years (including 97 leap years) -const DAYS_PER_400_YEARS: i64 = DAYS_PER_NORMAL_YEAR * 400 + 97; -/// Unix time at `2000-03-01T00:00:00Z` (Wednesday) -const UNIX_OFFSET_SECS: i64 = 951868800; -/// Offset year -const OFFSET_YEAR: i64 = 2000; -/// Month days in a leap year from March -const DAY_IN_MONTHS_LEAP_YEAR_FROM_MARCH: [i64; 12] = - [31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29]; - -/// Compute the number of days since Unix epoch (`1970-01-01T00:00:00Z`). -/// -/// ## Inputs -/// -/// * `year`: Year -/// * `month`: Month in `[1, 12]` -/// * `month_day`: Day of the month in `[1, 31]` -pub(crate) const fn days_since_unix_epoch(year: i32, month: usize, month_day: i64) -> i64 { - let is_leap_year = is_leap_year(year); - - let year = year as i64; - - let mut result = (year - 1970) * 365; - - if year >= 1970 { - result += (year - 1968) / 4; - result -= (year - 1900) / 100; - result += (year - 1600) / 400; - - if is_leap_year && month < 3 { - result -= 1; - } - } else { - result += (year - 1972) / 4; - result -= (year - 2000) / 100; - result += (year - 2000) / 400; - - if is_leap_year && month >= 3 { - result += 1; - } - } - - result += CUMUL_DAY_IN_MONTHS_NORMAL_YEAR[month - 1] + month_day - 1; - - result -} - -/// Check if a year is a leap year -pub(crate) const fn is_leap_year(year: i32) -> bool { - year % 400 == 0 || (year % 4 == 0 && year % 100 != 0) -} - -#[cfg(test)] -mod tests { - use super::super::timezone::Transition; - use super::super::{Error, TimeZone}; - use super::{AlternateTime, LocalTimeType, RuleDay, TransitionRule}; - - #[test] - fn test_quoted() -> Result<(), Error> { - let transition_rule = TransitionRule::from_tz_string(b"<-03>+3<+03>-3,J1,J365", false)?; - assert_eq!( - transition_rule, - AlternateTime::new( - LocalTimeType::new(-10800, false, Some(b"-03"))?, - LocalTimeType::new(10800, true, Some(b"+03"))?, - RuleDay::julian_1(1)?, - 7200, - RuleDay::julian_1(365)?, - 7200, - )? - .into() - ); - Ok(()) - } - - #[test] - fn test_full() -> Result<(), Error> { - let tz_string = b"NZST-12:00:00NZDT-13:00:00,M10.1.0/02:00:00,M3.3.0/02:00:00"; - let transition_rule = TransitionRule::from_tz_string(tz_string, false)?; - assert_eq!( - transition_rule, - AlternateTime::new( - LocalTimeType::new(43200, false, Some(b"NZST"))?, - LocalTimeType::new(46800, true, Some(b"NZDT"))?, - RuleDay::month_weekday(10, 1, 0)?, - 7200, - RuleDay::month_weekday(3, 3, 0)?, - 7200, - )? - .into() - ); - Ok(()) - } - - #[test] - fn test_negative_dst() -> Result<(), Error> { - let tz_string = b"IST-1GMT0,M10.5.0,M3.5.0/1"; - let transition_rule = TransitionRule::from_tz_string(tz_string, false)?; - assert_eq!( - transition_rule, - AlternateTime::new( - LocalTimeType::new(3600, false, Some(b"IST"))?, - LocalTimeType::new(0, true, Some(b"GMT"))?, - RuleDay::month_weekday(10, 5, 0)?, - 7200, - RuleDay::month_weekday(3, 5, 0)?, - 3600, - )? - .into() - ); - Ok(()) - } - - #[test] - fn test_negative_hour() -> Result<(), Error> { - let tz_string = b"<-03>3<-02>,M3.5.0/-2,M10.5.0/-1"; - assert!(TransitionRule::from_tz_string(tz_string, false).is_err()); - - assert_eq!( - TransitionRule::from_tz_string(tz_string, true)?, - AlternateTime::new( - LocalTimeType::new(-10800, false, Some(b"-03"))?, - LocalTimeType::new(-7200, true, Some(b"-02"))?, - RuleDay::month_weekday(3, 5, 0)?, - -7200, - RuleDay::month_weekday(10, 5, 0)?, - -3600, - )? - .into() - ); - Ok(()) - } - - #[test] - fn test_all_year_dst() -> Result<(), Error> { - let tz_string = b"EST5EDT,0/0,J365/25"; - assert!(TransitionRule::from_tz_string(tz_string, false).is_err()); - - assert_eq!( - TransitionRule::from_tz_string(tz_string, true)?, - AlternateTime::new( - LocalTimeType::new(-18000, false, Some(b"EST"))?, - LocalTimeType::new(-14400, true, Some(b"EDT"))?, - RuleDay::julian_0(0)?, - 0, - RuleDay::julian_1(365)?, - 90000, - )? - .into() - ); - Ok(()) - } - - #[test] - fn test_v3_file() -> Result<(), Error> { - let bytes = b"TZif3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x04\0\0\x1c\x20\0\0IST\0TZif3\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x01\0\0\0\0\0\0\0\x01\0\0\0\x01\0\0\0\x04\0\0\0\0\x7f\xe8\x17\x80\0\0\0\x1c\x20\0\0IST\0\x01\x01\x0aIST-2IDT,M3.4.4/26,M10.5.0\x0a"; - - let time_zone = TimeZone::from_tz_data(bytes)?; - - let time_zone_result = TimeZone::new( - vec![Transition::new(2145916800, 0)], - vec![LocalTimeType::new(7200, false, Some(b"IST"))?], - Vec::new(), - Some(TransitionRule::from(AlternateTime::new( - LocalTimeType::new(7200, false, Some(b"IST"))?, - LocalTimeType::new(10800, true, Some(b"IDT"))?, - RuleDay::month_weekday(3, 4, 4)?, - 93600, - RuleDay::month_weekday(10, 5, 0)?, - 7200, - )?)), - )?; - - assert_eq!(time_zone, time_zone_result); - - Ok(()) - } - - #[test] - fn test_rule_day() -> Result<(), Error> { - let rule_day_j1 = RuleDay::julian_1(60)?; - assert_eq!(rule_day_j1.transition_date(2000), (3, 1)); - assert_eq!(rule_day_j1.transition_date(2001), (3, 1)); - assert_eq!(rule_day_j1.unix_time(2000, 43200), 951912000); - - let rule_day_j0 = RuleDay::julian_0(59)?; - assert_eq!(rule_day_j0.transition_date(2000), (2, 29)); - assert_eq!(rule_day_j0.transition_date(2001), (3, 1)); - assert_eq!(rule_day_j0.unix_time(2000, 43200), 951825600); - - let rule_day_mwd = RuleDay::month_weekday(2, 5, 2)?; - assert_eq!(rule_day_mwd.transition_date(2000), (2, 29)); - assert_eq!(rule_day_mwd.transition_date(2001), (2, 27)); - assert_eq!(rule_day_mwd.unix_time(2000, 43200), 951825600); - assert_eq!(rule_day_mwd.unix_time(2001, 43200), 983275200); - - Ok(()) - } - - #[test] - fn test_transition_rule() -> Result<(), Error> { - let transition_rule_fixed = TransitionRule::from(LocalTimeType::new(-36000, false, None)?); - assert_eq!(transition_rule_fixed.find_local_time_type(0)?.offset(), -36000); - - let transition_rule_dst = TransitionRule::from(AlternateTime::new( - LocalTimeType::new(43200, false, Some(b"NZST"))?, - LocalTimeType::new(46800, true, Some(b"NZDT"))?, - RuleDay::month_weekday(10, 1, 0)?, - 7200, - RuleDay::month_weekday(3, 3, 0)?, - 7200, - )?); - - assert_eq!(transition_rule_dst.find_local_time_type(953384399)?.offset(), 46800); - assert_eq!(transition_rule_dst.find_local_time_type(953384400)?.offset(), 43200); - assert_eq!(transition_rule_dst.find_local_time_type(970322399)?.offset(), 43200); - assert_eq!(transition_rule_dst.find_local_time_type(970322400)?.offset(), 46800); - - let transition_rule_negative_dst = TransitionRule::from(AlternateTime::new( - LocalTimeType::new(3600, false, Some(b"IST"))?, - LocalTimeType::new(0, true, Some(b"GMT"))?, - RuleDay::month_weekday(10, 5, 0)?, - 7200, - RuleDay::month_weekday(3, 5, 0)?, - 3600, - )?); - - assert_eq!(transition_rule_negative_dst.find_local_time_type(954032399)?.offset(), 0); - assert_eq!(transition_rule_negative_dst.find_local_time_type(954032400)?.offset(), 3600); - assert_eq!(transition_rule_negative_dst.find_local_time_type(972781199)?.offset(), 3600); - assert_eq!(transition_rule_negative_dst.find_local_time_type(972781200)?.offset(), 0); - - let transition_rule_negative_time_1 = TransitionRule::from(AlternateTime::new( - LocalTimeType::new(0, false, None)?, - LocalTimeType::new(0, true, None)?, - RuleDay::julian_0(100)?, - 0, - RuleDay::julian_0(101)?, - -86500, - )?); - - assert!(transition_rule_negative_time_1.find_local_time_type(8639899)?.is_dst()); - assert!(!transition_rule_negative_time_1.find_local_time_type(8639900)?.is_dst()); - assert!(!transition_rule_negative_time_1.find_local_time_type(8639999)?.is_dst()); - assert!(transition_rule_negative_time_1.find_local_time_type(8640000)?.is_dst()); - - let transition_rule_negative_time_2 = TransitionRule::from(AlternateTime::new( - LocalTimeType::new(-10800, false, Some(b"-03"))?, - LocalTimeType::new(-7200, true, Some(b"-02"))?, - RuleDay::month_weekday(3, 5, 0)?, - -7200, - RuleDay::month_weekday(10, 5, 0)?, - -3600, - )?); - - assert_eq!( - transition_rule_negative_time_2.find_local_time_type(954032399)?.offset(), - -10800 - ); - assert_eq!( - transition_rule_negative_time_2.find_local_time_type(954032400)?.offset(), - -7200 - ); - assert_eq!( - transition_rule_negative_time_2.find_local_time_type(972781199)?.offset(), - -7200 - ); - assert_eq!( - transition_rule_negative_time_2.find_local_time_type(972781200)?.offset(), - -10800 - ); - - let transition_rule_all_year_dst = TransitionRule::from(AlternateTime::new( - LocalTimeType::new(-18000, false, Some(b"EST"))?, - LocalTimeType::new(-14400, true, Some(b"EDT"))?, - RuleDay::julian_0(0)?, - 0, - RuleDay::julian_1(365)?, - 90000, - )?); - - assert_eq!(transition_rule_all_year_dst.find_local_time_type(946702799)?.offset(), -14400); - assert_eq!(transition_rule_all_year_dst.find_local_time_type(946702800)?.offset(), -14400); - - Ok(()) - } - - #[test] - fn test_transition_rule_overflow() -> Result<(), Error> { - let transition_rule_1 = TransitionRule::from(AlternateTime::new( - LocalTimeType::new(-1, false, None)?, - LocalTimeType::new(-1, true, None)?, - RuleDay::julian_1(365)?, - 0, - RuleDay::julian_1(1)?, - 0, - )?); - - let transition_rule_2 = TransitionRule::from(AlternateTime::new( - LocalTimeType::new(1, false, None)?, - LocalTimeType::new(1, true, None)?, - RuleDay::julian_1(365)?, - 0, - RuleDay::julian_1(1)?, - 0, - )?); - - let min_unix_time = -67768100567971200; - let max_unix_time = 67767976233532799; - - assert!(matches!( - transition_rule_1.find_local_time_type(min_unix_time), - Err(Error::OutOfRange(_)) - )); - assert!(matches!( - transition_rule_2.find_local_time_type(max_unix_time), - Err(Error::OutOfRange(_)) - )); - - Ok(()) - } -} diff --git a/chrono-0.4.44/src/offset/local/tz_info/timezone.rs b/chrono-0.4.44/src/offset/local/tz_info/timezone.rs deleted file mode 100644 index 77497994ae..0000000000 --- a/chrono-0.4.44/src/offset/local/tz_info/timezone.rs +++ /dev/null @@ -1,948 +0,0 @@ -//! Types related to a time zone. - -use std::fs::{self, File}; -use std::io::{self, Read}; -use std::path::{Path, PathBuf}; -use std::{cmp::Ordering, fmt, str}; - -use super::rule::{AlternateTime, TransitionRule}; -use super::{DAYS_PER_WEEK, Error, SECONDS_PER_DAY, parser}; -use crate::NaiveDateTime; - -/// Time zone -#[derive(Debug, Clone, Eq, PartialEq)] -pub(crate) struct TimeZone { - /// List of transitions - transitions: Vec, - /// List of local time types (cannot be empty) - local_time_types: Vec, - /// List of leap seconds - leap_seconds: Vec, - /// Extra transition rule applicable after the last transition - extra_rule: Option, -} - -impl TimeZone { - /// Returns local time zone. - /// - /// This method in not supported on non-UNIX platforms, and returns the UTC time zone instead. - pub(crate) fn local(env_tz: Option<&str>) -> Result { - match env_tz { - Some(tz) => Self::from_posix_tz(tz), - None => Self::from_posix_tz("localtime"), - } - } - - /// Construct a time zone from a POSIX TZ string, as described in [the POSIX documentation of the `TZ` environment variable](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html). - fn from_posix_tz(tz_string: &str) -> Result { - // It is commonly agreed (but not standard) that setting an empty `TZ=` uses UTC. - if tz_string.is_empty() { - return Ok(Self::utc()); - } - - if tz_string == "localtime" { - return Self::from_tz_data(&fs::read("/etc/localtime")?); - } - - // attributes are not allowed on if blocks in Rust 1.38 - #[cfg(any(target_os = "android", target_env = "ohos"))] - { - if let Ok(Some(bytes)) = crate::offset::local::tz_data::for_zone(tz_string) { - return Self::from_tz_data(&bytes); - } - } - - let mut chars = tz_string.chars(); - if chars.next() == Some(':') { - return Self::from_file(&mut find_tz_file(chars.as_str())?); - } - - if let Ok(mut file) = find_tz_file(tz_string) { - return Self::from_file(&mut file); - } - - // TZ string extensions are not allowed - let tz_string = tz_string.trim_matches(|c: char| c.is_ascii_whitespace()); - let rule = TransitionRule::from_tz_string(tz_string.as_bytes(), false)?; - Self::new( - vec![], - match rule { - TransitionRule::Fixed(local_time_type) => vec![local_time_type], - TransitionRule::Alternate(AlternateTime { std, dst, .. }) => vec![std, dst], - }, - vec![], - Some(rule), - ) - } - - /// Construct a time zone - pub(super) fn new( - transitions: Vec, - local_time_types: Vec, - leap_seconds: Vec, - extra_rule: Option, - ) -> Result { - let new = Self { transitions, local_time_types, leap_seconds, extra_rule }; - new.as_ref().validate()?; - Ok(new) - } - - /// Construct a time zone from the contents of a time zone file - fn from_file(file: &mut File) -> Result { - let mut bytes = Vec::new(); - file.read_to_end(&mut bytes)?; - Self::from_tz_data(&bytes) - } - - /// Construct a time zone from the contents of a time zone file - /// - /// Parse TZif data as described in [RFC 8536](https://datatracker.ietf.org/doc/html/rfc8536). - pub(crate) fn from_tz_data(bytes: &[u8]) -> Result { - parser::parse(bytes) - } - - /// Construct a time zone with the specified UTC offset in seconds - fn fixed(ut_offset: i32) -> Result { - Ok(Self { - transitions: Vec::new(), - local_time_types: vec![LocalTimeType::with_offset(ut_offset)?], - leap_seconds: Vec::new(), - extra_rule: None, - }) - } - - /// Construct the time zone associated to UTC - pub(crate) fn utc() -> Self { - Self { - transitions: Vec::new(), - local_time_types: vec![LocalTimeType::UTC], - leap_seconds: Vec::new(), - extra_rule: None, - } - } - - /// Find the local time type associated to the time zone at the specified Unix time in seconds - pub(crate) fn find_local_time_type(&self, unix_time: i64) -> Result<&LocalTimeType, Error> { - self.as_ref().find_local_time_type(unix_time) - } - - pub(crate) fn find_local_time_type_from_local( - &self, - local_time: NaiveDateTime, - ) -> Result, Error> { - self.as_ref().find_local_time_type_from_local(local_time) - } - - /// Returns a reference to the time zone - fn as_ref(&self) -> TimeZoneRef<'_> { - TimeZoneRef { - transitions: &self.transitions, - local_time_types: &self.local_time_types, - leap_seconds: &self.leap_seconds, - extra_rule: &self.extra_rule, - } - } -} - -/// Reference to a time zone -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub(crate) struct TimeZoneRef<'a> { - /// List of transitions - transitions: &'a [Transition], - /// List of local time types (cannot be empty) - local_time_types: &'a [LocalTimeType], - /// List of leap seconds - leap_seconds: &'a [LeapSecond], - /// Extra transition rule applicable after the last transition - extra_rule: &'a Option, -} - -impl<'a> TimeZoneRef<'a> { - /// Find the local time type associated to the time zone at the specified Unix time in seconds - pub(crate) fn find_local_time_type(&self, unix_time: i64) -> Result<&'a LocalTimeType, Error> { - let extra_rule = match self.transitions.last() { - None => match self.extra_rule { - Some(extra_rule) => extra_rule, - None => return Ok(&self.local_time_types[0]), - }, - Some(last_transition) => { - let unix_leap_time = match self.unix_time_to_unix_leap_time(unix_time) { - Ok(unix_leap_time) => unix_leap_time, - Err(Error::OutOfRange(error)) => return Err(Error::FindLocalTimeType(error)), - Err(err) => return Err(err), - }; - - if unix_leap_time >= last_transition.unix_leap_time { - match self.extra_rule { - Some(extra_rule) => extra_rule, - None => { - // RFC 8536 3.2: - // "Local time for timestamps on or after the last transition is - // specified by the TZ string in the footer (Section 3.3) if present - // and nonempty; otherwise, it is unspecified." - // - // Older versions of macOS (1.12 and before?) have TZif file with a - // missing TZ string, and use the offset given by the last transition. - return Ok( - &self.local_time_types[last_transition.local_time_type_index] - ); - } - } - } else { - let index = match self - .transitions - .binary_search_by_key(&unix_leap_time, Transition::unix_leap_time) - { - Ok(x) => x + 1, - Err(x) => x, - }; - - let local_time_type_index = if index > 0 { - self.transitions[index - 1].local_time_type_index - } else { - 0 - }; - return Ok(&self.local_time_types[local_time_type_index]); - } - } - }; - - match extra_rule.find_local_time_type(unix_time) { - Ok(local_time_type) => Ok(local_time_type), - Err(Error::OutOfRange(error)) => Err(Error::FindLocalTimeType(error)), - err => err, - } - } - - pub(crate) fn find_local_time_type_from_local( - &self, - local_time: NaiveDateTime, - ) -> Result, Error> { - // #TODO: this is wrong as we need 'local_time_to_local_leap_time ? - // but ... does the local time even include leap seconds ?? - // let unix_leap_time = match self.unix_time_to_unix_leap_time(local_time) { - // Ok(unix_leap_time) => unix_leap_time, - // Err(Error::OutOfRange(error)) => return Err(Error::FindLocalTimeType(error)), - // Err(err) => return Err(err), - // }; - let local_leap_time = local_time.and_utc().timestamp(); - - // if we have at least one transition, - // we must check _all_ of them, in case of any Overlapping (MappedLocalTime::Ambiguous) or Skipping (MappedLocalTime::None) transitions - let offset_after_last = if !self.transitions.is_empty() { - let mut prev = self.local_time_types[0]; - - for transition in self.transitions { - let after_ltt = self.local_time_types[transition.local_time_type_index]; - - // the end and start here refers to where the time starts prior to the transition - // and where it ends up after. not the temporal relationship. - let transition_end = transition.unix_leap_time + i64::from(after_ltt.ut_offset); - let transition_start = transition.unix_leap_time + i64::from(prev.ut_offset); - - match transition_start.cmp(&transition_end) { - Ordering::Greater => { - // backwards transition, eg from DST to regular - // this means a given local time could have one of two possible offsets - if local_leap_time < transition_end { - return Ok(crate::MappedLocalTime::Single(prev)); - } else if local_leap_time >= transition_end - && local_leap_time <= transition_start - { - if prev.ut_offset < after_ltt.ut_offset { - return Ok(crate::MappedLocalTime::Ambiguous(prev, after_ltt)); - } else { - return Ok(crate::MappedLocalTime::Ambiguous(after_ltt, prev)); - } - } - } - Ordering::Equal => { - // should this ever happen? presumably we have to handle it anyway. - if local_leap_time < transition_start { - return Ok(crate::MappedLocalTime::Single(prev)); - } else if local_leap_time == transition_end { - if prev.ut_offset < after_ltt.ut_offset { - return Ok(crate::MappedLocalTime::Ambiguous(prev, after_ltt)); - } else { - return Ok(crate::MappedLocalTime::Ambiguous(after_ltt, prev)); - } - } - } - Ordering::Less => { - // forwards transition, eg from regular to DST - // this means that times that are skipped are invalid local times - if local_leap_time <= transition_start { - return Ok(crate::MappedLocalTime::Single(prev)); - } else if local_leap_time < transition_end { - return Ok(crate::MappedLocalTime::None); - } else if local_leap_time == transition_end { - return Ok(crate::MappedLocalTime::Single(after_ltt)); - } - } - } - - // try the next transition, we are fully after this one - prev = after_ltt; - } - - prev - } else { - self.local_time_types[0] - }; - - if let Some(extra_rule) = self.extra_rule { - match extra_rule.find_local_time_type_from_local(local_time) { - Ok(local_time_type) => Ok(local_time_type), - Err(Error::OutOfRange(error)) => Err(Error::FindLocalTimeType(error)), - err => err, - } - } else { - Ok(crate::MappedLocalTime::Single(offset_after_last)) - } - } - - /// Check time zone inputs - fn validate(&self) -> Result<(), Error> { - // Check local time types - let local_time_types_size = self.local_time_types.len(); - if local_time_types_size == 0 { - return Err(Error::TimeZone("list of local time types must not be empty")); - } - - // Check transitions - let mut i_transition = 0; - while i_transition < self.transitions.len() { - if self.transitions[i_transition].local_time_type_index >= local_time_types_size { - return Err(Error::TimeZone("invalid local time type index")); - } - - if i_transition + 1 < self.transitions.len() - && self.transitions[i_transition].unix_leap_time - >= self.transitions[i_transition + 1].unix_leap_time - { - return Err(Error::TimeZone("invalid transition")); - } - - i_transition += 1; - } - - // Check leap seconds - if !(self.leap_seconds.is_empty() - || self.leap_seconds[0].unix_leap_time >= 0 - && self.leap_seconds[0].correction.saturating_abs() == 1) - { - return Err(Error::TimeZone("invalid leap second")); - } - - let min_interval = SECONDS_PER_28_DAYS - 1; - - let mut i_leap_second = 0; - while i_leap_second < self.leap_seconds.len() { - if i_leap_second + 1 < self.leap_seconds.len() { - let x0 = &self.leap_seconds[i_leap_second]; - let x1 = &self.leap_seconds[i_leap_second + 1]; - - let diff_unix_leap_time = x1.unix_leap_time.saturating_sub(x0.unix_leap_time); - let abs_diff_correction = - x1.correction.saturating_sub(x0.correction).saturating_abs(); - - if !(diff_unix_leap_time >= min_interval && abs_diff_correction == 1) { - return Err(Error::TimeZone("invalid leap second")); - } - } - i_leap_second += 1; - } - - // Check extra rule - let (extra_rule, last_transition) = match (&self.extra_rule, self.transitions.last()) { - (Some(rule), Some(trans)) => (rule, trans), - _ => return Ok(()), - }; - - let last_local_time_type = &self.local_time_types[last_transition.local_time_type_index]; - let unix_time = match self.unix_leap_time_to_unix_time(last_transition.unix_leap_time) { - Ok(unix_time) => unix_time, - Err(Error::OutOfRange(error)) => return Err(Error::TimeZone(error)), - Err(err) => return Err(err), - }; - - let rule_local_time_type = match extra_rule.find_local_time_type(unix_time) { - Ok(rule_local_time_type) => rule_local_time_type, - Err(Error::OutOfRange(error)) => return Err(Error::TimeZone(error)), - Err(err) => return Err(err), - }; - - let check = last_local_time_type.ut_offset == rule_local_time_type.ut_offset - && last_local_time_type.is_dst == rule_local_time_type.is_dst - && match (&last_local_time_type.name, &rule_local_time_type.name) { - (Some(x), Some(y)) => x.equal(y), - (None, None) => true, - _ => false, - }; - - if !check { - return Err(Error::TimeZone( - "extra transition rule is inconsistent with the last transition", - )); - } - - Ok(()) - } - - /// Convert Unix time to Unix leap time, from the list of leap seconds in a time zone - const fn unix_time_to_unix_leap_time(&self, unix_time: i64) -> Result { - let mut unix_leap_time = unix_time; - - let mut i = 0; - while i < self.leap_seconds.len() { - let leap_second = &self.leap_seconds[i]; - - if unix_leap_time < leap_second.unix_leap_time { - break; - } - - unix_leap_time = match unix_time.checked_add(leap_second.correction as i64) { - Some(unix_leap_time) => unix_leap_time, - None => return Err(Error::OutOfRange("out of range operation")), - }; - - i += 1; - } - - Ok(unix_leap_time) - } - - /// Convert Unix leap time to Unix time, from the list of leap seconds in a time zone - fn unix_leap_time_to_unix_time(&self, unix_leap_time: i64) -> Result { - if unix_leap_time == i64::MIN { - return Err(Error::OutOfRange("out of range operation")); - } - - let index = match self - .leap_seconds - .binary_search_by_key(&(unix_leap_time - 1), LeapSecond::unix_leap_time) - { - Ok(x) => x + 1, - Err(x) => x, - }; - - let correction = if index > 0 { self.leap_seconds[index - 1].correction } else { 0 }; - - match unix_leap_time.checked_sub(correction as i64) { - Some(unix_time) => Ok(unix_time), - None => Err(Error::OutOfRange("out of range operation")), - } - } - - /// The UTC time zone - const UTC: TimeZoneRef<'static> = TimeZoneRef { - transitions: &[], - local_time_types: &[LocalTimeType::UTC], - leap_seconds: &[], - extra_rule: &None, - }; -} - -/// Transition of a TZif file -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub(super) struct Transition { - /// Unix leap time - unix_leap_time: i64, - /// Index specifying the local time type of the transition - local_time_type_index: usize, -} - -impl Transition { - /// Construct a TZif file transition - pub(super) const fn new(unix_leap_time: i64, local_time_type_index: usize) -> Self { - Self { unix_leap_time, local_time_type_index } - } - - /// Returns Unix leap time - const fn unix_leap_time(&self) -> i64 { - self.unix_leap_time - } -} - -/// Leap second of a TZif file -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub(super) struct LeapSecond { - /// Unix leap time - unix_leap_time: i64, - /// Leap second correction - correction: i32, -} - -impl LeapSecond { - /// Construct a TZif file leap second - pub(super) const fn new(unix_leap_time: i64, correction: i32) -> Self { - Self { unix_leap_time, correction } - } - - /// Returns Unix leap time - const fn unix_leap_time(&self) -> i64 { - self.unix_leap_time - } -} - -/// ASCII-encoded fixed-capacity string, used for storing time zone names -#[derive(Copy, Clone, Eq, PartialEq)] -struct TimeZoneName { - /// Length-prefixed string buffer - bytes: [u8; 8], -} - -impl TimeZoneName { - /// Construct a time zone name - /// - /// man tzfile(5): - /// Time zone designations should consist of at least three (3) and no more than six (6) ASCII - /// characters from the set of alphanumerics, “-â€, and “+â€. This is for compatibility with - /// POSIX requirements for time zone abbreviations. - fn new(input: &[u8]) -> Result { - let len = input.len(); - - if !(3..=7).contains(&len) { - return Err(Error::LocalTimeType( - "time zone name must have between 3 and 7 characters", - )); - } - - let mut bytes = [0; 8]; - bytes[0] = input.len() as u8; - - let mut i = 0; - while i < len { - let b = input[i]; - match b { - b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z' | b'+' | b'-' => {} - _ => return Err(Error::LocalTimeType("invalid characters in time zone name")), - } - - bytes[i + 1] = b; - i += 1; - } - - Ok(Self { bytes }) - } - - /// Returns time zone name as a byte slice - fn as_bytes(&self) -> &[u8] { - match self.bytes[0] { - 3 => &self.bytes[1..4], - 4 => &self.bytes[1..5], - 5 => &self.bytes[1..6], - 6 => &self.bytes[1..7], - 7 => &self.bytes[1..8], - _ => unreachable!(), - } - } - - /// Check if two time zone names are equal - fn equal(&self, other: &Self) -> bool { - self.bytes == other.bytes - } -} - -impl AsRef for TimeZoneName { - fn as_ref(&self) -> &str { - // SAFETY: ASCII is valid UTF-8 - unsafe { str::from_utf8_unchecked(self.as_bytes()) } - } -} - -impl fmt::Debug for TimeZoneName { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.as_ref().fmt(f) - } -} - -/// Local time type associated to a time zone -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub(crate) struct LocalTimeType { - /// Offset from UTC in seconds - pub(super) ut_offset: i32, - /// Daylight Saving Time indicator - is_dst: bool, - /// Time zone name - name: Option, -} - -impl LocalTimeType { - /// Construct a local time type - pub(super) fn new(ut_offset: i32, is_dst: bool, name: Option<&[u8]>) -> Result { - if ut_offset == i32::MIN { - return Err(Error::LocalTimeType("invalid UTC offset")); - } - - let name = match name { - Some(name) => TimeZoneName::new(name)?, - None => return Ok(Self { ut_offset, is_dst, name: None }), - }; - - Ok(Self { ut_offset, is_dst, name: Some(name) }) - } - - /// Construct a local time type with the specified UTC offset in seconds - pub(super) const fn with_offset(ut_offset: i32) -> Result { - if ut_offset == i32::MIN { - return Err(Error::LocalTimeType("invalid UTC offset")); - } - - Ok(Self { ut_offset, is_dst: false, name: None }) - } - - /// Returns offset from UTC in seconds - pub(crate) const fn offset(&self) -> i32 { - self.ut_offset - } - - /// Returns daylight saving time indicator - pub(super) const fn is_dst(&self) -> bool { - self.is_dst - } - - pub(super) const UTC: LocalTimeType = Self { ut_offset: 0, is_dst: false, name: None }; -} - -/// Open the TZif file corresponding to a TZ string -fn find_tz_file(path: impl AsRef) -> Result { - // Don't check system timezone directories on non-UNIX platforms - #[cfg(not(unix))] - return Ok(File::open(path)?); - - #[cfg(unix)] - { - let path = path.as_ref(); - if path.is_absolute() { - return Ok(File::open(path)?); - } - - for folder in &ZONE_INFO_DIRECTORIES { - if let Ok(file) = File::open(PathBuf::from(folder).join(path)) { - return Ok(file); - } - } - - Err(Error::Io(io::ErrorKind::NotFound.into())) - } -} - -// Possible system timezone directories -#[cfg(unix)] -const ZONE_INFO_DIRECTORIES: [&str; 4] = - ["/usr/share/zoneinfo", "/share/zoneinfo", "/etc/zoneinfo", "/usr/share/lib/zoneinfo"]; - -/// Number of seconds in one week -pub(crate) const SECONDS_PER_WEEK: i64 = SECONDS_PER_DAY * DAYS_PER_WEEK; -/// Number of seconds in 28 days -const SECONDS_PER_28_DAYS: i64 = SECONDS_PER_DAY * 28; - -#[cfg(test)] -mod tests { - use super::super::Error; - use super::{LeapSecond, LocalTimeType, TimeZone, TimeZoneName, Transition, TransitionRule}; - - #[test] - fn test_no_dst() -> Result<(), Error> { - let tz_string = b"HST10"; - let transition_rule = TransitionRule::from_tz_string(tz_string, false)?; - assert_eq!(transition_rule, LocalTimeType::new(-36000, false, Some(b"HST"))?.into()); - Ok(()) - } - - #[test] - fn test_error() -> Result<(), Error> { - assert!(matches!( - TransitionRule::from_tz_string(b"IST-1GMT0", false), - Err(Error::UnsupportedTzString(_)) - )); - assert!(matches!( - TransitionRule::from_tz_string(b"EET-2EEST", false), - Err(Error::UnsupportedTzString(_)) - )); - - Ok(()) - } - - #[test] - fn test_v1_file_with_leap_seconds() -> Result<(), Error> { - let bytes = b"TZif\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x01\0\0\0\x1b\0\0\0\0\0\0\0\x01\0\0\0\x04\0\0\0\0\0\0UTC\0\x04\xb2\x58\0\0\0\0\x01\x05\xa4\xec\x01\0\0\0\x02\x07\x86\x1f\x82\0\0\0\x03\x09\x67\x53\x03\0\0\0\x04\x0b\x48\x86\x84\0\0\0\x05\x0d\x2b\x0b\x85\0\0\0\x06\x0f\x0c\x3f\x06\0\0\0\x07\x10\xed\x72\x87\0\0\0\x08\x12\xce\xa6\x08\0\0\0\x09\x15\x9f\xca\x89\0\0\0\x0a\x17\x80\xfe\x0a\0\0\0\x0b\x19\x62\x31\x8b\0\0\0\x0c\x1d\x25\xea\x0c\0\0\0\x0d\x21\xda\xe5\x0d\0\0\0\x0e\x25\x9e\x9d\x8e\0\0\0\x0f\x27\x7f\xd1\x0f\0\0\0\x10\x2a\x50\xf5\x90\0\0\0\x11\x2c\x32\x29\x11\0\0\0\x12\x2e\x13\x5c\x92\0\0\0\x13\x30\xe7\x24\x13\0\0\0\x14\x33\xb8\x48\x94\0\0\0\x15\x36\x8c\x10\x15\0\0\0\x16\x43\xb7\x1b\x96\0\0\0\x17\x49\x5c\x07\x97\0\0\0\x18\x4f\xef\x93\x18\0\0\0\x19\x55\x93\x2d\x99\0\0\0\x1a\x58\x68\x46\x9a\0\0\0\x1b\0\0"; - - let time_zone = TimeZone::from_tz_data(bytes)?; - - let time_zone_result = TimeZone::new( - Vec::new(), - vec![LocalTimeType::new(0, false, Some(b"UTC"))?], - vec![ - LeapSecond::new(78796800, 1), - LeapSecond::new(94694401, 2), - LeapSecond::new(126230402, 3), - LeapSecond::new(157766403, 4), - LeapSecond::new(189302404, 5), - LeapSecond::new(220924805, 6), - LeapSecond::new(252460806, 7), - LeapSecond::new(283996807, 8), - LeapSecond::new(315532808, 9), - LeapSecond::new(362793609, 10), - LeapSecond::new(394329610, 11), - LeapSecond::new(425865611, 12), - LeapSecond::new(489024012, 13), - LeapSecond::new(567993613, 14), - LeapSecond::new(631152014, 15), - LeapSecond::new(662688015, 16), - LeapSecond::new(709948816, 17), - LeapSecond::new(741484817, 18), - LeapSecond::new(773020818, 19), - LeapSecond::new(820454419, 20), - LeapSecond::new(867715220, 21), - LeapSecond::new(915148821, 22), - LeapSecond::new(1136073622, 23), - LeapSecond::new(1230768023, 24), - LeapSecond::new(1341100824, 25), - LeapSecond::new(1435708825, 26), - LeapSecond::new(1483228826, 27), - ], - None, - )?; - - assert_eq!(time_zone, time_zone_result); - - Ok(()) - } - - #[test] - fn test_v2_file() -> Result<(), Error> { - let bytes = b"TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x06\0\0\0\x06\0\0\0\0\0\0\0\x07\0\0\0\x06\0\0\0\x14\x80\0\0\0\xbb\x05\x43\x48\xbb\x21\x71\x58\xcb\x89\x3d\xc8\xd2\x23\xf4\x70\xd2\x61\x49\x38\xd5\x8d\x73\x48\x01\x02\x01\x03\x04\x01\x05\xff\xff\x6c\x02\0\0\xff\xff\x6c\x58\0\x04\xff\xff\x7a\x68\x01\x08\xff\xff\x7a\x68\x01\x0c\xff\xff\x7a\x68\x01\x10\xff\xff\x73\x60\0\x04LMT\0HST\0HDT\0HWT\0HPT\0\0\0\0\0\x01\0\0\0\0\0\x01\0TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x06\0\0\0\x06\0\0\0\0\0\0\0\x07\0\0\0\x06\0\0\0\x14\xff\xff\xff\xff\x74\xe0\x70\xbe\xff\xff\xff\xff\xbb\x05\x43\x48\xff\xff\xff\xff\xbb\x21\x71\x58\xff\xff\xff\xff\xcb\x89\x3d\xc8\xff\xff\xff\xff\xd2\x23\xf4\x70\xff\xff\xff\xff\xd2\x61\x49\x38\xff\xff\xff\xff\xd5\x8d\x73\x48\x01\x02\x01\x03\x04\x01\x05\xff\xff\x6c\x02\0\0\xff\xff\x6c\x58\0\x04\xff\xff\x7a\x68\x01\x08\xff\xff\x7a\x68\x01\x0c\xff\xff\x7a\x68\x01\x10\xff\xff\x73\x60\0\x04LMT\0HST\0HDT\0HWT\0HPT\0\0\0\0\0\x01\0\0\0\0\0\x01\0\x0aHST10\x0a"; - - let time_zone = TimeZone::from_tz_data(bytes)?; - - let time_zone_result = TimeZone::new( - vec![ - Transition::new(-2334101314, 1), - Transition::new(-1157283000, 2), - Transition::new(-1155436200, 1), - Transition::new(-880198200, 3), - Transition::new(-769395600, 4), - Transition::new(-765376200, 1), - Transition::new(-712150200, 5), - ], - vec![ - LocalTimeType::new(-37886, false, Some(b"LMT"))?, - LocalTimeType::new(-37800, false, Some(b"HST"))?, - LocalTimeType::new(-34200, true, Some(b"HDT"))?, - LocalTimeType::new(-34200, true, Some(b"HWT"))?, - LocalTimeType::new(-34200, true, Some(b"HPT"))?, - LocalTimeType::new(-36000, false, Some(b"HST"))?, - ], - Vec::new(), - Some(TransitionRule::from(LocalTimeType::new(-36000, false, Some(b"HST"))?)), - )?; - - assert_eq!(time_zone, time_zone_result); - - assert_eq!( - *time_zone.find_local_time_type(-1156939200)?, - LocalTimeType::new(-34200, true, Some(b"HDT"))? - ); - assert_eq!( - *time_zone.find_local_time_type(1546300800)?, - LocalTimeType::new(-36000, false, Some(b"HST"))? - ); - - Ok(()) - } - - #[test] - fn test_no_tz_string() -> Result<(), Error> { - // Guayaquil from macOS 10.11 - let bytes = b"TZif\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x02\0\0\0\x02\0\0\0\0\0\0\0\x01\0\0\0\x02\0\0\0\x08\xb6\xa4B\x18\x01\xff\xff\xb6h\0\0\xff\xff\xb9\xb0\0\x04QMT\0ECT\0\0\0\0\0"; - - let time_zone = TimeZone::from_tz_data(bytes)?; - dbg!(&time_zone); - - let time_zone_result = TimeZone::new( - vec![Transition::new(-1230749160, 1)], - vec![ - LocalTimeType::new(-18840, false, Some(b"QMT"))?, - LocalTimeType::new(-18000, false, Some(b"ECT"))?, - ], - Vec::new(), - None, - )?; - - assert_eq!(time_zone, time_zone_result); - - assert_eq!( - *time_zone.find_local_time_type(-1500000000)?, - LocalTimeType::new(-18840, false, Some(b"QMT"))? - ); - assert_eq!( - *time_zone.find_local_time_type(0)?, - LocalTimeType::new(-18000, false, Some(b"ECT"))? - ); - - Ok(()) - } - - #[test] - fn test_tz_ascii_str() -> Result<(), Error> { - assert!(matches!(TimeZoneName::new(b""), Err(Error::LocalTimeType(_)))); - assert!(matches!(TimeZoneName::new(b"A"), Err(Error::LocalTimeType(_)))); - assert!(matches!(TimeZoneName::new(b"AB"), Err(Error::LocalTimeType(_)))); - assert_eq!(TimeZoneName::new(b"CET")?.as_bytes(), b"CET"); - assert_eq!(TimeZoneName::new(b"CHADT")?.as_bytes(), b"CHADT"); - assert_eq!(TimeZoneName::new(b"abcdefg")?.as_bytes(), b"abcdefg"); - assert_eq!(TimeZoneName::new(b"UTC+02")?.as_bytes(), b"UTC+02"); - assert_eq!(TimeZoneName::new(b"-1230")?.as_bytes(), b"-1230"); - assert!(matches!(TimeZoneName::new("−0330".as_bytes()), Err(Error::LocalTimeType(_)))); // MINUS SIGN (U+2212) - assert!(matches!(TimeZoneName::new(b"\x00123"), Err(Error::LocalTimeType(_)))); - assert!(matches!(TimeZoneName::new(b"12345678"), Err(Error::LocalTimeType(_)))); - assert!(matches!(TimeZoneName::new(b"GMT\0\0\0"), Err(Error::LocalTimeType(_)))); - - Ok(()) - } - - #[test] - fn test_time_zone() -> Result<(), Error> { - let utc = LocalTimeType::UTC; - let cet = LocalTimeType::with_offset(3600)?; - - let utc_local_time_types = vec![utc]; - let fixed_extra_rule = TransitionRule::from(cet); - - let time_zone_1 = TimeZone::new(vec![], utc_local_time_types.clone(), vec![], None)?; - let time_zone_2 = - TimeZone::new(vec![], utc_local_time_types.clone(), vec![], Some(fixed_extra_rule))?; - let time_zone_3 = - TimeZone::new(vec![Transition::new(0, 0)], utc_local_time_types.clone(), vec![], None)?; - let time_zone_4 = TimeZone::new( - vec![Transition::new(i32::MIN.into(), 0), Transition::new(0, 1)], - vec![utc, cet], - Vec::new(), - Some(fixed_extra_rule), - )?; - - assert_eq!(*time_zone_1.find_local_time_type(0)?, utc); - assert_eq!(*time_zone_2.find_local_time_type(0)?, cet); - - assert_eq!(*time_zone_3.find_local_time_type(-1)?, utc); - assert_eq!(*time_zone_3.find_local_time_type(0)?, utc); - - assert_eq!(*time_zone_4.find_local_time_type(-1)?, utc); - assert_eq!(*time_zone_4.find_local_time_type(0)?, cet); - - let time_zone_err = TimeZone::new( - vec![Transition::new(0, 0)], - utc_local_time_types, - vec![], - Some(fixed_extra_rule), - ); - assert!(time_zone_err.is_err()); - - Ok(()) - } - - #[test] - fn test_time_zone_from_posix_tz() -> Result<(), Error> { - #[cfg(unix)] - { - // if the TZ var is set, this essentially _overrides_ the - // time set by the localtime symlink - // so just ensure that ::local() acts as expected - // in this case - if let Ok(tz) = std::env::var("TZ") { - let time_zone_local = TimeZone::local(Some(tz.as_str()))?; - let time_zone_local_1 = TimeZone::from_posix_tz(&tz)?; - assert_eq!(time_zone_local, time_zone_local_1); - } - - // `TimeZone::from_posix_tz("UTC")` will return `Error` if the environment does not have - // a time zone database, like for example some docker containers. - // In that case skip the test. - if let Ok(time_zone_utc) = TimeZone::from_posix_tz("UTC") { - assert_eq!(time_zone_utc.find_local_time_type(0)?.offset(), 0); - } - } - - assert!(TimeZone::from_posix_tz("EST5EDT,0/0,J365/25").is_err()); - assert_eq!(TimeZone::from_posix_tz("").unwrap().find_local_time_type(0)?.offset(), 0); - - Ok(()) - } - - #[test] - fn test_leap_seconds() -> Result<(), Error> { - let time_zone = TimeZone::new( - Vec::new(), - vec![LocalTimeType::new(0, false, Some(b"UTC"))?], - vec![ - LeapSecond::new(78796800, 1), - LeapSecond::new(94694401, 2), - LeapSecond::new(126230402, 3), - LeapSecond::new(157766403, 4), - LeapSecond::new(189302404, 5), - LeapSecond::new(220924805, 6), - LeapSecond::new(252460806, 7), - LeapSecond::new(283996807, 8), - LeapSecond::new(315532808, 9), - LeapSecond::new(362793609, 10), - LeapSecond::new(394329610, 11), - LeapSecond::new(425865611, 12), - LeapSecond::new(489024012, 13), - LeapSecond::new(567993613, 14), - LeapSecond::new(631152014, 15), - LeapSecond::new(662688015, 16), - LeapSecond::new(709948816, 17), - LeapSecond::new(741484817, 18), - LeapSecond::new(773020818, 19), - LeapSecond::new(820454419, 20), - LeapSecond::new(867715220, 21), - LeapSecond::new(915148821, 22), - LeapSecond::new(1136073622, 23), - LeapSecond::new(1230768023, 24), - LeapSecond::new(1341100824, 25), - LeapSecond::new(1435708825, 26), - LeapSecond::new(1483228826, 27), - ], - None, - )?; - - let time_zone_ref = time_zone.as_ref(); - - assert!(matches!(time_zone_ref.unix_leap_time_to_unix_time(1136073621), Ok(1136073599))); - assert!(matches!(time_zone_ref.unix_leap_time_to_unix_time(1136073622), Ok(1136073600))); - assert!(matches!(time_zone_ref.unix_leap_time_to_unix_time(1136073623), Ok(1136073600))); - assert!(matches!(time_zone_ref.unix_leap_time_to_unix_time(1136073624), Ok(1136073601))); - - assert!(matches!(time_zone_ref.unix_time_to_unix_leap_time(1136073599), Ok(1136073621))); - assert!(matches!(time_zone_ref.unix_time_to_unix_leap_time(1136073600), Ok(1136073623))); - assert!(matches!(time_zone_ref.unix_time_to_unix_leap_time(1136073601), Ok(1136073624))); - - Ok(()) - } - - #[test] - fn test_leap_seconds_overflow() -> Result<(), Error> { - let time_zone_err = TimeZone::new( - vec![Transition::new(i64::MIN, 0)], - vec![LocalTimeType::UTC], - vec![LeapSecond::new(0, 1)], - Some(TransitionRule::from(LocalTimeType::UTC)), - ); - assert!(time_zone_err.is_err()); - - let time_zone = TimeZone::new( - vec![Transition::new(i64::MAX, 0)], - vec![LocalTimeType::UTC], - vec![LeapSecond::new(0, 1)], - None, - )?; - assert!(matches!( - time_zone.find_local_time_type(i64::MAX), - Err(Error::FindLocalTimeType(_)) - )); - - Ok(()) - } -} diff --git a/chrono-0.4.44/src/offset/local/unix.rs b/chrono-0.4.44/src/offset/local/unix.rs deleted file mode 100644 index 890d550456..0000000000 --- a/chrono-0.4.44/src/offset/local/unix.rs +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::{cell::RefCell, collections::hash_map, env, fs, hash::Hasher, time::SystemTime}; - -use super::tz_info::TimeZone; -use super::{FixedOffset, NaiveDateTime}; -use crate::MappedLocalTime; - -pub(super) fn offset_from_utc_datetime(utc: &NaiveDateTime) -> MappedLocalTime { - offset(utc, false) -} - -pub(super) fn offset_from_local_datetime(local: &NaiveDateTime) -> MappedLocalTime { - offset(local, true) -} - -fn offset(d: &NaiveDateTime, local: bool) -> MappedLocalTime { - TZ_INFO.with(|maybe_cache| { - maybe_cache.borrow_mut().get_or_insert_with(Cache::default).offset(*d, local) - }) -} - -// we have to store the `Cache` in an option as it can't -// be initialized in a static context. -thread_local! { - static TZ_INFO: RefCell> = Default::default(); -} - -enum Source { - LocalTime { mtime: SystemTime }, - Environment { hash: u64 }, -} - -impl Source { - fn new(env_tz: Option<&str>) -> Source { - match env_tz { - Some(tz) => { - let mut hasher = hash_map::DefaultHasher::new(); - hasher.write(tz.as_bytes()); - let hash = hasher.finish(); - Source::Environment { hash } - } - None => match fs::symlink_metadata("/etc/localtime") { - Ok(data) => Source::LocalTime { - // we have to pick a sensible default when the mtime fails - // by picking SystemTime::now() we raise the probability of - // the cache being invalidated if/when the mtime starts working - mtime: data.modified().unwrap_or_else(|_| SystemTime::now()), - }, - Err(_) => { - // as above, now() should be a better default than some constant - // TODO: see if we can improve caching in the case where the fallback is a valid timezone - Source::LocalTime { mtime: SystemTime::now() } - } - }, - } - } -} - -struct Cache { - zone: TimeZone, - source: Source, - last_checked: SystemTime, -} - -#[cfg(target_os = "aix")] -const TZDB_LOCATION: &str = "/usr/share/lib/zoneinfo"; - -#[cfg(not(any(target_os = "android", target_os = "aix", target_env = "ohos")))] -const TZDB_LOCATION: &str = "/usr/share/zoneinfo"; - -fn fallback_timezone() -> Option { - let tz_name = iana_time_zone::get_timezone().ok()?; - #[cfg(not(any(target_os = "android", target_env = "ohos")))] - let bytes = fs::read(format!("{TZDB_LOCATION}/{tz_name}")).ok()?; - #[cfg(any(target_os = "android", target_env = "ohos"))] - let bytes = crate::offset::local::tz_data::for_zone(&tz_name).ok()??; - TimeZone::from_tz_data(&bytes).ok() -} - -impl Default for Cache { - fn default() -> Cache { - // default to UTC if no local timezone can be found - let env_tz = env::var("TZ").ok(); - let env_ref = env_tz.as_deref(); - Cache { - last_checked: SystemTime::now(), - source: Source::new(env_ref), - zone: current_zone(env_ref), - } - } -} - -fn current_zone(var: Option<&str>) -> TimeZone { - TimeZone::local(var).ok().or_else(fallback_timezone).unwrap_or_else(TimeZone::utc) -} - -impl Cache { - fn offset(&mut self, d: NaiveDateTime, local: bool) -> MappedLocalTime { - let now = SystemTime::now(); - - match now.duration_since(self.last_checked) { - // If the cache has been around for less than a second then we reuse it - // unconditionally. This is a reasonable tradeoff because the timezone - // generally won't be changing _that_ often, but if the time zone does - // change, it will reflect sufficiently quickly from an application - // user's perspective. - Ok(d) if d.as_secs() < 1 => (), - Ok(_) | Err(_) => { - let env_tz = env::var("TZ").ok(); - let env_ref = env_tz.as_deref(); - let new_source = Source::new(env_ref); - - let out_of_date = match (&self.source, &new_source) { - // change from env to file or file to env, must recreate the zone - (Source::Environment { .. }, Source::LocalTime { .. }) - | (Source::LocalTime { .. }, Source::Environment { .. }) => true, - // stay as file, but mtime has changed - (Source::LocalTime { mtime: old_mtime }, Source::LocalTime { mtime }) - if old_mtime != mtime => - { - true - } - // stay as env, but hash of variable has changed - (Source::Environment { hash: old_hash }, Source::Environment { hash }) - if old_hash != hash => - { - true - } - // cache can be reused - _ => false, - }; - - if out_of_date { - self.zone = current_zone(env_ref); - } - - self.last_checked = now; - self.source = new_source; - } - } - - if !local { - let offset = self - .zone - .find_local_time_type(d.and_utc().timestamp()) - .expect("unable to select local time type") - .offset(); - - return match FixedOffset::east_opt(offset) { - Some(offset) => MappedLocalTime::Single(offset), - None => MappedLocalTime::None, - }; - } - - // we pass through the year as the year of a local point in time must either be valid in that locale, or - // the entire time was skipped in which case we will return MappedLocalTime::None anyway. - self.zone - .find_local_time_type_from_local(d) - .expect("unable to select local time type") - .and_then(|o| FixedOffset::east_opt(o.offset())) - } -} diff --git a/chrono-0.4.44/src/offset/local/win_bindings.rs b/chrono-0.4.44/src/offset/local/win_bindings.rs deleted file mode 100644 index cb563b4ebb..0000000000 --- a/chrono-0.4.44/src/offset/local/win_bindings.rs +++ /dev/null @@ -1,59 +0,0 @@ -#![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)] - -windows_link::link!("kernel32.dll" "system" fn GetTimeZoneInformationForYear(wyear : u16, pdtzi : *const DYNAMIC_TIME_ZONE_INFORMATION, ptzi : *mut TIME_ZONE_INFORMATION) -> BOOL); -windows_link::link!("kernel32.dll" "system" fn SystemTimeToFileTime(lpsystemtime : *const SYSTEMTIME, lpfiletime : *mut FILETIME) -> BOOL); -windows_link::link!("kernel32.dll" "system" fn SystemTimeToTzSpecificLocalTime(lptimezoneinformation : *const TIME_ZONE_INFORMATION, lpuniversaltime : *const SYSTEMTIME, lplocaltime : *mut SYSTEMTIME) -> BOOL); -windows_link::link!("kernel32.dll" "system" fn TzSpecificLocalTimeToSystemTime(lptimezoneinformation : *const TIME_ZONE_INFORMATION, lplocaltime : *const SYSTEMTIME, lpuniversaltime : *mut SYSTEMTIME) -> BOOL); -pub type BOOL = i32; -#[repr(C)] -#[derive(Clone, Copy)] -pub struct DYNAMIC_TIME_ZONE_INFORMATION { - pub Bias: i32, - pub StandardName: [u16; 32], - pub StandardDate: SYSTEMTIME, - pub StandardBias: i32, - pub DaylightName: [u16; 32], - pub DaylightDate: SYSTEMTIME, - pub DaylightBias: i32, - pub TimeZoneKeyName: [u16; 128], - pub DynamicDaylightTimeDisabled: bool, -} -impl Default for DYNAMIC_TIME_ZONE_INFORMATION { - fn default() -> Self { - unsafe { core::mem::zeroed() } - } -} -#[repr(C)] -#[derive(Clone, Copy, Default)] -pub struct FILETIME { - pub dwLowDateTime: u32, - pub dwHighDateTime: u32, -} -#[repr(C)] -#[derive(Clone, Copy, Default)] -pub struct SYSTEMTIME { - pub wYear: u16, - pub wMonth: u16, - pub wDayOfWeek: u16, - pub wDay: u16, - pub wHour: u16, - pub wMinute: u16, - pub wSecond: u16, - pub wMilliseconds: u16, -} -#[repr(C)] -#[derive(Clone, Copy)] -pub struct TIME_ZONE_INFORMATION { - pub Bias: i32, - pub StandardName: [u16; 32], - pub StandardDate: SYSTEMTIME, - pub StandardBias: i32, - pub DaylightName: [u16; 32], - pub DaylightDate: SYSTEMTIME, - pub DaylightBias: i32, -} -impl Default for TIME_ZONE_INFORMATION { - fn default() -> Self { - unsafe { core::mem::zeroed() } - } -} diff --git a/chrono-0.4.44/src/offset/local/windows.rs b/chrono-0.4.44/src/offset/local/windows.rs deleted file mode 100644 index dcab6c44e6..0000000000 --- a/chrono-0.4.44/src/offset/local/windows.rs +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::cmp::Ordering; -use std::mem::MaybeUninit; -use std::ptr; - -use super::win_bindings::{GetTimeZoneInformationForYear, SYSTEMTIME, TIME_ZONE_INFORMATION}; - -use crate::offset::local::{Transition, lookup_with_dst_transitions}; -use crate::{Datelike, FixedOffset, MappedLocalTime, NaiveDate, NaiveDateTime, NaiveTime, Weekday}; - -// We don't use `SystemTimeToTzSpecificLocalTime` because it doesn't support the same range of dates -// as Chrono. Also it really isn't that difficult to work out the correct offset from the provided -// DST rules. -// -// This method uses `overflowing_sub_offset` because it is no problem if the transition time in UTC -// falls a couple of hours inside the buffer space around the `NaiveDateTime` range (although it is -// very theoretical to have a transition at midnight around `NaiveDate::(MIN|MAX)`. -pub(super) fn offset_from_utc_datetime(utc: &NaiveDateTime) -> MappedLocalTime { - // Using a `TzInfo` based on the year of an UTC datetime is technically wrong, we should be - // using the rules for the year of the corresponding local time. But this matches what - // `SystemTimeToTzSpecificLocalTime` is documented to do. - let tz_info = match TzInfo::for_year(utc.year()) { - Some(tz_info) => tz_info, - None => return MappedLocalTime::None, - }; - let offset = match (tz_info.std_transition, tz_info.dst_transition) { - (Some(std_transition), Some(dst_transition)) => { - let std_transition_utc = std_transition.overflowing_sub_offset(tz_info.dst_offset); - let dst_transition_utc = dst_transition.overflowing_sub_offset(tz_info.std_offset); - if dst_transition_utc < std_transition_utc { - match utc >= &dst_transition_utc && utc < &std_transition_utc { - true => tz_info.dst_offset, - false => tz_info.std_offset, - } - } else { - match utc >= &std_transition_utc && utc < &dst_transition_utc { - true => tz_info.std_offset, - false => tz_info.dst_offset, - } - } - } - (Some(std_transition), None) => { - let std_transition_utc = std_transition.overflowing_sub_offset(tz_info.dst_offset); - match utc < &std_transition_utc { - true => tz_info.dst_offset, - false => tz_info.std_offset, - } - } - (None, Some(dst_transition)) => { - let dst_transition_utc = dst_transition.overflowing_sub_offset(tz_info.std_offset); - match utc < &dst_transition_utc { - true => tz_info.std_offset, - false => tz_info.dst_offset, - } - } - (None, None) => tz_info.std_offset, - }; - MappedLocalTime::Single(offset) -} - -// We don't use `TzSpecificLocalTimeToSystemTime` because it doesn't let us choose how to handle -// ambiguous cases (during a DST transition). Instead we get the timezone information for the -// current year and compute it ourselves, like we do on Unix. -pub(super) fn offset_from_local_datetime(local: &NaiveDateTime) -> MappedLocalTime { - let tz_info = match TzInfo::for_year(local.year()) { - Some(tz_info) => tz_info, - None => return MappedLocalTime::None, - }; - // Create a sorted slice of transitions and use `lookup_with_dst_transitions`. - match (tz_info.std_transition, tz_info.dst_transition) { - (Some(std_transition), Some(dst_transition)) => { - let std_transition = - Transition::new(std_transition, tz_info.dst_offset, tz_info.std_offset); - let dst_transition = - Transition::new(dst_transition, tz_info.std_offset, tz_info.dst_offset); - let transitions = match std_transition.cmp(&dst_transition) { - Ordering::Less => [std_transition, dst_transition], - Ordering::Greater => [dst_transition, std_transition], - Ordering::Equal => { - // This doesn't make sense. Let's just return the standard offset. - return MappedLocalTime::Single(tz_info.std_offset); - } - }; - lookup_with_dst_transitions(&transitions, *local) - } - (Some(std_transition), None) => { - let transitions = - [Transition::new(std_transition, tz_info.dst_offset, tz_info.std_offset)]; - lookup_with_dst_transitions(&transitions, *local) - } - (None, Some(dst_transition)) => { - let transitions = - [Transition::new(dst_transition, tz_info.std_offset, tz_info.dst_offset)]; - lookup_with_dst_transitions(&transitions, *local) - } - (None, None) => MappedLocalTime::Single(tz_info.std_offset), - } -} - -// The basis for Windows timezone and DST support has been in place since Windows 2000. It does not -// allow for complex rules like the IANA timezone database: -// - A timezone has the same base offset the whole year. -// - There seem to be either zero or two DST transitions (but we support having just one). -// - As of Vista(?) only years from 2004 until a few years into the future are supported. -// - All other years get the base settings, which seem to be that of the current year. -// -// These details don't matter much, we just work with the offsets and transition dates Windows -// returns through `GetTimeZoneInformationForYear` for a particular year. -struct TzInfo { - // Offset from UTC during standard time. - std_offset: FixedOffset, - // Offset from UTC during daylight saving time. - dst_offset: FixedOffset, - // Transition from standard time to daylight saving time, given in local standard time. - std_transition: Option, - // Transition from daylight saving time to standard time, given in local daylight saving time. - dst_transition: Option, -} - -impl TzInfo { - fn for_year(year: i32) -> Option { - // The API limits years to 1601..=30827. - // Working with timezones and daylight saving time this far into the past or future makes - // little sense. But whatever is extrapolated for 1601 or 30827 is what can be extrapolated - // for years beyond. - let ref_year = year.clamp(1601, 30827) as u16; - let tz_info = unsafe { - let mut tz_info = MaybeUninit::::uninit(); - if GetTimeZoneInformationForYear(ref_year, ptr::null_mut(), tz_info.as_mut_ptr()) == 0 { - return None; - } - tz_info.assume_init() - }; - let std_offset = (tz_info.Bias) - .checked_add(tz_info.StandardBias) - .and_then(|o| o.checked_mul(60)) - .and_then(FixedOffset::west_opt)?; - let dst_offset = (tz_info.Bias) - .checked_add(tz_info.DaylightBias) - .and_then(|o| o.checked_mul(60)) - .and_then(FixedOffset::west_opt)?; - Some(TzInfo { - std_offset, - dst_offset, - std_transition: naive_date_time_from_system_time(tz_info.StandardDate, year).ok()?, - dst_transition: naive_date_time_from_system_time(tz_info.DaylightDate, year).ok()?, - }) - } -} - -/// Resolve a `SYSTEMTIME` object to an `Option`. -/// -/// A `SYSTEMTIME` within a `TIME_ZONE_INFORMATION` struct can be zero to indicate there is no -/// transition. -/// If it has year, month and day values it is a concrete date. -/// If the year is missing the `SYSTEMTIME` is a rule, which this method resolves for the provided -/// year. A rule has a month, weekday, and nth weekday of the month as components. -/// -/// Returns `Err` if any of the values is invalid, which should never happen. -fn naive_date_time_from_system_time( - st: SYSTEMTIME, - year: i32, -) -> Result, ()> { - if st.wYear == 0 && st.wMonth == 0 { - return Ok(None); - } - let time = NaiveTime::from_hms_milli_opt( - st.wHour as u32, - st.wMinute as u32, - st.wSecond as u32, - st.wMilliseconds as u32, - ) - .ok_or(())?; - - if st.wYear != 0 { - // We have a concrete date. - let date = - NaiveDate::from_ymd_opt(st.wYear as i32, st.wMonth as u32, st.wDay as u32).ok_or(())?; - return Ok(Some(date.and_time(time))); - } - - // Resolve a rule with month, weekday, and nth weekday of the month to a date in the current - // year. - let weekday = match st.wDayOfWeek { - 0 => Weekday::Sun, - 1 => Weekday::Mon, - 2 => Weekday::Tue, - 3 => Weekday::Wed, - 4 => Weekday::Thu, - 5 => Weekday::Fri, - 6 => Weekday::Sat, - _ => return Err(()), - }; - let nth_day = match st.wDay { - 1..=5 => st.wDay as u8, - _ => return Err(()), - }; - let date = NaiveDate::from_weekday_of_month_opt(year, st.wMonth as u32, weekday, nth_day) - .or_else(|| NaiveDate::from_weekday_of_month_opt(year, st.wMonth as u32, weekday, 4)) - .ok_or(())?; // `st.wMonth` must be invalid - Ok(Some(date.and_time(time))) -} - -#[cfg(test)] -mod tests { - use crate::offset::local::win_bindings::{ - FILETIME, SYSTEMTIME, SystemTimeToFileTime, TzSpecificLocalTimeToSystemTime, - }; - use crate::{DateTime, FixedOffset, Local, NaiveDate, NaiveDateTime, TimeDelta}; - use crate::{Datelike, TimeZone, Timelike}; - use std::mem::MaybeUninit; - use std::ptr; - - #[test] - fn verify_against_tz_specific_local_time_to_system_time() { - // The implementation in Windows itself is the source of truth on how to work with the OS - // timezone information. This test compares for every hour over a period of 125 years our - // implementation to `TzSpecificLocalTimeToSystemTime`. - // - // This uses parts of a previous Windows `Local` implementation in chrono. - fn from_local_time(dt: &NaiveDateTime) -> DateTime { - let st = system_time_from_naive_date_time(dt); - let utc_time = local_to_utc_time(&st); - let utc_secs = system_time_as_unix_seconds(&utc_time); - let local_secs = system_time_as_unix_seconds(&st); - let offset = (local_secs - utc_secs) as i32; - let offset = FixedOffset::east_opt(offset).unwrap(); - DateTime::from_naive_utc_and_offset(*dt - offset, offset) - } - fn system_time_from_naive_date_time(dt: &NaiveDateTime) -> SYSTEMTIME { - SYSTEMTIME { - // Valid values: 1601-30827 - wYear: dt.year() as u16, - // Valid values:1-12 - wMonth: dt.month() as u16, - // Valid values: 0-6, starting Sunday. - // NOTE: enum returns 1-7, starting Monday, so we are - // off here, but this is not currently used in local. - wDayOfWeek: dt.weekday() as u16, - // Valid values: 1-31 - wDay: dt.day() as u16, - // Valid values: 0-23 - wHour: dt.hour() as u16, - // Valid values: 0-59 - wMinute: dt.minute() as u16, - // Valid values: 0-59 - wSecond: dt.second() as u16, - // Valid values: 0-999 - wMilliseconds: 0, - } - } - fn local_to_utc_time(local: &SYSTEMTIME) -> SYSTEMTIME { - let mut sys_time = MaybeUninit::::uninit(); - unsafe { TzSpecificLocalTimeToSystemTime(ptr::null(), local, sys_time.as_mut_ptr()) }; - // SAFETY: TzSpecificLocalTimeToSystemTime must have succeeded at this point, so we can - // assume the value is initialized. - unsafe { sys_time.assume_init() } - } - const HECTONANOSECS_IN_SEC: i64 = 10_000_000; - const HECTONANOSEC_TO_UNIX_EPOCH: i64 = 11_644_473_600 * HECTONANOSECS_IN_SEC; - fn system_time_as_unix_seconds(st: &SYSTEMTIME) -> i64 { - let mut init = MaybeUninit::::uninit(); - unsafe { - SystemTimeToFileTime(st, init.as_mut_ptr()); - } - // SystemTimeToFileTime must have succeeded at this point, so we can assume the value is - // initialized. - let filetime = unsafe { init.assume_init() }; - let bit_shift = - ((filetime.dwHighDateTime as u64) << 32) | (filetime.dwLowDateTime as u64); - (bit_shift as i64 - HECTONANOSEC_TO_UNIX_EPOCH) / HECTONANOSECS_IN_SEC - } - - let mut date = NaiveDate::from_ymd_opt(1975, 1, 1).unwrap().and_hms_opt(0, 30, 0).unwrap(); - - while date.year() < 2078 { - // Windows doesn't handle non-existing dates, it just treats it as valid. - if let Some(our_result) = Local.from_local_datetime(&date).earliest() { - assert_eq!(from_local_time(&date), our_result); - } - date += TimeDelta::try_hours(1).unwrap(); - } - } -} diff --git a/chrono-0.4.44/src/offset/mod.rs b/chrono-0.4.44/src/offset/mod.rs deleted file mode 100644 index 295700b4f7..0000000000 --- a/chrono-0.4.44/src/offset/mod.rs +++ /dev/null @@ -1,715 +0,0 @@ -// This is a part of Chrono. -// See README.md and LICENSE.txt for details. - -//! The time zone, which calculates offsets from the local time to UTC. -//! -//! There are four operations provided by the `TimeZone` trait: -//! -//! 1. Converting the local `NaiveDateTime` to `DateTime` -//! 2. Converting the UTC `NaiveDateTime` to `DateTime` -//! 3. Converting `DateTime` to the local `NaiveDateTime` -//! 4. Constructing `DateTime` objects from various offsets -//! -//! 1 is used for constructors. 2 is used for the `with_timezone` method of date and time types. -//! 3 is used for other methods, e.g. `year()` or `format()`, and provided by an associated type -//! which implements `Offset` (which then passed to `TimeZone` for actual implementations). -//! Technically speaking `TimeZone` has a total knowledge about given timescale, -//! but `Offset` is used as a cache to avoid the repeated conversion -//! and provides implementations for 1 and 3. -//! An `TimeZone` instance can be reconstructed from the corresponding `Offset` instance. - -use core::fmt; - -use crate::Weekday; -use crate::format::{ParseResult, Parsed, StrftimeItems, parse}; -use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime}; -#[allow(deprecated)] -use crate::{Date, DateTime}; - -pub(crate) mod fixed; -pub use self::fixed::FixedOffset; - -#[cfg(feature = "clock")] -pub(crate) mod local; -#[cfg(feature = "clock")] -pub use self::local::Local; - -pub(crate) mod utc; -pub use self::utc::Utc; - -/// The result of mapping a local time to a concrete instant in a given time zone. -/// -/// The calculation to go from a local time (wall clock time) to an instant in UTC can end up in -/// three cases: -/// * A single, simple result. -/// * An ambiguous result when the clock is turned backwards during a transition due to for example -/// DST. -/// * No result when the clock is turned forwards during a transition due to for example DST. -/// -///

-/// -/// In wasm, when using [`Local`], only the [`LocalResult::Single`] variant is returned. -/// Specifically: -/// -/// * When the clock is turned backwards, where `Ambiguous(earliest, latest)` would be expected, -/// `Single(earliest)` is returned instead. -/// * When the clock is turned forwards, where `None` would be expected, `Single(t)` is returned, -/// with `t` being the requested local time represented as though there is no transition on that -/// day (i.e. still "summer time") -/// -/// This is caused because of limitations in the JavaScript -/// [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) -/// API, which always parses a local time as a single, valid time - even for an -/// input which describes a nonexistent or ambiguous time. -/// -/// See further discussion and workarounds in . -/// -///
-/// -/// When the clock is turned backwards it creates a _fold_ in local time, during which the local -/// time is _ambiguous_. When the clock is turned forwards it creates a _gap_ in local time, during -/// which the local time is _missing_, or does not exist. -/// -/// Chrono does not return a default choice or invalid data during time zone transitions, but has -/// the `MappedLocalTime` type to help deal with the result correctly. -/// -/// The type of `T` is usually a [`DateTime`] but may also be only an offset. -pub type MappedLocalTime = LocalResult; -#[derive(Clone, PartialEq, Debug, Copy, Eq, Hash)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// Old name of [`MappedLocalTime`]. See that type for more documentation. -pub enum LocalResult { - /// The local time maps to a single unique result. - Single(T), - - /// The local time is _ambiguous_ because there is a _fold_ in the local time. - /// - /// This variant contains the two possible results, in the order `(earliest, latest)`. - Ambiguous(T, T), - - /// The local time does not exist because there is a _gap_ in the local time. - /// - /// This variant may also be returned if there was an error while resolving the local time, - /// caused by for example missing time zone data files, an error in an OS API, or overflow. - None, -} - -impl MappedLocalTime { - /// Returns `Some` if the time zone mapping has a single result. - /// - /// # Errors - /// - /// Returns `None` if local time falls in a _fold_ or _gap_ in the local time, or if there was - /// an error. - #[must_use] - pub fn single(self) -> Option { - match self { - MappedLocalTime::Single(t) => Some(t), - _ => None, - } - } - - /// Returns the earliest possible result of the time zone mapping. - /// - /// # Errors - /// - /// Returns `None` if local time falls in a _gap_ in the local time, or if there was an error. - #[must_use] - pub fn earliest(self) -> Option { - match self { - MappedLocalTime::Single(t) | MappedLocalTime::Ambiguous(t, _) => Some(t), - _ => None, - } - } - - /// Returns the latest possible result of the time zone mapping. - /// - /// # Errors - /// - /// Returns `None` if local time falls in a _gap_ in the local time, or if there was an error. - #[must_use] - pub fn latest(self) -> Option { - match self { - MappedLocalTime::Single(t) | MappedLocalTime::Ambiguous(_, t) => Some(t), - _ => None, - } - } - - /// Maps a `MappedLocalTime` into `MappedLocalTime` with given function. - #[must_use] - pub fn map U>(self, mut f: F) -> MappedLocalTime { - match self { - MappedLocalTime::None => MappedLocalTime::None, - MappedLocalTime::Single(v) => MappedLocalTime::Single(f(v)), - MappedLocalTime::Ambiguous(min, max) => MappedLocalTime::Ambiguous(f(min), f(max)), - } - } - - /// Maps a `MappedLocalTime` into `MappedLocalTime` with given function. - /// - /// Returns `MappedLocalTime::None` if the function returns `None`. - #[must_use] - pub(crate) fn and_then Option>(self, mut f: F) -> MappedLocalTime { - match self { - MappedLocalTime::None => MappedLocalTime::None, - MappedLocalTime::Single(v) => match f(v) { - Some(new) => MappedLocalTime::Single(new), - None => MappedLocalTime::None, - }, - MappedLocalTime::Ambiguous(min, max) => match (f(min), f(max)) { - (Some(min), Some(max)) => MappedLocalTime::Ambiguous(min, max), - _ => MappedLocalTime::None, - }, - } - } -} - -#[allow(deprecated)] -impl MappedLocalTime> { - /// Makes a new `DateTime` from the current date and given `NaiveTime`. - /// The offset in the current date is preserved. - /// - /// Propagates any error. Ambiguous result would be discarded. - #[inline] - #[must_use] - pub fn and_time(self, time: NaiveTime) -> MappedLocalTime> { - match self { - MappedLocalTime::Single(d) => { - d.and_time(time).map_or(MappedLocalTime::None, MappedLocalTime::Single) - } - _ => MappedLocalTime::None, - } - } - - /// Makes a new `DateTime` from the current date, hour, minute and second. - /// The offset in the current date is preserved. - /// - /// Propagates any error. Ambiguous result would be discarded. - #[inline] - #[must_use] - pub fn and_hms_opt(self, hour: u32, min: u32, sec: u32) -> MappedLocalTime> { - match self { - MappedLocalTime::Single(d) => { - d.and_hms_opt(hour, min, sec).map_or(MappedLocalTime::None, MappedLocalTime::Single) - } - _ => MappedLocalTime::None, - } - } - - /// Makes a new `DateTime` from the current date, hour, minute, second and millisecond. - /// The millisecond part can exceed 1,000 in order to represent the leap second. - /// The offset in the current date is preserved. - /// - /// Propagates any error. Ambiguous result would be discarded. - #[inline] - #[must_use] - pub fn and_hms_milli_opt( - self, - hour: u32, - min: u32, - sec: u32, - milli: u32, - ) -> MappedLocalTime> { - match self { - MappedLocalTime::Single(d) => d - .and_hms_milli_opt(hour, min, sec, milli) - .map_or(MappedLocalTime::None, MappedLocalTime::Single), - _ => MappedLocalTime::None, - } - } - - /// Makes a new `DateTime` from the current date, hour, minute, second and microsecond. - /// The microsecond part can exceed 1,000,000 in order to represent the leap second. - /// The offset in the current date is preserved. - /// - /// Propagates any error. Ambiguous result would be discarded. - #[inline] - #[must_use] - pub fn and_hms_micro_opt( - self, - hour: u32, - min: u32, - sec: u32, - micro: u32, - ) -> MappedLocalTime> { - match self { - MappedLocalTime::Single(d) => d - .and_hms_micro_opt(hour, min, sec, micro) - .map_or(MappedLocalTime::None, MappedLocalTime::Single), - _ => MappedLocalTime::None, - } - } - - /// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond. - /// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second. - /// The offset in the current date is preserved. - /// - /// Propagates any error. Ambiguous result would be discarded. - #[inline] - #[must_use] - pub fn and_hms_nano_opt( - self, - hour: u32, - min: u32, - sec: u32, - nano: u32, - ) -> MappedLocalTime> { - match self { - MappedLocalTime::Single(d) => d - .and_hms_nano_opt(hour, min, sec, nano) - .map_or(MappedLocalTime::None, MappedLocalTime::Single), - _ => MappedLocalTime::None, - } - } -} - -impl MappedLocalTime { - /// Returns a single unique conversion result or panics. - /// - /// `unwrap()` is best combined with time zone types where the mapping can never fail like - /// [`Utc`] and [`FixedOffset`]. Note that for [`FixedOffset`] there is a rare case where a - /// resulting [`DateTime`] can be out of range. - /// - /// # Panics - /// - /// Panics if the local time falls within a _fold_ or a _gap_ in the local time, and on any - /// error that may have been returned by the type implementing [`TimeZone`]. - #[must_use] - #[track_caller] - pub fn unwrap(self) -> T { - match self { - MappedLocalTime::None => panic!("No such local time"), - MappedLocalTime::Single(t) => t, - MappedLocalTime::Ambiguous(t1, t2) => { - panic!("Ambiguous local time, ranging from {t1:?} to {t2:?}") - } - } - } -} - -/// The offset from the local time to UTC. -pub trait Offset: Sized + Clone + fmt::Debug { - /// Returns the fixed offset from UTC to the local time stored. - fn fix(&self) -> FixedOffset; -} - -/// The time zone. -/// -/// The methods here are the primary constructors for the [`DateTime`] type. -pub trait TimeZone: Sized + Clone { - /// An associated offset type. - /// This type is used to store the actual offset in date and time types. - /// The original `TimeZone` value can be recovered via `TimeZone::from_offset`. - type Offset: Offset; - - /// Make a new `DateTime` from year, month, day, time components and current time zone. - /// - /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. - /// - /// Returns `MappedLocalTime::None` on invalid input data. - fn with_ymd_and_hms( - &self, - year: i32, - month: u32, - day: u32, - hour: u32, - min: u32, - sec: u32, - ) -> MappedLocalTime> { - match NaiveDate::from_ymd_opt(year, month, day).and_then(|d| d.and_hms_opt(hour, min, sec)) - { - Some(dt) => self.from_local_datetime(&dt), - None => MappedLocalTime::None, - } - } - - /// Makes a new `Date` from year, month, day and the current time zone. - /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. - /// - /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24), - /// but it will propagate to the `DateTime` values constructed via this date. - /// - /// Panics on the out-of-range date, invalid month and/or day. - #[deprecated(since = "0.4.23", note = "use `with_ymd_and_hms()` instead")] - #[allow(deprecated)] - fn ymd(&self, year: i32, month: u32, day: u32) -> Date { - self.ymd_opt(year, month, day).unwrap() - } - - /// Makes a new `Date` from year, month, day and the current time zone. - /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. - /// - /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24), - /// but it will propagate to the `DateTime` values constructed via this date. - /// - /// Returns `None` on the out-of-range date, invalid month and/or day. - #[deprecated(since = "0.4.23", note = "use `with_ymd_and_hms()` instead")] - #[allow(deprecated)] - fn ymd_opt(&self, year: i32, month: u32, day: u32) -> MappedLocalTime> { - match NaiveDate::from_ymd_opt(year, month, day) { - Some(d) => self.from_local_date(&d), - None => MappedLocalTime::None, - } - } - - /// Makes a new `Date` from year, day of year (DOY or "ordinal") and the current time zone. - /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. - /// - /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24), - /// but it will propagate to the `DateTime` values constructed via this date. - /// - /// Panics on the out-of-range date and/or invalid DOY. - #[deprecated( - since = "0.4.23", - note = "use `from_local_datetime()` with a `NaiveDateTime` instead" - )] - #[allow(deprecated)] - fn yo(&self, year: i32, ordinal: u32) -> Date { - self.yo_opt(year, ordinal).unwrap() - } - - /// Makes a new `Date` from year, day of year (DOY or "ordinal") and the current time zone. - /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. - /// - /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24), - /// but it will propagate to the `DateTime` values constructed via this date. - /// - /// Returns `None` on the out-of-range date and/or invalid DOY. - #[deprecated( - since = "0.4.23", - note = "use `from_local_datetime()` with a `NaiveDateTime` instead" - )] - #[allow(deprecated)] - fn yo_opt(&self, year: i32, ordinal: u32) -> MappedLocalTime> { - match NaiveDate::from_yo_opt(year, ordinal) { - Some(d) => self.from_local_date(&d), - None => MappedLocalTime::None, - } - } - - /// Makes a new `Date` from ISO week date (year and week number), day of the week (DOW) and - /// the current time zone. - /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. - /// The resulting `Date` may have a different year from the input year. - /// - /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24), - /// but it will propagate to the `DateTime` values constructed via this date. - /// - /// Panics on the out-of-range date and/or invalid week number. - #[deprecated( - since = "0.4.23", - note = "use `from_local_datetime()` with a `NaiveDateTime` instead" - )] - #[allow(deprecated)] - fn isoywd(&self, year: i32, week: u32, weekday: Weekday) -> Date { - self.isoywd_opt(year, week, weekday).unwrap() - } - - /// Makes a new `Date` from ISO week date (year and week number), day of the week (DOW) and - /// the current time zone. - /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. - /// The resulting `Date` may have a different year from the input year. - /// - /// The time zone normally does not affect the date (unless it is between UTC-24 and UTC+24), - /// but it will propagate to the `DateTime` values constructed via this date. - /// - /// Returns `None` on the out-of-range date and/or invalid week number. - #[deprecated( - since = "0.4.23", - note = "use `from_local_datetime()` with a `NaiveDateTime` instead" - )] - #[allow(deprecated)] - fn isoywd_opt(&self, year: i32, week: u32, weekday: Weekday) -> MappedLocalTime> { - match NaiveDate::from_isoywd_opt(year, week, weekday) { - Some(d) => self.from_local_date(&d), - None => MappedLocalTime::None, - } - } - - /// Makes a new `DateTime` from the number of non-leap seconds - /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp") - /// and the number of nanoseconds since the last whole non-leap second. - /// - /// The nanosecond part can exceed 1,000,000,000 in order to represent a - /// [leap second](crate::NaiveTime#leap-second-handling), but only when `secs % 60 == 59`. - /// (The true "UNIX timestamp" cannot represent a leap second unambiguously.) - /// - /// # Panics - /// - /// Panics on the out-of-range number of seconds and/or invalid nanosecond, - /// for a non-panicking version see [`timestamp_opt`](#method.timestamp_opt). - #[deprecated(since = "0.4.23", note = "use `timestamp_opt()` instead")] - fn timestamp(&self, secs: i64, nsecs: u32) -> DateTime { - self.timestamp_opt(secs, nsecs).unwrap() - } - - /// Makes a new `DateTime` from the number of non-leap seconds - /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp") - /// and the number of nanoseconds since the last whole non-leap second. - /// - /// The nanosecond part can exceed 1,000,000,000 in order to represent a - /// [leap second](crate::NaiveTime#leap-second-handling), but only when `secs % 60 == 59`. - /// (The true "UNIX timestamp" cannot represent a leap second unambiguously.) - /// - /// # Errors - /// - /// Returns `MappedLocalTime::None` on out-of-range number of seconds and/or - /// invalid nanosecond, otherwise always returns `MappedLocalTime::Single`. - /// - /// # Example - /// - /// ``` - /// use chrono::{TimeZone, Utc}; - /// - /// assert_eq!(Utc.timestamp_opt(1431648000, 0).unwrap().to_string(), "2015-05-15 00:00:00 UTC"); - /// ``` - fn timestamp_opt(&self, secs: i64, nsecs: u32) -> MappedLocalTime> { - match DateTime::from_timestamp(secs, nsecs) { - Some(dt) => MappedLocalTime::Single(self.from_utc_datetime(&dt.naive_utc())), - None => MappedLocalTime::None, - } - } - - /// Makes a new `DateTime` from the number of non-leap milliseconds - /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp"). - /// - /// Panics on out-of-range number of milliseconds for a non-panicking - /// version see [`timestamp_millis_opt`](#method.timestamp_millis_opt). - #[deprecated(since = "0.4.23", note = "use `timestamp_millis_opt()` instead")] - fn timestamp_millis(&self, millis: i64) -> DateTime { - self.timestamp_millis_opt(millis).unwrap() - } - - /// Makes a new `DateTime` from the number of non-leap milliseconds - /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp"). - /// - /// - /// Returns `MappedLocalTime::None` on out-of-range number of milliseconds - /// and/or invalid nanosecond, otherwise always returns - /// `MappedLocalTime::Single`. - /// - /// # Example - /// - /// ``` - /// use chrono::{MappedLocalTime, TimeZone, Utc}; - /// match Utc.timestamp_millis_opt(1431648000) { - /// MappedLocalTime::Single(dt) => assert_eq!(dt.timestamp(), 1431648), - /// _ => panic!("Incorrect timestamp_millis"), - /// }; - /// ``` - fn timestamp_millis_opt(&self, millis: i64) -> MappedLocalTime> { - match DateTime::from_timestamp_millis(millis) { - Some(dt) => MappedLocalTime::Single(self.from_utc_datetime(&dt.naive_utc())), - None => MappedLocalTime::None, - } - } - - /// Makes a new `DateTime` from the number of non-leap nanoseconds - /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp"). - /// - /// Unlike [`timestamp_millis_opt`](#method.timestamp_millis_opt), this never fails. - /// - /// # Example - /// - /// ``` - /// use chrono::{TimeZone, Utc}; - /// - /// assert_eq!(Utc.timestamp_nanos(1431648000000000).timestamp(), 1431648); - /// ``` - fn timestamp_nanos(&self, nanos: i64) -> DateTime { - self.from_utc_datetime(&DateTime::from_timestamp_nanos(nanos).naive_utc()) - } - - /// Makes a new `DateTime` from the number of non-leap microseconds - /// since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp"). - /// - /// # Example - /// - /// ``` - /// use chrono::{TimeZone, Utc}; - /// - /// assert_eq!(Utc.timestamp_micros(1431648000000).unwrap().timestamp(), 1431648); - /// ``` - fn timestamp_micros(&self, micros: i64) -> MappedLocalTime> { - match DateTime::from_timestamp_micros(micros) { - Some(dt) => MappedLocalTime::Single(self.from_utc_datetime(&dt.naive_utc())), - None => MappedLocalTime::None, - } - } - - /// Parses a string with the specified format string and returns a - /// `DateTime` with the current offset. - /// - /// See the [`crate::format::strftime`] module on the - /// supported escape sequences. - /// - /// If the to-be-parsed string includes an offset, it *must* match the - /// offset of the TimeZone, otherwise an error will be returned. - /// - /// See also [`DateTime::parse_from_str`] which gives a [`DateTime`] with - /// parsed [`FixedOffset`]. - /// - /// See also [`NaiveDateTime::parse_from_str`] which gives a [`NaiveDateTime`] without - /// an offset, but can be converted to a [`DateTime`] with [`NaiveDateTime::and_utc`] or - /// [`NaiveDateTime::and_local_timezone`]. - #[deprecated( - since = "0.4.29", - note = "use `DateTime::parse_from_str` or `NaiveDateTime::parse_from_str` with `and_utc()` or `and_local_timezone()` instead" - )] - fn datetime_from_str(&self, s: &str, fmt: &str) -> ParseResult> { - let mut parsed = Parsed::new(); - parse(&mut parsed, s, StrftimeItems::new(fmt))?; - parsed.to_datetime_with_timezone(self) - } - - /// Reconstructs the time zone from the offset. - fn from_offset(offset: &Self::Offset) -> Self; - - /// Creates the offset(s) for given local `NaiveDate` if possible. - fn offset_from_local_date(&self, local: &NaiveDate) -> MappedLocalTime; - - /// Creates the offset(s) for given local `NaiveDateTime` if possible. - fn offset_from_local_datetime(&self, local: &NaiveDateTime) -> MappedLocalTime; - - /// Converts the local `NaiveDate` to the timezone-aware `Date` if possible. - #[allow(clippy::wrong_self_convention)] - #[deprecated(since = "0.4.23", note = "use `from_local_datetime()` instead")] - #[allow(deprecated)] - fn from_local_date(&self, local: &NaiveDate) -> MappedLocalTime> { - self.offset_from_local_date(local).map(|offset| { - // since FixedOffset is within +/- 1 day, the date is never affected - Date::from_utc(*local, offset) - }) - } - - /// Converts the local `NaiveDateTime` to the timezone-aware `DateTime` if possible. - #[allow(clippy::wrong_self_convention)] - fn from_local_datetime(&self, local: &NaiveDateTime) -> MappedLocalTime> { - self.offset_from_local_datetime(local).and_then(|off| { - local - .checked_sub_offset(off.fix()) - .map(|dt| DateTime::from_naive_utc_and_offset(dt, off)) - }) - } - - /// Creates the offset for given UTC `NaiveDate`. This cannot fail. - fn offset_from_utc_date(&self, utc: &NaiveDate) -> Self::Offset; - - /// Creates the offset for given UTC `NaiveDateTime`. This cannot fail. - fn offset_from_utc_datetime(&self, utc: &NaiveDateTime) -> Self::Offset; - - /// Converts the UTC `NaiveDate` to the local time. - /// The UTC is continuous and thus this cannot fail (but can give the duplicate local time). - #[allow(clippy::wrong_self_convention)] - #[deprecated(since = "0.4.23", note = "use `from_utc_datetime()` instead")] - #[allow(deprecated)] - fn from_utc_date(&self, utc: &NaiveDate) -> Date { - Date::from_utc(*utc, self.offset_from_utc_date(utc)) - } - - /// Converts the UTC `NaiveDateTime` to the local time. - /// The UTC is continuous and thus this cannot fail (but can give the duplicate local time). - #[allow(clippy::wrong_self_convention)] - fn from_utc_datetime(&self, utc: &NaiveDateTime) -> DateTime { - DateTime::from_naive_utc_and_offset(*utc, self.offset_from_utc_datetime(utc)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_fixed_offset_min_max_dates() { - for offset_hour in -23..=23 { - dbg!(offset_hour); - let offset = FixedOffset::east_opt(offset_hour * 60 * 60).unwrap(); - - let local_max = offset.from_utc_datetime(&NaiveDateTime::MAX); - assert_eq!(local_max.naive_utc(), NaiveDateTime::MAX); - let local_min = offset.from_utc_datetime(&NaiveDateTime::MIN); - assert_eq!(local_min.naive_utc(), NaiveDateTime::MIN); - - let local_max = offset.from_local_datetime(&NaiveDateTime::MAX); - if offset_hour >= 0 { - assert_eq!(local_max.unwrap().naive_local(), NaiveDateTime::MAX); - } else { - assert_eq!(local_max, MappedLocalTime::None); - } - let local_min = offset.from_local_datetime(&NaiveDateTime::MIN); - if offset_hour <= 0 { - assert_eq!(local_min.unwrap().naive_local(), NaiveDateTime::MIN); - } else { - assert_eq!(local_min, MappedLocalTime::None); - } - } - } - - #[test] - fn test_negative_millis() { - let dt = Utc.timestamp_millis_opt(-1000).unwrap(); - assert_eq!(dt.to_string(), "1969-12-31 23:59:59 UTC"); - let dt = Utc.timestamp_millis_opt(-7000).unwrap(); - assert_eq!(dt.to_string(), "1969-12-31 23:59:53 UTC"); - let dt = Utc.timestamp_millis_opt(-7001).unwrap(); - assert_eq!(dt.to_string(), "1969-12-31 23:59:52.999 UTC"); - let dt = Utc.timestamp_millis_opt(-7003).unwrap(); - assert_eq!(dt.to_string(), "1969-12-31 23:59:52.997 UTC"); - let dt = Utc.timestamp_millis_opt(-999).unwrap(); - assert_eq!(dt.to_string(), "1969-12-31 23:59:59.001 UTC"); - let dt = Utc.timestamp_millis_opt(-1).unwrap(); - assert_eq!(dt.to_string(), "1969-12-31 23:59:59.999 UTC"); - let dt = Utc.timestamp_millis_opt(-60000).unwrap(); - assert_eq!(dt.to_string(), "1969-12-31 23:59:00 UTC"); - let dt = Utc.timestamp_millis_opt(-3600000).unwrap(); - assert_eq!(dt.to_string(), "1969-12-31 23:00:00 UTC"); - - for (millis, expected) in &[ - (-7000, "1969-12-31 23:59:53 UTC"), - (-7001, "1969-12-31 23:59:52.999 UTC"), - (-7003, "1969-12-31 23:59:52.997 UTC"), - ] { - match Utc.timestamp_millis_opt(*millis) { - MappedLocalTime::Single(dt) => { - assert_eq!(dt.to_string(), *expected); - } - e => panic!("Got {e:?} instead of an okay answer"), - } - } - } - - #[test] - fn test_negative_nanos() { - let dt = Utc.timestamp_nanos(-1_000_000_000); - assert_eq!(dt.to_string(), "1969-12-31 23:59:59 UTC"); - let dt = Utc.timestamp_nanos(-999_999_999); - assert_eq!(dt.to_string(), "1969-12-31 23:59:59.000000001 UTC"); - let dt = Utc.timestamp_nanos(-1); - assert_eq!(dt.to_string(), "1969-12-31 23:59:59.999999999 UTC"); - let dt = Utc.timestamp_nanos(-60_000_000_000); - assert_eq!(dt.to_string(), "1969-12-31 23:59:00 UTC"); - let dt = Utc.timestamp_nanos(-3_600_000_000_000); - assert_eq!(dt.to_string(), "1969-12-31 23:00:00 UTC"); - } - - #[test] - fn test_nanos_never_panics() { - Utc.timestamp_nanos(i64::MAX); - Utc.timestamp_nanos(i64::default()); - Utc.timestamp_nanos(i64::MIN); - } - - #[test] - fn test_negative_micros() { - let dt = Utc.timestamp_micros(-1_000_000).unwrap(); - assert_eq!(dt.to_string(), "1969-12-31 23:59:59 UTC"); - let dt = Utc.timestamp_micros(-999_999).unwrap(); - assert_eq!(dt.to_string(), "1969-12-31 23:59:59.000001 UTC"); - let dt = Utc.timestamp_micros(-1).unwrap(); - assert_eq!(dt.to_string(), "1969-12-31 23:59:59.999999 UTC"); - let dt = Utc.timestamp_micros(-60_000_000).unwrap(); - assert_eq!(dt.to_string(), "1969-12-31 23:59:00 UTC"); - let dt = Utc.timestamp_micros(-3_600_000_000).unwrap(); - assert_eq!(dt.to_string(), "1969-12-31 23:00:00 UTC"); - } -} diff --git a/chrono-0.4.44/src/offset/utc.rs b/chrono-0.4.44/src/offset/utc.rs deleted file mode 100644 index c1a27c8598..0000000000 --- a/chrono-0.4.44/src/offset/utc.rs +++ /dev/null @@ -1,159 +0,0 @@ -// This is a part of Chrono. -// See README.md and LICENSE.txt for details. - -//! The UTC (Coordinated Universal Time) time zone. - -use core::fmt; -#[cfg(all( - feature = "now", - not(all( - target_arch = "wasm32", - feature = "wasmbind", - not(any(target_os = "emscripten", target_os = "wasi", target_os = "linux")) - )) -))] -use std::time::{SystemTime, UNIX_EPOCH}; - -#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))] -use rkyv::{Archive, Deserialize, Serialize}; - -use super::{FixedOffset, MappedLocalTime, Offset, TimeZone}; -use crate::naive::{NaiveDate, NaiveDateTime}; -#[cfg(feature = "now")] -#[allow(deprecated)] -use crate::{Date, DateTime}; - -/// The UTC time zone. This is the most efficient time zone when you don't need the local time. -/// It is also used as an offset (which is also a dummy type). -/// -/// Using the [`TimeZone`](./trait.TimeZone.html) methods -/// on the UTC struct is the preferred way to construct `DateTime` -/// instances. -/// -/// # Example -/// -/// ``` -/// use chrono::{DateTime, TimeZone, Utc}; -/// -/// let dt = DateTime::from_timestamp(61, 0).unwrap(); -/// -/// assert_eq!(Utc.timestamp_opt(61, 0).unwrap(), dt); -/// assert_eq!(Utc.with_ymd_and_hms(1970, 1, 1, 0, 1, 1).unwrap(), dt); -/// ``` -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -#[cfg_attr( - any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"), - derive(Archive, Deserialize, Serialize), - archive(compare(PartialEq)), - archive_attr(derive(Clone, Copy, PartialEq, Eq, Debug, Hash)) -)] -#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] -#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(arbitrary::Arbitrary))] -pub struct Utc; - -#[cfg(feature = "now")] -impl Utc { - /// Returns a `Date` which corresponds to the current date. - #[deprecated( - since = "0.4.23", - note = "use `Utc::now()` instead, potentially with `.date_naive()`" - )] - #[allow(deprecated)] - #[must_use] - pub fn today() -> Date { - Utc::now().date() - } - - /// Returns a `DateTime` which corresponds to the current date and time in UTC. - /// - /// See also the similar [`Local::now()`] which returns `DateTime`, i.e. the local date - /// and time including offset from UTC. - /// - /// [`Local::now()`]: crate::Local::now - /// - /// # Example - /// - /// ``` - /// # #![allow(unused_variables)] - /// # use chrono::{FixedOffset, Utc}; - /// // Current time in UTC - /// let now_utc = Utc::now(); - /// - /// // Current date in UTC - /// let today_utc = now_utc.date_naive(); - /// - /// // Current time in some timezone (let's use +05:00) - /// let offset = FixedOffset::east_opt(5 * 60 * 60).unwrap(); - /// let now_with_offset = Utc::now().with_timezone(&offset); - /// ``` - #[cfg(not(all( - target_arch = "wasm32", - feature = "wasmbind", - not(any(target_os = "emscripten", target_os = "wasi", target_os = "linux")) - )))] - #[must_use] - pub fn now() -> DateTime { - let now = - SystemTime::now().duration_since(UNIX_EPOCH).expect("system time before Unix epoch"); - DateTime::from_timestamp(now.as_secs() as i64, now.subsec_nanos()).unwrap() - } - - /// Returns a `DateTime` which corresponds to the current date and time. - #[cfg(all( - target_arch = "wasm32", - feature = "wasmbind", - not(any(target_os = "emscripten", target_os = "wasi", target_os = "linux")) - ))] - #[must_use] - pub fn now() -> DateTime { - let now = js_sys::Date::new_0(); - DateTime::::from(now) - } -} - -impl TimeZone for Utc { - type Offset = Utc; - - fn from_offset(_state: &Utc) -> Utc { - Utc - } - - fn offset_from_local_date(&self, _local: &NaiveDate) -> MappedLocalTime { - MappedLocalTime::Single(Utc) - } - fn offset_from_local_datetime(&self, _local: &NaiveDateTime) -> MappedLocalTime { - MappedLocalTime::Single(Utc) - } - - fn offset_from_utc_date(&self, _utc: &NaiveDate) -> Utc { - Utc - } - fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> Utc { - Utc - } -} - -impl Offset for Utc { - fn fix(&self) -> FixedOffset { - FixedOffset::east_opt(0).unwrap() - } -} - -impl fmt::Debug for Utc { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Z") - } -} - -impl fmt::Display for Utc { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "UTC") - } -} - -#[cfg(feature = "defmt")] -impl defmt::Format for Utc { - fn format(&self, fmt: defmt::Formatter) { - defmt::write!(fmt, "Z"); - } -} diff --git a/chrono-0.4.44/src/round.rs b/chrono-0.4.44/src/round.rs deleted file mode 100644 index 34c4fe442c..0000000000 --- a/chrono-0.4.44/src/round.rs +++ /dev/null @@ -1,1095 +0,0 @@ -// This is a part of Chrono. -// See README.md and LICENSE.txt for details. - -//! Functionality for rounding or truncating a `DateTime` by a `TimeDelta`. - -use crate::{DateTime, NaiveDateTime, TimeDelta, TimeZone, Timelike}; -use core::cmp::Ordering; -use core::fmt; -use core::ops::{Add, Sub}; - -/// Extension trait for subsecond rounding or truncation to a maximum number -/// of digits. Rounding can be used to decrease the error variance when -/// serializing/persisting to lower precision. Truncation is the default -/// behavior in Chrono display formatting. Either can be used to guarantee -/// equality (e.g. for testing) when round-tripping through a lower precision -/// format. -pub trait SubsecRound { - /// Return a copy rounded to the specified number of subsecond digits. With - /// 9 or more digits, self is returned unmodified. Halfway values are - /// rounded up (away from zero). - /// - /// # Example - /// ``` rust - /// # use chrono::{SubsecRound, Timelike, NaiveDate}; - /// let dt = NaiveDate::from_ymd_opt(2018, 1, 11) - /// .unwrap() - /// .and_hms_milli_opt(12, 0, 0, 154) - /// .unwrap() - /// .and_utc(); - /// assert_eq!(dt.round_subsecs(2).nanosecond(), 150_000_000); - /// assert_eq!(dt.round_subsecs(1).nanosecond(), 200_000_000); - /// ``` - fn round_subsecs(self, digits: u16) -> Self; - - /// Return a copy truncated to the specified number of subsecond - /// digits. With 9 or more digits, self is returned unmodified. - /// - /// # Example - /// ``` rust - /// # use chrono::{SubsecRound, Timelike, NaiveDate}; - /// let dt = NaiveDate::from_ymd_opt(2018, 1, 11) - /// .unwrap() - /// .and_hms_milli_opt(12, 0, 0, 154) - /// .unwrap() - /// .and_utc(); - /// assert_eq!(dt.trunc_subsecs(2).nanosecond(), 150_000_000); - /// assert_eq!(dt.trunc_subsecs(1).nanosecond(), 100_000_000); - /// ``` - fn trunc_subsecs(self, digits: u16) -> Self; -} - -impl SubsecRound for T -where - T: Timelike + Add + Sub, -{ - fn round_subsecs(self, digits: u16) -> T { - let span = span_for_digits(digits); - let delta_down = self.nanosecond() % span; - if delta_down > 0 { - let delta_up = span - delta_down; - if delta_up <= delta_down { - self + TimeDelta::nanoseconds(delta_up.into()) - } else { - self - TimeDelta::nanoseconds(delta_down.into()) - } - } else { - self // unchanged - } - } - - fn trunc_subsecs(self, digits: u16) -> T { - let span = span_for_digits(digits); - let delta_down = self.nanosecond() % span; - if delta_down > 0 { - self - TimeDelta::nanoseconds(delta_down.into()) - } else { - self // unchanged - } - } -} - -// Return the maximum span in nanoseconds for the target number of digits. -const fn span_for_digits(digits: u16) -> u32 { - // fast lookup form of: 10^(9-min(9,digits)) - match digits { - 0 => 1_000_000_000, - 1 => 100_000_000, - 2 => 10_000_000, - 3 => 1_000_000, - 4 => 100_000, - 5 => 10_000, - 6 => 1_000, - 7 => 100, - 8 => 10, - _ => 1, - } -} - -/// Extension trait for rounding or truncating a DateTime by a TimeDelta. -/// -/// # Limitations -/// Both rounding and truncating are done via [`TimeDelta::num_nanoseconds`] and -/// [`DateTime::timestamp_nanos_opt`]. This means that they will fail if either the -/// `TimeDelta` or the `DateTime` are too big to represented as nanoseconds. They -/// will also fail if the `TimeDelta` is bigger than the timestamp, negative or zero. -pub trait DurationRound: Sized { - /// Error that can occur in rounding or truncating - #[cfg(feature = "std")] - type Err: std::error::Error; - - /// Error that can occur in rounding or truncating - #[cfg(all(not(feature = "std"), feature = "core-error"))] - type Err: core::error::Error; - - /// Error that can occur in rounding or truncating - #[cfg(all(not(feature = "std"), not(feature = "core-error")))] - type Err: fmt::Debug + fmt::Display; - - /// Return a copy rounded by TimeDelta. - /// - /// # Example - /// ``` rust - /// # use chrono::{DurationRound, TimeDelta, NaiveDate}; - /// let dt = NaiveDate::from_ymd_opt(2018, 1, 11) - /// .unwrap() - /// .and_hms_milli_opt(12, 0, 0, 154) - /// .unwrap() - /// .and_utc(); - /// assert_eq!( - /// dt.duration_round(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(), - /// "2018-01-11 12:00:00.150 UTC" - /// ); - /// assert_eq!( - /// dt.duration_round(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), - /// "2018-01-12 00:00:00 UTC" - /// ); - /// ``` - fn duration_round(self, duration: TimeDelta) -> Result; - - /// Return a copy truncated by TimeDelta. - /// - /// # Example - /// ``` rust - /// # use chrono::{DurationRound, TimeDelta, NaiveDate}; - /// let dt = NaiveDate::from_ymd_opt(2018, 1, 11) - /// .unwrap() - /// .and_hms_milli_opt(12, 0, 0, 154) - /// .unwrap() - /// .and_utc(); - /// assert_eq!( - /// dt.duration_trunc(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(), - /// "2018-01-11 12:00:00.150 UTC" - /// ); - /// assert_eq!( - /// dt.duration_trunc(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), - /// "2018-01-11 00:00:00 UTC" - /// ); - /// ``` - fn duration_trunc(self, duration: TimeDelta) -> Result; - - /// Return a copy rounded **up** by TimeDelta. - /// - /// # Example - /// ``` rust - /// # use chrono::{DurationRound, TimeDelta, NaiveDate}; - /// let dt = NaiveDate::from_ymd_opt(2018, 1, 11) - /// .unwrap() - /// .and_hms_milli_opt(12, 0, 0, 154) - /// .unwrap() - /// .and_utc(); - /// assert_eq!( - /// dt.duration_round_up(TimeDelta::milliseconds(10)).unwrap().to_string(), - /// "2018-01-11 12:00:00.160 UTC" - /// ); - /// assert_eq!( - /// dt.duration_round_up(TimeDelta::hours(1)).unwrap().to_string(), - /// "2018-01-11 13:00:00 UTC" - /// ); - /// - /// assert_eq!( - /// dt.duration_round_up(TimeDelta::days(1)).unwrap().to_string(), - /// "2018-01-12 00:00:00 UTC" - /// ); - /// ``` - fn duration_round_up(self, duration: TimeDelta) -> Result; -} - -impl DurationRound for DateTime { - type Err = RoundingError; - - fn duration_round(self, duration: TimeDelta) -> Result { - duration_round(self.naive_local(), self, duration) - } - - fn duration_trunc(self, duration: TimeDelta) -> Result { - duration_trunc(self.naive_local(), self, duration) - } - - fn duration_round_up(self, duration: TimeDelta) -> Result { - duration_round_up(self.naive_local(), self, duration) - } -} - -impl DurationRound for NaiveDateTime { - type Err = RoundingError; - - fn duration_round(self, duration: TimeDelta) -> Result { - duration_round(self, self, duration) - } - - fn duration_trunc(self, duration: TimeDelta) -> Result { - duration_trunc(self, self, duration) - } - - fn duration_round_up(self, duration: TimeDelta) -> Result { - duration_round_up(self, self, duration) - } -} - -fn duration_round( - naive: NaiveDateTime, - original: T, - duration: TimeDelta, -) -> Result -where - T: Timelike + Add + Sub, -{ - if let Some(span) = duration.num_nanoseconds() { - if span <= 0 { - return Err(RoundingError::DurationExceedsLimit); - } - let stamp = - naive.and_utc().timestamp_nanos_opt().ok_or(RoundingError::TimestampExceedsLimit)?; - let delta_down = stamp % span; - if delta_down == 0 { - Ok(original) - } else { - let (delta_up, delta_down) = if delta_down < 0 { - (delta_down.abs(), span - delta_down.abs()) - } else { - (span - delta_down, delta_down) - }; - if delta_up <= delta_down { - Ok(original + TimeDelta::nanoseconds(delta_up)) - } else { - Ok(original - TimeDelta::nanoseconds(delta_down)) - } - } - } else { - Err(RoundingError::DurationExceedsLimit) - } -} - -fn duration_trunc( - naive: NaiveDateTime, - original: T, - duration: TimeDelta, -) -> Result -where - T: Timelike + Add + Sub, -{ - if let Some(span) = duration.num_nanoseconds() { - if span <= 0 { - return Err(RoundingError::DurationExceedsLimit); - } - let stamp = - naive.and_utc().timestamp_nanos_opt().ok_or(RoundingError::TimestampExceedsLimit)?; - let delta_down = stamp % span; - match delta_down.cmp(&0) { - Ordering::Equal => Ok(original), - Ordering::Greater => Ok(original - TimeDelta::nanoseconds(delta_down)), - Ordering::Less => Ok(original - TimeDelta::nanoseconds(span - delta_down.abs())), - } - } else { - Err(RoundingError::DurationExceedsLimit) - } -} - -fn duration_round_up( - naive: NaiveDateTime, - original: T, - duration: TimeDelta, -) -> Result -where - T: Timelike + Add + Sub, -{ - if let Some(span) = duration.num_nanoseconds() { - if span <= 0 { - return Err(RoundingError::DurationExceedsLimit); - } - let stamp = - naive.and_utc().timestamp_nanos_opt().ok_or(RoundingError::TimestampExceedsLimit)?; - let delta_down = stamp % span; - match delta_down.cmp(&0) { - Ordering::Equal => Ok(original), - Ordering::Greater => Ok(original + TimeDelta::nanoseconds(span - delta_down)), - Ordering::Less => Ok(original + TimeDelta::nanoseconds(delta_down.abs())), - } - } else { - Err(RoundingError::DurationExceedsLimit) - } -} - -/// An error from rounding by `TimeDelta` -/// -/// See: [`DurationRound`] -#[derive(Debug, Clone, PartialEq, Eq, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum RoundingError { - /// Error when the TimeDelta exceeds the TimeDelta from or until the Unix epoch. - /// - /// Note: this error is not produced anymore. - DurationExceedsTimestamp, - - /// Error when `TimeDelta.num_nanoseconds` exceeds the limit. - /// - /// ``` rust - /// # use chrono::{DurationRound, TimeDelta, RoundingError, NaiveDate}; - /// let dt = NaiveDate::from_ymd_opt(2260, 12, 31) - /// .unwrap() - /// .and_hms_nano_opt(23, 59, 59, 1_75_500_000) - /// .unwrap() - /// .and_utc(); - /// - /// assert_eq!( - /// dt.duration_round(TimeDelta::try_days(300 * 365).unwrap()), - /// Err(RoundingError::DurationExceedsLimit) - /// ); - /// ``` - DurationExceedsLimit, - - /// Error when `DateTime.timestamp_nanos` exceeds the limit. - /// - /// ``` rust - /// # use chrono::{DurationRound, TimeDelta, RoundingError, TimeZone, Utc}; - /// let dt = Utc.with_ymd_and_hms(2300, 12, 12, 0, 0, 0).unwrap(); - /// - /// assert_eq!( - /// dt.duration_round(TimeDelta::try_days(1).unwrap()), - /// Err(RoundingError::TimestampExceedsLimit) - /// ); - /// ``` - TimestampExceedsLimit, -} - -impl fmt::Display for RoundingError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - RoundingError::DurationExceedsTimestamp => { - write!(f, "duration in nanoseconds exceeds timestamp") - } - RoundingError::DurationExceedsLimit => { - write!(f, "duration exceeds num_nanoseconds limit") - } - RoundingError::TimestampExceedsLimit => { - write!(f, "timestamp exceeds num_nanoseconds limit") - } - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for RoundingError { - #[allow(deprecated)] - fn description(&self) -> &str { - "error from rounding or truncating with DurationRound" - } -} - -#[cfg(all(not(feature = "std"), feature = "core-error"))] -impl core::error::Error for RoundingError { - #[allow(deprecated)] - fn description(&self) -> &str { - "error from rounding or truncating with DurationRound" - } -} - -#[cfg(test)] -mod tests { - use super::{DurationRound, RoundingError, SubsecRound, TimeDelta}; - use crate::Timelike; - use crate::offset::{FixedOffset, TimeZone, Utc}; - use crate::{DateTime, NaiveDate}; - - #[test] - fn test_round_subsecs() { - let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap(); - let dt = pst - .from_local_datetime( - &NaiveDate::from_ymd_opt(2018, 1, 11) - .unwrap() - .and_hms_nano_opt(10, 5, 13, 84_660_684) - .unwrap(), - ) - .unwrap(); - - assert_eq!(dt.round_subsecs(10), dt); - assert_eq!(dt.round_subsecs(9), dt); - assert_eq!(dt.round_subsecs(8).nanosecond(), 84_660_680); - assert_eq!(dt.round_subsecs(7).nanosecond(), 84_660_700); - assert_eq!(dt.round_subsecs(6).nanosecond(), 84_661_000); - assert_eq!(dt.round_subsecs(5).nanosecond(), 84_660_000); - assert_eq!(dt.round_subsecs(4).nanosecond(), 84_700_000); - assert_eq!(dt.round_subsecs(3).nanosecond(), 85_000_000); - assert_eq!(dt.round_subsecs(2).nanosecond(), 80_000_000); - assert_eq!(dt.round_subsecs(1).nanosecond(), 100_000_000); - - assert_eq!(dt.round_subsecs(0).nanosecond(), 0); - assert_eq!(dt.round_subsecs(0).second(), 13); - - let dt = Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2018, 1, 11) - .unwrap() - .and_hms_nano_opt(10, 5, 27, 750_500_000) - .unwrap(), - ) - .unwrap(); - assert_eq!(dt.round_subsecs(9), dt); - assert_eq!(dt.round_subsecs(4), dt); - assert_eq!(dt.round_subsecs(3).nanosecond(), 751_000_000); - assert_eq!(dt.round_subsecs(2).nanosecond(), 750_000_000); - assert_eq!(dt.round_subsecs(1).nanosecond(), 800_000_000); - - assert_eq!(dt.round_subsecs(0).nanosecond(), 0); - assert_eq!(dt.round_subsecs(0).second(), 28); - } - - #[test] - fn test_round_leap_nanos() { - let dt = Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2016, 12, 31) - .unwrap() - .and_hms_nano_opt(23, 59, 59, 1_750_500_000) - .unwrap(), - ) - .unwrap(); - assert_eq!(dt.round_subsecs(9), dt); - assert_eq!(dt.round_subsecs(4), dt); - assert_eq!(dt.round_subsecs(2).nanosecond(), 1_750_000_000); - assert_eq!(dt.round_subsecs(1).nanosecond(), 1_800_000_000); - assert_eq!(dt.round_subsecs(1).second(), 59); - - assert_eq!(dt.round_subsecs(0).nanosecond(), 0); - assert_eq!(dt.round_subsecs(0).second(), 0); - } - - #[test] - fn test_trunc_subsecs() { - let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap(); - let dt = pst - .from_local_datetime( - &NaiveDate::from_ymd_opt(2018, 1, 11) - .unwrap() - .and_hms_nano_opt(10, 5, 13, 84_660_684) - .unwrap(), - ) - .unwrap(); - - assert_eq!(dt.trunc_subsecs(10), dt); - assert_eq!(dt.trunc_subsecs(9), dt); - assert_eq!(dt.trunc_subsecs(8).nanosecond(), 84_660_680); - assert_eq!(dt.trunc_subsecs(7).nanosecond(), 84_660_600); - assert_eq!(dt.trunc_subsecs(6).nanosecond(), 84_660_000); - assert_eq!(dt.trunc_subsecs(5).nanosecond(), 84_660_000); - assert_eq!(dt.trunc_subsecs(4).nanosecond(), 84_600_000); - assert_eq!(dt.trunc_subsecs(3).nanosecond(), 84_000_000); - assert_eq!(dt.trunc_subsecs(2).nanosecond(), 80_000_000); - assert_eq!(dt.trunc_subsecs(1).nanosecond(), 0); - - assert_eq!(dt.trunc_subsecs(0).nanosecond(), 0); - assert_eq!(dt.trunc_subsecs(0).second(), 13); - - let dt = pst - .from_local_datetime( - &NaiveDate::from_ymd_opt(2018, 1, 11) - .unwrap() - .and_hms_nano_opt(10, 5, 27, 750_500_000) - .unwrap(), - ) - .unwrap(); - assert_eq!(dt.trunc_subsecs(9), dt); - assert_eq!(dt.trunc_subsecs(4), dt); - assert_eq!(dt.trunc_subsecs(3).nanosecond(), 750_000_000); - assert_eq!(dt.trunc_subsecs(2).nanosecond(), 750_000_000); - assert_eq!(dt.trunc_subsecs(1).nanosecond(), 700_000_000); - - assert_eq!(dt.trunc_subsecs(0).nanosecond(), 0); - assert_eq!(dt.trunc_subsecs(0).second(), 27); - } - - #[test] - fn test_trunc_leap_nanos() { - let dt = Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2016, 12, 31) - .unwrap() - .and_hms_nano_opt(23, 59, 59, 1_750_500_000) - .unwrap(), - ) - .unwrap(); - assert_eq!(dt.trunc_subsecs(9), dt); - assert_eq!(dt.trunc_subsecs(4), dt); - assert_eq!(dt.trunc_subsecs(2).nanosecond(), 1_750_000_000); - assert_eq!(dt.trunc_subsecs(1).nanosecond(), 1_700_000_000); - assert_eq!(dt.trunc_subsecs(1).second(), 59); - - assert_eq!(dt.trunc_subsecs(0).nanosecond(), 1_000_000_000); - assert_eq!(dt.trunc_subsecs(0).second(), 59); - } - - #[test] - fn test_duration_round() { - let dt = Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2016, 12, 31) - .unwrap() - .and_hms_nano_opt(23, 59, 59, 175_500_000) - .unwrap(), - ) - .unwrap(); - - assert_eq!( - dt.duration_round(TimeDelta::new(-1, 0).unwrap()), - Err(RoundingError::DurationExceedsLimit) - ); - assert_eq!(dt.duration_round(TimeDelta::zero()), Err(RoundingError::DurationExceedsLimit)); - - assert_eq!( - dt.duration_round(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(), - "2016-12-31 23:59:59.180 UTC" - ); - - // round up - let dt = Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2012, 12, 12) - .unwrap() - .and_hms_milli_opt(18, 22, 30, 0) - .unwrap(), - ) - .unwrap(); - assert_eq!( - dt.duration_round(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(), - "2012-12-12 18:25:00 UTC" - ); - // round down - let dt = Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2012, 12, 12) - .unwrap() - .and_hms_milli_opt(18, 22, 29, 999) - .unwrap(), - ) - .unwrap(); - assert_eq!( - dt.duration_round(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(), - "2012-12-12 18:20:00 UTC" - ); - - assert_eq!( - dt.duration_round(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(), - "2012-12-12 18:20:00 UTC" - ); - assert_eq!( - dt.duration_round(TimeDelta::try_minutes(30).unwrap()).unwrap().to_string(), - "2012-12-12 18:30:00 UTC" - ); - assert_eq!( - dt.duration_round(TimeDelta::try_hours(1).unwrap()).unwrap().to_string(), - "2012-12-12 18:00:00 UTC" - ); - assert_eq!( - dt.duration_round(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), - "2012-12-13 00:00:00 UTC" - ); - - // timezone east - let dt = - FixedOffset::east_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap(); - assert_eq!( - dt.duration_round(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), - "2020-10-28 00:00:00 +01:00" - ); - assert_eq!( - dt.duration_round(TimeDelta::try_weeks(1).unwrap()).unwrap().to_string(), - "2020-10-29 00:00:00 +01:00" - ); - - // timezone west - let dt = - FixedOffset::west_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap(); - assert_eq!( - dt.duration_round(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), - "2020-10-28 00:00:00 -01:00" - ); - assert_eq!( - dt.duration_round(TimeDelta::try_weeks(1).unwrap()).unwrap().to_string(), - "2020-10-29 00:00:00 -01:00" - ); - } - - #[test] - fn test_duration_round_naive() { - let dt = Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2016, 12, 31) - .unwrap() - .and_hms_nano_opt(23, 59, 59, 175_500_000) - .unwrap(), - ) - .unwrap() - .naive_utc(); - - assert_eq!( - dt.duration_round(TimeDelta::new(-1, 0).unwrap()), - Err(RoundingError::DurationExceedsLimit) - ); - assert_eq!(dt.duration_round(TimeDelta::zero()), Err(RoundingError::DurationExceedsLimit)); - - assert_eq!( - dt.duration_round(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(), - "2016-12-31 23:59:59.180" - ); - - // round up - let dt = Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2012, 12, 12) - .unwrap() - .and_hms_milli_opt(18, 22, 30, 0) - .unwrap(), - ) - .unwrap() - .naive_utc(); - assert_eq!( - dt.duration_round(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(), - "2012-12-12 18:25:00" - ); - // round down - let dt = Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2012, 12, 12) - .unwrap() - .and_hms_milli_opt(18, 22, 29, 999) - .unwrap(), - ) - .unwrap() - .naive_utc(); - assert_eq!( - dt.duration_round(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(), - "2012-12-12 18:20:00" - ); - - assert_eq!( - dt.duration_round(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(), - "2012-12-12 18:20:00" - ); - assert_eq!( - dt.duration_round(TimeDelta::try_minutes(30).unwrap()).unwrap().to_string(), - "2012-12-12 18:30:00" - ); - assert_eq!( - dt.duration_round(TimeDelta::try_hours(1).unwrap()).unwrap().to_string(), - "2012-12-12 18:00:00" - ); - assert_eq!( - dt.duration_round(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), - "2012-12-13 00:00:00" - ); - } - - #[test] - fn test_duration_round_pre_epoch() { - let dt = Utc.with_ymd_and_hms(1969, 12, 12, 12, 12, 12).unwrap(); - assert_eq!( - dt.duration_round(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(), - "1969-12-12 12:10:00 UTC" - ); - } - - #[test] - fn test_duration_trunc() { - let dt = Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2016, 12, 31) - .unwrap() - .and_hms_nano_opt(23, 59, 59, 175_500_000) - .unwrap(), - ) - .unwrap(); - - assert_eq!( - dt.duration_trunc(TimeDelta::new(-1, 0).unwrap()), - Err(RoundingError::DurationExceedsLimit) - ); - assert_eq!(dt.duration_trunc(TimeDelta::zero()), Err(RoundingError::DurationExceedsLimit)); - - assert_eq!( - dt.duration_trunc(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(), - "2016-12-31 23:59:59.170 UTC" - ); - - // would round up - let dt = Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2012, 12, 12) - .unwrap() - .and_hms_milli_opt(18, 22, 30, 0) - .unwrap(), - ) - .unwrap(); - assert_eq!( - dt.duration_trunc(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(), - "2012-12-12 18:20:00 UTC" - ); - // would round down - let dt = Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2012, 12, 12) - .unwrap() - .and_hms_milli_opt(18, 22, 29, 999) - .unwrap(), - ) - .unwrap(); - assert_eq!( - dt.duration_trunc(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(), - "2012-12-12 18:20:00 UTC" - ); - assert_eq!( - dt.duration_trunc(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(), - "2012-12-12 18:20:00 UTC" - ); - assert_eq!( - dt.duration_trunc(TimeDelta::try_minutes(30).unwrap()).unwrap().to_string(), - "2012-12-12 18:00:00 UTC" - ); - assert_eq!( - dt.duration_trunc(TimeDelta::try_hours(1).unwrap()).unwrap().to_string(), - "2012-12-12 18:00:00 UTC" - ); - assert_eq!( - dt.duration_trunc(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), - "2012-12-12 00:00:00 UTC" - ); - - // timezone east - let dt = - FixedOffset::east_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap(); - assert_eq!( - dt.duration_trunc(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), - "2020-10-27 00:00:00 +01:00" - ); - assert_eq!( - dt.duration_trunc(TimeDelta::try_weeks(1).unwrap()).unwrap().to_string(), - "2020-10-22 00:00:00 +01:00" - ); - - // timezone west - let dt = - FixedOffset::west_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap(); - assert_eq!( - dt.duration_trunc(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), - "2020-10-27 00:00:00 -01:00" - ); - assert_eq!( - dt.duration_trunc(TimeDelta::try_weeks(1).unwrap()).unwrap().to_string(), - "2020-10-22 00:00:00 -01:00" - ); - } - - #[test] - fn test_duration_trunc_naive() { - let dt = Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2016, 12, 31) - .unwrap() - .and_hms_nano_opt(23, 59, 59, 175_500_000) - .unwrap(), - ) - .unwrap() - .naive_utc(); - - assert_eq!( - dt.duration_trunc(TimeDelta::new(-1, 0).unwrap()), - Err(RoundingError::DurationExceedsLimit) - ); - assert_eq!(dt.duration_trunc(TimeDelta::zero()), Err(RoundingError::DurationExceedsLimit)); - - assert_eq!( - dt.duration_trunc(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(), - "2016-12-31 23:59:59.170" - ); - - // would round up - let dt = Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2012, 12, 12) - .unwrap() - .and_hms_milli_opt(18, 22, 30, 0) - .unwrap(), - ) - .unwrap() - .naive_utc(); - assert_eq!( - dt.duration_trunc(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(), - "2012-12-12 18:20:00" - ); - // would round down - let dt = Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2012, 12, 12) - .unwrap() - .and_hms_milli_opt(18, 22, 29, 999) - .unwrap(), - ) - .unwrap() - .naive_utc(); - assert_eq!( - dt.duration_trunc(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(), - "2012-12-12 18:20:00" - ); - assert_eq!( - dt.duration_trunc(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(), - "2012-12-12 18:20:00" - ); - assert_eq!( - dt.duration_trunc(TimeDelta::try_minutes(30).unwrap()).unwrap().to_string(), - "2012-12-12 18:00:00" - ); - assert_eq!( - dt.duration_trunc(TimeDelta::try_hours(1).unwrap()).unwrap().to_string(), - "2012-12-12 18:00:00" - ); - assert_eq!( - dt.duration_trunc(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), - "2012-12-12 00:00:00" - ); - } - - #[test] - fn test_duration_trunc_pre_epoch() { - let dt = Utc.with_ymd_and_hms(1969, 12, 12, 12, 12, 12).unwrap(); - assert_eq!( - dt.duration_trunc(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(), - "1969-12-12 12:10:00 UTC" - ); - } - - #[test] - fn issue1010() { - let dt = DateTime::from_timestamp(-4_227_854_320, 678_774_288).unwrap(); - let span = TimeDelta::microseconds(-7_019_067_213_869_040); - assert_eq!(dt.duration_trunc(span), Err(RoundingError::DurationExceedsLimit)); - - let dt = DateTime::from_timestamp(320_041_586, 920_103_021).unwrap(); - let span = TimeDelta::nanoseconds(-8_923_838_508_697_114_584); - assert_eq!(dt.duration_round(span), Err(RoundingError::DurationExceedsLimit)); - - let dt = DateTime::from_timestamp(-2_621_440, 0).unwrap(); - let span = TimeDelta::nanoseconds(-9_223_372_036_854_771_421); - assert_eq!(dt.duration_round(span), Err(RoundingError::DurationExceedsLimit)); - } - - #[test] - fn test_duration_trunc_close_to_epoch() { - let span = TimeDelta::try_minutes(15).unwrap(); - - let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_opt(0, 0, 15).unwrap(); - assert_eq!(dt.duration_trunc(span).unwrap().to_string(), "1970-01-01 00:00:00"); - - let dt = NaiveDate::from_ymd_opt(1969, 12, 31).unwrap().and_hms_opt(23, 59, 45).unwrap(); - assert_eq!(dt.duration_trunc(span).unwrap().to_string(), "1969-12-31 23:45:00"); - } - - #[test] - fn test_duration_round_close_to_epoch() { - let span = TimeDelta::try_minutes(15).unwrap(); - - let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_opt(0, 0, 15).unwrap(); - assert_eq!(dt.duration_round(span).unwrap().to_string(), "1970-01-01 00:00:00"); - - let dt = NaiveDate::from_ymd_opt(1969, 12, 31).unwrap().and_hms_opt(23, 59, 45).unwrap(); - assert_eq!(dt.duration_round(span).unwrap().to_string(), "1970-01-01 00:00:00"); - } - - #[test] - fn test_duration_round_close_to_min_max() { - let span = TimeDelta::nanoseconds(i64::MAX); - - let dt = DateTime::from_timestamp_nanos(i64::MIN / 2 - 1); - assert_eq!( - dt.duration_round(span).unwrap().to_string(), - "1677-09-21 00:12:43.145224193 UTC" - ); - - let dt = DateTime::from_timestamp_nanos(i64::MIN / 2 + 1); - assert_eq!(dt.duration_round(span).unwrap().to_string(), "1970-01-01 00:00:00 UTC"); - - let dt = DateTime::from_timestamp_nanos(i64::MAX / 2 + 1); - assert_eq!( - dt.duration_round(span).unwrap().to_string(), - "2262-04-11 23:47:16.854775807 UTC" - ); - - let dt = DateTime::from_timestamp_nanos(i64::MAX / 2 - 1); - assert_eq!(dt.duration_round(span).unwrap().to_string(), "1970-01-01 00:00:00 UTC"); - } - - #[test] - fn test_duration_round_up() { - let dt = NaiveDate::from_ymd_opt(2016, 12, 31) - .unwrap() - .and_hms_nano_opt(23, 59, 59, 175_500_000) - .unwrap() - .and_utc(); - - assert_eq!( - dt.duration_round_up(TimeDelta::new(-1, 0).unwrap()), - Err(RoundingError::DurationExceedsLimit) - ); - - assert_eq!( - dt.duration_round_up(TimeDelta::zero()), - Err(RoundingError::DurationExceedsLimit) - ); - - assert_eq!(dt.duration_round_up(TimeDelta::MAX), Err(RoundingError::DurationExceedsLimit)); - - assert_eq!( - dt.duration_round_up(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(), - "2016-12-31 23:59:59.180 UTC" - ); - - // round up - let dt = NaiveDate::from_ymd_opt(2012, 12, 12) - .unwrap() - .and_hms_milli_opt(18, 22, 30, 0) - .unwrap() - .and_utc(); - - assert_eq!( - dt.duration_round_up(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(), - "2012-12-12 18:25:00 UTC" - ); - - assert_eq!( - dt.duration_round_up(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(), - "2012-12-12 18:30:00 UTC" - ); - assert_eq!( - dt.duration_round_up(TimeDelta::try_minutes(30).unwrap()).unwrap().to_string(), - "2012-12-12 18:30:00 UTC" - ); - assert_eq!( - dt.duration_round_up(TimeDelta::try_hours(1).unwrap()).unwrap().to_string(), - "2012-12-12 19:00:00 UTC" - ); - assert_eq!( - dt.duration_round_up(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), - "2012-12-13 00:00:00 UTC" - ); - - // timezone east - let dt = - FixedOffset::east_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap(); - assert_eq!( - dt.duration_round_up(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), - "2020-10-28 00:00:00 +01:00" - ); - assert_eq!( - dt.duration_round_up(TimeDelta::try_weeks(1).unwrap()).unwrap().to_string(), - "2020-10-29 00:00:00 +01:00" - ); - - // timezone west - let dt = - FixedOffset::west_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap(); - assert_eq!( - dt.duration_round_up(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), - "2020-10-28 00:00:00 -01:00" - ); - assert_eq!( - dt.duration_round_up(TimeDelta::try_weeks(1).unwrap()).unwrap().to_string(), - "2020-10-29 00:00:00 -01:00" - ); - } - - #[test] - fn test_duration_round_up_naive() { - let dt = NaiveDate::from_ymd_opt(2016, 12, 31) - .unwrap() - .and_hms_nano_opt(23, 59, 59, 175_500_000) - .unwrap(); - - assert_eq!( - dt.duration_round_up(TimeDelta::new(-1, 0).unwrap()), - Err(RoundingError::DurationExceedsLimit) - ); - assert_eq!( - dt.duration_round_up(TimeDelta::zero()), - Err(RoundingError::DurationExceedsLimit) - ); - - assert_eq!(dt.duration_round_up(TimeDelta::MAX), Err(RoundingError::DurationExceedsLimit)); - - assert_eq!( - dt.duration_round_up(TimeDelta::try_milliseconds(10).unwrap()).unwrap().to_string(), - "2016-12-31 23:59:59.180" - ); - - let dt = Utc - .from_local_datetime( - &NaiveDate::from_ymd_opt(2012, 12, 12) - .unwrap() - .and_hms_milli_opt(18, 22, 30, 0) - .unwrap(), - ) - .unwrap() - .naive_utc(); - assert_eq!( - dt.duration_round_up(TimeDelta::try_minutes(5).unwrap()).unwrap().to_string(), - "2012-12-12 18:25:00" - ); - assert_eq!( - dt.duration_round_up(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(), - "2012-12-12 18:30:00" - ); - assert_eq!( - dt.duration_round_up(TimeDelta::try_minutes(30).unwrap()).unwrap().to_string(), - "2012-12-12 18:30:00" - ); - assert_eq!( - dt.duration_round_up(TimeDelta::try_hours(1).unwrap()).unwrap().to_string(), - "2012-12-12 19:00:00" - ); - assert_eq!( - dt.duration_round_up(TimeDelta::try_days(1).unwrap()).unwrap().to_string(), - "2012-12-13 00:00:00" - ); - } - - #[test] - fn test_duration_round_up_pre_epoch() { - let dt = Utc.with_ymd_and_hms(1969, 12, 12, 12, 12, 12).unwrap(); - assert_eq!( - dt.duration_round_up(TimeDelta::try_minutes(10).unwrap()).unwrap().to_string(), - "1969-12-12 12:20:00 UTC" - ); - - let time_delta = TimeDelta::minutes(30); - assert_eq!( - DateTime::UNIX_EPOCH.duration_round_up(time_delta).unwrap().to_string(), - "1970-01-01 00:00:00 UTC" - ) - } - - #[test] - fn test_duration_round_up_close_to_min_max() { - let mut dt = NaiveDate::from_ymd_opt(2012, 12, 12) - .unwrap() - .and_hms_milli_opt(18, 22, 30, 0) - .unwrap() - .and_utc(); - - let span = TimeDelta::nanoseconds(i64::MAX); - - assert_eq!( - dt.duration_round_up(span).unwrap().to_string(), - DateTime::from_timestamp_nanos(i64::MAX).to_string() - ); - - dt = DateTime::UNIX_EPOCH + TimeDelta::nanoseconds(1); - assert_eq!(dt.duration_round_up(span).unwrap(), DateTime::from_timestamp_nanos(i64::MAX)); - - let dt = DateTime::from_timestamp_nanos(1); - assert_eq!( - dt.duration_round_up(span).unwrap().to_string(), - "2262-04-11 23:47:16.854775807 UTC" - ); - - let dt = DateTime::from_timestamp_nanos(-1); - assert_eq!(dt.duration_round_up(span).unwrap(), DateTime::UNIX_EPOCH); - - // Rounds to 1677-09-21 00:12:43.145224193 UTC if at i64::MIN. - // because i64::MIN is 1677-09-21 00:12:43.145224192 UTC. - // - // v - // We add 2 to get to 1677-09-21 00:12:43.145224194 UTC - // this issue is because abs(i64::MIN) == i64::MAX + 1 - let dt = DateTime::from_timestamp_nanos(i64::MIN + 2); - assert_eq!(dt.duration_round_up(span).unwrap(), DateTime::UNIX_EPOCH); - } -} diff --git a/chrono-0.4.44/src/time_delta.rs b/chrono-0.4.44/src/time_delta.rs deleted file mode 100644 index a622ab818e..0000000000 --- a/chrono-0.4.44/src/time_delta.rs +++ /dev/null @@ -1,1456 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Temporal quantification - -#[cfg(all(not(feature = "std"), feature = "core-error"))] -use core::error::Error; -use core::fmt; -use core::ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign}; -use core::time::Duration; -#[cfg(feature = "std")] -use std::error::Error; - -use crate::{expect, try_opt}; - -#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))] -use rkyv::{Archive, Deserialize, Serialize}; - -/// The number of nanoseconds in a microsecond. -const NANOS_PER_MICRO: i32 = 1000; -/// The number of nanoseconds in a millisecond. -const NANOS_PER_MILLI: i32 = 1_000_000; -/// The number of nanoseconds in seconds. -pub(crate) const NANOS_PER_SEC: i32 = 1_000_000_000; -/// The number of microseconds per second. -const MICROS_PER_SEC: i64 = 1_000_000; -/// The number of milliseconds per second. -const MILLIS_PER_SEC: i64 = 1000; -/// The number of seconds in a minute. -const SECS_PER_MINUTE: i64 = 60; -/// The number of seconds in an hour. -const SECS_PER_HOUR: i64 = 3600; -/// The number of (non-leap) seconds in days. -const SECS_PER_DAY: i64 = 86_400; -/// The number of (non-leap) seconds in a week. -const SECS_PER_WEEK: i64 = 604_800; - -/// Time duration with nanosecond precision. -/// -/// This also allows for negative durations; see individual methods for details. -/// -/// A `TimeDelta` is represented internally as a complement of seconds and -/// nanoseconds. The range is restricted to that of `i64` milliseconds, with the -/// minimum value notably being set to `-i64::MAX` rather than allowing the full -/// range of `i64::MIN`. This is to allow easy flipping of sign, so that for -/// instance `abs()` can be called without any checks. -#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] -#[cfg_attr( - any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"), - derive(Archive, Deserialize, Serialize), - archive(compare(PartialEq, PartialOrd)), - archive_attr(derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)) -)] -#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct TimeDelta { - secs: i64, - nanos: i32, // Always 0 <= nanos < NANOS_PER_SEC -} - -/// The minimum possible `TimeDelta`: `-i64::MAX` milliseconds. -pub(crate) const MIN: TimeDelta = TimeDelta { - secs: -i64::MAX / MILLIS_PER_SEC - 1, - nanos: NANOS_PER_SEC + (-i64::MAX % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI, -}; - -/// The maximum possible `TimeDelta`: `i64::MAX` milliseconds. -pub(crate) const MAX: TimeDelta = TimeDelta { - secs: i64::MAX / MILLIS_PER_SEC, - nanos: (i64::MAX % MILLIS_PER_SEC) as i32 * NANOS_PER_MILLI, -}; - -impl TimeDelta { - /// Makes a new `TimeDelta` with given number of seconds and nanoseconds. - /// - /// # Errors - /// - /// Returns `None` when the duration is out of bounds, or if `nanos` ≥ 1,000,000,000. - pub const fn new(secs: i64, nanos: u32) -> Option { - if secs < MIN.secs - || secs > MAX.secs - || nanos >= 1_000_000_000 - || (secs == MAX.secs && nanos > MAX.nanos as u32) - || (secs == MIN.secs && nanos < MIN.nanos as u32) - { - return None; - } - Some(TimeDelta { secs, nanos: nanos as i32 }) - } - - /// Makes a new `TimeDelta` with the given number of weeks. - /// - /// Equivalent to `TimeDelta::seconds(weeks * 7 * 24 * 60 * 60)` with - /// overflow checks. - /// - /// # Panics - /// - /// Panics when the duration is out of bounds. - #[inline] - #[must_use] - #[track_caller] - pub const fn weeks(weeks: i64) -> TimeDelta { - expect(TimeDelta::try_weeks(weeks), "TimeDelta::weeks out of bounds") - } - - /// Makes a new `TimeDelta` with the given number of weeks. - /// - /// Equivalent to `TimeDelta::try_seconds(weeks * 7 * 24 * 60 * 60)` with - /// overflow checks. - /// - /// # Errors - /// - /// Returns `None` when the `TimeDelta` would be out of bounds. - #[inline] - pub const fn try_weeks(weeks: i64) -> Option { - TimeDelta::try_seconds(try_opt!(weeks.checked_mul(SECS_PER_WEEK))) - } - - /// Makes a new `TimeDelta` with the given number of days. - /// - /// Equivalent to `TimeDelta::seconds(days * 24 * 60 * 60)` with overflow - /// checks. - /// - /// # Panics - /// - /// Panics when the `TimeDelta` would be out of bounds. - #[inline] - #[must_use] - #[track_caller] - pub const fn days(days: i64) -> TimeDelta { - expect(TimeDelta::try_days(days), "TimeDelta::days out of bounds") - } - - /// Makes a new `TimeDelta` with the given number of days. - /// - /// Equivalent to `TimeDelta::try_seconds(days * 24 * 60 * 60)` with overflow - /// checks. - /// - /// # Errors - /// - /// Returns `None` when the `TimeDelta` would be out of bounds. - #[inline] - pub const fn try_days(days: i64) -> Option { - TimeDelta::try_seconds(try_opt!(days.checked_mul(SECS_PER_DAY))) - } - - /// Makes a new `TimeDelta` with the given number of hours. - /// - /// Equivalent to `TimeDelta::seconds(hours * 60 * 60)` with overflow checks. - /// - /// # Panics - /// - /// Panics when the `TimeDelta` would be out of bounds. - #[inline] - #[must_use] - #[track_caller] - pub const fn hours(hours: i64) -> TimeDelta { - expect(TimeDelta::try_hours(hours), "TimeDelta::hours out of bounds") - } - - /// Makes a new `TimeDelta` with the given number of hours. - /// - /// Equivalent to `TimeDelta::try_seconds(hours * 60 * 60)` with overflow checks. - /// - /// # Errors - /// - /// Returns `None` when the `TimeDelta` would be out of bounds. - #[inline] - pub const fn try_hours(hours: i64) -> Option { - TimeDelta::try_seconds(try_opt!(hours.checked_mul(SECS_PER_HOUR))) - } - - /// Makes a new `TimeDelta` with the given number of minutes. - /// - /// Equivalent to `TimeDelta::seconds(minutes * 60)` with overflow checks. - /// - /// # Panics - /// - /// Panics when the `TimeDelta` would be out of bounds. - #[inline] - #[must_use] - #[track_caller] - pub const fn minutes(minutes: i64) -> TimeDelta { - expect(TimeDelta::try_minutes(minutes), "TimeDelta::minutes out of bounds") - } - - /// Makes a new `TimeDelta` with the given number of minutes. - /// - /// Equivalent to `TimeDelta::try_seconds(minutes * 60)` with overflow checks. - /// - /// # Errors - /// - /// Returns `None` when the `TimeDelta` would be out of bounds. - #[inline] - pub const fn try_minutes(minutes: i64) -> Option { - TimeDelta::try_seconds(try_opt!(minutes.checked_mul(SECS_PER_MINUTE))) - } - - /// Makes a new `TimeDelta` with the given number of seconds. - /// - /// # Panics - /// - /// Panics when `seconds` is more than `i64::MAX / 1_000` or less than `-i64::MAX / 1_000` - /// (in this context, this is the same as `i64::MIN / 1_000` due to rounding). - #[inline] - #[must_use] - #[track_caller] - pub const fn seconds(seconds: i64) -> TimeDelta { - expect(TimeDelta::try_seconds(seconds), "TimeDelta::seconds out of bounds") - } - - /// Makes a new `TimeDelta` with the given number of seconds. - /// - /// # Errors - /// - /// Returns `None` when `seconds` is more than `i64::MAX / 1_000` or less than - /// `-i64::MAX / 1_000` (in this context, this is the same as `i64::MIN / 1_000` due to - /// rounding). - #[inline] - pub const fn try_seconds(seconds: i64) -> Option { - TimeDelta::new(seconds, 0) - } - - /// Makes a new `TimeDelta` with the given number of milliseconds. - /// - /// # Panics - /// - /// Panics when the `TimeDelta` would be out of bounds, i.e. when `milliseconds` is more than - /// `i64::MAX` or less than `-i64::MAX`. Notably, this is not the same as `i64::MIN`. - #[inline] - #[track_caller] - pub const fn milliseconds(milliseconds: i64) -> TimeDelta { - expect(TimeDelta::try_milliseconds(milliseconds), "TimeDelta::milliseconds out of bounds") - } - - /// Makes a new `TimeDelta` with the given number of milliseconds. - /// - /// # Errors - /// - /// Returns `None` the `TimeDelta` would be out of bounds, i.e. when `milliseconds` is more - /// than `i64::MAX` or less than `-i64::MAX`. Notably, this is not the same as `i64::MIN`. - #[inline] - pub const fn try_milliseconds(milliseconds: i64) -> Option { - // We don't need to compare against MAX, as this function accepts an - // i64, and MAX is aligned to i64::MAX milliseconds. - if milliseconds < -i64::MAX { - return None; - } - let (secs, millis) = div_mod_floor_64(milliseconds, MILLIS_PER_SEC); - let d = TimeDelta { secs, nanos: millis as i32 * NANOS_PER_MILLI }; - Some(d) - } - - /// Makes a new `TimeDelta` with the given number of microseconds. - /// - /// The number of microseconds acceptable by this constructor is less than - /// the total number that can actually be stored in a `TimeDelta`, so it is - /// not possible to specify a value that would be out of bounds. This - /// function is therefore infallible. - #[inline] - pub const fn microseconds(microseconds: i64) -> TimeDelta { - let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC); - let nanos = micros as i32 * NANOS_PER_MICRO; - TimeDelta { secs, nanos } - } - - /// Makes a new `TimeDelta` with the given number of nanoseconds. - /// - /// The number of nanoseconds acceptable by this constructor is less than - /// the total number that can actually be stored in a `TimeDelta`, so it is - /// not possible to specify a value that would be out of bounds. This - /// function is therefore infallible. - #[inline] - pub const fn nanoseconds(nanos: i64) -> TimeDelta { - let (secs, nanos) = div_mod_floor_64(nanos, NANOS_PER_SEC as i64); - TimeDelta { secs, nanos: nanos as i32 } - } - - /// Returns the total number of whole weeks in the `TimeDelta`. - #[inline] - pub const fn num_weeks(&self) -> i64 { - self.num_days() / 7 - } - - /// Returns the total number of whole days in the `TimeDelta`. - #[inline] - pub const fn num_days(&self) -> i64 { - self.num_seconds() / SECS_PER_DAY - } - - /// Returns the total number of whole hours in the `TimeDelta`. - #[inline] - pub const fn num_hours(&self) -> i64 { - self.num_seconds() / SECS_PER_HOUR - } - - /// Returns the total number of whole minutes in the `TimeDelta`. - #[inline] - pub const fn num_minutes(&self) -> i64 { - self.num_seconds() / SECS_PER_MINUTE - } - - /// Returns the total number of whole seconds in the `TimeDelta`. - pub const fn num_seconds(&self) -> i64 { - // If secs is negative, nanos should be subtracted from the duration. - if self.secs < 0 && self.nanos > 0 { self.secs + 1 } else { self.secs } - } - - /// Returns the fractional number of seconds in the `TimeDelta`. - pub fn as_seconds_f64(self) -> f64 { - self.secs as f64 + self.nanos as f64 / NANOS_PER_SEC as f64 - } - - /// Returns the fractional number of seconds in the `TimeDelta`. - pub fn as_seconds_f32(self) -> f32 { - self.secs as f32 + self.nanos as f32 / NANOS_PER_SEC as f32 - } - - /// Returns the total number of whole milliseconds in the `TimeDelta`. - pub const fn num_milliseconds(&self) -> i64 { - // A proper TimeDelta will not overflow, because MIN and MAX are defined such - // that the range is within the bounds of an i64, from -i64::MAX through to - // +i64::MAX inclusive. Notably, i64::MIN is excluded from this range. - let secs_part = self.num_seconds() * MILLIS_PER_SEC; - let nanos_part = self.subsec_nanos() / NANOS_PER_MILLI; - secs_part + nanos_part as i64 - } - - /// Returns the number of milliseconds in the fractional part of the duration. - /// - /// This is the number of milliseconds such that - /// `subsec_millis() + num_seconds() * 1_000` is the truncated number of - /// milliseconds in the duration. - pub const fn subsec_millis(&self) -> i32 { - self.subsec_nanos() / NANOS_PER_MILLI - } - - /// Returns the total number of whole microseconds in the `TimeDelta`, - /// or `None` on overflow (exceeding 2^63 microseconds in either direction). - pub const fn num_microseconds(&self) -> Option { - let secs_part = try_opt!(self.num_seconds().checked_mul(MICROS_PER_SEC)); - let nanos_part = self.subsec_nanos() / NANOS_PER_MICRO; - secs_part.checked_add(nanos_part as i64) - } - - /// Returns the number of microseconds in the fractional part of the duration. - /// - /// This is the number of microseconds such that - /// `subsec_micros() + num_seconds() * 1_000_000` is the truncated number of - /// microseconds in the duration. - pub const fn subsec_micros(&self) -> i32 { - self.subsec_nanos() / NANOS_PER_MICRO - } - - /// Returns the total number of whole nanoseconds in the `TimeDelta`, - /// or `None` on overflow (exceeding 2^63 nanoseconds in either direction). - pub const fn num_nanoseconds(&self) -> Option { - let secs_part = try_opt!(self.num_seconds().checked_mul(NANOS_PER_SEC as i64)); - let nanos_part = self.subsec_nanos(); - secs_part.checked_add(nanos_part as i64) - } - - /// Returns the number of nanoseconds in the fractional part of the duration. - /// - /// This is the number of nanoseconds such that - /// `subsec_nanos() + num_seconds() * 1_000_000_000` is the total number of - /// nanoseconds in the `TimeDelta`. - pub const fn subsec_nanos(&self) -> i32 { - if self.secs < 0 && self.nanos > 0 { self.nanos - NANOS_PER_SEC } else { self.nanos } - } - - /// Add two `TimeDelta`s, returning `None` if overflow occurred. - #[must_use] - pub const fn checked_add(&self, rhs: &TimeDelta) -> Option { - // No overflow checks here because we stay comfortably within the range of an `i64`. - // Range checks happen in `TimeDelta::new`. - let mut secs = self.secs + rhs.secs; - let mut nanos = self.nanos + rhs.nanos; - if nanos >= NANOS_PER_SEC { - nanos -= NANOS_PER_SEC; - secs += 1; - } - TimeDelta::new(secs, nanos as u32) - } - - /// Subtract two `TimeDelta`s, returning `None` if overflow occurred. - #[must_use] - pub const fn checked_sub(&self, rhs: &TimeDelta) -> Option { - // No overflow checks here because we stay comfortably within the range of an `i64`. - // Range checks happen in `TimeDelta::new`. - let mut secs = self.secs - rhs.secs; - let mut nanos = self.nanos - rhs.nanos; - if nanos < 0 { - nanos += NANOS_PER_SEC; - secs -= 1; - } - TimeDelta::new(secs, nanos as u32) - } - - /// Multiply a `TimeDelta` with a i32, returning `None` if overflow occurred. - #[must_use] - pub const fn checked_mul(&self, rhs: i32) -> Option { - // Multiply nanoseconds as i64, because it cannot overflow that way. - let total_nanos = self.nanos as i64 * rhs as i64; - let (extra_secs, nanos) = div_mod_floor_64(total_nanos, NANOS_PER_SEC as i64); - // Multiply seconds as i128 to prevent overflow - let secs: i128 = self.secs as i128 * rhs as i128 + extra_secs as i128; - if secs <= i64::MIN as i128 || secs >= i64::MAX as i128 { - return None; - }; - Some(TimeDelta { secs: secs as i64, nanos: nanos as i32 }) - } - - /// Divide a `TimeDelta` with a i32, returning `None` if dividing by 0. - #[must_use] - pub const fn checked_div(&self, rhs: i32) -> Option { - if rhs == 0 { - return None; - } - let secs = self.secs / rhs as i64; - let carry = self.secs % rhs as i64; - let extra_nanos = carry * NANOS_PER_SEC as i64 / rhs as i64; - let nanos = self.nanos / rhs + extra_nanos as i32; - - let (secs, nanos) = match nanos { - i32::MIN..=-1 => (secs - 1, nanos + NANOS_PER_SEC), - NANOS_PER_SEC..=i32::MAX => (secs + 1, nanos - NANOS_PER_SEC), - _ => (secs, nanos), - }; - - Some(TimeDelta { secs, nanos }) - } - - /// Returns the `TimeDelta` as an absolute (non-negative) value. - #[inline] - pub const fn abs(&self) -> TimeDelta { - if self.secs < 0 && self.nanos != 0 { - TimeDelta { secs: (self.secs + 1).abs(), nanos: NANOS_PER_SEC - self.nanos } - } else { - TimeDelta { secs: self.secs.abs(), nanos: self.nanos } - } - } - - /// The minimum possible `TimeDelta`: `-i64::MAX` milliseconds. - #[deprecated(since = "0.4.39", note = "Use `TimeDelta::MIN` instead")] - #[inline] - pub const fn min_value() -> TimeDelta { - MIN - } - - /// The maximum possible `TimeDelta`: `i64::MAX` milliseconds. - #[deprecated(since = "0.4.39", note = "Use `TimeDelta::MAX` instead")] - #[inline] - pub const fn max_value() -> TimeDelta { - MAX - } - - /// A `TimeDelta` where the stored seconds and nanoseconds are equal to zero. - #[inline] - pub const fn zero() -> TimeDelta { - TimeDelta { secs: 0, nanos: 0 } - } - - /// Returns `true` if the `TimeDelta` equals `TimeDelta::zero()`. - #[inline] - pub const fn is_zero(&self) -> bool { - self.secs == 0 && self.nanos == 0 - } - - /// Creates a `TimeDelta` object from `std::time::Duration` - /// - /// This function errors when original duration is larger than the maximum - /// value supported for this type. - pub const fn from_std(duration: Duration) -> Result { - // We need to check secs as u64 before coercing to i64 - if duration.as_secs() > MAX.secs as u64 { - return Err(OutOfRangeError(())); - } - match TimeDelta::new(duration.as_secs() as i64, duration.subsec_nanos()) { - Some(d) => Ok(d), - None => Err(OutOfRangeError(())), - } - } - - /// Creates a `std::time::Duration` object from a `TimeDelta`. - /// - /// This function errors when duration is less than zero. As standard - /// library implementation is limited to non-negative values. - pub const fn to_std(&self) -> Result { - if self.secs < 0 { - return Err(OutOfRangeError(())); - } - Ok(Duration::new(self.secs as u64, self.nanos as u32)) - } - - /// This duplicates `Neg::neg` because trait methods can't be const yet. - pub(crate) const fn neg(self) -> TimeDelta { - let (secs_diff, nanos) = match self.nanos { - 0 => (0, 0), - nanos => (1, NANOS_PER_SEC - nanos), - }; - TimeDelta { secs: -self.secs - secs_diff, nanos } - } - - /// The minimum possible `TimeDelta`: `-i64::MAX` milliseconds. - pub const MIN: Self = MIN; - - /// The maximum possible `TimeDelta`: `i64::MAX` milliseconds. - pub const MAX: Self = MAX; -} - -impl Neg for TimeDelta { - type Output = TimeDelta; - - #[inline] - #[track_caller] - fn neg(self) -> TimeDelta { - let (secs_diff, nanos) = match self.nanos { - 0 => (0, 0), - nanos => (1, NANOS_PER_SEC - nanos), - }; - TimeDelta { secs: -self.secs - secs_diff, nanos } - } -} - -impl Add for TimeDelta { - type Output = TimeDelta; - - #[track_caller] - fn add(self, rhs: TimeDelta) -> TimeDelta { - self.checked_add(&rhs).expect("`TimeDelta + TimeDelta` overflowed") - } -} - -impl Sub for TimeDelta { - type Output = TimeDelta; - - #[track_caller] - fn sub(self, rhs: TimeDelta) -> TimeDelta { - self.checked_sub(&rhs).expect("`TimeDelta - TimeDelta` overflowed") - } -} - -impl AddAssign for TimeDelta { - #[track_caller] - fn add_assign(&mut self, rhs: TimeDelta) { - let new = self.checked_add(&rhs).expect("`TimeDelta + TimeDelta` overflowed"); - *self = new; - } -} - -impl SubAssign for TimeDelta { - #[track_caller] - fn sub_assign(&mut self, rhs: TimeDelta) { - let new = self.checked_sub(&rhs).expect("`TimeDelta - TimeDelta` overflowed"); - *self = new; - } -} - -impl Mul for TimeDelta { - type Output = TimeDelta; - - #[track_caller] - fn mul(self, rhs: i32) -> TimeDelta { - self.checked_mul(rhs).expect("`TimeDelta * i32` overflowed") - } -} - -impl Div for TimeDelta { - type Output = TimeDelta; - - #[track_caller] - fn div(self, rhs: i32) -> TimeDelta { - self.checked_div(rhs).expect("`i32` is zero") - } -} - -impl<'a> core::iter::Sum<&'a TimeDelta> for TimeDelta { - fn sum>(iter: I) -> TimeDelta { - iter.fold(TimeDelta::zero(), |acc, x| acc + *x) - } -} - -impl core::iter::Sum for TimeDelta { - fn sum>(iter: I) -> TimeDelta { - iter.fold(TimeDelta::zero(), |acc, x| acc + x) - } -} - -impl fmt::Display for TimeDelta { - /// Format a `TimeDelta` using the [ISO 8601] format - /// - /// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601#Durations - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // technically speaking, negative duration is not valid ISO 8601, - // but we need to print it anyway. - let (abs, sign) = if self.secs < 0 { (-*self, "-") } else { (*self, "") }; - - write!(f, "{sign}P")?; - // Plenty of ways to encode an empty string. `P0D` is short and not too strange. - if abs.secs == 0 && abs.nanos == 0 { - return f.write_str("0D"); - } - - f.write_fmt(format_args!("T{}", abs.secs))?; - - if abs.nanos > 0 { - // Count the number of significant digits, while removing all trailing zero's. - let mut figures = 9usize; - let mut fraction_digits = abs.nanos; - loop { - let div = fraction_digits / 10; - let last_digit = fraction_digits % 10; - if last_digit != 0 { - break; - } - fraction_digits = div; - figures -= 1; - } - f.write_fmt(format_args!(".{fraction_digits:0figures$}"))?; - } - f.write_str("S")?; - Ok(()) - } -} - -/// Represents error when converting `TimeDelta` to/from a standard library -/// implementation -/// -/// The `std::time::Duration` supports a range from zero to `u64::MAX` -/// *seconds*, while this module supports signed range of up to -/// `i64::MAX` of *milliseconds*. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct OutOfRangeError(()); - -impl fmt::Display for OutOfRangeError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Source duration value is out of range for the target type") - } -} - -#[cfg(any(feature = "std", feature = "core-error"))] -impl Error for OutOfRangeError { - #[allow(deprecated)] - fn description(&self) -> &str { - "out of range error" - } -} - -#[inline] -const fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) { - (this.div_euclid(other), this.rem_euclid(other)) -} - -#[cfg(all(feature = "arbitrary", feature = "std"))] -impl arbitrary::Arbitrary<'_> for TimeDelta { - fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result { - const MIN_SECS: i64 = -i64::MAX / MILLIS_PER_SEC - 1; - const MAX_SECS: i64 = i64::MAX / MILLIS_PER_SEC; - - let secs: i64 = u.int_in_range(MIN_SECS..=MAX_SECS)?; - let nanos: i32 = u.int_in_range(0..=(NANOS_PER_SEC - 1))?; - let duration = TimeDelta { secs, nanos }; - - if duration < MIN || duration > MAX { - Err(arbitrary::Error::IncorrectFormat) - } else { - Ok(duration) - } - } -} - -#[cfg(feature = "serde")] -mod serde { - use super::TimeDelta; - use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error}; - - impl Serialize for TimeDelta { - fn serialize(&self, serializer: S) -> Result { - <(i64, i32) as Serialize>::serialize(&(self.secs, self.nanos), serializer) - } - } - - impl<'de> Deserialize<'de> for TimeDelta { - fn deserialize>(deserializer: D) -> Result { - let (secs, nanos) = <(i64, i32) as Deserialize>::deserialize(deserializer)?; - TimeDelta::new(secs, nanos as u32).ok_or(Error::custom("TimeDelta out of bounds")) - } - } - - #[cfg(test)] - mod tests { - use super::{super::MAX, TimeDelta}; - - #[test] - fn test_serde() { - let duration = TimeDelta::new(123, 456).unwrap(); - assert_eq!( - serde_json::from_value::(serde_json::to_value(duration).unwrap()) - .unwrap(), - duration - ); - } - - #[test] - #[should_panic(expected = "TimeDelta out of bounds")] - fn test_serde_oob_panic() { - let _ = - serde_json::from_value::(serde_json::json!([MAX.secs + 1, 0])).unwrap(); - } - } -} - -#[cfg(test)] -mod tests { - use super::OutOfRangeError; - use super::{MAX, MIN, TimeDelta}; - use crate::expect; - use core::time::Duration; - - #[test] - fn test_duration() { - let days = |d| TimeDelta::try_days(d).unwrap(); - let seconds = |s| TimeDelta::try_seconds(s).unwrap(); - - assert!(seconds(1) != TimeDelta::zero()); - assert_eq!(seconds(1) + seconds(2), seconds(3)); - assert_eq!(seconds(86_399) + seconds(4), days(1) + seconds(3)); - assert_eq!(days(10) - seconds(1000), seconds(863_000)); - assert_eq!(days(10) - seconds(1_000_000), seconds(-136_000)); - assert_eq!( - days(2) + seconds(86_399) + TimeDelta::nanoseconds(1_234_567_890), - days(3) + TimeDelta::nanoseconds(234_567_890) - ); - assert_eq!(-days(3), days(-3)); - assert_eq!(-(days(3) + seconds(70)), days(-4) + seconds(86_400 - 70)); - - let mut d = TimeDelta::default(); - d += TimeDelta::try_minutes(1).unwrap(); - d -= seconds(30); - assert_eq!(d, seconds(30)); - } - - #[test] - fn test_duration_num_days() { - assert_eq!(TimeDelta::zero().num_days(), 0); - assert_eq!(TimeDelta::try_days(1).unwrap().num_days(), 1); - assert_eq!(TimeDelta::try_days(-1).unwrap().num_days(), -1); - assert_eq!(TimeDelta::try_seconds(86_399).unwrap().num_days(), 0); - assert_eq!(TimeDelta::try_seconds(86_401).unwrap().num_days(), 1); - assert_eq!(TimeDelta::try_seconds(-86_399).unwrap().num_days(), 0); - assert_eq!(TimeDelta::try_seconds(-86_401).unwrap().num_days(), -1); - assert_eq!(TimeDelta::try_days(i32::MAX as i64).unwrap().num_days(), i32::MAX as i64); - assert_eq!(TimeDelta::try_days(i32::MIN as i64).unwrap().num_days(), i32::MIN as i64); - } - - #[test] - fn test_duration_num_seconds() { - assert_eq!(TimeDelta::zero().num_seconds(), 0); - assert_eq!(TimeDelta::try_seconds(1).unwrap().num_seconds(), 1); - assert_eq!(TimeDelta::try_seconds(-1).unwrap().num_seconds(), -1); - assert_eq!(TimeDelta::try_milliseconds(999).unwrap().num_seconds(), 0); - assert_eq!(TimeDelta::try_milliseconds(1001).unwrap().num_seconds(), 1); - assert_eq!(TimeDelta::try_milliseconds(-999).unwrap().num_seconds(), 0); - assert_eq!(TimeDelta::try_milliseconds(-1001).unwrap().num_seconds(), -1); - } - - #[test] - fn test_duration_seconds_max_allowed() { - let duration = TimeDelta::try_seconds(i64::MAX / 1_000).unwrap(); - assert_eq!(duration.num_seconds(), i64::MAX / 1_000); - assert_eq!( - duration.secs as i128 * 1_000_000_000 + duration.nanos as i128, - i64::MAX as i128 / 1_000 * 1_000_000_000 - ); - } - - #[test] - fn test_duration_seconds_max_overflow() { - assert!(TimeDelta::try_seconds(i64::MAX / 1_000 + 1).is_none()); - } - - #[test] - #[should_panic(expected = "TimeDelta::seconds out of bounds")] - fn test_duration_seconds_max_overflow_panic() { - let _ = TimeDelta::seconds(i64::MAX / 1_000 + 1); - } - - #[test] - fn test_duration_seconds_min_allowed() { - let duration = TimeDelta::try_seconds(i64::MIN / 1_000).unwrap(); // Same as -i64::MAX / 1_000 due to rounding - assert_eq!(duration.num_seconds(), i64::MIN / 1_000); // Same as -i64::MAX / 1_000 due to rounding - assert_eq!( - duration.secs as i128 * 1_000_000_000 + duration.nanos as i128, - -i64::MAX as i128 / 1_000 * 1_000_000_000 - ); - } - - #[test] - fn test_duration_seconds_min_underflow() { - assert!(TimeDelta::try_seconds(-i64::MAX / 1_000 - 1).is_none()); - } - - #[test] - #[should_panic(expected = "TimeDelta::seconds out of bounds")] - fn test_duration_seconds_min_underflow_panic() { - let _ = TimeDelta::seconds(-i64::MAX / 1_000 - 1); - } - - #[test] - fn test_duration_as_seconds_f64() { - assert_eq!(TimeDelta::seconds(1).as_seconds_f64(), 1.0); - assert_eq!(TimeDelta::seconds(-1).as_seconds_f64(), -1.0); - assert_eq!(TimeDelta::seconds(100).as_seconds_f64(), 100.0); - assert_eq!(TimeDelta::seconds(-100).as_seconds_f64(), -100.0); - - assert_eq!(TimeDelta::milliseconds(500).as_seconds_f64(), 0.5); - assert_eq!(TimeDelta::milliseconds(-500).as_seconds_f64(), -0.5); - assert_eq!(TimeDelta::milliseconds(1_500).as_seconds_f64(), 1.5); - assert_eq!(TimeDelta::milliseconds(-1_500).as_seconds_f64(), -1.5); - } - - #[test] - fn test_duration_as_seconds_f32() { - assert_eq!(TimeDelta::seconds(1).as_seconds_f32(), 1.0); - assert_eq!(TimeDelta::seconds(-1).as_seconds_f32(), -1.0); - assert_eq!(TimeDelta::seconds(100).as_seconds_f32(), 100.0); - assert_eq!(TimeDelta::seconds(-100).as_seconds_f32(), -100.0); - - assert_eq!(TimeDelta::milliseconds(500).as_seconds_f32(), 0.5); - assert_eq!(TimeDelta::milliseconds(-500).as_seconds_f32(), -0.5); - assert_eq!(TimeDelta::milliseconds(1_500).as_seconds_f32(), 1.5); - assert_eq!(TimeDelta::milliseconds(-1_500).as_seconds_f32(), -1.5); - } - - #[test] - fn test_duration_subsec_nanos() { - assert_eq!(TimeDelta::zero().subsec_nanos(), 0); - assert_eq!(TimeDelta::nanoseconds(1).subsec_nanos(), 1); - assert_eq!(TimeDelta::nanoseconds(-1).subsec_nanos(), -1); - assert_eq!(TimeDelta::seconds(1).subsec_nanos(), 0); - assert_eq!(TimeDelta::nanoseconds(1_000_000_001).subsec_nanos(), 1); - } - - #[test] - fn test_duration_subsec_micros() { - assert_eq!(TimeDelta::zero().subsec_micros(), 0); - assert_eq!(TimeDelta::microseconds(1).subsec_micros(), 1); - assert_eq!(TimeDelta::microseconds(-1).subsec_micros(), -1); - assert_eq!(TimeDelta::seconds(1).subsec_micros(), 0); - assert_eq!(TimeDelta::microseconds(1_000_001).subsec_micros(), 1); - assert_eq!(TimeDelta::nanoseconds(1_000_001_999).subsec_micros(), 1); - } - - #[test] - fn test_duration_subsec_millis() { - assert_eq!(TimeDelta::zero().subsec_millis(), 0); - assert_eq!(TimeDelta::milliseconds(1).subsec_millis(), 1); - assert_eq!(TimeDelta::milliseconds(-1).subsec_millis(), -1); - assert_eq!(TimeDelta::seconds(1).subsec_millis(), 0); - assert_eq!(TimeDelta::milliseconds(1_001).subsec_millis(), 1); - assert_eq!(TimeDelta::microseconds(1_001_999).subsec_millis(), 1); - } - - #[test] - fn test_duration_num_milliseconds() { - assert_eq!(TimeDelta::zero().num_milliseconds(), 0); - assert_eq!(TimeDelta::try_milliseconds(1).unwrap().num_milliseconds(), 1); - assert_eq!(TimeDelta::try_milliseconds(-1).unwrap().num_milliseconds(), -1); - assert_eq!(TimeDelta::microseconds(999).num_milliseconds(), 0); - assert_eq!(TimeDelta::microseconds(1001).num_milliseconds(), 1); - assert_eq!(TimeDelta::microseconds(-999).num_milliseconds(), 0); - assert_eq!(TimeDelta::microseconds(-1001).num_milliseconds(), -1); - } - - #[test] - fn test_duration_milliseconds_max_allowed() { - // The maximum number of milliseconds acceptable through the constructor is - // equal to the number that can be stored in a TimeDelta. - let duration = TimeDelta::try_milliseconds(i64::MAX).unwrap(); - assert_eq!(duration.num_milliseconds(), i64::MAX); - assert_eq!( - duration.secs as i128 * 1_000_000_000 + duration.nanos as i128, - i64::MAX as i128 * 1_000_000 - ); - } - - #[test] - fn test_duration_milliseconds_max_overflow() { - // Here we ensure that trying to add one millisecond to the maximum storable - // value will fail. - assert!( - TimeDelta::try_milliseconds(i64::MAX) - .unwrap() - .checked_add(&TimeDelta::try_milliseconds(1).unwrap()) - .is_none() - ); - } - - #[test] - fn test_duration_milliseconds_min_allowed() { - // The minimum number of milliseconds acceptable through the constructor is - // not equal to the number that can be stored in a TimeDelta - there is a - // difference of one (i64::MIN vs -i64::MAX). - let duration = TimeDelta::try_milliseconds(-i64::MAX).unwrap(); - assert_eq!(duration.num_milliseconds(), -i64::MAX); - assert_eq!( - duration.secs as i128 * 1_000_000_000 + duration.nanos as i128, - -i64::MAX as i128 * 1_000_000 - ); - } - - #[test] - fn test_duration_milliseconds_min_underflow() { - // Here we ensure that trying to subtract one millisecond from the minimum - // storable value will fail. - assert!( - TimeDelta::try_milliseconds(-i64::MAX) - .unwrap() - .checked_sub(&TimeDelta::try_milliseconds(1).unwrap()) - .is_none() - ); - } - - #[test] - #[should_panic(expected = "TimeDelta::milliseconds out of bounds")] - fn test_duration_milliseconds_min_underflow_panic() { - // Here we ensure that trying to create a value one millisecond below the - // minimum storable value will fail. This test is necessary because the - // storable range is -i64::MAX, but the constructor type of i64 will allow - // i64::MIN, which is one value below. - let _ = TimeDelta::milliseconds(i64::MIN); // Same as -i64::MAX - 1 - } - - #[test] - fn test_duration_num_microseconds() { - assert_eq!(TimeDelta::zero().num_microseconds(), Some(0)); - assert_eq!(TimeDelta::microseconds(1).num_microseconds(), Some(1)); - assert_eq!(TimeDelta::microseconds(-1).num_microseconds(), Some(-1)); - assert_eq!(TimeDelta::nanoseconds(999).num_microseconds(), Some(0)); - assert_eq!(TimeDelta::nanoseconds(1001).num_microseconds(), Some(1)); - assert_eq!(TimeDelta::nanoseconds(-999).num_microseconds(), Some(0)); - assert_eq!(TimeDelta::nanoseconds(-1001).num_microseconds(), Some(-1)); - - // overflow checks - const MICROS_PER_DAY: i64 = 86_400_000_000; - assert_eq!( - TimeDelta::try_days(i64::MAX / MICROS_PER_DAY).unwrap().num_microseconds(), - Some(i64::MAX / MICROS_PER_DAY * MICROS_PER_DAY) - ); - assert_eq!( - TimeDelta::try_days(-i64::MAX / MICROS_PER_DAY).unwrap().num_microseconds(), - Some(-i64::MAX / MICROS_PER_DAY * MICROS_PER_DAY) - ); - assert_eq!( - TimeDelta::try_days(i64::MAX / MICROS_PER_DAY + 1).unwrap().num_microseconds(), - None - ); - assert_eq!( - TimeDelta::try_days(-i64::MAX / MICROS_PER_DAY - 1).unwrap().num_microseconds(), - None - ); - } - #[test] - fn test_duration_microseconds_max_allowed() { - // The number of microseconds acceptable through the constructor is far - // fewer than the number that can actually be stored in a TimeDelta, so this - // is not a particular insightful test. - let duration = TimeDelta::microseconds(i64::MAX); - assert_eq!(duration.num_microseconds(), Some(i64::MAX)); - assert_eq!( - duration.secs as i128 * 1_000_000_000 + duration.nanos as i128, - i64::MAX as i128 * 1_000 - ); - // Here we create a TimeDelta with the maximum possible number of - // microseconds by creating a TimeDelta with the maximum number of - // milliseconds and then checking that the number of microseconds matches - // the storage limit. - let duration = TimeDelta::try_milliseconds(i64::MAX).unwrap(); - assert!(duration.num_microseconds().is_none()); - assert_eq!( - duration.secs as i128 * 1_000_000_000 + duration.nanos as i128, - i64::MAX as i128 * 1_000_000 - ); - } - #[test] - fn test_duration_microseconds_max_overflow() { - // This test establishes that a TimeDelta can store more microseconds than - // are representable through the return of duration.num_microseconds(). - let duration = TimeDelta::microseconds(i64::MAX) + TimeDelta::microseconds(1); - assert!(duration.num_microseconds().is_none()); - assert_eq!( - duration.secs as i128 * 1_000_000_000 + duration.nanos as i128, - (i64::MAX as i128 + 1) * 1_000 - ); - // Here we ensure that trying to add one microsecond to the maximum storable - // value will fail. - assert!( - TimeDelta::try_milliseconds(i64::MAX) - .unwrap() - .checked_add(&TimeDelta::microseconds(1)) - .is_none() - ); - } - #[test] - fn test_duration_microseconds_min_allowed() { - // The number of microseconds acceptable through the constructor is far - // fewer than the number that can actually be stored in a TimeDelta, so this - // is not a particular insightful test. - let duration = TimeDelta::microseconds(i64::MIN); - assert_eq!(duration.num_microseconds(), Some(i64::MIN)); - assert_eq!( - duration.secs as i128 * 1_000_000_000 + duration.nanos as i128, - i64::MIN as i128 * 1_000 - ); - // Here we create a TimeDelta with the minimum possible number of - // microseconds by creating a TimeDelta with the minimum number of - // milliseconds and then checking that the number of microseconds matches - // the storage limit. - let duration = TimeDelta::try_milliseconds(-i64::MAX).unwrap(); - assert!(duration.num_microseconds().is_none()); - assert_eq!( - duration.secs as i128 * 1_000_000_000 + duration.nanos as i128, - -i64::MAX as i128 * 1_000_000 - ); - } - #[test] - fn test_duration_microseconds_min_underflow() { - // This test establishes that a TimeDelta can store more microseconds than - // are representable through the return of duration.num_microseconds(). - let duration = TimeDelta::microseconds(i64::MIN) - TimeDelta::microseconds(1); - assert!(duration.num_microseconds().is_none()); - assert_eq!( - duration.secs as i128 * 1_000_000_000 + duration.nanos as i128, - (i64::MIN as i128 - 1) * 1_000 - ); - // Here we ensure that trying to subtract one microsecond from the minimum - // storable value will fail. - assert!( - TimeDelta::try_milliseconds(-i64::MAX) - .unwrap() - .checked_sub(&TimeDelta::microseconds(1)) - .is_none() - ); - } - - #[test] - fn test_duration_num_nanoseconds() { - assert_eq!(TimeDelta::zero().num_nanoseconds(), Some(0)); - assert_eq!(TimeDelta::nanoseconds(1).num_nanoseconds(), Some(1)); - assert_eq!(TimeDelta::nanoseconds(-1).num_nanoseconds(), Some(-1)); - - // overflow checks - const NANOS_PER_DAY: i64 = 86_400_000_000_000; - assert_eq!( - TimeDelta::try_days(i64::MAX / NANOS_PER_DAY).unwrap().num_nanoseconds(), - Some(i64::MAX / NANOS_PER_DAY * NANOS_PER_DAY) - ); - assert_eq!( - TimeDelta::try_days(-i64::MAX / NANOS_PER_DAY).unwrap().num_nanoseconds(), - Some(-i64::MAX / NANOS_PER_DAY * NANOS_PER_DAY) - ); - assert_eq!( - TimeDelta::try_days(i64::MAX / NANOS_PER_DAY + 1).unwrap().num_nanoseconds(), - None - ); - assert_eq!( - TimeDelta::try_days(-i64::MAX / NANOS_PER_DAY - 1).unwrap().num_nanoseconds(), - None - ); - } - #[test] - fn test_duration_nanoseconds_max_allowed() { - // The number of nanoseconds acceptable through the constructor is far fewer - // than the number that can actually be stored in a TimeDelta, so this is not - // a particular insightful test. - let duration = TimeDelta::nanoseconds(i64::MAX); - assert_eq!(duration.num_nanoseconds(), Some(i64::MAX)); - assert_eq!( - duration.secs as i128 * 1_000_000_000 + duration.nanos as i128, - i64::MAX as i128 - ); - // Here we create a TimeDelta with the maximum possible number of nanoseconds - // by creating a TimeDelta with the maximum number of milliseconds and then - // checking that the number of nanoseconds matches the storage limit. - let duration = TimeDelta::try_milliseconds(i64::MAX).unwrap(); - assert!(duration.num_nanoseconds().is_none()); - assert_eq!( - duration.secs as i128 * 1_000_000_000 + duration.nanos as i128, - i64::MAX as i128 * 1_000_000 - ); - } - - #[test] - fn test_duration_nanoseconds_max_overflow() { - // This test establishes that a TimeDelta can store more nanoseconds than are - // representable through the return of duration.num_nanoseconds(). - let duration = TimeDelta::nanoseconds(i64::MAX) + TimeDelta::nanoseconds(1); - assert!(duration.num_nanoseconds().is_none()); - assert_eq!( - duration.secs as i128 * 1_000_000_000 + duration.nanos as i128, - i64::MAX as i128 + 1 - ); - // Here we ensure that trying to add one nanosecond to the maximum storable - // value will fail. - assert!( - TimeDelta::try_milliseconds(i64::MAX) - .unwrap() - .checked_add(&TimeDelta::nanoseconds(1)) - .is_none() - ); - } - - #[test] - fn test_duration_nanoseconds_min_allowed() { - // The number of nanoseconds acceptable through the constructor is far fewer - // than the number that can actually be stored in a TimeDelta, so this is not - // a particular insightful test. - let duration = TimeDelta::nanoseconds(i64::MIN); - assert_eq!(duration.num_nanoseconds(), Some(i64::MIN)); - assert_eq!( - duration.secs as i128 * 1_000_000_000 + duration.nanos as i128, - i64::MIN as i128 - ); - // Here we create a TimeDelta with the minimum possible number of nanoseconds - // by creating a TimeDelta with the minimum number of milliseconds and then - // checking that the number of nanoseconds matches the storage limit. - let duration = TimeDelta::try_milliseconds(-i64::MAX).unwrap(); - assert!(duration.num_nanoseconds().is_none()); - assert_eq!( - duration.secs as i128 * 1_000_000_000 + duration.nanos as i128, - -i64::MAX as i128 * 1_000_000 - ); - } - - #[test] - fn test_duration_nanoseconds_min_underflow() { - // This test establishes that a TimeDelta can store more nanoseconds than are - // representable through the return of duration.num_nanoseconds(). - let duration = TimeDelta::nanoseconds(i64::MIN) - TimeDelta::nanoseconds(1); - assert!(duration.num_nanoseconds().is_none()); - assert_eq!( - duration.secs as i128 * 1_000_000_000 + duration.nanos as i128, - i64::MIN as i128 - 1 - ); - // Here we ensure that trying to subtract one nanosecond from the minimum - // storable value will fail. - assert!( - TimeDelta::try_milliseconds(-i64::MAX) - .unwrap() - .checked_sub(&TimeDelta::nanoseconds(1)) - .is_none() - ); - } - - #[test] - fn test_max() { - assert_eq!( - MAX.secs as i128 * 1_000_000_000 + MAX.nanos as i128, - i64::MAX as i128 * 1_000_000 - ); - assert_eq!(MAX, TimeDelta::try_milliseconds(i64::MAX).unwrap()); - assert_eq!(MAX.num_milliseconds(), i64::MAX); - assert_eq!(MAX.num_microseconds(), None); - assert_eq!(MAX.num_nanoseconds(), None); - } - - #[test] - fn test_min() { - assert_eq!( - MIN.secs as i128 * 1_000_000_000 + MIN.nanos as i128, - -i64::MAX as i128 * 1_000_000 - ); - assert_eq!(MIN, TimeDelta::try_milliseconds(-i64::MAX).unwrap()); - assert_eq!(MIN.num_milliseconds(), -i64::MAX); - assert_eq!(MIN.num_microseconds(), None); - assert_eq!(MIN.num_nanoseconds(), None); - } - - #[test] - fn test_duration_ord() { - let milliseconds = |ms| TimeDelta::try_milliseconds(ms).unwrap(); - - assert!(milliseconds(1) < milliseconds(2)); - assert!(milliseconds(2) > milliseconds(1)); - assert!(milliseconds(-1) > milliseconds(-2)); - assert!(milliseconds(-2) < milliseconds(-1)); - assert!(milliseconds(-1) < milliseconds(1)); - assert!(milliseconds(1) > milliseconds(-1)); - assert!(milliseconds(0) < milliseconds(1)); - assert!(milliseconds(0) > milliseconds(-1)); - assert!(milliseconds(1_001) < milliseconds(1_002)); - assert!(milliseconds(-1_001) > milliseconds(-1_002)); - assert!(TimeDelta::nanoseconds(1_234_567_890) < TimeDelta::nanoseconds(1_234_567_891)); - assert!(TimeDelta::nanoseconds(-1_234_567_890) > TimeDelta::nanoseconds(-1_234_567_891)); - assert!(milliseconds(i64::MAX) > milliseconds(i64::MAX - 1)); - assert!(milliseconds(-i64::MAX) < milliseconds(-i64::MAX + 1)); - } - - #[test] - fn test_duration_checked_ops() { - let milliseconds = |ms| TimeDelta::try_milliseconds(ms).unwrap(); - let seconds = |s| TimeDelta::try_seconds(s).unwrap(); - - assert_eq!( - milliseconds(i64::MAX).checked_add(&milliseconds(0)), - Some(milliseconds(i64::MAX)) - ); - assert_eq!( - milliseconds(i64::MAX - 1).checked_add(&TimeDelta::microseconds(999)), - Some(milliseconds(i64::MAX - 2) + TimeDelta::microseconds(1999)) - ); - assert!(milliseconds(i64::MAX).checked_add(&TimeDelta::microseconds(1000)).is_none()); - assert!(milliseconds(i64::MAX).checked_add(&TimeDelta::nanoseconds(1)).is_none()); - - assert_eq!( - milliseconds(-i64::MAX).checked_sub(&milliseconds(0)), - Some(milliseconds(-i64::MAX)) - ); - assert_eq!( - milliseconds(-i64::MAX + 1).checked_sub(&TimeDelta::microseconds(999)), - Some(milliseconds(-i64::MAX + 2) - TimeDelta::microseconds(1999)) - ); - assert!(milliseconds(-i64::MAX).checked_sub(&milliseconds(1)).is_none()); - assert!(milliseconds(-i64::MAX).checked_sub(&TimeDelta::nanoseconds(1)).is_none()); - - assert!(seconds(i64::MAX / 1000).checked_mul(2000).is_none()); - assert!(seconds(i64::MIN / 1000).checked_mul(2000).is_none()); - assert!(seconds(1).checked_div(0).is_none()); - } - - #[test] - fn test_duration_abs() { - let milliseconds = |ms| TimeDelta::try_milliseconds(ms).unwrap(); - - assert_eq!(milliseconds(1300).abs(), milliseconds(1300)); - assert_eq!(milliseconds(1000).abs(), milliseconds(1000)); - assert_eq!(milliseconds(300).abs(), milliseconds(300)); - assert_eq!(milliseconds(0).abs(), milliseconds(0)); - assert_eq!(milliseconds(-300).abs(), milliseconds(300)); - assert_eq!(milliseconds(-700).abs(), milliseconds(700)); - assert_eq!(milliseconds(-1000).abs(), milliseconds(1000)); - assert_eq!(milliseconds(-1300).abs(), milliseconds(1300)); - assert_eq!(milliseconds(-1700).abs(), milliseconds(1700)); - assert_eq!(milliseconds(-i64::MAX).abs(), milliseconds(i64::MAX)); - } - - #[test] - #[allow(clippy::erasing_op)] - fn test_duration_mul() { - assert_eq!(TimeDelta::zero() * i32::MAX, TimeDelta::zero()); - assert_eq!(TimeDelta::zero() * i32::MIN, TimeDelta::zero()); - assert_eq!(TimeDelta::nanoseconds(1) * 0, TimeDelta::zero()); - assert_eq!(TimeDelta::nanoseconds(1) * 1, TimeDelta::nanoseconds(1)); - assert_eq!(TimeDelta::nanoseconds(1) * 1_000_000_000, TimeDelta::try_seconds(1).unwrap()); - assert_eq!(TimeDelta::nanoseconds(1) * -1_000_000_000, -TimeDelta::try_seconds(1).unwrap()); - assert_eq!(-TimeDelta::nanoseconds(1) * 1_000_000_000, -TimeDelta::try_seconds(1).unwrap()); - assert_eq!( - TimeDelta::nanoseconds(30) * 333_333_333, - TimeDelta::try_seconds(10).unwrap() - TimeDelta::nanoseconds(10) - ); - assert_eq!( - (TimeDelta::nanoseconds(1) - + TimeDelta::try_seconds(1).unwrap() - + TimeDelta::try_days(1).unwrap()) - * 3, - TimeDelta::nanoseconds(3) - + TimeDelta::try_seconds(3).unwrap() - + TimeDelta::try_days(3).unwrap() - ); - assert_eq!( - TimeDelta::try_milliseconds(1500).unwrap() * -2, - TimeDelta::try_seconds(-3).unwrap() - ); - assert_eq!( - TimeDelta::try_milliseconds(-1500).unwrap() * 2, - TimeDelta::try_seconds(-3).unwrap() - ); - } - - #[test] - fn test_duration_div() { - assert_eq!(TimeDelta::zero() / i32::MAX, TimeDelta::zero()); - assert_eq!(TimeDelta::zero() / i32::MIN, TimeDelta::zero()); - assert_eq!(TimeDelta::nanoseconds(123_456_789) / 1, TimeDelta::nanoseconds(123_456_789)); - assert_eq!(TimeDelta::nanoseconds(123_456_789) / -1, -TimeDelta::nanoseconds(123_456_789)); - assert_eq!(-TimeDelta::nanoseconds(123_456_789) / -1, TimeDelta::nanoseconds(123_456_789)); - assert_eq!(-TimeDelta::nanoseconds(123_456_789) / 1, -TimeDelta::nanoseconds(123_456_789)); - assert_eq!(TimeDelta::try_seconds(1).unwrap() / 3, TimeDelta::nanoseconds(333_333_333)); - assert_eq!(TimeDelta::try_seconds(4).unwrap() / 3, TimeDelta::nanoseconds(1_333_333_333)); - assert_eq!( - TimeDelta::try_seconds(-1).unwrap() / 2, - TimeDelta::try_milliseconds(-500).unwrap() - ); - assert_eq!( - TimeDelta::try_seconds(1).unwrap() / -2, - TimeDelta::try_milliseconds(-500).unwrap() - ); - assert_eq!( - TimeDelta::try_seconds(-1).unwrap() / -2, - TimeDelta::try_milliseconds(500).unwrap() - ); - assert_eq!(TimeDelta::try_seconds(-4).unwrap() / 3, TimeDelta::nanoseconds(-1_333_333_333)); - assert_eq!(TimeDelta::try_seconds(-4).unwrap() / -3, TimeDelta::nanoseconds(1_333_333_333)); - } - - #[test] - fn test_duration_sum() { - let duration_list_1 = [TimeDelta::zero(), TimeDelta::try_seconds(1).unwrap()]; - let sum_1: TimeDelta = duration_list_1.iter().sum(); - assert_eq!(sum_1, TimeDelta::try_seconds(1).unwrap()); - - let duration_list_2 = [ - TimeDelta::zero(), - TimeDelta::try_seconds(1).unwrap(), - TimeDelta::try_seconds(6).unwrap(), - TimeDelta::try_seconds(10).unwrap(), - ]; - let sum_2: TimeDelta = duration_list_2.iter().sum(); - assert_eq!(sum_2, TimeDelta::try_seconds(17).unwrap()); - - let duration_arr = [ - TimeDelta::zero(), - TimeDelta::try_seconds(1).unwrap(), - TimeDelta::try_seconds(6).unwrap(), - TimeDelta::try_seconds(10).unwrap(), - ]; - let sum_3: TimeDelta = duration_arr.into_iter().sum(); - assert_eq!(sum_3, TimeDelta::try_seconds(17).unwrap()); - } - - #[test] - fn test_duration_fmt() { - assert_eq!(TimeDelta::zero().to_string(), "P0D"); - assert_eq!(TimeDelta::try_days(42).unwrap().to_string(), "PT3628800S"); - assert_eq!(TimeDelta::try_days(-42).unwrap().to_string(), "-PT3628800S"); - assert_eq!(TimeDelta::try_seconds(42).unwrap().to_string(), "PT42S"); - assert_eq!(TimeDelta::try_milliseconds(42).unwrap().to_string(), "PT0.042S"); - assert_eq!(TimeDelta::microseconds(42).to_string(), "PT0.000042S"); - assert_eq!(TimeDelta::nanoseconds(42).to_string(), "PT0.000000042S"); - assert_eq!( - (TimeDelta::try_days(7).unwrap() + TimeDelta::try_milliseconds(6543).unwrap()) - .to_string(), - "PT604806.543S" - ); - assert_eq!(TimeDelta::try_seconds(-86_401).unwrap().to_string(), "-PT86401S"); - assert_eq!(TimeDelta::nanoseconds(-1).to_string(), "-PT0.000000001S"); - - // the format specifier should have no effect on `TimeDelta` - assert_eq!( - format!( - "{:30}", - TimeDelta::try_days(1).unwrap() + TimeDelta::try_milliseconds(2345).unwrap() - ), - "PT86402.345S" - ); - } - - #[test] - fn test_to_std() { - assert_eq!(TimeDelta::try_seconds(1).unwrap().to_std(), Ok(Duration::new(1, 0))); - assert_eq!(TimeDelta::try_seconds(86_401).unwrap().to_std(), Ok(Duration::new(86_401, 0))); - assert_eq!( - TimeDelta::try_milliseconds(123).unwrap().to_std(), - Ok(Duration::new(0, 123_000_000)) - ); - assert_eq!( - TimeDelta::try_milliseconds(123_765).unwrap().to_std(), - Ok(Duration::new(123, 765_000_000)) - ); - assert_eq!(TimeDelta::nanoseconds(777).to_std(), Ok(Duration::new(0, 777))); - assert_eq!(MAX.to_std(), Ok(Duration::new(9_223_372_036_854_775, 807_000_000))); - assert_eq!(TimeDelta::try_seconds(-1).unwrap().to_std(), Err(OutOfRangeError(()))); - assert_eq!(TimeDelta::try_milliseconds(-1).unwrap().to_std(), Err(OutOfRangeError(()))); - } - - #[test] - fn test_from_std() { - assert_eq!( - Ok(TimeDelta::try_seconds(1).unwrap()), - TimeDelta::from_std(Duration::new(1, 0)) - ); - assert_eq!( - Ok(TimeDelta::try_seconds(86_401).unwrap()), - TimeDelta::from_std(Duration::new(86_401, 0)) - ); - assert_eq!( - Ok(TimeDelta::try_milliseconds(123).unwrap()), - TimeDelta::from_std(Duration::new(0, 123_000_000)) - ); - assert_eq!( - Ok(TimeDelta::try_milliseconds(123_765).unwrap()), - TimeDelta::from_std(Duration::new(123, 765_000_000)) - ); - assert_eq!(Ok(TimeDelta::nanoseconds(777)), TimeDelta::from_std(Duration::new(0, 777))); - assert_eq!(Ok(MAX), TimeDelta::from_std(Duration::new(9_223_372_036_854_775, 807_000_000))); - assert_eq!( - TimeDelta::from_std(Duration::new(9_223_372_036_854_776, 0)), - Err(OutOfRangeError(())) - ); - assert_eq!( - TimeDelta::from_std(Duration::new(9_223_372_036_854_775, 807_000_001)), - Err(OutOfRangeError(())) - ); - } - - #[test] - fn test_duration_const() { - const ONE_WEEK: TimeDelta = expect(TimeDelta::try_weeks(1), ""); - const ONE_DAY: TimeDelta = expect(TimeDelta::try_days(1), ""); - const ONE_HOUR: TimeDelta = expect(TimeDelta::try_hours(1), ""); - const ONE_MINUTE: TimeDelta = expect(TimeDelta::try_minutes(1), ""); - const ONE_SECOND: TimeDelta = expect(TimeDelta::try_seconds(1), ""); - const ONE_MILLI: TimeDelta = expect(TimeDelta::try_milliseconds(1), ""); - const ONE_MICRO: TimeDelta = TimeDelta::microseconds(1); - const ONE_NANO: TimeDelta = TimeDelta::nanoseconds(1); - let combo: TimeDelta = ONE_WEEK - + ONE_DAY - + ONE_HOUR - + ONE_MINUTE - + ONE_SECOND - + ONE_MILLI - + ONE_MICRO - + ONE_NANO; - - assert!(ONE_WEEK != TimeDelta::zero()); - assert!(ONE_DAY != TimeDelta::zero()); - assert!(ONE_HOUR != TimeDelta::zero()); - assert!(ONE_MINUTE != TimeDelta::zero()); - assert!(ONE_SECOND != TimeDelta::zero()); - assert!(ONE_MILLI != TimeDelta::zero()); - assert!(ONE_MICRO != TimeDelta::zero()); - assert!(ONE_NANO != TimeDelta::zero()); - assert_eq!( - combo, - TimeDelta::try_seconds(86400 * 7 + 86400 + 3600 + 60 + 1).unwrap() - + TimeDelta::nanoseconds(1 + 1_000 + 1_000_000) - ); - } - - #[test] - #[cfg(feature = "rkyv-validation")] - fn test_rkyv_validation() { - let duration = TimeDelta::try_seconds(1).unwrap(); - let bytes = rkyv::to_bytes::<_, 16>(&duration).unwrap(); - assert_eq!(rkyv::from_bytes::(&bytes).unwrap(), duration); - } -} diff --git a/chrono-0.4.44/src/traits.rs b/chrono-0.4.44/src/traits.rs deleted file mode 100644 index 450f0a2259..0000000000 --- a/chrono-0.4.44/src/traits.rs +++ /dev/null @@ -1,409 +0,0 @@ -use crate::{IsoWeek, Month, Weekday}; - -/// The common set of methods for date component. -/// -/// Methods such as [`year`], [`month`], [`day`] and [`weekday`] can be used to get basic -/// information about the date. -/// -/// The `with_*` methods can change the date. -/// -/// # Warning -/// -/// The `with_*` methods can be convenient to change a single component of a date, but they must be -/// used with some care. Examples to watch out for: -/// -/// - [`with_year`] changes the year component of a year-month-day value. Don't use this method if -/// you want the ordinal to stay the same after changing the year, of if you want the week and -/// weekday values to stay the same. -/// - Don't combine two `with_*` methods to change two components of the date. For example to -/// change both the year and month components of a date. This could fail because an intermediate -/// value does not exist, while the final date would be valid. -/// -/// For more complex changes to a date, it is best to use the methods on [`NaiveDate`] to create a -/// new value instead of altering an existing date. -/// -/// [`year`]: Datelike::year -/// [`month`]: Datelike::month -/// [`day`]: Datelike::day -/// [`weekday`]: Datelike::weekday -/// [`with_year`]: Datelike::with_year -/// [`NaiveDate`]: crate::NaiveDate -pub trait Datelike: Sized { - /// Returns the year number in the [calendar date](./naive/struct.NaiveDate.html#calendar-date). - fn year(&self) -> i32; - - /// Returns the absolute year number starting from 1 with a boolean flag, - /// which is false when the year predates the epoch (BCE/BC) and true otherwise (CE/AD). - #[inline] - fn year_ce(&self) -> (bool, u32) { - let year = self.year(); - if year < 1 { (false, (1 - year) as u32) } else { (true, year as u32) } - } - - /// Returns the quarter number starting from 1. - /// - /// The return value ranges from 1 to 4. - #[inline] - fn quarter(&self) -> u32 { - (self.month() - 1).div_euclid(3) + 1 - } - - /// Returns the month number starting from 1. - /// - /// The return value ranges from 1 to 12. - fn month(&self) -> u32; - - /// Returns the month number starting from 0. - /// - /// The return value ranges from 0 to 11. - fn month0(&self) -> u32; - - /// Returns the day of month starting from 1. - /// - /// The return value ranges from 1 to 31. (The last day of month differs by months.) - fn day(&self) -> u32; - - /// Returns the day of month starting from 0. - /// - /// The return value ranges from 0 to 30. (The last day of month differs by months.) - fn day0(&self) -> u32; - - /// Returns the day of year starting from 1. - /// - /// The return value ranges from 1 to 366. (The last day of year differs by years.) - fn ordinal(&self) -> u32; - - /// Returns the day of year starting from 0. - /// - /// The return value ranges from 0 to 365. (The last day of year differs by years.) - fn ordinal0(&self) -> u32; - - /// Returns the day of week. - fn weekday(&self) -> Weekday; - - /// Returns the ISO week. - fn iso_week(&self) -> IsoWeek; - - /// Makes a new value with the year number changed, while keeping the same month and day. - /// - /// This method assumes you want to work on the date as a year-month-day value. Don't use it if - /// you want the ordinal to stay the same after changing the year, of if you want the week and - /// weekday values to stay the same. - /// - /// # Errors - /// - /// Returns `None` when: - /// - /// - The resulting date does not exist (February 29 in a non-leap year). - /// - The year is out of range for [`NaiveDate`]. - /// - In case of [`DateTime`] if the resulting date and time fall within a timezone - /// transition such as from DST to standard time. - /// - /// [`NaiveDate`]: crate::NaiveDate - /// [`DateTime`]: crate::DateTime - /// - /// # Examples - /// - /// ``` - /// use chrono::{Datelike, NaiveDate}; - /// - /// assert_eq!( - /// NaiveDate::from_ymd_opt(2020, 5, 13).unwrap().with_year(2023).unwrap(), - /// NaiveDate::from_ymd_opt(2023, 5, 13).unwrap() - /// ); - /// // Resulting date 2023-02-29 does not exist: - /// assert!(NaiveDate::from_ymd_opt(2020, 2, 29).unwrap().with_year(2023).is_none()); - /// - /// // Don't use `with_year` if you want the ordinal date to stay the same: - /// assert_ne!( - /// NaiveDate::from_yo_opt(2020, 100).unwrap().with_year(2023).unwrap(), - /// NaiveDate::from_yo_opt(2023, 100).unwrap() // result is 2023-101 - /// ); - /// ``` - fn with_year(&self, year: i32) -> Option; - - /// Makes a new value with the month number (starting from 1) changed. - /// - /// # Errors - /// - /// Returns `None` when: - /// - /// - The resulting date does not exist (for example `month(4)` when day of the month is 31). - /// - In case of [`DateTime`] if the resulting date and time fall within a timezone - /// transition such as from DST to standard time. - /// - The value for `month` is out of range. - /// - /// [`DateTime`]: crate::DateTime - /// - /// # Examples - /// - /// ``` - /// use chrono::{Datelike, NaiveDate}; - /// - /// assert_eq!( - /// NaiveDate::from_ymd_opt(2023, 5, 12).unwrap().with_month(9).unwrap(), - /// NaiveDate::from_ymd_opt(2023, 9, 12).unwrap() - /// ); - /// // Resulting date 2023-09-31 does not exist: - /// assert!(NaiveDate::from_ymd_opt(2023, 5, 31).unwrap().with_month(9).is_none()); - /// ``` - /// - /// Don't combine multiple `Datelike::with_*` methods. The intermediate value may not exist. - /// ``` - /// use chrono::{Datelike, NaiveDate}; - /// - /// fn with_year_month(date: NaiveDate, year: i32, month: u32) -> Option { - /// date.with_year(year)?.with_month(month) - /// } - /// let d = NaiveDate::from_ymd_opt(2020, 2, 29).unwrap(); - /// assert!(with_year_month(d, 2019, 1).is_none()); // fails because of invalid intermediate value - /// - /// // Correct version: - /// fn with_year_month_fixed(date: NaiveDate, year: i32, month: u32) -> Option { - /// NaiveDate::from_ymd_opt(year, month, date.day()) - /// } - /// let d = NaiveDate::from_ymd_opt(2020, 2, 29).unwrap(); - /// assert_eq!(with_year_month_fixed(d, 2019, 1), NaiveDate::from_ymd_opt(2019, 1, 29)); - /// ``` - fn with_month(&self, month: u32) -> Option; - - /// Makes a new value with the month number (starting from 0) changed. - /// - /// # Errors - /// - /// Returns `None` when: - /// - /// - The resulting date does not exist (for example `month0(3)` when day of the month is 31). - /// - In case of [`DateTime`] if the resulting date and time fall within a timezone - /// transition such as from DST to standard time. - /// - The value for `month0` is out of range. - /// - /// [`DateTime`]: crate::DateTime - fn with_month0(&self, month0: u32) -> Option; - - /// Makes a new value with the day of month (starting from 1) changed. - /// - /// # Errors - /// - /// Returns `None` when: - /// - /// - The resulting date does not exist (for example `day(31)` in April). - /// - In case of [`DateTime`] if the resulting date and time fall within a timezone - /// transition such as from DST to standard time. - /// - The value for `day` is out of range. - /// - /// [`DateTime`]: crate::DateTime - fn with_day(&self, day: u32) -> Option; - - /// Makes a new value with the day of month (starting from 0) changed. - /// - /// # Errors - /// - /// Returns `None` when: - /// - /// - The resulting date does not exist (for example `day0(30)` in April). - /// - In case of [`DateTime`] if the resulting date and time fall within a timezone - /// transition such as from DST to standard time. - /// - The value for `day0` is out of range. - /// - /// [`DateTime`]: crate::DateTime - fn with_day0(&self, day0: u32) -> Option; - - /// Makes a new value with the day of year (starting from 1) changed. - /// - /// # Errors - /// - /// Returns `None` when: - /// - /// - The resulting date does not exist (`with_ordinal(366)` in a non-leap year). - /// - In case of [`DateTime`] if the resulting date and time fall within a timezone - /// transition such as from DST to standard time. - /// - The value for `ordinal` is out of range. - /// - /// [`DateTime`]: crate::DateTime - fn with_ordinal(&self, ordinal: u32) -> Option; - - /// Makes a new value with the day of year (starting from 0) changed. - /// - /// # Errors - /// - /// Returns `None` when: - /// - /// - The resulting date does not exist (`with_ordinal0(365)` in a non-leap year). - /// - In case of [`DateTime`] if the resulting date and time fall within a timezone - /// transition such as from DST to standard time. - /// - The value for `ordinal0` is out of range. - /// - /// [`DateTime`]: crate::DateTime - fn with_ordinal0(&self, ordinal0: u32) -> Option; - - /// Counts the days in the proleptic Gregorian calendar, with January 1, Year 1 (CE) as day 1. - /// - /// # Examples - /// - /// ``` - /// use chrono::{Datelike, NaiveDate}; - /// - /// assert_eq!(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().num_days_from_ce(), 719_163); - /// assert_eq!(NaiveDate::from_ymd_opt(2, 1, 1).unwrap().num_days_from_ce(), 366); - /// assert_eq!(NaiveDate::from_ymd_opt(1, 1, 1).unwrap().num_days_from_ce(), 1); - /// assert_eq!(NaiveDate::from_ymd_opt(0, 1, 1).unwrap().num_days_from_ce(), -365); - /// ``` - fn num_days_from_ce(&self) -> i32 { - // See test_num_days_from_ce_against_alternative_impl below for a more straightforward - // implementation. - - // we know this wouldn't overflow since year is limited to 1/2^13 of i32's full range. - let mut year = self.year() - 1; - let mut ndays = 0; - if year < 0 { - let excess = 1 + (-year) / 400; - year += excess * 400; - ndays -= excess * 146_097; - } - let div_100 = year / 100; - ndays += ((year * 1461) >> 2) - div_100 + (div_100 >> 2); - ndays + self.ordinal() as i32 - } - - /// Get the length in days of the month - fn num_days_in_month(&self) -> u8 { - use num_traits::FromPrimitive; - // The value returned from `self.month()` is guaranteed to be in the - // range [1,12], which will never result in a `None` value here. - let month = Month::from_u32(self.month()).unwrap(); - // `Month::num_days` will only return `None` if the provided year is out - // of range. Since we are passing it directly from a verified date, we - // know it is in range, and the result will never be `None`. - month.num_days(self.year()).unwrap() - } -} - -/// The common set of methods for time component. -pub trait Timelike: Sized { - /// Returns the hour number from 0 to 23. - fn hour(&self) -> u32; - - /// Returns the hour number from 1 to 12 with a boolean flag, - /// which is false for AM and true for PM. - #[inline] - fn hour12(&self) -> (bool, u32) { - let hour = self.hour(); - let mut hour12 = hour % 12; - if hour12 == 0 { - hour12 = 12; - } - (hour >= 12, hour12) - } - - /// Returns the minute number from 0 to 59. - fn minute(&self) -> u32; - - /// Returns the second number from 0 to 59. - fn second(&self) -> u32; - - /// Returns the number of nanoseconds since the whole non-leap second. - /// The range from 1,000,000,000 to 1,999,999,999 represents - /// the [leap second](./naive/struct.NaiveTime.html#leap-second-handling). - fn nanosecond(&self) -> u32; - - /// Makes a new value with the hour number changed. - /// - /// Returns `None` when the resulting value would be invalid. - fn with_hour(&self, hour: u32) -> Option; - - /// Makes a new value with the minute number changed. - /// - /// Returns `None` when the resulting value would be invalid. - fn with_minute(&self, min: u32) -> Option; - - /// Makes a new value with the second number changed. - /// - /// Returns `None` when the resulting value would be invalid. - /// As with the [`second`](#tymethod.second) method, - /// the input range is restricted to 0 through 59. - fn with_second(&self, sec: u32) -> Option; - - /// Makes a new value with nanoseconds since the whole non-leap second changed. - /// - /// Returns `None` when the resulting value would be invalid. - /// As with the [`nanosecond`](#tymethod.nanosecond) method, - /// the input range can exceed 1,000,000,000 for leap seconds. - fn with_nanosecond(&self, nano: u32) -> Option; - - /// Returns the number of non-leap seconds past the last midnight. - /// - /// Every value in 00:00:00-23:59:59 maps to an integer in 0-86399. - /// - /// This method is not intended to provide the real number of seconds since midnight on a given - /// day. It does not take things like DST transitions into account. - #[inline] - fn num_seconds_from_midnight(&self) -> u32 { - self.hour() * 3600 + self.minute() * 60 + self.second() - } -} - -#[cfg(test)] -mod tests { - use super::Datelike; - use crate::{Days, NaiveDate}; - - /// Tests `Datelike::num_days_from_ce` against an alternative implementation. - /// - /// The alternative implementation is not as short as the current one but it is simpler to - /// understand, with less unexplained magic constants. - #[test] - fn test_num_days_from_ce_against_alternative_impl() { - /// Returns the number of multiples of `div` in the range `start..end`. - /// - /// If the range `start..end` is back-to-front, i.e. `start` is greater than `end`, the - /// behaviour is defined by the following equation: - /// `in_between(start, end, div) == - in_between(end, start, div)`. - /// - /// When `div` is 1, this is equivalent to `end - start`, i.e. the length of `start..end`. - /// - /// # Panics - /// - /// Panics if `div` is not positive. - fn in_between(start: i32, end: i32, div: i32) -> i32 { - assert!(div > 0, "in_between: nonpositive div = {div}"); - let start = (start.div_euclid(div), start.rem_euclid(div)); - let end = (end.div_euclid(div), end.rem_euclid(div)); - // The lowest multiple of `div` greater than or equal to `start`, divided. - let start = start.0 + (start.1 != 0) as i32; - // The lowest multiple of `div` greater than or equal to `end`, divided. - let end = end.0 + (end.1 != 0) as i32; - end - start - } - - /// Alternative implementation to `Datelike::num_days_from_ce` - fn num_days_from_ce(date: &Date) -> i32 { - let year = date.year(); - let diff = move |div| in_between(1, year, div); - // 365 days a year, one more in leap years. In the gregorian calendar, leap years are all - // the multiples of 4 except multiples of 100 but including multiples of 400. - date.ordinal() as i32 + 365 * diff(1) + diff(4) - diff(100) + diff(400) - } - - for year in NaiveDate::MIN.year()..=NaiveDate::MAX.year() { - let jan1_year = NaiveDate::from_ymd_opt(year, 1, 1).unwrap(); - assert_eq!( - jan1_year.num_days_from_ce(), - num_days_from_ce(&jan1_year), - "on {jan1_year:?}" - ); - let mid_year = jan1_year + Days::new(133); - assert_eq!(mid_year.num_days_from_ce(), num_days_from_ce(&mid_year), "on {mid_year:?}"); - } - } - - #[test] - fn test_num_days_in_month() { - let feb_leap_year = NaiveDate::from_ymd_opt(2004, 2, 1).unwrap(); - assert_eq!(feb_leap_year.num_days_in_month(), 29); - let feb = feb_leap_year.with_year(2005).unwrap(); - assert_eq!(feb.num_days_in_month(), 28); - let march = feb.with_month(3).unwrap(); - assert_eq!(march.num_days_in_month(), 31); - } -} diff --git a/chrono-0.4.44/src/weekday.rs b/chrono-0.4.44/src/weekday.rs deleted file mode 100644 index da4746b8ae..0000000000 --- a/chrono-0.4.44/src/weekday.rs +++ /dev/null @@ -1,419 +0,0 @@ -use core::fmt; - -#[cfg(any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"))] -use rkyv::{Archive, Deserialize, Serialize}; - -use crate::OutOfRange; - -/// The day of week. -/// -/// The order of the days of week depends on the context. -/// (This is why this type does *not* implement `PartialOrd` or `Ord` traits.) -/// One should prefer `*_from_monday` or `*_from_sunday` methods to get the correct result. -/// -/// # Example -/// ``` -/// use chrono::Weekday; -/// -/// let monday = "Monday".parse::().unwrap(); -/// assert_eq!(monday, Weekday::Mon); -/// -/// let sunday = Weekday::try_from(6).unwrap(); -/// assert_eq!(sunday, Weekday::Sun); -/// -/// assert_eq!(sunday.num_days_from_monday(), 6); // starts counting with Monday = 0 -/// assert_eq!(sunday.number_from_monday(), 7); // starts counting with Monday = 1 -/// assert_eq!(sunday.num_days_from_sunday(), 0); // starts counting with Sunday = 0 -/// assert_eq!(sunday.number_from_sunday(), 1); // starts counting with Sunday = 1 -/// -/// assert_eq!(sunday.succ(), monday); -/// assert_eq!(sunday.pred(), Weekday::Sat); -/// ``` -#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)] -#[cfg_attr( - any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"), - derive(Archive, Deserialize, Serialize), - archive(compare(PartialEq)), - archive_attr(derive(Clone, Copy, PartialEq, Eq, Debug, Hash)) -)] -#[cfg_attr(feature = "rkyv-validation", archive(check_bytes))] -#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(arbitrary::Arbitrary))] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Weekday { - /// Monday. - Mon = 0, - /// Tuesday. - Tue = 1, - /// Wednesday. - Wed = 2, - /// Thursday. - Thu = 3, - /// Friday. - Fri = 4, - /// Saturday. - Sat = 5, - /// Sunday. - Sun = 6, -} - -impl Weekday { - /// The next day in the week. - /// - /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` - /// ----------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- - /// `w.succ()`: | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` | `Mon` - #[inline] - #[must_use] - pub const fn succ(&self) -> Weekday { - match *self { - Weekday::Mon => Weekday::Tue, - Weekday::Tue => Weekday::Wed, - Weekday::Wed => Weekday::Thu, - Weekday::Thu => Weekday::Fri, - Weekday::Fri => Weekday::Sat, - Weekday::Sat => Weekday::Sun, - Weekday::Sun => Weekday::Mon, - } - } - - /// The previous day in the week. - /// - /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` - /// ----------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- - /// `w.pred()`: | `Sun` | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` - #[inline] - #[must_use] - pub const fn pred(&self) -> Weekday { - match *self { - Weekday::Mon => Weekday::Sun, - Weekday::Tue => Weekday::Mon, - Weekday::Wed => Weekday::Tue, - Weekday::Thu => Weekday::Wed, - Weekday::Fri => Weekday::Thu, - Weekday::Sat => Weekday::Fri, - Weekday::Sun => Weekday::Sat, - } - } - - /// Returns a day-of-week number starting from Monday = 1. (ISO 8601 weekday number) - /// - /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` - /// ------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- - /// `w.number_from_monday()`: | 1 | 2 | 3 | 4 | 5 | 6 | 7 - #[inline] - pub const fn number_from_monday(&self) -> u32 { - self.days_since(Weekday::Mon) + 1 - } - - /// Returns a day-of-week number starting from Sunday = 1. - /// - /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` - /// ------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- - /// `w.number_from_sunday()`: | 2 | 3 | 4 | 5 | 6 | 7 | 1 - #[inline] - pub const fn number_from_sunday(&self) -> u32 { - self.days_since(Weekday::Sun) + 1 - } - - /// Returns a day-of-week number starting from Monday = 0. - /// - /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` - /// --------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- - /// `w.num_days_from_monday()`: | 0 | 1 | 2 | 3 | 4 | 5 | 6 - /// - /// # Example - /// - /// ``` - /// # #[cfg(feature = "clock")] { - /// # use chrono::{Local, Datelike}; - /// // MTWRFSU is occasionally used as a single-letter abbreviation of the weekdays. - /// // Use `num_days_from_monday` to index into the array. - /// const MTWRFSU: [char; 7] = ['M', 'T', 'W', 'R', 'F', 'S', 'U']; - /// - /// let today = Local::now().weekday(); - /// println!("{}", MTWRFSU[today.num_days_from_monday() as usize]); - /// # } - /// ``` - #[inline] - pub const fn num_days_from_monday(&self) -> u32 { - self.days_since(Weekday::Mon) - } - - /// Returns a day-of-week number starting from Sunday = 0. - /// - /// `w`: | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` - /// --------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | ----- - /// `w.num_days_from_sunday()`: | 1 | 2 | 3 | 4 | 5 | 6 | 0 - #[inline] - pub const fn num_days_from_sunday(&self) -> u32 { - self.days_since(Weekday::Sun) - } - - /// The number of days since the given day. - /// - /// # Examples - /// - /// ``` - /// use chrono::Weekday::*; - /// assert_eq!(Mon.days_since(Mon), 0); - /// assert_eq!(Sun.days_since(Tue), 5); - /// assert_eq!(Wed.days_since(Sun), 3); - /// ``` - pub const fn days_since(&self, other: Weekday) -> u32 { - let lhs = *self as u32; - let rhs = other as u32; - if lhs < rhs { 7 + lhs - rhs } else { lhs - rhs } - } -} - -impl fmt::Display for Weekday { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.pad(match *self { - Weekday::Mon => "Mon", - Weekday::Tue => "Tue", - Weekday::Wed => "Wed", - Weekday::Thu => "Thu", - Weekday::Fri => "Fri", - Weekday::Sat => "Sat", - Weekday::Sun => "Sun", - }) - } -} - -/// Any weekday can be represented as an integer from 0 to 6, which equals to -/// [`Weekday::num_days_from_monday`](#method.num_days_from_monday) in this implementation. -/// Do not heavily depend on this though; use explicit methods whenever possible. -impl TryFrom for Weekday { - type Error = OutOfRange; - - fn try_from(value: u8) -> Result { - match value { - 0 => Ok(Weekday::Mon), - 1 => Ok(Weekday::Tue), - 2 => Ok(Weekday::Wed), - 3 => Ok(Weekday::Thu), - 4 => Ok(Weekday::Fri), - 5 => Ok(Weekday::Sat), - 6 => Ok(Weekday::Sun), - _ => Err(OutOfRange::new()), - } - } -} - -/// Any weekday can be represented as an integer from 0 to 6, which equals to -/// [`Weekday::num_days_from_monday`](#method.num_days_from_monday) in this implementation. -/// Do not heavily depend on this though; use explicit methods whenever possible. -impl num_traits::FromPrimitive for Weekday { - #[inline] - fn from_i64(n: i64) -> Option { - match n { - 0 => Some(Weekday::Mon), - 1 => Some(Weekday::Tue), - 2 => Some(Weekday::Wed), - 3 => Some(Weekday::Thu), - 4 => Some(Weekday::Fri), - 5 => Some(Weekday::Sat), - 6 => Some(Weekday::Sun), - _ => None, - } - } - - #[inline] - fn from_u64(n: u64) -> Option { - match n { - 0 => Some(Weekday::Mon), - 1 => Some(Weekday::Tue), - 2 => Some(Weekday::Wed), - 3 => Some(Weekday::Thu), - 4 => Some(Weekday::Fri), - 5 => Some(Weekday::Sat), - 6 => Some(Weekday::Sun), - _ => None, - } - } -} - -/// An error resulting from reading `Weekday` value with `FromStr`. -#[derive(Clone, PartialEq, Eq)] -pub struct ParseWeekdayError { - pub(crate) _dummy: (), -} - -#[cfg(all(not(feature = "std"), feature = "core-error"))] -impl core::error::Error for ParseWeekdayError {} - -#[cfg(feature = "std")] -impl std::error::Error for ParseWeekdayError {} - -impl fmt::Display for ParseWeekdayError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_fmt(format_args!("{self:?}")) - } -} - -impl fmt::Debug for ParseWeekdayError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "ParseWeekdayError {{ .. }}") - } -} - -#[cfg(feature = "defmt")] -impl defmt::Format for ParseWeekdayError { - fn format(&self, fmt: defmt::Formatter) { - defmt::write!(fmt, "ParseWeekdayError {{ .. }}") - } -} - -// the actual `FromStr` implementation is in the `format` module to leverage the existing code - -#[cfg(feature = "serde")] -mod weekday_serde { - use super::Weekday; - use core::fmt; - use serde::{de, ser}; - - impl ser::Serialize for Weekday { - fn serialize(&self, serializer: S) -> Result - where - S: ser::Serializer, - { - serializer.collect_str(&self) - } - } - - struct WeekdayVisitor; - - impl de::Visitor<'_> for WeekdayVisitor { - type Value = Weekday; - - fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("Weekday") - } - - fn visit_str(self, value: &str) -> Result - where - E: de::Error, - { - value.parse().map_err(|_| E::custom("short or long weekday names expected")) - } - } - - impl<'de> de::Deserialize<'de> for Weekday { - fn deserialize(deserializer: D) -> Result - where - D: de::Deserializer<'de>, - { - deserializer.deserialize_str(WeekdayVisitor) - } - } -} - -#[cfg(test)] -mod tests { - use super::Weekday; - - #[test] - fn test_days_since() { - for i in 0..7 { - let base_day = Weekday::try_from(i).unwrap(); - - assert_eq!(base_day.num_days_from_monday(), base_day.days_since(Weekday::Mon)); - assert_eq!(base_day.num_days_from_sunday(), base_day.days_since(Weekday::Sun)); - - assert_eq!(base_day.days_since(base_day), 0); - - assert_eq!(base_day.days_since(base_day.pred()), 1); - assert_eq!(base_day.days_since(base_day.pred().pred()), 2); - assert_eq!(base_day.days_since(base_day.pred().pred().pred()), 3); - assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred()), 4); - assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred().pred()), 5); - assert_eq!(base_day.days_since(base_day.pred().pred().pred().pred().pred().pred()), 6); - - assert_eq!(base_day.days_since(base_day.succ()), 6); - assert_eq!(base_day.days_since(base_day.succ().succ()), 5); - assert_eq!(base_day.days_since(base_day.succ().succ().succ()), 4); - assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ()), 3); - assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ().succ()), 2); - assert_eq!(base_day.days_since(base_day.succ().succ().succ().succ().succ().succ()), 1); - } - } - - #[test] - fn test_formatting_alignment() { - // No exhaustive testing here as we just delegate the - // implementation to Formatter::pad. Just some basic smoke - // testing to ensure that it's in fact being done. - assert_eq!(format!("{:x>7}", Weekday::Mon), "xxxxMon"); - assert_eq!(format!("{:^7}", Weekday::Mon), " Mon "); - assert_eq!(format!("{:Z<7}", Weekday::Mon), "MonZZZZ"); - } - - #[test] - #[cfg(feature = "serde")] - fn test_serde_serialize() { - use Weekday::*; - use serde_json::to_string; - - let cases: Vec<(Weekday, &str)> = vec![ - (Mon, "\"Mon\""), - (Tue, "\"Tue\""), - (Wed, "\"Wed\""), - (Thu, "\"Thu\""), - (Fri, "\"Fri\""), - (Sat, "\"Sat\""), - (Sun, "\"Sun\""), - ]; - - for (weekday, expected_str) in cases { - let string = to_string(&weekday).unwrap(); - assert_eq!(string, expected_str); - } - } - - #[test] - #[cfg(feature = "serde")] - fn test_serde_deserialize() { - use Weekday::*; - use serde_json::from_str; - - let cases: Vec<(&str, Weekday)> = vec![ - ("\"mon\"", Mon), - ("\"MONDAY\"", Mon), - ("\"MonDay\"", Mon), - ("\"mOn\"", Mon), - ("\"tue\"", Tue), - ("\"tuesday\"", Tue), - ("\"wed\"", Wed), - ("\"wednesday\"", Wed), - ("\"thu\"", Thu), - ("\"thursday\"", Thu), - ("\"fri\"", Fri), - ("\"friday\"", Fri), - ("\"sat\"", Sat), - ("\"saturday\"", Sat), - ("\"sun\"", Sun), - ("\"sunday\"", Sun), - ]; - - for (str, expected_weekday) in cases { - let weekday = from_str::(str).unwrap(); - assert_eq!(weekday, expected_weekday); - } - - let errors: Vec<&str> = - vec!["\"not a weekday\"", "\"monDAYs\"", "\"mond\"", "mon", "\"thur\"", "\"thurs\""]; - - for str in errors { - from_str::(str).unwrap_err(); - } - } - - #[test] - #[cfg(feature = "rkyv-validation")] - fn test_rkyv_validation() { - let mon = Weekday::Mon; - let bytes = rkyv::to_bytes::<_, 1>(&mon).unwrap(); - - assert_eq!(rkyv::from_bytes::(&bytes).unwrap(), mon); - } -} diff --git a/chrono-0.4.44/src/weekday_set.rs b/chrono-0.4.44/src/weekday_set.rs deleted file mode 100644 index 989131c209..0000000000 --- a/chrono-0.4.44/src/weekday_set.rs +++ /dev/null @@ -1,500 +0,0 @@ -use core::{ - fmt::{self, Debug}, - iter::FusedIterator, -}; - -use crate::Weekday; - -/// A collection of [`Weekday`]s stored as a single byte. -/// -/// This type is `Copy` and provides efficient set-like and slice-like operations. -/// Many operations are `const` as well. -/// -/// Implemented as a bitmask where bits 1-7 correspond to Monday-Sunday. -#[derive(Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct WeekdaySet(u8); // Invariant: the 8-th bit is always 0. - -impl WeekdaySet { - /// Create a `WeekdaySet` from an array of [`Weekday`]s. - /// - /// # Example - /// ``` - /// # use chrono::WeekdaySet; - /// use chrono::Weekday::*; - /// assert_eq!(WeekdaySet::EMPTY, WeekdaySet::from_array([])); - /// assert_eq!(WeekdaySet::single(Mon), WeekdaySet::from_array([Mon])); - /// assert_eq!(WeekdaySet::ALL, WeekdaySet::from_array([Mon, Tue, Wed, Thu, Fri, Sat, Sun])); - /// ``` - pub const fn from_array(days: [Weekday; C]) -> Self { - let mut acc = Self::EMPTY; - let mut idx = 0; - while idx < days.len() { - acc.0 |= Self::single(days[idx]).0; - idx += 1; - } - acc - } - - /// Create a `WeekdaySet` from a single [`Weekday`]. - pub const fn single(weekday: Weekday) -> Self { - match weekday { - Weekday::Mon => Self(0b000_0001), - Weekday::Tue => Self(0b000_0010), - Weekday::Wed => Self(0b000_0100), - Weekday::Thu => Self(0b000_1000), - Weekday::Fri => Self(0b001_0000), - Weekday::Sat => Self(0b010_0000), - Weekday::Sun => Self(0b100_0000), - } - } - - /// Returns `Some(day)` if this collection contains exactly one day. - /// - /// Returns `None` otherwise. - /// - /// # Example - /// ``` - /// # use chrono::WeekdaySet; - /// use chrono::Weekday::*; - /// assert_eq!(WeekdaySet::single(Mon).single_day(), Some(Mon)); - /// assert_eq!(WeekdaySet::from_array([Mon, Tue]).single_day(), None); - /// assert_eq!(WeekdaySet::EMPTY.single_day(), None); - /// assert_eq!(WeekdaySet::ALL.single_day(), None); - /// ``` - pub const fn single_day(self) -> Option { - match self { - Self(0b000_0001) => Some(Weekday::Mon), - Self(0b000_0010) => Some(Weekday::Tue), - Self(0b000_0100) => Some(Weekday::Wed), - Self(0b000_1000) => Some(Weekday::Thu), - Self(0b001_0000) => Some(Weekday::Fri), - Self(0b010_0000) => Some(Weekday::Sat), - Self(0b100_0000) => Some(Weekday::Sun), - _ => None, - } - } - - /// Adds a day to the collection. - /// - /// Returns `true` if the day was new to the collection. - /// - /// # Example - /// ``` - /// # use chrono::WeekdaySet; - /// use chrono::Weekday::*; - /// let mut weekdays = WeekdaySet::single(Mon); - /// assert!(weekdays.insert(Tue)); - /// assert!(!weekdays.insert(Tue)); - /// ``` - pub fn insert(&mut self, day: Weekday) -> bool { - if self.contains(day) { - return false; - } - - self.0 |= Self::single(day).0; - true - } - - /// Removes a day from the collection. - /// - /// Returns `true` if the collection did contain the day. - /// - /// # Example - /// ``` - /// # use chrono::WeekdaySet; - /// use chrono::Weekday::*; - /// let mut weekdays = WeekdaySet::single(Mon); - /// assert!(weekdays.remove(Mon)); - /// assert!(!weekdays.remove(Mon)); - /// ``` - pub fn remove(&mut self, day: Weekday) -> bool { - if self.contains(day) { - self.0 &= !Self::single(day).0; - return true; - } - - false - } - - /// Returns `true` if `other` contains all days in `self`. - /// - /// # Example - /// ``` - /// # use chrono::WeekdaySet; - /// use chrono::Weekday::*; - /// assert!(WeekdaySet::single(Mon).is_subset(WeekdaySet::ALL)); - /// assert!(!WeekdaySet::single(Mon).is_subset(WeekdaySet::EMPTY)); - /// assert!(WeekdaySet::EMPTY.is_subset(WeekdaySet::single(Mon))); - /// ``` - pub const fn is_subset(self, other: Self) -> bool { - self.intersection(other).0 == self.0 - } - - /// Returns days that are in both `self` and `other`. - /// - /// # Example - /// ``` - /// # use chrono::WeekdaySet; - /// use chrono::Weekday::*; - /// assert_eq!(WeekdaySet::single(Mon).intersection(WeekdaySet::single(Mon)), WeekdaySet::single(Mon)); - /// assert_eq!(WeekdaySet::single(Mon).intersection(WeekdaySet::single(Tue)), WeekdaySet::EMPTY); - /// assert_eq!(WeekdaySet::ALL.intersection(WeekdaySet::single(Mon)), WeekdaySet::single(Mon)); - /// assert_eq!(WeekdaySet::ALL.intersection(WeekdaySet::EMPTY), WeekdaySet::EMPTY); - /// ``` - pub const fn intersection(self, other: Self) -> Self { - Self(self.0 & other.0) - } - - /// Returns days that are in either `self` or `other`. - /// - /// # Example - /// ``` - /// # use chrono::WeekdaySet; - /// use chrono::Weekday::*; - /// assert_eq!(WeekdaySet::single(Mon).union(WeekdaySet::single(Mon)), WeekdaySet::single(Mon)); - /// assert_eq!(WeekdaySet::single(Mon).union(WeekdaySet::single(Tue)), WeekdaySet::from_array([Mon, Tue])); - /// assert_eq!(WeekdaySet::ALL.union(WeekdaySet::single(Mon)), WeekdaySet::ALL); - /// assert_eq!(WeekdaySet::ALL.union(WeekdaySet::EMPTY), WeekdaySet::ALL); - /// ``` - pub const fn union(self, other: Self) -> Self { - Self(self.0 | other.0) - } - - /// Returns days that are in `self` or `other` but not in both. - /// - /// # Example - /// ``` - /// # use chrono::WeekdaySet; - /// use chrono::Weekday::*; - /// assert_eq!(WeekdaySet::single(Mon).symmetric_difference(WeekdaySet::single(Mon)), WeekdaySet::EMPTY); - /// assert_eq!(WeekdaySet::single(Mon).symmetric_difference(WeekdaySet::single(Tue)), WeekdaySet::from_array([Mon, Tue])); - /// assert_eq!( - /// WeekdaySet::ALL.symmetric_difference(WeekdaySet::single(Mon)), - /// WeekdaySet::from_array([Tue, Wed, Thu, Fri, Sat, Sun]), - /// ); - /// assert_eq!(WeekdaySet::ALL.symmetric_difference(WeekdaySet::EMPTY), WeekdaySet::ALL); - /// ``` - pub const fn symmetric_difference(self, other: Self) -> Self { - Self(self.0 ^ other.0) - } - - /// Returns days that are in `self` but not in `other`. - /// - /// # Example - /// ``` - /// # use chrono::WeekdaySet; - /// use chrono::Weekday::*; - /// assert_eq!(WeekdaySet::single(Mon).difference(WeekdaySet::single(Mon)), WeekdaySet::EMPTY); - /// assert_eq!(WeekdaySet::single(Mon).difference(WeekdaySet::single(Tue)), WeekdaySet::single(Mon)); - /// assert_eq!(WeekdaySet::EMPTY.difference(WeekdaySet::single(Mon)), WeekdaySet::EMPTY); - /// ``` - pub const fn difference(self, other: Self) -> Self { - Self(self.0 & !other.0) - } - - /// Get the first day in the collection, starting from Monday. - /// - /// Returns `None` if the collection is empty. - /// - /// # Example - /// ``` - /// # use chrono::WeekdaySet; - /// use chrono::Weekday::*; - /// assert_eq!(WeekdaySet::single(Mon).first(), Some(Mon)); - /// assert_eq!(WeekdaySet::single(Tue).first(), Some(Tue)); - /// assert_eq!(WeekdaySet::ALL.first(), Some(Mon)); - /// assert_eq!(WeekdaySet::EMPTY.first(), None); - /// ``` - pub const fn first(self) -> Option { - if self.is_empty() { - return None; - } - - // Find the first non-zero bit. - let bit = 1 << self.0.trailing_zeros(); - - Self(bit).single_day() - } - - /// Get the last day in the collection, starting from Sunday. - /// - /// Returns `None` if the collection is empty. - /// - /// # Example - /// ``` - /// # use chrono::WeekdaySet; - /// use chrono::Weekday::*; - /// assert_eq!(WeekdaySet::single(Mon).last(), Some(Mon)); - /// assert_eq!(WeekdaySet::single(Sun).last(), Some(Sun)); - /// assert_eq!(WeekdaySet::from_array([Mon, Tue]).last(), Some(Tue)); - /// assert_eq!(WeekdaySet::EMPTY.last(), None); - /// ``` - pub fn last(self) -> Option { - if self.is_empty() { - return None; - } - - // Find the last non-zero bit. - let bit = 1 << (7 - self.0.leading_zeros()); - - Self(bit).single_day() - } - - /// Split the collection in two at the given day. - /// - /// Returns a tuple `(before, after)`. `before` contains all days starting from Monday - /// up to but __not__ including `weekday`. `after` contains all days starting from `weekday` - /// up to and including Sunday. - const fn split_at(self, weekday: Weekday) -> (Self, Self) { - let days_after = 0b1000_0000 - Self::single(weekday).0; - let days_before = days_after ^ 0b0111_1111; - (Self(self.0 & days_before), Self(self.0 & days_after)) - } - - /// Iterate over the [`Weekday`]s in the collection starting from a given day. - /// - /// Wraps around from Sunday to Monday if necessary. - /// - /// # Example - /// ``` - /// # use chrono::WeekdaySet; - /// use chrono::Weekday::*; - /// let weekdays = WeekdaySet::from_array([Mon, Wed, Fri]); - /// let mut iter = weekdays.iter(Wed); - /// assert_eq!(iter.next(), Some(Wed)); - /// assert_eq!(iter.next(), Some(Fri)); - /// assert_eq!(iter.next(), Some(Mon)); - /// assert_eq!(iter.next(), None); - /// ``` - pub const fn iter(self, start: Weekday) -> WeekdaySetIter { - WeekdaySetIter { days: self, start } - } - - /// Returns `true` if the collection contains the given day. - /// - /// # Example - /// ``` - /// # use chrono::WeekdaySet; - /// use chrono::Weekday::*; - /// assert!(WeekdaySet::single(Mon).contains(Mon)); - /// assert!(WeekdaySet::from_array([Mon, Tue]).contains(Tue)); - /// assert!(!WeekdaySet::single(Mon).contains(Tue)); - /// ``` - pub const fn contains(self, day: Weekday) -> bool { - self.0 & Self::single(day).0 != 0 - } - - /// Returns `true` if the collection is empty. - /// - /// # Example - /// ``` - /// # use chrono::{Weekday, WeekdaySet}; - /// assert!(WeekdaySet::EMPTY.is_empty()); - /// assert!(!WeekdaySet::single(Weekday::Mon).is_empty()); - /// ``` - pub const fn is_empty(self) -> bool { - self.len() == 0 - } - /// Returns the number of days in the collection. - /// - /// # Example - /// ``` - /// # use chrono::WeekdaySet; - /// use chrono::Weekday::*; - /// assert_eq!(WeekdaySet::single(Mon).len(), 1); - /// assert_eq!(WeekdaySet::from_array([Mon, Wed, Fri]).len(), 3); - /// assert_eq!(WeekdaySet::ALL.len(), 7); - /// ``` - pub const fn len(self) -> u8 { - self.0.count_ones() as u8 - } - - /// An empty `WeekdaySet`. - pub const EMPTY: Self = Self(0b000_0000); - /// A `WeekdaySet` containing all seven `Weekday`s. - pub const ALL: Self = Self(0b111_1111); -} - -/// Print the underlying bitmask, padded to 7 bits. -/// -/// # Example -/// ``` -/// # use chrono::WeekdaySet; -/// use chrono::Weekday::*; -/// assert_eq!(format!("{:?}", WeekdaySet::single(Mon)), "WeekdaySet(0000001)"); -/// assert_eq!(format!("{:?}", WeekdaySet::single(Tue)), "WeekdaySet(0000010)"); -/// assert_eq!(format!("{:?}", WeekdaySet::ALL), "WeekdaySet(1111111)"); -/// ``` -impl Debug for WeekdaySet { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "WeekdaySet({:0>7b})", self.0) - } -} - -#[cfg(feature = "defmt")] -impl defmt::Format for WeekdaySet { - fn format(&self, f: defmt::Formatter<'_>) { - defmt::write!( - f, - "WeekdaySet({}{}{}{}{}{}{})", - 0x1 & (self.0 >> 6), - 0x1 & (self.0 >> 5), - 0x1 & (self.0 >> 4), - 0x1 & (self.0 >> 3), - 0x1 & (self.0 >> 2), - 0x1 & (self.0 >> 1), - 0x1 & (self.0 >> 0), - ) - } -} - -/// An iterator over a collection of weekdays, starting from a given day. -/// -/// See [`WeekdaySet::iter()`]. -#[derive(Debug, Clone)] -pub struct WeekdaySetIter { - days: WeekdaySet, - start: Weekday, -} - -impl Iterator for WeekdaySetIter { - type Item = Weekday; - - fn next(&mut self) -> Option { - if self.days.is_empty() { - return None; - } - - // Split the collection in two at `start`. - // Look for the first day among the days after `start` first, including `start` itself. - // If there are no days after `start`, look for the first day among the days before `start`. - let (before, after) = self.days.split_at(self.start); - let days = if after.is_empty() { before } else { after }; - - let next = days.first().expect("the collection is not empty"); - self.days.remove(next); - Some(next) - } -} - -impl DoubleEndedIterator for WeekdaySetIter { - fn next_back(&mut self) -> Option { - if self.days.is_empty() { - return None; - } - - // Split the collection in two at `start`. - // Look for the last day among the days before `start` first, NOT including `start` itself. - // If there are no days before `start`, look for the last day among the days after `start`. - let (before, after) = self.days.split_at(self.start); - let days = if before.is_empty() { after } else { before }; - - let next_back = days.last().expect("the collection is not empty"); - self.days.remove(next_back); - Some(next_back) - } -} - -impl ExactSizeIterator for WeekdaySetIter { - fn len(&self) -> usize { - self.days.len().into() - } -} - -impl FusedIterator for WeekdaySetIter {} - -/// Print the collection as a slice-like list of weekdays. -/// -/// # Example -/// ``` -/// # use chrono::WeekdaySet; -/// use chrono::Weekday::*; -/// assert_eq!("[]", WeekdaySet::EMPTY.to_string()); -/// assert_eq!("[Mon]", WeekdaySet::single(Mon).to_string()); -/// assert_eq!("[Mon, Fri, Sun]", WeekdaySet::from_array([Mon, Fri, Sun]).to_string()); -/// ``` -impl fmt::Display for WeekdaySet { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "[")?; - let mut iter = self.iter(Weekday::Mon); - if let Some(first) = iter.next() { - write!(f, "{first}")?; - } - for weekday in iter { - write!(f, ", {weekday}")?; - } - write!(f, "]") - } -} - -impl FromIterator for WeekdaySet { - fn from_iter>(iter: T) -> Self { - iter.into_iter().map(Self::single).fold(Self::EMPTY, Self::union) - } -} - -#[cfg(test)] -mod tests { - use crate::Weekday; - - use super::WeekdaySet; - - impl WeekdaySet { - /// Iterate over all 128 possible sets, from `EMPTY` to `ALL`. - fn iter_all() -> impl Iterator { - (0b0000_0000..0b1000_0000).map(Self) - } - } - - /// Panics if the 8-th bit of `self` is not 0. - fn assert_8th_bit_invariant(days: WeekdaySet) { - assert!(days.0 & 0b1000_0000 == 0, "the 8-th bit of {days:?} is not 0"); - } - - #[test] - fn debug_prints_8th_bit_if_not_zero() { - assert_eq!(format!("{:?}", WeekdaySet(0b1000_0000)), "WeekdaySet(10000000)"); - } - - #[test] - fn bitwise_set_operations_preserve_8th_bit_invariant() { - for set1 in WeekdaySet::iter_all() { - for set2 in WeekdaySet::iter_all() { - assert_8th_bit_invariant(set1.union(set2)); - assert_8th_bit_invariant(set1.intersection(set2)); - assert_8th_bit_invariant(set1.symmetric_difference(set2)); - } - } - } - - /// Test `split_at` on all possible arguments. - #[test] - fn split_at_is_equivalent_to_iterating() { - use Weekday::*; - - // `split_at()` is used in `iter()`, so we must not iterate - // over all days with `WeekdaySet::ALL.iter(Mon)`. - const WEEK: [Weekday; 7] = [Mon, Tue, Wed, Thu, Fri, Sat, Sun]; - - for weekdays in WeekdaySet::iter_all() { - for split_day in WEEK { - let expected_before: WeekdaySet = WEEK - .into_iter() - .take_while(|&day| day != split_day) - .filter(|&day| weekdays.contains(day)) - .collect(); - let expected_after: WeekdaySet = WEEK - .into_iter() - .skip_while(|&day| day != split_day) - .filter(|&day| weekdays.contains(day)) - .collect(); - - assert_eq!( - (expected_before, expected_after), - weekdays.split_at(split_day), - "split_at({split_day}) failed for {weekdays}", - ); - } - } - } -} diff --git a/chrono-0.4.44/tests/dateutils.rs b/chrono-0.4.44/tests/dateutils.rs deleted file mode 100644 index 474034e276..0000000000 --- a/chrono-0.4.44/tests/dateutils.rs +++ /dev/null @@ -1,162 +0,0 @@ -#![cfg(all(unix, feature = "clock", feature = "std"))] - -use std::{path, process, thread}; - -#[cfg(target_os = "linux")] -use chrono::Days; -use chrono::{Datelike, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Timelike}; - -fn verify_against_date_command_local(path: &'static str, dt: NaiveDateTime) { - let output = process::Command::new(path) - .arg("-d") - .arg(format!("{}-{:02}-{:02} {:02}:05:01", dt.year(), dt.month(), dt.day(), dt.hour())) - .arg("+%Y-%m-%d %H:%M:%S %:z") - .output() - .unwrap(); - - let date_command_str = String::from_utf8(output.stdout).unwrap(); - - // The below would be preferred. At this stage neither earliest() or latest() - // seems to be consistent with the output of the `date` command, so we simply - // compare both. - // let local = Local - // .with_ymd_and_hms(year, month, day, hour, 5, 1) - // // looks like the "date" command always returns a given time when it is ambiguous - // .earliest(); - - // if let Some(local) = local { - // assert_eq!(format!("{}\n", local), date_command_str); - // } else { - // // we are in a "Spring forward gap" due to DST, and so date also returns "" - // assert_eq!("", date_command_str); - // } - - // This is used while a decision is made whether the `date` output needs to - // be exactly matched, or whether MappedLocalTime::Ambiguous should be handled - // differently - - let date = NaiveDate::from_ymd_opt(dt.year(), dt.month(), dt.day()).unwrap(); - match Local.from_local_datetime(&date.and_hms_opt(dt.hour(), 5, 1).unwrap()) { - chrono::MappedLocalTime::Ambiguous(a, b) => { - assert!(format!("{a}\n") == date_command_str || format!("{b}\n") == date_command_str) - } - chrono::MappedLocalTime::Single(a) => { - assert_eq!(format!("{a}\n"), date_command_str); - } - chrono::MappedLocalTime::None => { - assert_eq!("", date_command_str); - } - } -} - -/// path to Unix `date` command. Should work on most Linux and Unixes. Not the -/// path for MacOS (/bin/date) which uses a different version of `date` with -/// different arguments (so it won't run which is okay). -/// for testing only -#[allow(dead_code)] -#[cfg(not(target_os = "aix"))] -const DATE_PATH: &str = "/usr/bin/date"; -#[allow(dead_code)] -#[cfg(target_os = "aix")] -const DATE_PATH: &str = "/opt/freeware/bin/date"; - -#[cfg(test)] -/// test helper to sanity check the date command behaves as expected -/// asserts the command succeeded -fn assert_run_date_version() { - // note environment variable `LANG` - match std::env::var_os("LANG") { - Some(lang) => eprintln!("LANG: {lang:?}"), - None => eprintln!("LANG not set"), - } - let out = process::Command::new(DATE_PATH).arg("--version").output().unwrap(); - let stdout = String::from_utf8(out.stdout).unwrap(); - let stderr = String::from_utf8(out.stderr).unwrap(); - // note the `date` binary version - eprintln!("command: {DATE_PATH:?} --version\nstdout: {stdout:?}\nstderr: {stderr:?}"); - assert!(out.status.success(), "command failed: {DATE_PATH:?} --version"); -} - -#[test] -fn try_verify_against_date_command() { - if !path::Path::new(DATE_PATH).exists() { - eprintln!("date command {DATE_PATH:?} not found, skipping"); - return; - } - assert_run_date_version(); - - eprintln!("Run command {DATE_PATH:?} for every hour from 1975 to 2077, skipping some years...",); - - let mut children = vec![]; - for year in [1975, 1976, 1977, 2020, 2021, 2022, 2073, 2074, 2075, 2076, 2077].iter() { - children.push(thread::spawn(|| { - let mut date = NaiveDate::from_ymd_opt(*year, 1, 1).unwrap().and_time(NaiveTime::MIN); - let end = NaiveDate::from_ymd_opt(*year + 1, 1, 1).unwrap().and_time(NaiveTime::MIN); - while date <= end { - verify_against_date_command_local(DATE_PATH, date); - date += chrono::TimeDelta::try_hours(1).unwrap(); - } - })); - } - for child in children { - // Wait for the thread to finish. Returns a result. - let _ = child.join(); - } -} - -#[cfg(target_os = "linux")] -fn verify_against_date_command_format_local(path: &'static str, dt: NaiveDateTime) { - let required_format = - "d%d D%D F%F H%H I%I j%j k%k l%l m%m M%M q%q S%S T%T u%u U%U w%w W%W X%X y%y Y%Y z%:z"; - // a%a - depends from localization - // A%A - depends from localization - // b%b - depends from localization - // B%B - depends from localization - // h%h - depends from localization - // c%c - depends from localization - // p%p - depends from localization - // r%r - depends from localization - // x%x - fails, date is dd/mm/yyyy, chrono is dd/mm/yy, same as %D - // Z%Z - too many ways to represent it, will most likely fail - - let output = process::Command::new(path) - .env("LANG", "c") - .env("LC_ALL", "c") - .arg("-d") - .arg(format!( - "{}-{:02}-{:02} {:02}:{:02}:{:02}", - dt.year(), - dt.month(), - dt.day(), - dt.hour(), - dt.minute(), - dt.second() - )) - .arg(format!("+{required_format}")) - .output() - .unwrap(); - - let date_command_str = String::from_utf8(output.stdout).unwrap(); - let date = NaiveDate::from_ymd_opt(dt.year(), dt.month(), dt.day()).unwrap(); - let ldt = Local - .from_local_datetime(&date.and_hms_opt(dt.hour(), dt.minute(), dt.second()).unwrap()) - .unwrap(); - let formatted_date = format!("{}\n", ldt.format(required_format)); - assert_eq!(date_command_str, formatted_date); -} - -#[test] -#[cfg(target_os = "linux")] -fn try_verify_against_date_command_format() { - if !path::Path::new(DATE_PATH).exists() { - eprintln!("date command {DATE_PATH:?} not found, skipping"); - return; - } - assert_run_date_version(); - - let mut date = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_opt(12, 11, 13).unwrap(); - while date.year() < 2008 { - verify_against_date_command_format_local(DATE_PATH, date); - date = date + Days::new(55); - } -} diff --git a/chrono-0.4.44/tests/wasm.rs b/chrono-0.4.44/tests/wasm.rs deleted file mode 100644 index ceb9b3de97..0000000000 --- a/chrono-0.4.44/tests/wasm.rs +++ /dev/null @@ -1,89 +0,0 @@ -//! Run this test with: -//! `env TZ="$(date +%z)" NOW="$(date +%s)" wasm-pack test --node -- --features wasmbind` -//! -//! The `TZ` and `NOW` variables are used to compare the results inside the WASM environment with -//! the host system. -//! The check will fail if the local timezone does not match one of the timezones defined below. - -#![cfg(all( - target_arch = "wasm32", - feature = "wasmbind", - feature = "clock", - not(any(target_os = "emscripten", target_os = "wasi")) -))] - -use chrono::prelude::*; -use wasm_bindgen_test::*; - -#[wasm_bindgen_test] -fn now() { - let utc: DateTime = Utc::now(); - let local: DateTime = Local::now(); - - // Ensure time set by the test script is correct - let now = env!("NOW"); - let actual = NaiveDateTime::parse_from_str(&now, "%s").unwrap().and_utc(); - let diff = utc - actual; - assert!( - diff < chrono::TimeDelta::try_minutes(5).unwrap(), - "expected {} - {} == {} < 5m (env var: {})", - utc, - actual, - diff, - now, - ); - - let tz = env!("TZ"); - eprintln!("testing with tz={}", tz); - - // Ensure offset retrieved when getting local time is correct - let expected_offset = match tz { - "ACST-9:30" => FixedOffset::east_opt(19 * 30 * 60).unwrap(), - "Asia/Katmandu" => FixedOffset::east_opt(23 * 15 * 60).unwrap(), // No DST thankfully - "EDT" | "EST4" | "-0400" => FixedOffset::east_opt(-4 * 60 * 60).unwrap(), - "EST" | "-0500" => FixedOffset::east_opt(-5 * 60 * 60).unwrap(), - "UTC0" | "+0000" => FixedOffset::east_opt(0).unwrap(), - tz => panic!("unexpected TZ {}", tz), - }; - assert_eq!( - &expected_offset, - local.offset(), - "expected: {:?} local: {:?}", - expected_offset, - local.offset(), - ); -} - -#[wasm_bindgen_test] -fn from_is_exact() { - let now = js_sys::Date::new_0(); - - let dt = DateTime::::from(now.clone()); - - assert_eq!(now.get_time() as i64, dt.timestamp_millis()); -} - -#[wasm_bindgen_test] -fn local_from_local_datetime() { - let now = Local::now(); - let ndt = now.naive_local(); - let res = match Local.from_local_datetime(&ndt).single() { - Some(v) => v, - None => panic! {"Required for test!"}, - }; - assert_eq!(now, res); -} - -#[wasm_bindgen_test] -fn convert_all_parts_with_milliseconds() { - let time: DateTime = "2020-12-01T03:01:55.974Z".parse().unwrap(); - let js_date = js_sys::Date::from(time); - - assert_eq!(js_date.get_utc_full_year(), 2020); - assert_eq!(js_date.get_utc_month(), 11); // months are numbered 0..=11 - assert_eq!(js_date.get_utc_date(), 1); - assert_eq!(js_date.get_utc_hours(), 3); - assert_eq!(js_date.get_utc_minutes(), 1); - assert_eq!(js_date.get_utc_seconds(), 55); - assert_eq!(js_date.get_utc_milliseconds(), 974); -} diff --git a/chrono-0.4.44/tests/win_bindings.rs b/chrono-0.4.44/tests/win_bindings.rs deleted file mode 100644 index 6a38dcecba..0000000000 --- a/chrono-0.4.44/tests/win_bindings.rs +++ /dev/null @@ -1,39 +0,0 @@ -use std::fs; -use windows_bindgen::bindgen; - -#[test] -fn gen_bindings() { - let existing = fs::read_to_string(BINDINGS).unwrap(); - - bindgen([ - "--out", - BINDINGS, - "--flat", - "--no-comment", - "--no-deps", - "--sys", - "--filter", - "GetTimeZoneInformationForYear", - "SystemTimeToFileTime", - "SystemTimeToTzSpecificLocalTime", - "TzSpecificLocalTimeToSystemTime", - ]) - .unwrap(); - - // Check the output is the same as before. - // Depending on the git configuration the file may have been checked out with `\r\n` newlines or - // with `\n`. Compare line-by-line to ignore this difference. - let mut new = fs::read_to_string(BINDINGS).unwrap(); - if existing.contains("\r\n") && !new.contains("\r\n") { - new = new.replace("\n", "\r\n"); - } else if !existing.contains("\r\n") && new.contains("\r\n") { - new = new.replace("\r\n", "\n"); - } - - similar_asserts::assert_eq!(existing, new); - if !new.lines().eq(existing.lines()) { - panic!("generated file `{BINDINGS}` is changed."); - } -} - -const BINDINGS: &str = "src/offset/local/win_bindings.rs"; diff --git a/chumsky-0.12.0/.cargo-checksum.json b/chumsky-0.12.0/.cargo-checksum.json deleted file mode 100644 index a42f288866..0000000000 --- a/chumsky-0.12.0/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{".cargo_vcs_info.json":"02fdccd290345a0b0387a277718d16dba4ac02a39f93995a0cb8e00903742caa",".github/FUNDING.yml":"ab18b2d04a647da2d2e56121974f3de4db30e8b05b1cb4006e204a9928acb064",".github/workflows/rust.yml":"1199b651fe9b28ce8ab6920107fa16d09d6a3fe2da1cae663c13b20e358cb21e","CHANGELOG.md":"ecc29796f74c4afa7550c558dcaeb09c56219a383144c5c195bfc5a728cb090a","Cargo.lock":"a27f232e2b32574c4f4d05c06d795d90a04ede033882eef06e4141a3bcf79c6a","Cargo.toml":"c3fef02fffb4c79b4338bb317f1b1c833d744513a39c0a3ce78f6cba7105b66c","Cargo.toml.orig":"4bc71823f592ef6e4b19482dd7b1801122975dda738ac20dfd200ec941092410","LICENSE":"88f7ddf73afcffee97e0a19211ddeecd7d178ecb5c09bbfe472ce4cfcceb6269","README.md":"ec266a14705248b57506b9c134c6cd8b141cfed761d7b39ef1b3336b73fa0e53","benches/backtrack.rs":"d207b4c1baabe016ddf7265831a262287d5647de99b24a4b318b639d04eeb7a0","benches/cbor.rs":"4686a25bb9c7b4523e99f1b23e3984a104b2f8f2c50188deab2f7ff82eaf151d","benches/json.pest":"d0b95c1b222cea4b3f668a55138ce50ba914e04189f4e4aa2641b6f94f3b127c","benches/json.rs":"9972b8cd9c57cf3a559acbf9d44ee2cb37e864cced6765bd48789ab9c165be1b","benches/lex.rs":"0b29cc8c2c3e76bf8feefb1acda9a5e69ed5a707a4d4c2e966b1c4229ff960dd","benches/parser.rs":"b7bfdc1535c486734df36238e41165ef2a446c6d7cde59e5fc4c905604ec88bd","benches/tokens.txt":"2f77148de6ac2840498b94fb7383eb75ba57d6848467221fca0b71e190dacdd2","benches/utils.rs":"bb589553bcd7b45bb841f70003d7194c309837b261c05d721f195d39b0c0ab00","examples/brainfuck.rs":"fab968d94d69bc62dc5aa33f914ab527360c70052829cf09345ab99543baf9ee","examples/debug.rs":"110cb07876bb86dd2610016ad4e5faedee92bf1806ad5428926b70531f4ac491","examples/foo.rs":"0be0f8351ef8a6b3e85f0690993a89250579e5d5c11655c066b561fc67eb5905","examples/indent.rs":"2a4b3192ab7fb938a4e5e6e7421cc74e514077be7df39922f238b744f08a18c1","examples/io.rs":"709c019a58729d09c2e5a7982b660f1b0facc0f3f8e935fda208f98ef471dde1","examples/json.rs":"1c926d20b2420bba40dfe912d30edc97acd449f3c1026b5a0e943a47d03feb94","examples/json_fast.rs":"0a378cc447b21679b4eabfc879276c32e312c6ebf881f6f77d37298ae7ad32f7","examples/logos.rs":"7a10b8b38f75157dfd2cf02beaae42908b35b3f19ba69a03a334c7e309a03161","examples/mini_ml.rs":"aaa4d1101c84e8dacb03ad7b151ec2dfe44310582e102f924ef1fed715cb4e62","examples/nano_rust.rs":"9a198a357e855eee1ad3320334604ef99468f57a4a57ed2390fa8e8c3c43145f","examples/nested.rs":"8302cf06d960bcd04f4060d1736a390cf063cb7604714e97b95c69b75a8d8622","examples/nested_spans.rs":"83980009a4e11c851b0a6d9771d40f589e48a9294e98c195119fd4a7a317f90c","examples/pythonic.rs":"18a98bf6f3ee17e58a1a3da7affe099da08d8ecc9a980c9a278ec74f9ff9c363","examples/sample.bf":"824a423496b3847b635d8da95d183867115318e671f731149c3603c732dbed77","examples/sample.foo":"f0c3345067c4716498e0c37d142254c6eb8edbb600b3fedd16a90c1031a6fbdf","examples/sample.io":"f4861ac3504a1c932b40367dfe5e1d7ac71a89a2064402a3a233b9fabaa7db10","examples/sample.json":"e79d3d751fe16760274b5ff22c6463aaaade108b6bd2a54af588489b88f809f7","examples/sample.mini_ml":"6890c6368493676316c9eecd9ba324de33325bfdae5a14aa6ff6a35a4fda7a03","examples/sample.nrs":"e62d5df3a6b54b1fd5efdc531731e984eda7b81b3067c8f561b372832309520e","examples/sample.py":"d6a788dd91e4f8d9083826583d503d8b3eea13f8aa5d9d713e10123cb12e48c4","examples/zero-copy.rs":"07f009af5e3bc0bd1c72a5cf60ecd37fc4bd0fb066532d6bee3b90bdb4d4187d","guide/README.md":"ea7239ec6cdc5d3b1af180b4f5b6a5a00e157da9e38d4a76750afac9062708b8","guide/debugging.md":"6de7f0412900caa8ddf796a1dbe36c7de4eb8d6027e383c19c102bcce7e6cc0c","guide/error_and_recovery.md":"c989eee148bd74c8f19a89c1450b17831593f981045a09266cd6a98ab5d173ef","guide/getting_started.md":"eafe91f99c8d0855b7d082d14a2f4f3008f8c25a4f45fb42011919fd12cc463c","guide/intro.md":"b6cbfc87a3ca126b18b95f71d0c8fa32aa980f49ffe7eab65e94bd22a9d73feb","guide/key_concepts.md":"8e17f015a8a5f8cd9f3dee878ffa7ee5c694fdac96b35ca60757df3e5c22996d","guide/meet_the_parsers.md":"b6fb09022fa75cacf8ca8eb6f4eaf49aae6f2f580ae17caae7a4fa44e7b9cd4c","guide/recursion.md":"8d9cfe4eb9c50eae504ee738f6248e0878044a6a444cbd76f08af2bce44953b7","guide/technical_notes.md":"9001ebbf771adba9338c331887e524d57933fd9d8dcbb4c53006d0af5888320f","guide/tutorial.md":"01ec5d701b902414ee72e0a7fbd16d962c5aae5eeebc033dd3cc601299799a10","src/blanket.rs":"9800a90e4727b724b6fd56801975d7edae7bcc675e900bb878e418d845b56af1","src/cache.rs":"959d9b1972cab9395b177c267e6f3d75e3927a9aab49c19ec17bf7b1dcc3f6fe","src/combinator.rs":"ad5f6ccca5474083461f66f9cd5ab6d9a50c8e2d69697968f7f776c97ab961eb","src/container.rs":"bbb592b5bcd666cf692ca263cb1b5d60a1e952b1f71b6bc7ddb6ef339e8be264","src/debug.rs":"cc5b462fdef97e3227a825ef277643be78e4d3f3bbc10f6bf0a4be7454b573fb","src/either.rs":"1a6a27e22083343d72cfc835706991a02d077b494958c16403907b77cf4955f0","src/error.rs":"50279b6f37d849e9f9d77c62bf50e89f9e48cd5c738b54c6cb8bc14db1c014c8","src/extension.rs":"d6ae5423ff4d4d0e821e2f14ce5cc4a10529727aea6bd49a3226f5a9abc92dba","src/extra.rs":"08e645ec8e2f5fd26a707c0e25ed1345c2d5b01218faf0ada7a588a6a7a819b2","src/guide.rs":"c36df23f057cc129e3f0331794790964d9dfa5cc42f6409ffac58bfccab6d78b","src/input.rs":"9a39cd537f7562d3fa14c1cd0cdbe30674a1663930a1ebe409de5a60dbf15538","src/inspector.rs":"343dd9d28c7e50dad0342a9a6919865ab733c6098d86f422b93fe2fb104291b2","src/label.rs":"02357e6f7fd837d766bde0e5ba41a74a7a50a8deac422893c7cafe74fdd8b030","src/lib.rs":"76677259500c19cd21df4452861fb773c8481fee13ed34503ec513aa94660c5a","src/number.rs":"95de9b867beebbfb1a1f0cb16d70548fb0b74db5a6f6195c0f7021a2f09393ca","src/pratt.rs":"2f111830f2dc34eeeed2d6d3cda2a3942544890c1ca5a9646188f7680c893ce8","src/primitive.rs":"3162793b3b9c8fabcc8f3c8639f7bae153d6c3c77b7990ccee57ee76e55f8b95","src/private.rs":"5a95aa6646e094ca87aae438962c3eaa9dfcbe45e641824d4f9e8d3bb8b7d60d","src/recovery.rs":"1d010882b1ded2c2ca486135074ebe18498dd3dd2b03e864727dbbc1a993140d","src/recursive.rs":"9d23ff4ec2bdb6ca9cd7d47631f563d9b2519ace2e66427fb18e9609330ff5a5","src/regex.rs":"ccf3db8c8f33ca7b1a1b6b90c7ef23c1cf02160d33f51d686da49124c27a955e","src/span.rs":"847c7eb130e2353df8f304bfe2d7e250f1c7cb33cd1277e513fe492a9f7de139","src/stream.rs":"016c177306f3de014cb52e317446a5487dfad4fa68e7f5c37b158a02bace91b5","src/text.rs":"fffff056baace54aa74cb33014ec10016559bf9381997429094dfbb54a15145e","src/tokio.rs":"6862256c15ec39c86fe72f7a08ad65b327c3668819e4b845617156a2aeda7557","src/util.rs":"f33d933ca2ca4780236fdc9cdf3d4521600ab717a2e696a38bdc77dd229aa61c"},"package":"4ba4a05c9ce83b07de31b31c874e87c069881ac4355db9e752e3a55c11ec75a6"} \ No newline at end of file diff --git a/chumsky-0.12.0/.cargo_vcs_info.json b/chumsky-0.12.0/.cargo_vcs_info.json deleted file mode 100644 index 25c0f5229b..0000000000 --- a/chumsky-0.12.0/.cargo_vcs_info.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "git": { - "sha1": "c727d07d93dbc523124bca497360f7fcd86131fb" - }, - "path_in_vcs": "" -} \ No newline at end of file diff --git a/chumsky-0.12.0/.github/workflows/rust.yml b/chumsky-0.12.0/.github/workflows/rust.yml deleted file mode 100644 index a5418a70fa..0000000000 --- a/chumsky-0.12.0/.github/workflows/rust.yml +++ /dev/null @@ -1,73 +0,0 @@ -name: Rust - -on: - push: - branches: [ main ] - pull_request: - branches: [ main ] - -env: - CARGO_TERM_COLOR: always - -permissions: read-all - -jobs: - check: - name: Check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Install latest nightly - uses: dtolnay/rust-toolchain@master - with: - toolchain: nightly - components: rustfmt, clippy - - name: Run cargo check (all features) - run: cargo check --benches --examples --tests --all-features - - name: Run cargo check (no features) - run: cargo check --benches --examples --tests --no-default-features - - name: Run cargo check (release, all features) - run: cargo check --release --benches --examples --tests --all-features - - name: Run cargo clippy - run: cargo clippy --benches --examples --tests --all-features -- -D warnings - - name: Run cargo fmt - run: cargo fmt --check - - name: Run cargo doc - run: cargo doc --all-features - #env: - # RUSTDOCFLAGS: --cfg docsrs - test: - name: Test - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Install latest nightly - uses: dtolnay/rust-toolchain@master - with: - toolchain: nightly - components: rustfmt, clippy - - name: Run cargo test - run: cargo test --all-features - env: - RUSTDOCFLAGS: --cfg docsrs - msrv: - name: MSRV - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Install MSRV - uses: dtolnay/rust-toolchain@master - with: - toolchain: "1.65" - components: rustfmt, clippy - - name: Check MSRV compatibility - run: cargo check --features _test_stable - semver: - name: SemVer - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Check semver compatibility - uses: obi1kenobi/cargo-semver-checks-action@v2 - with: - rust-toolchain: stable diff --git a/chumsky-0.12.0/CHANGELOG.md b/chumsky-0.12.0/CHANGELOG.md deleted file mode 100644 index 14bc56e033..0000000000 --- a/chumsky-0.12.0/CHANGELOG.md +++ /dev/null @@ -1,286 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -# Unreleased - -### Added - -### Removed - -### Changed - -### Fixed - -# [0.12.0] - 2025-12-15 - -### Added - -- `MapExtra::emit`, which allows emitting secondary errors during mapping operations -- `InputRef::emit`, which allows emitting secondary errors within `custom` parsers -- `labelled_with`, which avoids the need to implement `Clone` for labels -- `Input::split_token_span`, a convenience function for splitting `(Token, Span)` inputs so that chumsky can understand them -- `Input::split_spanned`, which does the same as above, but for implementors of `WrappingSpan` -- `spanned`, a combinator which automatically annotates a parser output with a span -- Experimental: - - `IterParser::parse_iter`, a way to turn an `IterParser` (like `x.repeated()`) into an iterator - - `Parser::debug`, which provides access to various parser debugging utilities. - -### Changed - -- Made `nested_in` more flexible by allowing it to map between different input types instead of requiring the same input as the outer parser - -### Fixed - -- A prioritisation bug with `nested_in` - -# [0.11.2] - 2025-11-05 - -### Added - -- Implement `Default`, `PartialOrd`, and `Ord` for `SimpleSpan` -- Implement `PartialOrd` and `Ord` for `Rich` - -# [0.11.1] - 2025-09-12 - -### Fixed - -- Patched compilation error that only appeared in release builds - -# [0.11.0] - 2025-09-11 - -### Added - -- The `set(...)` combinator, which may be used to conveniently parse a set of patterns, in any order -- Support for non-associative infix operators are now supported by the `.pratt(...)` combinator -- `Parser::try_foldl`, allowing folding operations to short-circuit in a manner similar to `Parser::try_map` -- `Parser::contextual`, which allows a parser to be disabled or enabled in a context-sensitive manner -- Implemented `ValueInput` for `IterInput`, which was previously missing -- More types that implement the `State` trait: - - `RollbackState`, which reverts parser state when a parser rewinds and may be used to, for example, count the number of times a pattern appears in the input - - `TruncateState`, which truncates a vector when a parser rewinds, and may be used to implement an arena allocator for AST nodes -- Implemented `IterParser` for `a.then(b)` when both `a` and `b` are both `IterParser`s that produce the same output type - -### Changed - -- Made lifetime bounds on `recursive` and `ParserExtra` more permissive -- Improved support for grapheme parsing -- Text parsers now report labels -- `Parser::filter` now generates a `DefaultExpected::SomethingElse` label instead of nothing (this can be overridden with the `.labelled(...)` function) -- Improved areas of documentation -- Make whitespace parsers reject codepoints that are vulnerable to [CVE-2021-42574](https://www.cve.org/CVERecord?id=CVE-2021-42574) -- Maybe the `select!` parser more permissive, accepting any implementor of `Input` instead of requiring `ValueInput` too - -### Fixed - -- Many minor incorrect debug-only sanity checks have been fixed -- Many minor span and error prioritisation behavioural problems have been fixed (most notably, `Parser::try_map`) -- The `.rewind()` parser no longer rewinds any secondary error that were encountered -- Accidental `text::ascii::keyword` lifetime regression - -# [0.10.1] - 2025-04-13 - -### Added - -- Implemented `Container` for `VecDeque` -- New section covering recursion in the guide - -### Changed - -- `Boxed` types now have a default type parameter of `extra::Default`, like `Parser` and `IterParser` -- The tutorial has been updated for `0.10` and has been moved to the guide - -### Fixed - -- Nonsense spans occasionally generated for non-existent tokens -- Improved docs have been added for several items -- Many minor documentation issues have been fixed - -# [0.10.0] - 2025-03-22 - -*Note: version 0.10 is a from-scratch rewrite of chumsky with innumerable small changes. To avoid this changelog being -longer than the compiled works of Douglas Adams, the following is a high-level overview of the major feature additions -and does not include small details.* - -### Added - -- Support for zero-copy parsing (i.e: parser outputs that hold references to the parser input) -- Support for parsing nested inputs like token trees -- Support for parsing context-sensitive grammars such as Python-style indentation, Rust-style raw strings, and much -more -- Support for parsing by graphemes as well as unicode codepoints -- Support for caching parsers independent of the lifetime of the parser -- A new trait, `IterParser`, that allows expressing parsers that generate many outputs -- Added the ability to collect iterable parsers into fixed-size arrays, along with a plethora of other container types -- Support for manipulating shared state during parsing, elegantly allowing support for arena allocators, cstrees, -interners, and much more -- Support for a vast array of new input types: slices, strings, arrays, `impl Read`ers, iterators, etc. -- Experimental support for memoization, allowing chumsky to parse left-recursive grammars and reducing the -computational complexity of parsing certain grammars -- An extension API, allowing third-party crates to extend chumsky's capabilities and introduce new combinators -- A `pratt` parser combinator, allowing for conveniently and simply creating expression parsers with precise operator -precedence -- A `regex` combinator, allowing the parsing of terms based on a specific regex pattern -- Properly differentiated ASCII and Unicode text parsers - -## Removed - -- `Parser::then_with` has been removed in favour of the new context-sensitive combinators - -### Changed - -- Performance has *radically* improved -- Error generation and handling is now significantly more flexible - -# [0.9.2] - 2023-03-02 - -### Fixed - -- Properly fixed `skip_then_retry_until` regression - -# [0.9.1] - 2023-03-02 - -### Fixed - -- Regression in `skip_then_retry_until` recovery strategy - -# [0.9.0] - 2023-02-07 - -### Added - -- A `spill-stack` feature that uses `stacker` to avoid stack overflow errors for deeply recursive parsers -- The ability to access the token span when using `select!` like `select! { |span| Token::Num(x) => (x, span) }` -- Added a `skip_parser` recovery strategy that allows you to implement your own recovery strategies in terms of other - parsers. For example, `.recover_with(skip_parser(take_until(just(';'))))` skips tokens until after the next semicolon -- A `not` combinator that consumes a single token if it is *not* the start of a given pattern. For example, - `just("\\n").or(just('"')).not()` matches any `char` that is not either the final quote of a string, and is not the - start of a newline escape sequence -- A `semantic_indentation` parser for parsing indentation-sensitive languages. Note that this is likely to be - deprecated/removed in the future in favour of a more powerful solution -- `#[must_use]` attribute for parsers to ensure that they're not accidentally created without being used -- `Option>` and `Vec>` now implement `Chain` and `Option` implements `Chain` -- `choice` now supports both arrays and vectors of parsers in addition to tuples -- The `Simple` error type now implements `Eq` - -### Changed - -- `text::whitespace` returns a `Repeated` instead of an `impl Parser`, allowing you to call methods like `at_least` and - `exactly` on it. -- Improved `no_std` support -- Improved examples and documentation -- Use zero-width spans for EoI by default -- Don't allow defining a recursive parser more than once -- Various minor bug fixes -- Improved `Display` implementations for various built-in error types and `SimpleReason` -- Use an `OrderedContainer` trait to avoid unexpected behaviour for unordered containers in combination with `just` - -### Fixed - -- Made several parsers (`todo`, `unwrapped`, etc.) more useful by reporting the parser's location on panic -- Boxing a parser that is already boxed just gives you the original parser to avoid double indirection -- Improved compilation speeds - -# [0.8.0] - 2022-02-07 - -### Added - -- `then_with` combinator to allow limited support for parsing nested patterns -- impl From<&[T; N]> for Stream -- `SkipUntil/SkipThenRetryUntil::skip_start/consume_end` for more precise control over skip-based recovery - -### Changed - -- Allowed `Validate` to map the output type -- Switched to zero-size End Of Input spans for default implementations of `Stream` -- Made `delimited_by` take combinators instead of specific tokens -- Minor optimisations -- Documentation improvements - -### Fixed - -- Compilation error with `--no-default-features` -- Made default behaviour of `skip_until` more sensible - -# [0.7.0] - 2021-12-16 - -### Added - -- A new [tutorial](tutorial.md) to help new users - -- `select` macro, a wrapper over `filter_map` that makes extracting data from specific tokens easy -- `choice` parser, a better alternative to long `or` chains (which sometimes have poor compilation performance) -- `todo` parser, that panics when used (but not when created) (akin to Rust's `todo!` macro, but for parsers) -- `keyword` parser, that parses *exact* identifiers - -- `from_str` combinator to allow converting a pattern to a value inline, using `std::str::FromStr` -- `unwrapped` combinator, to automatically unwrap an output value inline -- `rewind` combinator, that allows reverting the input stream on success. It's most useful when requiring that a - pattern is followed by some terminating pattern without the first parser greedily consuming it -- `map_err_with_span` combinator, to allow fetching the span of the input that was parsed by a parser before an error - was encountered - -- `or_else` combinator, to allow processing and potentially recovering from a parser error -- `SeparatedBy::at_most` to require that a separated pattern appear at most a specific number of times -- `SeparatedBy::exactly` to require that a separated pattern be repeated exactly a specific number of times -- `Repeated::exactly` to require that a pattern be repeated exactly a specific number of times - -- More trait implementations for various things, making the crate more useful - -### Changed - -- Made `just`, `one_of`, and `none_of` significant more useful. They can now accept strings, arrays, slices, vectors, - sets, or just single tokens as before -- Added the return type of each parser to its documentation -- More explicit documentation of parser behaviour -- More doc examples -- Deprecated `seq` (`just` has been generalised and can now be used to parse specific input sequences) -- Sealed the `Character` trait so that future changes are not breaking -- Sealed the `Chain` trait and made it more powerful -- Moved trait constraints on `Parser` to where clauses for improved readability - -### Fixed - -- Fixed a subtle bug that allowed `separated_by` to parse an extra trailing separator when it shouldn't -- Filled a 'hole' in the `Error` trait's API that conflated a lack of expected tokens with expectation of end of input -- Made recursive parsers use weak reference-counting to avoid memory leaks - -# [0.6.0] - 2021-11-22 - -### Added - -- `skip_until` error recovery strategy -- `SeparatedBy::at_least` and `SeparatedBy::at_most` for parsing a specific number of separated items -- `Parser::validate` for integrated AST validation -- `Recursive::declare` and `Recursive::define` for more precise control over recursive declarations - -### Changed - -- Improved `separated_by` error messages -- Improved documentation -- Hid a new (probably) unused implementation details - -# [0.5.0] - 2021-10-30 - -### Added - -- `take_until` primitive - -### Changed - -- Added span to fallback output function in `nested_delimiters` - -# [0.4.0] - 2021-10-28 - -### Added - -- Support for LL(k) parsing -- Custom error recovery strategies -- Debug mode -- Nested input flattening - -### Changed - -- Radically improved error quality diff --git a/chumsky-0.12.0/Cargo.lock b/chumsky-0.12.0/Cargo.lock deleted file mode 100644 index 8fdb991cba..0000000000 --- a/chumsky-0.12.0/Cargo.lock +++ /dev/null @@ -1,2068 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "ahash" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" -dependencies = [ - "cfg-if", - "getrandom", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - -[[package]] -name = "anes" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" - -[[package]] -name = "ar_archive_writer" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c269894b6fe5e9d7ada0cf69b5bf847ff35bc25fc271f08e1d080fce80339a" -dependencies = [ - "object 0.32.2", -] - -[[package]] -name = "ariadne" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f5e3dca4e09a6f340a61a0e9c7b61e030c69fc27bf29d73218f7e5e3b7638f" -dependencies = [ - "unicode-width 0.1.14", - "yansi", -] - -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "backtrace" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object 0.37.3", - "rustc-demangle", - "windows-link", -] - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "beef" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bstr" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" -dependencies = [ - "memchr", - "regex-automata 0.4.13", - "serde", -] - -[[package]] -name = "bumpalo" -version = "3.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" - -[[package]] -name = "bytemuck" -version = "1.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" - -[[package]] -name = "byteorder-lite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" - -[[package]] -name = "bytes" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" - -[[package]] -name = "cast" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" - -[[package]] -name = "cc" -version = "1.2.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" -dependencies = [ - "find-msvc-tools", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "chumsky" -version = "0.12.0" -dependencies = [ - "ariadne", - "bytes", - "ciborium", - "criterion", - "either", - "hashbrown 0.15.5", - "lasso", - "lexical", - "logos", - "nom 7.1.3", - "nom 8.0.0", - "pest", - "pest_derive", - "pom", - "pprof", - "railroad", - "regex-automata 0.3.9", - "serde", - "serde_json", - "slotmap", - "sn", - "spin", - "stacker", - "unicode-ident", - "unicode-segmentation", - "winnow", -] - -[[package]] -name = "ciborium" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" -dependencies = [ - "ciborium-io", - "ciborium-ll", - "serde", -] - -[[package]] -name = "ciborium-io" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" - -[[package]] -name = "ciborium-ll" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" -dependencies = [ - "ciborium-io", - "half", -] - -[[package]] -name = "clap" -version = "3.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" -dependencies = [ - "bitflags 1.3.2", - "clap_lex", - "indexmap 1.9.3", - "textwrap", -] - -[[package]] -name = "clap_lex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - -[[package]] -name = "core_maths" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" -dependencies = [ - "libm", -] - -[[package]] -name = "cpp_demangle" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2bb79cb74d735044c972aae58ed0aaa9a837e85b01106a54c39e42e97f62253" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "criterion" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" -dependencies = [ - "anes", - "atty", - "cast", - "ciborium", - "clap", - "criterion-plot", - "itertools", - "lazy_static", - "num-traits", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_derive", - "serde_json", - "tinytemplate", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" -dependencies = [ - "cast", - "itertools", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crunchy" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" - -[[package]] -name = "crypto-common" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "data-url" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" - -[[package]] -name = "debugid" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" -dependencies = [ - "uuid", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "euclid" -version = "0.22.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48" -dependencies = [ - "num-traits", -] - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - -[[package]] -name = "fdeflate" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "find-msvc-tools" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" - -[[package]] -name = "findshlibs" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" -dependencies = [ - "cc", - "lazy_static", - "libc", - "winapi", -] - -[[package]] -name = "flate2" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "float-cmp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "fontconfig-parser" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" -dependencies = [ - "roxmltree", -] - -[[package]] -name = "fontdb" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" -dependencies = [ - "fontconfig-parser", - "log", - "memmap2 0.9.9", - "slotmap", - "tinyvec", - "ttf-parser", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasip2", -] - -[[package]] -name = "gif" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" -dependencies = [ - "color_quant", - "weezl", -] - -[[package]] -name = "gimli" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" - -[[package]] -name = "half" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" -dependencies = [ - "cfg-if", - "crunchy", - "zerocopy", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", - "allocator-api2", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash", -] - -[[package]] -name = "hashbrown" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - -[[package]] -name = "image-webp" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" -dependencies = [ - "byteorder-lite", - "quick-error", -] - -[[package]] -name = "imagesize" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "indexmap" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" -dependencies = [ - "equivalent", - "hashbrown 0.16.0", -] - -[[package]] -name = "inferno" -version = "0.11.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "232929e1d75fe899576a3d5c7416ad0d88dbfbb3c3d6aa00873a7408a50ddb88" -dependencies = [ - "ahash", - "indexmap 2.12.0", - "is-terminal", - "itoa", - "log", - "num-format", - "once_cell", - "quick-xml", - "rgb", - "str_stack", -] - -[[package]] -name = "is-terminal" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" -dependencies = [ - "hermit-abi 0.5.2", - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" - -[[package]] -name = "js-sys" -version = "0.3.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "kurbo" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62" -dependencies = [ - "arrayvec", - "euclid", - "smallvec", -] - -[[package]] -name = "lasso" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e14eda50a3494b3bf7b9ce51c52434a761e383d7238ce1dd5dcec2fbc13e9fb" -dependencies = [ - "hashbrown 0.14.5", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "lexical" -version = "6.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7aefb36fd43fef7003334742cbf77b243fcd36418a1d1bdd480d613a67968f6" -dependencies = [ - "lexical-core", -] - -[[package]] -name = "lexical-core" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46" -dependencies = [ - "lexical-parse-float", - "lexical-parse-integer", - "lexical-util", - "lexical-write-float", - "lexical-write-integer", -] - -[[package]] -name = "lexical-parse-float" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f" -dependencies = [ - "lexical-parse-integer", - "lexical-util", - "static_assertions", -] - -[[package]] -name = "lexical-parse-integer" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9" -dependencies = [ - "lexical-util", - "static_assertions", -] - -[[package]] -name = "lexical-util" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc" -dependencies = [ - "static_assertions", -] - -[[package]] -name = "lexical-write-float" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accabaa1c4581f05a3923d1b4cfd124c329352288b7b9da09e766b0668116862" -dependencies = [ - "lexical-util", - "lexical-write-integer", - "static_assertions", -] - -[[package]] -name = "lexical-write-integer" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b6f3d1f4422866b68192d62f77bc5c700bee84f3069f2469d7bc8c77852446" -dependencies = [ - "lexical-util", - "static_assertions", -] - -[[package]] -name = "libc" -version = "0.2.177" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" - -[[package]] -name = "libm" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" - -[[package]] -name = "linux-raw-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" - -[[package]] -name = "logos" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c000ca4d908ff18ac99b93a062cb8958d331c3220719c52e77cb19cc6ac5d2c1" -dependencies = [ - "logos-derive", -] - -[[package]] -name = "logos-codegen" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc487311295e0002e452025d6b580b77bb17286de87b57138f3b5db711cded68" -dependencies = [ - "beef", - "fnv", - "proc-macro2", - "quote", - "regex-syntax 0.6.29", - "syn", -] - -[[package]] -name = "logos-derive" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbfc0d229f1f42d790440136d941afd806bc9e949e2bcb8faa813b0f00d1267e" -dependencies = [ - "logos-codegen", -] - -[[package]] -name = "memchr" -version = "2.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" - -[[package]] -name = "memmap2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" -dependencies = [ - "libc", -] - -[[package]] -name = "memmap2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" -dependencies = [ - "libc", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "nix" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", -] - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "nom" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" -dependencies = [ - "memchr", -] - -[[package]] -name = "num-format" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" -dependencies = [ - "arrayvec", - "itoa", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "object" -version = "0.32.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" -dependencies = [ - "memchr", -] - -[[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "oorandom" -version = "11.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" - -[[package]] -name = "os_str_bytes" -version = "6.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link", -] - -[[package]] -name = "pest" -version = "2.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e7521a040efde50c3ab6bbadafbe15ab6dc042686926be59ac35d74607df4" -dependencies = [ - "memchr", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187da9a3030dbafabbbfb20cb323b976dc7b7ce91fcd84f2f74d6e31d378e2de" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b401d98f5757ebe97a26085998d6c0eecec4995cad6ab7fc30ffdf4b052843" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pest_meta" -version = "2.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f27a2cfee9f9039c4d86faa5af122a0ac3851441a34865b8a043b46be0065a" -dependencies = [ - "pest", - "sha2", -] - -[[package]] -name = "pico-args" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" - -[[package]] -name = "plotters" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" - -[[package]] -name = "plotters-svg" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" -dependencies = [ - "plotters-backend", -] - -[[package]] -name = "png" -version = "0.17.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "pom" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c972d8f86e943ad532d0b04e8965a749ad1d18bb981a9c7b3ae72fe7fd7744b" -dependencies = [ - "bstr", -] - -[[package]] -name = "pprof" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196ded5d4be535690899a4631cc9f18cdc41b7ebf24a79400f46f48e49a11059" -dependencies = [ - "backtrace", - "cfg-if", - "criterion", - "findshlibs", - "inferno", - "libc", - "log", - "nix", - "once_cell", - "parking_lot", - "smallvec", - "symbolic-demangle", - "tempfile", - "thiserror", -] - -[[package]] -name = "proc-macro2" -version = "1.0.103" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "psm" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d11f2fedc3b7dafdc2851bc52f277377c5473d378859be234bc7ebb593144d01" -dependencies = [ - "ar_archive_writer", - "cc", -] - -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - -[[package]] -name = "quick-xml" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" -dependencies = [ - "memchr", -] - -[[package]] -name = "quote" -version = "1.0.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "railroad" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5b8e8a7c20c600f9b98cbf46b64e63d5c9e69deb98cee1ff264de9f1dda5d" -dependencies = [ - "resvg", - "unicode-width 0.2.2", -] - -[[package]] -name = "rayon" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.10.0", -] - -[[package]] -name = "regex" -version = "1.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.4.13", - "regex-syntax 0.8.8", -] - -[[package]] -name = "regex-automata" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.7.5", -] - -[[package]] -name = "regex-automata" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.8.8", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" - -[[package]] -name = "regex-syntax" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" - -[[package]] -name = "resvg" -version = "0.45.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8928798c0a55e03c9ca6c4c6846f76377427d2c1e1f7e6de3c06ae57942df43" -dependencies = [ - "gif", - "image-webp", - "log", - "pico-args", - "rgb", - "svgtypes", - "tiny-skia", - "usvg", - "zune-jpeg", -] - -[[package]] -name = "rgb" -version = "0.8.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "roxmltree" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" - -[[package]] -name = "rustc-demangle" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" - -[[package]] -name = "rustix" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" -dependencies = [ - "bitflags 2.10.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "rustybuzz" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" -dependencies = [ - "bitflags 2.10.0", - "bytemuck", - "core_maths", - "log", - "smallvec", - "ttf-parser", - "unicode-bidi-mirroring", - "unicode-ccc", - "unicode-properties", - "unicode-script", -] - -[[package]] -name = "ryu" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.145" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" -dependencies = [ - "indexmap 2.12.0", - "itoa", - "memchr", - "ryu", - "serde", - "serde_core", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "simd-adler32" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "simplecss" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" -dependencies = [ - "log", -] - -[[package]] -name = "siphasher" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" - -[[package]] -name = "slotmap" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" -dependencies = [ - "version_check", -] - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - -[[package]] -name = "sn" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fab6ae84ee9505d8786b19c3a1c68e70431d8465a835d4c59dd4a843884647b" - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "stacker" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1f8b29fb42aafcea4edeeb6b2f2d7ecd0d969c48b4cf0d2e64aafc471dd6e59" -dependencies = [ - "cc", - "cfg-if", - "libc", - "psm", - "windows-sys 0.59.0", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "str_stack" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" - -[[package]] -name = "strict-num" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" -dependencies = [ - "float-cmp", -] - -[[package]] -name = "svgtypes" -version = "0.15.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" -dependencies = [ - "kurbo", - "siphasher", -] - -[[package]] -name = "symbolic-common" -version = "10.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b55cdc318ede251d0957f07afe5fed912119b8c1bc5a7804151826db999e737" -dependencies = [ - "debugid", - "memmap2 0.5.10", - "stable_deref_trait", - "uuid", -] - -[[package]] -name = "symbolic-demangle" -version = "10.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79be897be8a483a81fff6a3a4e195b4ac838ef73ca42d348b3f722da9902e489" -dependencies = [ - "cpp_demangle", - "rustc-demangle", - "symbolic-common", -] - -[[package]] -name = "syn" -version = "2.0.110" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tempfile" -version = "3.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" -dependencies = [ - "fastrand", - "getrandom", - "once_cell", - "rustix", - "windows-sys 0.61.2", -] - -[[package]] -name = "textwrap" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tiny-skia" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" -dependencies = [ - "arrayref", - "arrayvec", - "bytemuck", - "cfg-if", - "log", - "png", - "tiny-skia-path", -] - -[[package]] -name = "tiny-skia-path" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" -dependencies = [ - "arrayref", - "bytemuck", - "strict-num", -] - -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "ttf-parser" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" -dependencies = [ - "core_maths", -] - -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" - -[[package]] -name = "ucd-trie" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" - -[[package]] -name = "unicode-bidi" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" - -[[package]] -name = "unicode-bidi-mirroring" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe" - -[[package]] -name = "unicode-ccc" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e" - -[[package]] -name = "unicode-ident" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" - -[[package]] -name = "unicode-properties" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" - -[[package]] -name = "unicode-script" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb421b350c9aff471779e262955939f565ec18b86c15364e6bdf0d662ca7c1f" - -[[package]] -name = "unicode-segmentation" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "unicode-vo" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" - -[[package]] -name = "unicode-width" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - -[[package]] -name = "unicode-width" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" - -[[package]] -name = "usvg" -version = "0.45.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80be9b06fbae3b8b303400ab20778c80bbaf338f563afe567cf3c9eea17b47ef" -dependencies = [ - "base64", - "data-url", - "flate2", - "fontdb", - "imagesize", - "kurbo", - "log", - "pico-args", - "roxmltree", - "rustybuzz", - "simplecss", - "siphasher", - "strict-num", - "svgtypes", - "tiny-skia-path", - "unicode-bidi", - "unicode-script", - "unicode-vo", - "xmlwriter", -] - -[[package]] -name = "uuid" -version = "1.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "wasip2" -version = "1.0.1+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.105" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "web-sys" -version = "0.3.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "weezl" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "winnow" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" -dependencies = [ - "memchr", -] - -[[package]] -name = "wit-bindgen" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" - -[[package]] -name = "xmlwriter" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" - -[[package]] -name = "yansi" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" - -[[package]] -name = "zerocopy" -version = "0.8.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zune-core" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" - -[[package]] -name = "zune-jpeg" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" -dependencies = [ - "zune-core", -] diff --git a/chumsky-0.12.0/Cargo.toml b/chumsky-0.12.0/Cargo.toml deleted file mode 100644 index 0fa8262a84..0000000000 --- a/chumsky-0.12.0/Cargo.toml +++ /dev/null @@ -1,306 +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 are reading this file be aware that the original Cargo.toml -# will likely look very different (and much more reasonable). -# See Cargo.toml.orig for the original contents. - -[package] -edition = "2021" -rust-version = "1.65" -name = "chumsky" -version = "0.12.0" -authors = [ - "Joshua Barretto ", - "Elijah Hartvigsen ", -] -build = false -exclude = [ - "/misc/*", - "/benches/samples/*", -] -autolib = false -autobins = false -autoexamples = false -autotests = false -autobenches = false -description = "A parser library for humans with powerful error recovery" -readme = "README.md" -keywords = [ - "parser", - "combinator", - "token", - "language", - "syntax", -] -categories = [ - "parsing", - "text-processing", -] -license = "MIT" -repository = "https://github.com/zesterer/chumsky" - -[package.metadata.docs.rs] -all-features = true -rustdoc-args = [ - "--cfg", - "docsrs", -] - -[features] -_test_stable = [ - "std", - "stacker", - "memoization", - "extension", - "sync", -] -bytes = ["dep:bytes"] -debug = [ - "unstable", - "nightly", - "dep:railroad", -] -default = [ - "std", - "stacker", -] -docsrs = [] -either = ["dep:either"] -extension = [] -lexical-numbers = [ - "lexical", - "unstable", -] -memoization = [] -nightly = [] -pratt = ["unstable"] -regex = ["dep:regex-automata"] -serde = ["dep:serde"] -stacker = [ - "dep:stacker", - "std", -] -std = [ - "regex-automata?/std", - "serde?/std", -] -sync = ["spin"] -unstable = [] - -[lib] -name = "chumsky" -path = "src/lib.rs" - -[[example]] -name = "brainfuck" -path = "examples/brainfuck.rs" - -[[example]] -name = "debug" -path = "examples/debug.rs" -required-features = ["debug"] - -[[example]] -name = "foo" -path = "examples/foo.rs" -required-features = ["std"] - -[[example]] -name = "indent" -path = "examples/indent.rs" - -[[example]] -name = "io" -path = "examples/io.rs" -required-features = ["std"] - -[[example]] -name = "json" -path = "examples/json.rs" -required-features = ["std"] - -[[example]] -name = "json_fast" -path = "examples/json_fast.rs" -required-features = ["std"] - -[[example]] -name = "logos" -path = "examples/logos.rs" - -[[example]] -name = "mini_ml" -path = "examples/mini_ml.rs" -required-features = ["pratt"] - -[[example]] -name = "nano_rust" -path = "examples/nano_rust.rs" - -[[example]] -name = "nested" -path = "examples/nested.rs" - -[[example]] -name = "nested_spans" -path = "examples/nested_spans.rs" - -[[example]] -name = "pythonic" -path = "examples/pythonic.rs" - -[[example]] -name = "zero-copy" -path = "examples/zero-copy.rs" - -[[bench]] -name = "backtrack" -path = "benches/backtrack.rs" -harness = false - -[[bench]] -name = "cbor" -path = "benches/cbor.rs" -harness = false - -[[bench]] -name = "json" -path = "benches/json.rs" -harness = false -required-features = ["std"] - -[[bench]] -name = "lex" -path = "benches/lex.rs" -harness = false - -[[bench]] -name = "parser" -path = "benches/parser.rs" -harness = false - -[[bench]] -name = "utils" -path = "benches/utils.rs" - -[dependencies.bytes] -version = "1" -optional = true -default-features = false - -[dependencies.either] -version = "1.8.1" -optional = true - -[dependencies.hashbrown] -version = "0.15" - -[dependencies.lexical] -version = "6.1.1" -features = [ - "parse-integers", - "parse-floats", - "format", -] -optional = true -default-features = false - -[dependencies.railroad] -version = "0.3.3" -optional = true - -[dependencies.regex-automata] -version = "0.3" -features = [ - "alloc", - "meta", - "perf", - "unicode", - "nfa", - "dfa", - "hybrid", -] -optional = true -default-features = false - -[dependencies.serde] -version = "1.0" -features = ["derive"] -optional = true -default-features = false - -[dependencies.spin] -version = "0.9" -features = ["once"] -optional = true -default-features = false - -[dependencies.stacker] -version = "0.1" -optional = true - -[dependencies.unicode-ident] -version = "1.0.10" - -[dependencies.unicode-segmentation] -version = "1" - -[dev-dependencies.ariadne] -version = "0.5" - -[dev-dependencies.ciborium] -version = "0.2" - -[dev-dependencies.criterion] -version = "0.4.0" - -[dev-dependencies.lasso] -version = "0.7" - -[dev-dependencies.logos] -version = "0.13" - -[dev-dependencies.nom] -version = "7.1" - -[dev-dependencies.nom8] -version = "8" -package = "nom" - -[dev-dependencies.pest] -version = "2.5" - -[dev-dependencies.pest_derive] -version = "2.5" - -[dev-dependencies.pom] -version = "3.2" - -[dev-dependencies.serde_json] -version = "1.0" -features = ["preserve_order"] - -[dev-dependencies.slotmap] -version = "1.0" - -[dev-dependencies.sn] -version = "0.1" - -[dev-dependencies.winnow] -version = "0.7.0" - -[target."cfg(unix)".dev-dependencies.pprof] -version = "0.11" -features = [ - "flamegraph", - "criterion", -] - -[profile.bench] -debug = 2 diff --git a/chumsky-0.12.0/Cargo.toml.orig b/chumsky-0.12.0/Cargo.toml.orig deleted file mode 100644 index 921b4d2b6c..0000000000 --- a/chumsky-0.12.0/Cargo.toml.orig +++ /dev/null @@ -1,159 +0,0 @@ -[package] -name = "chumsky" -version = "0.12.0" -description = "A parser library for humans with powerful error recovery" -authors = ["Joshua Barretto ", "Elijah Hartvigsen "] -repository = "https://github.com/zesterer/chumsky" -license = "MIT" -keywords = ["parser", "combinator", "token", "language", "syntax"] -categories = ["parsing", "text-processing"] -edition = "2021" -exclude = [ - "/misc/*", - "/benches/samples/*", -] -rust-version = "1.65" - -[features] -default = ["std", "stacker"] - -# Integrate with the standard library. -std = [ - "regex-automata?/std", - "serde?/std" -] - -# Enable nightly-only features like better compiler diagnostics and a Parser impl for ! (the never type). -nightly = [] - -# Allows deeper recursion by dynamically spilling stack state on to the heap. -stacker = ["dep:stacker", "std"] - -# Allows parser memoization, speeding up heavily back-tracking parsers and allowing left recursion. -memoization = [] - -# Allows extending chumsky by writing your own parser implementations. -extension = [] - -# Make builtin parsers such as `Boxed` use atomic instead of non-atomic internals. -# TODO: Remove or rework this -sync = ["spin"] - -# Enable Pratt parsing combinator -pratt = ["unstable"] - -# Utilities for debugging parsers -debug = ["unstable", "nightly", "dep:railroad"] - -# Allow the use of unstable features (aka features where the API is not settled) -unstable = [] - -# Allows use of the `Number` parser, which is backed by the `lexical` crate -lexical-numbers = ["lexical", "unstable"] - -# Adds impl of Parser for either::Either -either = ["dep:either"] - -# Enables regex combinators -regex = ["dep:regex-automata"] - -# Enable serde serialization support -serde = ["dep:serde"] - -# Enable support for using Tokio's byte slices as inputs -bytes = ["dep:bytes"] - -# Enable dependencies only needed for generation of documentation on docs.rs -docsrs = [] - -# An alias of all features that work with the stable compiler. -# Do not use this feature, its removal is not considered a breaking change and its behaviour may change. -# If you're working on chumsky and you're adding a feature that does not require nightly support, please add it to this list. -_test_stable = ["std", "stacker", "memoization", "extension", "sync"] - -[package.metadata.docs.rs] -all-features = true -rustdoc-args = ["--cfg", "docsrs"] - -[dependencies] -hashbrown = "0.15" -stacker = { version = "0.1", optional = true } -regex-automata = { version = "0.3", default-features = false, optional = true, features = ["alloc", "meta", "perf", "unicode", "nfa", "dfa", "hybrid"] } -spin = { version = "0.9", features = ["once"], default-features = false, optional = true } -lexical = { version = "6.1.1", default-features = false, features = ["parse-integers", "parse-floats", "format"], optional = true } -either = { version = "1.8.1", optional = true } -serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] } -unicode-ident = "1.0.10" -unicode-segmentation = "1" -bytes = { version = "1", default-features = false, optional = true } -railroad = { version = "0.3.3", optional = true } - -[dev-dependencies] -ariadne = "0.5" -pom = "3.2" -nom = "7.1" -nom8 = { package = "nom", version = "8"} -winnow = "0.7.0" -serde_json = { version = "1.0", features = ["preserve_order"] } -ciborium = { version = "0.2" } -criterion = "0.4.0" -pest = "2.5" -pest_derive = "2.5" -sn = "0.1" -logos = "0.13" -lasso = "0.7" -slotmap = "1.0" - -[target.'cfg(unix)'.dev-dependencies] -pprof = { version = "0.11", features = ["flamegraph", "criterion"] } - -[profile.bench] -debug = true - -[[bench]] -name = "json" -harness = false -required-features = ["std"] - -[[bench]] -name = "lex" -harness = false - -[[bench]] -name = "parser" -harness = false - -[[bench]] -name = "backtrack" -harness = false - -[[bench]] -name = "cbor" -harness = false - -[[example]] -name = "nano_rust" - -[[example]] -name = "json" -required-features = ["std"] - -[[example]] -name = "json_fast" -required-features = ["std"] - -[[example]] -name = "io" -required-features = ["std"] - -[[example]] -name = "foo" -required-features = ["std"] - -[[example]] -name = "mini_ml" -required-features = ["pratt"] - -[[example]] -name = "debug" -required-features = ["debug"] diff --git a/chumsky-0.12.0/README.md b/chumsky-0.12.0/README.md deleted file mode 100644 index d2565d9b96..0000000000 --- a/chumsky-0.12.0/README.md +++ /dev/null @@ -1,223 +0,0 @@ -[![crates.io](https://img.shields.io/crates/v/chumsky.svg)](https://crates.io/crates/chumsky) -[![crates.io](https://docs.rs/chumsky/badge.svg)](https://docs.rs/chumsky) -[![License](https://img.shields.io/crates/l/chumsky.svg)](https://github.com/zesterer/chumsky) -[![actions-badge](https://github.com/zesterer/chumsky/actions/workflows/rust.yml/badge.svg)](https://github.com/zesterer/chumsky/actions) - -Chumsky is a parser library for Rust that makes writing expressive, high-performance parsers easy. - - - Example usage with my own language, Tao - - -*Note: Error diagnostic rendering in this example is performed by [Ariadne](https://github.com/zesterer/ariadne)* - -Although chumsky is designed primarily for user-facing parsers such as compilers, chumsky is just as much at home -parsing binary protocols at the networking layer, configuration files, or any other form of complex input validation -that you may need. It also has `no_std` support, making it suitable for embedded environments. - -## Features - -- 🪄 **Expressive combinators** that make writing your parser a joy -- ðŸŽ›ï¸ **Fully generic** across input, token, output, span, and error types -- 📑 **Zero-copy parsing** minimises allocation by having outputs hold references/slices of the input -- 🚦 **Flexible error recovery** strategies out of the box -- â˜‘ï¸ **Check-only mode** for fast verification of inputs, automatically supported -- 🚀 **Internal optimiser** leverages the power of [GATs](https://smallcultfollowing.com/babysteps/blog/2022/06/27/many-modes-a-gats-pattern/) to optimise your parser for you -- 📖 **Text-oriented parsers** for text inputs (i.e: `&[u8]` and `&str`) -- ðŸ‘ï¸â€ðŸ—¨ï¸ **Context-free grammars** are fully supported, with support for context-sensitivity -- 🔄 **Left recursion and memoization** have opt-in support -- 🪺 **Nested inputs** such as token trees are fully supported both as inputs and outputs -- ðŸ·ï¸ **Pattern labelling** for dynamic, user-friendly error messages -- ðŸ—ƒï¸ **Caching** allows parsers to be created once and reused many times -- â†”ï¸ **Pratt parsing** support for simple yet flexible expression parsing -- 🪛 **no_std** support, allowing chumsky to run in embedded environments - -## Example - -See [`examples/brainfuck.rs`](https://github.com/zesterer/chumsky/blob/main/examples/brainfuck.rs) for a full -[Brainfuck](https://en.wikipedia.org/wiki/Brainfuck) interpreter -(`cargo run --example brainfuck -- examples/sample.bf`). - -```rust,ignore -use chumsky::prelude::*; - -/// An AST (Abstract Syntax Tree) for Brainfuck instructions -#[derive(Clone)] -enum Instr { - Left, Right, - Incr, Decr, - Read, Write, - Loop(Vec), // In Brainfuck, `[...]` loop instructions contain any number of instructions -} - -/// A function that generates a Brainfuck parser -fn brainfuck<'a>() -> impl Parser<'a, &'a str, Vec> { - // Brainfuck syntax is recursive: each instruction can contain many sub-instructions (via `[...]` loops) - recursive(|bf| choice(( - // All of the basic instructions are just single characters - just('<').to(Instr::Left), - just('>').to(Instr::Right), - just('+').to(Instr::Incr), - just('-').to(Instr::Decr), - just(',').to(Instr::Read), - just('.').to(Instr::Write), - // Loops are strings of Brainfuck instructions, delimited by square brackets - bf.delimited_by(just('['), just(']')).map(Instr::Loop), - )) - // Brainfuck instructions appear sequentially, so parse as many as we need - .repeated() - .collect()) -} - -// Parse some Brainfuck with our parser -brainfuck().parse("--[>--->->->++>-<<<<<-------]>--.>---------.>--..+++.>----.>+++++++++.<<.+++.------.<-.>>+.") -``` - -You can find more examples [here](https://github.com/zesterer/chumsky/tree/main/examples). - -## Guide and documentation - -Chumsky has an extensive [guide](https://docs.rs/chumsky/latest/chumsky/guide) that walks you through the library: all -the way from setting up and basic theory to advanced uses of the crate. It includes technical details of chumsky's -behaviour, examples of uses, a handy index for all of the combinators, technical details about the crate, and even a -tutorial that leads you through the development of a fully-functioning interpreter for a simple programming language. - -The crate docs should also be similarly useful: most important functions include at least one contextually-relevant -example, and all crate items are fully documented. - -In addition, chumsky comes with a suite of fully-fledged -[example projects](https://github.com/zesterer/chumsky/tree/main/examples). These include: - -- Parsers for existing syntaxes like Brainfuck and JSON -- Integration demos for third-party crates, like [`logos`](https://crates.io/crates/logos) -- Parsers for new toy programming languages: a Rust-like language and a full-on lexer, parser, type-checker, and - interpreter for a minature ML-like language. -- Examples of parsing non-trivial inputs like token trees, `impl Read`ers, and zero-copy, zero-alloc parsing. - -## Cargo features - -Chumsky contains several optional features that extend the crate's functionality. - -- `bytes`: adds support for parsing types from the [`bytes`](https://docs.rs/bytes/) crate. - -- `either`: implements `Parser` for `either::Either`, allowing dynamic configuration of parsers at run-time - -- `extension`: enables the extension API, allowing you to write your own first-class combinators that integrate with - and extend chumsky - -- `lexical-numbers`: Enables use of the `Number` parser for parsing various numeric formats - -- `memoization`: enables [memoization](https://en.wikipedia.org/wiki/Memoization#Parsers) features - -- `nightly`: enable support for features only supported by the nightly Rust compiler - -- `pratt`: enables the [pratt parsing](https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html) - combinator - -- `regex`: enables the regex combinator - -- `serde`: enables `serde` (de)serialization support for several types - -- `stacker` (enabled by default): avoid stack overflows by spilling stack data to the heap via the `stacker` crate - -- `std` (enabled by default): support for standard library features - -- `unstable`: enables experimental chumsky features (API features enabled by `unstable` are NOT considered to fall - under the semver guarantees of chumsky!) - -## *What* is a parser combinator? - -Parser combinators are a technique for implementing parsers by defining them in terms of other parsers. The resulting -parsers use a [recursive descent](https://en.wikipedia.org/wiki/Recursive_descent_parser) strategy to transform a stream -of tokens into an output. Using parser combinators to define parsers is roughly analogous to using Rust's -[`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) trait to define iterative algorithms: the -type-driven API of `Iterator` makes it more difficult to make mistakes and easier to encode complicated iteration logic -than if one were to write the same code by hand. The same is true of parser combinators. - -## *Why* use parser combinators? - -Writing parsers with good error recovery is conceptually difficult and time-consuming. It requires understanding the -intricacies of the recursive descent algorithm, and then implementing recovery strategies on top of it. If you're -developing a programming language, you'll almost certainly change your mind about syntax in the process, leading to some -slow and painful parser refactoring. Parser combinators solve both problems by providing an ergonomic API that allows -for rapidly iterating upon a syntax. - -Parser combinators are also a great fit for domain-specific languages for which an existing parser does not exist. -Writing a reliable, fault-tolerant parser for such situations can go from being a multi-day task to a half-hour task -with the help of a decent parser combinator library. - -## Classification - -Chumsky's parsers are [recursive descent](https://en.wikipedia.org/wiki/Recursive_descent_parser) parsers and are -capable of parsing [parsing expression grammars (PEGs)](https://en.wikipedia.org/wiki/Parsing_expression_grammar), which -includes all known context-free languages. However, chumsky doesn't stop there: it also supports context-sensitive -grammars via a set of dedicated combinators that integrate cleanly with the rest of the library. This allows it to -additionally parse a number of context-sensitive syntaxes like Rust-style raw strings, Python-style semantic -indentation, and much more. - -## Error recovery - -Chumsky has support for error recovery, meaning that it can encounter a syntax error, report the error, and then -attempt to recover itself into a state in which it can continue parsing so that multiple errors can be produced at once -and a partial [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree) can still be generated from the input for future -compilation stages to consume. - -## Performance - -Chumsky allows you to choose your priorities. When needed, it can be configured for high-quality parser errors. It can -also be configured for *performance*. - -It's difficult to produce general benchmark results for parser libraries. By their nature, the performance of a parser -is intimately tied to exactly how the grammar they implement has been specified. That said, here are some numbers for a -fairly routine JSON parsing benchmark implemented idiomatically in various libraries. As you can see, chumsky ranks -quite well! - -| Ranking | Library | Time (smaller is better) | Throughput | -|---------|------------------------------------------------------|--------------------------|------------| -| 1 | `chumsky` (check-only) | 140.77 µs | 797 MB/s | -| 2 | [`winnow`](https://github.com/winnow-rs/winnow) | 178.91 µs | 627 MB/s | -| 3 | `chumsky` | 210.43 µs | 533 MB/s | -| 4 | [`sn`](https://github.com/Jacherr/sn) (hand-written) | 237.94 µs | 472 MB/s | -| 5 | [`serde_json`](https://github.com/serde-rs/json) | 477.41 µs | 235 MB/s | -| 6 | [`nom`](https://github.com/rust-bakery/nom) | 526.52 µs | 213 MB/s | -| 7 | [`pest`](https://github.com/pest-parser/pest) | 1.9706 ms | 57 MB/s | -| 8 | [`pom`](https://github.com/J-F-Liu/pom) | 13.730 ms | 8 MB/s | - -What should you take from this? It's difficult to say. 'Chumsky is faster than X' or 'chumsky is slower than Y' is too -strong a statement: this is just one particular benchmark with one particular set of implementations and one -particular workload. - -That said, there is something you can take: chumsky isn't going to be your bottleneck. In this benchmark, chumsky is -within 20% of the performance of the 'pack leader' and has performance comparable to a hand-written parser. The -performance standards for Rust libraries are already far above most language ecosystems, so you can be sure that -chumsky will keep pace with your use-case. - -Benchmarks were performed on a single core of an AMD Ryzen 7 3700x. - -## Notes - -My apologies to Noam for choosing such an absurd name. - -## License - -Chumsky is licensed under the MIT license (see `LICENSE` in the main repository). - -## Provenance - -This software is proudly and fondly written, maintained, used - and most crucially - **understood** by real human beings. -While we can't personally attest to the provenance of every line of code ever contributed, the vast majority of the -codebase has certainly been developed without the aid of large language models and other stochastic 'intelligence'. - -While the license may not guarantee warranty 'of any kind', you can at least use this software in the comforting knowledge -that its veracity and coherence is vouched for by sentient intelligence with skin in the game and a reputation to uphold. - -## Contribution guidelines - -We expect contributors to adhere to the ethos of the project. - -Source code is not an artifact, an intermediate representation, nor a bothersome annoyance whose creation is to be -offloaded to metal and transistors. Source code is a **source of truth** - the only source of truth that constitutes this -software project - and it deserves to be understood and curated by the *accountable* and *reasoned* mind of a human being. - -Please refrain from contributing changes that you have not personally understood and instigated the authorship of. We do -not expect perfection, but we do expect you to personally understand your own motivations and decisions. diff --git a/chumsky-0.12.0/examples/debug.rs b/chumsky-0.12.0/examples/debug.rs deleted file mode 100644 index 24506e97c8..0000000000 --- a/chumsky-0.12.0/examples/debug.rs +++ /dev/null @@ -1,104 +0,0 @@ -//! This is a Brainfuck parser and interpreter -//! Run it with the following command: -//! cargo run --example debug - -use chumsky::prelude::*; -use std::collections::HashMap; - -#[derive(Clone, Debug)] -pub enum Json { - Null, - Bool(bool), - Str(String), - Num(f64), - Array(Vec), - Object(HashMap), -} - -fn json<'a>() -> impl Parser<'a, &'a str, Json> { - recursive(|value| { - let digits = text::digits(10).to_slice(); - - let frac = just('.').then(digits); - - let exp = just('e') - .or(just('E')) - .then(one_of("+-").or_not()) - .then(digits); - - let number = just('-') - .or_not() - .then(text::int(10)) - .then(frac.or_not()) - .then(exp.or_not()) - .to_slice() - .map(|s: &str| s.parse().unwrap()); - - let escape = just('\\') - .then(choice(( - just('\\'), - just('/'), - just('"'), - just('b').to('\x08'), - just('f').to('\x0C'), - just('n').to('\n'), - just('r').to('\r'), - just('t').to('\t'), - just('u').ignore_then(text::digits(16).exactly(4).to_slice().validate( - |digits, _, emitter| { - char::from_u32(u32::from_str_radix(digits, 16).unwrap()).unwrap_or_else( - || { - emitter.emit(Default::default()); - '\u{FFFD}' // unicode replacement character - }, - ) - }, - )), - ))) - .ignored(); - - let string = none_of("\\\"") - .ignored() - .or(escape) - .repeated() - .to_slice() - .map(ToString::to_string) - .delimited_by(just('"'), just('"')); - - let array = value - .clone() - .separated_by(just(',').padded()) - .allow_trailing() - .collect() - .padded() - .delimited_by(just('['), just(']')); - - let member = string.then_ignore(just(':').padded()).then(value); - let object = member - .clone() - .separated_by(just(',').padded()) - .collect() - .padded() - .delimited_by(just('{'), just('}')); - - choice(( - just("null").to(Json::Null), - just("true").to(Json::Bool(true)), - just("false").to(Json::Bool(false)), - number.map(Json::Num).labelled("number"), - string.map(Json::Str).labelled("string"), - array.map(Json::Array).labelled("array"), - object.map(Json::Object).labelled("object"), - )) - .padded() - .labelled("JSON value") - }) -} - -fn main() { - // Generate an eBNF grammar for the parser - println!("{}", json().debug().to_ebnf()); - - // Generate a railroad diagram for the parser - println!("{}", json().debug().to_railroad_svg()); -} diff --git a/chumsky-0.12.0/examples/indent.rs b/chumsky-0.12.0/examples/indent.rs deleted file mode 100644 index 8db4222554..0000000000 --- a/chumsky-0.12.0/examples/indent.rs +++ /dev/null @@ -1,48 +0,0 @@ -use chumsky::prelude::*; - -#[derive(Clone, Debug)] -pub enum Stmt { - Expr, - Loop(Vec), -} - -fn parser<'a>() -> impl Parser<'a, &'a str, Vec> { - let expr = just("expr"); // TODO - - let block = recursive(|block| { - let indent = just(' ') - .repeated() - .configure(|cfg, parent_indent| cfg.exactly(*parent_indent)); - - let expr_stmt = expr.then_ignore(text::newline()).to(Stmt::Expr); - let control_flow = just("loop:") - .then(text::newline()) - .ignore_then(block) - .map(Stmt::Loop); - let stmt = expr_stmt.or(control_flow); - - text::whitespace() - .count() - .ignore_with_ctx(stmt.separated_by(indent).collect()) - }); - - block.with_ctx(0) -} - -fn main() { - let stmts = parser().padded().parse( - r#" -expr -expr -loop: - expr - loop: - expr - expr - expr -expr -"#, - ); - println!("{:#?}", stmts.output()); - println!("{:?}", stmts.errors().collect::>()); -} diff --git a/chumsky-0.12.0/src/cache.rs b/chumsky-0.12.0/src/cache.rs deleted file mode 100644 index 4d1b798a1d..0000000000 --- a/chumsky-0.12.0/src/cache.rs +++ /dev/null @@ -1,86 +0,0 @@ -//! Traits and types that allow parsers to be cached between invocations. -//! -//! # Example -//! -//! ``` -//! #![feature(lazy_cell)] -//! use std::sync::{LazyLock, Arc}; -//! use chumsky::{prelude::*, cache::{Cache, Cached}}; -//! -//! #[derive(Debug, PartialEq)] -//! enum Token<'a> { Ident(&'a str), Int(u64) } -//! -//! #[derive(Default)] -//! struct TokenParser; -//! impl Cached for TokenParser { -//! type Parser<'a> = Arc, extra::Default> + Send + Sync + 'a>; -//! -//! fn make_parser<'a>(self) -> Self::Parser<'a> { -//! let ident = text::ident().map(Token::Ident); -//! let num = text::int(10).from_str().unwrapped().map(Token::Int); -//! Arc::new(ident.or(num)) -//! } -//! } -//! -//! // The parser cache doesn't have a lifetime and so can be stored pretty much anywhere: -//! static PARSER: LazyLock> = LazyLock::new(Cache::default); -//! -//! // The parser can be used from any context simply by calling `.get()` on the cache -//! assert_eq!(PARSER.get().parse("42").into_result(), Ok(Token::Int(42))); -//! assert_eq!(PARSER.get().parse("hello").into_result(), Ok(Token::Ident("hello"))); -//! ``` - -use super::*; - -/// Implementing this trait allows you to cache parsers for use with inputs of different lifetimes, avoiding the -/// need to recreate the parser for each input lifetime. -pub trait Cached { - /// The type of the parser to be cached. - /// - /// Because parsers tend to have unwieldy types, it is recommended to perform type erasure here. For example, - /// a parser with input type `&'src str` and output type `Token<'src>` might have one of the following types. - /// - /// ```ignore - /// Boxed<'src, 'src, &'src str, Token<'src>, extra::Default> - /// Arc, extra::Default> + Send + Sync + 'src> - /// ``` - type Parser<'src>; - - /// Create an instance of the parser - fn make_parser<'src>(self) -> Self::Parser<'src>; -} - -/// Allows a parser to be cached for reuse with inputs and outputs of different lifetimes. -pub struct Cache { - parser: C::Parser<'static>, - #[allow(dead_code)] - phantom: EmptyPhantom, -} - -impl Default for Cache { - fn default() -> Self { - Self::new(C::default()) - } -} - -impl Cache { - /// Create a new cached parser. - pub fn new(cacher: C) -> Self { - Self { - parser: cacher.make_parser(), - phantom: EmptyPhantom::new(), - } - } - - /// Get a reference to the cached parser. - /// - /// Because this function is generic over an input lifetime, the returned parser can be used in many - /// different contexts. - pub fn get<'src>(&self) -> &C::Parser<'src> { - // SAFETY: This is safe because the API of `Cache` requires that the parser we store is bound by an arbitrary - // lifetime variable (see `Cached::make_parser`). Therefore, the implementor of `Cached` has no way to - // 'discover' the lifetime and so, because lifetimes are entirely removed during monomorphisation, the parser - // must be valid for arbitrary lifetimes. - unsafe { &*(&self.parser as *const C::Parser<'_>).cast() } - } -} diff --git a/chumsky-0.12.0/src/debug.rs b/chumsky-0.12.0/src/debug.rs deleted file mode 100644 index 116d1a6e81..0000000000 --- a/chumsky-0.12.0/src/debug.rs +++ /dev/null @@ -1,245 +0,0 @@ -//! Unstable utilities for debugging in-development parsers. -//! -//! See [`Parser::debug`]. - -use super::*; - -#[doc(hidden)] -#[derive(Debug)] -pub enum SeqInfo { - Char(char), - String(String), - Opaque(String), - Unknown(String), -} - -impl SeqInfo { - fn show(&self) -> String { - match self { - Self::Char(c) => format!("'{c}'"), - Self::String(s) => format!("\"{s}\""), - Self::Opaque(s) => s.to_string(), - Self::Unknown(s) => s.to_string(), - } - } -} - -#[doc(hidden)] -#[derive(Debug)] -pub enum NodeInfo { - Unknown(String), - // The root of a recursive definition - Recursive(usize, Box), - RecursiveRef(usize), - Repeated(core::ops::Range, Box), - SeparatedBy(Box, Box), - Choice(Vec), - Any, - Just(SeqInfo), - OneOf(SeqInfo), - NoneOf(SeqInfo), - Then(Box, Box), - Padded(Box), - Filter(Box), - OrNot(Box), - Labelled(String, Box), - Builtin(String), - NestedIn(Box, Box), -} - -impl NodeInfo { - // ctx { 0 = any, 1 = or, 2 = then } - fn bnf_inner(&self, depth: usize, defs: &mut Vec, ctx: usize) -> String { - match self { - Self::Unknown(s) => format!(""), - Self::Recursive(r, inner) => { - let def = inner.bnf_inner(1, defs, 0); - defs.push(format!("def_{r} ::= {def};")); - format!("def_{r}") - } - Self::Repeated(_, inner) => format!("{{ {} }}", inner.bnf_inner(depth, defs, 0)), - Self::SeparatedBy(inner, sep) => format!( - "{{ {} [{}]}}", - inner.bnf_inner(depth, defs, 2), - sep.bnf_inner(depth, defs, 0) - ), - Self::OrNot(inner) => format!("[ {} ]", inner.bnf_inner(depth, defs, 0)), - Self::Choice(inners) => { - let s = inners - .iter() - .map(|i| i.bnf_inner(depth + 1, defs, 1)) - .collect::>() - .join(&format!("\n{}| ", " ".repeat(depth))); - if ctx == 1 || ctx == 0 { - s - } else { - format!("({s})") - } - } - Self::Just(seq) => seq.show(), - Self::OneOf(seq) => format!("one_of({})", seq.show()), - Self::NoneOf(seq) => format!("none_of({})", seq.show()), - Self::Then(a, b) => { - let s = format!( - "{} {}", - a.bnf_inner(depth, defs, 2), - b.bnf_inner(depth, defs, 2) - ); - if ctx == 1 || ctx == 0 { - s - } else { - format!("({s})") - } - } - Self::Any => "any".to_string(), - Self::Builtin(s) => s.to_string(), - Self::NestedIn(a, b) => format!( - "({}).nested_in({})", - a.bnf_inner(depth, defs, 0), - b.bnf_inner(depth, defs, 0) - ), - Self::Padded(inner) | Self::Filter(inner) | Self::Labelled(_, inner) => { - inner.bnf_inner(depth, defs, ctx) - } - Self::RecursiveRef(r) => format!("def_{r}"), - } - } - - fn railroad_inner(&self, defs: &mut Vec>) -> Box { - use railroad::*; - match self { - Self::Unknown(s) => Box::new(Comment::new(s.to_string())), - Self::Recursive(r, inner) => { - let inner = inner.railroad_inner(defs); - defs.push(Box::new(LabeledBox::new( - Sequence::new(vec![ - Box::new(SimpleStart) as Box, - inner, - Box::new(SimpleEnd), - ]), - Terminal::new(format!("def_{r}")), - ))); - Box::new(Terminal::new(format!("def_{r}"))) - } - Self::Repeated(_, inner) => Box::new(Repeat::new(inner.railroad_inner(defs), Empty)), - Self::SeparatedBy(inner, sep) => Box::new(Repeat::new( - inner.railroad_inner(defs), - sep.railroad_inner(defs), - )), - Self::Choice(inners) => Box::new(Choice::new( - inners.iter().map(|i| i.railroad_inner(defs)).collect(), - )), - Self::Just(seq) => Box::new(NonTerminal::new(seq.show())), - Self::OneOf(seq) => Box::new(Terminal::new(format!("one_of({})", seq.show()))), - Self::NoneOf(seq) => Box::new(Terminal::new(format!("none_of({})", seq.show()))), - Self::Then(a, b) => Box::new(Sequence::new(vec![ - a.railroad_inner(defs), - b.railroad_inner(defs), - ])), - Self::RecursiveRef(r) => Box::new(Terminal::new(format!("def_{r}"))), - // Self::Padded(inner) => Box::new(Sequence::new(vec![ - // Box::new(Terminal::new(format!("whitespace"))) as Box, - // inner.railroad_inner(defs), - // Box::new(Terminal::new(format!("whitespace"))), - // ])), - Self::Padded(inner) => inner.railroad_inner(defs), - Self::Filter(inner) => Box::new(LabeledBox::new( - inner.railroad_inner(defs), - Comment::new("filtered".to_string()), - )), - Self::Labelled(label, inner) => Box::new(LabeledBox::new( - inner.railroad_inner(defs), - NonTerminal::new(label.to_string()), - )), - Self::Builtin(s) => Box::new(Terminal::new(s.to_string())), - Self::NestedIn(inner, outer) => Box::new(LabeledBox::new( - Box::new(LabeledBox::new( - outer.railroad_inner(defs), - NonTerminal::new("outer".to_string()), - )), - Box::new(LabeledBox::new( - Sequence::new(vec![ - Box::new(SimpleStart) as Box, - inner.railroad_inner(defs), - Box::new(SimpleEnd), - ]), - NonTerminal::new("inner".to_string()), - )), - )), - Self::Any => Box::new(Terminal::new("any".to_string())), - Self::OrNot(inner) => Box::new(Optional::new(inner.railroad_inner(defs))), - } - } -} - -#[doc(hidden)] -#[derive(Default)] -pub struct NodeScope { - rec_count: usize, - // (ptr, index, name) - rec: Vec<(usize, usize)>, -} - -impl NodeScope { - pub fn lookup_rec(&mut self, ptr: usize, f: impl FnOnce(&mut Self) -> NodeInfo) -> NodeInfo { - self.rec - .iter() - .rev() - .find(|(p, _)| *p == ptr) - .map(|(_, r)| NodeInfo::RecursiveRef(*r)) - .unwrap_or_else(|| { - self.rec_count += 1; - self.rec.push((ptr, self.rec_count)); - NodeInfo::Recursive(self.rec_count, Box::new(f(self))) - }) - } -} - -/// A catch-all box of tricks for debugging a parser. -pub struct DebugInfo<'a> { - pub(crate) node_info: NodeInfo, - pub(crate) phantom: PhantomData<&'a ()>, -} - -impl<'a> DebugInfo<'a> { - /// Generate a string containing an [eBNF](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form#EBNF) grammar for this parser. - /// - /// The exact format of the grammar definition is not specified, and is only intended to be read by a human being. - pub fn to_ebnf(&self) -> String { - let mut defs = Vec::new(); - let def = self.node_info.bnf_inner(1, &mut defs, 0); - defs.push(def); - defs.join("\n\n") - } - - /// Generate a human-readable [railroad diagram](https://en.wikipedia.org/wiki/Syntax_diagram) that describes the grammar. - /// - /// The resulting diagram is in [SVG](https://en.wikipedia.org/wiki/SVG) format and may be printed to the console, written to a file, etc. - /// - /// The exact format of the diagram is not specified and its quality may depend heavily on annotations, such as [`Parser::labelled`]. - /// Aspects of the grammar may also not be captured. For example, context-sensitive parsers are largely ignored today. - /// - /// # Examples - /// - /// Here is a generated railroad diagram for the example JSON parser. See `examples/json.rs` in the repository. - /// - /// ![A railroad diagram of the grammar for JSON](https://github.com/zesterer/chumsky/raw/main/misc/json-railroad.svg) - pub fn to_railroad_svg(&self) -> impl core::fmt::Display + Clone { - use railroad::*; - - let mut seq = Sequence::default(); - let mut defs = Vec::new(); - let def = self.node_info.railroad_inner(&mut defs); - defs.push(def); - seq.push(Rc::new(VerticalGrid::new(defs)) as Rc); - - let mut dia = Diagram::new(seq); - - dia.add_element( - svg::Element::new("style") - .set("type", "text/css") - .text(DEFAULT_CSS), - ); - dia - } -} diff --git a/chumsky-0.12.0/src/error.rs b/chumsky-0.12.0/src/error.rs deleted file mode 100644 index 3dd3488589..0000000000 --- a/chumsky-0.12.0/src/error.rs +++ /dev/null @@ -1,857 +0,0 @@ -//! Error types, traits and utilities. -//! -//! *“I like the cover," he said. "Don't Panic. It's the first helpful or intelligible thing anybody's said to me all -//! day.â€* -//! -//! You can implement the [`Error`] trait to create your own parser errors, or you can use one provided by the crate -//! like [`Cheap`], [`Simple`] or [`Rich`]. - -use super::*; -use alloc::{borrow::Cow, string::ToString}; - -pub use label::LabelError; - -/// A trait that describes parser error types. -/// -/// If you have a custom error type in your compiler, or your needs are not sufficiently met by [`Simple`], you should -/// implement this trait. If your error type has 'extra' features that allow for more specific error messages, you can -/// use the [`Parser::map_err`] or [`Parser::try_map`] functions to take advantage of these inline within your parser. -/// -/// # Examples -/// -/// ``` -/// use chumsky::{prelude::*, error::{Error, LabelError}, util::MaybeRef, DefaultExpected}; -/// type Span = SimpleSpan; -/// -/// // A custom error type -/// #[derive(Debug, PartialEq)] -/// enum MyError { -/// ExpectedFound { -/// span: Span, -/// expected: Vec>, -/// found: Option, -/// }, -/// NotADigit(Span, char), -/// } -/// -/// impl<'a> Error<'a, &'a str> for MyError { -/// fn merge(mut self, mut other: Self) -> Self { -/// if let (Self::ExpectedFound { expected, .. }, Self::ExpectedFound { expected: expected_other, .. }) = ( -/// &mut self, -/// &mut other, -/// ) { -/// expected.append(expected_other); -/// } -/// self -/// } -/// } -/// -/// impl<'a> LabelError<'a, &'a str, DefaultExpected<'a, char>> for MyError { -/// fn expected_found>>( -/// expected: Iter, -/// found: Option>, -/// span: Span, -/// ) -> Self { -/// Self::ExpectedFound { -/// span, -/// expected: expected -/// .into_iter() -/// .map(|e| e.into_owned()) -/// .collect(), -/// found: found.as_deref().copied(), -/// } -/// } -/// } -/// -/// let numeral = any::<_, extra::Err>().try_map(|c: char, span| match c.to_digit(10) { -/// Some(x) => Ok(x), -/// None => Err(MyError::NotADigit(span, c)), -/// }); -/// -/// assert_eq!(numeral.parse("3").into_result(), Ok(3)); -/// assert_eq!(numeral.parse("7").into_result(), Ok(7)); -/// assert_eq!(numeral.parse("f").into_errors(), vec![MyError::NotADigit((0..1).into(), 'f')]); -/// ``` -// TODO: Add support for more specialised kinds of error: unclosed delimiters, and more -pub trait Error<'a, I: Input<'a>>: - Sized + LabelError<'a, I, DefaultExpected<'a, I::Token>> -{ - /// Merge two errors that point to the same input together, combining their information. - #[inline(always)] - fn merge(self, other: Self) -> Self { - #![allow(unused_variables)] - self - } -} - -/// A ZST error type that tracks only whether a parse error occurred at all. This type is for when -/// you want maximum parse speed, at the cost of all error reporting. -/// -/// # Examples -/// -/// ``` -/// use chumsky::prelude::*; -/// -/// let parser = just::<_, _, extra::Err>("valid"); -/// let error = parser.parse("invalid").into_errors()[0]; -/// -/// assert_eq!(error, EmptyErr::default()); -/// ``` -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone, Default)] -pub struct EmptyErr(()); - -impl<'a, I: Input<'a>> Error<'a, I> for EmptyErr {} - -impl<'a, I: Input<'a>, L> LabelError<'a, I, L> for EmptyErr { - #[inline(always)] - fn expected_found>( - _: E, - _: Option>, - _: I::Span, - ) -> Self { - EmptyErr(()) - } -} - -impl fmt::Display for EmptyErr { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "error") - } -} - -/// A very cheap error type that tracks only the error span ([`SimpleSpan`] by default). -/// This type is most useful when you want fast parsing but do not particularly care about the quality of error messages. -/// -/// # Examples -/// -/// ``` -/// use chumsky::prelude::*; -/// -/// let parser = just::<_, _, extra::Err>("+"); -/// let error = parser.parse("-").into_errors()[0]; -/// -/// assert_eq!(error.span(), &SimpleSpan::new((), 0..1)); -/// ``` -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct Cheap> { - span: S, -} - -impl Cheap { - /// Create a new [`Cheap`] error. - pub fn new(span: S) -> Self { - Self { span } - } - - /// Get the span than that error related to. - /// - /// If the span type is unspecified, it is [`SimpleSpan`]. - pub fn span(&self) -> &S { - &self.span - } -} - -impl<'a, I: Input<'a>> Error<'a, I> for Cheap {} - -impl<'a, I: Input<'a>, L> LabelError<'a, I, L> for Cheap { - #[inline] - fn expected_found>( - _expected: E, - _found: Option>, - span: I::Span, - ) -> Self { - Self { span } - } -} - -impl fmt::Debug for Cheap -where - S: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "at {:?}", self.span)?; - Ok(()) - } -} - -impl fmt::Display for Cheap -where - S: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self, f) - } -} - -/// A simple error type that tracks the error span ([`SimpleSpan`] by default) and found token. This type is most useful when you want fast parsing -/// but do not particularly care about the quality of error messages. -/// -/// # Examples -/// -/// ``` -/// use chumsky::prelude::*; -/// -/// let parser = just::<_, _, extra::Err>>("+"); -/// let error = parser.parse("-").into_errors()[0]; -/// -/// assert_eq!(error.span(), &SimpleSpan::new((), 0..1)); -/// assert_eq!(error.found(), Some(&'-')); -/// ``` -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct Simple<'a, T, S = SimpleSpan> { - span: S, - found: Option>, -} - -impl Simple<'_, T, S> { - /// Get the span than that error related to. - /// - /// If the span type is unspecified, it is [`SimpleSpan`]. - pub fn span(&self) -> &S { - &self.span - } - - /// Get the token found by this error when parsing. `None` implies that the error expected the end of input. - pub fn found(&self) -> Option<&T> { - self.found.as_deref() - } -} - -impl<'a, T, S> Simple<'a, T, S> { - /// Create a new [`Simple`] error. - pub fn new(found: Option>, span: S) -> Self { - Self { span, found } - } - - /// Transform this error's tokens using the given function. - /// - /// This is useful when you wish to combine errors from multiple compilation passes (lexing and parsing, say) where - /// the token type for each pass is different (`char` vs `MyToken`, say). - pub fn map_token U>(self, f: F) -> Simple<'a, U, S> - where - T: Clone, - { - Simple { - span: self.span, - found: self.found.map(|found| f(found.into_inner()).into()), - } - } -} - -impl<'a, I: Input<'a>> Error<'a, I> for Simple<'a, I::Token, I::Span> {} - -impl<'a, I: Input<'a>, L> LabelError<'a, I, L> for Simple<'a, I::Token, I::Span> { - #[inline] - fn expected_found>( - _expected: E, - found: Option>, - span: I::Span, - ) -> Self { - Self { span, found } - } -} - -impl fmt::Debug for Simple<'_, T, S> -where - T: fmt::Debug, - S: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "found ")?; - write_token(f, T::fmt, self.found.as_deref())?; - write!(f, " at {:?}", self.span)?; - Ok(()) - } -} - -impl fmt::Display for Simple<'_, T, S> -where - T: fmt::Debug, - S: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self, f) - } -} - -/// An expected pattern for a [`Rich`] error. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[non_exhaustive] -pub enum RichPattern<'a, T> { - /// A specific token. - Token(MaybeRef<'a, T>), - /// A labelled pattern. - Label(Cow<'a, str>), - /// A specific keyword. - Identifier(String), - /// Anything other than the end of input. - Any, - /// Something other than the provided input. - SomethingElse, - /// The end of input. - EndOfInput, -} - -impl<'a, T> From> for RichPattern<'a, T> { - fn from(expected: DefaultExpected<'a, T>) -> Self { - match expected { - DefaultExpected::Token(tok) => Self::Token(tok), - DefaultExpected::Any => Self::Any, - DefaultExpected::SomethingElse => Self::SomethingElse, - DefaultExpected::EndOfInput => Self::EndOfInput, - } - } -} - -impl<'a, Slice: core::fmt::Debug, T> From> for RichPattern<'a, T> { - fn from(expected: text::TextExpected) -> Self { - match expected { - text::TextExpected::Whitespace => Self::Label(Cow::Borrowed("whitespace")), - text::TextExpected::InlineWhitespace => Self::Label(Cow::Borrowed("inline whitespace")), - text::TextExpected::Newline => Self::Label(Cow::Borrowed("newline")), - text::TextExpected::Digit(start, _end) if start > 0 => { - Self::Label(Cow::Borrowed("non-zero digit")) - } - text::TextExpected::Digit(_, _) => Self::Label(Cow::Borrowed("digit")), - text::TextExpected::AnyIdentifier => Self::Label(Cow::Borrowed("identifier")), - text::TextExpected::Identifier(i) => Self::Identifier(alloc::format!("{i:?}")), - text::TextExpected::Int => Self::Label(Cow::Borrowed("int")), - } - } -} - -impl<'a, T> From> for RichPattern<'a, T> { - fn from(tok: MaybeRef<'a, T>) -> Self { - Self::Token(tok) - } -} - -impl From<&'static str> for RichPattern<'_, T> { - fn from(label: &'static str) -> Self { - Self::Label(Cow::Borrowed(label)) - } -} - -impl From for RichPattern<'_, T> { - fn from(label: String) -> Self { - Self::Label(Cow::Owned(label)) - } -} - -impl From for RichPattern<'_, char> { - fn from(c: char) -> Self { - Self::Token(MaybeRef::Val(c)) - } -} - -impl<'a, T> RichPattern<'a, T> { - /// Transform this pattern's tokens using the given function. - /// - /// This is useful when you wish to combine errors from multiple compilation passes (lexing and parsing, say) where - /// the token type for each pass is different (`char` vs `MyToken`, say). - pub fn map_token U>(self, mut f: F) -> RichPattern<'a, U> - where - T: Clone, - { - match self { - Self::Token(t) => RichPattern::Token(f(t.into_inner()).into()), - Self::Label(l) => RichPattern::Label(l), - Self::Identifier(i) => RichPattern::Identifier(i), - Self::Any => RichPattern::Any, - Self::SomethingElse => RichPattern::SomethingElse, - Self::EndOfInput => RichPattern::EndOfInput, - } - } - - /// Convert this pattern into an owned version of itself by cloning any borrowed internal tokens, if necessary. - pub fn into_owned<'b>(self) -> RichPattern<'b, T> - where - T: Clone, - { - match self { - Self::Token(tok) => RichPattern::Token(tok.into_owned()), - Self::Label(l) => RichPattern::Label(Cow::Owned(l.into_owned())), - Self::Identifier(i) => RichPattern::Identifier(i), - Self::Any => RichPattern::Any, - Self::SomethingElse => RichPattern::SomethingElse, - Self::EndOfInput => RichPattern::EndOfInput, - } - } - - fn write( - &self, - f: &mut fmt::Formatter, - mut fmt_token: impl FnMut(&T, &mut fmt::Formatter<'_>) -> fmt::Result, - ) -> fmt::Result { - match self { - Self::Token(tok) => { - write!(f, "'")?; - fmt_token(tok, f)?; - write!(f, "'") - } - Self::Label(l) => write!(f, "{l}"), - Self::Identifier(i) => write!(f, "'{i}'"), - Self::Any => write!(f, "any"), - Self::SomethingElse => write!(f, "something else"), - Self::EndOfInput => write!(f, "end of input"), - } - } -} - -impl fmt::Debug for RichPattern<'_, T> -where - T: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.write(f, |t, f| write!(f, "{t:?}")) - } -} - -impl fmt::Display for RichPattern<'_, T> -where - T: fmt::Display, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.write(f, |t, f| write!(f, "{t}")) - } -} - -/// The reason for a [`Rich`] error. -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub enum RichReason<'a, T> { - /// An unexpected input was found - ExpectedFound { - /// The tokens expected - expected: Vec>, - /// The tokens found - found: Option>, - }, - /// An error with a custom message - Custom(String), -} - -impl<'a, T> RichReason<'a, T> { - /// Return the token that was found by this error reason. `None` implies that the end of input was expected. - pub fn found(&self) -> Option<&T> { - match self { - Self::ExpectedFound { found, .. } => found.as_deref(), - Self::Custom(_) => None, - } - } - - /// Convert this reason into an owned version of itself by cloning any borrowed internal tokens, if necessary. - pub fn into_owned<'b>(self) -> RichReason<'b, T> - where - T: Clone, - { - match self { - Self::ExpectedFound { found, expected } => RichReason::ExpectedFound { - expected: expected.into_iter().map(RichPattern::into_owned).collect(), - found: found.map(MaybeRef::into_owned), - }, - Self::Custom(msg) => RichReason::Custom(msg), - } - } - - fn take_found(&mut self) -> Option> { - match self { - RichReason::ExpectedFound { found, .. } => found.take(), - RichReason::Custom(_) => None, - } - } - - /// Transform this `RichReason`'s tokens using the given function. - /// - /// This is useful when you wish to combine errors from multiple compilation passes (lexing and parsing, say) where - /// the token type for each pass is different (`char` vs `MyToken`, say). - pub fn map_token U>(self, mut f: F) -> RichReason<'a, U> - where - T: Clone, - { - match self { - RichReason::ExpectedFound { expected, found } => RichReason::ExpectedFound { - expected: expected - .into_iter() - .map(|pat| pat.map_token(&mut f)) - .collect(), - found: found.map(|found| f(found.into_inner()).into()), - }, - RichReason::Custom(msg) => RichReason::Custom(msg), - } - } - - fn inner_fmt( - &self, - f: &mut fmt::Formatter<'_>, - mut fmt_token: impl FnMut(&T, &mut fmt::Formatter<'_>) -> fmt::Result, - mut fmt_span: impl FnMut(&S, &mut fmt::Formatter<'_>) -> fmt::Result, - span: Option<&S>, - context: &[(RichPattern<'a, T>, S)], - ) -> fmt::Result { - match self { - RichReason::ExpectedFound { expected, found } => { - write!(f, "found ")?; - write_token(f, &mut fmt_token, found.as_deref())?; - if let Some(span) = span { - write!(f, " at ")?; - fmt_span(span, f)?; - } - write!(f, " expected ")?; - match &expected[..] { - [] => write!(f, "something else")?, - [expected] => expected.write(f, &mut fmt_token)?, - _ => { - for expected in &expected[..expected.len() - 1] { - expected.write(f, &mut fmt_token)?; - write!(f, ", ")?; - } - write!(f, "or ")?; - expected.last().unwrap().write(f, &mut fmt_token)?; - } - } - } - RichReason::Custom(msg) => { - write!(f, "{msg}")?; - if let Some(span) = span { - write!(f, " at ")?; - fmt_span(span, f)?; - } - } - } - for (l, s) in context { - write!(f, " in ")?; - l.write(f, &mut fmt_token)?; - write!(f, " at ")?; - fmt_span(s, f)?; - } - Ok(()) - } -} - -impl RichReason<'_, T> -where - T: PartialEq, -{ - #[inline] - fn flat_merge(self, other: Self) -> Self { - match (self, other) { - // Prefer first error, if ambiguous - (a @ RichReason::Custom(_), _) => a, - (_, b @ RichReason::Custom(_)) => b, - ( - RichReason::ExpectedFound { - expected: mut this_expected, - found, - }, - RichReason::ExpectedFound { - expected: mut other_expected, - .. - }, - ) => { - // Try to avoid allocations if we possibly can by using the longer vector - if other_expected.len() > this_expected.len() { - core::mem::swap(&mut this_expected, &mut other_expected); - } - for expected in other_expected { - if !this_expected[..].contains(&expected) { - this_expected.push(expected); - } - } - RichReason::ExpectedFound { - expected: this_expected, - found, - } - } - } - } -} - -impl fmt::Display for RichReason<'_, T> -where - T: fmt::Display, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.inner_fmt(f, T::fmt, |_: &(), _| Ok(()), None, &[]) - } -} - -/// A rich default error type that tracks error spans, expected inputs, and the actual input found at an error site. -/// -/// Please note that it uses a [`Vec`] to remember expected symbols. If you find this to be too slow, you can -/// implement [`Error`] for your own error type or use [`Simple`] instead. -/// -/// This error type stores a span ([`SimpleSpan`] by default), a [`RichReason`], and a list of expected [`RichPattern`] with their spans. -/// -/// # Examples -/// -/// ``` -/// use chumsky::prelude::*; -/// use chumsky::error::{RichReason, RichPattern}; -/// -/// let parser = one_of::<_, _, extra::Err>>("1234"); -/// let error = parser.parse("5").into_errors()[0].clone(); -/// -/// assert_eq!(error.span(), &SimpleSpan::new((), 0..1)); -/// assert!(matches!(error.reason(), &RichReason::ExpectedFound {..})); -/// assert_eq!(error.found(), Some(&'5')); -/// -/// ``` -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Rich<'a, T, S = SimpleSpan> { - span: S, - reason: Box>, - context: Vec<(RichPattern<'a, T>, S)>, -} - -impl Rich<'_, T, S> { - fn inner_fmt( - &self, - f: &mut fmt::Formatter<'_>, - fmt_token: impl FnMut(&T, &mut fmt::Formatter<'_>) -> fmt::Result, - fmt_span: impl FnMut(&S, &mut fmt::Formatter<'_>) -> fmt::Result, - with_spans: bool, - ) -> fmt::Result { - self.reason.inner_fmt( - f, - fmt_token, - fmt_span, - if with_spans { Some(&self.span) } else { None }, - &self.context, - ) - } -} - -impl<'a, T, S> Rich<'a, T, S> { - /// Create an error with a custom message and span - #[inline] - pub fn custom(span: S, msg: M) -> Self { - Rich { - span, - reason: Box::new(RichReason::Custom(msg.to_string())), - context: Vec::new(), - } - } - - /// Get the span associated with this error. - /// - /// If the span type is unspecified, it is [`SimpleSpan`]. - pub fn span(&self) -> &S { - &self.span - } - - /// Get the reason for this error. - pub fn reason(&self) -> &RichReason<'a, T> { - &self.reason - } - - /// Take the reason from this error. - pub fn into_reason(self) -> RichReason<'a, T> { - *self.reason - } - - /// Get the token found by this error when parsing. `None` implies that the error expected the end of input. - pub fn found(&self) -> Option<&T> { - self.reason.found() - } - - /// Return an iterator over the labelled contexts of this error, from least general to most. - /// - /// 'Context' here means parser patterns that the parser was in the process of parsing when the error occurred. To - /// add labelled contexts, see [`Parser::labelled`]. - pub fn contexts(&self) -> impl Iterator, &S)> { - self.context.iter().map(|(l, s)| (l, s)) - } - - /// Convert this error into an owned version of itself by cloning any borrowed internal tokens, if necessary. - pub fn into_owned<'b>(self) -> Rich<'b, T, S> - where - T: Clone, - { - Rich { - reason: Box::new(self.reason.into_owned()), - context: self - .context - .into_iter() - .map(|(p, s)| (p.into_owned(), s)) - .collect(), - ..self - } - } - - /// Get an iterator over the expected items associated with this error - pub fn expected(&self) -> impl ExactSizeIterator> { - match &*self.reason { - RichReason::ExpectedFound { expected, .. } => expected.iter(), - RichReason::Custom(_) => [].iter(), - } - } - - /// Transform this error's tokens using the given function. - /// - /// This is useful when you wish to combine errors from multiple compilation passes (lexing and parsing, say) where - /// the token type for each pass is different (`char` vs `MyToken`, say). - pub fn map_token U>(self, mut f: F) -> Rich<'a, U, S> - where - T: Clone, - { - Rich { - span: self.span, - reason: Box::new(self.reason.map_token(&mut f)), - context: self - .context - .into_iter() - .map(|(p, s)| (p.map_token(&mut f), s)) - .collect(), - } - } -} - -impl<'a, I: Input<'a>> Error<'a, I> for Rich<'a, I::Token, I::Span> -where - I::Token: PartialEq, -{ - #[inline] - fn merge(self, other: Self) -> Self { - let new_reason = self.reason.flat_merge(*other.reason); - Self { - span: self.span, - reason: Box::new(new_reason), - context: self.context, // TOOD: Merge contexts - } - } -} - -impl<'a, I: Input<'a>, L> LabelError<'a, I, L> for Rich<'a, I::Token, I::Span> -where - I::Token: PartialEq, - L: Into>, -{ - #[inline] - fn expected_found>( - expected: E, - found: Option>, - span: I::Span, - ) -> Self { - Self { - span, - reason: Box::new(RichReason::ExpectedFound { - expected: expected.into_iter().map(|tok| tok.into()).collect(), - found, - }), - context: Vec::new(), - } - } - - #[inline] - fn merge_expected_found>( - mut self, - new_expected: E, - new_found: Option>, - _span: I::Span, - ) -> Self { - match &mut *self.reason { - RichReason::ExpectedFound { expected, found } => { - for new_expected in new_expected { - let new_expected = new_expected.into(); - if !expected[..].contains(&new_expected) { - expected.push(new_expected); - } - } - *found = found.take().or(new_found); //land - } - RichReason::Custom(_) => {} - } - // TOOD: Merge contexts - self - } - - #[inline] - fn replace_expected_found>( - mut self, - new_expected: E, - new_found: Option>, - span: I::Span, - ) -> Self { - self.span = span; - match &mut *self.reason { - RichReason::ExpectedFound { expected, found } => { - expected.clear(); - expected.extend(new_expected.into_iter().map(|tok| tok.into())); - *found = new_found; - } - _ => { - *self.reason = RichReason::ExpectedFound { - expected: new_expected.into_iter().map(|tok| tok.into()).collect(), - found: new_found, - }; - } - } - self.context.clear(); - self - } - - #[inline] - fn label_with(&mut self, label: L) { - // Opportunistically attempt to reuse allocations if we can - match &mut *self.reason { - RichReason::ExpectedFound { expected, found: _ } => { - expected.clear(); - expected.push(label.into()); - } - _ => { - *self.reason = RichReason::ExpectedFound { - expected: vec![label.into()], - found: self.reason.take_found(), - }; - } - } - } - - #[inline] - fn in_context(&mut self, label: L, span: I::Span) { - let label = label.into(); - if self.context.iter().all(|(l, _)| l != &label) { - self.context.push((label, span)); - } - } -} - -impl fmt::Debug for Rich<'_, T, S> -where - T: fmt::Debug, - S: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.inner_fmt(f, T::fmt, S::fmt, true) - } -} - -impl fmt::Display for Rich<'_, T, S> -where - T: fmt::Display, - S: fmt::Display, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.inner_fmt(f, T::fmt, S::fmt, false) - } -} - -fn write_token( - f: &mut fmt::Formatter, - mut fmt_token: impl FnMut(&T, &mut fmt::Formatter<'_>) -> fmt::Result, - tok: Option<&T>, -) -> fmt::Result { - match tok { - Some(tok) => { - write!(f, "'")?; - fmt_token(tok, f)?; - write!(f, "'") - } - None => write!(f, "end of input"), - } -} diff --git a/chumsky-0.12.0/src/lib.rs b/chumsky-0.12.0/src/lib.rs deleted file mode 100644 index 0575c7b75e..0000000000 --- a/chumsky-0.12.0/src/lib.rs +++ /dev/null @@ -1,4258 +0,0 @@ -#![cfg_attr(not(any(doc, feature = "std", test)), no_std)] -#![cfg_attr(docsrs, feature(doc_cfg), deny(rustdoc::all))] -#![cfg_attr( - feature = "nightly", - feature(never_type, fn_traits, tuple_trait, unboxed_closures, specialization) -)] -#![cfg_attr(feature = "nightly", allow(incomplete_features))] -#![doc = include_str!("../README.md")] -#![deny(missing_docs, clippy::undocumented_unsafe_blocks)] -// A lot of clippy's default lints are silly and annoying -#![allow( - clippy::style, - clippy::useless_format, - clippy::should_implement_trait, - clippy::type_complexity, - clippy::result_unit_err -)] - -extern crate alloc; -extern crate core; - -macro_rules! go_extra { - ( $O :ty ) => { - #[inline(always)] - fn go_emit(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult { - Parser::::go::(self, inp) - } - #[inline(always)] - fn go_check(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult { - Parser::::go::(self, inp) - } - }; -} - -mod blanket; -#[cfg(feature = "unstable")] -pub mod cache; -pub mod combinator; -pub mod container; -#[cfg(feature = "debug")] -pub mod debug; -#[cfg(feature = "either")] -mod either; -pub mod error; -#[cfg(feature = "extension")] -pub mod extension; -pub mod extra; -#[cfg(docsrs)] -pub mod guide; -pub mod input; -pub mod inspector; -pub mod label; -#[cfg(feature = "lexical-numbers")] -pub mod number; -#[cfg(feature = "pratt")] -pub mod pratt; -pub mod primitive; -mod private; -pub mod recovery; -pub mod recursive; -#[cfg(feature = "regex")] -pub mod regex; -pub mod span; -mod stream; -pub mod text; -#[cfg(feature = "bytes")] -mod tokio; -pub mod util; - -/// Commonly used functions, traits and types. -/// -/// *Listen, three eyes,†he said, “don’t you try to outweird me, I get stranger things than you free with my breakfast -/// cereal.â€* -pub mod prelude { - #[cfg(feature = "lexical-numbers")] - pub use super::number::number; - #[cfg(feature = "regex")] - pub use super::regex::regex; - pub use super::{ - error::{Cheap, EmptyErr, Error as _, Rich, Simple}, - extra, - input::Input, - primitive::{ - any, any_ref, choice, custom, empty, end, group, just, map_ctx, none_of, one_of, set, - todo, - }, - recovery::{nested_delimiters, skip_then_retry_until, skip_until, via_parser}, - recursive::{recursive, Recursive}, - span::{SimpleSpan, Span as _, SpanWrap as _, Spanned}, - text, Boxed, ConfigIterParser, ConfigParser, IterParser, ParseResult, Parser, - }; - pub use crate::{select, select_ref}; -} - -use crate::input::InputOwn; -use alloc::{ - boxed::Box, - rc::{self, Rc}, - string::String, - vec, - vec::Vec, -}; -#[cfg(feature = "nightly")] -use core::marker::Tuple; -use core::{ - borrow::Borrow, - cell::{Cell, RefCell}, - cmp::{Eq, Ord, Ordering}, - fmt, - hash::Hash, - marker::PhantomData, - mem::MaybeUninit, - ops::{Deref, DerefMut, Range, RangeFrom}, - panic::Location, - str::FromStr, -}; -use hashbrown::HashMap; -#[cfg(feature = "serde")] -use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; - -use self::{ - combinator::*, - container::*, - error::Error, - extra::ParserExtra, - input::{ - BorrowInput, Emitter, ExactSizeInput, InputRef, MapExtra, SliceInput, StrInput, ValueInput, - }, - inspector::Inspector, - label::{LabelError, Labelled, LabelledWith}, - prelude::*, - primitive::Any, - private::{Check, Emit, IPResult, Located, MaybeUninitExt, Mode, PResult, Sealed}, - recovery::{RecoverWith, Strategy}, - span::{Span, WrappingSpan}, - text::*, - util::{IntoMaybe, MaybeMut, MaybeRef}, -}; -#[cfg(all(feature = "extension", doc))] -use self::{extension::v1::*, primitive::custom, stream::Stream}; - -/// A type that allows mentioning type parameters *without* all of the customary omission of auto traits that comes -/// with `PhantomData`. -struct EmptyPhantom(core::marker::PhantomData); - -impl EmptyPhantom { - const fn new() -> Self { - Self(core::marker::PhantomData) - } -} - -impl Copy for EmptyPhantom {} -impl Clone for EmptyPhantom { - fn clone(&self) -> Self { - *self - } -} -// SAFETY: This is safe because `EmptyPhantom` doesn't actually contain a `T`. -unsafe impl Send for EmptyPhantom {} -// SAFETY: This is safe because `EmptyPhantom` doesn't actually contain a `T`. -unsafe impl Sync for EmptyPhantom {} -impl Unpin for EmptyPhantom {} -impl core::panic::UnwindSafe for EmptyPhantom {} -impl core::panic::RefUnwindSafe for EmptyPhantom {} - -pub(crate) type DynParser<'src, 'b, I, O, E> = dyn Parser<'src, I, O, E> + 'b; -#[cfg(feature = "pratt")] -pub(crate) type DynOperator<'src, 'b, I, O, E> = dyn pratt::Operator<'src, I, O, E> + 'b; - -/// Labels corresponding to a variety of patterns. -#[derive(Clone, Debug, PartialEq)] -#[non_exhaustive] -pub enum DefaultExpected<'a, T> { - /// A specific token was expected. - Token(MaybeRef<'a, T>), - /// Anything other than the end of input was expected. - Any, - /// Something other than the provided input was expected. - SomethingElse, - /// The end of input was expected. - EndOfInput, -} - -impl DefaultExpected<'_, T> { - /// Convert this [`DefaultExpected`] into an owned version of itself, cloning any inner references if required. - #[inline] - pub fn into_owned(self) -> DefaultExpected<'static, T> - where - T: Clone, - { - match self { - Self::Token(tok) => DefaultExpected::Token(tok.into_owned()), - Self::Any => DefaultExpected::Any, - Self::SomethingElse => DefaultExpected::SomethingElse, - Self::EndOfInput => DefaultExpected::EndOfInput, - } - } -} - -/// The result of performing a parse on an input with [`Parser`]. -/// -/// Unlike `Result`, this type is designed to express the fact that generating outputs and errors are not -/// mutually-exclusive operations: it is possible for a parse to produce non-terminal errors (see -/// [`Parser::recover_with`] while still producing useful output). -/// -/// If you don't care for recovered outputs and you with to treat success/failure as a binary, you may use -/// [`ParseResult::into_result`]. -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct ParseResult { - output: Option, - errs: Vec, -} - -impl ParseResult { - pub(crate) fn new(output: Option, errs: Vec) -> ParseResult { - ParseResult { output, errs } - } - - /// Whether this result contains output - pub fn has_output(&self) -> bool { - self.output.is_some() - } - - /// Whether this result has any errors - pub fn has_errors(&self) -> bool { - !self.errs.is_empty() - } - - /// Get a reference to the output of this result, if it exists - pub fn output(&self) -> Option<&T> { - self.output.as_ref() - } - - /// Get an iterator over the parse errors for this result. The iterator will produce no items if there were no - /// errors. - pub fn errors(&self) -> impl ExactSizeIterator + DoubleEndedIterator { - self.errs.iter() - } - - /// Convert this `ParseResult` into an option containing the output, if any exists - pub fn into_output(self) -> Option { - self.output - } - - /// Convert this `ParseResult` into a vector containing any errors. The vector will be empty if there were no - /// errors. - pub fn into_errors(self) -> Vec { - self.errs - } - - /// Convert this `ParseResult` into a tuple containing the output, if any existed, and errors, if any were - /// encountered. - pub fn into_output_errors(self) -> (Option, Vec) { - (self.output, self.errs) - } - - /// Convert this `ParseResult` into a standard `Result`. This discards output if parsing generated any errors, - /// matching the old behavior of [`Parser::parse`]. - pub fn into_result(self) -> Result> { - if self.errs.is_empty() { - self.output.ok_or(self.errs) - } else { - Err(self.errs) - } - } - - /// Convert this `ParseResult` into the output. If any errors were generated (including non-fatal errors!), a - /// panic will occur instead. - /// - /// The use of this function is discouraged in user-facing code. However, it may be convenient for use in tests. - #[track_caller] - pub fn unwrap(self) -> T - where - E: fmt::Debug, - { - if self.has_errors() { - panic!( - "called `ParseResult::unwrap` on a parse result containing errors: {:?}", - &self.errs - ) - } else { - self.output.expect("parser generated no errors or output") - } - } -} - -/// A trait implemented by parsers. -/// -/// Parsers take inputs of type `I`, which will implement [`Input`]. Refer to the documentation on [`Input`] for examples -/// of common input types. It will then attempt to parse them into a value of type `O`, which may be just about any type. -/// In doing so, they may encounter errors. These need not be fatal to the parsing process: syntactic errors can be -/// recovered from and a valid output may still be generated alongside any syntax errors that were encountered along the -/// way. Usually, this output comes in the form of an -/// [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) (AST). -/// -/// The final type parameter, `E`, is expected to be one of the type in the [`extra`] module, -/// implementing [`ParserExtra`]. This trait is used to encapsulate the various types a parser -/// uses that are not simply its input and output. Refer to the documentation on the [`ParserExtra`] trait -/// for more detail on the contained types. If not provided, it will default to [`extra::Default`], -/// which will have the least overhead, but also the least meaningful errors. -/// -/// The lifetime of the parser is used for zero-copy output - the input is bound by the lifetime, -/// and returned values or parser state may take advantage of this to borrow tokens or slices of the -/// input and hold on to them, if the input supports this. -/// -/// # Stability -/// -/// This trait is not intended to be implemented by downstream users of `chumsky`. While you can technically implement -/// it, doing so is considered to be outside the stability guarantees of the crate. Your code may break with a future, -/// semver-compatible release! Instead of implementing this trait, you should consider other options: -/// -/// 1) Try using combinators like [`Parser::try_map`] and [`Parser::validate`] to implement custom error generation -/// -/// 2) Use [`custom`] to implement your own parsing logic inline within an existing parser -/// -/// 3) Use chumsky's [`extension`] API to write an extension parser that feels like it's native to chumsky -/// -/// 4) If you believe you've found a common use-case that's missing from chumsky, you could open a pull request to -/// implement it in chumsky itself rather than implementing `Parser` yourself. -// #[cfg_attr( -// feature = "nightly", -// diagnostic::on_unimplemented( -// message = "The following is not a parser from `{I}` to `{O}`: `{Self}`", -// label = "This parser is not compatible because it does not implement `Parser<{I}, {O}, E>`", -// note = "You should check that the output types of your parsers are consistent with the combinators you're using", -// ) -// )] -pub trait Parser<'src, I: Input<'src>, O, E: ParserExtra<'src, I> = extra::Default> { - /// Generate debugging information for this parser. - /// - /// This is an unstable feature, and will likely remain so indefinitely. As such, it **does not fall inside the semver - /// guarantees** of the broader crate. It is intended to aid the development of parsers and should not be used as part - /// of production software. - #[cfg(feature = "debug")] - fn debug(&self) -> debug::DebugInfo<'_> { - debug::DebugInfo { - node_info: self.node_info(&mut Default::default()), - phantom: PhantomData, - } - } - - #[doc(hidden)] - #[cfg(feature = "debug")] - fn node_info(&self, _scope: &mut debug::NodeScope) -> debug::NodeInfo { - let ty = core::any::type_name::(); - debug::NodeInfo::Unknown(ty.split_once('<').map_or(ty, |(ty, _)| ty).to_string()) - } - - #[doc(hidden)] - fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult - where - Self: Sized; - - #[doc(hidden)] - fn go_emit(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult; - #[doc(hidden)] - fn go_check(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult; - - /// Parse a stream of tokens, yielding an output if possible, and any errors encountered along the way. - /// - /// If `None` is returned (i.e: parsing failed) then there will *always* be at least one item in the error `Vec`. - /// If you want to include non-default state, use [`Parser::parse_with_state`] instead. - /// - /// Although the signature of this function looks complicated, it's simpler than you think! You can pass a - /// [`&[T]`], a [`&str`], [`Stream`], or anything implementing [`Input`] to it. - fn parse(&self, input: I) -> ParseResult - where - I: Input<'src>, - E::State: Default, - E::Context: Default, - { - self.parse_with_state(input, &mut E::State::default()) - } - - /// Parse a stream of tokens, yielding an output if possible, and any errors encountered along the way. - /// The provided state will be passed on to parsers that expect it, such as [`map_with`](Parser::map_with). - /// - /// If `None` is returned (i.e: parsing failed) then there will *always* be at least one item in the error `Vec`. - /// If you want to just use a default state value, use [`Parser::parse`] instead. - /// - /// Although the signature of this function looks complicated, it's simpler than you think! You can pass a - /// [`&[T]`], a [`&str`], [`Stream`], or anything implementing [`Input`] to it. - fn parse_with_state(&self, input: I, state: &mut E::State) -> ParseResult - where - I: Input<'src>, - E::Context: Default, - { - let mut own = InputOwn::new_state(input, state); - let mut inp = own.as_ref_start(); - let res = self.then_ignore(end()).go::(&mut inp); - let alt = inp.take_alt().map(|alt| alt.err).unwrap_or_else(|| { - let fake_span = inp.span_since(&inp.cursor()); - // TODO: Why is this needed? - E::Error::expected_found([], None, fake_span) - }); - let mut errs = own.into_errs(); - let out = match res { - Ok(out) => Some(out), - Err(()) => { - errs.push(alt); - None - } - }; - ParseResult::new(out, errs) - } - - /// Parse a stream of tokens, ignoring any output, and returning any errors encountered along the way. - /// - /// If parsing failed, then there will *always* be at least one item in the returned `Vec`. - /// If you want to include non-default state, use [`Parser::check_with_state`] instead. - /// - /// Although the signature of this function looks complicated, it's simpler than you think! You can pass a - /// [`&[T]`], a [`&str`], [`Stream`], or anything implementing [`Input`] to it. - fn check(&self, input: I) -> ParseResult<(), E::Error> - where - Self: Sized, - I: Input<'src>, - E::State: Default, - E::Context: Default, - { - self.check_with_state(input, &mut E::State::default()) - } - - /// Parse a stream of tokens, ignoring any output, and returning any errors encountered along the way. - /// - /// If parsing failed, then there will *always* be at least one item in the returned `Vec`. - /// If you want to just use a default state value, use [`Parser::check`] instead. - /// - /// Although the signature of this function looks complicated, it's simpler than you think! You can pass a - /// [`&[T]`], a [`&str`], [`Stream`], or anything implementing [`Input`] to it. - fn check_with_state(&self, input: I, state: &mut E::State) -> ParseResult<(), E::Error> - where - Self: Sized, - I: Input<'src>, - E::Context: Default, - { - let mut own = InputOwn::new_state(input, state); - let mut inp = own.as_ref_start(); - let res = self.then_ignore(end()).go::(&mut inp); - let alt = inp.take_alt().map(|alt| alt.err).unwrap_or_else(|| { - let fake_span = inp.span_since(&inp.cursor()); - // TODO: Why is this needed? - E::Error::expected_found([], None, fake_span) - }); - let mut errs = own.into_errs(); - let out = match res { - Ok(()) => Some(()), - Err(()) => { - errs.push(alt); - None - } - }; - ParseResult::new(out, errs) - } - - /// Convert the output of this parser into a slice of the input, based on the current parser's - /// span. - /// - /// Note: unlike the parser `.repeated().collect()`, this method includes all tokens that are - /// "ignored" by the parser, including any padding, separators, and sub-parsers with - /// [`Parser::ignored`], [`Parser::ignore_then`], and [`Parser::then_ignore`]. - /// - /// # Examples - /// Example with input of type `&str` (token type is `char`). - /// ``` - /// # use chumsky::prelude::*; - /// // Matches a number with underscores that is surrounded by apostrophes. - /// let quoted_numeric = any::<&str, extra::Err>>() - /// .filter(|c: &char| c.is_digit(10)) - /// .separated_by(just("_").repeated().at_most(1)) - /// .to_slice() - /// .padded_by(just("'")); - /// assert_eq!(quoted_numeric.parse("'1_23'").into_result(), Ok("1_23")); - /// ``` - /// Example with input of type `&[u32]` (token type is `u32`). - /// ``` - /// # use chumsky::prelude::*; - /// // Matches even numbers, then ignoring the rest of the input when an odd number is reached. - /// let even_matcher = any::<&[u32], extra::Err>>() - /// .filter(|c: &u32| c % 2 == 0) - /// .repeated() - /// .to_slice() - /// .lazy(); - /// assert_eq!(even_matcher.parse(&[2, 4, 8, 5, 6]).unwrap(), &[2, 4, 8]); - /// ``` - fn to_slice(self) -> ToSlice - where - Self: Sized, - { - ToSlice { - parser: self, - phantom: EmptyPhantom::new(), - } - } - - /// Filter the output of this parser, accepting only inputs that match the given predicate. - /// - /// The output type of this parser is `I`, the input that was found. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// let lowercase = any::<_, extra::Err>>() - /// .filter(char::is_ascii_lowercase) - /// .repeated() - /// .at_least(1) - /// .collect::(); - /// - /// assert_eq!(lowercase.parse("hello").into_result(), Ok("hello".to_string())); - /// assert!(lowercase.parse("Hello").has_errors()); - /// ``` - fn filter bool>(self, f: F) -> Filter - where - Self: Sized, - { - Filter { - parser: self, - filter: f, - } - } - - /// Map the output of this parser to another value. - /// - /// The output type of this parser is `U`, the same as the function's output. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// #[derive(Debug, PartialEq)] - /// enum Token { Word(String), Num(u64) } - /// - /// let word = any::<_, extra::Err>>() - /// .filter(|c: &char| c.is_alphabetic()) - /// .repeated().at_least(1) - /// .collect::() - /// .map(Token::Word); - /// - /// let num = any::<_, extra::Err>>() - /// .filter(|c: &char| c.is_ascii_digit()) - /// .repeated().at_least(1) - /// .collect::() - /// .map(|s| Token::Num(s.parse().unwrap())); - /// - /// let token = word.or(num); - /// - /// assert_eq!(token.parse("test").into_result(), Ok(Token::Word("test".to_string()))); - /// assert_eq!(token.parse("42").into_result(), Ok(Token::Num(42))); - /// ``` - fn map U>(self, f: F) -> Map - where - Self: Sized, - { - Map { - parser: self, - mapper: f, - phantom: EmptyPhantom::new(), - } - } - - /// Map the output of this parser to another value, with the opportunity to get extra metadata from the parse like the span or parser state. - /// - /// See the docs for [`MapExtra`] for examples of metadata that can be fetched. - /// - /// The output type of this parser is `U`, the same as the function's output. - /// - /// # Examples - /// - /// Using the span of the output in the mapping function: - /// - /// ``` - /// # use chumsky::prelude::*; - /// - /// // It's common for AST nodes to use a wrapper type that allows attaching span information to them - /// #[derive(Debug, PartialEq)] - /// pub struct Spanned(T, SimpleSpan); - /// - /// let ident = text::ascii::ident::<_, extra::Err>>() - /// .map_with(|ident, e| Spanned(ident, e.span())) // Equivalent to `.map_with_span(|ident, span| Spanned(ident, span))` - /// .padded(); - /// - /// assert_eq!(ident.parse("hello").into_result(), Ok(Spanned("hello", (0..5).into()))); - /// assert_eq!(ident.parse(" hello ").into_result(), Ok(Spanned("hello", (7..12).into()))); - /// ``` - /// - /// Using the parser state in the mapping function to intern strings: - /// - /// ``` - /// # use chumsky::prelude::*; - /// use std::ops::Range; - /// use lasso::{Rodeo, Spur}; - /// - /// // It's common for AST nodes to use interned versions of identifiers - /// // Keys are generally smaller, faster to compare, and can be `Copy` - /// #[derive(Copy, Clone)] - /// pub struct Ident(Spur); - /// - /// let ident = text::ascii::ident::<_, extra::Full, extra::SimpleState, ()>>() - /// .map_with(|ident, e| Ident(e.state().get_or_intern(ident))) - /// .padded() - /// .repeated() - /// .at_least(1) - /// .collect::>(); - /// - /// // Test out parser - /// - /// let mut interner = extra::SimpleState(Rodeo::new()); - /// - /// match ident.parse_with_state("hello", &mut interner).into_result() { - /// Ok(idents) => { - /// assert_eq!(interner.resolve(&idents[0].0), "hello"); - /// } - /// Err(e) => panic!("Parsing Failed: {:?}", e), - /// } - /// - /// match ident.parse_with_state("hello hello", &mut interner).into_result() { - /// Ok(idents) => { - /// assert_eq!(idents[0].0, idents[1].0); - /// } - /// Err(e) => panic!("Parsing Failed: {:?}", e), - /// } - /// ``` - /// - /// Using the parse context in the mapping function: - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// - /// fn palindrome_parser<'src>() -> impl Parser<'src, &'src str, String> { - /// recursive(|chain| { - /// choice(( - /// just(String::new()) - /// .configure(|cfg, ctx: &String| cfg.seq(ctx.clone())) - /// .then_ignore(end()), - /// any() - /// .map_with(|x, e| format!("{x}{}", e.ctx())) - /// .ignore_with_ctx(chain), - /// )) - /// }) - /// .with_ctx(String::new()) - /// } - /// - /// assert_eq!(palindrome_parser().parse("abccba").into_result().as_deref(), Ok("cba")); - /// assert_eq!(palindrome_parser().parse("hello olleh").into_result().as_deref(), Ok(" olleh")); - /// assert!(palindrome_parser().parse("abccb").into_result().is_err()); - /// ``` - fn map_with) -> U>(self, f: F) -> MapWith - where - Self: Sized, - { - MapWith { - parser: self, - mapper: f, - phantom: EmptyPhantom::new(), - } - } - - /// Map the output of this parser to another value. - /// If the output of this parser isn't a tuple, use [`Parser::map`]. - /// - /// The output type of this parser is `U`, the same as the function's output. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::prelude::*; - /// #[derive(Clone, Copy, Debug, PartialEq, Eq)] - /// pub enum Value { - /// One(u8), - /// Two(u8, u8), - /// Three(u8, u8, u8), - /// } - /// - /// fn parser<'src>() -> impl Parser<'src, &'src [u8], Vec> { - /// choice(( - /// just(1).ignore_then(any()).map(Value::One), - /// just(2) - /// .ignore_then(group((any(), any()))) - /// .map_group(Value::Two), - /// just(3) - /// .ignore_then(group((any(), any(), any()))) - /// .map_group(Value::Three), - /// )) - /// .repeated() - /// .collect() - /// } - /// - /// let bytes = &[3, 1, 2, 3, 1, 127, 2, 21, 69]; - /// assert_eq!( - /// parser().parse(bytes).into_result(), - /// Ok(vec![ - /// Value::Three(1, 2, 3), - /// Value::One(127), - /// Value::Two(21, 69) - /// ]) - /// ); - /// ``` - #[cfg(feature = "nightly")] - fn map_group>(self, f: F) -> MapGroup - where - Self: Sized, - O: Tuple, - { - MapGroup { - parser: self, - mapper: f, - phantom: EmptyPhantom::new(), - } - } - - /// Transform the output of this parser to the pattern's span. - /// - /// This is commonly used when you know what pattern you've parsed and are only interested in the span of the - /// pattern. - /// - /// The output type of this parser is `I::Span`. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::prelude::*; - /// - /// // It's common for AST nodes to use a wrapper type that allows attaching span information to them - /// #[derive(Debug, PartialEq)] - /// pub enum Expr<'src> { - /// Int(&'src str, SimpleSpan), - /// // The span is that of the operator, '+' - /// Add(Box>, SimpleSpan, Box>), - /// } - /// - /// let int = text::int::<_, extra::Err>>(10) - /// .to_slice() - /// .map_with(|int, e| Expr::Int(int, e.span())) - /// .padded(); - /// - /// let add_op = just('+').to_span().padded(); - /// let sum = int.foldl( - /// add_op.then(int).repeated(), - /// |a, (op_span, b)| Expr::Add(Box::new(a), op_span, Box::new(b)), - /// ); - /// - /// assert_eq!(sum.parse("42 + 7 + 13").into_result(), Ok(Expr::Add( - /// Box::new(Expr::Add( - /// Box::new(Expr::Int("42", (0..2).into())), - /// (3..4).into(), - /// Box::new(Expr::Int("7", (5..6).into())), - /// )), - /// (7..8).into(), - /// Box::new(Expr::Int("13", (9..11).into())), - /// ))); - /// ``` - fn to_span(self) -> ToSpan - where - Self: Sized, - { - ToSpan { - parser: self, - phantom: EmptyPhantom::new(), - } - } - /// Left-fold the output of the parser into a single value, possibly failing during the reduction. - /// The parser only consumes input from the inner parser until it either completes or the reduction - /// step fails ("short circuting"). - /// - /// The output type of this parser is `A`, the left-hand component of the original parser's output. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// let int = text::int::<_, extra::Err>>(10) - /// .from_str::() - /// .unwrapped(); - /// - /// let sum = int - /// .clone() - /// .try_foldl(just('+').ignore_then(int).repeated(), |a, b, e| a.checked_add(b).ok_or(Simple::new(None, e.span()))); - /// - /// assert_eq!(sum.parse("1+12+3+9").into_result(), Ok(25)); - /// assert_eq!(sum.parse("6").into_result(), Ok(6)); - /// assert!(sum.parse("255+1").has_errors()); // due to u8 overflow - /// ``` - #[cfg_attr(debug_assertions, track_caller)] - fn try_foldl(self, other: B, f: F) -> TryFoldl - where - F: Fn(O, OB, &mut MapExtra<'src, '_, I, E>) -> Result, - B: IterParser<'src, I, OB, E>, - Self: Sized, - { - TryFoldl { - parser_a: self, - parser_b: other, - folder: f, - phantom: EmptyPhantom::new(), - } - } - - /// Wrap the output of this parser in the pattern's span. - /// - /// This is often used to preserve the span of AST nodes for error generation by future passes. - /// - /// The output type of this parser is `::Spanned`. For parsers using [`SimpleSpan`], - /// that means the output type is [`Spanned`]. - fn spanned(self) -> combinator::Spanned - where - Self: Sized, - { - combinator::Spanned { - parser: self, - phantom: EmptyPhantom::new(), - } - } - - /// After a successful parse, apply a fallible function to the output. If the function produces an error, treat it - /// as a parsing error. - /// - /// If you wish parsing of this pattern to continue when an error is generated instead of halting, consider using - /// [`Parser::validate`] instead. - /// - /// The output type of this parser is `U`, the [`Ok`] return value of the function. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::prelude::*; - /// let byte = text::int::<_, extra::Err>>(10) - /// .try_map(|s: &str, span| s - /// .parse::() - /// .map_err(|e| Rich::custom(span, e))); - /// - /// assert!(byte.parse("255").has_output()); - /// assert!(byte.parse("256").has_errors()); // Out of range - /// ``` - #[doc(alias = "filter_map")] - fn try_map Result>(self, f: F) -> TryMap - where - Self: Sized, - { - TryMap { - parser: self, - mapper: f, - phantom: EmptyPhantom::new(), - } - } - - /// After a successful parse, apply a fallible function to the output, with the opportunity to get extra metadata. - /// If the function produces an error, treat it as a parsing error. - /// - /// If you wish parsing of this pattern to continue when an error is generated instead of halting, consider using - /// [`Parser::validate`] instead. - /// - /// The output type of this parser is `U`, the [`Ok`] return value of the function. - fn try_map_with) -> Result>( - self, - f: F, - ) -> TryMapWith - where - Self: Sized, - { - TryMapWith { - parser: self, - mapper: f, - phantom: EmptyPhantom::new(), - } - } - - /// Ignore the output of this parser, yielding `()` as an output instead. - /// - /// This can be used to reduce the cost of parsing by avoiding unnecessary allocations (most collections containing - /// [ZSTs](https://doc.rust-lang.org/nomicon/exotic-sizes.html#zero-sized-types-zsts) - /// [do not allocate](https://doc.rust-lang.org/std/vec/struct.Vec.html#guarantees)). For example, it's common to - /// want to ignore whitespace in many grammars (see [`text::whitespace`]). - /// - /// The output type of this parser is `()`. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// // A parser that parses any number of whitespace characters without allocating - /// let whitespace = any::<_, extra::Err>>() - /// .filter(|c: &char| c.is_whitespace()) - /// .ignored() - /// .repeated() - /// .collect::>(); - /// - /// assert_eq!(whitespace.parse(" ").into_result(), Ok(vec![(); 4])); - /// assert!(whitespace.parse(" hello").has_errors()); - /// ``` - fn ignored(self) -> Ignored - where - Self: Sized, - { - Ignored { - parser: self, - phantom: EmptyPhantom::new(), - } - } - - /// Memoize the parser such that later attempts to parse the same input 'remember' the attempt and exit early. - /// - /// If you're finding that certain inputs produce exponential behavior in your parser, strategically applying - /// memoization to a ['garden path'](https://en.wikipedia.org/wiki/Garden-path_sentence) rule is often an effective - /// way to solve the problem. At the limit, applying memoization to all combinators will turn any parser into one - /// with `O(n)`, albeit with very significant per-element overhead and high memory usage. - /// - /// Memoization also works with recursion, so this can be used to write parsers using - /// [left recursion](https://en.wikipedia.org/wiki/Left_recursion). - // TODO: Example - #[cfg(feature = "memoization")] - fn memoized(self) -> Memoized - where - Self: Sized, - { - Memoized { parser: self } - } - - /// Transform all outputs of this parser to a predetermined value. - /// - /// The output type of this parser is `U`, the type of the predetermined value. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// #[derive(Clone, Debug, PartialEq)] - /// enum Op { Add, Sub, Mul, Div } - /// - /// let op = just::<_, _, extra::Err>>('+').to(Op::Add) - /// .or(just('-').to(Op::Sub)) - /// .or(just('*').to(Op::Mul)) - /// .or(just('/').to(Op::Div)); - /// - /// assert_eq!(op.parse("+").into_result(), Ok(Op::Add)); - /// assert_eq!(op.parse("/").into_result(), Ok(Op::Div)); - /// ``` - fn to(self, to: U) -> To - where - Self: Sized, - { - To { - parser: self, - to, - phantom: EmptyPhantom::new(), - } - } - - /// Label this parser with the given label. - /// - /// Labelling a parser makes all errors generated by the parser refer to the label rather than any sub-elements - /// within the parser. For example, labelling a parser for an expression would yield "expected expression" errors - /// rather than "expected integer, string, binary op, etc." errors. - // TODO: Example - fn labelled(self, label: L) -> Labelled - where - Self: Sized, - E::Error: LabelError<'src, I, L>, - { - Labelled { - parser: self, - label, - is_context: false, - is_builtin: false, - } - } - - /// Label this parser with a generated label, with the opportunity to get extra metadata from the parse like the - /// span or parser state. - /// - /// Labelling a parser makes all errors generated by the parser refer to the label rather than any sub-elements - /// within the parser. For example, labelling a parser for an expression would yield "expected expression" errors - /// rather than "expected integer, string, binary op, etc." errors. - fn labelled_with(self, label: F) -> LabelledWith - where - Self: Sized, - E::Error: LabelError<'src, I, L>, - F: Fn() -> L, - { - LabelledWith { - parser: self, - label, - is_context: false, - is_builtin: false, - phantom: PhantomData, - } - } - - /// Parse one thing and then another thing, yielding a tuple of the two outputs. - /// - /// The output type of this parser is `(O, U)`, a combination of the outputs of both parsers. - /// - /// If you instead only need the output of __one__ of the parsers, use [`ignore_then`](Self::ignore_then) - /// or [`then_ignore`](Self::then_ignore). - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// let word = any::<_, extra::Err>>() - /// .filter(|c: &char| c.is_alphabetic()) - /// .repeated() - /// .at_least(1) - /// .collect::(); - /// let two_words = word.then_ignore(just(' ')).then(word); - /// - /// assert_eq!(two_words.parse("dog cat").into_result(), Ok(("dog".to_string(), "cat".to_string()))); - /// assert!(two_words.parse("hedgehog").has_errors()); - /// ``` - fn then>(self, other: B) -> Then - where - Self: Sized, - { - Then { - parser_a: self, - parser_b: other, - phantom: EmptyPhantom::new(), - } - } - - /// Parse one thing and then another thing, yielding only the output of the latter. - /// - /// The output type of this parser is `U`, the same as the second parser. - /// - /// If you instead only need the output of the first parser, use [`then_ignore`](Self::then_ignore). - /// If you need the output of __both__ parsers, use [`then`](Self::then). - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// let zeroes = any::<_, extra::Err>>().filter(|c: &char| *c == '0').ignored().repeated().collect::>(); - /// let digits = any().filter(|c: &char| c.is_ascii_digit()) - /// .repeated() - /// .collect::(); - /// let integer = zeroes - /// .ignore_then(digits) - /// .from_str() - /// .unwrapped(); - /// - /// assert_eq!(integer.parse("00064").into_result(), Ok(64)); - /// assert_eq!(integer.parse("32").into_result(), Ok(32)); - /// ``` - fn ignore_then>(self, other: B) -> IgnoreThen - where - Self: Sized, - { - IgnoreThen { - parser_a: self, - parser_b: other, - phantom: EmptyPhantom::new(), - } - } - - /// Parse one thing and then another thing, yielding only the output of the former. - /// - /// The output type of this parser is `O`, the same as the original parser. - /// - /// If you instead only need the output of the second parser, use [`ignore_then`](Self::ignore_then). - /// If you need the output of __both__ parsers, use [`then`](Self::then). - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// let word = any::<_, extra::Err>>() - /// .filter(|c: &char| c.is_alphabetic()) - /// .repeated() - /// .at_least(1) - /// .collect::(); - /// - /// let punctuated = word - /// .then_ignore(just('!').or(just('?')).or_not()); - /// - /// let sentence = punctuated - /// .padded() // Allow for whitespace gaps - /// .repeated() - /// .collect::>(); - /// - /// assert_eq!( - /// sentence.parse("hello! how are you?").into_result(), - /// Ok(vec![ - /// "hello".to_string(), - /// "how".to_string(), - /// "are".to_string(), - /// "you".to_string(), - /// ]), - /// ); - /// ``` - fn then_ignore>(self, other: B) -> ThenIgnore - where - Self: Sized, - { - ThenIgnore { - parser_a: self, - parser_b: other, - phantom: EmptyPhantom::new(), - } - } - - /// Parse input as part of a token-tree - using an input generated from within the current - /// input. In other words, this parser will attempt to create a *new* input stream from within - /// the one it is being run on, and the parser it was called on will be provided this *new* input. - /// By default, the original parser is expected to consume up to the end of the new stream. To - /// allow only consuming part of the stream, use [`Parser::lazy`] to ignore trailing tokens. - /// - /// The provided parser `P` is expected to have both an input and output type which match the input - /// type of the parser it is called on. As an example, if the original parser takes an input of - /// `Stream>`, `P` will be run first against that input, and is expected to - /// output a new `Stream>` which the original parser will be run against. - /// - /// The output of this parser is `O`, the output of the parser it is called on. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, util::MaybeRef, error::Simple}; - /// #[derive(Debug, Clone, PartialEq)] - /// enum Token<'src> { - /// Struct, - /// Ident(&'src str), - /// Item(&'src str), - /// Group(Vec>), - /// } - /// - /// let group = select_ref! { Token::Group(g) => g.as_slice() }; - /// - /// let ident = select_ref! { Token::Ident(i) => *i }; - /// - /// let items = select_ref! { Token::Item(i) => *i } - /// .repeated() - /// .collect::>() - /// .nested_in(group); - /// - /// let struc = just::<_, _, extra::Err>>(&Token::Struct) - /// .ignore_then(ident) - /// .then(items); - /// - /// let tl = struc - /// .repeated() - /// .collect::>(); - /// - /// let tokens = [ - /// Token::Struct, - /// Token::Ident("foo"), - /// Token::Group(vec![ - /// Token::Item("a"), - /// Token::Item("b"), - /// ]), - /// ]; - /// - /// assert_eq!(tl.parse(&tokens).into_result(), Ok(vec![("foo", vec!["a", "b"])])); - /// ``` - fn nested_in, J, F>(self, other: B) -> NestedIn - where - Self: Sized, - I: 'src, - J: Input<'src>, - F: ParserExtra<'src, J>, - { - NestedIn { - parser_a: self, - parser_b: other, - phantom: EmptyPhantom::new(), - } - } - - /// Parse one thing and then another thing, creating the second parser from the result of - /// the first. If you do need the context in the output, use [`Parser::then_with_ctx`]. - /// - /// The output of this parser is `U`, the result of the second parser - /// - /// Error recovery for this parser may be sub-optimal, as if the first parser succeeds on - /// recovery then the second produces an error, the primary error will point to the location in - /// the second parser which failed, ignoring that the first parser may be the root cause. There - /// may be other pathological errors cases as well. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// let successor = just(b'\0').configure(|cfg, ctx: &u8| cfg.seq(*ctx + 1)); - /// - /// // A parser that parses a single letter and then its successor - /// let successive_letters = one_of::<_, _, extra::Err>>(b'a'..=b'z') - /// .ignore_with_ctx(successor); - /// - /// assert_eq!(successive_letters.parse(b"ab").into_result(), Ok(b'b')); // 'b' follows 'a' - /// assert!(successive_letters.parse(b"ac").has_errors()); // 'c' does not follow 'a' - /// ``` - fn ignore_with_ctx( - self, - then: P, - ) -> IgnoreWithCtx> - where - Self: Sized, - O: 'src, - P: Parser<'src, I, U, extra::Full>, - { - IgnoreWithCtx { - parser: self, - then, - phantom: EmptyPhantom::new(), - } - } - - /// Parse one thing and then another thing, creating the second parser from the result of - /// the first. If you don't need the context in the output, prefer [`Parser::ignore_with_ctx`]. - /// - /// The output of this parser is `(E::Context, O)`, - /// a combination of the context and the output of the parser. - /// - /// Error recovery for this parser may be sub-optimal, as if the first parser succeeds on - /// recovery then the second produces an error, the primary error will point to the location in - /// the second parser which failed, ignoring that the first parser may be the root cause. There - /// may be other pathological errors cases as well. - fn then_with_ctx( - self, - then: P, - ) -> ThenWithCtx> - where - Self: Sized, - O: 'src, - P: Parser<'src, I, U, extra::Full>, - { - ThenWithCtx { - parser: self, - then, - phantom: EmptyPhantom::new(), - } - } - - /// Run the previous contextual parser with the provided context. - /// - /// ``` - /// # use chumsky::prelude::*; - /// # use chumsky::primitive::JustCfg; - /// - /// let generic = just(b'0').configure(|cfg, ctx: &u8| cfg.seq(*ctx)); - /// - /// let parse_a = just::<_, _, extra::Default>(b'b').ignore_then(generic.with_ctx(b'a')); - /// let parse_b = just::<_, _, extra::Default>(b'a').ignore_then(generic.with_ctx(b'b')); - /// - /// assert_eq!(parse_a.parse(b"ba" as &[_]).into_result(), Ok::<_, Vec>(b'a')); - /// assert!(parse_a.parse(b"bb").has_errors()); - /// assert_eq!(parse_b.parse(b"ab" as &[_]).into_result(), Ok(b'b')); - /// assert!(parse_b.parse(b"aa").has_errors()); - /// ``` - fn with_ctx(self, ctx: E::Context) -> WithCtx - where - Self: Sized, - E::Context: Clone, - { - WithCtx { parser: self, ctx } - } - - /// Runs the previous parser with the provided state. - /// - /// This is very uncommonly used and exists mostly for completeness. - /// - /// One possible use-case is 'glueing' together parsers declared in different places with incompatible state types. - /// - /// Note that the state value will be cloned and dropping *during* parsing, so it is recommended to ensure that - /// this is a relatively performant operation. - fn with_state(self, state: State) -> WithState - where - Self: Sized, - State: 'src + Clone, - { - WithState { - parser: self, - state, - } - } - - /// Applies both parsers to the same position in the input, succeeding - /// only if both succeed. The returned value will be that of the first parser, - /// and the input will be at the end of the first parser if `and_is` succeeds. - /// - /// The second parser is allowed to consume more or less input than the first parser, - /// but like its output, how much it consumes won't affect the final result. - /// - /// The motivating use-case is in combination with [`Parser::not`], allowing a parser - /// to consume something only if it isn't also something like an escape sequence or a nested block. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// - /// let escape = just("\\n").to('\n'); - /// - /// // C-style string literal - /// let string = none_of::<_, _, extra::Err>>('"') - /// .and_is(escape.not()) - /// .or(escape) - /// .repeated() - /// .collect::() - /// .padded_by(just('"')); - /// - /// assert_eq!( - /// string.parse("\"wxyz\"").into_result().as_deref(), - /// Ok("wxyz"), - /// ); - /// assert_eq!( - /// string.parse("\"a\nb\"").into_result().as_deref(), - /// Ok("a\nb"), - /// ); - /// ``` - fn and_is(self, other: B) -> AndIs - where - Self: Sized, - B: Parser<'src, I, U, E>, - { - AndIs { - parser_a: self, - parser_b: other, - phantom: EmptyPhantom::new(), - } - } - - /// Parse the pattern surrounded by the given delimiters. - /// - /// The output type of this parser is `O`, the same as the original parser. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// // A LISP-style S-expression - /// #[derive(Debug, PartialEq)] - /// enum SExpr { - /// Ident(String), - /// Num(u64), - /// List(Vec), - /// } - /// - /// let ident = any::<_, extra::Err>>().filter(|c: &char| c.is_alphabetic()) - /// .repeated() - /// .at_least(1) - /// .collect::(); - /// - /// let num = text::int(10) - /// .from_str() - /// .unwrapped(); - /// - /// let s_expr = recursive(|s_expr| s_expr - /// .padded() - /// .repeated() - /// .collect::>() - /// .map(SExpr::List) - /// .delimited_by(just('('), just(')')) - /// .or(ident.map(SExpr::Ident)) - /// .or(num.map(SExpr::Num))); - /// - /// // A valid input - /// assert_eq!( - /// s_expr.parse("(add (mul 42 3) 15)").into_result(), - /// Ok(SExpr::List(vec![ - /// SExpr::Ident("add".to_string()), - /// SExpr::List(vec![ - /// SExpr::Ident("mul".to_string()), - /// SExpr::Num(42), - /// SExpr::Num(3), - /// ]), - /// SExpr::Num(15), - /// ])), - /// ); - /// ``` - fn delimited_by(self, start: B, end: C) -> DelimitedBy - where - Self: Sized, - B: Parser<'src, I, U, E>, - C: Parser<'src, I, V, E>, - { - DelimitedBy { - parser: self, - start, - end, - phantom: EmptyPhantom::new(), - } - } - - /// Parse a pattern, but with an instance of another pattern on either end, yielding the output of the inner. - /// - /// The output type of this parser is `O`, the same as the original parser. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// let ident = text::ascii::ident::<_, extra::Err>>() - /// .padded_by(just('!')); - /// - /// assert_eq!(ident.parse("!hello!").into_result(), Ok("hello")); - /// assert!(ident.parse("hello!").has_errors()); - /// assert!(ident.parse("!hello").has_errors()); - /// assert!(ident.parse("hello").has_errors()); - /// ``` - fn padded_by(self, padding: B) -> PaddedBy - where - Self: Sized, - B: Parser<'src, I, U, E>, - { - PaddedBy { - parser: self, - padding, - phantom: EmptyPhantom::new(), - } - } - - /// Parse one thing or, on failure, another thing. - /// - /// The output of both parsers must be of the same type, because either output can be produced. - /// - /// If both parser succeed, the output of the first parser is guaranteed to be prioritized over the output of the - /// second. - /// - /// If both parsers produce errors, the combinator will attempt to select from or combine the errors to produce an - /// error that is most likely to be useful to a human attempting to understand the problem. The exact algorithm - /// used is left unspecified, and is not part of the crate's semver guarantees, although regressions in error - /// quality should be reported in the issue tracker of the main repository. - /// - /// Please note that long chains of [`Parser::or`] combinators have been known to result in poor compilation times. - /// If you feel you are experiencing this, consider using [`choice`] instead. - /// - /// The output type of this parser is `O`, the output of both parsers. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// let op = just::<_, _, extra::Err>>('+') - /// .or(just('-')) - /// .or(just('*')) - /// .or(just('/')); - /// - /// assert_eq!(op.parse("+").into_result(), Ok('+')); - /// assert_eq!(op.parse("/").into_result(), Ok('/')); - /// assert!(op.parse("!").has_errors()); - /// ``` - fn or(self, other: B) -> Or - where - Self: Sized, - B: Parser<'src, I, O, E>, - { - Or { - choice: choice((self, other)), - } - } - - /// Attempt to parse something, but only if it exists. - /// - /// If parsing of the pattern is successful, the output is `Some(_)`. Otherwise, the output is `None`. - /// - /// The output type of this parser is `Option`. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// let word = any::<_, extra::Err>>().filter(|c: &char| c.is_alphabetic()) - /// .repeated() - /// .at_least(1) - /// .collect::(); - /// - /// let word_or_question = word - /// .then(just('?').or_not()); - /// - /// assert_eq!(word_or_question.parse("hello?").into_result(), Ok(("hello".to_string(), Some('?')))); - /// assert_eq!(word_or_question.parse("wednesday").into_result(), Ok(("wednesday".to_string(), None))); - /// ``` - fn or_not(self) -> OrNot - where - Self: Sized, - { - OrNot { parser: self } - } - - /// Invert the result of the contained parser, failing if it succeeds and succeeding if it fails. - /// The output of this parser is always `()`, the unit type. - /// - /// The motivating case for this is in combination with [`Parser::and_is`], allowing a parser - /// to consume something only if it isn't also something like an escape sequence or a nested block. - /// - /// Caveats: - /// - The error message produced by `not` by default will likely be fairly unhelpful - it can - /// only tell the span that was wrong. - /// - If not careful, it's fairly easy to create non-intuitive behavior due to end-of-input - /// being a valid token for a parser to consume, and as most parsers fail at end of input, - /// `not` will succeed on it. - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// - /// #[derive(Debug, PartialEq)] - /// enum Tree<'src> { - /// Text(&'src str), - /// Group(Vec), - /// } - /// - /// // Arbitrary text, nested in a tree with { ... } delimiters - /// let tree = recursive::<_, _, extra::Err>, _, _>(|tree| { - /// let text = any() - /// .and_is(one_of("{}").not()) - /// .repeated() - /// .at_least(1) - /// .to_slice() - /// .map(Tree::Text); - /// - /// let group = tree - /// .repeated() - /// .collect() - /// .delimited_by(just('{'), just('}')) - /// .map(Tree::Group); - /// - /// text.or(group) - /// }); - /// - /// assert_eq!( - /// tree.parse("{abcd{efg{hijk}lmn{opq}rs}tuvwxyz}").into_result(), - /// Ok(Tree::Group(vec![ - /// Tree::Text("abcd"), - /// Tree::Group(vec![ - /// Tree::Text("efg"), - /// Tree::Group(vec![ - /// Tree::Text("hijk"), - /// ]), - /// Tree::Text("lmn"), - /// Tree::Group(vec![ - /// Tree::Text("opq"), - /// ]), - /// Tree::Text("rs"), - /// ]), - /// Tree::Text("tuvwxyz"), - /// ])), - /// ); - /// ``` - fn not(self) -> Not - where - Self: Sized, - { - Not { - parser: self, - phantom: EmptyPhantom::new(), - } - } - - /// Parse a pattern zero or more times (analog to Regex's `*`). - /// - /// Input is eagerly parsed. Be aware that the parser will accept no occurrences of the pattern too. Consider using - /// [`Repeated::at_least`] instead if you wish to parse a minimum number of elements. - /// - /// The output type of this parser is, by default, `()`. If you want to collect the items into a [`Container`] - /// (such as a [`Vec`]), use [`IterParser::collect`]. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// let num = any::<_, extra::Err>>() - /// .filter(|c: &char| c.is_ascii_digit()) - /// .repeated() - /// .at_least(1) - /// .collect::() - /// .from_str() - /// .unwrapped(); - /// - /// let sum = num.clone() - /// .foldl(just('+').ignore_then(num).repeated(), |a, b| a + b); - /// - /// assert_eq!(sum.parse("2+13+4+0+5").into_result(), Ok(24)); - /// ``` - #[cfg_attr(debug_assertions, track_caller)] - fn repeated(self) -> Repeated - where - Self: Sized, - { - Repeated { - parser: self, - at_least: 0, - at_most: !0, - #[cfg(debug_assertions)] - location: *Location::caller(), - phantom: EmptyPhantom::new(), - } - } - - /// Parse a pattern, separated by another, any number of times. - /// - /// You can use [`SeparatedBy::allow_leading`] or [`SeparatedBy::allow_trailing`] to allow leading or trailing - /// separators. - /// - /// The output type of this parser can be any [`Container`]. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// let shopping = text::ascii::ident::<_, extra::Err>>() - /// .padded() - /// .separated_by(just(',')) - /// .collect::>(); - /// - /// assert_eq!(shopping.parse("eggs").into_result(), Ok(vec!["eggs"])); - /// assert_eq!(shopping.parse("eggs, flour, milk").into_result(), Ok(vec!["eggs", "flour", "milk"])); - /// ``` - /// - /// See [`SeparatedBy::allow_leading`] and [`SeparatedBy::allow_trailing`] for more examples. - #[cfg_attr(debug_assertions, track_caller)] - fn separated_by(self, separator: B) -> SeparatedBy - where - Self: Sized, - B: Parser<'src, I, U, E>, - { - SeparatedBy { - parser: self, - separator, - at_least: 0, - at_most: !0, - allow_leading: false, - allow_trailing: false, - #[cfg(debug_assertions)] - location: *Location::caller(), - phantom: EmptyPhantom::new(), - } - } - - /// Left-fold the output of the parser into a single value. - /// - /// The output of the original parser must be of type `(A, impl IntoIterator)`. - /// - /// The output type of this parser is `A`, the left-hand component of the original parser's output. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// let int = text::int::<_, extra::Err>>(10) - /// .from_str() - /// .unwrapped(); - /// - /// let sum = int - /// .clone() - /// .foldl(just('+').ignore_then(int).repeated(), |a, b| a + b); - /// - /// assert_eq!(sum.parse("1+12+3+9").into_result(), Ok(25)); - /// assert_eq!(sum.parse("6").into_result(), Ok(6)); - /// ``` - #[cfg_attr(debug_assertions, track_caller)] - fn foldl(self, other: B, f: F) -> Foldl - where - F: Fn(O, OB) -> O, - B: IterParser<'src, I, OB, E>, - Self: Sized, - { - Foldl { - parser_a: self, - parser_b: other, - folder: f, - phantom: EmptyPhantom::new(), - } - } - - /// Left-fold the output of the parser into a single value, making use of the parser's state when doing so. - /// - /// The output of the original parser must be of type `(A, impl IntoIterator)`. - /// - /// The output type of this parser is `A`, the left-hand component of the original parser's output. - /// - /// # Examples - /// - /// ## General - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple, extra::SimpleState}; - /// let int = text::int::<_, extra::Full, SimpleState, ()>>(10) - /// .from_str() - /// .unwrapped(); - /// - /// let sum = int - /// .clone() - /// .foldl_with(just('+').ignore_then(int).repeated(), |a, b, e| (a + b) * **e.state()); - /// - /// let mut multiplier = SimpleState(2i32); - /// assert_eq!(sum.parse_with_state("1+12+3+9", &mut multiplier).into_result(), Ok(134)); - /// assert_eq!(sum.parse_with_state("6", &mut multiplier).into_result(), Ok(6)); - /// ``` - /// - /// ## Interning / Arena Allocation - /// - /// This example assumes use of the `slotmap` crate for arena allocation. - /// - /// ``` - /// # use chumsky::prelude::*; - /// use slotmap::{new_key_type, SlotMap}; - /// - /// // Metadata type for node Ids for extra type safety - /// new_key_type! { - /// pub struct NodeId; - /// } - /// - /// // AST nodes reference other nodes with `NodeId`s instead of containing boxed/owned values - /// #[derive(Copy, Clone, Debug, PartialEq)] - /// enum Expr { - /// Int(i32), - /// Add(NodeId, NodeId), - /// } - /// - /// type NodeArena = SlotMap; - /// - /// // Now, define our parser - /// let int = text::int::<&str, extra::Full, extra::SimpleState, ()>>(10) - /// .padded() - /// .map_with(|s, e| - /// // Return the ID of the new integer node - /// e.state().insert(Expr::Int(s.parse().unwrap())) - /// ); - /// - /// let sum = int.foldl_with( - /// just('+').padded().ignore_then(int).repeated(), - /// |a: NodeId, b: NodeId, e| { - /// // Inserting an item into the arena returns its ID - /// e.state().insert(Expr::Add(a, b)) - /// } - /// ); - /// - /// // Test our parser - /// let mut arena = extra::SimpleState(NodeArena::default()); - /// let four_plus_eight = sum.parse_with_state("4 + 8", &mut arena).unwrap(); - /// if let Expr::Add(a, b) = arena[four_plus_eight] { - /// assert_eq!(arena[a], Expr::Int(4)); - /// assert_eq!(arena[b], Expr::Int(8)); - /// } else { - /// panic!("Not an Expr::Add"); - /// } - /// ``` - #[cfg_attr(debug_assertions, track_caller)] - fn foldl_with(self, other: B, f: F) -> FoldlWith - where - F: Fn(O, OB, &mut MapExtra<'src, '_, I, E>) -> O, - B: IterParser<'src, I, OB, E>, - Self: Sized, - { - FoldlWith { - parser_a: self, - parser_b: other, - folder: f, - phantom: EmptyPhantom::new(), - } - } - - /// Parse a pattern. Afterwards, the input stream will be rewound to its original state, as if parsing had not - /// occurred. - /// - /// This combinator is useful for cases in which you wish to avoid a parser accidentally consuming too much input, - /// causing later parsers to fail as a result. A typical use-case of this is that you want to parse something that - /// is not followed by something else. - /// - /// The output type of this parser is `O`, the same as the original parser. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::prelude::*; - /// let just_numbers = text::digits::<_, extra::Err>>(10) - /// .to_slice() - /// .padded() - /// .then_ignore(none_of("+-*/").rewind()) - /// .separated_by(just(',')) - /// .collect::>(); - /// // 3 is not parsed because it's followed by '+'. - /// assert_eq!(just_numbers.lazy().parse("1, 2, 3 + 4").into_result(), Ok(vec!["1", "2"])); - /// ``` - fn rewind(self) -> Rewind - where - Self: Sized, - { - Rewind { parser: self } - } - - /// Make the parser lazy, such that it parses as much of the input as it can finishes successfully, leaving the trailing input untouched. - /// - /// The output type of this parser is `O`, the same as the original parser. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::prelude::*; - /// let digits = one_of::<_, _, extra::Err>>('0'..='9') - /// .repeated() - /// .collect::() - /// .lazy(); - /// - /// assert_eq!(digits.parse("12345abcde").into_result().as_deref(), Ok("12345")); - /// ``` - fn lazy(self) -> Lazy<'src, Self, I, E> - where - Self: Sized, - I: ValueInput<'src>, - { - self.then_ignore(any().repeated()) - } - - /// Parse a pattern, ignoring any amount of whitespace both before and after the pattern. - /// - /// The output type of this parser is `O`, the same as the original parser. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::prelude::*; - /// let ident = text::ascii::ident::<_, extra::Err>>().padded(); - /// - /// // A pattern with no whitespace surrounding it is accepted - /// assert_eq!(ident.parse("hello").into_result(), Ok("hello")); - /// // A pattern with arbitrary whitespace surrounding it is also accepted - /// assert_eq!(ident.parse(" \t \n \t world \t ").into_result(), Ok("world")); - /// ``` - fn padded(self) -> Padded - where - Self: Sized, - I: Input<'src>, - I::Token: Char, - { - Padded { parser: self } - } - - // /// Flatten a nested collection. - // /// - // /// This use-cases of this method are broadly similar to those of [`Iterator::flatten`]. - // /// - // /// The output type of this parser is `Vec`, where the original parser output was - // /// `impl IntoIterator>`. - // fn flatten(self) -> Map Vec> - // where - // Self: Sized, - // O: IntoIterator, - // Inner: IntoIterator, - // { - // self.map(|xs| xs.into_iter().flat_map(|xs| xs.into_iter()).collect()) - // } - - /// Apply a fallback recovery strategy to this parser should it fail. - /// - /// There is no silver bullet for error recovery, so this function allows you to specify one of several different - /// strategies at the location of your choice. Prefer an error recovery strategy that more precisely mirrors valid - /// syntax where possible to make error recovery more reliable. - /// - /// Because chumsky is a [PEG](https://en.m.wikipedia.org/wiki/Parsing_expression_grammar) parser, which always - /// take the first successful parsing route through a grammar, recovering from an error may cause the parser to - /// erroneously miss alternative valid routes through the grammar that do not generate recoverable errors. If you - /// run into cases where valid syntax fails to parse without errors, this might be happening: consider removing - /// error recovery or switching to a more specific error recovery strategy. - /// - /// The output type of this parser is `O`, the same as the original parser. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// #[derive(Debug, PartialEq)] - /// enum Expr<'src> { - /// Error, - /// Int(&'src str), - /// List(Vec>), - /// } - /// - /// let recovery = just::<_, _, extra::Err>>('[') - /// .then(none_of(']').repeated().then(just(']'))); - /// - /// let expr = recursive::<_, _, extra::Err>, _, _>(|expr| expr - /// .separated_by(just(',')) - /// .collect::>() - /// .delimited_by(just('['), just(']')) - /// .map(Expr::List) - /// // If parsing a list expression fails, recover at the next delimiter, generating an error AST node - /// .recover_with(via_parser(recovery.map(|_| Expr::Error))) - /// .or(text::int(10).map(Expr::Int)) - /// .padded()); - /// - /// assert!(expr.parse("five").has_errors()); // Text is not a valid expression in this language... - /// assert_eq!( - /// expr.parse("[1, 2, 3]").into_result(), - /// Ok(Expr::List(vec![Expr::Int("1"), Expr::Int("2"), Expr::Int("3")])), - /// ); // ...but lists and numbers are! - /// - /// // This input has two syntax errors... - /// let res = expr.parse("[[1, two], [3, four]]"); - /// // ...and error recovery allows us to catch both of them! - /// assert_eq!(res.errors().len(), 2); - /// // Additionally, the AST we get back still has useful information. - /// assert_eq!(res.output(), Some(&Expr::List(vec![Expr::Error, Expr::Error]))); - /// ``` - fn recover_with>(self, strategy: S) -> RecoverWith - where - Self: Sized, - { - RecoverWith { - parser: self, - strategy, - } - } - - /// Map the primary error of this parser to another value. - /// - /// This function is most useful when using a custom error type, allowing you to augment errors according to - /// context. - /// - /// The output type of this parser is `O`, the same as the original parser. - // TODO: Map E -> D, not E -> E - fn map_err(self, f: F) -> MapErr - where - Self: Sized, - F: Fn(E::Error) -> E::Error, - { - MapErr { - parser: self, - mapper: f, - } - } - - // /// Map the primary error of this parser to another value, making use of the span from the start of the attempted - // /// to the point at which the error was encountered. - // /// - // /// This function is useful for augmenting errors to allow them to display the span of the initial part of a - // /// pattern, for example to add a "while parsing" clause to your error messages. - // /// - // /// The output type of this parser is `O`, the same as the original parser. - // /// - // // TODO: Map E -> D, not E -> E - // fn map_err_with_span(self, f: F) -> MapErrWithSpan - // where - // Self: Sized, - // F: Fn(E::Error, I::Span) -> E::Error, - // { - // MapErrWithSpan { - // parser: self, - // mapper: f, - // } - // } - - /// Map the primary error of this parser to another value, making use of the parser state. - /// - /// This function is useful for augmenting errors to allow them to include context in non context-free - /// languages, or provide contextual notes on possible causes. - /// - /// The output type of this parser is `O`, the same as the original parser. - /// - // TODO: Map E -> D, not E -> E - fn map_err_with_state(self, f: F) -> MapErrWithState - where - Self: Sized, - F: Fn(E::Error, I::Span, &mut E::State) -> E::Error, - { - MapErrWithState { - parser: self, - mapper: f, - } - } - - /// Validate an output, producing non-terminal errors if it does not fulfill certain criteria. - /// The errors will not immediately halt parsing on this path, but instead it will continue, - /// potentially emitting one or more other errors, only failing after the pattern has otherwise - /// successfully, or emitted another terminal error. - /// - /// This function also permits mapping the output to a value of another type, similar to [`Parser::map`]. - /// - /// If you wish parsing of this pattern to halt when an error is generated instead of continuing, consider using - /// [`Parser::try_map`] instead. - /// - /// The output type of this parser is `U`, the result of the validation closure. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::prelude::*; - /// let large_int = text::int::<_, extra::Err>>(10) - /// .from_str() - /// .unwrapped() - /// .validate(|x: u32, e, emitter| { - /// if x < 256 { emitter.emit(Rich::custom(e.span(), format!("{} must be 256 or higher.", x))) } - /// x - /// }); - /// - /// assert_eq!(large_int.parse("537").into_result(), Ok(537)); - /// assert!(large_int.parse("243").into_result().is_err()); - /// ``` - /// - /// To show the difference in behavior from [`Parser::try_map`]: - /// - /// ``` - /// # use chumsky::{text::TextExpected, util::MaybeRef, error::LabelError, prelude::*}; - /// - /// // Start with the same large_int validator - /// let large_int_val = text::int::<_, extra::Err>>(10) - /// .from_str() - /// .unwrapped() - /// .validate(|x: u32, e, emitter| { - /// if x < 256 { emitter.emit(Rich::custom(e.span(), format!("{} must be 256 or higher", x))) } - /// x - /// }); - /// - /// // A try_map version of the same parser - /// let large_int_tm = text::int::<_, extra::Err>>(10) - /// .from_str() - /// .unwrapped() - /// .try_map(|x: u32, span| { - /// if x < 256 { - /// Err(Rich::custom(span, format!("{} must be 256 or higher", x))) - /// } else { - /// Ok(x) - /// } - /// }); - /// - /// // Parser that uses the validation version - /// let multi_step_val = large_int_val.then(text::ascii::ident().padded()); - /// // Parser that uses the try_map version - /// let multi_step_tm = large_int_tm.then(text::ascii::ident().padded()); - /// - /// // On success, both parsers are equivalent - /// assert_eq!( - /// multi_step_val.parse("512 foo").into_result(), - /// Ok((512, "foo")) - /// ); - /// - /// assert_eq!( - /// multi_step_tm.parse("512 foo").into_result(), - /// Ok((512, "foo")) - /// ); - /// - /// // However, on failure, they may produce different errors: - /// assert_eq!( - /// multi_step_val.parse("100 2").into_result(), - /// Err(vec![ - /// Rich::::custom((0..3).into(), "100 must be 256 or higher"), - /// as LabelError<&str, _>>::expected_found([TextExpected::<&str>::AnyIdentifier], Some(MaybeRef::Val('2')), (4..5).into()), - /// ]) - /// ); - /// - /// assert_eq!( - /// multi_step_tm.parse("100 2").into_result(), - /// Err(vec![Rich::::custom((0..3).into(), "100 must be 256 or higher")]) - /// ); - /// ``` - /// - /// As is seen in the above example, validation doesn't prevent the emission of later errors in the - /// same parser, but still produces an error in the output. - /// - fn validate(self, f: F) -> Validate - where - Self: Sized, - F: Fn(O, &mut MapExtra<'src, '_, I, E>, &mut Emitter) -> U, - { - Validate { - parser: self, - validator: f, - phantom: EmptyPhantom::new(), - } - } - - // /// Map the primary error of this parser to a result. If the result is [`Ok`], the parser succeeds with that value. - // /// - // /// Note that, if the closure returns [`Err`], the parser will not consume any input. - // /// - // /// The output type of this parser is `U`, the [`Ok`] type of the result. - // fn or_else(self, f: F) -> OrElse - // where - // Self: Sized, - // F: Fn(E::Error) -> Result, - // { - // OrElse { - // parser: self, - // or_else: f, - // } - // } - - /// Attempt to convert the output of this parser into something else using Rust's [`FromStr`] trait. - /// - /// This is most useful when wanting to convert literal values into their corresponding Rust type, such as when - /// parsing integers. - /// - /// The output type of this parser is `Result`, the result of attempting to parse the output, `O`, into - /// the value `U`. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::prelude::*; - /// let uint64 = text::int::<_, extra::Err>>(10) - /// .from_str::() - /// .unwrapped(); - /// - /// assert_eq!(uint64.parse("7").into_result(), Ok(7)); - /// assert_eq!(uint64.parse("42").into_result(), Ok(42)); - /// ``` - #[allow(clippy::wrong_self_convention)] - fn from_str(self) -> Map Result> - where - Self: Sized, - U: FromStr, - O: AsRef, - { - self.map(|o| o.as_ref().parse()) - } - - /// For parsers that produce a [`Result`] as their output, unwrap the result (panicking if an [`Err`] is - /// encountered). - /// - /// In general, this method should be avoided except in cases where all possible that the parser might produce can - /// by parsed using [`FromStr`] without producing an error. - /// - /// This combinator is not named `unwrap` to avoid confusion: it unwraps *during parsing*, not immediately. - /// - /// The output type of this parser is `U`, the [`Ok`] value of the [`Result`]. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::prelude::*; - /// let boolean = just::<_, _, extra::Err>>("true") - /// .or(just("false")) - /// .from_str::() - /// .unwrapped(); // Cannot panic: the only possible outputs generated by the parser are "true" or "false" - /// - /// assert_eq!(boolean.parse("true").into_result(), Ok(true)); - /// assert_eq!(boolean.parse("false").into_result(), Ok(false)); - /// // Does not panic, because the original parser only accepts "true" or "false" - /// assert!(boolean.parse("42").has_errors()); - /// ``` - #[track_caller] - fn unwrapped(self) -> Unwrapped - where - Self: Sized, - { - Unwrapped { - parser: self, - #[cfg(debug_assertions)] - location: *Location::caller(), - phantom: EmptyPhantom::new(), - } - } - - /// Turn this [`Parser`] into an [`IterParser`] if its output type implements [`IntoIterator`]. - /// - /// The resulting iterable parser will emit each element of the output type in turn. - /// - /// This is *broadly* analogous to functions like [`Vec::into_iter`], but operating at the level of parser outputs. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::prelude::*; - /// // Parses whole integers - /// let num = text::int::<&str, extra::Default>(10).padded().map(|x: &str| x.parse::().unwrap()); - /// // Parses a range like `0..4` into a vector like `[0, 1, 2, 3]` - /// let range = num.then_ignore(just("..")).then(num) - /// .map(|(x, y)| x..y) - /// .into_iter() - /// .collect::>(); - /// // Parses a list of numbers into a vector - /// let list = num.separated_by(just(',')).collect::>(); - /// let set = range.or(list); - /// assert_eq!(set.parse("0, 1, 2, 3").unwrap(), [0, 1, 2, 3]); - /// assert_eq!(set.parse("0..4").unwrap(), [0, 1, 2, 3]); - /// ``` - fn into_iter(self) -> IntoIter - where - Self: Sized, - O: IntoIterator, - { - IntoIter { - parser: self, - phantom: EmptyPhantom::new(), - } - } - - /// Box the parser, yielding a parser that performs parsing through dynamic dispatch. - /// - /// Boxing a parser might be useful for: - /// - /// - Dynamically building up parsers at run-time - /// - /// - Improving compilation times (Rust can struggle to compile code containing very long types) - /// - /// - Passing a parser over an FFI boundary - /// - /// - Getting around compiler implementation problems with long types such as - /// [this](https://github.com/rust-lang/rust/issues/54540). - /// - /// - Places where you need to name the type of a parser - /// - /// Boxing a parser is broadly equivalent to boxing other combinators via dynamic dispatch, such as [`Iterator`]. - /// - /// The output type of this parser is `O`, the same as the original parser. - /// - /// # Examples - /// - /// When not using `boxed`, the following patterns are either impossible or very difficult to express: - /// - /// ```compile_fail - /// # use chumsky::prelude::*; - /// - /// pub trait Parseable: Sized { - /// type Parser<'src>: Parser<'src, &'src str, Self>; - /// - /// fn parser<'src>() -> Self::Parser<'src>; - /// } - /// - /// impl Parseable for i32 { - /// // We *can* write this type, but it will be very impractical, and change on any alterations - /// // to the implementation - /// type Parser<'src> = ???; - /// - /// fn parser<'src>() -> Self::Parser<'src> { - /// todo() - /// } - /// } - /// ``` - /// - /// ```compile_fail - /// # use chumsky::prelude::*; - /// # fn user_input<'src>() -> impl IntoIterator> { [just('b')] } - /// - /// let user_input = user_input(); - /// - /// let mut parser = just('a'); - /// for i in user_input { - /// // Doesn't work due to type mismatch - since every combinator creates a unique type - /// parser = parser.or(i); - /// } - /// - /// let parser = parser.then(just('z')); - /// let _ = parser.parse("b").into_result(); - /// ``` - /// - /// However, with `boxed`, we can express them by making the parsers all share a common type: - /// - /// ``` - /// use chumsky::prelude::*; - /// - /// pub trait Parseable: Sized { - /// fn parser<'src>() -> Boxed<'src, 'src, &'src str, Self>; - /// } - /// - /// impl Parseable for i32 { - /// fn parser<'src>() -> Boxed<'src, 'src, &'src str, Self> { - /// todo().boxed() - /// } - /// } - /// ``` - /// - /// ``` - /// # use chumsky::prelude::*; - /// # fn user_input<'src>() -> impl IntoIterator> { [just('b'), just('c')] } - /// let user_input = user_input(); - /// let mut parser = just('a').boxed(); - /// for i in user_input { - /// // Doesn't work due to type mismatch - since every combinator creates a unique type - /// parser = parser.or(i).boxed(); - /// } - /// let parser = parser.then(just('z')); - /// parser.parse("az").into_result().unwrap(); - /// ``` - /// - fn boxed<'b>(self) -> Boxed<'src, 'b, I, O, E> - where - Self: Sized + 'b, - { - Boxed { - inner: Rc::new(self), - } - } - - /// Simplify the type of the parser using Rust's `impl Trait` syntax. - /// - /// The only reason for using this function is to make Rust's compiler errors easier to debug: it does not change - /// the behaviour of the parser at all, and is in fact just a simple identity function. - #[cfg(feature = "nightly")] - fn simplify(self) -> impl Parser<'src, I, O, E> - where - Self: Sized + 'src, - { - self - } - - /// Have this parser be enabled or disabled depending on context. - /// - /// This method, by itself, does nothing: you must use [`ConfigParser::configure`] to specify when the parser is - /// enabled. - /// - /// # Example - /// - /// ``` - /// # use chumsky::prelude::*; - /// - /// // Our parser can be in two modes depending on context: hexadecimal, or denary - /// #[derive(Clone)] - /// enum Mode { Hex, Dec } - /// - /// let digits = one_of::<_, _, extra::Context>("0123456789") - /// .or(one_of("abcdef").contextual().configure(|cfg, ctx| matches!(ctx, Mode::Hex))) - /// .repeated(); - /// - /// let num = just::<_, _, extra::Default>("0x").ignore_then(digits.with_ctx(Mode::Hex)) - /// // Fallback: when '0x' isn't present, parse using denary mode - /// .or(digits.with_ctx(Mode::Dec)) - /// .to_slice(); - /// - /// assert_eq!(num.parse("0x1a3f5b").into_result(), Ok("0x1a3f5b")); - /// assert_eq!(num.parse("12345").into_result(), Ok("12345")); - /// // Without the '0x' prefix, hexadecimal digits are invalid - /// assert!(num.parse("1a3f5b").has_errors()); - /// ``` - fn contextual(self) -> Contextual - where - Self: Sized, - { - Contextual { inner: self } - } - - /// Use [Pratt parsing](https://en.wikipedia.org/wiki/Operator-precedence_parser#Pratt_parsing) to ergonomically - /// parse this pattern separated by prefix, postfix, and infix operators of various associativites and precedence. - /// - /// Pratt parsing is a powerful technique and is recommended when writing parsers for expressions. - /// - /// # Example - /// - /// See the documentation in [`pratt`] for more extensive examples and details. - /// - /// ``` - /// # use chumsky::prelude::*; - /// use chumsky::pratt::*; - /// - /// let int = text::int::<_, extra::Err>>(10) - /// .from_str() - /// .unwrapped() - /// .padded(); - /// - /// let op = |c| just(c).padded(); - /// - /// let expr = int.pratt(( - /// prefix(2, op('-'), |_, x: i64, _| -x), - /// infix(left(1), op('*'), |x, _, y, _| x * y), - /// infix(left(1), op('/'), |x, _, y, _| x / y), - /// infix(left(0), op('+'), |x, _, y, _| x + y), - /// infix(left(0), op('-'), |x, _, y, _| x - y), - /// )); - /// - /// // Pratt parsing can handle unary operators... - /// assert_eq!(expr.parse("-7").into_result(), Ok(-7)); - /// // ...and infix binary operators... - /// assert_eq!(expr.parse("6 + 3").into_result(), Ok(9)); - /// // ...and arbitrary precedence levels between them. - /// assert_eq!(expr.parse("2 + 3 * -4").into_result(), Ok(-10)); - /// ``` - #[cfg(feature = "pratt")] - fn pratt(self, ops: Ops) -> pratt::Pratt - where - Self: Sized, - { - pratt::Pratt { atom: self, ops } - } -} - -#[cfg(feature = "nightly")] -impl<'src, I, O, E> Parser<'src, I, O, E> for ! -where - I: Input<'src>, - E: ParserExtra<'src, I>, -{ - fn go(&self, _inp: &mut InputRef<'src, '_, I, E>) -> PResult { - *self - } - - go_extra!(O); -} - -/// A [`Parser`] that can be configured with runtime context. -/// -/// This allows for context-sensitive parsing -/// of input. Note that chumsky only supports 'left'-sensitive parsing, where the context for a parser -/// is derived from earlier in the input. -/// -/// Chumsky distinguishes 'state' from 'context'. State is not able to change what input a parser -/// accepts, but may be used to change the contents of the type it emits. In this way state is expected -/// to be idempotent - combinators such as [`Parser::map_with`] are allowed to not call the -/// provided closure at all if they don't emit any output. Context and configuration, on the other hand, -/// is used to change what kind of input a parser may accept, and thus must always be evaluated. Context -/// isn't usable in any map combinator however - while it may affect accepted input, it is not expected -/// to change the final result outside of how it changes what the parser itself returns. -/// -/// Not all parsers currently support configuration. If you feel like you need a parser to be configurable -/// and it isn't currently, please open an issue on the issue tracker of the main repository. -pub trait ConfigParser<'src, I, O, E>: Parser<'src, I, O, E> -where - I: Input<'src>, - E: ParserExtra<'src, I>, -{ - /// A type describing the configurable aspects of the parser. - type Config: Default; - - #[doc(hidden)] - fn go_cfg( - &self, - inp: &mut InputRef<'src, '_, I, E>, - cfg: Self::Config, - ) -> PResult; - - #[doc(hidden)] - #[inline(always)] - fn go_emit_cfg( - &self, - inp: &mut InputRef<'src, '_, I, E>, - cfg: Self::Config, - ) -> PResult { - self.go_cfg::(inp, cfg) - } - #[doc(hidden)] - #[inline(always)] - fn go_check_cfg( - &self, - inp: &mut InputRef<'src, '_, I, E>, - cfg: Self::Config, - ) -> PResult { - self.go_cfg::(inp, cfg) - } - - /// A combinator that allows configuration of the parser from the current context. Context - /// is most often derived from [`Parser::ignore_with_ctx`], [`Parser::then_with_ctx`] or [`map_ctx`], - /// and is how chumsky supports parsing things such as indentation-sensitive grammars. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::prelude::*; - /// - /// let int = text::int::<_, extra::Err>>(10) - /// .from_str() - /// .unwrapped(); - /// - /// // By default, accepts any number of items - /// let item = text::ascii::ident() - /// .padded() - /// .repeated(); - /// - /// // With configuration, we can declare an exact number of items based on a prefix length - /// let len_prefixed_arr = int - /// .ignore_with_ctx(item.configure(|repeat, ctx| repeat.exactly(*ctx)).collect::>()); - /// - /// assert_eq!( - /// len_prefixed_arr.parse("2 foo bar").into_result(), - /// Ok(vec!["foo", "bar"]), - /// ); - /// - /// assert_eq!( - /// len_prefixed_arr.parse("0").into_result(), - /// Ok(vec![]), - /// ); - /// - /// len_prefixed_arr.parse("3 foo bar baz bam").into_result().unwrap_err(); - /// len_prefixed_arr.parse("3 foo bar").into_result().unwrap_err(); - /// ``` - fn configure(self, cfg: F) -> Configure - where - Self: Sized, - F: Fn(Self::Config, &E::Context) -> Self::Config, - { - Configure { parser: self, cfg } - } -} - -/// Data that is needed by IterParser when debug_assertions are enabled. -#[derive(Clone, Copy)] -pub struct IterParserDebug { - #[cfg(debug_assertions)] - pub(crate) nonconsumption_is_ok: bool, -} - -impl IterParserDebug { - #[inline(always)] - pub(crate) fn new(nonconsumption_is_ok: bool) -> Self { - Self { - #[cfg(debug_assertions)] - nonconsumption_is_ok, - } - } -} - -/// An iterator that wraps an iterable parser. See [`IterParser::parse_iter`]. -#[cfg(feature = "unstable")] -pub struct ParseIter< - 'a, - 'src, - 'iter, - P: IterParser<'src, I, O, E>, - I: Input<'src>, - O, - E: ParserExtra<'src, I>, -> { - parser: &'a mut P, - own: InputOwn<'src, 'iter, I, E>, - iter_state: Option>, - #[allow(dead_code)] - phantom: EmptyPhantom<(&'src (), O)>, -} - -#[cfg(feature = "unstable")] -impl<'a, 'src, P, I: Input<'src>, O, E: ParserExtra<'src, I>> Iterator - for ParseIter<'a, 'src, '_, P, I, O, E> -where - P: IterParser<'src, I, O, E>, -{ - type Item = O; - - fn next(&mut self) -> Option { - let mut inp = self.own.as_ref_start(); - let parser = &self.parser; - - let iter_state = match &mut self.iter_state { - Some(state) => state, - None => { - let state = parser.make_iter::(&mut inp).ok()?; - self.iter_state = Some(state); - self.iter_state.as_mut().unwrap() - } - }; - - let res = parser.next::(&mut inp, iter_state, IterParserDebug::new(true)); - // TODO: Avoid clone - self.own.start = inp.cursor().inner; - res.ok().and_then(|res| res) - } -} - -/// An iterable equivalent of [`Parser`], i.e: a parser that generates a sequence of outputs. -pub trait IterParser<'src, I, O, E = extra::Default> -where - I: Input<'src>, - E: ParserExtra<'src, I>, -{ - #[doc(hidden)] - type IterState - where - I: 'src; - - #[doc(hidden)] - fn make_iter( - &self, - inp: &mut InputRef<'src, '_, I, E>, - ) -> PResult>; - #[doc(hidden)] - fn next( - &self, - inp: &mut InputRef<'src, '_, I, E>, - state: &mut Self::IterState, - debug: IterParserDebug, - ) -> IPResult; - - #[doc(hidden)] - #[cfg(feature = "debug")] - fn node_info(&self, _scope: &mut debug::NodeScope) -> debug::NodeInfo { - let ty = core::any::type_name::(); - debug::NodeInfo::Unknown(ty.split_once('<').map_or(ty, |(ty, _)| ty).to_string()) - } - - /// Collect this iterable parser into a [`Container`]. - /// - /// This is commonly useful for collecting parsers that output many values into containers of various kinds: - /// [`Vec`]s, [`String`]s, or even [`HashMap`]s. This method is analogous to [`Iterator::collect`]. - /// - /// The output type of this iterable parser is `C`, the type being collected into. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// let word = any::<_, extra::Err>>().filter(|c: &char| c.is_alphabetic()) // This parser produces an output of `char` - /// .repeated() // This parser is iterable (i.e: implements `IterParser`) - /// .collect::(); // We collect the `char`s into a `String` - /// - /// assert_eq!(word.parse("hello").into_result(), Ok("hello".to_string())); - /// ``` - #[cfg_attr(debug_assertions, track_caller)] - fn collect>(self) -> Collect - where - Self: Sized, - { - Collect { - parser: self, - phantom: EmptyPhantom::new(), - } - } - - /// Collect this iterable parser into a [`ContainerExactly`]. - /// - /// This is useful for situations where the number of items to consume is statically known. - /// A common use-case is collecting into an array. - /// - /// The output type of this iterable parser if `C`, the type being collected into. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// let three_digit = any::<_, extra::Err>>().filter(|c: &char| c.is_numeric()) - /// .repeated() - /// .collect_exactly::<[_; 3]>(); - /// - /// assert_eq!(three_digit.parse("123").into_result(), Ok(['1', '2', '3'])); - /// assert!(three_digit.parse("12").into_result().is_err()); - /// assert!(three_digit.parse("1234").into_result().is_err()); - /// ``` - fn collect_exactly>(self) -> CollectExactly - where - Self: Sized, - { - CollectExactly { - parser: self, - phantom: EmptyPhantom::new(), - } - } - - /// Collect this iterable parser into a [`usize`], outputting the number of elements that were parsed. - /// - /// This is sugar for [`.collect::()`](Self::collect). - /// - /// # Examples - /// - /// ``` - /// # use chumsky::prelude::*; - /// - /// // Counts how many chess squares are in the input. - /// let squares = one_of::<_, _, extra::Err>>('a'..='z').then(one_of('1'..='8')).padded().repeated().count(); - /// - /// assert_eq!(squares.parse("a1 b2 c3").into_result(), Ok(3)); - /// assert_eq!(squares.parse("e5 e7 c6 c7 f6 d5 e6 d7 e4 c5 d6 c4 b6 f5").into_result(), Ok(14)); - /// assert_eq!(squares.parse("").into_result(), Ok(0)); - /// ``` - fn count(self) -> Collect - where - Self: Sized, - { - self.collect() - } - - /// Enumerate outputs of this iterable parser. - /// - /// This function behaves in a similar way to [`Iterator::enumerate`]. - /// - /// The output type of this iterable parser is `(usize, O)`. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// let word = text::ascii::ident::<_, extra::Err>>() - /// .padded() - /// .repeated() // This parser is iterable (i.e: implements `IterParser`) - /// .enumerate() - /// .collect::>(); - /// - /// assert_eq!(word.parse("hello world").into_result(), Ok(vec![(0, "hello"), (1, "world")])); - /// ``` - fn enumerate(self) -> Enumerate - where - Self: Sized, - { - Enumerate { - parser: self, - phantom: EmptyPhantom::new(), - } - } - - /// Fold the output of the parser into the given accumulator. - /// - /// The output type of this iterable parser is `B`, the accumulator type. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// let int = text::int::<_, extra::Err>>(10) - /// .from_str::() - /// .unwrapped(); - /// - /// let sum = int - /// .padded() - /// .repeated() - /// .fold(0, |sum, x| sum + x); - /// - /// assert_eq!(sum.parse("3 7 2").into_result(), Ok(12)); - /// assert_eq!(sum.parse("").into_result(), Ok(0)); - /// assert_eq!(sum.parse("42 1").into_result(), Ok(43)); - /// ``` - #[cfg_attr(debug_assertions, track_caller)] - fn fold(self, init: B, f: F) -> Fold - where - B: Clone, - F: Fn(B, O) -> B, - Self: Sized, - { - Fold { - parser: self, - init, - folder: f, - phantom: EmptyPhantom::new(), - } - } - - /// Right-fold the output of the parser into a single value. - /// - /// The output type of this iterable parser is `B`, the right-hand component of the original parser's output. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple}; - /// let int = text::int::<_, extra::Err>>(10) - /// .from_str() - /// .unwrapped(); - /// - /// let signed = just('+').to(1) - /// .or(just('-').to(-1)) - /// .repeated() - /// .foldr(int, |a, b| a * b); - /// - /// assert_eq!(signed.parse("3").into_result(), Ok(3)); - /// assert_eq!(signed.parse("-17").into_result(), Ok(-17)); - /// assert_eq!(signed.parse("--+-+-5").into_result(), Ok(5)); - /// ``` - #[cfg_attr(debug_assertions, track_caller)] - fn foldr(self, other: B, f: F) -> Foldr - where - F: Fn(O, OA) -> OA, - B: Parser<'src, I, OA, E>, - Self: Sized, - { - Foldr { - parser_a: self, - parser_b: other, - folder: f, - phantom: EmptyPhantom::new(), - } - } - - /// Right-fold the output of the parser into a single value, making use of the parser's state when doing so. - /// - /// The output type of this parser is `B`, the right-hand component of the original parser's output. - /// - /// # Examples - /// - /// ``` - /// # use chumsky::{prelude::*, error::Simple, extra::SimpleState}; - /// let int = text::int::<_, extra::Full, SimpleState, ()>>(10) - /// .from_str() - /// .unwrapped(); - /// - /// let signed = just('+').to(1) - /// .or(just('-').to(-1)) - /// .repeated() - /// .foldr_with(int, |a, b, e| { - /// **e.state() += 1; - /// a * b - /// }); - /// - /// // Test our parser - /// let mut folds = SimpleState(0i32); - /// assert_eq!(signed.parse_with_state("3", &mut folds).into_result(), Ok(3)); - /// assert_eq!(signed.parse_with_state("-17", &mut folds).into_result(), Ok(-17)); - /// assert_eq!(signed.parse_with_state("--+-+-5", &mut folds).into_result(), Ok(5)); - /// ``` - /// - /// - #[cfg_attr(debug_assertions, track_caller)] - fn foldr_with(self, other: B, f: F) -> FoldrWith - where - F: Fn(O, OA, &mut MapExtra<'src, '_, I, E>) -> OA, - B: Parser<'src, I, OA, E>, - Self: Sized, - { - FoldrWith { - parser_a: self, - parser_b: other, - folder: f, - phantom: EmptyPhantom::new(), - } - } - - /// TODO - #[cfg(feature = "nightly")] - fn flatten(self) -> Flatten - where - O: IntoIterator, - Self: Sized, - { - Flatten { - parser: self, - phantom: EmptyPhantom::new(), - } - } - - /// Parse the given input with this [`IterParser`]. - /// - /// The provided closure gives access to an iterator, which may be used to iterate the parser's outputs. Once the closure has terminated, a [`ParseResult`] will be returned containing the output of the closure and any parse errors that were encountered during iteration. - /// - /// # Examples - /// - /// ``` - /// - /// ``` - #[cfg(feature = "unstable")] - fn parse_iter(&mut self, input: I, f: F) -> ParseResult - where - Self: IterParser<'src, I, O, E> + Sized, - I: Input<'src>, - E::State: Default, - E::Context: Default, - F: FnOnce(&mut ParseIter<'_, 'src, '_, Self, I, O, E>) -> R, - { - self.parse_iter_with_state(input, &mut Default::default(), f) - } - - /// Parse the given input with this [`IterParser`], using the given state. - /// - /// See [`IterParser::parse_iter`] for more information. - #[cfg(feature = "unstable")] - fn parse_iter_with_state( - &mut self, - input: I, - state: &mut E::State, - f: F, - ) -> ParseResult - where - Self: IterParser<'src, I, O, E> + Sized, - I: Input<'src>, - E::Context: Default, - F: FnOnce(&mut ParseIter<'_, 'src, '_, Self, I, O, E>) -> R, - { - let mut iter = ParseIter { - parser: self, - own: InputOwn::new_state(input, state), - iter_state: None, - phantom: EmptyPhantom::new(), - }; - let out = f(&mut iter); - let mut inp = iter.own.as_ref_start(); - let res = end().go::(&mut inp); - let alt = inp.take_alt().map(|alt| alt.err).unwrap_or_else(|| { - let fake_span = inp.span_since(&inp.cursor()); - // TODO: Why is this needed? - E::Error::expected_found([], None, fake_span) - }); - let mut errs = iter.own.into_errs(); - if res.is_err() { - errs.push(alt); - } - - ParseResult::new(Some(out), errs) - } -} - -/// An iterable equivalent of [`ConfigParser`], i.e: a parser that generates a sequence of outputs and -/// can be configured at runtime. -pub trait ConfigIterParser<'src, I, O, E = extra::Default>: IterParser<'src, I, O, E> -where - I: Input<'src>, - E: ParserExtra<'src, I>, -{ - /// A trait describing the configurable aspects of the iterable parser. - type Config: Default; - - #[doc(hidden)] - fn next_cfg( - &self, - inp: &mut InputRef<'src, '_, I, E>, - state: &mut Self::IterState, - cfg: &Self::Config, - debug: IterParserDebug, - ) -> IPResult; - - /// A combinator that allows configuration of the parser from the current context - fn configure(self, cfg: F) -> IterConfigure - where - Self: Sized, - F: Fn(Self::Config, &E::Context) -> Self::Config, - { - IterConfigure { - parser: self, - cfg, - phantom: EmptyPhantom::new(), - } - } - - /// A combinator that allows fallible configuration of the parser from the current context - - /// if an error is returned, parsing fails. - fn try_configure(self, cfg: F) -> TryIterConfigure - where - Self: Sized, - F: Fn(Self::Config, &E::Context, I::Span) -> Result, - { - TryIterConfigure { - parser: self, - cfg, - phantom: EmptyPhantom::new(), - } - } -} - -/// See [`Parser::boxed`]. -/// -/// Due to current implementation details, the inner value is not, in fact, a [`Box`], but is an [`Rc`] to facilitate -/// efficient cloning. This is likely to change in the future. Unlike [`Box`], [`Rc`] has no size guarantees: although -/// it is *currently* the same size as a raw pointer. -// TODO: Don't use an Rc (why?) -pub struct Boxed<'src, 'b, I: Input<'src>, O, E: ParserExtra<'src, I> = extra::Default> { - inner: Rc>, -} - -impl<'src, I: Input<'src>, O, E: ParserExtra<'src, I>> Clone for Boxed<'src, '_, I, O, E> { - fn clone(&self) -> Self { - Self { - inner: self.inner.clone(), - } - } -} - -impl<'src, I, O, E> Parser<'src, I, O, E> for Boxed<'src, '_, I, O, E> -where - I: Input<'src>, - E: ParserExtra<'src, I>, -{ - #[doc(hidden)] - #[cfg(feature = "debug")] - fn node_info(&self, scope: &mut debug::NodeScope) -> debug::NodeInfo { - self.inner.node_info(scope) - } - - #[inline] - fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult { - M::invoke(&*self.inner, inp) - } - - fn boxed<'c>(self) -> Boxed<'src, 'c, I, O, E> - where - Self: Sized + 'c, - { - // Never double-box parsers - self - } - - go_extra!(O); -} - -impl<'src, I, O, E, T> Parser<'src, I, O, E> for ::alloc::boxed::Box -where - I: Input<'src>, - E: ParserExtra<'src, I>, - T: Parser<'src, I, O, E>, -{ - #[inline] - fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult - where - Self: Sized, - { - T::go::(self, inp) - } - - go_extra!(O); -} - -impl<'src, I, O, E, T> Parser<'src, I, O, E> for ::alloc::rc::Rc -where - I: Input<'src>, - E: ParserExtra<'src, I>, - T: Parser<'src, I, O, E>, -{ - #[inline] - fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult - where - Self: Sized, - { - T::go::(self, inp) - } - - go_extra!(O); -} - -impl<'src, I, O, E, T> Parser<'src, I, O, E> for ::alloc::sync::Arc -where - I: Input<'src>, - E: ParserExtra<'src, I>, - T: Parser<'src, I, O, E>, -{ - #[inline] - fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult - where - Self: Sized, - { - T::go::(self, inp) - } - - go_extra!(O); -} - -/// Create a parser that selects one or more input patterns and map them to an output value. -/// -/// This is most useful when turning the tokens of a previous compilation pass (such as lexing) into data that can be -/// used for parsing, although it can also generally be used to select inputs and map them to outputs. Any unmapped -/// input patterns will become syntax errors, just as with [`Parser::filter`]. -/// -/// Internally, [`select!`] is very similar to a single-token [`Parser::filter`] and thinking of it as such might make -/// it less confusing. -/// -/// `select!` requires that tokens implement [`Clone`] and the input type implements [`ValueInput`]. If you're trying -/// to access tokens referentially (for the sake of nested parsing, or simply because you want to avoid cloning the -/// token), see [`select_ref!`]. -/// -/// # Examples -/// -/// `select!` is syntactically similar to a `match` expression and has support for -/// [pattern guards](https://doc.rust-lang.org/reference/expressions/match-expr.html#match-guards): -/// -/// ``` -/// # use chumsky::{prelude::*, error::Simple}; -/// #[derive(Clone)] -/// enum Token<'src> { Ident(&'src str) } -/// -/// enum Expr<'src> { Local(&'src str), Null, True, False } -/// -/// # let _: chumsky::primitive::Select<_, &[Token], Expr, extra::Default> = -/// select! { -/// Token::Ident(s) if s == "true" => Expr::True, -/// Token::Ident(s) if s == "false" => Expr::False, -/// Token::Ident(s) if s == "null" => Expr::Null, -/// Token::Ident(s) => Expr::Local(s), -/// } -/// # ; -/// ``` -/// -/// If you require access to the token's span or other metadata, you may add an argument after a pattern to gain access -/// to it (see the docs for [`Parser::map_with`] and [`MapExtra`]): -/// -/// ``` -/// # use chumsky::{prelude::*, error::Simple}; -/// #[derive(Clone)] -/// enum Token<'src> { Num(f64), Str(&'src str) } -/// -/// enum Expr<'src> { Num(f64), Str(&'src str) } -/// -/// type Span = SimpleSpan; -/// -/// impl<'src> Expr<'src> { -/// fn spanned(self, span: Span) -> (Self, Span) { (self, span) } -/// } -/// -/// # let _: chumsky::primitive::Select<_, &[Token], (Expr, Span), extra::Default> = -/// select! { -/// Token::Num(x) = e => Expr::Num(x).spanned(e.span()), -/// Token::Str(s) = e => Expr::Str(s).spanned(e.span()), -/// } -/// # ; -/// ``` -/// -/// ``` -/// # use chumsky::{prelude::*, error::Simple}; -/// // The type of our parser's input (tokens like this might be emitted by your compiler's lexer) -/// #[derive(Clone, Debug, PartialEq)] -/// enum Token { -/// Num(u64), -/// Bool(bool), -/// LParen, -/// RParen, -/// } -/// -/// // The type of our parser's output, a syntax tree -/// #[derive(Debug, PartialEq)] -/// enum Ast { -/// Num(u64), -/// Bool(bool), -/// List(Vec), -/// } -/// -/// // Our parser converts a stream of input tokens into an AST -/// // `select!` is used to deconstruct some of the tokens and turn them into AST nodes -/// let ast = recursive::<_, _, extra::Err>, _, _>(|ast| { -/// let literal = select! { -/// Token::Num(x) => Ast::Num(x), -/// Token::Bool(x) => Ast::Bool(x), -/// }; -/// -/// literal.or(ast -/// .repeated() -/// .collect() -/// .delimited_by(just(Token::LParen), just(Token::RParen)) -/// .map(Ast::List)) -/// }); -/// -/// use Token::*; -/// assert_eq!( -/// ast.parse(&[LParen, Num(5), LParen, Bool(false), Num(42), RParen, RParen]).into_result(), -/// Ok(Ast::List(vec![ -/// Ast::Num(5), -/// Ast::List(vec![ -/// Ast::Bool(false), -/// Ast::Num(42), -/// ]), -/// ])), -/// ); -/// ``` -#[macro_export] -macro_rules! select { - ($($p:pat $(= $extra:ident)? $(if $guard:expr)? $(=> $out:expr)?),+ $(,)?) => ({ - $crate::primitive::select( - move |x, extra| match (x, extra) { - $(($p $(,$extra)?, ..) $(if $guard)? => ::core::option::Option::Some({ () $(;$out)? })),+, - _ => ::core::option::Option::None, - } - ) - }); -} - -/// A version of [`select!`] that selects on token by reference instead of by value. -/// -/// Useful if you want to extract elements from a token in a zero-copy manner. -/// -/// See the docs for [`select!`] for more information. -/// -/// Requires that the parser input implements [`BorrowInput`]. -#[macro_export] -macro_rules! select_ref { - ($($p:pat $(= $extra:ident)? $(if $guard:expr)? $(=> $out:expr)?),+ $(,)?) => ({ - $crate::primitive::select_ref( - move |x, extra| match (x, extra) { - $(($p $(,$extra)?, ..) $(if $guard)? => ::core::option::Option::Some({ () $(;$out)? })),+, - _ => ::core::option::Option::None, - } - ) - }); -} - -#[cfg(test)] -mod tests { - use crate::prelude::*; - - #[test] - fn zero_copy() { - use crate::input::WithContext; - use crate::prelude::*; - - #[derive(PartialEq, Debug)] - enum Token<'src> { - Ident(&'src str), - String(&'src str), - } - - type FileId = u32; - type Span = SimpleSpan; - - fn parser<'src>( - ) -> impl Parser<'src, WithContext, [(Span, Token<'src>); 6]> { - let ident = any() - .filter(|c: &char| c.is_alphanumeric()) - .repeated() - .at_least(1) - .to_slice() - .map(Token::Ident); - - let string = just('"') - .then(any().filter(|c: &char| *c != '"').repeated()) - .then(just('"')) - .to_slice() - .map(Token::String); - - ident - .or(string) - .map_with(|token, e| (e.span(), token)) - .padded() - .repeated() - .collect_exactly() - } - - assert_eq!( - parser() - .parse(r#"hello "world" these are "test" tokens"#.with_context(42)) - .into_result(), - Ok([ - (Span::new(42, 0..5), Token::Ident("hello")), - (Span::new(42, 6..13), Token::String("\"world\"")), - (Span::new(42, 14..19), Token::Ident("these")), - (Span::new(42, 20..23), Token::Ident("are")), - (Span::new(42, 24..30), Token::String("\"test\"")), - (Span::new(42, 31..37), Token::Ident("tokens")), - ]), - ); - } - - #[test] - fn zero_copy_map_span() { - use crate::{ - input::{SliceInput, ValueInput}, - prelude::*, - }; - - #[derive(PartialEq, Debug)] - enum Token<'src> { - Ident(&'src str), - String(&'src str), - } - - type FileId<'src> = &'src str; - type Span<'src> = SimpleSpan>; - - fn parser<'src, I>() -> impl Parser<'src, I, [(Span<'src>, Token<'src>); 6]> - where - I: ValueInput<'src, Token = char, Span = Span<'src>> - + SliceInput<'src, Slice = &'src str>, - { - let ident = any() - .filter(|c: &char| c.is_alphanumeric()) - .repeated() - .at_least(1) - .to_slice() - .map(Token::Ident); - - let string = just('"') - .then(any().filter(|c: &char| *c != '"').repeated()) - .then(just('"')) - .to_slice() - .map(Token::String); - - ident - .or(string) - .map_with(|token, e| (e.span(), token)) - .padded() - .repeated() - .collect_exactly() - } - - let filename = "file.txt".to_string(); - let fstr = filename.as_str(); - - assert_eq!( - parser() - .parse( - r#"hello "world" these are "test" tokens"# - .map_span(|span| Span::new(fstr, span.start()..span.end())) - ) - .into_result(), - Ok([ - (Span::new("file.txt", 0..5), Token::Ident("hello")), - (Span::new("file.txt", 6..13), Token::String("\"world\"")), - (Span::new("file.txt", 14..19), Token::Ident("these")), - (Span::new("file.txt", 20..23), Token::Ident("are")), - (Span::new("file.txt", 24..30), Token::String("\"test\"")), - (Span::new("file.txt", 31..37), Token::Ident("tokens")), - ]), - ); - } - - #[test] - fn zero_copy_repetition() { - use crate::prelude::*; - - fn parser<'src>() -> impl Parser<'src, &'src str, Vec> { - any() - .filter(|c: &char| c.is_ascii_digit()) - .repeated() - .at_least(1) - .at_most(3) - .to_slice() - .map(|b: &str| b.parse::().unwrap()) - .padded() - .separated_by(just(',').padded()) - .allow_trailing() - .collect() - .delimited_by(just('['), just(']')) - } - - assert_eq!( - parser().parse("[122 , 23,43, 4, ]").into_result(), - Ok(vec![122, 23, 43, 4]), - ); - assert_eq!( - parser().parse("[0, 3, 6, 900,120]").into_result(), - Ok(vec![0, 3, 6, 900, 120]), - ); - assert_eq!( - parser().parse("[200,400,50 ,0,0, ]").into_result(), - Ok(vec![200, 400, 50, 0, 0]), - ); - - assert!(parser().parse("[1234,123,12,1]").has_errors()); - assert!(parser().parse("[,0, 1, 456]").has_errors()); - assert!(parser().parse("[3, 4, 5, 67 89,]").has_errors()); - } - - #[test] - fn zero_copy_group() { - use crate::prelude::*; - - fn parser<'src>() -> impl Parser<'src, &'src str, (&'src str, u64, char)> { - group(( - any() - .filter(|c: &char| c.is_ascii_alphabetic()) - .repeated() - .at_least(1) - .to_slice() - .padded(), - any() - .filter(|c: &char| c.is_ascii_digit()) - .repeated() - .at_least(1) - .to_slice() - .map(|s: &str| s.parse::().unwrap()) - .padded(), - any().filter(|c: &char| !c.is_whitespace()).padded(), - )) - } - - assert_eq!( - parser().parse("abc 123 [").into_result(), - Ok(("abc", 123, '[')), - ); - assert_eq!( - parser().parse("among3d").into_result(), - Ok(("among", 3, 'd')), - ); - assert_eq!( - parser().parse("cba321,").into_result(), - Ok(("cba", 321, ',')), - ); - - assert!(parser().parse("abc 123 ").has_errors()); - assert!(parser().parse("123abc ]").has_errors()); - assert!(parser().parse("and one &").has_errors()); - } - - #[test] - fn zero_copy_group_array() { - use crate::prelude::*; - - fn parser<'src>() -> impl Parser<'src, &'src str, [char; 3]> { - group([just('a'), just('b'), just('c')]) - } - - assert_eq!(parser().parse("abc").into_result(), Ok(['a', 'b', 'c'])); - assert!(parser().parse("abd").has_errors()); - } - - #[test] - fn unicode_str() { - let input = "🄯🄚🹠🴎ðŸ„ðŸ‹ðŸ°ðŸ„‚🬯🈦g🸵ðŸ©ðŸ•”🈳2🬙🨞🅢🭳🎅h🵚🧿ðŸ©ðŸ°¬k🠡🀔🈆ðŸ¹ðŸ¤ŸðŸ‰—🴟📵🰄🤿ðŸœðŸ™˜ðŸ¹„5🠻🡉🱖🠓"; - let mut own = crate::input::InputOwn::<_, extra::Default>::new(input); - let mut inp = own.as_ref_start(); - - while let Some(_c) = inp.next() {} - } - - #[test] - #[cfg(feature = "unstable")] - fn iter() { - use crate::prelude::*; - - fn many_letters<'src>() -> impl IterParser<'src, &'src str, char> { - any().filter(char::is_ascii_alphabetic).repeated() - } - - let res = many_letters().parse_iter("abcdef", |iter| iter.collect::()); - - assert_eq!(res.into_result().unwrap(), "abcdef"); - - let res = many_letters().parse_iter("123456", |iter| iter.collect::()); - - assert!(res.has_errors()); - } - - #[test] - #[cfg(feature = "memoization")] - fn exponential() { - use crate::prelude::*; - - fn parser<'src>() -> impl Parser<'src, &'src str, String> { - recursive(|expr| { - let atom = any() - .filter(|c: &char| c.is_alphabetic()) - .repeated() - .at_least(1) - .collect() - .or(expr.delimited_by(just('('), just(')'))); - - atom.clone() - .then_ignore(just('+')) - .then(atom.clone()) - .map(|(a, b)| format!("{a}{b}")) - .memoized() - .or(atom) - }) - .then_ignore(end()) - } - - parser() - .parse("((((((((((((((((((((((((((((((a+b))))))))))))))))))))))))))))))") - .into_result() - .unwrap(); - } - - #[test] - #[cfg(feature = "memoization")] - fn left_recursive() { - use crate::prelude::*; - - fn parser<'src>() -> impl Parser<'src, &'src str, String> { - recursive(|expr| { - let atom = any() - .filter(|c: &char| c.is_alphabetic()) - .repeated() - .at_least(1) - .collect(); - - let sum = expr - .clone() - .then_ignore(just('+')) - .then(expr) - .map(|(a, b)| format!("{a}{b}")) - .memoized(); - - sum.or(atom) - }) - .then_ignore(end()) - } - - assert_eq!(parser().parse("a+b+c").into_result().unwrap(), "abc"); - } - - #[cfg(debug_assertions)] - mod debug_asserts { - use crate::prelude::*; - - // TODO panic when left recursive parser is detected - // #[test] - // #[should_panic] - // fn debug_assert_left_recursive() { - // recursive(|expr| { - // let atom = any::<&str, extra::Default>() - // .filter(|c: &char| c.is_alphabetic()) - // .repeated() - // .at_least(1) - // .collect(); - - // let sum = expr - // .clone() - // .then_ignore(just('+')) - // .then(expr) - // .map(|(a, b)| format!("{a}{b}")); - - // sum.or(atom) - // }) - // .then_ignore(end()) - // .parse("a+b+c"); - // } - - #[test] - #[should_panic] - #[cfg(debug_assertions)] - fn debug_assert_collect() { - empty::<&str, extra::Default>() - .to(()) - .repeated() - .collect::<()>() - .parse("a+b+c") - .unwrap(); - } - - #[test] - #[should_panic] - #[cfg(debug_assertions)] - fn debug_assert_separated_by() { - empty::<&str, extra::Default>() - .to(()) - .separated_by(empty()) - .collect::<()>() - .parse("a+b+c"); - } - - #[test] - fn debug_assert_separated_by2() { - assert_eq!( - empty::<&str, extra::Default>() - .to(()) - .separated_by(just(',')) - .count() - .parse(",") - .unwrap(), - 2 - ); - } - - #[test] - #[should_panic] - #[cfg(debug_assertions)] - fn debug_assert_foldl() { - assert_eq!( - empty::<&str, extra::Default>() - .to(1) - .foldl(empty().repeated(), |n, ()| n + 1) - .parse("a+b+c") - .unwrap(), - 3 - ); - } - - #[test] - #[should_panic] - #[cfg(debug_assertions)] - fn debug_assert_foldl_with() { - use extra::SimpleState; - - let state = 100; - empty::<&str, extra::Full, ()>>() - .foldl_with(empty().to(()).repeated(), |_, _, _| ()) - .parse_with_state("a+b+c", &mut state.into()); - } - - #[test] - #[should_panic] - #[cfg(debug_assertions)] - fn debug_assert_foldr() { - empty::<&str, extra::Default>() - .to(()) - .repeated() - .foldr(empty(), |_, _| ()) - .parse("a+b+c"); - } - - #[test] - #[should_panic] - #[cfg(debug_assertions)] - fn debug_assert_foldr_with_state() { - empty::<&str, extra::Default>() - .to(()) - .repeated() - .foldr_with(empty(), |_, _, _| ()) - .parse_with_state("a+b+c", &mut ()); - } - - #[test] - #[should_panic] - #[cfg(debug_assertions)] - fn debug_assert_repeated() { - empty::<&str, extra::Default>() - .to(()) - .repeated() - .parse("a+b+c"); - } - - // TODO what about IterConfigure and TryIterConfigure? - } - - #[test] - #[should_panic] - fn recursive_define_twice() { - let mut expr = Recursive::declare(); - expr.define({ - let atom = any::<&str, extra::Default>() - .filter(|c: &char| c.is_alphabetic()) - .repeated() - .at_least(1) - .collect(); - let sum = expr - .clone() - .then_ignore(just('+')) - .then(expr.clone()) - .map(|(a, b)| format!("{a}{b}")); - - sum.or(atom) - }); - expr.define(expr.clone()); - - expr.then_ignore(end()).parse("a+b+c"); - } - - #[test] - #[should_panic] - fn todo_err() { - let expr = todo::<&str, String, extra::Default>(); - expr.then_ignore(end()).parse("a+b+c"); - } - - #[test] - fn box_impl() { - fn parser<'src>() -> impl Parser<'src, &'src str, Vec> { - Box::new( - any() - .filter(|c: &char| c.is_ascii_digit()) - .repeated() - .at_least(1) - .at_most(3) - .to_slice() - .map(|b: &str| b.parse::().unwrap()) - .padded() - .separated_by(just(',').padded()) - .allow_trailing() - .collect() - .delimited_by(just('['), just(']')), - ) - } - - assert_eq!( - parser().parse("[122 , 23,43, 4, ]").into_result(), - Ok(vec![122, 23, 43, 4]), - ); - assert_eq!( - parser().parse("[0, 3, 6, 900,120]").into_result(), - Ok(vec![0, 3, 6, 900, 120]), - ); - assert_eq!( - parser().parse("[200,400,50 ,0,0, ]").into_result(), - Ok(vec![200, 400, 50, 0, 0]), - ); - } - - #[test] - fn rc_impl() { - use alloc::rc::Rc; - - fn parser<'src>() -> impl Parser<'src, &'src str, Vec> { - Rc::new( - any() - .filter(|c: &char| c.is_ascii_digit()) - .repeated() - .at_least(1) - .at_most(3) - .to_slice() - .map(|b: &str| b.parse::().unwrap()) - .padded() - .separated_by(just(',').padded()) - .allow_trailing() - .collect() - .delimited_by(just('['), just(']')), - ) - } - - assert_eq!( - parser().parse("[122 , 23,43, 4, ]").into_result(), - Ok(vec![122, 23, 43, 4]), - ); - assert_eq!( - parser().parse("[0, 3, 6, 900,120]").into_result(), - Ok(vec![0, 3, 6, 900, 120]), - ); - assert_eq!( - parser().parse("[200,400,50 ,0,0, ]").into_result(), - Ok(vec![200, 400, 50, 0, 0]), - ); - } - - #[derive(Copy, Clone, Debug, PartialEq, Eq)] - struct MyErr(&'static str); - - impl<'src, I: Input<'src>> crate::Error<'src, I> for MyErr { - fn merge(self, other: Self) -> Self { - if other == MyErr("special") { - MyErr("special") - } else { - self - } - } - } - - impl<'src, I> crate::LabelError<'src, I, crate::DefaultExpected<'src, I::Token>> for MyErr - where - I: Input<'src>, - { - fn expected_found>>( - _expected: E, - _found: Option>, - _span: I::Span, - ) -> Self { - MyErr("expected found") - } - } - - #[test] - fn err_prio_0() { - #[allow(dead_code)] - fn always_err<'src>() -> impl Parser<'src, &'src str, (), extra::Err> { - empty().try_map(|_, _| Err(MyErr("special"))) - } - - assert_eq!( - always_err().parse("test").into_result().unwrap_err(), - vec![MyErr("special")] - ) - } - - #[test] - fn err_prio_1() { - #[allow(dead_code)] - fn always_err_choice<'src>() -> impl Parser<'src, &'src str, (), extra::Err> { - choice((just("something").ignored(), empty())).try_map(|_, _| Err(MyErr("special"))) - } - - assert_eq!( - always_err_choice().parse("test").into_result().unwrap_err(), - vec![MyErr("special")] - ) - } - - #[test] - fn into_iter_no_error() { - fn parser<'src>() -> impl Parser<'src, &'src str, (), extra::Err> { - let many_as = just('a') - .ignored() - .repeated() - .at_least(1) - .collect::>(); - - many_as.into_iter().collect() - } - - assert_eq!(parser().parse("aaa").into_result(), Ok(())); - } - - #[cfg(feature = "nightly")] - #[test] - fn flatten() { - fn parser<'src>() -> impl Parser<'src, &'src str, Vec, extra::Err> { - let many_as = just('a') - .map(Some) - .or(any().to(None)) - .repeated() - .flatten() - .collect::>(); - - many_as.into_iter().collect() - } - - assert_eq!( - parser().parse("abracadabra").into_result(), - Ok(vec!['a', 'a', 'a', 'a', 'a']) - ); - } - - #[test] - fn iterable_then() { - fn parser<'src>() -> impl Parser<'src, &'src str, Vec> { - just('a') - .map(Some) - .into_iter() - .then(just('b').repeated()) - .then(just('c').repeated()) - .collect() - } - - assert_eq!( - parser().parse("abbcc").into_result(), - Ok(vec!['a', 'b', 'b', 'c', 'c']) - ); - assert_eq!(parser().parse("acc").into_result(), Ok(vec!['a', 'c', 'c'])); - assert!(parser().parse("bbc").has_errors()); - } - - #[test] - #[cfg(feature = "unstable")] - fn cached() { - fn my_parser<'src>() -> impl Parser<'src, &'src str, &'src str, extra::Default> { - any().repeated().exactly(5).to_slice() - } - - struct MyCache; - - impl crate::cache::Cached for MyCache { - type Parser<'src> = Boxed<'src, 'src, &'src str, &'src str, extra::Default>; - - fn make_parser<'src>(self) -> Self::Parser<'src> { - Parser::boxed(my_parser()) - } - } - - // usage < definition - { - let parser = crate::cache::Cache::new(MyCache); - - for _ in 0..2 { - let s = "hello".to_string(); - - assert_eq!(parser.get().parse(&s).into_result(), Ok("hello")); - assert!(parser.get().parse("goodbye").into_result().is_err()); - } - } - - // usage > definition - { - let s = "hello".to_string(); - - for _ in 0..2 { - let parser = crate::cache::Cache::new(MyCache); - - assert_eq!(parser.get().parse(&s).into_result(), Ok("hello")); - assert!(parser.get().parse("goodbye").into_result().is_err()); - } - } - } - - #[test] - #[allow(dead_code)] - fn map_with_compiles() { - enum Token {} - enum Expr {} - - fn expr<'src, I>() -> impl Parser<'src, I, (Expr, SimpleSpan)> + 'src - where - I: Input<'src, Token = Token, Span = SimpleSpan> + 'src, - { - todo().map_with(|expr, e| (expr, e.span())) - } - } - - #[test] - fn label() { - use crate::label::LabelError; - - fn parser<'src>() -> impl Parser<'src, &'src str, (), extra::Err>> { - just("hello").labelled("greeting").as_context().ignored() - } - - let mut err = as crate::LabelError<&str, char>>::expected_found( - ['h'], - Some('b'.into()), - (0..1).into(), - ); - as LabelError<&str, _>>::label_with(&mut err, "greeting"); - assert_eq!(parser().parse("bye").into_errors(), vec![err]); - - let mut err = as crate::LabelError<&str, char>>::expected_found( - ['l'], - Some('p'.into()), - (3..4).into(), - ); - as LabelError<&str, _>>::in_context(&mut err, "greeting", (0..3).into()); - assert_eq!(parser().parse("help").into_errors(), vec![err]); - - fn parser2<'src>() -> impl Parser<'src, &'src str, (), extra::Err>> { - text::keyword("hello") - .labelled("greeting") - .as_context() - .ignored() - } - - let mut err = - as crate::LabelError<&str, char>>::expected_found(['h'], None, (0..7).into()); - as LabelError<&str, _>>::label_with(&mut err, "greeting"); - assert_eq!(parser2().parse("goodbye").into_errors(), vec![err]); - } - - #[test] - fn labelled_with() { - use crate::label::LabelError; - - fn parser<'src>() -> impl Parser<'src, &'src str, (), extra::Err>> { - just("hello") - .ignored() - .recover_with(via_parser(empty())) - .labelled_with(|| "greeting") - .as_context() - } - - let mut err = - as LabelError<&str, char>>::expected_found(['h'], None, (0..0).into()); - as LabelError<&str, _>>::in_context(&mut err, "greeting", (0..0).into()); - assert_eq!(parser().parse("").into_errors(), vec![err]); - } - - #[test] - #[allow(dead_code)] - fn invalid_escape() { - use crate::LabelError; - - fn string<'src>() -> impl Parser<'src, &'src str, &'src str, extra::Err>> { - let quote = just("\""); - let escaped = just("\\").then(just("n")); - let unescaped = none_of("\\\""); - - unescaped - .ignored() - .or(escaped.ignored()) - .repeated() - .to_slice() - .delimited_by(quote, quote) - } - - assert_eq!( - string().parse(r#""Hello\m""#).into_result(), - Err(vec![ - as LabelError::<&str, char>>::expected_found( - ['n'], - Some('m'.into()), - (7..8).into(), - ) - ]), - ); - } - - #[test] - #[allow(dead_code)] - fn map_err_missed_info() { - use crate::{extra::Err, LabelError}; - - fn erroneous_map_err<'src>() -> impl Parser<'src, &'src str, (), Err>> { - group(( - just("a").or_not(), - just("b").map_err(|mut err| { - LabelError::<&str, _>::label_with(&mut err, 'l'); - err - }), - )) - .ignored() - } - - assert_eq!( - erroneous_map_err().parse("_").into_output_errors(), - ( - None, - vec![LabelError::<&str, _>::expected_found( - ['a', 'l'], - Some('_'.into()), - SimpleSpan::new((), 0..1), - )] - ), - ); - - fn erroneous_then<'src>() -> impl Parser<'src, &'src str, (), Err>> { - group(( - just("a").or_not(), - empty().map_err(|mut err| { - LabelError::<&str, _>::label_with(&mut err, 'l'); - err - }), - just("c"), - )) - .ignored() - } - - assert_eq!( - erroneous_then().parse("_").into_output_errors(), - ( - None, - vec![LabelError::<&str, _>::expected_found( - ['a', 'c'], - Some('_'.into()), - SimpleSpan::new((), 0..1), - )] - ), - ); - } - - #[test] - fn map_err() { - use crate::LabelError; - - let parser = just::>('"').map_err(move |e: Rich| { - println!("Found = {:?}", e.found()); - println!("Expected = {:?}", e.expected().collect::>()); - println!("Span = {:?}", e.span()); - LabelError::<&str, char>::expected_found( - ['"'], - e.found().copied().map(Into::into), - *e.span(), - ) - }); - - assert_eq!( - parser.parse(r#"H"#).into_result(), - Err(vec![LabelError::<&str, char>::expected_found( - ['"'], - Some('H'.into()), - (0..1).into() - )]) - ); - } - - #[test] - fn try_map() { - use crate::{DefaultExpected, LabelError}; - - let parser = group(( - just("a").or_not(), - just("b").try_map(|_, _| Ok(())).or_not(), - just::<_, &str, extra::Err>>("c"), - )) - .ignored(); - - assert_eq!( - parser.parse("").into_output_errors(), - ( - None, - vec![LabelError::<&str, _>::expected_found( - vec![ - DefaultExpected::Token('a'.into()), - DefaultExpected::Token('b'.into()), - DefaultExpected::Token('c'.into()), - ], - None, - SimpleSpan::new((), 0..0) - )] - ) - ); - } - - #[test] - fn try_map_with() { - use crate::{DefaultExpected, LabelError}; - - let parser = group(( - just("a").or_not(), - just("b").try_map_with(|_, _| Ok(())).or_not(), - just::<_, &str, extra::Err>>("c"), - )) - .ignored(); - - assert_eq!( - parser.parse("").into_output_errors(), - ( - None, - vec![LabelError::<&str, _>::expected_found( - vec![ - DefaultExpected::Token('a'.into()), - DefaultExpected::Token('b'.into()), - DefaultExpected::Token('c'.into()), - ], - None, - SimpleSpan::new((), 0..0) - )] - ) - ); - } - - #[test] - fn filter() { - use crate::{DefaultExpected, LabelError}; - - let parser = just::<_, _, extra::Err>>("a").filter(|_| false); - - assert_eq!( - parser.parse("a").into_result(), - Err(vec![LabelError::<&str, _>::expected_found( - [DefaultExpected::SomethingElse], - Some('a'.into()), - SimpleSpan::new((), 0..1) - ),]) - ); - - let parser = group(( - just("a").or_not(), - just("b").filter(|_| false).or_not(), - just::<_, &str, extra::Err>>("c"), - )); - - assert_eq!( - parser.parse("b").into_output_errors(), - ( - None, - vec![LabelError::<&str, _>::expected_found( - vec![ - DefaultExpected::Token('a'.into()), - DefaultExpected::SomethingElse, - DefaultExpected::Token('c'.into()), - ], - Some('b'.into()), - SimpleSpan::new((), 0..1) - )] - ) - ); - } - - #[test] - fn rewind() { - use crate::{DefaultExpected, LabelError}; - - let parser = group((just("a"), any(), just("b").or_not())) - .rewind() - .then(just::<_, _, extra::Err>>("ac")); - - assert_eq!( - parser.parse("ad").into_output_errors(), - ( - None, - vec![LabelError::<&str, _>::expected_found( - [DefaultExpected::Token('c'.into())], - Some('d'.into()), - SimpleSpan::new((), 1..2) - )] - ) - ) - } - - #[test] - fn separated_by() { - use crate::{error::Simple, extra}; - - let parser = just::<_, &str, extra::Err>>("a") - .or_not() - .separated_by(just("b")); - - assert_eq!(parser.parse("bba").into_result(), Ok(())); - } - - #[test] - fn zero_size_custom_failure() { - fn my_custom<'src>() -> impl Parser<'src, &'src str, ()> { - custom(|inp| { - let check = inp.save(); - if inp.parse(just("foo")).is_err() { - inp.rewind(check); - } - Ok(()) - }) - } - - assert!(my_custom().parse("not foo").has_errors()); - } - - #[test] - fn labels() { - use crate::{DefaultExpected, Error, LabelError, TextExpected}; - - let parser = just("a") - .or_not() - .then(text::whitespace::<&str, extra::Err>>()); - - assert_eq!( - parser.parse("b").into_output_errors(), - ( - None, - vec![Error::<&str>::merge( - Error::<&str>::merge( - LabelError::<&str, _>::expected_found( - vec![DefaultExpected::Token('a'.into())], - Some('b'.into()), - SimpleSpan::new((), 0..1) - ), - LabelError::<&str, _>::expected_found( - vec![TextExpected::<&str>::Whitespace], - Some('b'.into()), - SimpleSpan::new((), 0..1) - ), - ), - LabelError::<&str, _>::expected_found( - vec![DefaultExpected::EndOfInput], - Some('b'.into()), - SimpleSpan::new((), 0..1) - ), - )] - ) - ); - } - - #[test] - fn labelled_not() { - use crate::{DefaultExpected, LabelError}; - - let parser = any::<_, extra::Err>>().not().labelled("label"); - - let mut err = LabelError::<&str, _>::expected_found( - [DefaultExpected::SomethingElse], - Some('b'.into()), - SimpleSpan::new((), 0..1), - ); - LabelError::<&str, _>::label_with(&mut err, "label"); - assert_eq!(parser.parse("b").into_output_errors(), (None, vec![err])); - } - - #[test] - fn state_rewind() { - use crate::{extra::Full, inspector::TruncateState}; - - let parser = any::<_, Full, ()>>() - .map_with(|out, extra| { - extra.state().0.push(out); - extra.state().0.len() - 1 - }) - .rewind() - .then_ignore(any()); - - let mut state = TruncateState::default(); - let res = parser.parse_with_state("a", &mut state).unwrap(); - assert_eq!(res, 0); - assert_eq!(state.0.as_slice(), ['a']); - } - - #[test] - fn error_rewind() { - let parser = any::<_, extra::Default>() - .validate(|out, _, emitter| { - emitter.emit(EmptyErr::default()); - out - }) - .rewind() - .then_ignore(any()); - - assert_eq!( - parser.parse("a").into_output_errors(), - (Some('a'), vec![EmptyErr::default()]) - ); - } - - /* - #[test] - fn label_sets() { - use crate::{DefaultExpected, Error, LabelError, TextExpected, text::whitespace}; - - fn tuple<'input>() -> impl Parser<'input, &'input str, (), extra::Err>> { - just("a") - .repeated() - .then_ignore(whitespace()) - .separated_by(just(",")) - .then_ignore(just(")")) - } - - assert_eq!( - tuple().parse("a").into_output_errors(), - ( - None, - vec![Error::<&str>::merge( - LabelError::<&str, _>::expected_found( - vec![TextExpected::<&str>::Whitespace], - None, - SimpleSpan::new((), 1..1) - ), - LabelError::<&str, _>::expected_found( - vec![ - DefaultExpected::Token('a'.into()), - DefaultExpected::Token(','.into()), - DefaultExpected::Token(')'.into()), - ], - None, - SimpleSpan::new((), 1..1) - ) - )] - ) - ); - } - */ - - // Prevent a regression - #[test] - fn labelled_recovery_dont_panic() { - fn parser<'i>() -> impl Parser<'i, &'i str, SimpleSpan> { - choice((choice((just("true"), just("false"))) - .labelled("boolean") - .to_span(),)) - .recover_with(via_parser(any().and_is(text::newline().not()).to_span())) - } - - let _ = parser().parse("tru"); - } -} diff --git a/chumsky-0.12.0/src/private.rs b/chumsky-0.12.0/src/private.rs deleted file mode 100644 index ea01373f3a..0000000000 --- a/chumsky-0.12.0/src/private.rs +++ /dev/null @@ -1,374 +0,0 @@ -use super::*; - -#[derive(Clone)] -pub(crate) struct Located { - pub(crate) pos: T, - pub(crate) err: E, -} - -impl Located { - #[inline] - pub fn at(pos: T, err: E) -> Self { - Self { pos, err } - } -} - -/// The result of calling [`Parser::go`] -pub(crate) type PResult = Result<::Output, ()>; -/// The result of calling [`IterParser::next`] -pub(crate) type IPResult = Result::Output>, ()>; - -/// An abstract parse mode - can be [`Emit`] or [`Check`] in practice, and represents the -/// common interface for handling both in the same method. -pub trait Mode { - /// The output of this mode for a given type - type Output; - - /// Bind the result of a closure into an output - fn bind T>(f: F) -> Self::Output; - - /// Given an [`Output`](Self::Output), takes its value and return a newly generated output - fn map U>(x: Self::Output, f: F) -> Self::Output; - - /// Choose between two fallible functions to execute depending on the mode. - fn choose Result, G: FnOnce(A) -> Result<(), E>>( - arg: A, - f: F, - g: G, - ) -> Result, E>; - - /// Given two [`Output`](Self::Output)s, take their values and combine them into a new - /// output value - fn combine V>( - x: Self::Output, - y: Self::Output, - f: F, - ) -> Self::Output; - /// By-reference version of [`Mode::combine`]. - fn combine_mut(x: &mut Self::Output, y: Self::Output, f: F); - - /// Given an array of outputs, bind them into an output of arrays - fn array(x: [Self::Output; N]) -> Self::Output<[T; N]>; - - fn from_mut(r: &mut Self::Output) -> Self::Output<&mut T>; - - fn get_or T>(r: Self::Output, f: F) -> T; - - /// Invoke a parser user the current mode. This is normally equivalent to - /// [`parser.go::(inp)`](Parser::go), but it can be called on unsized values such as - /// `dyn Parser`. - fn invoke<'a, I, O, E, P>(parser: &P, inp: &mut InputRef<'a, '_, I, E>) -> PResult - where - I: Input<'a>, - E: ParserExtra<'a, I>, - P: Parser<'a, I, O, E> + ?Sized; - - /// Invoke a parser with configuration using the current mode. This is normally equivalent - /// to [`parser.go::(inp)`](ConfigParser::go_cfg), but it can be called on unsized values - /// such as `dyn Parser`. - fn invoke_cfg<'a, I, O, E, P>( - parser: &P, - inp: &mut InputRef<'a, '_, I, E>, - cfg: P::Config, - ) -> PResult - where - I: Input<'a>, - E: ParserExtra<'a, I>, - P: ConfigParser<'a, I, O, E> + ?Sized; - - #[cfg(feature = "pratt")] - fn invoke_pratt_op_prefix<'src, 'parse, Op, I, O, E>( - op: &Op, - inp: &mut InputRef<'src, 'parse, I, E>, - pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, - f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> pratt::OperatorResult, ()> - where - Op: pratt::Operator<'src, I, O, E>, - I: Input<'src>, - E: ParserExtra<'src, I>; - #[cfg(feature = "pratt")] - fn invoke_pratt_op_postfix<'src, 'parse, Op, I, O, E>( - op: &Op, - inp: &mut InputRef<'src, 'parse, I, E>, - pre_expr: &input::Cursor<'src, 'parse, I>, - pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, - lhs: Self::Output, - min_power: i32, - ) -> pratt::OperatorResult, Self::Output> - where - Op: pratt::Operator<'src, I, O, E>, - I: Input<'src>, - E: ParserExtra<'src, I>; - #[cfg(feature = "pratt")] - fn invoke_pratt_op_infix<'src, 'parse, Op, I, O, E>( - op: &Op, - inp: &mut InputRef<'src, 'parse, I, E>, - pre_expr: &input::Cursor<'src, 'parse, I>, - pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, - lhs: Self::Output, - min_power: i32, - f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> pratt::OperatorResult, Self::Output> - where - Op: pratt::Operator<'src, I, O, E>, - I: Input<'src>, - E: ParserExtra<'src, I>; -} - -/// Emit mode - generates parser output -pub struct Emit; - -impl Mode for Emit { - type Output = T; - #[inline(always)] - fn bind T>(f: F) -> Self::Output { - f() - } - #[inline(always)] - fn map U>(x: Self::Output, f: F) -> Self::Output { - f(x) - } - #[inline(always)] - fn choose Result, G: FnOnce(A) -> Result<(), E>>( - arg: A, - f: F, - _: G, - ) -> Result, E> { - f(arg) - } - #[inline(always)] - fn combine V>( - x: Self::Output, - y: Self::Output, - f: F, - ) -> Self::Output { - f(x, y) - } - #[inline(always)] - fn combine_mut(x: &mut Self::Output, y: Self::Output, f: F) { - f(x, y) - } - #[inline(always)] - fn array(x: [Self::Output; N]) -> Self::Output<[T; N]> { - x - } - - #[inline(always)] - fn from_mut(r: &mut Self::Output) -> Self::Output<&mut T> { - r - } - #[inline(always)] - fn get_or T>(r: Self::Output, _f: F) -> T { - r - } - - #[inline(always)] - fn invoke<'a, I, O, E, P>(parser: &P, inp: &mut InputRef<'a, '_, I, E>) -> PResult - where - I: Input<'a>, - E: ParserExtra<'a, I>, - P: Parser<'a, I, O, E> + ?Sized, - { - parser.go_emit(inp) - } - - #[inline(always)] - fn invoke_cfg<'a, I, O, E, P>( - parser: &P, - inp: &mut InputRef<'a, '_, I, E>, - cfg: P::Config, - ) -> PResult - where - I: Input<'a>, - E: ParserExtra<'a, I>, - P: ConfigParser<'a, I, O, E> + ?Sized, - { - parser.go_emit_cfg(inp, cfg) - } - - #[cfg(feature = "pratt")] - #[inline(always)] - fn invoke_pratt_op_prefix<'src, 'parse, Op, I, O, E>( - op: &Op, - inp: &mut InputRef<'src, 'parse, I, E>, - pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, - f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> pratt::OperatorResult, ()> - where - Op: pratt::Operator<'src, I, O, E>, - I: Input<'src>, - E: ParserExtra<'src, I>, - { - op.do_parse_prefix_emit(inp, pre_expr, &f) - } - #[cfg(feature = "pratt")] - #[inline(always)] - fn invoke_pratt_op_postfix<'src, 'parse, Op, I, O, E>( - op: &Op, - inp: &mut InputRef<'src, 'parse, I, E>, - pre_expr: &input::Cursor<'src, 'parse, I>, - pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, - lhs: Self::Output, - min_power: i32, - ) -> pratt::OperatorResult, Self::Output> - where - Op: pratt::Operator<'src, I, O, E>, - I: Input<'src>, - E: ParserExtra<'src, I>, - { - op.do_parse_postfix_emit(inp, pre_expr, pre_op, lhs, min_power) - } - #[cfg(feature = "pratt")] - #[inline(always)] - fn invoke_pratt_op_infix<'src, 'parse, Op, I, O, E>( - op: &Op, - inp: &mut InputRef<'src, 'parse, I, E>, - pre_expr: &input::Cursor<'src, 'parse, I>, - pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, - lhs: Self::Output, - min_power: i32, - f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> pratt::OperatorResult, Self::Output> - where - Op: pratt::Operator<'src, I, O, E>, - I: Input<'src>, - E: ParserExtra<'src, I>, - { - op.do_parse_infix_emit(inp, pre_expr, pre_op, lhs, min_power, &f) - } -} - -/// Check mode - all output is discarded, and only uses parsers to check validity -pub struct Check; - -impl Mode for Check { - type Output = (); - #[inline(always)] - fn bind T>(_: F) -> Self::Output {} - #[inline(always)] - fn map U>(_: Self::Output, _: F) -> Self::Output {} - #[inline(always)] - fn choose Result, G: FnOnce(A) -> Result<(), E>>( - arg: A, - _: F, - g: G, - ) -> Result, E> { - g(arg) - } - #[inline(always)] - fn combine V>( - _: Self::Output, - _: Self::Output, - _: F, - ) -> Self::Output { - } - #[inline(always)] - fn combine_mut(_: &mut Self::Output, _: Self::Output, _: F) {} - #[inline(always)] - fn array(_: [Self::Output; N]) -> Self::Output<[T; N]> {} - #[inline(always)] - fn from_mut(_r: &mut Self::Output) -> Self::Output<&mut T> {} - #[inline(always)] - fn get_or T>(_r: Self::Output, f: F) -> T { - f() - } - - #[inline(always)] - fn invoke<'a, I, O, E, P>(parser: &P, inp: &mut InputRef<'a, '_, I, E>) -> PResult - where - I: Input<'a>, - E: ParserExtra<'a, I>, - P: Parser<'a, I, O, E> + ?Sized, - { - parser.go_check(inp) - } - - #[inline(always)] - fn invoke_cfg<'a, I, O, E, P>( - parser: &P, - inp: &mut InputRef<'a, '_, I, E>, - cfg: P::Config, - ) -> PResult - where - I: Input<'a>, - E: ParserExtra<'a, I>, - P: ConfigParser<'a, I, O, E> + ?Sized, - { - parser.go_check_cfg(inp, cfg) - } - - #[cfg(feature = "pratt")] - #[inline(always)] - fn invoke_pratt_op_prefix<'src, 'parse, Op, I, O, E>( - op: &Op, - inp: &mut InputRef<'src, 'parse, I, E>, - pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, - f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> pratt::OperatorResult, ()> - where - Op: pratt::Operator<'src, I, O, E>, - I: Input<'src>, - E: ParserExtra<'src, I>, - { - op.do_parse_prefix_check(inp, pre_expr, &f) - } - #[cfg(feature = "pratt")] - #[inline(always)] - fn invoke_pratt_op_postfix<'src, 'parse, Op, I, O, E>( - op: &Op, - inp: &mut InputRef<'src, 'parse, I, E>, - pre_expr: &input::Cursor<'src, 'parse, I>, - pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, - lhs: Self::Output, - min_power: i32, - ) -> pratt::OperatorResult, Self::Output> - where - Op: pratt::Operator<'src, I, O, E>, - I: Input<'src>, - E: ParserExtra<'src, I>, - { - op.do_parse_postfix_check(inp, pre_expr, pre_op, lhs, min_power) - } - #[cfg(feature = "pratt")] - #[inline(always)] - fn invoke_pratt_op_infix<'src, 'parse, Op, I, O, E>( - op: &Op, - inp: &mut InputRef<'src, 'parse, I, E>, - pre_expr: &input::Cursor<'src, 'parse, I>, - pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, - lhs: Self::Output, - min_power: i32, - f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, - ) -> pratt::OperatorResult, Self::Output> - where - Op: pratt::Operator<'src, I, O, E>, - I: Input<'src>, - E: ParserExtra<'src, I>, - { - op.do_parse_infix_check(inp, pre_expr, pre_op, lhs, min_power, &f) - } -} - -// TODO: Remove this when MaybeUninit transforms to/from arrays stabilize in any form -pub(crate) trait MaybeUninitExt: Sized { - /// Identical to the unstable [`MaybeUninit::uninit_array`] - fn uninit_array() -> [Self; N]; - - /// Identical to the unstable [`MaybeUninit::array_assume_init`] - unsafe fn array_assume_init(uninit: [Self; N]) -> [T; N]; -} - -impl MaybeUninitExt for MaybeUninit { - #[allow(clippy::uninit_assumed_init)] - fn uninit_array() -> [Self; N] { - // SAFETY: Output type is entirely uninhabited - IE, it's made up entirely of `MaybeUninit` - unsafe { MaybeUninit::uninit().assume_init() } - } - - unsafe fn array_assume_init(uninit: [Self; N]) -> [T; N] { - (&uninit as *const [Self; N] as *const [T; N]).read() - } -} - -pub trait Sealed {} diff --git a/chumsky-0.12.0/src/text.rs b/chumsky-0.12.0/src/text.rs deleted file mode 100644 index 8d72de15c7..0000000000 --- a/chumsky-0.12.0/src/text.rs +++ /dev/null @@ -1,1209 +0,0 @@ -//! Text-specific parsers and utilities. -//! -//! *“Ford!" he said, "there's an infinite number of monkeys outside who want to talk to us about this script for -//! Hamlet they've worked out.â€* -//! -//! The parsers in this module are generic over both Unicode ([`char`]) and ASCII ([`u8`]) characters. Most parsers take -//! a type parameter, `C`, that can be either [`u8`] or [`char`] in order to handle either case. - -use crate::prelude::*; -use alloc::string::ToString; - -use super::*; - -/// A trait implemented by textual character types (currently, [`u8`] and [`char`]). -/// -/// This trait is currently sealed to minimize the impact of breaking changes. If you find a type that you think should -/// implement this trait, please [open an issue/PR](https://github.com/zesterer/chumsky/issues/new). -pub trait Char: Copy + PartialEq + Sealed { - /// Returns true if the character is canonically considered to be inline whitespace (i.e: not part of a newline). - fn is_inline_whitespace(&self) -> bool; - - /// Returns true if the character is canonically considered to be whitespace. - fn is_whitespace(&self) -> bool; - - /// Returns true if the character is canonically considered to be newline. - fn is_newline(&self) -> bool; - - /// Return the '0' digit of the character. - fn digit_zero() -> Self; - - /// Returns true if the character is canonically considered to be a numeric digit. - fn is_digit(&self, radix: u32) -> bool; - - /// Returns true if the character is canonically considered to be valid for starting an identifier. - fn is_ident_start(&self) -> bool; - - /// Returns true if the character is canonically considered to be a valid within an identifier. - fn is_ident_continue(&self) -> bool; - - /// Returns this character as a [`char`]. - fn to_ascii(&self) -> Option; -} - -impl Sealed for &Grapheme {} -impl Char for &Grapheme { - fn is_inline_whitespace(&self) -> bool { - self.as_str() == " " || self.as_str() == "\t" - } - - fn is_whitespace(&self) -> bool { - let mut iter = self.as_str().chars(); - iter.all(unicode::is_whitespace) - } - - fn is_newline(&self) -> bool { - [ - "\r\n", // CR LF - "\n", // Newline - "\r", // Carriage return - "\x0B", // Vertical tab - "\x0C", // Form feed - "\u{0085}", // Next line - "\u{2028}", // Line separator - "\u{2029}", // Paragraph separator - ] - .as_slice() - .contains(&self.as_str()) - } - - fn digit_zero() -> Self { - Grapheme::digit_zero() - } - - fn is_digit(&self, radix: u32) -> bool { - let mut iter = self.as_str().chars(); - match (iter.next(), iter.next()) { - (Some(i), None) => i.is_digit(radix), - _ => false, - } - } - - fn to_ascii(&self) -> Option { - let mut iter = self.as_bytes().iter(); - match (iter.next(), iter.next()) { - (Some(i), None) if i.is_ascii() => Some(*i), - _ => None, - } - } - - fn is_ident_start(&self) -> bool { - let (first, rest) = self.split(); - let is_start = unicode_ident::is_xid_start(first) || first == '_'; - is_start && rest.chars().all(unicode_ident::is_xid_continue) - } - - fn is_ident_continue(&self) -> bool { - let mut iter = self.as_str().chars(); - iter.all(unicode_ident::is_xid_continue) - } -} - -impl Sealed for char {} -impl Char for char { - fn is_inline_whitespace(&self) -> bool { - *self == ' ' || *self == '\t' - } - - fn is_whitespace(&self) -> bool { - unicode::is_whitespace(*self) - } - - fn is_newline(&self) -> bool { - [ - '\n', // Newline - '\r', // Carriage return - '\x0B', // Vertical tab - '\x0C', // Form feed - '\u{0085}', // Next line - '\u{2028}', // Line separator - '\u{2029}', // Paragraph separator - ] - .as_slice() - .contains(self) - } - - fn digit_zero() -> Self { - '0' - } - - fn is_digit(&self, radix: u32) -> bool { - char::is_digit(*self, radix) - } - - fn to_ascii(&self) -> Option { - self.is_ascii().then_some(*self as u8) - } - - fn is_ident_start(&self) -> bool { - unicode_ident::is_xid_start(*self) || *self == '_' - } - - fn is_ident_continue(&self) -> bool { - unicode_ident::is_xid_continue(*self) - } -} - -impl Sealed for u8 {} -impl Char for u8 { - fn is_inline_whitespace(&self) -> bool { - *self == b' ' || *self == b'\t' - } - - fn is_whitespace(&self) -> bool { - self.is_ascii_whitespace() - } - - fn is_newline(&self) -> bool { - [ - b'\n', // Newline - b'\r', // Carriage return - b'\x0B', // Vertical tab - b'\x0C', // Form feed - ] - .as_slice() - .contains(self) - } - - fn digit_zero() -> Self { - b'0' - } - - fn is_digit(&self, radix: u32) -> bool { - (*self as char).is_digit(radix) - } - - fn to_ascii(&self) -> Option { - Some(*self) - } - - fn is_ident_start(&self) -> bool { - (*self as char).is_ident_start() - } - - fn is_ident_continue(&self) -> bool { - (*self as char).is_ident_continue() - } -} - -/// A parser that accepts (and ignores) any number of whitespace characters before or after another pattern. -#[derive(Copy, Clone)] -pub struct Padded { - pub(crate) parser: A, -} - -impl<'src, I, O, E, A> Parser<'src, I, O, E> for Padded -where - I: Input<'src>, - E: ParserExtra<'src, I>, - I::Token: Char, - A: Parser<'src, I, O, E>, -{ - #[doc(hidden)] - #[cfg(feature = "debug")] - fn node_info(&self, scope: &mut debug::NodeScope) -> debug::NodeInfo { - debug::NodeInfo::Padded(Box::new(self.parser.node_info(scope))) - } - - fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult { - inp.skip_while(|c| c.is_whitespace()); - let out = self.parser.go::(inp)?; - inp.skip_while(|c| c.is_whitespace()); - Ok(out) - } - - go_extra!(O); -} - -/// Labels denoting a variety of text-related patterns. -#[derive(Clone, Debug)] -#[non_exhaustive] -pub enum TextExpected { - /// Whitespace (for example: spaces, tabs, or newlines). - Whitespace, - /// Inline whitespace (for example: spaces or tabs). - InlineWhitespace, - /// A newline character or sequence. - Newline, - /// A numeric digit within the given radix range. - /// - /// For example: - /// - /// - `Digit(0, 10)` implies any base-10 digit - /// - `Digit(1, 16)` implies any non-zero hexadecimal digit - Digit(u32, u32), - /// Any identifier. - AnyIdentifier, - /// A specific identifier. - Identifier(Slice), - /// An integer was expected - Int, -} - -impl Copy for TextExpected {} - -/// A parser that accepts (and ignores) any number of whitespace characters. -/// -/// This parser is a `Parser::Repeated` and so methods such as `at_least()` can be called on it. -/// -/// The output type of this parser is `()`. -/// -/// # Examples -/// -/// ``` -/// # use chumsky::prelude::*; -/// let whitespace = text::whitespace::<_, extra::Err>>(); -/// -/// // Any amount of whitespace is parsed... -/// assert_eq!(whitespace.parse("\t \n \r ").into_result(), Ok(())); -/// // ...including none at all! -/// assert_eq!(whitespace.parse("").into_result(), Ok(())); -/// ``` -pub fn whitespace<'src, I, E>() -> Repeated + Copy, (), I, E> -where - I: StrInput<'src>, - I::Token: Char + 'src, - E: ParserExtra<'src, I>, - E::Error: LabelError<'src, I, TextExpected<()>>, -{ - any() - .filter(|c: &I::Token| c.is_whitespace()) - .labelled_with(|| TextExpected::Whitespace) - .as_builtin() - .ignored() - .repeated() -} - -/// A parser that accepts (and ignores) any number of inline whitespace characters. -/// -/// This parser is a `Parser::Repeated` and so methods such as `at_least()` can be called on it. -/// -/// The output type of this parser is `()`. -/// -/// # Examples -/// -/// ``` -/// # use chumsky::prelude::*; -/// let inline_whitespace = text::inline_whitespace::<_, extra::Err>>(); -/// -/// // Any amount of inline whitespace is parsed... -/// assert_eq!(inline_whitespace.parse("\t ").into_result(), Ok(())); -/// // ...including none at all! -/// assert_eq!(inline_whitespace.parse("").into_result(), Ok(())); -/// // ... but not newlines -/// assert!(inline_whitespace.at_least(1).parse("\n\r").has_errors()); -/// ``` -pub fn inline_whitespace<'src, I, E>() -> Repeated + Copy, (), I, E> -where - I: StrInput<'src>, - I::Token: Char + 'src, - E: ParserExtra<'src, I>, - E::Error: LabelError<'src, I, TextExpected<()>>, -{ - any() - .filter(|c: &I::Token| c.is_inline_whitespace()) - .labelled_with(|| TextExpected::InlineWhitespace) - .as_builtin() - .ignored() - .repeated() -} - -/// A parser that accepts (and ignores) any newline characters or character sequences. -/// -/// The output type of this parser is `()`. -/// -/// This parser is quite extensive, recognizing: -/// -/// - Line feed (`\n`) -/// - Carriage return (`\r`) -/// - Carriage return + line feed (`\r\n`) -/// - Vertical tab (`\x0B`) -/// - Form feed (`\x0C`) -/// - Next line (`\u{0085}`) -/// - Line separator (`\u{2028}`) -/// - Paragraph separator (`\u{2029}`) -/// -/// # Examples -/// -/// ``` -/// # use chumsky::prelude::*; -/// let newline = text::newline::<_, extra::Err>>(); -/// -/// assert_eq!(newline.parse("\n").into_result(), Ok(())); -/// assert_eq!(newline.parse("\r").into_result(), Ok(())); -/// assert_eq!(newline.parse("\r\n").into_result(), Ok(())); -/// assert_eq!(newline.parse("\x0B").into_result(), Ok(())); -/// assert_eq!(newline.parse("\x0C").into_result(), Ok(())); -/// assert_eq!(newline.parse("\u{0085}").into_result(), Ok(())); -/// assert_eq!(newline.parse("\u{2028}").into_result(), Ok(())); -/// assert_eq!(newline.parse("\u{2029}").into_result(), Ok(())); -/// ``` -#[must_use] -pub fn newline<'src, I, E>() -> impl Parser<'src, I, (), E> + Copy -where - I: StrInput<'src>, - I::Token: Char + 'src, - E: ParserExtra<'src, I>, - &'src str: OrderedSeq<'src, I::Token>, - E::Error: LabelError<'src, I, TextExpected<()>>, -{ - custom(|inp| { - let before = inp.cursor(); - - if inp - .peek() - .map_or(false, |c: I::Token| c.to_ascii() == Some(b'\r')) - { - inp.skip(); - if inp - .peek() - .map_or(false, |c: I::Token| c.to_ascii() == Some(b'\n')) - { - inp.skip(); - } - Ok(()) - } else { - let c = inp.next(); - if c.map_or(false, |c: I::Token| c.is_newline()) { - Ok(()) - } else { - let span = inp.span_since(&before); - Err(LabelError::expected_found( - [TextExpected::Newline], - c.map(MaybeRef::Val), - span, - )) - } - } - }) - .labelled_with(|| TextExpected::Newline) - .as_builtin() -} - -/// A parser that accepts one or more ASCII digits. -/// -/// The output type of this parser is `I::Slice` (i.e: [`&str`] when `I` is [`&str`], and [`&[u8]`] -/// when `I::Slice` is [`&[u8]`]). -/// -/// The `radix` parameter functions identically to [`char::is_digit`]. If in doubt, choose `10`. -/// -/// # Examples -/// -/// ``` -/// # use chumsky::prelude::*; -/// let digits = text::digits::<_, extra::Err>>(10).to_slice(); -/// -/// assert_eq!(digits.parse("0").into_result(), Ok("0")); -/// assert_eq!(digits.parse("1").into_result(), Ok("1")); -/// assert_eq!(digits.parse("01234").into_result(), Ok("01234")); -/// assert_eq!(digits.parse("98345").into_result(), Ok("98345")); -/// // A string of zeroes is still valid. Use `int` if this is not desirable. -/// assert_eq!(digits.parse("0000").into_result(), Ok("0000")); -/// assert!(digits.parse("").has_errors()); -/// ``` -#[must_use] -pub fn digits<'src, I, E>( - radix: u32, -) -> Repeated>::Token, E> + Copy, I::Token, I, E> -where - I: StrInput<'src>, - I::Token: Char + 'src, - E: ParserExtra<'src, I>, - E::Error: LabelError<'src, I, TextExpected<()>>, -{ - any() - .filter(move |c: &I::Token| c.is_digit(radix)) - .labelled_with(move || TextExpected::Digit(0, radix)) - .as_builtin() - .map_err(move |mut err: E::Error| { - err.label_with(TextExpected::Digit(0, radix)); - err - }) - .repeated() - .at_least(1) -} - -/// A parser that accepts a non-negative integer. -/// -/// An integer is defined as a non-empty sequence of ASCII digits, where the first digit is non-zero or the sequence -/// has length one. -/// -/// The output type of this parser is `I::Slice` (i.e: [`&str`] when `I` is [`&str`], and [`&[u8]`] -/// when `I::Slice` is [`&[u8]`]). -/// -/// The `radix` parameter functions identically to [`char::is_digit`]. If in doubt, choose `10`. -/// -/// # Examples -/// -/// ``` -/// # use chumsky::prelude::*; -/// let dec = text::int::<_, extra::Err>>(10); -/// -/// assert_eq!(dec.parse("0").into_result(), Ok("0")); -/// assert_eq!(dec.parse("1").into_result(), Ok("1")); -/// assert_eq!(dec.parse("1452").into_result(), Ok("1452")); -/// // No leading zeroes are permitted! -/// assert!(dec.parse("04").has_errors()); -/// -/// let hex = text::int::<_, extra::Err>>(16); -/// -/// assert_eq!(hex.parse("2A").into_result(), Ok("2A")); -/// assert_eq!(hex.parse("d").into_result(), Ok("d")); -/// assert_eq!(hex.parse("b4").into_result(), Ok("b4")); -/// assert!(hex.parse("0B").has_errors()); -/// ``` -/// -#[must_use] -pub fn int<'src, I, E>(radix: u32) -> impl Parser<'src, I, >::Slice, E> + Copy -where - I: StrInput<'src>, - I::Token: Char + 'src, - E: ParserExtra<'src, I>, - E::Error: LabelError<'src, I, TextExpected<()>> + LabelError<'src, I, MaybeRef<'src, I::Token>>, -{ - any() - .filter(move |c: &I::Token| c.is_digit(radix) && c != &I::Token::digit_zero()) - .then( - any() - .filter(move |c: &I::Token| c.is_digit(radix)) - .repeated(), - ) - .ignored() - .or(just(I::Token::digit_zero()).ignored()) - .to_slice() - .labelled_with(|| TextExpected::Int) - .as_builtin() -} - -/// Parsers and utilities for working with ASCII inputs. -pub mod ascii { - use super::*; - - /// A parser that accepts a C-style identifier. - /// - /// The output type of this parser is [`SliceInput::Slice`] (i.e: [`&str`] when `I` is [`&str`], and [`&[u8]`] when `I` is - /// [`&[u8]`]). - /// - /// An identifier is defined as an ASCII alphabetic character or an underscore followed by any number of alphanumeric - /// characters or underscores. The regex pattern for it is `[a-zA-Z_][a-zA-Z0-9_]*`. - #[must_use] - pub fn ident<'src, I, E>() -> impl Parser<'src, I, >::Slice, E> + Copy - where - I: StrInput<'src>, - I::Token: Char + 'src, - E: ParserExtra<'src, I>, - E::Error: LabelError<'src, I, TextExpected<()>>, - { - any() - .filter(|c: &I::Token| { - c.to_ascii() - .map_or(false, |i| i.is_ascii_alphabetic() || i == b'_') - }) - .then( - any() - .filter(|c: &I::Token| { - c.to_ascii() - .map_or(false, |i| i.is_ascii_alphanumeric() || i == b'_') - }) - .repeated(), - ) - .to_slice() - .labelled_with(|| TextExpected::AnyIdentifier) - .as_builtin() - } - - /// Like [`ident`], but only accepts a specific identifier while rejecting trailing identifier characters. - /// - /// The output type of this parser is `I::Slice` (i.e: [`&str`] when `I` is [`&str`], and [`&[u8]`] - /// when `I::Slice` is [`&[u8]`]). - /// - /// # Examples - /// - /// ``` - /// # use chumsky::prelude::*; - /// let def = text::ascii::keyword::<_, _, extra::Err>>("def"); - /// - /// // Exactly 'def' was found - /// assert_eq!(def.parse("def").into_result(), Ok("def")); - /// // Exactly 'def' was found, with non-identifier trailing characters - /// // This works because we made the parser lazy: it parses 'def' and ignores the rest - /// assert_eq!(def.clone().lazy().parse("def(foo, bar)").into_result(), Ok("def")); - /// // 'def' was found, but only as part of a larger identifier, so this fails to parse - /// assert!(def.lazy().parse("define").has_errors()); - /// ``` - #[track_caller] - pub fn keyword<'src, I, S, E>( - keyword: S, - ) -> impl Parser<'src, I, >::Slice, E> + Clone + 'src - where - I: StrInput<'src>, - I::Token: Char + fmt::Debug + 'src, - S: PartialEq + Clone + 'src, - E: ParserExtra<'src, I> + 'src, - E::Error: LabelError<'src, I, TextExpected<()>> + LabelError<'src, I, TextExpected>, - { - /* - #[cfg(debug_assertions)] - { - let mut cs = keyword.seq_iter(); - if let Some(c) = cs.next() { - let c = c.borrow().to_char(); - assert!(c.is_ascii_alphabetic() || c == '_', "The first character of a keyword must be ASCII alphabetic or an underscore, not {:?}", c); - } else { - panic!("Keyword must have at least one character"); - } - for c in cs { - let c = c.borrow().to_char(); - assert!(c.is_ascii_alphanumeric() || c == '_', "Trailing characters of a keyword must be ASCII alphanumeric or an underscore, not {:?}", c); - } - } - */ - ident() - .try_map({ - let keyword = keyword.clone(); - move |s: I::Slice, span| { - if keyword == s { - Ok(()) - } else { - Err(LabelError::expected_found( - [TextExpected::Identifier(keyword.clone())], - None, - span, - )) - } - } - }) - .to_slice() - .labelled(TextExpected::Identifier(keyword)) - .as_builtin() - } -} - -// Unicode is the default -pub use unicode::*; - -/// Parsers and utilities for working with unicode inputs. -pub mod unicode { - use super::*; - - use core::str::{Bytes, Chars}; - use unicode_segmentation::UnicodeSegmentation; - - /// A type containing one extended Unicode grapheme cluster. - #[derive(PartialEq, Eq)] - #[repr(transparent)] - pub struct Grapheme { - inner: str, - } - - impl Grapheme { - fn new(inner: &str) -> &Self { - // SAFETY: This is ok because Grapheme is #[repr(transparent)] - unsafe { &*(inner as *const str as *const Self) } - } - - /// Creates a new grapheme with the character `'0'` inside it. - pub fn digit_zero() -> &'static Self { - Self::new("0") - } - - /// Gets an iterator over code points. - pub fn code_points(&self) -> Chars<'_> { - self.inner.chars() - } - - /// Gets an iterator over bytes. - pub fn bytes(&self) -> Bytes<'_> { - self.inner.bytes() - } - - /// Gets the slice of code points that are contained in the grapheme cluster. - pub fn as_str(&self) -> &str { - &self.inner - } - - /// Gets the slice of bytes that are contained in the grapheme cluster. - pub fn as_bytes(&self) -> &[u8] { - self.inner.as_bytes() - } - - /// Splits the grapheme into the first code point and the remaining code points. - pub fn split(&self) -> (char, &str) { - let mut iter = self.inner.chars(); - // The operation never falls because the grapheme always contains at least one code point. - let first = iter.next().unwrap(); - (first, iter.as_str()) - } - } - - impl fmt::Debug for Grapheme { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("g'")?; - for i in self.as_str().chars() { - write!(f, "{}", i.escape_debug())?; - } - f.write_str("'")?; - Ok(()) - } - } - - impl fmt::Display for Grapheme { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.inner, f) - } - } - - impl AsRef for Grapheme { - fn as_ref(&self) -> &str { - self.as_str() - } - } - - impl AsRef<[u8]> for Grapheme { - fn as_ref(&self) -> &[u8] { - self.as_bytes() - } - } - - impl AsRef for Grapheme { - fn as_ref(&self) -> &Grapheme { - self - } - } - - impl Borrow for Grapheme { - fn borrow(&self) -> &str { - self.as_str() - } - } - - impl Borrow<[u8]> for Grapheme { - fn borrow(&self) -> &[u8] { - self.as_bytes() - } - } - - impl<'src> From<&'src Grapheme> for Box { - fn from(value: &'src Grapheme) -> Self { - let value: Box = Box::from(value.as_str()); - // SAFETY: This is ok because Grapheme is #[repr(transparent)] - unsafe { Box::from_raw(Box::into_raw(value) as *mut Grapheme) } - } - } - - impl From> for Box { - fn from(value: Box) -> Self { - // SAFETY: This is ok because Grapheme is #[repr(transparent)] - unsafe { Box::from_raw(Box::into_raw(value) as *mut str) } - } - } - - impl From> for Box<[u8]> { - fn from(value: Box) -> Self { - Box::::from(value).into() - } - } - - /// A type containing any number of extended Unicode grapheme clusters. - #[derive(PartialEq, Eq)] - #[repr(transparent)] - pub struct Graphemes { - inner: str, - } - - impl Graphemes { - /// Create a new graphemes. - pub fn new(inner: &str) -> &Self { - // SAFETY: This is ok because Graphemes is #[repr(transparent)] - unsafe { &*(inner as *const str as *const Self) } - } - - /// Gets an iterator over graphemes. - pub fn iter(&self) -> GraphemesIter<'_> { - self.into_iter() - } - - /// Gets an iterator over code points. - pub fn code_points(&self) -> Chars<'_> { - self.inner.chars() - } - - /// Gets an iterator over bytes. - pub fn bytes(&self) -> Bytes<'_> { - self.inner.bytes() - } - - /// Gets the slice of code points that are contained in the string. - pub fn as_str(&self) -> &str { - &self.inner - } - - /// Gets the slice of bytes that are contained in the string. - pub fn as_bytes(&self) -> &[u8] { - self.inner.as_bytes() - } - } - - impl fmt::Debug for Graphemes { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("g")?; - fmt::Debug::fmt(&self.inner, f) - } - } - - impl fmt::Display for Graphemes { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.inner, f) - } - } - - impl AsRef for Graphemes { - fn as_ref(&self) -> &str { - self.as_str() - } - } - - impl AsRef<[u8]> for Graphemes { - fn as_ref(&self) -> &[u8] { - self.as_bytes() - } - } - - impl AsRef for Graphemes { - fn as_ref(&self) -> &Graphemes { - self - } - } - - impl Borrow for Graphemes { - fn borrow(&self) -> &str { - self.as_str() - } - } - - impl Borrow<[u8]> for Graphemes { - fn borrow(&self) -> &[u8] { - self.as_bytes() - } - } - - impl<'src> From<&'src str> for &'src Graphemes { - fn from(value: &'src str) -> Self { - Graphemes::new(value) - } - } - - impl<'src> From<&'src Graphemes> for &'src str { - fn from(value: &'src Graphemes) -> Self { - value.as_str() - } - } - - impl<'src> From<&'src Graphemes> for Box { - fn from(value: &'src Graphemes) -> Self { - value.as_str().into() - } - } - - impl<'src> From<&'src str> for Box { - fn from(value: &'src str) -> Self { - Box::::from(value).into() - } - } - - impl From> for Box { - fn from(value: Box) -> Self { - // SAFETY: This is ok because Grapheme is #[repr(transparent)] - unsafe { Box::from_raw(Box::into_raw(value) as *mut Graphemes) } - } - } - - impl From> for Box { - fn from(value: Box) -> Self { - // SAFETY: This is ok because Grapheme is #[repr(transparent)] - unsafe { Box::from_raw(Box::into_raw(value) as *mut str) } - } - } - - impl From> for Box<[u8]> { - fn from(value: Box) -> Self { - Box::::from(value).into() - } - } - - impl<'src> IntoIterator for &'src Graphemes { - type Item = &'src Grapheme; - - type IntoIter = GraphemesIter<'src>; - - fn into_iter(self) -> Self::IntoIter { - GraphemesIter::new(self) - } - } - - impl Sealed for &'_ Graphemes {} - impl<'src> StrInput<'src> for &'src Graphemes { - #[doc(hidden)] - fn stringify(slice: Self::Slice) -> String { - slice.to_string() - } - } - - impl<'src> Input<'src> for &'src Graphemes { - type Cursor = usize; - type Span = SimpleSpan; - - type Token = &'src Grapheme; - type MaybeToken = &'src Grapheme; - - type Cache = Self; - - #[inline] - fn begin(self) -> (Self::Cursor, Self::Cache) { - (0, self) - } - - #[inline] - fn cursor_location(cursor: &Self::Cursor) -> usize { - *cursor - } - - #[inline(always)] - unsafe fn next_maybe( - this: &mut Self::Cache, - cursor: &mut Self::Cursor, - ) -> Option { - if *cursor < this.as_str().len() { - // SAFETY: `cursor < self.len()` above guarantees cursor is in-bounds - // We only ever return cursors that are at a code point boundary. - // The `next()` implementation returns `None`, only in the - // situation of zero length of the remaining part of the string. - // And the Unicode standard guarantees that any sequence of code - // points is a valid sequence of grapheme clusters, so the - // behaviour of the `next()` function should not change. - let c = this - .as_str() - .get_unchecked(*cursor..) - .graphemes(true) - .next() - .unwrap_unchecked(); - *cursor += c.len(); - Some(Grapheme::new(c)) - } else { - None - } - } - - #[inline(always)] - unsafe fn span(_this: &mut Self::Cache, range: Range<&Self::Cursor>) -> Self::Span { - (*range.start..*range.end).into() - } - } - - impl<'src> ExactSizeInput<'src> for &'src Graphemes { - #[inline(always)] - unsafe fn span_from(this: &mut Self::Cache, range: RangeFrom<&Self::Cursor>) -> Self::Span { - (*range.start..this.as_str().len()).into() - } - } - - impl<'src> ValueInput<'src> for &'src Graphemes { - #[inline(always)] - unsafe fn next(this: &mut Self::Cache, cursor: &mut Self::Cursor) -> Option { - Self::next_maybe(this, cursor) - } - } - - impl<'src> SliceInput<'src> for &'src Graphemes { - type Slice = Self; - - #[inline(always)] - fn full_slice(this: &mut Self::Cache) -> Self::Slice { - *this - } - - #[inline(always)] - unsafe fn slice(this: &mut Self::Cache, range: Range<&Self::Cursor>) -> Self::Slice { - Graphemes::new(&this.as_str()[*range.start..*range.end]) - } - - #[inline(always)] - unsafe fn slice_from( - this: &mut Self::Cache, - from: RangeFrom<&Self::Cursor>, - ) -> Self::Slice { - Graphemes::new(&this.as_str()[*from.start..]) - } - } - - /// Grapheme iterator type. - #[derive(Debug, Clone)] - pub struct GraphemesIter<'src> { - iter: unicode_segmentation::Graphemes<'src>, - } - - impl<'src> GraphemesIter<'src> { - /// Create a new grapheme iterator. - pub fn new(graphemes: &'src Graphemes) -> Self { - Self { - iter: graphemes.as_str().graphemes(true), - } - } - - /// Gets the slice of code points that are contained in the grapheme cluster. - pub fn as_str(self) -> &'src str { - self.iter.as_str() - } - } - - impl<'src> Iterator for GraphemesIter<'src> { - type Item = &'src Grapheme; - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - #[inline] - fn next(&mut self) -> Option { - self.iter.next().map(Grapheme::new) - } - } - - impl DoubleEndedIterator for GraphemesIter<'_> { - #[inline] - fn next_back(&mut self) -> Option { - self.iter.next_back().map(Grapheme::new) - } - } - - /// A parser that accepts an identifier. - /// - /// The output type of this parser is [`SliceInput::Slice`] (i.e: [`&str`] when `I` is [`&str`], and [`&[u8]`] when `I` is - /// [`&[u8]`]). - /// - /// An identifier is defined as per "Default Identifiers" in [Unicode Standard Annex #31](https://www.unicode.org/reports/tr31/). - #[must_use] - pub fn ident<'src, I, E>() -> impl Parser<'src, I, >::Slice, E> + Copy - where - I: StrInput<'src>, - I::Token: Char + 'src, - E: ParserExtra<'src, I>, - E::Error: LabelError<'src, I, TextExpected<()>>, - { - any() - .filter(|c: &I::Token| c.is_ident_start()) - .then( - any() - .filter(|c: &I::Token| c.is_ident_continue()) - .repeated(), - ) - .to_slice() - .labelled(TextExpected::AnyIdentifier) - .as_builtin() - } - - /// Like [`ident`], but only accepts a specific identifier while rejecting trailing identifier characters. - /// - /// The output type of this parser is `I::Slice` (i.e: [`&str`] when `I` is [`&str`], and [`&[u8]`] - /// when `I::Slice` is [`&[u8]`]). - /// - /// # Examples - /// - /// ``` - /// # use chumsky::prelude::*; - /// let def = text::ascii::keyword::<_, _, extra::Err>>("def"); - /// - /// // Exactly 'def' was found - /// assert_eq!(def.parse("def").into_result(), Ok("def")); - /// // Exactly 'def' was found, with non-identifier trailing characters - /// // This works because we made the parser lazy: it parses 'def' and ignores the rest - /// assert_eq!(def.clone().lazy().parse("def(foo, bar)").into_result(), Ok("def")); - /// // 'def' was found, but only as part of a larger identifier, so this fails to parse - /// assert!(def.lazy().parse("define").has_errors()); - /// ``` - #[track_caller] - pub fn keyword<'src, I, S, E>( - keyword: S, - ) -> impl Parser<'src, I, >::Slice, E> + Clone + 'src - where - I: StrInput<'src>, - I::Slice: PartialEq, - I::Token: Char + fmt::Debug + 'src, - S: PartialEq + Clone + 'src, - E: ParserExtra<'src, I> + 'src, - E::Error: LabelError<'src, I, TextExpected<()>> + LabelError<'src, I, TextExpected>, - { - /* - #[cfg(debug_assertions)] - { - let mut cs = keyword.seq_iter(); - if let Some(c) = cs.next() { - let c = c.borrow(); - assert!( - c.is_ident_start(), - "The first character of a keyword must be a valid unicode XID_START, not {:?}", - c - ); - } else { - panic!("Keyword must have at least one character"); - } - for c in cs { - let c = c.borrow(); - assert!(c.is_ident_continue(), "Trailing characters of a keyword must be valid as unicode XID_CONTINUE, not {:?}", c); - } - } - */ - ident() - .try_map({ - let keyword = keyword.clone(); - move |s: I::Slice, span| { - if keyword == s { - Ok(()) - } else { - Err(LabelError::expected_found( - [TextExpected::Identifier(keyword.clone())], - None, - span, - )) - } - } - }) - .to_slice() - .labelled(TextExpected::Identifier(keyword.clone())) - .as_builtin() - } - - /// Like [`char::is_whitespace`], but rejects the characters U+202A, U+202B, U+202C, U+202D, U+202E, U+2066, U+2067, U+2068, U+2069 - /// to mitigate against [CVE-2021-42574](https://nvd.nist.gov/vuln/detail/CVE-2021-42574) - pub fn is_whitespace(c: char) -> bool { - c.is_whitespace() - && !matches!( - c, - '\u{202A}' - | '\u{202B}' - | '\u{202C}' - | '\u{202D}' - | '\u{202E}' - | '\u{2066}' - | '\u{2067}' - | '\u{2068}' - | '\u{2069}' - ) - } -} - -#[cfg(test)] -mod tests { - use crate::prelude::*; - use std::fmt; - - fn make_ascii_kw_parser<'src, I>(s: I::Slice) -> impl Parser<'src, I, ()> - where - I: crate::StrInput<'src>, - I::Slice: PartialEq, - I::Token: crate::Char + fmt::Debug + 'src, - { - text::ascii::keyword(s).ignored() - } - - fn make_unicode_kw_parser<'src, I>(s: I::Slice) -> impl Parser<'src, I, ()> - where - I: crate::StrInput<'src>, - I::Slice: PartialEq, - I::Token: crate::Char + fmt::Debug + 'src, - { - text::unicode::keyword(s).ignored() - } - - fn test_ok<'src, P: Parser<'src, &'src str, &'src str>>(parser: P, input: &'src str) { - assert_eq!( - parser.parse(input), - ParseResult { - output: Some(input), - errs: vec![] - } - ); - } - - fn test_err<'src, P: Parser<'src, &'src str, &'src str>>(parser: P, input: &'src str) { - assert_eq!( - parser.parse(input), - ParseResult { - output: None, - errs: vec![EmptyErr::default()] - } - ); - } - - #[test] - fn keyword_good() { - make_ascii_kw_parser::<&str>("hello"); - make_ascii_kw_parser::<&str>("_42"); - make_ascii_kw_parser::<&str>("_42"); - - make_unicode_kw_parser::<&str>("שלו×"); - make_unicode_kw_parser::<&str>("привет"); - make_unicode_kw_parser::<&str>("你好"); - } - - #[test] - fn ident() { - let ident = text::ident::<&str, extra::Default>(); - test_ok(ident, "foo"); - test_ok(ident, "foo_bar"); - test_ok(ident, "foo_"); - test_ok(ident, "_foo"); - test_ok(ident, "_"); - test_ok(ident, "__"); - test_ok(ident, "__init__"); - test_err(ident, ""); - test_err(ident, "."); - test_err(ident, "123"); - } - - #[test] - fn whitespace() { - use crate::{whitespace, LabelError, TextExpected}; - - let parser = whitespace::<&str, extra::Err>>().exactly(1); - - assert_eq!( - parser.parse("").into_output_errors(), - ( - None, - vec![LabelError::<&str, _>::expected_found( - vec![TextExpected::<&str>::Whitespace], - None, - SimpleSpan::new((), 0..0) - )] - ) - ); - } - - /* - #[test] - #[should_panic] - fn keyword_numeric() { - make_ascii_kw_parser::<&str>("42"); - } - - #[test] - #[should_panic] - fn keyword_empty() { - make_ascii_kw_parser::<&str>(""); - } - - #[test] - #[should_panic] - fn keyword_not_alphanum() { - make_ascii_kw_parser::<&str>("hi\n"); - } - - #[test] - #[should_panic] - fn keyword_unicode_in_ascii() { - make_ascii_kw_parser::<&str>("שלו×"); - } - */ -} diff --git a/chumsky-0.13.0/.cargo-checksum.json b/chumsky-0.13.0/.cargo-checksum.json new file mode 100644 index 0000000000..2365e3f627 --- /dev/null +++ b/chumsky-0.13.0/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".cargo_vcs_info.json":"57e2a145e793b62e29b1d2f40d57de66d7b5a5c70afb42b2d7767d0d29fcb85b",".github/FUNDING.yml":"ab18b2d04a647da2d2e56121974f3de4db30e8b05b1cb4006e204a9928acb064",".github/workflows/rust.yml":"ba3cd2df8bf43154202a6dfeacadfd8cf8cc3b72eee03e5f0f8c911dddd32718","CHANGELOG.md":"cf49705e87a267b45dd66a47afc0d1a4885a3716e73d02bbc882ea4b6b9c4fc2","CONTRIBUTING.md":"0f1aa4546148eef588c7b93da30c8133b6af92091d39218cdefd2b90a5c237e4","Cargo.lock":"ff7aa3c77323cfcc051ffe913e03d8f5500d8629353caa033ba96b1bc65596a8","Cargo.toml":"4be12d6741ee0dd219ef1b64bac7c29556f2c5b36e17145b9d5c9bf304fa1284","Cargo.toml.orig":"3626e94be8d126bd5458fcef51b14ac7c82678e43c022a2eaa756e19237156a3","LICENSE":"88f7ddf73afcffee97e0a19211ddeecd7d178ecb5c09bbfe472ce4cfcceb6269","README.md":"631bdc717938e5b0bedbf039c042591a3428fad17aa6711c262481eabd855028","benches/backtrack.rs":"d207b4c1baabe016ddf7265831a262287d5647de99b24a4b318b639d04eeb7a0","benches/cbor.rs":"4686a25bb9c7b4523e99f1b23e3984a104b2f8f2c50188deab2f7ff82eaf151d","benches/json.pest":"d0b95c1b222cea4b3f668a55138ce50ba914e04189f4e4aa2641b6f94f3b127c","benches/json.rs":"9972b8cd9c57cf3a559acbf9d44ee2cb37e864cced6765bd48789ab9c165be1b","benches/lex.rs":"0b29cc8c2c3e76bf8feefb1acda9a5e69ed5a707a4d4c2e966b1c4229ff960dd","benches/parser.rs":"b7bfdc1535c486734df36238e41165ef2a446c6d7cde59e5fc4c905604ec88bd","benches/tokens.txt":"2f77148de6ac2840498b94fb7383eb75ba57d6848467221fca0b71e190dacdd2","benches/utils.rs":"bb589553bcd7b45bb841f70003d7194c309837b261c05d721f195d39b0c0ab00","examples/brainfuck.rs":"fab968d94d69bc62dc5aa33f914ab527360c70052829cf09345ab99543baf9ee","examples/debug.rs":"4e211c566ff68e9347ffdb78e8917be0158be0f062b5e819f9788897ca691398","examples/foo.rs":"0be0f8351ef8a6b3e85f0690993a89250579e5d5c11655c066b561fc67eb5905","examples/indent.rs":"e6e597948d47625c087e954632e1ddeab295d26f834df9f166c6cee2a01654dd","examples/io.rs":"709c019a58729d09c2e5a7982b660f1b0facc0f3f8e935fda208f98ef471dde1","examples/json.rs":"1c926d20b2420bba40dfe912d30edc97acd449f3c1026b5a0e943a47d03feb94","examples/json_fast.rs":"0a378cc447b21679b4eabfc879276c32e312c6ebf881f6f77d37298ae7ad32f7","examples/logos.rs":"c9b064b0cf6ca377348db6d2f548f653d957f4db68ef2c2de3b7f67d6d94f780","examples/mini_ml.rs":"8977e5b749153ed6365e2a4b488712469d7e2a2fe2c7c68c6e7d435b16520a92","examples/nano_rust.rs":"98152fc911f27ffeb51f5ce06bd2fa24918040cb58c93d9d76d7a15579d4d812","examples/nested.rs":"8302cf06d960bcd04f4060d1736a390cf063cb7604714e97b95c69b75a8d8622","examples/nested_spans.rs":"83980009a4e11c851b0a6d9771d40f589e48a9294e98c195119fd4a7a317f90c","examples/pythonic.rs":"18a98bf6f3ee17e58a1a3da7affe099da08d8ecc9a980c9a278ec74f9ff9c363","examples/sample.bf":"824a423496b3847b635d8da95d183867115318e671f731149c3603c732dbed77","examples/sample.foo":"f0c3345067c4716498e0c37d142254c6eb8edbb600b3fedd16a90c1031a6fbdf","examples/sample.io":"f4861ac3504a1c932b40367dfe5e1d7ac71a89a2064402a3a233b9fabaa7db10","examples/sample.json":"e79d3d751fe16760274b5ff22c6463aaaade108b6bd2a54af588489b88f809f7","examples/sample.mini_ml":"6890c6368493676316c9eecd9ba324de33325bfdae5a14aa6ff6a35a4fda7a03","examples/sample.nrs":"e62d5df3a6b54b1fd5efdc531731e984eda7b81b3067c8f561b372832309520e","examples/sample.py":"d6a788dd91e4f8d9083826583d503d8b3eea13f8aa5d9d713e10123cb12e48c4","examples/zero-copy.rs":"07f009af5e3bc0bd1c72a5cf60ecd37fc4bd0fb066532d6bee3b90bdb4d4187d","guide/README.md":"ea7239ec6cdc5d3b1af180b4f5b6a5a00e157da9e38d4a76750afac9062708b8","guide/debugging.md":"6de7f0412900caa8ddf796a1dbe36c7de4eb8d6027e383c19c102bcce7e6cc0c","guide/error_and_recovery.md":"c989eee148bd74c8f19a89c1450b17831593f981045a09266cd6a98ab5d173ef","guide/getting_started.md":"f18bb5ea0ab7522ded37d9e6457c56b8b026b1a86e724db35a7f79e5a631c688","guide/intro.md":"b6cbfc87a3ca126b18b95f71d0c8fa32aa980f49ffe7eab65e94bd22a9d73feb","guide/key_concepts.md":"8e17f015a8a5f8cd9f3dee878ffa7ee5c694fdac96b35ca60757df3e5c22996d","guide/meet_the_parsers.md":"80a524c0796a187fc6b1635a79936fa765a2c1405093aac99b0f02bfa369ac5f","guide/recursion.md":"8d9cfe4eb9c50eae504ee738f6248e0878044a6a444cbd76f08af2bce44953b7","guide/technical_notes.md":"9001ebbf771adba9338c331887e524d57933fd9d8dcbb4c53006d0af5888320f","guide/tutorial.md":"96805d25e975ae9d166791362141fab99aa8b85b06466393f11e35b2e4c80b3f","src/blanket.rs":"9800a90e4727b724b6fd56801975d7edae7bcc675e900bb878e418d845b56af1","src/combinator.rs":"aac631728c1046f6c37313da801007f9e48d29a603ed0f25e8d299ad95108a6c","src/container.rs":"7d9d76d4243a5ff9fe63ea19577ee60d668265d1e0c17ca3b06d035d52a7e61f","src/debug.rs":"0e78fafb925c56989be1e85fd2119b297875d715462a2e74c00ea0fb60cbcd2d","src/either.rs":"1a6a27e22083343d72cfc835706991a02d077b494958c16403907b77cf4955f0","src/error.rs":"8980ad8613d3737b0d41c3fd4729648375844cd6230720c6091a899085572797","src/extension.rs":"d6ae5423ff4d4d0e821e2f14ce5cc4a10529727aea6bd49a3226f5a9abc92dba","src/extra.rs":"08e645ec8e2f5fd26a707c0e25ed1345c2d5b01218faf0ada7a588a6a7a819b2","src/guide.rs":"c36df23f057cc129e3f0331794790964d9dfa5cc42f6409ffac58bfccab6d78b","src/input.rs":"c1fa2da618bed5df362e6e7c7bdcce3ec8f2f860357e7e98e18863907ebb74bc","src/inspector.rs":"f6ba2b202f79060885364252f5e158f3f807ad984f254cc0852d079b27cf09f0","src/label.rs":"598ab1037e2a9494e5fe6a789a8803979c36a1b8b36f04173723a27200663cf0","src/lib.rs":"7d14bb0543907df0651376ca32e3c29fd67f2be541589294eef8524e57ebb412","src/number.rs":"95de9b867beebbfb1a1f0cb16d70548fb0b74db5a6f6195c0f7021a2f09393ca","src/pratt.rs":"3e5c640512065b655b153907ac1cbefb00a7dc673e020fe633adab2156c31f4e","src/primitive.rs":"045685e53d83a8c8c10e996a673b69ccaee8693e661fdbad4d8dea515bee452e","src/private.rs":"12f1304afad920555255cc3c146fab109e3d5032c71d90b2f6808eb971340c12","src/recovery.rs":"b3eae2f2b6083e9d2d2ece728842f0ad7ea2e97647950ab1497517d1de3301be","src/recursive.rs":"9d23ff4ec2bdb6ca9cd7d47631f563d9b2519ace2e66427fb18e9609330ff5a5","src/regex.rs":"ccf3db8c8f33ca7b1a1b6b90c7ef23c1cf02160d33f51d686da49124c27a955e","src/span.rs":"847c7eb130e2353df8f304bfe2d7e250f1c7cb33cd1277e513fe492a9f7de139","src/stream.rs":"016c177306f3de014cb52e317446a5487dfad4fa68e7f5c37b158a02bace91b5","src/text.rs":"37b18266ab48b2a3e606da252bac4750193466e47ae840c95486b421600145e4","src/tokio.rs":"6862256c15ec39c86fe72f7a08ad65b327c3668819e4b845617156a2aeda7557","src/util.rs":"f33d933ca2ca4780236fdc9cdf3d4521600ab717a2e696a38bdc77dd229aa61c"},"package":"e0d2bfadce76f963d776feff99db6dc33783829539258314776383b33e2a00f8"} \ No newline at end of file diff --git a/chumsky-0.13.0/.cargo_vcs_info.json b/chumsky-0.13.0/.cargo_vcs_info.json new file mode 100644 index 0000000000..d371936717 --- /dev/null +++ b/chumsky-0.13.0/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "857a0e15ed8f700124fdf2dc8bbcaf829341e8e8" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/chumsky-0.12.0/.github/FUNDING.yml b/chumsky-0.13.0/.github/FUNDING.yml similarity index 100% rename from chumsky-0.12.0/.github/FUNDING.yml rename to chumsky-0.13.0/.github/FUNDING.yml diff --git a/chumsky-0.13.0/.github/workflows/rust.yml b/chumsky-0.13.0/.github/workflows/rust.yml new file mode 100644 index 0000000000..d411983757 --- /dev/null +++ b/chumsky-0.13.0/.github/workflows/rust.yml @@ -0,0 +1,71 @@ +name: Rust + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +env: + CARGO_TERM_COLOR: always + MSRV: 1.65.0 + +permissions: read-all + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Install toolchain + uses: https://github.com/dtolnay/rust-toolchain@stable + with: + toolchain: nightly + components: rustfmt, clippy + - name: Check (no features) + run: cargo check --benches --examples --tests --no-default-features + - name: Check (all features) + run: cargo check --benches --examples --tests --all-features + - name: Clippy (all features) + run: cargo clippy --examples --all-features -- -D warnings + - name: Format + run: cargo fmt --all --check + - name: Docs (all features) + run: cargo doc --features _test_stable,nightly + env: + RUSTDOCFLAGS: --cfg docsrs + + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Install toolchain + uses: https://github.com/dtolnay/rust-toolchain@stable + with: + toolchain: nightly + - name: Tests + run: cargo test --all-features --all-targets + + msrv: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Install toolchain + uses: https://github.com/dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.MSRV }} + - name: MSRV check (all stable features) + run: cargo check --features _test_stable + + semver: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Install toolchain + uses: https://github.com/dtolnay/rust-toolchain@stable + with: + toolchain: stable + - name: Install cargo-binstall + uses: https://github.com/cargo-bins/cargo-binstall@main + - name: Semver compatibility + run: cargo binstall cargo-semver-checks && cargo +stable semver-checks diff --git a/chumsky-0.13.0/CHANGELOG.md b/chumsky-0.13.0/CHANGELOG.md new file mode 100644 index 0000000000..d13315da84 --- /dev/null +++ b/chumsky-0.13.0/CHANGELOG.md @@ -0,0 +1,316 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +# Unreleased + +### Added + +### Removed + +### Changed + +### Fixed + +# [0.13.0] - 2026-05-06 + +### Added + +- `select!` and `select_ref!` now support cfg attributes. +- You can now call `as_terminal` and `as_non_terminal` on labels that will hide the internals for the debug info +- `RichReason` can now be provided with an arbitrary `RichReason::Custom` type parameter +- Implemented `ConfigParser` for `OneOf` +- `Rich::map_span` +- `Parser::map_err_with` +- `Parser::filter_map` + +### Removed + +- Unused `spin` and `sync` feature flags + +### Changed + +- Debug info now uses the `Display` impl to render labels with instead of `Debug` +- Migrated to Codeberg, updated links accordingly +- Removed the unstable `Cache` API due to soundness issues +- Added `DefaultExpected::NothingElse` to allow parsers that can never succeed to generate correct errors +- Removed the `Container` trait in favour of std's `FromIterator`, allowing `.collect()` to work with a greater range of types + +### Fixed + +- `Labelled` -> `LabelledWith` conversion now carries over the proper debug settings +- Fixed crash when using memoisation with certain kinds of error generation +- `DefaultExpected::into_owned` now has more permissive lifetime constraints + +# [0.12.0] - 2025-12-15 + +### Added + +- `MapExtra::emit`, which allows emitting secondary errors during mapping operations +- `InputRef::emit`, which allows emitting secondary errors within `custom` parsers +- `labelled_with`, which avoids the need to implement `Clone` for labels +- `Input::split_token_span`, a convenience function for splitting `(Token, Span)` inputs so that chumsky can understand them +- `Input::split_spanned`, which does the same as above, but for implementors of `WrappingSpan` +- `spanned`, a combinator which automatically annotates a parser output with a span +- Experimental: + - `IterParser::parse_iter`, a way to turn an `IterParser` (like `x.repeated()`) into an iterator + - `Parser::debug`, which provides access to various parser debugging utilities. + +### Changed + +- Made `nested_in` more flexible by allowing it to map between different input types instead of requiring the same input as the outer parser + +### Fixed + +- A prioritisation bug with `nested_in` + +# [0.11.2] - 2025-11-05 + +### Added + +- Implement `Default`, `PartialOrd`, and `Ord` for `SimpleSpan` +- Implement `PartialOrd` and `Ord` for `Rich` + +# [0.11.1] - 2025-09-12 + +### Fixed + +- Patched compilation error that only appeared in release builds + +# [0.11.0] - 2025-09-11 + +### Added + +- The `set(...)` combinator, which may be used to conveniently parse a set of patterns, in any order +- Support for non-associative infix operators are now supported by the `.pratt(...)` combinator +- `Parser::try_foldl`, allowing folding operations to short-circuit in a manner similar to `Parser::try_map` +- `Parser::contextual`, which allows a parser to be disabled or enabled in a context-sensitive manner +- Implemented `ValueInput` for `IterInput`, which was previously missing +- More types that implement the `State` trait: + - `RollbackState`, which reverts parser state when a parser rewinds and may be used to, for example, count the number of times a pattern appears in the input + - `TruncateState`, which truncates a vector when a parser rewinds, and may be used to implement an arena allocator for AST nodes +- Implemented `IterParser` for `a.then(b)` when both `a` and `b` are both `IterParser`s that produce the same output type + +### Changed + +- Made lifetime bounds on `recursive` and `ParserExtra` more permissive +- Improved support for grapheme parsing +- Text parsers now report labels +- `Parser::filter` now generates a `DefaultExpected::SomethingElse` label instead of nothing (this can be overridden with the `.labelled(...)` function) +- Improved areas of documentation +- Make whitespace parsers reject codepoints that are vulnerable to [CVE-2021-42574](https://www.cve.org/CVERecord?id=CVE-2021-42574) +- Maybe the `select!` parser more permissive, accepting any implementor of `Input` instead of requiring `ValueInput` too + +### Fixed + +- Many minor incorrect debug-only sanity checks have been fixed +- Many minor span and error prioritisation behavioural problems have been fixed (most notably, `Parser::try_map`) +- The `.rewind()` parser no longer rewinds any secondary error that were encountered +- Accidental `text::ascii::keyword` lifetime regression + +# [0.10.1] - 2025-04-13 + +### Added + +- Implemented `Container` for `VecDeque` +- New section covering recursion in the guide + +### Changed + +- `Boxed` types now have a default type parameter of `extra::Default`, like `Parser` and `IterParser` +- The tutorial has been updated for `0.10` and has been moved to the guide + +### Fixed + +- Nonsense spans occasionally generated for non-existent tokens +- Improved docs have been added for several items +- Many minor documentation issues have been fixed + +# [0.10.0] - 2025-03-22 + +*Note: version 0.10 is a from-scratch rewrite of chumsky with innumerable small changes. To avoid this changelog being +longer than the compiled works of Douglas Adams, the following is a high-level overview of the major feature additions +and does not include small details.* + +### Added + +- Support for zero-copy parsing (i.e: parser outputs that hold references to the parser input) +- Support for parsing nested inputs like token trees +- Support for parsing context-sensitive grammars such as Python-style indentation, Rust-style raw strings, and much +more +- Support for parsing by graphemes as well as unicode codepoints +- Support for caching parsers independent of the lifetime of the parser +- A new trait, `IterParser`, that allows expressing parsers that generate many outputs +- Added the ability to collect iterable parsers into fixed-size arrays, along with a plethora of other container types +- Support for manipulating shared state during parsing, elegantly allowing support for arena allocators, cstrees, +interners, and much more +- Support for a vast array of new input types: slices, strings, arrays, `impl Read`ers, iterators, etc. +- Experimental support for memoization, allowing chumsky to parse left-recursive grammars and reducing the +computational complexity of parsing certain grammars +- An extension API, allowing third-party crates to extend chumsky's capabilities and introduce new combinators +- A `pratt` parser combinator, allowing for conveniently and simply creating expression parsers with precise operator +precedence +- A `regex` combinator, allowing the parsing of terms based on a specific regex pattern +- Properly differentiated ASCII and Unicode text parsers + +## Removed + +- `Parser::then_with` has been removed in favour of the new context-sensitive combinators + +### Changed + +- Performance has *radically* improved +- Error generation and handling is now significantly more flexible + +# [0.9.2] - 2023-03-02 + +### Fixed + +- Properly fixed `skip_then_retry_until` regression + +# [0.9.1] - 2023-03-02 + +### Fixed + +- Regression in `skip_then_retry_until` recovery strategy + +# [0.9.0] - 2023-02-07 + +### Added + +- A `spill-stack` feature that uses `stacker` to avoid stack overflow errors for deeply recursive parsers +- The ability to access the token span when using `select!` like `select! { |span| Token::Num(x) => (x, span) }` +- Added a `skip_parser` recovery strategy that allows you to implement your own recovery strategies in terms of other + parsers. For example, `.recover_with(skip_parser(take_until(just(';'))))` skips tokens until after the next semicolon +- A `not` combinator that consumes a single token if it is *not* the start of a given pattern. For example, + `just("\\n").or(just('"')).not()` matches any `char` that is not either the final quote of a string, and is not the + start of a newline escape sequence +- A `semantic_indentation` parser for parsing indentation-sensitive languages. Note that this is likely to be + deprecated/removed in the future in favour of a more powerful solution +- `#[must_use]` attribute for parsers to ensure that they're not accidentally created without being used +- `Option>` and `Vec>` now implement `Chain` and `Option` implements `Chain` +- `choice` now supports both arrays and vectors of parsers in addition to tuples +- The `Simple` error type now implements `Eq` + +### Changed + +- `text::whitespace` returns a `Repeated` instead of an `impl Parser`, allowing you to call methods like `at_least` and + `exactly` on it. +- Improved `no_std` support +- Improved examples and documentation +- Use zero-width spans for EoI by default +- Don't allow defining a recursive parser more than once +- Various minor bug fixes +- Improved `Display` implementations for various built-in error types and `SimpleReason` +- Use an `OrderedContainer` trait to avoid unexpected behaviour for unordered containers in combination with `just` + +### Fixed + +- Made several parsers (`todo`, `unwrapped`, etc.) more useful by reporting the parser's location on panic +- Boxing a parser that is already boxed just gives you the original parser to avoid double indirection +- Improved compilation speeds + +# [0.8.0] - 2022-02-07 + +### Added + +- `then_with` combinator to allow limited support for parsing nested patterns +- impl From<&[T; N]> for Stream +- `SkipUntil/SkipThenRetryUntil::skip_start/consume_end` for more precise control over skip-based recovery + +### Changed + +- Allowed `Validate` to map the output type +- Switched to zero-size End Of Input spans for default implementations of `Stream` +- Made `delimited_by` take combinators instead of specific tokens +- Minor optimisations +- Documentation improvements + +### Fixed + +- Compilation error with `--no-default-features` +- Made default behaviour of `skip_until` more sensible + +# [0.7.0] - 2021-12-16 + +### Added + +- A new [tutorial](tutorial.md) to help new users + +- `select` macro, a wrapper over `filter_map` that makes extracting data from specific tokens easy +- `choice` parser, a better alternative to long `or` chains (which sometimes have poor compilation performance) +- `todo` parser, that panics when used (but not when created) (akin to Rust's `todo!` macro, but for parsers) +- `keyword` parser, that parses *exact* identifiers + +- `from_str` combinator to allow converting a pattern to a value inline, using `std::str::FromStr` +- `unwrapped` combinator, to automatically unwrap an output value inline +- `rewind` combinator, that allows reverting the input stream on success. It's most useful when requiring that a + pattern is followed by some terminating pattern without the first parser greedily consuming it +- `map_err_with_span` combinator, to allow fetching the span of the input that was parsed by a parser before an error + was encountered + +- `or_else` combinator, to allow processing and potentially recovering from a parser error +- `SeparatedBy::at_most` to require that a separated pattern appear at most a specific number of times +- `SeparatedBy::exactly` to require that a separated pattern be repeated exactly a specific number of times +- `Repeated::exactly` to require that a pattern be repeated exactly a specific number of times + +- More trait implementations for various things, making the crate more useful + +### Changed + +- Made `just`, `one_of`, and `none_of` significant more useful. They can now accept strings, arrays, slices, vectors, + sets, or just single tokens as before +- Added the return type of each parser to its documentation +- More explicit documentation of parser behaviour +- More doc examples +- Deprecated `seq` (`just` has been generalised and can now be used to parse specific input sequences) +- Sealed the `Character` trait so that future changes are not breaking +- Sealed the `Chain` trait and made it more powerful +- Moved trait constraints on `Parser` to where clauses for improved readability + +### Fixed + +- Fixed a subtle bug that allowed `separated_by` to parse an extra trailing separator when it shouldn't +- Filled a 'hole' in the `Error` trait's API that conflated a lack of expected tokens with expectation of end of input +- Made recursive parsers use weak reference-counting to avoid memory leaks + +# [0.6.0] - 2021-11-22 + +### Added + +- `skip_until` error recovery strategy +- `SeparatedBy::at_least` and `SeparatedBy::at_most` for parsing a specific number of separated items +- `Parser::validate` for integrated AST validation +- `Recursive::declare` and `Recursive::define` for more precise control over recursive declarations + +### Changed + +- Improved `separated_by` error messages +- Improved documentation +- Hid a new (probably) unused implementation details + +# [0.5.0] - 2021-10-30 + +### Added + +- `take_until` primitive + +### Changed + +- Added span to fallback output function in `nested_delimiters` + +# [0.4.0] - 2021-10-28 + +### Added + +- Support for LL(k) parsing +- Custom error recovery strategies +- Debug mode +- Nested input flattening + +### Changed + +- Radically improved error quality diff --git a/chumsky-0.13.0/CONTRIBUTING.md b/chumsky-0.13.0/CONTRIBUTING.md new file mode 100644 index 0000000000..cb3ed287e6 --- /dev/null +++ b/chumsky-0.13.0/CONTRIBUTING.md @@ -0,0 +1,31 @@ +# Contributing to Chumsky + +We expect contributors to adhere to the ethos of the project: + +Source code is not an artifact, an intermediate representation, nor a bothersome annoyance whose creation is to be +offloaded to metal and transistors. Source code is a **source of truth** - the only source of truth that constitutes this +software project - and it deserves to be understood and curated by the *accountable* and *reasoned* mind of a human being. + +We do not expect perfection, but we do expect you to personally understand your own motivations and decisions. + +## Code of conduct + +Chumsky follows the [Rust CoC](https://www.rust-lang.org/policies/code-of-conduct). + +## License + +By contributing to the project, you agree to publish your code under the license terms laid out in `LICENSE`. + +Although the project has no immediate plans to relicense, there is the possibility that the project may transition to a less +permissive license (such as a member of the GPL family) in the future. + +## Use of Large Language Models ('LLMs' or 'AI') + +We do not allow contributions that have been authored, completely or in part, by large language models. + +Contributors that feel the need to use such models must use them in 'read-only' mode (i.e: as a means of explaining the function of +the codebase). You are expected to fully check the model's reasoning before taking it at its word. + +We expect both code contributors and issue reporters to understand the meaning of their contributions and reports in a human +capacity, and to be able to vouch for and justify their work. Attempting to circumvent the spirit of this principle is not +permitted. diff --git a/chumsky-0.13.0/Cargo.lock b/chumsky-0.13.0/Cargo.lock new file mode 100644 index 0000000000..598a9232a8 --- /dev/null +++ b/chumsky-0.13.0/Cargo.lock @@ -0,0 +1,2044 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "ar_archive_writer" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c269894b6fe5e9d7ada0cf69b5bf847ff35bc25fc271f08e1d080fce80339a" +dependencies = [ + "object 0.32.2", +] + +[[package]] +name = "ariadne" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f5e3dca4e09a6f340a61a0e9c7b61e030c69fc27bf29d73218f7e5e3b7638f" +dependencies = [ + "unicode-width 0.1.14", + "yansi", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object 0.37.3", + "rustc-demangle", + "windows-link", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "bytemuck" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chumsky" +version = "0.13.0" +dependencies = [ + "ariadne", + "bytes", + "ciborium", + "criterion", + "either", + "hashbrown 0.15.5", + "lasso", + "lexical", + "logos", + "nom 7.1.3", + "nom 8.0.0", + "pest", + "pest_derive", + "pom", + "pprof", + "railroad", + "regex-automata", + "serde", + "serde_json", + "slotmap", + "sn", + "stacker", + "unicode-ident", + "unicode-segmentation", + "winnow", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "bitflags 1.3.2", + "clap_lex", + "indexmap 1.9.3", + "textwrap", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "core_maths" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" +dependencies = [ + "libm", +] + +[[package]] +name = "cpp_demangle" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2bb79cb74d735044c972aae58ed0aaa9a837e85b01106a54c39e42e97f62253" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "criterion" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" +dependencies = [ + "anes", + "atty", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "data-url" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" + +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "uuid", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "euclid" +version = "0.22.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48" +dependencies = [ + "num-traits", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" + +[[package]] +name = "findshlibs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" +dependencies = [ + "cc", + "lazy_static", + "libc", + "winapi", +] + +[[package]] +name = "flate2" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "fontconfig-parser" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" +dependencies = [ + "roxmltree", +] + +[[package]] +name = "fontdb" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2 0.9.9", + "slotmap", + "tinyvec", + "ttf-parser", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "gif" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "image-webp" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imagesize" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +dependencies = [ + "equivalent", + "hashbrown 0.16.0", +] + +[[package]] +name = "inferno" +version = "0.11.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "232929e1d75fe899576a3d5c7416ad0d88dbfbb3c3d6aa00873a7408a50ddb88" +dependencies = [ + "ahash", + "indexmap 2.12.0", + "is-terminal", + "itoa", + "log", + "num-format", + "once_cell", + "quick-xml", + "rgb", + "str_stack", +] + +[[package]] +name = "is-terminal" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" +dependencies = [ + "hermit-abi 0.5.2", + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "kurbo" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62" +dependencies = [ + "arrayvec", + "euclid", + "smallvec", +] + +[[package]] +name = "lasso" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e14eda50a3494b3bf7b9ce51c52434a761e383d7238ce1dd5dcec2fbc13e9fb" +dependencies = [ + "hashbrown 0.14.5", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lexical" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7aefb36fd43fef7003334742cbf77b243fcd36418a1d1bdd480d613a67968f6" +dependencies = [ + "lexical-core", +] + +[[package]] +name = "lexical-core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46" +dependencies = [ + "lexical-parse-float", + "lexical-parse-integer", + "lexical-util", + "lexical-write-float", + "lexical-write-integer", +] + +[[package]] +name = "lexical-parse-float" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f" +dependencies = [ + "lexical-parse-integer", + "lexical-util", + "static_assertions", +] + +[[package]] +name = "lexical-parse-integer" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9" +dependencies = [ + "lexical-util", + "static_assertions", +] + +[[package]] +name = "lexical-util" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc" +dependencies = [ + "static_assertions", +] + +[[package]] +name = "lexical-write-float" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accabaa1c4581f05a3923d1b4cfd124c329352288b7b9da09e766b0668116862" +dependencies = [ + "lexical-util", + "lexical-write-integer", + "static_assertions", +] + +[[package]] +name = "lexical-write-integer" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b6f3d1f4422866b68192d62f77bc5c700bee84f3069f2469d7bc8c77852446" +dependencies = [ + "lexical-util", + "static_assertions", +] + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "logos" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c000ca4d908ff18ac99b93a062cb8958d331c3220719c52e77cb19cc6ac5d2c1" +dependencies = [ + "logos-derive", +] + +[[package]] +name = "logos-codegen" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc487311295e0002e452025d6b580b77bb17286de87b57138f3b5db711cded68" +dependencies = [ + "beef", + "fnv", + "proc-macro2", + "quote", + "regex-syntax 0.6.29", + "syn", +] + +[[package]] +name = "logos-derive" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbfc0d229f1f42d790440136d941afd806bc9e949e2bcb8faa813b0f00d1267e" +dependencies = [ + "logos-codegen", +] + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memmap2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" +dependencies = [ + "libc", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec", + "itoa", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pest" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "989e7521a040efde50c3ab6bbadafbe15ab6dc042686926be59ac35d74607df4" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "187da9a3030dbafabbbfb20cb323b976dc7b7ce91fcd84f2f74d6e31d378e2de" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49b401d98f5757ebe97a26085998d6c0eecec4995cad6ab7fc30ffdf4b052843" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f27a2cfee9f9039c4d86faa5af122a0ac3851441a34865b8a043b46be0065a" +dependencies = [ + "pest", + "sha2", +] + +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "pom" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c972d8f86e943ad532d0b04e8965a749ad1d18bb981a9c7b3ae72fe7fd7744b" +dependencies = [ + "bstr", +] + +[[package]] +name = "pprof" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196ded5d4be535690899a4631cc9f18cdc41b7ebf24a79400f46f48e49a11059" +dependencies = [ + "backtrace", + "cfg-if", + "criterion", + "findshlibs", + "inferno", + "libc", + "log", + "nix", + "once_cell", + "parking_lot", + "smallvec", + "symbolic-demangle", + "tempfile", + "thiserror", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psm" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d11f2fedc3b7dafdc2851bc52f277377c5473d378859be234bc7ebb593144d01" +dependencies = [ + "ar_archive_writer", + "cc", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quick-xml" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "railroad" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5b8e8a7c20c600f9b98cbf46b64e63d5c9e69deb98cee1ff264de9f1dda5d" +dependencies = [ + "resvg", + "unicode-width 0.2.2", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.10.0", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax 0.8.8", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.8", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "resvg" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8928798c0a55e03c9ca6c4c6846f76377427d2c1e1f7e6de3c06ae57942df43" +dependencies = [ + "gif", + "image-webp", + "log", + "pico-args", + "rgb", + "svgtypes", + "tiny-skia", + "usvg", + "zune-jpeg", +] + +[[package]] +name = "rgb" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + +[[package]] +name = "rustc-demangle" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" + +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags 2.10.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "rustybuzz" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" +dependencies = [ + "bitflags 2.10.0", + "bytemuck", + "core_maths", + "log", + "smallvec", + "ttf-parser", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "indexmap 2.12.0", + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simplecss" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" +dependencies = [ + "log", +] + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "sn" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fab6ae84ee9505d8786b19c3a1c68e70431d8465a835d4c59dd4a843884647b" + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "stacker" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1f8b29fb42aafcea4edeeb6b2f2d7ecd0d969c48b4cf0d2e64aafc471dd6e59" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.59.0", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "str_stack" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +dependencies = [ + "float-cmp", +] + +[[package]] +name = "svgtypes" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" +dependencies = [ + "kurbo", + "siphasher", +] + +[[package]] +name = "symbolic-common" +version = "10.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b55cdc318ede251d0957f07afe5fed912119b8c1bc5a7804151826db999e737" +dependencies = [ + "debugid", + "memmap2 0.5.10", + "stable_deref_trait", + "uuid", +] + +[[package]] +name = "symbolic-demangle" +version = "10.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79be897be8a483a81fff6a3a4e195b4ac838ef73ca42d348b3f722da9902e489" +dependencies = [ + "cpp_demangle", + "rustc-demangle", + "symbolic-common", +] + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "textwrap" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "png", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "ttf-parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" +dependencies = [ + "core_maths", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + +[[package]] +name = "unicode-bidi-mirroring" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe" + +[[package]] +name = "unicode-ccc" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + +[[package]] +name = "unicode-script" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb421b350c9aff471779e262955939f565ec18b86c15364e6bdf0d662ca7c1f" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-vo" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "usvg" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80be9b06fbae3b8b303400ab20778c80bbaf338f563afe567cf3c9eea17b47ef" +dependencies = [ + "base64", + "data-url", + "flate2", + "fontdb", + "imagesize", + "kurbo", + "log", + "pico-args", + "roxmltree", + "rustybuzz", + "simplecss", + "siphasher", + "strict-num", + "svgtypes", + "tiny-skia-path", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "xmlwriter", +] + +[[package]] +name = "uuid" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "weezl" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-jpeg" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" +dependencies = [ + "zune-core", +] diff --git a/chumsky-0.13.0/Cargo.toml b/chumsky-0.13.0/Cargo.toml new file mode 100644 index 0000000000..9db5b55681 --- /dev/null +++ b/chumsky-0.13.0/Cargo.toml @@ -0,0 +1,303 @@ +# 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 are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +rust-version = "1.65" +name = "chumsky" +version = "0.13.0" +authors = [ + "Joshua Barretto ", + "Elijah Hartvigsen ", +] +build = false +exclude = [ + "/misc/*", + "/benches/samples/*", +] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "A parser library for humans with powerful error recovery" +readme = "README.md" +keywords = [ + "parser", + "combinator", + "token", + "language", + "syntax", +] +categories = [ + "parsing", + "text-processing", +] +license = "MIT" +repository = "https://codeberg.org/zesterer/chumsky" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = [ + "--cfg", + "docsrs", +] + +[features] +_test_stable = [ + "std", + "stacker", + "memoization", + "extension", + "pratt", + "either", + "regex", + "serde", + "bytes", +] +bytes = ["dep:bytes"] +debug = [ + "unstable", + "nightly", + "dep:railroad", +] +default = [ + "std", + "stacker", +] +docsrs = [] +either = ["dep:either"] +extension = [] +lexical-numbers = [ + "lexical", + "unstable", +] +memoization = [] +nightly = [] +pratt = [] +regex = ["dep:regex-automata"] +serde = ["dep:serde"] +stacker = [ + "dep:stacker", + "std", +] +std = [ + "regex-automata?/std", + "serde?/std", +] +unstable = [] + +[lib] +name = "chumsky" +path = "src/lib.rs" + +[[example]] +name = "brainfuck" +path = "examples/brainfuck.rs" + +[[example]] +name = "debug" +path = "examples/debug.rs" +required-features = ["debug"] + +[[example]] +name = "foo" +path = "examples/foo.rs" +required-features = ["std"] + +[[example]] +name = "indent" +path = "examples/indent.rs" + +[[example]] +name = "io" +path = "examples/io.rs" +required-features = ["std"] + +[[example]] +name = "json" +path = "examples/json.rs" +required-features = ["std"] + +[[example]] +name = "json_fast" +path = "examples/json_fast.rs" +required-features = ["std"] + +[[example]] +name = "logos" +path = "examples/logos.rs" + +[[example]] +name = "mini_ml" +path = "examples/mini_ml.rs" +required-features = ["pratt"] + +[[example]] +name = "nano_rust" +path = "examples/nano_rust.rs" + +[[example]] +name = "nested" +path = "examples/nested.rs" + +[[example]] +name = "nested_spans" +path = "examples/nested_spans.rs" + +[[example]] +name = "pythonic" +path = "examples/pythonic.rs" + +[[example]] +name = "zero-copy" +path = "examples/zero-copy.rs" + +[[bench]] +name = "backtrack" +path = "benches/backtrack.rs" +harness = false + +[[bench]] +name = "cbor" +path = "benches/cbor.rs" +harness = false + +[[bench]] +name = "json" +path = "benches/json.rs" +harness = false +required-features = ["std"] + +[[bench]] +name = "lex" +path = "benches/lex.rs" +harness = false + +[[bench]] +name = "parser" +path = "benches/parser.rs" +harness = false + +[[bench]] +name = "utils" +path = "benches/utils.rs" + +[dependencies.bytes] +version = "1" +optional = true +default-features = false + +[dependencies.either] +version = "1.8.1" +optional = true + +[dependencies.hashbrown] +version = "0.15" + +[dependencies.lexical] +version = "6.1.1" +features = [ + "parse-integers", + "parse-floats", + "format", +] +optional = true +default-features = false + +[dependencies.railroad] +version = "0.3.3" +optional = true + +[dependencies.regex-automata] +version = "0.4" +features = [ + "alloc", + "meta", + "perf", + "unicode", + "nfa", + "dfa", + "hybrid", +] +optional = true +default-features = false + +[dependencies.serde] +version = "1.0" +features = ["derive"] +optional = true +default-features = false + +[dependencies.stacker] +version = "0.1" +optional = true + +[dependencies.unicode-ident] +version = "1.0.10" + +[dependencies.unicode-segmentation] +version = "1" + +[dev-dependencies.ariadne] +version = "0.5" + +[dev-dependencies.ciborium] +version = "0.2" + +[dev-dependencies.criterion] +version = "0.4.0" + +[dev-dependencies.lasso] +version = "0.7" + +[dev-dependencies.logos] +version = "0.13" + +[dev-dependencies.nom] +version = "7.1" + +[dev-dependencies.nom8] +version = "8" +package = "nom" + +[dev-dependencies.pest] +version = "2.5" + +[dev-dependencies.pest_derive] +version = "2.5" + +[dev-dependencies.pom] +version = "3.2" + +[dev-dependencies.serde_json] +version = "1.0" +features = ["preserve_order"] + +[dev-dependencies.slotmap] +version = "1.0" + +[dev-dependencies.sn] +version = "0.1" + +[dev-dependencies.winnow] +version = "0.7.0" + +[target."cfg(unix)".dev-dependencies.pprof] +version = "0.11" +features = [ + "flamegraph", + "criterion", +] + +[profile.bench] +debug = 2 diff --git a/chumsky-0.13.0/Cargo.toml.orig b/chumsky-0.13.0/Cargo.toml.orig new file mode 100644 index 0000000000..ddb1908b32 --- /dev/null +++ b/chumsky-0.13.0/Cargo.toml.orig @@ -0,0 +1,155 @@ +[package] +name = "chumsky" +version = "0.13.0" +description = "A parser library for humans with powerful error recovery" +authors = ["Joshua Barretto ", "Elijah Hartvigsen "] +repository = "https://codeberg.org/zesterer/chumsky" +license = "MIT" +keywords = ["parser", "combinator", "token", "language", "syntax"] +categories = ["parsing", "text-processing"] +edition = "2021" +exclude = [ + "/misc/*", + "/benches/samples/*", +] +rust-version = "1.65" + +[features] +default = ["std", "stacker"] + +# Integrate with the standard library. +std = [ + "regex-automata?/std", + "serde?/std" +] + +# Enable nightly-only features like better compiler diagnostics and a Parser impl for ! (the never type). +nightly = [] + +# Allows deeper recursion by dynamically spilling stack state on to the heap. +stacker = ["dep:stacker", "std"] + +# Allows parser memoization, speeding up heavily back-tracking parsers and allowing left recursion. +memoization = [] + +# Allows extending chumsky by writing your own parser implementations. +extension = [] + +# Enable Pratt parsing combinator +pratt = [] + +# Utilities for debugging parsers +debug = ["unstable", "nightly", "dep:railroad"] + +# Allow the use of unstable features (aka features where the API is not settled) +unstable = [] + +# Allows use of the `Number` parser, which is backed by the `lexical` crate +lexical-numbers = ["lexical", "unstable"] + +# Adds impl of Parser for either::Either +either = ["dep:either"] + +# Enables regex combinators +regex = ["dep:regex-automata"] + +# Enable serde serialization support +serde = ["dep:serde"] + +# Enable support for using Tokio's byte slices as inputs +bytes = ["dep:bytes"] + +# Enable dependencies only needed for generation of documentation on docs.rs +docsrs = [] + +# An alias of all features that work with the stable compiler. +# Do not use this feature, its removal is not considered a breaking change and its behaviour may change. +# If you're working on chumsky, and you're adding a feature that does not require nightly support, +# please add it to this list. +_test_stable = ["std", "stacker", "memoization", "extension", "pratt", "either", "regex", "serde", "bytes"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[dependencies] +hashbrown = "0.15" +stacker = { version = "0.1", optional = true } +regex-automata = { version = "0.4", default-features = false, optional = true, features = ["alloc", "meta", "perf", "unicode", "nfa", "dfa", "hybrid"] } +lexical = { version = "6.1.1", default-features = false, features = ["parse-integers", "parse-floats", "format"], optional = true } +either = { version = "1.8.1", optional = true } +serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] } +unicode-ident = "1.0.10" +unicode-segmentation = "1" +bytes = { version = "1", default-features = false, optional = true } +railroad = { version = "0.3.3", optional = true } + +[dev-dependencies] +ariadne = "0.5" +pom = "3.2" +nom = "7.1" +nom8 = { package = "nom", version = "8"} +winnow = "0.7.0" +serde_json = { version = "1.0", features = ["preserve_order"] } +ciborium = { version = "0.2" } +criterion = "0.4.0" +pest = "2.5" +pest_derive = "2.5" +sn = "0.1" +logos = "0.13" +lasso = "0.7" +slotmap = "1.0" + +[target.'cfg(unix)'.dev-dependencies] +pprof = { version = "0.11", features = ["flamegraph", "criterion"] } + +[profile.bench] +debug = true + +[[bench]] +name = "json" +harness = false +required-features = ["std"] + +[[bench]] +name = "lex" +harness = false + +[[bench]] +name = "parser" +harness = false + +[[bench]] +name = "backtrack" +harness = false + +[[bench]] +name = "cbor" +harness = false + +[[example]] +name = "nano_rust" + +[[example]] +name = "json" +required-features = ["std"] + +[[example]] +name = "json_fast" +required-features = ["std"] + +[[example]] +name = "io" +required-features = ["std"] + +[[example]] +name = "foo" +required-features = ["std"] + +[[example]] +name = "mini_ml" +required-features = ["pratt"] + +[[example]] +name = "debug" +required-features = ["debug"] diff --git a/chumsky-0.12.0/LICENSE b/chumsky-0.13.0/LICENSE similarity index 100% rename from chumsky-0.12.0/LICENSE rename to chumsky-0.13.0/LICENSE diff --git a/chumsky-0.13.0/README.md b/chumsky-0.13.0/README.md new file mode 100644 index 0000000000..6c96d78ea7 --- /dev/null +++ b/chumsky-0.13.0/README.md @@ -0,0 +1,217 @@ +[![crates.io](https://img.shields.io/crates/v/chumsky.svg)](https://crates.io/crates/chumsky) +[![License](https://img.shields.io/crates/l/chumsky.svg)](https://codeberg.org/zesterer/chumsky) +[![crates.io](https://docs.rs/chumsky/badge.svg)](https://docs.rs/chumsky) +![actions-badge](https://codeberg.org/zesterer/chumsky/badges/workflows/rust.yml/badge.svg?branch=main) + +Chumsky is a parser library for Rust that makes writing expressive, high-performance parsers easy. + +

+ Example usage with my own language, Tao +

+ +*Note: Error diagnostic rendering in this example is performed by [Ariadne](https://codeberg.org/zesterer/ariadne)* + +Although chumsky is designed primarily for user-facing parsers such as compilers, chumsky is just as much at home +parsing binary protocols at the networking layer, configuration files, or any other form of complex input validation +that you may need. It also has `no_std` support, making it suitable for embedded environments. + +## Features + +- 🪄 **Expressive combinators** that make writing your parser a joy +- ðŸŽ›ï¸ **Fully generic** across input, token, output, span, and error types +- 📑 **Zero-copy parsing** minimises allocation by having outputs hold references/slices of the input +- 🚦 **Flexible error recovery** strategies out of the box +- â˜‘ï¸ **Check-only mode** for fast verification of inputs, automatically supported +- 🚀 **Internal optimiser** leverages the power of [GATs](https://smallcultfollowing.com/babysteps/blog/2022/06/27/many-modes-a-gats-pattern/) to optimise your parser for you +- 📖 **Text-oriented parsers** for text inputs (i.e: `&[u8]` and `&str`) +- ðŸ‘ï¸â€ðŸ—¨ï¸ **Context-free grammars** are fully supported, with support for context-sensitivity +- 🔄 **Left recursion and memoization** have opt-in support +- 🪺 **Nested inputs** such as token trees are fully supported both as inputs and outputs +- ðŸ·ï¸ **Pattern labelling** for dynamic, user-friendly error messages +- ðŸ—ƒï¸ **Caching** allows parsers to be created once and reused many times +- â†”ï¸ **Pratt parsing** support for simple yet flexible expression parsing +- 🪛 **no_std** support, allowing chumsky to run in embedded environments +- 🔬**Debugging** utilities, including automatic generation of parser [railroad diagrams](https://en.wikipedia.org/wiki/Syntax_diagram) + +## Example + +See [`examples/brainfuck.rs`](https://codeberg.org/zesterer/chumsky/src/branch/main/examples/brainfuck.rs) for a full +[Brainfuck](https://en.wikipedia.org/wiki/Brainfuck) interpreter +(`cargo run --example brainfuck -- examples/sample.bf`). + +```rust,ignore +use chumsky::prelude::*; + +/// An AST (Abstract Syntax Tree) for Brainfuck instructions +#[derive(Clone)] +enum Instr { + Left, Right, + Incr, Decr, + Read, Write, + Loop(Vec), // In Brainfuck, `[...]` loop instructions contain any number of instructions +} + +/// A function that generates a Brainfuck parser +fn brainfuck<'a>() -> impl Parser<'a, &'a str, Vec> { + // Brainfuck syntax is recursive: each instruction can contain many sub-instructions (via `[...]` loops) + recursive(|bf| choice(( + // All of the basic instructions are just single characters + just('<').to(Instr::Left), + just('>').to(Instr::Right), + just('+').to(Instr::Incr), + just('-').to(Instr::Decr), + just(',').to(Instr::Read), + just('.').to(Instr::Write), + // Loops are strings of Brainfuck instructions, delimited by square brackets + bf.delimited_by(just('['), just(']')).map(Instr::Loop), + )) + // Brainfuck instructions appear sequentially, so parse as many as we need + .repeated() + .collect()) +} + +// Parse some Brainfuck with our parser +brainfuck().parse("--[>--->->->++>-<<<<<-------]>--.>---------.>--..+++.>----.>+++++++++.<<.+++.------.<-.>>+.") +``` + +You can find more examples [here](https://codeberg.org/zesterer/chumsky/src/branch/main/examples). + +## Guide and documentation + +Chumsky has an extensive [guide](https://docs.rs/chumsky/latest/chumsky/guide) that walks you through the library: all +the way from setting up and basic theory to advanced uses of the crate. It includes technical details of chumsky's +behaviour, examples of uses, a handy index for all of the combinators, technical details about the crate, and even a +tutorial that leads you through the development of a fully-functioning interpreter for a simple programming language. + +The crate docs should also be similarly useful: most important functions include at least one contextually-relevant +example, and all crate items are fully documented. + +In addition, chumsky comes with a suite of fully-fledged +[example projects](https://codeberg.org/zesterer/chumsky/src/branch/main/examples). These include: + +- Parsers for existing syntaxes like Brainfuck and JSON +- Integration demos for third-party crates, like [`logos`](https://crates.io/crates/logos) +- Parsers for new toy programming languages: a Rust-like language and a full-on lexer, parser, type-checker, and + interpreter for a minature ML-like language. +- Examples of parsing non-trivial inputs like token trees, `impl Read`ers, and zero-copy, zero-alloc parsing. + +## Cargo features + +Chumsky contains several optional features that extend the crate's functionality. + +- `bytes`: adds support for parsing types from the [`bytes`](https://docs.rs/bytes/) crate. + +- `either`: implements `Parser` for `either::Either`, allowing dynamic configuration of parsers at run-time + +- `extension`: enables the extension API, allowing you to write your own first-class combinators that integrate with + and extend chumsky + +- `lexical-numbers`: Enables use of the `Number` parser for parsing various numeric formats + +- `memoization`: enables [memoization](https://en.wikipedia.org/wiki/Memoization#Parsers) features + +- `nightly`: enable support for features only supported by the nightly Rust compiler + +- `pratt`: enables the [pratt parsing](https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html) + combinator + +- `regex`: enables the regex combinator + +- `serde`: enables `serde` (de)serialization support for several types + +- `stacker` (enabled by default): avoid stack overflows by spilling stack data to the heap via the `stacker` crate + +- `std` (enabled by default): support for standard library features + +- `unstable`: enables experimental chumsky features (API features enabled by `unstable` are NOT considered to fall + under the semver guarantees of chumsky!) + +## *What* is a parser combinator? + +Parser combinators are a technique for implementing parsers by defining them in terms of other parsers. The resulting +parsers use a [recursive descent](https://en.wikipedia.org/wiki/Recursive_descent_parser) strategy to transform a stream +of tokens into an output. Using parser combinators to define parsers is roughly analogous to using Rust's +[`Iterator`](https://doc.rust-lang.org/std/iter/trait.Iterator.html) trait to define iterative algorithms: the +type-driven API of `Iterator` makes it more difficult to make mistakes and easier to encode complicated iteration logic +than if one were to write the same code by hand. The same is true of parser combinators. + +## *Why* use parser combinators? + +Writing parsers with good error recovery is conceptually difficult and time-consuming. It requires understanding the +intricacies of the recursive descent algorithm, and then implementing recovery strategies on top of it. If you're +developing a programming language, you'll almost certainly change your mind about syntax in the process, leading to some +slow and painful parser refactoring. Parser combinators solve both problems by providing an ergonomic API that allows +for rapidly iterating upon a syntax. + +Parser combinators are also a great fit for domain-specific languages for which an existing parser does not exist. +Writing a reliable, fault-tolerant parser for such situations can go from being a multi-day task to a half-hour task +with the help of a decent parser combinator library. + +## Classification + +Chumsky's parsers are [recursive descent](https://en.wikipedia.org/wiki/Recursive_descent_parser) parsers and are +capable of parsing [parsing expression grammars (PEGs)](https://en.wikipedia.org/wiki/Parsing_expression_grammar), which +includes all known context-free languages. However, chumsky doesn't stop there: it also supports context-sensitive +grammars via a set of dedicated combinators that integrate cleanly with the rest of the library. This allows it to +additionally parse a number of context-sensitive syntaxes like Rust-style raw strings, Python-style semantic +indentation, and much more. + +## Error recovery + +Chumsky has support for error recovery, meaning that it can encounter a syntax error, report the error, and then +attempt to recover itself into a state in which it can continue parsing so that multiple errors can be produced at once +and a partial [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree) can still be generated from the input for future +compilation stages to consume. + +## Performance + +Chumsky allows you to choose your priorities. When needed, it can be configured for high-quality parser errors. It can +also be configured for *performance*. + +It's difficult to produce general benchmark results for parser libraries. By their nature, the performance of a parser +is intimately tied to exactly how the grammar they implement has been specified. That said, here are some numbers for a +fairly routine JSON parsing benchmark implemented idiomatically in various libraries. As you can see, chumsky ranks +quite well! + +| Ranking | Library | Time (smaller is better) | Throughput | +|---------|------------------------------------------------------|--------------------------|------------| +| 1 | `chumsky` (check-only) | 140.77 µs | 797 MB/s | +| 2 | [`winnow`](https://github.com/winnow-rs/winnow) | 178.91 µs | 627 MB/s | +| 3 | `chumsky` | 210.43 µs | 533 MB/s | +| 4 | [`sn`](https://github.com/Jacherr/sn) (hand-written) | 237.94 µs | 472 MB/s | +| 5 | [`serde_json`](https://github.com/serde-rs/json) | 477.41 µs | 235 MB/s | +| 6 | [`nom`](https://github.com/rust-bakery/nom) | 526.52 µs | 213 MB/s | +| 7 | [`pest`](https://github.com/pest-parser/pest) | 1.9706 ms | 57 MB/s | +| 8 | [`pom`](https://github.com/J-F-Liu/pom) | 13.730 ms | 8 MB/s | + +What should you take from this? It's difficult to say. 'Chumsky is faster than X' or 'chumsky is slower than Y' is too +strong a statement: this is just one particular benchmark with one particular set of implementations and one +particular workload. + +That said, there is something you can take: chumsky isn't going to be your bottleneck. In this benchmark, chumsky is +within 20% of the performance of the 'pack leader' and has performance comparable to a hand-written parser. The +performance standards for Rust libraries are already far above most language ecosystems, so you can be sure that +chumsky will keep pace with your use-case. + +Benchmarks were performed on a single core of an AMD Ryzen 7 3700x. + +## Notes + +My apologies to Noam for choosing such an absurd name. + +## License + +Chumsky is licensed under the MIT license (see `LICENSE` in the main repository). + +## Provenance + +This software is proudly and fondly written, maintained, used - and most crucially - **understood** by real human beings. +While we can't personally attest to the provenance of every line of code ever contributed, the vast majority of the +codebase has certainly been developed without the aid of large language models and other stochastic 'intelligence'. + +While the license may not guarantee warranty 'of any kind', you can at least use this software in the comforting knowledge +that its veracity and coherence is vouched for by sentient intelligence with skin in the game and a reputation to uphold. + +## Contribution guidelines + +See [`CONTRIBUTING.md`](CONTRIBUTING.md). diff --git a/chumsky-0.12.0/benches/backtrack.rs b/chumsky-0.13.0/benches/backtrack.rs similarity index 100% rename from chumsky-0.12.0/benches/backtrack.rs rename to chumsky-0.13.0/benches/backtrack.rs diff --git a/chumsky-0.12.0/benches/cbor.rs b/chumsky-0.13.0/benches/cbor.rs similarity index 100% rename from chumsky-0.12.0/benches/cbor.rs rename to chumsky-0.13.0/benches/cbor.rs diff --git a/chumsky-0.12.0/benches/json.pest b/chumsky-0.13.0/benches/json.pest similarity index 100% rename from chumsky-0.12.0/benches/json.pest rename to chumsky-0.13.0/benches/json.pest diff --git a/chumsky-0.12.0/benches/json.rs b/chumsky-0.13.0/benches/json.rs similarity index 100% rename from chumsky-0.12.0/benches/json.rs rename to chumsky-0.13.0/benches/json.rs diff --git a/chumsky-0.12.0/benches/lex.rs b/chumsky-0.13.0/benches/lex.rs similarity index 100% rename from chumsky-0.12.0/benches/lex.rs rename to chumsky-0.13.0/benches/lex.rs diff --git a/chumsky-0.12.0/benches/parser.rs b/chumsky-0.13.0/benches/parser.rs similarity index 100% rename from chumsky-0.12.0/benches/parser.rs rename to chumsky-0.13.0/benches/parser.rs diff --git a/chumsky-0.12.0/benches/tokens.txt b/chumsky-0.13.0/benches/tokens.txt similarity index 100% rename from chumsky-0.12.0/benches/tokens.txt rename to chumsky-0.13.0/benches/tokens.txt diff --git a/chumsky-0.12.0/benches/utils.rs b/chumsky-0.13.0/benches/utils.rs similarity index 100% rename from chumsky-0.12.0/benches/utils.rs rename to chumsky-0.13.0/benches/utils.rs diff --git a/chumsky-0.12.0/examples/brainfuck.rs b/chumsky-0.13.0/examples/brainfuck.rs similarity index 100% rename from chumsky-0.12.0/examples/brainfuck.rs rename to chumsky-0.13.0/examples/brainfuck.rs diff --git a/chumsky-0.13.0/examples/debug.rs b/chumsky-0.13.0/examples/debug.rs new file mode 100644 index 0000000000..6267e3855e --- /dev/null +++ b/chumsky-0.13.0/examples/debug.rs @@ -0,0 +1,104 @@ +//! This is a Brainfuck parser and interpreter +//! Run it with the following command: +//! cargo run --features="debug" --example debug + +use chumsky::prelude::*; +use std::collections::HashMap; + +#[derive(Clone, Debug)] +pub enum Json { + Null, + Bool(bool), + Str(String), + Num(f64), + Array(Vec), + Object(HashMap), +} + +fn json<'a>() -> impl Parser<'a, &'a str, Json> { + recursive(|value| { + let digits = text::digits(10).to_slice(); + + let frac = just('.').then(digits); + + let exp = just('e') + .or(just('E')) + .then(one_of("+-").or_not()) + .then(digits); + + let number = just('-') + .or_not() + .then(text::int(10)) + .then(frac.or_not()) + .then(exp.or_not()) + .to_slice() + .map(|s: &str| s.parse().unwrap()); + + let escape = just('\\') + .then(choice(( + just('\\'), + just('/'), + just('"'), + just('b').to('\x08'), + just('f').to('\x0C'), + just('n').to('\n'), + just('r').to('\r'), + just('t').to('\t'), + just('u').ignore_then(text::digits(16).exactly(4).to_slice().validate( + |digits, _, emitter| { + char::from_u32(u32::from_str_radix(digits, 16).unwrap()).unwrap_or_else( + || { + emitter.emit(Default::default()); + '\u{FFFD}' // unicode replacement character + }, + ) + }, + )), + ))) + .ignored(); + + let string = none_of("\\\"") + .ignored() + .or(escape) + .repeated() + .to_slice() + .map(ToString::to_string) + .delimited_by(just('"'), just('"')); + + let array = value + .clone() + .separated_by(just(',').padded()) + .allow_trailing() + .collect() + .padded() + .delimited_by(just('['), just(']')); + + let member = string.then_ignore(just(':').padded()).then(value); + let object = member + .clone() + .separated_by(just(',').padded()) + .collect() + .padded() + .delimited_by(just('{'), just('}')); + + choice(( + just("null").to(Json::Null), + just("true").to(Json::Bool(true)), + just("false").to(Json::Bool(false)), + number.map(Json::Num).labelled("number"), + string.map(Json::Str).labelled("string"), + array.map(Json::Array).labelled("array"), + object.map(Json::Object).labelled("object"), + )) + .padded() + .labelled("JSON value") + }) +} + +fn main() { + // Generate an eBNF grammar for the parser + println!("{}", json().debug().to_ebnf()); + + // Generate a railroad diagram for the parser + println!("{}", json().debug().to_railroad_svg()); +} diff --git a/chumsky-0.12.0/examples/foo.rs b/chumsky-0.13.0/examples/foo.rs similarity index 100% rename from chumsky-0.12.0/examples/foo.rs rename to chumsky-0.13.0/examples/foo.rs diff --git a/chumsky-0.13.0/examples/indent.rs b/chumsky-0.13.0/examples/indent.rs new file mode 100644 index 0000000000..b4fc43bb36 --- /dev/null +++ b/chumsky-0.13.0/examples/indent.rs @@ -0,0 +1,116 @@ +use ariadne::{Color, Label, Report, ReportKind, Source}; +use chumsky::{extra::Full, prelude::*, text}; + +type Extras<'a> = Full, (), &'a str>; + +/// Parse any amount of indentation at the start of a line, ignoring preding empty lines +fn indent<'a>() -> impl Parser<'a, &'a str, &'a str, Extras<'a>> + Copy { + let empty_lines = text::inline_whitespace().then(text::newline()).repeated(); + empty_lines.ignore_then(text::inline_whitespace().to_slice()) +} + +#[derive(Clone, Debug)] +pub enum Stmt { + Expr, + Loop(Vec), +} + +fn parser<'a>() -> impl Parser<'a, &'a str, Vec, Extras<'a>> { + let expr = text::ident(); // TODO + + // Parses everything beyond the initial indentation in a block + let block = recursive(|block| { + let block_start_indent = indent().try_map_with(move |indent, ext| match *ext.ctx() { + ctx if indent.starts_with(ctx) && indent.len() > ctx.len() => Ok(indent), + _ => Err(Rich::custom( + ext.span(), + "Indent must increase here".to_string(), + )), + }); + + let expr_stmt = expr.then_ignore(text::newline()).to(Stmt::Expr); + let control_flow = just("loop:") + .then(text::newline()) + // First, we parse some indentation to determine what indentation the new block should have + .ignore_then(block_start_indent.ignore_with_ctx(block)) + .map(Stmt::Loop); + let stmt = expr_stmt.or(control_flow); + + let block_continue_indent = indent().try_map_with(move |indent, ext| match *ext.ctx() { + ctx if indent == ctx => Ok(indent), + ctx if ctx.starts_with(indent) => { + Err(Rich::custom(ext.span(), "Unexpected deindent".to_string())) + } + ctx if indent.starts_with(ctx) => { + Err(Rich::custom(ext.span(), "Unexpected indent".to_string())) + } + ctx => Err(Rich::custom( + ext.span(), + format!("Mismatched indent: expected {ctx:?}, found {indent:?}"), + )), + }); + + stmt.separated_by(block_continue_indent).collect() + }); + + // The root block always has no indentation + indent() + .try_map_with(|indent, ext| { + if !indent.is_empty() { + Err(Rich::custom(ext.span(), "No indent allowed at root level")) + } else { + Ok("") + } + }) + .ignore_with_ctx(block) +} + +fn show_result(input: &str) { + let (res, errs) = parser().parse(input).into_output_errors(); + println!("Final parse result: {res:#?}"); + errs.into_iter().for_each(|e| { + Report::build(ReportKind::Error, ((), e.span().into_range())) + .with_config(ariadne::Config::new().with_index_type(ariadne::IndexType::Byte)) + .with_message(e.to_string()) + .with_label( + Label::new(((), e.span().into_range())) + .with_message(e.reason().to_string()) + .with_color(Color::Red), + ) + .finish() + .print(Source::from(&input)) + .unwrap() + }); +} + +fn main() { + eprintln!("Well-formed example:"); + show_result( + r#" +expr +expr +loop: + expr + loop: + expr + expr + expr +expr +"#, + ); + + eprintln!("Example with bad indent:"); + show_result( + r#" +foo +bar +loop: + baz + loop: + caz + car + dar +daz +"#, + ); +} diff --git a/chumsky-0.12.0/examples/io.rs b/chumsky-0.13.0/examples/io.rs similarity index 100% rename from chumsky-0.12.0/examples/io.rs rename to chumsky-0.13.0/examples/io.rs diff --git a/chumsky-0.12.0/examples/json.rs b/chumsky-0.13.0/examples/json.rs similarity index 100% rename from chumsky-0.12.0/examples/json.rs rename to chumsky-0.13.0/examples/json.rs diff --git a/chumsky-0.12.0/examples/json_fast.rs b/chumsky-0.13.0/examples/json_fast.rs similarity index 100% rename from chumsky-0.12.0/examples/json_fast.rs rename to chumsky-0.13.0/examples/json_fast.rs diff --git a/chumsky-0.12.0/examples/logos.rs b/chumsky-0.13.0/examples/logos.rs similarity index 95% rename from chumsky-0.12.0/examples/logos.rs rename to chumsky-0.13.0/examples/logos.rs index 280d68895e..238b399bab 100644 --- a/chumsky-0.12.0/examples/logos.rs +++ b/chumsky-0.13.0/examples/logos.rs @@ -64,17 +64,15 @@ enum SExpr { // This function signature looks complicated, but don't fear! We're just saying that this function is generic over // inputs that: // - Can have tokens pulled out of them by-value, by cloning (`ValueInput`) -// - Gives us access to slices of the original input (`SliceInput`) // - Produces tokens of type `Token`, the type we defined above (`Token = Token<'a>`) // - Produces spans of type `SimpleSpan`, a built-in span type provided by chumsky (`Span = SimpleSpan`) // The function then returns a parser that: // - Has an input type of type `I`, the one we declared as a type parameter // - Produces an `SExpr` as its output // - Uses `Rich`, a built-in error type provided by chumsky, for error generation -fn parser<'tokens, 'src: 'tokens, I>( -) -> impl Parser<'tokens, I, SExpr, extra::Err>>> +fn parser<'tok, 'src: 'tok, I>() -> impl Parser<'tok, I, SExpr, extra::Err>>> where - I: ValueInput<'tokens, Token = Token<'src>, Span = SimpleSpan>, + I: ValueInput<'tok, Token = Token<'src>, Span = SimpleSpan>, { recursive(|sexpr| { let atom = select! { diff --git a/chumsky-0.12.0/examples/mini_ml.rs b/chumsky-0.13.0/examples/mini_ml.rs similarity index 98% rename from chumsky-0.12.0/examples/mini_ml.rs rename to chumsky-0.13.0/examples/mini_ml.rs index 325a165c25..1f4a5dcee6 100644 --- a/chumsky-0.12.0/examples/mini_ml.rs +++ b/chumsky-0.13.0/examples/mini_ml.rs @@ -1,7 +1,7 @@ //! This is an entire lexer, parser, type-checker, and interpreter for a statically-typed ML-like functional //! programming language. See `sample.mini_ml` for sample source code. //! Run it with the following command: -//! cargo run --features=pratt,label --example mini_ml -- examples/sample.mini_ml +//! cargo run --features=pratt --example mini_ml -- examples/sample.mini_ml use ariadne::{sources, Color, Label, Report, ReportKind}; use chumsky::{ @@ -154,17 +154,15 @@ fn parser<'tokens, 'src: 'tokens>() -> impl Parser< // ( x ) expr.nested_in(select_ref! { Token::Parens(ts) = e => ts.split_spanned(e.span()) }), )) - .pratt(vec![ + .pratt(( // Multiply infix(left(10), just(Token::Asterisk), |x, _, y, e| { Expr::Mul(Box::new(x), Box::new(y)).with_span(e.span()) - }) - .boxed(), + }), // Add infix(left(9), just(Token::Plus), |x, _, y, e| { Expr::Add(Box::new(x), Box::new(y)).with_span(e.span()) - }) - .boxed(), + }), // Calls infix(left(1), empty(), |x, _, y, e| { Expr::Apply { @@ -172,9 +170,8 @@ fn parser<'tokens, 'src: 'tokens>() -> impl Parser< arg: Box::new(y), } .with_span(e.span()) - }) - .boxed(), - ]) + }), + )) .labelled("expression") .as_context() }) diff --git a/chumsky-0.12.0/examples/nano_rust.rs b/chumsky-0.13.0/examples/nano_rust.rs similarity index 99% rename from chumsky-0.12.0/examples/nano_rust.rs rename to chumsky-0.13.0/examples/nano_rust.rs index d10bd4d516..77529cbe78 100644 --- a/chumsky-0.12.0/examples/nano_rust.rs +++ b/chumsky-0.13.0/examples/nano_rust.rs @@ -1,7 +1,7 @@ //! This is an entire parser and interpreter for a dynamically-typed Rust-like expression-oriented //! programming language. See `sample.nrs` for sample source code. //! Run it with the following command: -//! cargo run --features="label" --example nano_rust -- examples/sample.nrs +//! cargo run --example nano_rust -- examples/sample.nrs use ariadne::{sources, Color, Label, Report, ReportKind}; use chumsky::{input::ValueInput, prelude::*}; diff --git a/chumsky-0.12.0/examples/nested.rs b/chumsky-0.13.0/examples/nested.rs similarity index 100% rename from chumsky-0.12.0/examples/nested.rs rename to chumsky-0.13.0/examples/nested.rs diff --git a/chumsky-0.12.0/examples/nested_spans.rs b/chumsky-0.13.0/examples/nested_spans.rs similarity index 100% rename from chumsky-0.12.0/examples/nested_spans.rs rename to chumsky-0.13.0/examples/nested_spans.rs diff --git a/chumsky-0.12.0/examples/pythonic.rs b/chumsky-0.13.0/examples/pythonic.rs similarity index 100% rename from chumsky-0.12.0/examples/pythonic.rs rename to chumsky-0.13.0/examples/pythonic.rs diff --git a/chumsky-0.12.0/examples/sample.bf b/chumsky-0.13.0/examples/sample.bf similarity index 100% rename from chumsky-0.12.0/examples/sample.bf rename to chumsky-0.13.0/examples/sample.bf diff --git a/chumsky-0.12.0/examples/sample.foo b/chumsky-0.13.0/examples/sample.foo similarity index 100% rename from chumsky-0.12.0/examples/sample.foo rename to chumsky-0.13.0/examples/sample.foo diff --git a/chumsky-0.12.0/examples/sample.io b/chumsky-0.13.0/examples/sample.io similarity index 100% rename from chumsky-0.12.0/examples/sample.io rename to chumsky-0.13.0/examples/sample.io diff --git a/chumsky-0.12.0/examples/sample.json b/chumsky-0.13.0/examples/sample.json similarity index 100% rename from chumsky-0.12.0/examples/sample.json rename to chumsky-0.13.0/examples/sample.json diff --git a/chumsky-0.12.0/examples/sample.mini_ml b/chumsky-0.13.0/examples/sample.mini_ml similarity index 100% rename from chumsky-0.12.0/examples/sample.mini_ml rename to chumsky-0.13.0/examples/sample.mini_ml diff --git a/chumsky-0.12.0/examples/sample.nrs b/chumsky-0.13.0/examples/sample.nrs similarity index 100% rename from chumsky-0.12.0/examples/sample.nrs rename to chumsky-0.13.0/examples/sample.nrs diff --git a/chumsky-0.12.0/examples/sample.py b/chumsky-0.13.0/examples/sample.py similarity index 100% rename from chumsky-0.12.0/examples/sample.py rename to chumsky-0.13.0/examples/sample.py diff --git a/chumsky-0.12.0/examples/zero-copy.rs b/chumsky-0.13.0/examples/zero-copy.rs similarity index 100% rename from chumsky-0.12.0/examples/zero-copy.rs rename to chumsky-0.13.0/examples/zero-copy.rs diff --git a/chumsky-0.12.0/guide/README.md b/chumsky-0.13.0/guide/README.md similarity index 100% rename from chumsky-0.12.0/guide/README.md rename to chumsky-0.13.0/guide/README.md diff --git a/chumsky-0.12.0/guide/debugging.md b/chumsky-0.13.0/guide/debugging.md similarity index 100% rename from chumsky-0.12.0/guide/debugging.md rename to chumsky-0.13.0/guide/debugging.md diff --git a/chumsky-0.12.0/guide/error_and_recovery.md b/chumsky-0.13.0/guide/error_and_recovery.md similarity index 100% rename from chumsky-0.12.0/guide/error_and_recovery.md rename to chumsky-0.13.0/guide/error_and_recovery.md diff --git a/chumsky-0.12.0/guide/getting_started.md b/chumsky-0.13.0/guide/getting_started.md similarity index 97% rename from chumsky-0.12.0/guide/getting_started.md rename to chumsky-0.13.0/guide/getting_started.md index ce88c5f340..848f86efb8 100644 --- a/chumsky-0.12.0/guide/getting_started.md +++ b/chumsky-0.13.0/guide/getting_started.md @@ -39,7 +39,7 @@ chumsky = "0.10" Chumsky currently has a MSRV of **1.65** due to internal systems that require Generic Associated Types (GATs). If you find that chumsky fails to compile on versions of Rust later than or equal to 1.65, please -[open a bug report](https://github.com/zesterer/chumsky/issues/new). +[open a bug report](https://codeberg.org/zesterer/chumsky/issues/new). Please note that chumsky's `nightly` feature is exempt from this minimum version requirement and may require up to and including the latest nightly Rust compiler to work. @@ -80,7 +80,7 @@ fn parser<'src>() -> impl Parser<'src, &'src str, ()> { 2. Because large parsers can have rather unwieldy types, we save ourselves the need to declare the exact return type with Rust's `impl Trait` syntax. This says to the compiler "we don't actually care what type is returned here, but - it needs to implement the `Parser<'src, &'src, str, ()>` trait, you figure it out". Note that, unlike `dyn Trait` + it needs to implement the `Parser<'src, &'src str, ()>` trait, you figure it out". Note that, unlike `dyn Trait` syntax, `impl Trait` has no runtime cost: the compiler simply *hides* the type from you rather than performing *type erasure*, which would require performing [dynamic dispatch](https://en.wikipedia.org/wiki/Dynamic_dispatch) while your code is running. @@ -135,7 +135,7 @@ create an instance of our parser, and then we call [`Parser::parse`] on it with parsing. The return value is the result of the parse. From here, the world is your lobster: you can move on to the tutorial sections of this guide or you can jump write into -writing parsers. The main repository has [plenty of examples](https://github.com/zesterer/chumsky/tree/main/examples) +writing parsers. The main repository has [plenty of examples](https://codeberg.org/zesterer/chumsky/src/branch/main/examples) to use as a reference and the crate has documentation that will help guide you, with many examples. ## Advice diff --git a/chumsky-0.12.0/guide/intro.md b/chumsky-0.13.0/guide/intro.md similarity index 100% rename from chumsky-0.12.0/guide/intro.md rename to chumsky-0.13.0/guide/intro.md diff --git a/chumsky-0.12.0/guide/key_concepts.md b/chumsky-0.13.0/guide/key_concepts.md similarity index 100% rename from chumsky-0.12.0/guide/key_concepts.md rename to chumsky-0.13.0/guide/key_concepts.md diff --git a/chumsky-0.12.0/guide/meet_the_parsers.md b/chumsky-0.13.0/guide/meet_the_parsers.md similarity index 85% rename from chumsky-0.12.0/guide/meet_the_parsers.md rename to chumsky-0.13.0/guide/meet_the_parsers.md index 489b206f85..b863de48d5 100644 --- a/chumsky-0.12.0/guide/meet_the_parsers.md +++ b/chumsky-0.13.0/guide/meet_the_parsers.md @@ -43,7 +43,7 @@ a particular token or set of tokens. | [`todo()`] | `foo.then(todo())` | A placeholder parser that panics when invoked. Spiritually similar to the [`todo!`] macro. | | [`custom`] | (see documentation for examples) | Allows implementing custom parsing logic, see the documentation for more information about how to write custom parsers. | | [`end`] | `x.then(end())` | Recognises only the end of the input. Not to be confused with [`empty`]. | -| [`empty()`] | `empty().then(y)` | Recognises no input (i.e: it will always succeed, without advancing the input). Not to be confused with [`end`]. | +| [`empty()`] | `empty().then(y)` | Recognises no input (i.e.: it will always succeed, without advancing the input). Not to be confused with [`end`]. | ## Combinators @@ -60,8 +60,8 @@ Combinators that allow combining smaller parsers together to make parsers for mo |---------------------------------|---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | [`Parser::then`] | `a.then(b)` | Parse one pattern and then another, producing a tuple of the two parsers outputs as an output. | | [`Parser::or`] | `a.or(b)` | Parse one pattern, or another if the first failed to parse. This allows you to implement branching parser logic that recognises one of many different patterns. | -| [`Parser::ignore_then`] | `a.ignore_then(b)` | Parse one pattern and then another, producing only the output of the second as an output (i.e: ignoring the output of the first). | -| [`Parser::then_ignore`] | `a.then_ignore(b)` | Parse one pattern and then another, producing only the output of the first as an output (i.e: ignoring the output of the second). | +| [`Parser::ignore_then`] | `a.ignore_then(b)` | Parse one pattern and then another, producing only the output of the second as an output (i.e.: ignoring the output of the first). | +| [`Parser::then_ignore`] | `a.then_ignore(b)` | Parse one pattern and then another, producing only the output of the first as an output (i.e.: ignoring the output of the second). | | [`Parser::delimited_by`] | `a.delimited_by(x, y)` | Parses a pattern, delimited by two other patterns on either side. Most often used to parse parenthesiesed expressions, blocks, or arrays. | | [`Parser::padded_by`] | `a.padded_by(b)` | Parses a pattern, delimited by a pattern on either side. Often used to consume whitespace or other irrelevant input that surrounds a pattern. | | [`Parser::repeated`] | `a.repeated().collect::>()` | Parse the given pattern any number of times (including none at all!). Note that [`Repeated`] implements the [`IterParser`] trait, so can be used with [`IterParser::collect`]. | @@ -75,17 +75,17 @@ Combinators that allow combining smaller parsers together to make parsers for mo Combinators that manipulate, generate, or combine the output of parsers in some manner (see [backtracking and input manipulation](#backtracking-and-input-manipulation) for combinators that recover from errors). -| Name | Example | Description | -|---------------------------------|---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [`Parser::map`] | `a.map(...)` | Map the output of a parser using the given mapping function. | -| [`Parser::map_with`] | `a.map_with(...)` | Map the output of a parser using the given mapping function, with access to metadata associated with the output. | -| [`Parser::to_slice`] | `a.to_slice()` | Parse a pattern. Discard the output of the pattern and instead use a slice of the input that the pattern corresponds to as the output. Requires inputs that implement [`SliceInput`]. | -| [`Parser::to`] | `a.to(x)` | Parse a pattern, ignoring the output value and using a constant value as the output value instead. | -| [`Parser::ignored`] | `a.ignored()` | Parse a pattern, ignoring the output value and using `()` as the output value instead. | -| [`IterParser::collect`] | `a.repeated().collect::>()` | Collects elements of an [`IterParser`] into a type implementing [`Container`]. | -| [`IterParser::collect_exactly`] | `a.repeated().collect::>()` | Collects elements of an [`IterParser`] into an exact-sized type implementing [`ContainerExactly`]. | -| [`IterParser::count`] | `a.repeated().count()` | Count the number of elements produced by an [`IterParser`]. | -| [`Parser::unwrapped`] | `a.unwrapped()` | Parse a pattern that returns either a [`Result`] or an [`Option`], then unwrap them. | +| Name | Example | Description | +|---------------------------------|--------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [`Parser::map`] | `a.map(...)` | Map the output of a parser using the given mapping function. | +| [`Parser::map_with`] | `a.map_with(...)` | Map the output of a parser using the given mapping function, with access to metadata associated with the output. | +| [`Parser::to_slice`] | `a.to_slice()` | Parse a pattern. Discard the output of the pattern and instead use a slice of the input that the pattern corresponds to as the output. Requires inputs that implement [`SliceInput`]. | +| [`Parser::to`] | `a.to(x)` | Parse a pattern, ignoring the output value and using a constant value as the output value instead. | +| [`Parser::ignored`] | `a.ignored()` | Parse a pattern, ignoring the output value and using `()` as the output value instead. | +| [`IterParser::collect`] | `a.repeated().collect::>()` | Collects elements of an [`IterParser`] into a type implementing [`FromIterator`]. | +| [`IterParser::collect_exactly`] | `a.repeated().collect_exactly::<[_; N]>()` | Collects elements of an [`IterParser`] into an exact-sized type implementing [`ContainerExactly`]. | +| [`IterParser::count`] | `a.repeated().count()` | Count the number of elements produced by an [`IterParser`]. | +| [`Parser::unwrapped`] | `a.unwrapped()` | Parse a pattern that returns either a [`Result`] or an [`Option`], then unwrap them. | ### Handling and emitting errors @@ -94,12 +94,14 @@ Combinators that manipulate or emit errors, along with fallibly validating parse | Name | Example | Description | |---------------------------------|---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | [`Parser::map_err`] | `a.map_err(...)` | Parse a pattern. On failure, map the parser error to another value. Often used to customise error messages or add extra information to them. | -| [`Parser::map_err_with_state`] | `a.lazy()` | Like [`Parser::map_err`], but provides access to the parser state (see [`Parser::parse_with_state`] for more information). | -| [`Parser::try_foldl`] | `a.try_foldl(...)` | Left-fold the output of the parser into a single value, possibly failing during the reduction. If the function produces an error, the parser fails with that error. | +| [`Parser::map_err_with`] | `a.map_err_with(...)` | Like [`Parser::map_err`], but provides access to metadata associated with the output. | +| [`Parser::map_err_with_state`] | `a.map_err_with_state(...)` | Like [`Parser::map_err`], but provides access to the parser state (see [`Parser::parse_with_state`] for more information). | +| [`Parser::try_foldl`] | `a.try_foldl(...)` | Left-fold the output of the parser into a single value, possibly failing during the reduction. If the function produces an error, the parser fails with that error. | | [`Parser::try_map`] | `a.try_map(...)` | Map the output of a parser using the given fallible mapping function. If the function produces an error, the parser fails with that error. | -| [`Parser::try_map_with`] | `a.try_map_with(...)` | | Map the output of a parser using the given fallible mapping function, with access to output metadata. If the function produces an error, the parser fails with that error. | +| [`Parser::try_map_with`] | `a.try_map_with(...)` | Map the output of a parser using the given fallible mapping function, with access to output metadata. If the function produces an error, the parser fails with that error. | | [`Parser::validate`] | `a.validate(...)` | Parse a pattern. On success, map the output to another value with the opportunity to emit extra secondary errors. Commonly used to check the validity of patterns in the parser. | | [`Parser::filter`] | `any().filter(char::is_lowercase)` | Parse a pattern and apply the given filtering function to the output. If the filter function returns [`false`], the parser fails. | +| [`Parser::filter_map`] | `any().filter_map(...)` | Parse a pattern and apply the given filter-map function to the output. If the filter-map function returns [`None`], the parser fails. | | [`Parser::labelled`] | `a.labelled("a")` | Parse a pattern, labelling it. What exactly this does depends on the error type, but it is generally used to give a pattern a more general name (for example, "expression"). | ### Text-oriented parsing @@ -108,7 +110,7 @@ Combinators intended only for the parsing and manipulation of text-like inputs. | Name | Example | Description | |---------------------------------|---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [`Parser::padded`] | `a.padded()` | Skips whitespace on either side of a pattern for text-like inputs specifically (i.e: those with [`u8`] or [`char`] tokens). A more specialised version of [`Parser::padded_by`]. | +| [`Parser::padded`] | `a.padded()` | Skips whitespace on either side of a pattern for text-like inputs specifically (i.e.: those with [`u8`] or [`char`] tokens). A more specialised version of [`Parser::padded_by`]. | | [`Parser::from_str`] | `just("true").from_str().unwrapped()` | Parse a pattern that outputs a string, then use Rust's [`FromStr`] trait to parse it. Often paired with [`Parser::unwrapped`] to unwrap any errors. | ### Utility and error recovery @@ -117,7 +119,7 @@ Miscellaneous combinators and those that relate to error recovery. | Name | Example | Description | |---------------------------------|---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [`Parser::boxed`] | `a.boxed()` | Performs type-erasure on a parser, allocating it on the heap. Stategically boxing of parsers can improve compilation times and allows dynamically building up parsers at runtime. | +| [`Parser::boxed`] | `a.boxed()` | Performs type-erasure on a parser, allocating it on the heap. Strategically boxing of parsers can improve compilation times and allows dynamically building up parsers at runtime. | | [`Parser::recover_with`] | `a.recover_with(r)` | Attempt to parse a pattern. On failure, the given recovery strategy is used to attempt to recovery from the error. See the documentation for more information. | | [`Parser::memoized`] | `a.memoized()` | Parse a pattern, but remember whether it succeeded or failed and reuse that information when parsing the same input again. Allows expressing left-recursive or exponential patterns. | @@ -129,7 +131,7 @@ Combinators that perform internal backtracking or that manipulate inputs in orde |---------------------------------|---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | [`Parser::not`] | `a.and_is(b.not())` | Doesn't parse anything, but rejects anything that *would* parse as the given pattern. On success, no input is consumed. | | [`Parser::and_is`] | `a.and_is(b)` | Parses one pattern, but only if the other parser also parses at the same location. | -| [`Parser::rewind`] | `a.then(b.rewind())` | Parses a pattern. On success, rewinds the input to the start of the pattern as if it had never been parsed. Often used parse patterns that expect to have something else after them | +| [`Parser::rewind`] | `a.then(b.rewind())` | Parses a pattern. On success, rewinds the input to the start of the pattern as if it had never been parsed. Often used parse patterns that expect to have something else after them. | | [`Parser::lazy`] | `a.lazy()` | Only useful on 'top-level' parsers. Makes the parser lazy such that it will only recognise as much input as it can and no more. | | [`Parser::nested_in`] | `a.nested_in(b)` | Parse one pattern from the output of another pattern, using the output of the second parser as the input of the first. Often used to pass token trees. | diff --git a/chumsky-0.12.0/guide/recursion.md b/chumsky-0.13.0/guide/recursion.md similarity index 100% rename from chumsky-0.12.0/guide/recursion.md rename to chumsky-0.13.0/guide/recursion.md diff --git a/chumsky-0.12.0/guide/technical_notes.md b/chumsky-0.13.0/guide/technical_notes.md similarity index 100% rename from chumsky-0.12.0/guide/technical_notes.md rename to chumsky-0.13.0/guide/technical_notes.md diff --git a/chumsky-0.12.0/guide/tutorial.md b/chumsky-0.13.0/guide/tutorial.md similarity index 98% rename from chumsky-0.12.0/guide/tutorial.md rename to chumsky-0.13.0/guide/tutorial.md index 815382f8fb..10a03bad7c 100644 --- a/chumsky-0.12.0/guide/tutorial.md +++ b/chumsky-0.13.0/guide/tutorial.md @@ -17,7 +17,7 @@ By the end of this tutorial, you'll have an interpreter that will let you run th This tutorial should take somewhere between 30 and 100 minutes to complete, depending on factors such as knowledge of Rust and compiler theory. -*You can find the source code for the full interpreter in [`examples/foo.rs`](https://github.com/zesterer/chumsky/blob/main/examples/foo.rs) in the main repository.* +*You can find the source code for the full interpreter in [`examples/foo.rs`](https://codeberg.org/zesterer/chumsky/src/branch/main/examples/foo.rs) in the main repository.* ## Assumptions @@ -31,7 +31,7 @@ This tutorial is here to show you how to use Chumsky: it's not a general-purpose As we go, we'll be encountering many functions and concepts from Chumsky. I strongly recommend you keep [Chumsky's documentation](https://docs.rs/chumsky/) open in another browser tab and use it to cross-reference your understanding or gain more insight into specific things that you'd like more clarification on. In particular, most of the functions we'll be using come from the [`Parser`](https://docs.rs/chumsky/latest/chumsky/trait.Parser.html) trait. Chumsky's docs include extensive doc examples for almost every function, so be sure to make use of them! -Chumsky also has [several longer examples](https://github.com/zesterer/chumsky/tree/main/examples) in the main repository: looking at these may help improve your understanding if you get stuck. +Chumsky also has [several longer examples](https://codeberg.org/zesterer/chumsky/src/branch/main/examples) in the main repository: looking at these may help improve your understanding if you get stuck. ## A note on imperative vs declarative parsers @@ -838,7 +838,7 @@ add(five, eight) ## Full source code You can find the full source code for Foo [in the crate -examples](https://github.com/zesterer/chumsky/blob/main/examples/foo.rs). +examples](https://codeberg.org/zesterer/chumsky/src/branch/main/examples/foo.rs). ## Conclusion diff --git a/chumsky-0.12.0/src/blanket.rs b/chumsky-0.13.0/src/blanket.rs similarity index 100% rename from chumsky-0.12.0/src/blanket.rs rename to chumsky-0.13.0/src/blanket.rs diff --git a/chumsky-0.12.0/src/combinator.rs b/chumsky-0.13.0/src/combinator.rs similarity index 94% rename from chumsky-0.12.0/src/combinator.rs rename to chumsky-0.13.0/src/combinator.rs index 4aa2aa1f08..d30fc311f0 100644 --- a/chumsky-0.12.0/src/combinator.rs +++ b/chumsky-0.13.0/src/combinator.rs @@ -260,6 +260,48 @@ where debug::NodeInfo::Filter(Box::new(self.parser.node_info(scope))) } + #[inline(always)] + fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult { + (&self.parser) + .filter_map(|out| if (self.filter)(&out) { Some(out) } else { None }) + .go::(inp) + } + + go_extra!(O); +} + +/// See [`Parser::filter_map`]. +pub struct FilterMap { + pub(crate) parser: A, + pub(crate) filter_mapper: F, + #[allow(dead_code)] + pub(crate) phantom: EmptyPhantom, +} + +impl Copy for FilterMap {} +impl Clone for FilterMap { + fn clone(&self) -> Self { + Self { + parser: self.parser.clone(), + filter_mapper: self.filter_mapper.clone(), + phantom: EmptyPhantom::new(), + } + } +} + +impl<'src, I, O, E, A, OA, F> Parser<'src, I, O, E> for FilterMap +where + I: Input<'src>, + E: ParserExtra<'src, I>, + A: Parser<'src, I, OA, E>, + F: Fn(OA) -> Option, +{ + #[doc(hidden)] + #[cfg(feature = "debug")] + fn node_info(&self, scope: &mut debug::NodeScope) -> debug::NodeInfo { + debug::NodeInfo::Filter(Box::new(self.parser.node_info(scope))) + } + #[inline(always)] fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult { let found = inp.peek_maybe(); @@ -274,19 +316,22 @@ where inp.errors.alt = old_alt; match res { Ok(out) => { - if (self.filter)(&out) { - // If successful, reinsert the original alt and then apply the new alt on top of it, since both are valid - if let Some(new_alt) = new_alt { - inp.add_alt_err(&new_alt.pos, new_alt.err); + match (self.filter_mapper)(out) { + Some(mapped) => { + // If successful, reinsert the original alt and then apply the new alt on top of it, since both are valid + if let Some(new_alt) = new_alt { + inp.add_alt_err(&new_alt.pos, new_alt.err); + } + Ok(M::bind(|| mapped)) + } + None => { + // If unsuccessful, reinsert the original alt but replace the new alt with the "something else" error (since it overrides it) + let expected = [DefaultExpected::SomethingElse]; + // TODO: Use something more detailed than the next token as the found + let err = E::Error::expected_found(expected, found, span); + inp.add_alt_err(&before.inner, err); + Err(()) } - Ok(M::bind(|| out)) - } else { - // If unsuccessful, reinsert the original alt but replace the new alt with the "something else" error (since it overrides it) - let expected = [DefaultExpected::SomethingElse]; - // TODO: Use something more detailed than the next token as the found - let err = E::Error::expected_found(expected, found, span); - inp.add_alt_err(&before.inner, err); - Err(()) } } @@ -1074,9 +1119,9 @@ where } let res = self.parser.go::(inp); - if res.is_err() { - let alt = inp.take_alt(); + // Remember the result of the parse at this location + let alt = inp.errors.alt.clone(); inp.memos.insert(key, alt); } else { inp.memos.remove(&key); @@ -1893,7 +1938,7 @@ where &self, inp: &mut InputRef<'src, '_, I, E>, count: &mut Self::IterState, - debug: IterParserDebug, + #[allow(unused_variables)] debug: IterParserDebug, ) -> IPResult { if *count as u64 >= self.at_most { return Ok(None); @@ -1939,7 +1984,7 @@ where inp: &mut InputRef<'src, '_, I, E>, count: &mut Self::IterState, cfg: &Self::Config, - debug: IterParserDebug, + #[allow(unused_variables)] debug: IterParserDebug, ) -> IPResult { let at_most = cfg.at_most.map(|x| x as u64).unwrap_or(self.at_most); let at_least = cfg.at_least.unwrap_or(self.at_least); @@ -2119,7 +2164,7 @@ where /// Allow a trailing separator to appear after the last item. /// - /// Note that if no items are parsed, no leading separator is permitted. + /// Note that if no items are parsed, no trailing separator is permitted. /// /// # Examples /// @@ -2177,7 +2222,7 @@ where &self, inp: &mut InputRef<'src, '_, I, E>, state: &mut Self::IterState, - debug: IterParserDebug, + #[allow(unused_variables)] debug: IterParserDebug, ) -> IPResult { #[cfg(debug_assertions)] let before = inp.cursor(); @@ -2331,6 +2376,53 @@ where } } +/// See [`IterParser::count`]. +pub struct Count { + pub(crate) parser: A, + #[allow(dead_code)] + pub(crate) phantom: EmptyPhantom, +} + +impl Copy for Count {} +impl Clone for Count { + fn clone(&self) -> Self { + Self { + parser: self.parser.clone(), + phantom: EmptyPhantom::new(), + } + } +} + +impl<'src, I, O, E, A> Parser<'src, I, usize, E> for Count +where + I: Input<'src>, + E: ParserExtra<'src, I>, + A: IterParser<'src, I, O, E>, +{ + #[doc(hidden)] + #[cfg(feature = "debug")] + fn node_info(&self, scope: &mut debug::NodeScope) -> debug::NodeInfo { + self.parser.node_info(scope) + } + + #[inline(always)] + fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult { + let mut iter_state = self.parser.make_iter::(inp)?; + let mut n = M::bind(|| 0); + loop { + match self + .parser + .next::(inp, &mut iter_state, IterParserDebug::new(false))? + { + Some(()) => n = M::map(n, |n| n + 1), + None => break Ok(n), + } + } + } + + go_extra!(usize); +} + /// See [`IterParser::collect`]. pub struct Collect { pub(crate) parser: A, @@ -2353,7 +2445,7 @@ where I: Input<'src>, E: ParserExtra<'src, I>, A: IterParser<'src, I, O, E>, - C: Container, + C: FromIterator, { #[doc(hidden)] #[cfg(feature = "debug")] @@ -2363,21 +2455,25 @@ where #[inline(always)] fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult { - let mut output = M::bind::(|| C::default()); let mut iter_state = self.parser.make_iter::(inp)?; - loop { + let mut is_err = false; + let c = M::from_iter(core::iter::from_fn(|| { match self .parser .next::(inp, &mut iter_state, IterParserDebug::new(false)) { - Ok(Some(out)) => { - M::combine_mut(&mut output, out, |output: &mut C, item| output.push(item)); + Ok(Some(out)) => Some(out), + Ok(None) => None, + Err(()) => { + is_err = true; + None } - - Ok(None) => break Ok(output), - - Err(()) => break Err(()), } + })); + if is_err { + Err(()) + } else { + Ok(c) } } @@ -3104,7 +3200,7 @@ where Self: Sized, { (&self.parser) - .map_err_with_state(|e, _, _| (self.mapper)(e)) + .map_err_with(|e, _| (self.mapper)(e)) .go::(inp) } @@ -3146,7 +3242,6 @@ where // go_extra!(O); // } -// TODO: Remove combinator, replace with map_err_with /// See [`Parser::map_err_with_state`]. #[derive(Copy, Clone)] pub struct MapErrWithState { @@ -3192,6 +3287,50 @@ where go_extra!(O); } +/// See [`Parser::map_err_with`]. +#[derive(Copy, Clone)] +pub struct MapErrWith { + pub(crate) parser: A, + pub(crate) mapper: F, +} + +impl<'src, I, O, E, A, F> Parser<'src, I, O, E> for MapErrWith +where + I: Input<'src>, + E: ParserExtra<'src, I>, + A: Parser<'src, I, O, E>, + F: Fn(E::Error, &mut MapExtra<'src, '_, I, E>) -> E::Error, +{ + #[inline(always)] + fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult + where + Self: Sized, + { + let start = inp.cursor(); + let old_alt = inp.take_alt(); + let res = self.parser.go::(inp); + let new_alt = inp.take_alt(); + + if res.is_ok() { + inp.errors.alt = old_alt; + if let Some(new_alt) = new_alt { + inp.add_alt_err(&new_alt.pos, new_alt.err); + } + } else { + // Can't fail! + let mut new_alt = new_alt.unwrap(); + new_alt.err = (self.mapper)(new_alt.err, &mut MapExtra::new(&start, inp)); + + inp.errors.alt = old_alt; + inp.add_alt_err(&new_alt.pos, new_alt.err); + } + + res + } + + go_extra!(O); +} + /// See [`Parser::validate`] pub struct Validate { pub(crate) parser: A, @@ -3326,7 +3465,7 @@ where self.inner.go::(inp) } else { let err_span = inp.span_since(&before); - inp.add_alt([DefaultExpected::SomethingElse], None, err_span); + inp.add_alt([DefaultExpected::NothingElse], None, err_span); Err(()) } } diff --git a/chumsky-0.12.0/src/container.rs b/chumsky-0.13.0/src/container.rs similarity index 84% rename from chumsky-0.12.0/src/container.rs rename to chumsky-0.13.0/src/container.rs index 8d8f0096ca..c3474e3166 100644 --- a/chumsky-0.12.0/src/container.rs +++ b/chumsky-0.13.0/src/container.rs @@ -2,156 +2,9 @@ use super::*; use alloc::collections::LinkedList; +use core::hash::BuildHasher; use hashbrown::HashSet; -/// A utility trait for types that can be constructed from a series of items. -pub trait Container: Default { - /// Create a container, attempting to pre-allocate enough space for `n` items. - /// - /// Failure to do so is not a problem, the size is only a hint. - fn with_capacity(n: usize) -> Self { - let _ = n; - Self::default() - } - /// Add a value to the end of this container. - fn push(&mut self, item: T); -} - -impl Container for Box -where - C: Container, -{ - fn with_capacity(n: usize) -> Self { - Box::new(C::with_capacity(n)) - } - - fn push(&mut self, item: T) { - C::push(self, item) - } -} - -impl Container for Cell -where - C: Container, -{ - fn with_capacity(n: usize) -> Self { - Cell::new(C::with_capacity(n)) - } - - fn push(&mut self, item: T) { - self.get_mut().push(item) - } -} - -impl Container for RefCell -where - C: Container, -{ - fn with_capacity(n: usize) -> Self { - RefCell::new(C::with_capacity(n)) - } - - fn push(&mut self, item: T) { - self.get_mut().push(item) - } -} - -impl Container for () { - fn push(&mut self, _: T) {} -} - -/// A collection that counts items instead of containing them. -impl Container for usize { - fn push(&mut self, _: T) { - *self += 1; - } -} - -impl Container for Vec { - fn with_capacity(n: usize) -> Self { - Self::with_capacity(n) - } - fn push(&mut self, item: T) { - (*self).push(item); - } -} - -impl Container for LinkedList { - fn push(&mut self, item: T) { - (*self).push_back(item); - } -} - -impl Container for String { - fn with_capacity(n: usize) -> Self { - // Note: we're assuming that most characters are going to be ASCII, and hence only require one byte to store. - Self::with_capacity(n) - } - fn push(&mut self, item: char) { - (*self).push(item) - } -} - -impl Container<(K, V)> for HashMap { - fn with_capacity(n: usize) -> Self { - Self::with_capacity(n) - } - fn push(&mut self, (key, value): (K, V)) { - (*self).insert(key, value); - } -} - -#[cfg(feature = "std")] -impl Container<(K, V)> for std::collections::HashMap { - fn with_capacity(n: usize) -> Self { - Self::with_capacity(n) - } - fn push(&mut self, (key, value): (K, V)) { - (*self).insert(key, value); - } -} - -impl Container for HashSet { - fn with_capacity(n: usize) -> Self { - Self::with_capacity(n) - } - fn push(&mut self, item: T) { - (*self).insert(item); - } -} - -#[cfg(feature = "std")] -impl Container for std::collections::HashSet { - fn with_capacity(n: usize) -> Self { - Self::with_capacity(n) - } - fn push(&mut self, item: T) { - (*self).insert(item); - } -} - -#[cfg(feature = "std")] -impl Container for std::collections::VecDeque { - fn with_capacity(n: usize) -> Self { - Self::with_capacity(n) - } - fn push(&mut self, item: T) { - self.push_back(item); - } -} - -impl Container<(K, V)> for alloc::collections::BTreeMap { - fn push(&mut self, (key, value): (K, V)) { - (*self).insert(key, value); - } -} - -impl Container for alloc::collections::BTreeSet { - fn push(&mut self, item: T) { - (*self).insert(item); - } -} - /// A utility trait for types that hold a specific constant number of output values. /// /// # Safety @@ -570,7 +423,7 @@ impl<'p, T: Clone> Seq<'p, T> for LinkedList { } } -impl<'p, T: Clone + Eq + Hash> Seq<'p, T> for HashSet { +impl<'p, T: Clone + Eq + Hash, S: BuildHasher> Seq<'p, T> for HashSet { type Item<'a> = &'a T where @@ -598,13 +451,14 @@ impl<'p, T: Clone + Eq + Hash> Seq<'p, T> for HashSet { fn to_maybe_ref<'b>(item: Self::Item<'b>) -> MaybeRef<'p, T> where 'p: 'b, + S: 'b, { MaybeRef::Val(item.clone()) } } #[cfg(feature = "std")] -impl<'p, T: Clone + Eq + Hash> Seq<'p, T> for std::collections::HashSet { +impl<'p, T: Clone + Eq + Hash, S: BuildHasher> Seq<'p, T> for std::collections::HashSet { type Item<'a> = &'a T where @@ -632,6 +486,7 @@ impl<'p, T: Clone + Eq + Hash> Seq<'p, T> for std::collections::HashSet { fn to_maybe_ref<'b>(item: Self::Item<'b>) -> MaybeRef<'p, T> where 'p: 'b, + S: 'b, { MaybeRef::Val(item.clone()) } diff --git a/chumsky-0.13.0/src/debug.rs b/chumsky-0.13.0/src/debug.rs new file mode 100644 index 0000000000..9b6728bdf4 --- /dev/null +++ b/chumsky-0.13.0/src/debug.rs @@ -0,0 +1,251 @@ +//! Unstable utilities for debugging in-development parsers. +//! +//! See [`Parser::debug`]. + +use crate::label::DebugInfoOverride; + +use super::*; + +#[doc(hidden)] +#[derive(Debug)] +pub enum SeqInfo { + Char(char), + String(String), + Opaque(String), + Unknown(String), +} + +impl SeqInfo { + fn show(&self) -> String { + match self { + Self::Char(c) => format!("'{c}'"), + Self::String(s) => format!("\"{s}\""), + Self::Opaque(s) => s.to_string(), + Self::Unknown(s) => s.to_string(), + } + } +} + +#[doc(hidden)] +#[derive(Debug)] +pub enum NodeInfo { + Unknown(String), + // The root of a recursive definition + Recursive(usize, Box), + RecursiveRef(usize), + Repeated(core::ops::Range, Box), + SeparatedBy(Box, Box), + Choice(Vec), + Any, + Just(SeqInfo), + OneOf(SeqInfo), + NoneOf(SeqInfo), + Then(Box, Box), + Padded(Box), + Filter(Box), + OrNot(Box), + Labelled(String, Box, Option), + NestedIn(Box, Box), +} + +impl NodeInfo { + // ctx { 0 = any, 1 = or, 2 = then } + fn bnf_inner(&self, depth: usize, defs: &mut Vec, ctx: usize) -> String { + match self { + Self::Unknown(s) => format!(""), + Self::Recursive(r, inner) => { + let def = inner.bnf_inner(1, defs, 0); + defs.push(format!("def_{r} ::= {def};")); + format!("def_{r}") + } + Self::Repeated(_, inner) => format!("{{ {} }}", inner.bnf_inner(depth, defs, 0)), + Self::SeparatedBy(inner, sep) => format!( + "{{ {} [{}]}}", + inner.bnf_inner(depth, defs, 2), + sep.bnf_inner(depth, defs, 0) + ), + Self::OrNot(inner) => format!("[ {} ]", inner.bnf_inner(depth, defs, 0)), + Self::Choice(inners) => { + let s = inners + .iter() + .map(|i| i.bnf_inner(depth + 1, defs, 1)) + .collect::>() + .join(&format!("\n{}| ", " ".repeat(depth))); + if ctx == 1 || ctx == 0 { + s + } else { + format!("({s})") + } + } + Self::Just(seq) => seq.show(), + Self::OneOf(seq) => format!("one_of({})", seq.show()), + Self::NoneOf(seq) => format!("none_of({})", seq.show()), + Self::Then(a, b) => { + let s = format!( + "{} {}", + a.bnf_inner(depth, defs, 2), + b.bnf_inner(depth, defs, 2) + ); + if ctx == 1 || ctx == 0 { + s + } else { + format!("({s})") + } + } + Self::Any => "any".to_string(), + Self::NestedIn(a, b) => format!( + "({}).nested_in({})", + a.bnf_inner(depth, defs, 0), + b.bnf_inner(depth, defs, 0) + ), + Self::Padded(inner) | Self::Filter(inner) | Self::Labelled(_, inner, None) => { + inner.bnf_inner(depth, defs, ctx) + } + Self::Labelled(label, _, Some(_)) => label.clone(), + Self::RecursiveRef(r) => format!("def_{r}"), + } + } + + fn railroad_inner(&self, defs: &mut Vec>) -> Box { + use railroad::*; + match self { + Self::Unknown(s) => Box::new(Comment::new(s.to_string())), + Self::Recursive(r, inner) => { + let inner = inner.railroad_inner(defs); + defs.push(Box::new(LabeledBox::new( + Sequence::new(vec![ + Box::new(SimpleStart) as Box, + inner, + Box::new(SimpleEnd), + ]), + Terminal::new(format!("def_{r}")), + ))); + Box::new(Terminal::new(format!("def_{r}"))) + } + Self::Repeated(_, inner) => Box::new(Repeat::new(inner.railroad_inner(defs), Empty)), + Self::SeparatedBy(inner, sep) => Box::new(Repeat::new( + inner.railroad_inner(defs), + sep.railroad_inner(defs), + )), + Self::Choice(inners) => Box::new(Choice::new( + inners.iter().map(|i| i.railroad_inner(defs)).collect(), + )), + Self::Just(seq) => Box::new(Terminal::new(seq.show())), + Self::OneOf(seq) => Box::new(Terminal::new(format!("one_of({})", seq.show()))), + Self::NoneOf(seq) => Box::new(Terminal::new(format!("none_of({})", seq.show()))), + Self::Then(a, b) => Box::new(Sequence::new(vec![ + a.railroad_inner(defs), + b.railroad_inner(defs), + ])), + Self::RecursiveRef(r) => Box::new(Terminal::new(format!("def_{r}"))), + // Self::Padded(inner) => Box::new(Sequence::new(vec![ + // Box::new(Terminal::new(format!("whitespace"))) as Box, + // inner.railroad_inner(defs), + // Box::new(Terminal::new(format!("whitespace"))), + // ])), + Self::Padded(inner) => inner.railroad_inner(defs), + Self::Filter(inner) => Box::new(LabeledBox::new( + inner.railroad_inner(defs), + Comment::new("filtered".to_string()), + )), + Self::Labelled(label, inner, None) => Box::new(LabeledBox::new( + inner.railroad_inner(defs), + NonTerminal::new(label.to_string()), + )), + Self::Labelled(label, _, Some(DebugInfoOverride::Terminal)) => { + Box::new(Terminal::new(label.to_string())) + } + Self::Labelled(label, _, Some(DebugInfoOverride::NonTerminal)) => { + Box::new(NonTerminal::new(label.to_string())) + } + Self::NestedIn(inner, outer) => Box::new(LabeledBox::new( + Box::new(LabeledBox::new( + outer.railroad_inner(defs), + NonTerminal::new("outer".to_string()), + )), + Box::new(LabeledBox::new( + Sequence::new(vec![ + Box::new(SimpleStart) as Box, + inner.railroad_inner(defs), + Box::new(SimpleEnd), + ]), + NonTerminal::new("inner".to_string()), + )), + )), + Self::Any => Box::new(Terminal::new("any".to_string())), + Self::OrNot(inner) => Box::new(Optional::new(inner.railroad_inner(defs))), + } + } +} + +#[doc(hidden)] +#[derive(Default)] +pub struct NodeScope { + rec_count: usize, + // (ptr, index, name) + rec: Vec<(usize, usize)>, +} + +impl NodeScope { + pub fn lookup_rec(&mut self, ptr: usize, f: impl FnOnce(&mut Self) -> NodeInfo) -> NodeInfo { + self.rec + .iter() + .rev() + .find(|(p, _)| *p == ptr) + .map(|(_, r)| NodeInfo::RecursiveRef(*r)) + .unwrap_or_else(|| { + self.rec_count += 1; + self.rec.push((ptr, self.rec_count)); + NodeInfo::Recursive(self.rec_count, Box::new(f(self))) + }) + } +} + +/// A catch-all box of tricks for debugging a parser. +pub struct DebugInfo<'a> { + pub(crate) node_info: NodeInfo, + pub(crate) phantom: PhantomData<&'a ()>, +} + +impl<'a> DebugInfo<'a> { + /// Generate a string containing an [eBNF](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form#EBNF) grammar for this parser. + /// + /// The exact format of the grammar definition is not specified, and is only intended to be read by a human being. + pub fn to_ebnf(&self) -> String { + let mut defs = Vec::new(); + let def = self.node_info.bnf_inner(1, &mut defs, 0); + defs.push(def); + defs.join("\n\n") + } + + /// Generate a human-readable [railroad diagram](https://en.wikipedia.org/wiki/Syntax_diagram) that describes the grammar. + /// + /// The resulting diagram is in [SVG](https://en.wikipedia.org/wiki/SVG) format and may be printed to the console, written to a file, etc. + /// + /// The exact format of the diagram is not specified and its quality may depend heavily on annotations, such as [`Parser::labelled`]. + /// Aspects of the grammar may also not be captured. For example, context-sensitive parsers are largely ignored today. + /// + /// # Examples + /// + /// Here is a generated railroad diagram for the example JSON parser. See `examples/json.rs` in the repository. + /// + /// ![A railroad diagram of the grammar for JSON](https://codeberg.org/zesterer/chumsky/src/branch/main/misc/json-railroad.svg) + pub fn to_railroad_svg(&self) -> impl core::fmt::Display + Clone { + use railroad::*; + + let mut seq = Sequence::default(); + let mut defs = Vec::new(); + let def = self.node_info.railroad_inner(&mut defs); + defs.push(def); + seq.push(Rc::new(VerticalGrid::new(defs)) as Rc); + + let mut dia = Diagram::new(seq); + + dia.add_element( + svg::Element::new("style") + .set("type", "text/css") + .text(DEFAULT_CSS), + ); + dia + } +} diff --git a/chumsky-0.12.0/src/either.rs b/chumsky-0.13.0/src/either.rs similarity index 100% rename from chumsky-0.12.0/src/either.rs rename to chumsky-0.13.0/src/either.rs diff --git a/chumsky-0.13.0/src/error.rs b/chumsky-0.13.0/src/error.rs new file mode 100644 index 0000000000..49009a04b0 --- /dev/null +++ b/chumsky-0.13.0/src/error.rs @@ -0,0 +1,912 @@ +//! Error types, traits and utilities. +//! +//! *“I like the cover," he said. "Don't Panic. It's the first helpful or intelligible thing anybody's said to me all +//! day.â€* +//! +//! You can implement the [`Error`] trait to create your own parser errors, or you can use one provided by the crate +//! like [`Cheap`], [`Simple`] or [`Rich`]. + +use super::*; +use alloc::borrow::Cow; + +pub use label::LabelError; + +/// A trait that describes parser error types. +/// +/// If you have a custom error type in your compiler, or your needs are not sufficiently met by [`Simple`], you should +/// implement this trait. If your error type has 'extra' features that allow for more specific error messages, you can +/// use the [`Parser::map_err`] or [`Parser::try_map`] functions to take advantage of these inline within your parser. +/// +/// # Examples +/// +/// ``` +/// use chumsky::{prelude::*, error::{Error, LabelError}, util::MaybeRef, DefaultExpected}; +/// type Span = SimpleSpan; +/// +/// // A custom error type +/// #[derive(Debug, PartialEq)] +/// enum MyError { +/// ExpectedFound { +/// span: Span, +/// expected: Vec>, +/// found: Option, +/// }, +/// NotADigit(Span, char), +/// } +/// +/// impl<'a> Error<'a, &'a str> for MyError { +/// fn merge(mut self, mut other: Self) -> Self { +/// if let (Self::ExpectedFound { expected, .. }, Self::ExpectedFound { expected: expected_other, .. }) = ( +/// &mut self, +/// &mut other, +/// ) { +/// expected.append(expected_other); +/// } +/// self +/// } +/// } +/// +/// impl<'a> LabelError<'a, &'a str, DefaultExpected<'a, char>> for MyError { +/// fn expected_found>>( +/// expected: Iter, +/// found: Option>, +/// span: Span, +/// ) -> Self { +/// Self::ExpectedFound { +/// span, +/// expected: expected +/// .into_iter() +/// .map(|e| e.into_owned()) +/// .collect(), +/// found: found.as_deref().copied(), +/// } +/// } +/// } +/// +/// let numeral = any::<_, extra::Err>().try_map(|c: char, span| match c.to_digit(10) { +/// Some(x) => Ok(x), +/// None => Err(MyError::NotADigit(span, c)), +/// }); +/// +/// assert_eq!(numeral.parse("3").into_result(), Ok(3)); +/// assert_eq!(numeral.parse("7").into_result(), Ok(7)); +/// assert_eq!(numeral.parse("f").into_errors(), vec![MyError::NotADigit((0..1).into(), 'f')]); +/// ``` +// TODO: Add support for more specialised kinds of error: unclosed delimiters, and more +pub trait Error<'a, I: Input<'a>>: + Sized + LabelError<'a, I, DefaultExpected<'a, I::Token>> +{ + /// Merge two errors that point to the same input together, combining their information. + #[inline(always)] + fn merge(self, other: Self) -> Self { + #![allow(unused_variables)] + self + } +} + +/// A ZST error type that tracks only whether a parse error occurred at all. This type is for when +/// you want maximum parse speed, at the cost of all error reporting. +/// +/// # Examples +/// +/// ``` +/// use chumsky::prelude::*; +/// +/// let parser = just::<_, _, extra::Err>("valid"); +/// let error = parser.parse("invalid").into_errors()[0]; +/// +/// assert_eq!(error, EmptyErr::default()); +/// ``` +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone, Default)] +pub struct EmptyErr(()); + +impl<'a, I: Input<'a>> Error<'a, I> for EmptyErr {} + +impl<'a, I: Input<'a>, L> LabelError<'a, I, L> for EmptyErr { + #[inline(always)] + fn expected_found>( + _: E, + _: Option>, + _: I::Span, + ) -> Self { + EmptyErr(()) + } +} + +impl fmt::Display for EmptyErr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "error") + } +} + +/// A very cheap error type that tracks only the error span ([`SimpleSpan`] by default). +/// This type is most useful when you want fast parsing but do not particularly care about the quality of error messages. +/// +/// # Examples +/// +/// ``` +/// use chumsky::prelude::*; +/// +/// let parser = just::<_, _, extra::Err>("+"); +/// let error = parser.parse("-").into_errors()[0]; +/// +/// assert_eq!(error.span(), &SimpleSpan::new((), 0..1)); +/// ``` +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Cheap> { + span: S, +} + +impl Cheap { + /// Create a new [`Cheap`] error. + pub fn new(span: S) -> Self { + Self { span } + } + + /// Get the span than that error related to. + /// + /// If the span type is unspecified, it is [`SimpleSpan`]. + pub fn span(&self) -> &S { + &self.span + } +} + +impl<'a, I: Input<'a>> Error<'a, I> for Cheap {} + +impl<'a, I: Input<'a>, L> LabelError<'a, I, L> for Cheap { + #[inline] + fn expected_found>( + _expected: E, + _found: Option>, + span: I::Span, + ) -> Self { + Self { span } + } +} + +impl fmt::Debug for Cheap +where + S: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "at {:?}", self.span)?; + Ok(()) + } +} + +impl fmt::Display for Cheap +where + S: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} + +/// A simple error type that tracks the error span ([`SimpleSpan`] by default) and found token. This type is most useful when you want fast parsing +/// but do not particularly care about the quality of error messages. +/// +/// # Examples +/// +/// ``` +/// use chumsky::prelude::*; +/// +/// let parser = just::<_, _, extra::Err>>("+"); +/// let error = parser.parse("-").into_errors()[0]; +/// +/// assert_eq!(error.span(), &SimpleSpan::new((), 0..1)); +/// assert_eq!(error.found(), Some(&'-')); +/// ``` +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Simple<'a, T, S = SimpleSpan> { + span: S, + found: Option>, +} + +impl Simple<'_, T, S> { + /// Get the span than that error related to. + /// + /// If the span type is unspecified, it is [`SimpleSpan`]. + pub fn span(&self) -> &S { + &self.span + } + + /// Get the token found by this error when parsing. `None` implies that the error expected the end of input. + pub fn found(&self) -> Option<&T> { + self.found.as_deref() + } +} + +impl<'a, T, S> Simple<'a, T, S> { + /// Create a new [`Simple`] error. + pub fn new(found: Option>, span: S) -> Self { + Self { span, found } + } + + /// Transform this error's tokens using the given function. + /// + /// This is useful when you wish to combine errors from multiple compilation passes (lexing and parsing, say) where + /// the token type for each pass is different (`char` vs `MyToken`, say). + pub fn map_token U>(self, f: F) -> Simple<'a, U, S> + where + T: Clone, + { + Simple { + span: self.span, + found: self.found.map(|found| f(found.into_inner()).into()), + } + } + + /// Transform this error's span using the given function. + /// + /// This is useful when you need to convert spans from one representation to another, + /// such as when enriching or interning spans. + pub fn map_span S2>(self, f: F) -> Simple<'a, T, S2> { + Simple { + span: f(self.span), + found: self.found, + } + } +} + +impl<'a, I: Input<'a>> Error<'a, I> for Simple<'a, I::Token, I::Span> {} + +impl<'a, I: Input<'a>, L> LabelError<'a, I, L> for Simple<'a, I::Token, I::Span> { + #[inline] + fn expected_found>( + _expected: E, + found: Option>, + span: I::Span, + ) -> Self { + Self { span, found } + } +} + +impl fmt::Debug for Simple<'_, T, S> +where + T: fmt::Debug, + S: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "found ")?; + write_token(f, T::fmt, self.found.as_deref())?; + write!(f, " at {:?}", self.span)?; + Ok(()) + } +} + +impl fmt::Display for Simple<'_, T, S> +where + T: fmt::Debug, + S: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self, f) + } +} + +/// An expected pattern for a [`Rich`] error. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[non_exhaustive] +pub enum RichPattern<'a, T> { + /// A specific token. + Token(MaybeRef<'a, T>), + /// A labelled pattern. + Label(Cow<'a, str>), + /// A specific keyword. + Identifier(String), + /// Anything other than the end of input. + Any, + /// Something other than the provided input. + SomethingElse, + /// The end of input. + EndOfInput, +} + +impl<'a, T> TryFrom> for RichPattern<'a, T> { + type Error = (); + fn try_from(expected: DefaultExpected<'a, T>) -> Result { + Ok(match expected { + DefaultExpected::Token(tok) => RichPattern::Token(tok), + DefaultExpected::Any => RichPattern::Any, + DefaultExpected::SomethingElse => RichPattern::SomethingElse, + DefaultExpected::EndOfInput => RichPattern::EndOfInput, + DefaultExpected::NothingElse => return Err(()), + }) + } +} + +impl<'a, Slice: core::fmt::Debug, T> TryFrom> for RichPattern<'a, T> { + type Error = (); + fn try_from(expected: text::TextExpected) -> Result { + Ok(match expected { + text::TextExpected::Whitespace => RichPattern::Label(Cow::Borrowed("whitespace")), + text::TextExpected::InlineWhitespace => { + RichPattern::Label(Cow::Borrowed("inline whitespace")) + } + text::TextExpected::Newline => RichPattern::Label(Cow::Borrowed("newline")), + text::TextExpected::Digit(start, _end) if start > 0 => { + RichPattern::Label(Cow::Borrowed("non-zero digit")) + } + text::TextExpected::Digit(_, _) => RichPattern::Label(Cow::Borrowed("digit")), + text::TextExpected::AnyIdentifier => RichPattern::Label(Cow::Borrowed("identifier")), + text::TextExpected::Identifier(i) => RichPattern::Identifier(alloc::format!("{i:?}")), + text::TextExpected::Int => RichPattern::Label(Cow::Borrowed("int")), + }) + } +} + +impl<'a, T> TryFrom> for RichPattern<'a, T> { + type Error = (); + fn try_from(tok: MaybeRef<'a, T>) -> Result { + Ok(RichPattern::Token(tok)) + } +} + +impl TryFrom<&'static str> for RichPattern<'_, T> { + type Error = (); + fn try_from(label: &'static str) -> Result { + Ok(RichPattern::Label(Cow::Borrowed(label))) + } +} + +impl TryFrom for RichPattern<'_, T> { + type Error = (); + fn try_from(label: String) -> Result { + Ok(RichPattern::Label(Cow::Owned(label))) + } +} + +impl TryFrom for RichPattern<'_, char> { + type Error = (); + fn try_from(c: char) -> Result { + Ok(Self::Token(MaybeRef::Val(c))) + } +} + +impl<'a, T> RichPattern<'a, T> { + /// Transform this pattern's tokens using the given function. + /// + /// This is useful when you wish to combine errors from multiple compilation passes (lexing and parsing, say) where + /// the token type for each pass is different (`char` vs `MyToken`, say). + pub fn map_token U>(self, mut f: F) -> RichPattern<'a, U> + where + T: Clone, + { + match self { + Self::Token(t) => RichPattern::Token(f(t.into_inner()).into()), + Self::Label(l) => RichPattern::Label(l), + Self::Identifier(i) => RichPattern::Identifier(i), + Self::Any => RichPattern::Any, + Self::SomethingElse => RichPattern::SomethingElse, + Self::EndOfInput => RichPattern::EndOfInput, + } + } + + /// Convert this pattern into an owned version of itself by cloning any borrowed internal tokens, if necessary. + pub fn into_owned<'b>(self) -> RichPattern<'b, T> + where + T: Clone, + { + match self { + Self::Token(tok) => RichPattern::Token(tok.into_owned()), + Self::Label(l) => RichPattern::Label(Cow::Owned(l.into_owned())), + Self::Identifier(i) => RichPattern::Identifier(i), + Self::Any => RichPattern::Any, + Self::SomethingElse => RichPattern::SomethingElse, + Self::EndOfInput => RichPattern::EndOfInput, + } + } + + fn write( + &self, + f: &mut fmt::Formatter, + mut fmt_token: impl FnMut(&T, &mut fmt::Formatter<'_>) -> fmt::Result, + ) -> fmt::Result { + match self { + Self::Token(tok) => { + write!(f, "'")?; + fmt_token(tok, f)?; + write!(f, "'") + } + Self::Label(l) => write!(f, "{l}"), + Self::Identifier(i) => write!(f, "'{i}'"), + Self::Any => write!(f, "any"), + Self::SomethingElse => write!(f, "something else"), + Self::EndOfInput => write!(f, "end of input"), + } + } +} + +impl fmt::Debug for RichPattern<'_, T> +where + T: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.write(f, |t, f| write!(f, "{t:?}")) + } +} + +impl fmt::Display for RichPattern<'_, T> +where + T: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.write(f, |t, f| write!(f, "{t}")) + } +} + +/// The reason for a [`Rich`] error. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum RichReason<'a, T, C = String> { + /// An unexpected input was found + ExpectedFound { + /// The tokens expected + expected: Vec>, + /// The tokens found + found: Option>, + }, + /// A custom error + Custom(C), +} + +impl<'a, T, C> RichReason<'a, T, C> { + /// Return the token that was found by this error reason. `None` implies that the end of input was expected. + pub fn found(&self) -> Option<&T> { + match self { + Self::ExpectedFound { found, .. } => found.as_deref(), + Self::Custom(_) => None, + } + } + + /// Convert this reason into an owned version of itself by cloning any borrowed internal tokens, if necessary. + pub fn into_owned<'b>(self) -> RichReason<'b, T, C> + where + T: Clone, + { + match self { + Self::ExpectedFound { found, expected } => RichReason::ExpectedFound { + expected: expected.into_iter().map(RichPattern::into_owned).collect(), + found: found.map(MaybeRef::into_owned), + }, + Self::Custom(msg) => RichReason::Custom(msg), + } + } + + fn take_found(&mut self) -> Option> { + match self { + RichReason::ExpectedFound { found, .. } => found.take(), + RichReason::Custom(_) => None, + } + } + + /// Transform this `RichReason`'s tokens using the given function. + /// + /// This is useful when you wish to combine errors from multiple compilation passes (lexing and parsing, say) where + /// the token type for each pass is different (`char` vs `MyToken`, say). + pub fn map_token U>(self, mut f: F) -> RichReason<'a, U, C> + where + T: Clone, + { + match self { + RichReason::ExpectedFound { expected, found } => RichReason::ExpectedFound { + expected: expected + .into_iter() + .map(|pat| pat.map_token(&mut f)) + .collect(), + found: found.map(|found| f(found.into_inner()).into()), + }, + RichReason::Custom(msg) => RichReason::Custom(msg), + } + } +} + +impl<'a, T, C> RichReason<'a, T, C> +where + C: fmt::Display, +{ + fn inner_fmt( + &self, + f: &mut fmt::Formatter<'_>, + mut fmt_token: impl FnMut(&T, &mut fmt::Formatter<'_>) -> fmt::Result, + mut fmt_span: impl FnMut(&S, &mut fmt::Formatter<'_>) -> fmt::Result, + span: Option<&S>, + context: &[(RichPattern<'a, T>, S)], + ) -> fmt::Result { + match self { + RichReason::ExpectedFound { expected, found } => { + write!(f, "found ")?; + write_token(f, &mut fmt_token, found.as_deref())?; + if let Some(span) = span { + write!(f, " at ")?; + fmt_span(span, f)?; + } + write!(f, " expected ")?; + match &expected[..] { + [] => write!(f, "something else")?, + [expected] => expected.write(f, &mut fmt_token)?, + _ => { + for expected in &expected[..expected.len() - 1] { + expected.write(f, &mut fmt_token)?; + write!(f, ", ")?; + } + write!(f, "or ")?; + expected.last().unwrap().write(f, &mut fmt_token)?; + } + } + } + RichReason::Custom(msg) => { + write!(f, "{msg}")?; + if let Some(span) = span { + write!(f, " at ")?; + fmt_span(span, f)?; + } + } + } + for (l, s) in context { + write!(f, " in ")?; + l.write(f, &mut fmt_token)?; + write!(f, " at ")?; + fmt_span(s, f)?; + } + Ok(()) + } +} + +impl RichReason<'_, T, C> +where + T: PartialEq, +{ + #[inline] + fn flat_merge(self, other: Self) -> Self { + match (self, other) { + // Prefer first error, if ambiguous + (a @ RichReason::Custom(_), _) => a, + (_, b @ RichReason::Custom(_)) => b, + ( + RichReason::ExpectedFound { + expected: mut this_expected, + found, + }, + RichReason::ExpectedFound { + expected: mut other_expected, + .. + }, + ) => { + // Try to avoid allocations if we possibly can by using the longer vector + if other_expected.len() > this_expected.len() { + core::mem::swap(&mut this_expected, &mut other_expected); + } + for expected in other_expected { + if !this_expected[..].contains(&expected) { + this_expected.push(expected); + } + } + RichReason::ExpectedFound { + expected: this_expected, + found, + } + } + } + } +} + +impl fmt::Display for RichReason<'_, T, C> +where + T: fmt::Display, + C: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner_fmt(f, T::fmt, |_: &(), _| Ok(()), None, &[]) + } +} + +/// A rich default error type that tracks error spans, expected inputs, and the actual input found at an error site. +/// +/// Please note that it uses a [`Vec`] to remember expected symbols. If you find this to be too slow, you can +/// implement [`Error`] for your own error type or use [`Simple`] instead. +/// +/// This error type stores a span ([`SimpleSpan`] by default), a [`RichReason`], and a list of expected [`RichPattern`] with their spans. +/// +/// # Examples +/// +/// ``` +/// use chumsky::prelude::*; +/// use chumsky::error::{RichReason, RichPattern}; +/// +/// let parser = one_of::<_, _, extra::Err>>("1234"); +/// let error = parser.parse("5").into_errors()[0].clone(); +/// +/// assert_eq!(error.span(), &SimpleSpan::new((), 0..1)); +/// assert!(matches!(error.reason(), &RichReason::ExpectedFound {..})); +/// assert_eq!(error.found(), Some(&'5')); +/// +/// ``` +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Rich<'a, T, S = SimpleSpan, C = String> { + span: S, + reason: Box>, + context: Vec<(RichPattern<'a, T>, S)>, +} + +impl Rich<'_, T, S, C> +where + C: fmt::Display, +{ + fn inner_fmt( + &self, + f: &mut fmt::Formatter<'_>, + fmt_token: impl FnMut(&T, &mut fmt::Formatter<'_>) -> fmt::Result, + fmt_span: impl FnMut(&S, &mut fmt::Formatter<'_>) -> fmt::Result, + with_spans: bool, + ) -> fmt::Result { + self.reason.inner_fmt( + f, + fmt_token, + fmt_span, + if with_spans { Some(&self.span) } else { None }, + &self.context, + ) + } +} + +impl<'a, T, S, C> Rich<'a, T, S, C> { + /// Create an error with a custom message and span + #[inline] + pub fn custom>(span: S, msg: M) -> Self { + Rich { + span, + reason: Box::new(RichReason::Custom(msg.into())), + context: Vec::new(), + } + } + + /// Get the span associated with this error. + /// + /// If the span type is unspecified, it is [`SimpleSpan`]. + pub fn span(&self) -> &S { + &self.span + } + + /// Get the reason for this error. + pub fn reason(&self) -> &RichReason<'a, T, C> { + &self.reason + } + + /// Take the reason from this error. + pub fn into_reason(self) -> RichReason<'a, T, C> { + *self.reason + } + + /// Get the token found by this error when parsing. `None` implies that the error expected the end of input. + pub fn found(&self) -> Option<&T> { + self.reason.found() + } + + /// Return an iterator over the labelled contexts of this error, from least general to most. + /// + /// 'Context' here means parser patterns that the parser was in the process of parsing when the error occurred. To + /// add labelled contexts, see [`Parser::labelled`]. + pub fn contexts(&self) -> impl Iterator, &S)> { + self.context.iter().map(|(l, s)| (l, s)) + } + + /// Convert this error into an owned version of itself by cloning any borrowed internal tokens, if necessary. + pub fn into_owned<'b>(self) -> Rich<'b, T, S, C> + where + T: Clone, + { + Rich { + reason: Box::new(self.reason.into_owned()), + context: self + .context + .into_iter() + .map(|(p, s)| (p.into_owned(), s)) + .collect(), + ..self + } + } + + /// Get an iterator over the expected items associated with this error + pub fn expected(&self) -> impl ExactSizeIterator> { + match &*self.reason { + RichReason::ExpectedFound { expected, .. } => expected.iter(), + RichReason::Custom(_) => [].iter(), + } + } + + /// Transform this error's tokens using the given function. + /// + /// This is useful when you wish to combine errors from multiple compilation passes (lexing and parsing, say) where + /// the token type for each pass is different (`char` vs `MyToken`, say). + pub fn map_token U>(self, mut f: F) -> Rich<'a, U, S, C> + where + T: Clone, + { + Rich { + span: self.span, + reason: Box::new(self.reason.map_token(&mut f)), + context: self + .context + .into_iter() + .map(|(p, s)| (p.map_token(&mut f), s)) + .collect(), + } + } + + /// Transform this error's spans using the given function. + /// + /// This is useful when you need to convert spans, such as enriching or + /// interning spans. + pub fn map_span S2>(self, mut f: F) -> Rich<'a, T, S2, C> { + Rich { + span: f(self.span), + reason: self.reason, + context: self.context.into_iter().map(|(p, s)| (p, f(s))).collect(), + } + } +} + +impl<'a, I: Input<'a>, C> Error<'a, I> for Rich<'a, I::Token, I::Span, C> +where + I::Token: PartialEq, +{ + #[inline] + fn merge(self, other: Self) -> Self { + let new_reason = self.reason.flat_merge(*other.reason); + Self { + span: self.span, + reason: Box::new(new_reason), + context: self.context, // TOOD: Merge contexts + } + } +} + +impl<'a, I: Input<'a>, L, C> LabelError<'a, I, L> for Rich<'a, I::Token, I::Span, C> +where + I::Token: PartialEq, + L: TryInto>, +{ + #[inline] + fn expected_found>( + expected: E, + found: Option>, + span: I::Span, + ) -> Self { + Self { + span, + reason: Box::new(RichReason::ExpectedFound { + expected: expected + .into_iter() + .filter_map(|tok| tok.try_into().ok()) + .collect(), + found, + }), + context: Vec::new(), + } + } + + #[inline] + fn merge_expected_found>( + mut self, + new_expected: E, + new_found: Option>, + _span: I::Span, + ) -> Self { + match &mut *self.reason { + RichReason::ExpectedFound { expected, found } => { + for new_expected in new_expected { + if let Ok(new_expected) = new_expected.try_into() { + if !expected[..].contains(&new_expected) { + expected.push(new_expected); + } + } + } + *found = found.take().or(new_found); //land + } + RichReason::Custom(_) => {} + } + // TOOD: Merge contexts + self + } + + #[inline] + fn replace_expected_found>( + mut self, + new_expected: E, + new_found: Option>, + span: I::Span, + ) -> Self { + self.span = span; + match &mut *self.reason { + RichReason::ExpectedFound { expected, found } => { + expected.clear(); + expected.extend( + new_expected + .into_iter() + .filter_map(|tok| tok.try_into().ok()), + ); + *found = new_found; + } + _ => { + *self.reason = RichReason::ExpectedFound { + expected: new_expected + .into_iter() + .filter_map(|tok| tok.try_into().ok()) + .collect(), + found: new_found, + }; + } + } + self.context.clear(); + self + } + + #[inline] + fn label_with(&mut self, label: L) { + // Opportunistically attempt to reuse allocations if we can + match &mut *self.reason { + RichReason::ExpectedFound { expected, found: _ } => { + expected.clear(); + expected.extend(label.try_into().ok()); + } + _ => { + *self.reason = RichReason::ExpectedFound { + expected: label.try_into().ok().into_iter().collect(), + found: self.reason.take_found(), + }; + } + } + } + + #[inline] + fn in_context(&mut self, label: L, span: I::Span) { + if let Ok(label) = label.try_into() { + if self.context.iter().all(|(l, _)| l != &label) { + self.context.push((label, span)); + } + } + } +} + +impl fmt::Debug for Rich<'_, T, S, C> +where + T: fmt::Debug, + S: fmt::Debug, + C: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.inner_fmt(f, T::fmt, S::fmt, true) + } +} + +impl fmt::Display for Rich<'_, T, S, C> +where + T: fmt::Display, + S: fmt::Display, + C: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner_fmt(f, T::fmt, S::fmt, false) + } +} + +fn write_token( + f: &mut fmt::Formatter, + mut fmt_token: impl FnMut(&T, &mut fmt::Formatter<'_>) -> fmt::Result, + tok: Option<&T>, +) -> fmt::Result { + match tok { + Some(tok) => { + write!(f, "'")?; + fmt_token(tok, f)?; + write!(f, "'") + } + None => write!(f, "end of input"), + } +} diff --git a/chumsky-0.12.0/src/extension.rs b/chumsky-0.13.0/src/extension.rs similarity index 100% rename from chumsky-0.12.0/src/extension.rs rename to chumsky-0.13.0/src/extension.rs diff --git a/chumsky-0.12.0/src/extra.rs b/chumsky-0.13.0/src/extra.rs similarity index 100% rename from chumsky-0.12.0/src/extra.rs rename to chumsky-0.13.0/src/extra.rs diff --git a/chumsky-0.12.0/src/guide.rs b/chumsky-0.13.0/src/guide.rs similarity index 100% rename from chumsky-0.12.0/src/guide.rs rename to chumsky-0.13.0/src/guide.rs diff --git a/chumsky-0.12.0/src/input.rs b/chumsky-0.13.0/src/input.rs similarity index 99% rename from chumsky-0.12.0/src/input.rs rename to chumsky-0.13.0/src/input.rs index 5cc99f3512..3140c71e01 100644 --- a/chumsky-0.12.0/src/input.rs +++ b/chumsky-0.13.0/src/input.rs @@ -1704,9 +1704,10 @@ impl<'src, 'parse, I: Input<'src>, E: ParserExtra<'src, I>> InputRef<'src, 'pars let _ = self.next_inner(); } + /// Get full slice of raw input. #[cfg_attr(not(feature = "regex"), allow(dead_code))] #[inline] - pub(crate) fn full_slice(&mut self) -> I::Slice + pub fn full_slice(&mut self) -> I::Slice where I: SliceInput<'src>, { diff --git a/chumsky-0.12.0/src/inspector.rs b/chumsky-0.13.0/src/inspector.rs similarity index 98% rename from chumsky-0.12.0/src/inspector.rs rename to chumsky-0.13.0/src/inspector.rs index c1022f217f..a0da628276 100644 --- a/chumsky-0.12.0/src/inspector.rs +++ b/chumsky-0.13.0/src/inspector.rs @@ -120,7 +120,7 @@ impl From for RollbackState { /// This might be useful for representing, say, an arena-style allocator. #[derive(Clone, Default, Debug)] pub struct TruncateState(pub Vec); -impl<'src, T: Clone, I: Input<'src>> Inspector<'src, I> for TruncateState { +impl<'src, T, I: Input<'src>> Inspector<'src, I> for TruncateState { type Checkpoint = usize; #[inline(always)] fn on_token(&mut self, _: &>::Token) {} diff --git a/chumsky-0.12.0/src/label.rs b/chumsky-0.13.0/src/label.rs similarity index 75% rename from chumsky-0.12.0/src/label.rs rename to chumsky-0.13.0/src/label.rs index 87914b2f29..3bedea8f22 100644 --- a/chumsky-0.12.0/src/label.rs +++ b/chumsky-0.13.0/src/label.rs @@ -58,13 +58,20 @@ pub trait LabelError<'src, I: Input<'src>, L>: Sized { } } +#[doc(hidden)] +#[derive(Copy, Clone, Debug)] +pub enum DebugInfoOverride { + Terminal, + NonTerminal, +} + /// See [`Parser::labelled`]. #[derive(Copy, Clone)] pub struct Labelled { pub(crate) parser: A, pub(crate) label: L, pub(crate) is_context: bool, - pub(crate) is_builtin: bool, + pub(crate) debug_info_override: Option, } impl Labelled { @@ -78,8 +85,26 @@ impl Labelled { } } + /// Annotate that the child parser is a terminal symbol. + /// + /// When the unstable `debug` feature is active this hides the child parser from the debug info, + /// but this behavior is not guaranteed and may change. + pub fn as_terminal(mut self) -> Self { + self.debug_info_override = Some(DebugInfoOverride::Terminal); + self + } + + /// Annotate that the child parser is a non-terminal symbol. + /// + /// When the unstable `debug` feature is active this hides the child parser from the debug info, + /// but this behavior is not guaranteed and may change. + pub fn as_non_terminal(mut self) -> Self { + self.debug_info_override = Some(DebugInfoOverride::NonTerminal); + self + } + pub(crate) fn as_builtin(mut self) -> Self { - self.is_builtin = true; + self.debug_info_override = Some(DebugInfoOverride::Terminal); self } } @@ -95,16 +120,19 @@ where #[doc(hidden)] #[cfg(feature = "debug")] fn node_info(&self, scope: &mut debug::NodeScope) -> debug::NodeInfo { - (&self.parser) - .labelled_with(|| self.label.clone()) - .node_info(scope) + LabelledWith { + is_context: self.is_context, + debug_info_override: self.debug_info_override, + ..(&self.parser).labelled_with(|| self.label.clone()) + } + .node_info(scope) } #[inline] fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult { LabelledWith { is_context: self.is_context, - is_builtin: self.is_builtin, + debug_info_override: self.debug_info_override, ..(&self.parser).labelled_with(|| self.label.clone()) } .go::(inp) @@ -118,7 +146,7 @@ pub struct LabelledWith { pub(crate) parser: A, pub(crate) label: F, pub(crate) is_context: bool, - pub(crate) is_builtin: bool, + pub(crate) debug_info_override: Option, pub(crate) phantom: PhantomData, } @@ -132,7 +160,7 @@ where parser: self.parser.clone(), label: self.label.clone(), is_context: self.is_context, - is_builtin: self.is_builtin, + debug_info_override: self.debug_info_override, phantom: PhantomData, } } @@ -156,8 +184,26 @@ impl LabelledWith { } } + /// Annotate that the child parser is a terminal symbol. + /// + /// When the unstable `debug` feature is active this hides the child parser from the debug info, + /// but this behavior is not guaranteed and may change. + pub fn as_terminal(mut self) -> Self { + self.debug_info_override = Some(DebugInfoOverride::Terminal); + self + } + + /// Annotate that the child parser is a non-terminal symbol. + /// + /// When the unstable `debug` feature is active this hides the child parser from the debug info, + /// but this behavior is not guaranteed and may change. + pub fn as_non_terminal(mut self) -> Self { + self.debug_info_override = Some(DebugInfoOverride::NonTerminal); + self + } + pub(crate) fn as_builtin(mut self) -> Self { - self.is_builtin = true; + self.debug_info_override = Some(DebugInfoOverride::Terminal); self } } @@ -181,9 +227,9 @@ where core::any::type_name::().to_string() } } - impl LabelString for T { + impl LabelString for T { default fn label_string(&self) -> String { - format!("{self:?}") + format!("{self}") } } impl LabelString for TextExpected { @@ -211,11 +257,11 @@ where let label_string = (self.label)().label_string(); - if self.is_builtin { - debug::NodeInfo::Builtin(label_string) - } else { - debug::NodeInfo::Labelled(label_string, Box::new(self.parser.node_info(scope))) - } + debug::NodeInfo::Labelled( + label_string, + Box::new(self.parser.node_info(scope)), + self.debug_info_override, + ) } #[inline] @@ -242,11 +288,12 @@ where } if self.is_context { + let after = inp.cursor(); for i in before.err_count..inp.errors.secondary.len() { let label = (self.label)(); let err = &mut inp.errors.secondary[i]; // SAFETY: cursors generated by previous call to `InputRef::next` (or similar). - let span = unsafe { I::span(inp.cache, &before.cursor().inner..&err.pos) }; + let span = unsafe { I::span(inp.cache, &before.cursor().inner..&after.inner) }; err.err.in_context(label, span); } } diff --git a/chumsky-0.13.0/src/lib.rs b/chumsky-0.13.0/src/lib.rs new file mode 100644 index 0000000000..9c336fcc13 --- /dev/null +++ b/chumsky-0.13.0/src/lib.rs @@ -0,0 +1,4407 @@ +#![cfg_attr(not(any(doc, feature = "std", test)), no_std)] +#![cfg_attr(docsrs, feature(doc_cfg), deny(rustdoc::all))] +#![cfg_attr( + feature = "nightly", + feature(never_type, fn_traits, tuple_trait, unboxed_closures, specialization) +)] +#![cfg_attr(feature = "nightly", allow(incomplete_features))] +#![doc = include_str!("../README.md")] +#![deny(missing_docs, clippy::undocumented_unsafe_blocks)] +// A lot of clippy's default lints are silly and annoying +#![allow(clippy::style, clippy::useless_format, clippy::type_complexity)] + +extern crate alloc; +extern crate core; + +macro_rules! go_extra { + ( $O :ty ) => { + #[inline(always)] + fn go_emit(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult { + Parser::::go::(self, inp) + } + #[inline(always)] + fn go_check(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult { + Parser::::go::(self, inp) + } + }; +} + +mod blanket; +pub mod combinator; +pub mod container; +#[cfg(feature = "debug")] +pub mod debug; +#[cfg(feature = "either")] +mod either; +pub mod error; +#[cfg(feature = "extension")] +pub mod extension; +pub mod extra; +#[cfg(docsrs)] +pub mod guide; +pub mod input; +pub mod inspector; +pub mod label; +#[cfg(feature = "lexical-numbers")] +pub mod number; +#[cfg(feature = "pratt")] +pub mod pratt; +pub mod primitive; +mod private; +pub mod recovery; +pub mod recursive; +#[cfg(feature = "regex")] +pub mod regex; +pub mod span; +mod stream; +pub mod text; +#[cfg(feature = "bytes")] +mod tokio; +pub mod util; + +/// Commonly used functions, traits and types. +/// +/// *Listen, three eyes,†he said, “don’t you try to outweird me, I get stranger things than you free with my breakfast +/// cereal.â€* +pub mod prelude { + #[cfg(feature = "lexical-numbers")] + pub use super::number::number; + #[cfg(feature = "regex")] + pub use super::regex::regex; + pub use super::{ + error::{Cheap, EmptyErr, Error as _, Rich, Simple}, + extra, + input::Input, + primitive::{ + any, any_ref, choice, custom, empty, end, group, just, map_ctx, none_of, one_of, set, + todo, + }, + recovery::{nested_delimiters, skip_then_retry_until, skip_until, via_parser}, + recursive::{recursive, Recursive}, + span::{SimpleSpan, Span as _, SpanWrap as _, Spanned}, + text, Boxed, ConfigIterParser, ConfigParser, IterParser, ParseResult, Parser, + }; + pub use crate::{select, select_ref}; +} + +use crate::input::InputOwn; +use alloc::{ + boxed::Box, + rc::{self, Rc}, + string::String, + vec::Vec, +}; +#[cfg(feature = "nightly")] +use core::marker::Tuple; +use core::{ + borrow::Borrow, + cmp::{Eq, Ord, Ordering}, + fmt, + hash::Hash, + marker::PhantomData, + mem::MaybeUninit, + ops::{Deref, DerefMut, Range, RangeFrom}, + panic::Location, + str::FromStr, +}; +#[cfg(feature = "memoization")] +use hashbrown::HashMap; +#[cfg(feature = "serde")] +use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; + +use self::{ + combinator::*, + container::*, + error::Error, + extra::ParserExtra, + input::{ + BorrowInput, Emitter, ExactSizeInput, InputRef, MapExtra, SliceInput, StrInput, ValueInput, + }, + inspector::Inspector, + label::{LabelError, Labelled, LabelledWith}, + prelude::*, + primitive::Any, + private::{Check, Emit, IPResult, Located, MaybeUninitExt, Mode, PResult, Sealed}, + recovery::{RecoverWith, Strategy}, + span::{Span, WrappingSpan}, + text::*, + util::{IntoMaybe, MaybeMut, MaybeRef}, +}; +#[cfg(all(feature = "extension", doc))] +use self::{extension::v1::*, primitive::custom, stream::Stream}; + +/// A type that allows mentioning type parameters *without* all of the customary omission of auto traits that comes +/// with `PhantomData`. +struct EmptyPhantom(core::marker::PhantomData); + +impl EmptyPhantom { + const fn new() -> Self { + Self(core::marker::PhantomData) + } +} + +impl Copy for EmptyPhantom {} +impl Clone for EmptyPhantom { + fn clone(&self) -> Self { + *self + } +} +// SAFETY: This is safe because `EmptyPhantom` doesn't actually contain a `T`. +unsafe impl Send for EmptyPhantom {} +// SAFETY: This is safe because `EmptyPhantom` doesn't actually contain a `T`. +unsafe impl Sync for EmptyPhantom {} +impl Unpin for EmptyPhantom {} +impl core::panic::UnwindSafe for EmptyPhantom {} +impl core::panic::RefUnwindSafe for EmptyPhantom {} + +pub(crate) type DynParser<'src, 'b, I, O, E> = dyn Parser<'src, I, O, E> + 'b; +#[cfg(feature = "pratt")] +pub(crate) type DynOperator<'src, 'b, I, O, E> = dyn pratt::Operator<'src, I, O, E> + 'b; + +/// Labels corresponding to a variety of patterns. +#[derive(Clone, Debug, PartialEq)] +#[non_exhaustive] +pub enum DefaultExpected<'a, T> { + /// A specific token was expected. + Token(MaybeRef<'a, T>), + /// Anything other than the end of input was expected. + Any, + /// Something other than the provided input was expected. + SomethingElse, + /// There was no input that could have satisfied the parser. + NothingElse, + /// The end of input was expected. + EndOfInput, +} + +impl DefaultExpected<'_, T> { + /// Convert this [`DefaultExpected`] into an owned version of itself, cloning any inner references if required. + #[inline] + pub fn into_owned<'b>(self) -> DefaultExpected<'b, T> + where + T: Clone, + { + match self { + Self::Token(tok) => DefaultExpected::Token(tok.into_owned()), + Self::Any => DefaultExpected::Any, + Self::SomethingElse => DefaultExpected::SomethingElse, + Self::NothingElse => DefaultExpected::NothingElse, + Self::EndOfInput => DefaultExpected::EndOfInput, + } + } +} + +/// The result of performing a parse on an input with [`Parser`]. +/// +/// Unlike `Result`, this type is designed to express the fact that generating outputs and errors are not +/// mutually-exclusive operations: it is possible for a parse to produce non-terminal errors (see +/// [`Parser::recover_with`] while still producing useful output). +/// +/// If you don't care for recovered outputs and you with to treat success/failure as a binary, you may use +/// [`ParseResult::into_result`]. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ParseResult { + output: Option, + errs: Vec, +} + +impl ParseResult { + pub(crate) fn new(output: Option, errs: Vec) -> ParseResult { + ParseResult { output, errs } + } + + /// Whether this result contains output + pub fn has_output(&self) -> bool { + self.output.is_some() + } + + /// Whether this result has any errors + pub fn has_errors(&self) -> bool { + !self.errs.is_empty() + } + + /// Get a reference to the output of this result, if it exists + pub fn output(&self) -> Option<&T> { + self.output.as_ref() + } + + /// Get an iterator over the parse errors for this result. The iterator will produce no items if there were no + /// errors. + pub fn errors(&self) -> impl ExactSizeIterator + DoubleEndedIterator { + self.errs.iter() + } + + /// Convert this `ParseResult` into an option containing the output, if any exists + pub fn into_output(self) -> Option { + self.output + } + + /// Convert this `ParseResult` into a vector containing any errors. The vector will be empty if there were no + /// errors. + pub fn into_errors(self) -> Vec { + self.errs + } + + /// Convert this `ParseResult` into a tuple containing the output, if any existed, and errors, if any were + /// encountered. + pub fn into_output_errors(self) -> (Option, Vec) { + (self.output, self.errs) + } + + /// Convert this `ParseResult` into a standard `Result`. This discards output if parsing generated any errors, + /// matching the old behavior of [`Parser::parse`]. + pub fn into_result(self) -> Result> { + if self.errs.is_empty() { + self.output.ok_or(self.errs) + } else { + Err(self.errs) + } + } + + /// Convert this `ParseResult` into the output. If any errors were generated (including non-fatal errors!), a + /// panic will occur instead. + /// + /// The use of this function is discouraged in user-facing code. However, it may be convenient for use in tests. + #[track_caller] + pub fn unwrap(self) -> T + where + E: fmt::Debug, + { + if self.has_errors() { + panic!( + "called `ParseResult::unwrap` on a parse result containing errors: {:?}", + &self.errs + ) + } else { + self.output.expect("parser generated no errors or output") + } + } +} + +/// A trait implemented by parsers. +/// +/// Parsers take inputs of type `I`, which will implement [`Input`]. Refer to the documentation on [`Input`] for examples +/// of common input types. It will then attempt to parse them into a value of type `O`, which may be just about any type. +/// In doing so, they may encounter errors. These need not be fatal to the parsing process: syntactic errors can be +/// recovered from and a valid output may still be generated alongside any syntax errors that were encountered along the +/// way. Usually, this output comes in the form of an +/// [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) (AST). +/// +/// The final type parameter, `E`, is expected to be one of the type in the [`extra`] module, +/// implementing [`ParserExtra`]. This trait is used to encapsulate the various types a parser +/// uses that are not simply its input and output. Refer to the documentation on the [`ParserExtra`] trait +/// for more detail on the contained types. If not provided, it will default to [`extra::Default`], +/// which will have the least overhead, but also the least meaningful errors. +/// +/// The lifetime of the parser is used for zero-copy output - the input is bound by the lifetime, +/// and returned values or parser state may take advantage of this to borrow tokens or slices of the +/// input and hold on to them, if the input supports this. +/// +/// # Stability +/// +/// This trait is not intended to be implemented by downstream users of `chumsky`. While you can technically implement +/// it, doing so is considered to be outside the stability guarantees of the crate. Your code may break with a future, +/// semver-compatible release! Instead of implementing this trait, you should consider other options: +/// +/// 1) Try using combinators like [`Parser::try_map`] and [`Parser::validate`] to implement custom error generation +/// +/// 2) Use [`custom`] to implement your own parsing logic inline within an existing parser +/// +/// 3) Use chumsky's [`extension`] API to write an extension parser that feels like it's native to chumsky +/// +/// 4) If you believe you've found a common use-case that's missing from chumsky, you could open a pull request to +/// implement it in chumsky itself rather than implementing `Parser` yourself. +// #[cfg_attr( +// feature = "nightly", +// diagnostic::on_unimplemented( +// message = "The following is not a parser from `{I}` to `{O}`: `{Self}`", +// label = "This parser is not compatible because it does not implement `Parser<{I}, {O}, E>`", +// note = "You should check that the output types of your parsers are consistent with the combinators you're using", +// ) +// )] +pub trait Parser<'src, I: Input<'src>, O, E: ParserExtra<'src, I> = extra::Default> { + /// Generate debugging information for this parser. + /// + /// This is an unstable feature, and will likely remain so indefinitely. As such, it **does not fall inside the semver + /// guarantees** of the broader crate. It is intended to aid the development of parsers and should not be used as part + /// of production software. + #[cfg(feature = "debug")] + fn debug(&self) -> debug::DebugInfo<'_> { + debug::DebugInfo { + node_info: self.node_info(&mut Default::default()), + phantom: PhantomData, + } + } + + #[doc(hidden)] + #[cfg(feature = "debug")] + fn node_info(&self, _scope: &mut debug::NodeScope) -> debug::NodeInfo { + let ty = core::any::type_name::(); + debug::NodeInfo::Unknown(ty.split_once('<').map_or(ty, |(ty, _)| ty).to_string()) + } + + #[doc(hidden)] + fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult + where + Self: Sized; + + #[doc(hidden)] + fn go_emit(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult; + #[doc(hidden)] + fn go_check(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult; + + /// Parse a stream of tokens, yielding an output if possible, and any errors encountered along the way. + /// + /// If `None` is returned (i.e: parsing failed) then there will *always* be at least one item in the error `Vec`. + /// If you want to include non-default state, use [`Parser::parse_with_state`] instead. + /// + /// Although the signature of this function looks complicated, it's simpler than you think! You can pass a + /// [`&[T]`], a [`&str`], [`Stream`], or anything implementing [`Input`] to it. + fn parse(&self, input: I) -> ParseResult + where + I: Input<'src>, + E::State: Default, + E::Context: Default, + { + self.parse_with_state(input, &mut E::State::default()) + } + + /// Parse a stream of tokens, yielding an output if possible, and any errors encountered along the way. + /// The provided state will be passed on to parsers that expect it, such as [`map_with`](Parser::map_with). + /// + /// If `None` is returned (i.e: parsing failed) then there will *always* be at least one item in the error `Vec`. + /// If you want to just use a default state value, use [`Parser::parse`] instead. + /// + /// Although the signature of this function looks complicated, it's simpler than you think! You can pass a + /// [`&[T]`], a [`&str`], [`Stream`], or anything implementing [`Input`] to it. + fn parse_with_state(&self, input: I, state: &mut E::State) -> ParseResult + where + I: Input<'src>, + E::Context: Default, + { + let mut own = InputOwn::new_state(input, state); + let mut inp = own.as_ref_start(); + let res = self.then_ignore(end()).go::(&mut inp); + let alt = inp.take_alt().map(|alt| alt.err).unwrap_or_else(|| { + let fake_span = inp.span_since(&inp.cursor()); + // TODO: Why is this needed? + E::Error::expected_found([], None, fake_span) + }); + let mut errs = own.into_errs(); + let out = match res { + Ok(out) => Some(out), + Err(()) => { + errs.push(alt); + None + } + }; + ParseResult::new(out, errs) + } + + /// Parse a stream of tokens, ignoring any output, and returning any errors encountered along the way. + /// + /// If parsing failed, then there will *always* be at least one item in the returned `Vec`. + /// If you want to include non-default state, use [`Parser::check_with_state`] instead. + /// + /// Although the signature of this function looks complicated, it's simpler than you think! You can pass a + /// [`&[T]`], a [`&str`], [`Stream`], or anything implementing [`Input`] to it. + fn check(&self, input: I) -> ParseResult<(), E::Error> + where + Self: Sized, + I: Input<'src>, + E::State: Default, + E::Context: Default, + { + self.check_with_state(input, &mut E::State::default()) + } + + /// Parse a stream of tokens, ignoring any output, and returning any errors encountered along the way. + /// + /// If parsing failed, then there will *always* be at least one item in the returned `Vec`. + /// If you want to just use a default state value, use [`Parser::check`] instead. + /// + /// Although the signature of this function looks complicated, it's simpler than you think! You can pass a + /// [`&[T]`], a [`&str`], [`Stream`], or anything implementing [`Input`] to it. + fn check_with_state(&self, input: I, state: &mut E::State) -> ParseResult<(), E::Error> + where + Self: Sized, + I: Input<'src>, + E::Context: Default, + { + let mut own = InputOwn::new_state(input, state); + let mut inp = own.as_ref_start(); + let res = self.then_ignore(end()).go::(&mut inp); + let alt = inp.take_alt().map(|alt| alt.err).unwrap_or_else(|| { + let fake_span = inp.span_since(&inp.cursor()); + // TODO: Why is this needed? + E::Error::expected_found([], None, fake_span) + }); + let mut errs = own.into_errs(); + let out = match res { + Ok(()) => Some(()), + Err(()) => { + errs.push(alt); + None + } + }; + ParseResult::new(out, errs) + } + + /// Convert the output of this parser into a slice of the input, based on the current parser's + /// span. + /// + /// Note: unlike the parser `.repeated().collect()`, this method includes all tokens that are + /// "ignored" by the parser, including any padding, separators, and sub-parsers with + /// [`Parser::ignored`], [`Parser::ignore_then`], and [`Parser::then_ignore`]. + /// + /// # Examples + /// Example with input of type `&str` (token type is `char`). + /// ``` + /// # use chumsky::prelude::*; + /// // Matches a number with underscores that is surrounded by apostrophes. + /// let quoted_numeric = any::<&str, extra::Err>>() + /// .filter(|c: &char| c.is_digit(10)) + /// .separated_by(just("_").repeated().at_most(1)) + /// .to_slice() + /// .padded_by(just("'")); + /// assert_eq!(quoted_numeric.parse("'1_23'").into_result(), Ok("1_23")); + /// ``` + /// Example with input of type `&[u32]` (token type is `u32`). + /// ``` + /// # use chumsky::prelude::*; + /// // Matches even numbers, then ignoring the rest of the input when an odd number is reached. + /// let even_matcher = any::<&[u32], extra::Err>>() + /// .filter(|c: &u32| c % 2 == 0) + /// .repeated() + /// .to_slice() + /// .lazy(); + /// assert_eq!(even_matcher.parse(&[2, 4, 8, 5, 6]).unwrap(), &[2, 4, 8]); + /// ``` + fn to_slice(self) -> ToSlice + where + Self: Sized, + { + ToSlice { + parser: self, + phantom: EmptyPhantom::new(), + } + } + + /// Filter the output of this parser, accepting only inputs that match the given predicate. + /// + /// The output type of this parser is `I`, the input that was found. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// let lowercase = any::<_, extra::Err>>() + /// .filter(char::is_ascii_lowercase) + /// .repeated() + /// .at_least(1) + /// .collect::(); + /// + /// assert_eq!(lowercase.parse("hello").into_result(), Ok("hello".to_string())); + /// assert!(lowercase.parse("Hello").has_errors()); + /// ``` + fn filter bool>(self, f: F) -> Filter + where + Self: Sized, + { + Filter { + parser: self, + filter: f, + } + } + + /// Filter and map the output of this parser, accepting only inputs that get mapped to `Some`. + /// + /// The output type of this parser is `U`. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// #[derive(Debug, PartialEq)] + /// enum Token { + /// Digit(char), // invariant: .is_ascii_digit() + /// Alpha(char), // invariant: .is_alphabetic() + /// } + /// + /// let token = any::<_, extra::Err>>() + /// .filter_map(|c: char| if c.is_ascii_digit() { + /// Some(Token::Digit(c)) + /// } else if c.is_alphabetic() { + /// Some(Token::Alpha(c)) + /// } else { + /// None + /// }); + /// + /// assert_eq!(token.parse("x").into_result(), Ok(Token::Alpha('x'))); + /// assert_eq!(token.parse("5").into_result(), Ok(Token::Digit('5'))); + /// assert!(token.parse("!").has_errors()); + /// ``` + fn filter_map Option>(self, f: F) -> FilterMap + where + Self: Sized, + { + FilterMap { + parser: self, + filter_mapper: f, + phantom: EmptyPhantom::new(), + } + } + + /// Map the output of this parser to another value. + /// + /// The output type of this parser is `U`, the same as the function's output. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// #[derive(Debug, PartialEq)] + /// enum Token { Word(String), Num(u64) } + /// + /// let word = any::<_, extra::Err>>() + /// .filter(|c: &char| c.is_alphabetic()) + /// .repeated().at_least(1) + /// .collect::() + /// .map(Token::Word); + /// + /// let num = any::<_, extra::Err>>() + /// .filter(|c: &char| c.is_ascii_digit()) + /// .repeated().at_least(1) + /// .collect::() + /// .map(|s| Token::Num(s.parse().unwrap())); + /// + /// let token = word.or(num); + /// + /// assert_eq!(token.parse("test").into_result(), Ok(Token::Word("test".to_string()))); + /// assert_eq!(token.parse("42").into_result(), Ok(Token::Num(42))); + /// ``` + fn map U>(self, f: F) -> Map + where + Self: Sized, + { + Map { + parser: self, + mapper: f, + phantom: EmptyPhantom::new(), + } + } + + /// Map the output of this parser to another value, with the opportunity to get extra metadata from the parse like the span or parser state. + /// + /// See the docs for [`MapExtra`] for examples of metadata that can be fetched. + /// + /// The output type of this parser is `U`, the same as the function's output. + /// + /// # Examples + /// + /// Using the span of the output in the mapping function: + /// + /// ``` + /// # use chumsky::prelude::*; + /// + /// // It's common for AST nodes to use a wrapper type that allows attaching span information to them + /// #[derive(Debug, PartialEq)] + /// pub struct Spanned(T, SimpleSpan); + /// + /// let ident = text::ascii::ident::<_, extra::Err>>() + /// .map_with(|ident, e| Spanned(ident, e.span())) // Equivalent to `.map_with_span(|ident, span| Spanned(ident, span))` + /// .padded(); + /// + /// assert_eq!(ident.parse("hello").into_result(), Ok(Spanned("hello", (0..5).into()))); + /// assert_eq!(ident.parse(" hello ").into_result(), Ok(Spanned("hello", (7..12).into()))); + /// ``` + /// + /// Using the parser state in the mapping function to intern strings: + /// + /// ``` + /// # use chumsky::prelude::*; + /// use std::ops::Range; + /// use lasso::{Rodeo, Spur}; + /// + /// // It's common for AST nodes to use interned versions of identifiers + /// // Keys are generally smaller, faster to compare, and can be `Copy` + /// #[derive(Copy, Clone)] + /// pub struct Ident(Spur); + /// + /// let ident = text::ascii::ident::<_, extra::Full, extra::SimpleState, ()>>() + /// .map_with(|ident, e| Ident(e.state().get_or_intern(ident))) + /// .padded() + /// .repeated() + /// .at_least(1) + /// .collect::>(); + /// + /// // Test out parser + /// + /// let mut interner = extra::SimpleState(Rodeo::new()); + /// + /// match ident.parse_with_state("hello", &mut interner).into_result() { + /// Ok(idents) => { + /// assert_eq!(interner.resolve(&idents[0].0), "hello"); + /// } + /// Err(e) => panic!("Parsing Failed: {:?}", e), + /// } + /// + /// match ident.parse_with_state("hello hello", &mut interner).into_result() { + /// Ok(idents) => { + /// assert_eq!(idents[0].0, idents[1].0); + /// } + /// Err(e) => panic!("Parsing Failed: {:?}", e), + /// } + /// ``` + /// + /// Using the parse context in the mapping function: + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// + /// fn palindrome_parser<'src>() -> impl Parser<'src, &'src str, String> { + /// recursive(|chain| { + /// choice(( + /// just(String::new()) + /// .configure(|cfg, ctx: &String| cfg.seq(ctx.clone())) + /// .then_ignore(end()), + /// any() + /// .map_with(|x, e| format!("{x}{}", e.ctx())) + /// .ignore_with_ctx(chain), + /// )) + /// }) + /// .with_ctx(String::new()) + /// } + /// + /// assert_eq!(palindrome_parser().parse("abccba").into_result().as_deref(), Ok("cba")); + /// assert_eq!(palindrome_parser().parse("hello olleh").into_result().as_deref(), Ok(" olleh")); + /// assert!(palindrome_parser().parse("abccb").into_result().is_err()); + /// ``` + fn map_with) -> U>(self, f: F) -> MapWith + where + Self: Sized, + { + MapWith { + parser: self, + mapper: f, + phantom: EmptyPhantom::new(), + } + } + + /// Map the output of this parser to another value. + /// If the output of this parser isn't a tuple, use [`Parser::map`]. + /// + /// The output type of this parser is `U`, the same as the function's output. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::prelude::*; + /// #[derive(Clone, Copy, Debug, PartialEq, Eq)] + /// pub enum Value { + /// One(u8), + /// Two(u8, u8), + /// Three(u8, u8, u8), + /// } + /// + /// fn parser<'src>() -> impl Parser<'src, &'src [u8], Vec> { + /// choice(( + /// just(1).ignore_then(any()).map(Value::One), + /// just(2) + /// .ignore_then(group((any(), any()))) + /// .map_group(Value::Two), + /// just(3) + /// .ignore_then(group((any(), any(), any()))) + /// .map_group(Value::Three), + /// )) + /// .repeated() + /// .collect() + /// } + /// + /// let bytes = &[3, 1, 2, 3, 1, 127, 2, 21, 69]; + /// assert_eq!( + /// parser().parse(bytes).into_result(), + /// Ok(vec![ + /// Value::Three(1, 2, 3), + /// Value::One(127), + /// Value::Two(21, 69) + /// ]) + /// ); + /// ``` + #[cfg(feature = "nightly")] + fn map_group>(self, f: F) -> MapGroup + where + Self: Sized, + O: Tuple, + { + MapGroup { + parser: self, + mapper: f, + phantom: EmptyPhantom::new(), + } + } + + /// Transform the output of this parser to the pattern's span. + /// + /// This is commonly used when you know what pattern you've parsed and are only interested in the span of the + /// pattern. + /// + /// The output type of this parser is `I::Span`. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::prelude::*; + /// + /// // It's common for AST nodes to use a wrapper type that allows attaching span information to them + /// #[derive(Debug, PartialEq)] + /// pub enum Expr<'src> { + /// Int(&'src str, SimpleSpan), + /// // The span is that of the operator, '+' + /// Add(Box>, SimpleSpan, Box>), + /// } + /// + /// let int = text::int::<_, extra::Err>>(10) + /// .to_slice() + /// .map_with(|int, e| Expr::Int(int, e.span())) + /// .padded(); + /// + /// let add_op = just('+').to_span().padded(); + /// let sum = int.foldl( + /// add_op.then(int).repeated(), + /// |a, (op_span, b)| Expr::Add(Box::new(a), op_span, Box::new(b)), + /// ); + /// + /// assert_eq!(sum.parse("42 + 7 + 13").into_result(), Ok(Expr::Add( + /// Box::new(Expr::Add( + /// Box::new(Expr::Int("42", (0..2).into())), + /// (3..4).into(), + /// Box::new(Expr::Int("7", (5..6).into())), + /// )), + /// (7..8).into(), + /// Box::new(Expr::Int("13", (9..11).into())), + /// ))); + /// ``` + fn to_span(self) -> ToSpan + where + Self: Sized, + { + ToSpan { + parser: self, + phantom: EmptyPhantom::new(), + } + } + /// Left-fold the output of the parser into a single value, possibly failing during the reduction. + /// The parser only consumes input from the inner parser until it either completes or the reduction + /// step fails ("short circuting"). + /// + /// The output type of this parser is `A`, the left-hand component of the original parser's output. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// let int = text::int::<_, extra::Err>>(10) + /// .from_str::() + /// .unwrapped(); + /// + /// let sum = int + /// .clone() + /// .try_foldl(just('+').ignore_then(int).repeated(), |a, b, e| a.checked_add(b).ok_or(Simple::new(None, e.span()))); + /// + /// assert_eq!(sum.parse("1+12+3+9").into_result(), Ok(25)); + /// assert_eq!(sum.parse("6").into_result(), Ok(6)); + /// assert!(sum.parse("255+1").has_errors()); // due to u8 overflow + /// ``` + #[cfg_attr(debug_assertions, track_caller)] + fn try_foldl(self, other: B, f: F) -> TryFoldl + where + F: Fn(O, OB, &mut MapExtra<'src, '_, I, E>) -> Result, + B: IterParser<'src, I, OB, E>, + Self: Sized, + { + TryFoldl { + parser_a: self, + parser_b: other, + folder: f, + phantom: EmptyPhantom::new(), + } + } + + /// Wrap the output of this parser in the pattern's span. + /// + /// This is often used to preserve the span of AST nodes for error generation by future passes. + /// + /// The output type of this parser is `::Spanned`. For parsers using [`SimpleSpan`], + /// that means the output type is [`Spanned`]. + fn spanned(self) -> combinator::Spanned + where + Self: Sized, + { + combinator::Spanned { + parser: self, + phantom: EmptyPhantom::new(), + } + } + + /// After a successful parse, apply a fallible function to the output. If the function produces an error, treat it + /// as a parsing error. + /// + /// If you wish parsing of this pattern to continue when an error is generated instead of halting, consider using + /// [`Parser::validate`] instead. + /// + /// The output type of this parser is `U`, the [`Ok`] return value of the function. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::prelude::*; + /// let byte = text::int::<_, extra::Err>>(10) + /// .try_map(|s: &str, span| s + /// .parse::() + /// .map_err(|e| Rich::custom(span, e.to_string()))); + /// + /// assert!(byte.parse("255").has_output()); + /// assert!(byte.parse("256").has_errors()); // Out of range + /// ``` + #[doc(alias = "filter_map")] + fn try_map Result>(self, f: F) -> TryMap + where + Self: Sized, + { + TryMap { + parser: self, + mapper: f, + phantom: EmptyPhantom::new(), + } + } + + /// After a successful parse, apply a fallible function to the output, with the opportunity to get extra metadata. + /// If the function produces an error, treat it as a parsing error. + /// + /// If you wish parsing of this pattern to continue when an error is generated instead of halting, consider using + /// [`Parser::validate`] instead. + /// + /// The output type of this parser is `U`, the [`Ok`] return value of the function. + fn try_map_with) -> Result>( + self, + f: F, + ) -> TryMapWith + where + Self: Sized, + { + TryMapWith { + parser: self, + mapper: f, + phantom: EmptyPhantom::new(), + } + } + + /// Ignore the output of this parser, yielding `()` as an output instead. + /// + /// This can be used to reduce the cost of parsing by avoiding unnecessary allocations (most collections containing + /// [ZSTs](https://doc.rust-lang.org/nomicon/exotic-sizes.html#zero-sized-types-zsts) + /// [do not allocate](https://doc.rust-lang.org/std/vec/struct.Vec.html#guarantees)). For example, it's common to + /// want to ignore whitespace in many grammars (see [`text::whitespace`]). + /// + /// The output type of this parser is `()`. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// // A parser that parses any number of whitespace characters without allocating + /// let whitespace = any::<_, extra::Err>>() + /// .filter(|c: &char| c.is_whitespace()) + /// .ignored() + /// .repeated() + /// .collect::>(); + /// + /// assert_eq!(whitespace.parse(" ").into_result(), Ok(vec![(); 4])); + /// assert!(whitespace.parse(" hello").has_errors()); + /// ``` + fn ignored(self) -> Ignored + where + Self: Sized, + { + Ignored { + parser: self, + phantom: EmptyPhantom::new(), + } + } + + /// Memoize the parser such that later attempts to parse the same input 'remember' the attempt and exit early. + /// + /// If you're finding that certain inputs produce exponential behavior in your parser, strategically applying + /// memoization to a ['garden path'](https://en.wikipedia.org/wiki/Garden-path_sentence) rule is often an effective + /// way to solve the problem. At the limit, applying memoization to all combinators will turn any parser into one + /// with `O(n)`, albeit with very significant per-element overhead and high memory usage. + /// + /// Memoization also works with recursion, so this can be used to write parsers using + /// [left recursion](https://en.wikipedia.org/wiki/Left_recursion). + // TODO: Example + #[cfg(feature = "memoization")] + fn memoized(self) -> Memoized + where + Self: Sized, + { + Memoized { parser: self } + } + + /// Transform all outputs of this parser to a predetermined value. + /// + /// The output type of this parser is `U`, the type of the predetermined value. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// #[derive(Clone, Debug, PartialEq)] + /// enum Op { Add, Sub, Mul, Div } + /// + /// let op = just::<_, _, extra::Err>>('+').to(Op::Add) + /// .or(just('-').to(Op::Sub)) + /// .or(just('*').to(Op::Mul)) + /// .or(just('/').to(Op::Div)); + /// + /// assert_eq!(op.parse("+").into_result(), Ok(Op::Add)); + /// assert_eq!(op.parse("/").into_result(), Ok(Op::Div)); + /// ``` + fn to(self, to: U) -> To + where + Self: Sized, + { + To { + parser: self, + to, + phantom: EmptyPhantom::new(), + } + } + + /// Label this parser with the given label. + /// + /// Labelling a parser makes all errors generated by the parser refer to the label rather than any sub-elements + /// within the parser. For example, labelling a parser for an expression would yield "expected expression" errors + /// rather than "expected integer, string, binary op, etc." errors. + /// + /// Note that this overrides any error messages manually specified through combinators like [`Self::try_map_with`] + // TODO: Example + fn labelled(self, label: L) -> Labelled + where + Self: Sized, + E::Error: LabelError<'src, I, L>, + { + Labelled { + parser: self, + label, + is_context: false, + debug_info_override: Default::default(), + } + } + + /// Label this parser with a generated label, with the opportunity to get extra metadata from the parse like the + /// span or parser state. + /// + /// Labelling a parser makes all errors generated by the parser refer to the label rather than any sub-elements + /// within the parser. For example, labelling a parser for an expression would yield "expected expression" errors + /// rather than "expected integer, string, binary op, etc." errors. + fn labelled_with(self, label: F) -> LabelledWith + where + Self: Sized, + E::Error: LabelError<'src, I, L>, + F: Fn() -> L, + { + LabelledWith { + parser: self, + label, + is_context: false, + debug_info_override: Default::default(), + phantom: PhantomData, + } + } + + /// Parse one thing and then another thing, yielding a tuple of the two outputs. + /// + /// The output type of this parser is `(O, U)`, a combination of the outputs of both parsers. + /// + /// If you instead only need the output of __one__ of the parsers, use [`ignore_then`](Self::ignore_then) + /// or [`then_ignore`](Self::then_ignore). + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// let word = any::<_, extra::Err>>() + /// .filter(|c: &char| c.is_alphabetic()) + /// .repeated() + /// .at_least(1) + /// .collect::(); + /// let two_words = word.then_ignore(just(' ')).then(word); + /// + /// assert_eq!(two_words.parse("dog cat").into_result(), Ok(("dog".to_string(), "cat".to_string()))); + /// assert!(two_words.parse("hedgehog").has_errors()); + /// ``` + fn then>(self, other: B) -> Then + where + Self: Sized, + { + Then { + parser_a: self, + parser_b: other, + phantom: EmptyPhantom::new(), + } + } + + /// Parse one thing and then another thing, yielding only the output of the latter. + /// + /// The output type of this parser is `U`, the same as the second parser. + /// + /// If you instead only need the output of the first parser, use [`then_ignore`](Self::then_ignore). + /// If you need the output of __both__ parsers, use [`then`](Self::then). + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// let zeroes = any::<_, extra::Err>>().filter(|c: &char| *c == '0').ignored().repeated().collect::>(); + /// let digits = any().filter(|c: &char| c.is_ascii_digit()) + /// .repeated() + /// .collect::(); + /// let integer = zeroes + /// .ignore_then(digits) + /// .from_str() + /// .unwrapped(); + /// + /// assert_eq!(integer.parse("00064").into_result(), Ok(64)); + /// assert_eq!(integer.parse("32").into_result(), Ok(32)); + /// ``` + fn ignore_then>(self, other: B) -> IgnoreThen + where + Self: Sized, + { + IgnoreThen { + parser_a: self, + parser_b: other, + phantom: EmptyPhantom::new(), + } + } + + /// Parse one thing and then another thing, yielding only the output of the former. + /// + /// The output type of this parser is `O`, the same as the original parser. + /// + /// If you instead only need the output of the second parser, use [`ignore_then`](Self::ignore_then). + /// If you need the output of __both__ parsers, use [`then`](Self::then). + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// let word = any::<_, extra::Err>>() + /// .filter(|c: &char| c.is_alphabetic()) + /// .repeated() + /// .at_least(1) + /// .collect::(); + /// + /// let punctuated = word + /// .then_ignore(just('!').or(just('?')).or_not()); + /// + /// let sentence = punctuated + /// .padded() // Allow for whitespace gaps + /// .repeated() + /// .collect::>(); + /// + /// assert_eq!( + /// sentence.parse("hello! how are you?").into_result(), + /// Ok(vec![ + /// "hello".to_string(), + /// "how".to_string(), + /// "are".to_string(), + /// "you".to_string(), + /// ]), + /// ); + /// ``` + fn then_ignore>(self, other: B) -> ThenIgnore + where + Self: Sized, + { + ThenIgnore { + parser_a: self, + parser_b: other, + phantom: EmptyPhantom::new(), + } + } + + /// Parse input as part of a token-tree - using an input generated from within the current + /// input. In other words, this parser will attempt to create a *new* input stream from within + /// the one it is being run on, and the parser it was called on will be provided this *new* input. + /// By default, the original parser is expected to consume up to the end of the new stream. To + /// allow only consuming part of the stream, use [`Parser::lazy`] to ignore trailing tokens. + /// + /// The provided parser `P` is expected to have both an input and output type which match the input + /// type of the parser it is called on. As an example, if the original parser takes an input of + /// `Stream>`, `P` will be run first against that input, and is expected to + /// output a new `Stream>` which the original parser will be run against. + /// + /// The output of this parser is `O`, the output of the parser it is called on. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, util::MaybeRef, error::Simple}; + /// #[derive(Debug, Clone, PartialEq)] + /// enum Token<'src> { + /// Struct, + /// Ident(&'src str), + /// Item(&'src str), + /// Group(Vec>), + /// } + /// + /// let group = select_ref! { Token::Group(g) => g.as_slice() }; + /// + /// let ident = select_ref! { Token::Ident(i) => *i }; + /// + /// let items = select_ref! { Token::Item(i) => *i } + /// .repeated() + /// .collect::>() + /// .nested_in(group); + /// + /// let struc = just::<_, _, extra::Err>>(&Token::Struct) + /// .ignore_then(ident) + /// .then(items); + /// + /// let tl = struc + /// .repeated() + /// .collect::>(); + /// + /// let tokens = [ + /// Token::Struct, + /// Token::Ident("foo"), + /// Token::Group(vec![ + /// Token::Item("a"), + /// Token::Item("b"), + /// ]), + /// ]; + /// + /// assert_eq!(tl.parse(&tokens).into_result(), Ok(vec![("foo", vec!["a", "b"])])); + /// ``` + fn nested_in, J, F>(self, other: B) -> NestedIn + where + Self: Sized, + I: 'src, + J: Input<'src>, + F: ParserExtra<'src, J>, + { + NestedIn { + parser_a: self, + parser_b: other, + phantom: EmptyPhantom::new(), + } + } + + /// Parse one thing and then another thing, creating the second parser from the result of + /// the first. If you do need the context in the output, use [`Parser::then_with_ctx`]. + /// + /// The output of this parser is `U`, the result of the second parser + /// + /// Error recovery for this parser may be sub-optimal, as if the first parser succeeds on + /// recovery then the second produces an error, the primary error will point to the location in + /// the second parser which failed, ignoring that the first parser may be the root cause. There + /// may be other pathological errors cases as well. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// let successor = just(b'\0').configure(|cfg, ctx: &u8| cfg.seq(*ctx + 1)); + /// + /// // A parser that parses a single letter and then its successor + /// let successive_letters = one_of::<_, _, extra::Err>>(b'a'..=b'z') + /// .ignore_with_ctx(successor); + /// + /// assert_eq!(successive_letters.parse(b"ab").into_result(), Ok(b'b')); // 'b' follows 'a' + /// assert!(successive_letters.parse(b"ac").has_errors()); // 'c' does not follow 'a' + /// ``` + fn ignore_with_ctx( + self, + then: P, + ) -> IgnoreWithCtx> + where + Self: Sized, + O: 'src, + P: Parser<'src, I, U, extra::Full>, + { + IgnoreWithCtx { + parser: self, + then, + phantom: EmptyPhantom::new(), + } + } + + /// Parse one thing and then another thing, creating the second parser from the result of + /// the first. If you don't need the context in the output, prefer [`Parser::ignore_with_ctx`]. + /// + /// The output of this parser is `(E::Context, O)`, + /// a combination of the context and the output of the parser. + /// + /// Error recovery for this parser may be sub-optimal, as if the first parser succeeds on + /// recovery then the second produces an error, the primary error will point to the location in + /// the second parser which failed, ignoring that the first parser may be the root cause. There + /// may be other pathological errors cases as well. + fn then_with_ctx( + self, + then: P, + ) -> ThenWithCtx> + where + Self: Sized, + O: 'src, + P: Parser<'src, I, U, extra::Full>, + { + ThenWithCtx { + parser: self, + then, + phantom: EmptyPhantom::new(), + } + } + + /// Run the previous contextual parser with the provided context. + /// + /// ``` + /// # use chumsky::prelude::*; + /// # use chumsky::primitive::JustCfg; + /// + /// let generic = just(b'0').configure(|cfg, ctx: &u8| cfg.seq(*ctx)); + /// + /// let parse_a = just::<_, _, extra::Default>(b'b').ignore_then(generic.with_ctx(b'a')); + /// let parse_b = just::<_, _, extra::Default>(b'a').ignore_then(generic.with_ctx(b'b')); + /// + /// assert_eq!(parse_a.parse(b"ba" as &[_]).into_result(), Ok::<_, Vec>(b'a')); + /// assert!(parse_a.parse(b"bb").has_errors()); + /// assert_eq!(parse_b.parse(b"ab" as &[_]).into_result(), Ok(b'b')); + /// assert!(parse_b.parse(b"aa").has_errors()); + /// ``` + fn with_ctx(self, ctx: E::Context) -> WithCtx + where + Self: Sized, + E::Context: Clone, + { + WithCtx { parser: self, ctx } + } + + /// Runs the previous parser with the provided state. + /// + /// This is very uncommonly used and exists mostly for completeness. + /// + /// One possible use-case is 'glueing' together parsers declared in different places with incompatible state types. + /// + /// Note that the state value will be cloned and dropping *during* parsing, so it is recommended to ensure that + /// this is a relatively performant operation. + fn with_state(self, state: State) -> WithState + where + Self: Sized, + State: 'src + Clone, + { + WithState { + parser: self, + state, + } + } + + /// Applies both parsers to the same position in the input, succeeding + /// only if both succeed. The returned value will be that of the first parser, + /// and the input will be at the end of the first parser if `and_is` succeeds. + /// + /// The second parser is allowed to consume more or less input than the first parser, + /// but like its output, how much it consumes won't affect the final result. + /// + /// The motivating use-case is in combination with [`Parser::not`], allowing a parser + /// to consume something only if it isn't also something like an escape sequence or a nested block. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// + /// let escape = just("\\n").to('\n'); + /// + /// // C-style string literal + /// let string = none_of::<_, _, extra::Err>>('"') + /// .and_is(escape.not()) + /// .or(escape) + /// .repeated() + /// .collect::() + /// .padded_by(just('"')); + /// + /// assert_eq!( + /// string.parse("\"wxyz\"").into_result().as_deref(), + /// Ok("wxyz"), + /// ); + /// assert_eq!( + /// string.parse("\"a\nb\"").into_result().as_deref(), + /// Ok("a\nb"), + /// ); + /// ``` + fn and_is(self, other: B) -> AndIs + where + Self: Sized, + B: Parser<'src, I, U, E>, + { + AndIs { + parser_a: self, + parser_b: other, + phantom: EmptyPhantom::new(), + } + } + + /// Parse the pattern surrounded by the given delimiters. + /// + /// The output type of this parser is `O`, the same as the original parser. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// // A LISP-style S-expression + /// #[derive(Debug, PartialEq)] + /// enum SExpr { + /// Ident(String), + /// Num(u64), + /// List(Vec), + /// } + /// + /// let ident = any::<_, extra::Err>>().filter(|c: &char| c.is_alphabetic()) + /// .repeated() + /// .at_least(1) + /// .collect::(); + /// + /// let num = text::int(10) + /// .from_str() + /// .unwrapped(); + /// + /// let s_expr = recursive(|s_expr| s_expr + /// .padded() + /// .repeated() + /// .collect::>() + /// .map(SExpr::List) + /// .delimited_by(just('('), just(')')) + /// .or(ident.map(SExpr::Ident)) + /// .or(num.map(SExpr::Num))); + /// + /// // A valid input + /// assert_eq!( + /// s_expr.parse("(add (mul 42 3) 15)").into_result(), + /// Ok(SExpr::List(vec![ + /// SExpr::Ident("add".to_string()), + /// SExpr::List(vec![ + /// SExpr::Ident("mul".to_string()), + /// SExpr::Num(42), + /// SExpr::Num(3), + /// ]), + /// SExpr::Num(15), + /// ])), + /// ); + /// ``` + fn delimited_by(self, start: B, end: C) -> DelimitedBy + where + Self: Sized, + B: Parser<'src, I, U, E>, + C: Parser<'src, I, V, E>, + { + DelimitedBy { + parser: self, + start, + end, + phantom: EmptyPhantom::new(), + } + } + + /// Parse a pattern, but with an instance of another pattern on either end, yielding the output of the inner. + /// + /// The output type of this parser is `O`, the same as the original parser. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// let ident = text::ascii::ident::<_, extra::Err>>() + /// .padded_by(just('!')); + /// + /// assert_eq!(ident.parse("!hello!").into_result(), Ok("hello")); + /// assert!(ident.parse("hello!").has_errors()); + /// assert!(ident.parse("!hello").has_errors()); + /// assert!(ident.parse("hello").has_errors()); + /// ``` + fn padded_by(self, padding: B) -> PaddedBy + where + Self: Sized, + B: Parser<'src, I, U, E>, + { + PaddedBy { + parser: self, + padding, + phantom: EmptyPhantom::new(), + } + } + + /// Parse one thing or, on failure, another thing. + /// + /// The output of both parsers must be of the same type, because either output can be produced. + /// + /// If both parser succeed, the output of the first parser is guaranteed to be prioritized over the output of the + /// second. + /// + /// If both parsers produce errors, the combinator will attempt to select from or combine the errors to produce an + /// error that is most likely to be useful to a human attempting to understand the problem. The exact algorithm + /// used is left unspecified, and is not part of the crate's semver guarantees, although regressions in error + /// quality should be reported in the issue tracker of the main repository. + /// + /// Please note that long chains of [`Parser::or`] combinators have been known to result in poor compilation times. + /// If you feel you are experiencing this, consider using [`choice`] instead. + /// + /// The output type of this parser is `O`, the output of both parsers. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// let op = just::<_, _, extra::Err>>('+') + /// .or(just('-')) + /// .or(just('*')) + /// .or(just('/')); + /// + /// assert_eq!(op.parse("+").into_result(), Ok('+')); + /// assert_eq!(op.parse("/").into_result(), Ok('/')); + /// assert!(op.parse("!").has_errors()); + /// ``` + fn or(self, other: B) -> Or + where + Self: Sized, + B: Parser<'src, I, O, E>, + { + Or { + choice: choice((self, other)), + } + } + + /// Attempt to parse something, but only if it exists. + /// + /// If parsing of the pattern is successful, the output is `Some(_)`. Otherwise, the output is `None`. + /// + /// The output type of this parser is `Option`. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// let word = any::<_, extra::Err>>().filter(|c: &char| c.is_alphabetic()) + /// .repeated() + /// .at_least(1) + /// .collect::(); + /// + /// let word_or_question = word + /// .then(just('?').or_not()); + /// + /// assert_eq!(word_or_question.parse("hello?").into_result(), Ok(("hello".to_string(), Some('?')))); + /// assert_eq!(word_or_question.parse("wednesday").into_result(), Ok(("wednesday".to_string(), None))); + /// ``` + fn or_not(self) -> OrNot + where + Self: Sized, + { + OrNot { parser: self } + } + + /// Invert the result of the contained parser, failing if it succeeds and succeeding if it fails. + /// The output of this parser is always `()`, the unit type. + /// + /// The motivating case for this is in combination with [`Parser::and_is`], allowing a parser + /// to consume something only if it isn't also something like an escape sequence or a nested block. + /// + /// Caveats: + /// - The error message produced by `not` by default will likely be fairly unhelpful - it can + /// only tell the span that was wrong. + /// - If not careful, it's fairly easy to create non-intuitive behavior due to end-of-input + /// being a valid token for a parser to consume, and as most parsers fail at end of input, + /// `not` will succeed on it. + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// + /// #[derive(Debug, PartialEq)] + /// enum Tree<'src> { + /// Text(&'src str), + /// Group(Vec), + /// } + /// + /// // Arbitrary text, nested in a tree with { ... } delimiters + /// let tree = recursive::<_, _, extra::Err>, _, _>(|tree| { + /// let text = any() + /// .and_is(one_of("{}").not()) + /// .repeated() + /// .at_least(1) + /// .to_slice() + /// .map(Tree::Text); + /// + /// let group = tree + /// .repeated() + /// .collect() + /// .delimited_by(just('{'), just('}')) + /// .map(Tree::Group); + /// + /// text.or(group) + /// }); + /// + /// assert_eq!( + /// tree.parse("{abcd{efg{hijk}lmn{opq}rs}tuvwxyz}").into_result(), + /// Ok(Tree::Group(vec![ + /// Tree::Text("abcd"), + /// Tree::Group(vec![ + /// Tree::Text("efg"), + /// Tree::Group(vec![ + /// Tree::Text("hijk"), + /// ]), + /// Tree::Text("lmn"), + /// Tree::Group(vec![ + /// Tree::Text("opq"), + /// ]), + /// Tree::Text("rs"), + /// ]), + /// Tree::Text("tuvwxyz"), + /// ])), + /// ); + /// ``` + fn not(self) -> Not + where + Self: Sized, + { + Not { + parser: self, + phantom: EmptyPhantom::new(), + } + } + + /// Parse a pattern zero or more times (analog to Regex's `*`). + /// + /// Input is eagerly parsed. Be aware that the parser will accept no occurrences of the pattern too. Consider using + /// [`Repeated::at_least`] instead if you wish to parse a minimum number of elements. + /// + /// The output type of this parser is, by default, `()`. If you want to collect the items into a container + /// (such as a [`Vec`]), use [`IterParser::collect`]. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// let num = any::<_, extra::Err>>() + /// .filter(|c: &char| c.is_ascii_digit()) + /// .repeated() + /// .at_least(1) + /// .collect::() + /// .from_str() + /// .unwrapped(); + /// + /// let sum = num.clone() + /// .foldl(just('+').ignore_then(num).repeated(), |a, b| a + b); + /// + /// assert_eq!(sum.parse("2+13+4+0+5").into_result(), Ok(24)); + /// ``` + #[cfg_attr(debug_assertions, track_caller)] + fn repeated(self) -> Repeated + where + Self: Sized, + { + Repeated { + parser: self, + at_least: 0, + at_most: !0, + #[cfg(debug_assertions)] + location: *Location::caller(), + phantom: EmptyPhantom::new(), + } + } + + /// Parse a pattern, separated by another, any number of times. + /// + /// You can use [`SeparatedBy::allow_leading`] or [`SeparatedBy::allow_trailing`] to allow leading or trailing + /// separators. + /// + /// The output type of this parser is, by default, `()`. If you want to collect the items into a container + /// (such as a [`Vec`]), use [`IterParser::collect`]. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// let shopping = text::ascii::ident::<_, extra::Err>>() + /// .padded() + /// .separated_by(just(',')) + /// .collect::>(); + /// + /// assert_eq!(shopping.parse("eggs").into_result(), Ok(vec!["eggs"])); + /// assert_eq!(shopping.parse("eggs, flour, milk").into_result(), Ok(vec!["eggs", "flour", "milk"])); + /// ``` + /// + /// See [`SeparatedBy::allow_leading`] and [`SeparatedBy::allow_trailing`] for more examples. + #[cfg_attr(debug_assertions, track_caller)] + fn separated_by(self, separator: B) -> SeparatedBy + where + Self: Sized, + B: Parser<'src, I, U, E>, + { + SeparatedBy { + parser: self, + separator, + at_least: 0, + at_most: !0, + allow_leading: false, + allow_trailing: false, + #[cfg(debug_assertions)] + location: *Location::caller(), + phantom: EmptyPhantom::new(), + } + } + + /// Left-fold the output of the parser into a single value. + /// + /// The output of the original parser must be of type `(A, impl IntoIterator)`. + /// + /// The output type of this parser is `A`, the left-hand component of the original parser's output. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// let int = text::int::<_, extra::Err>>(10) + /// .from_str() + /// .unwrapped(); + /// + /// let sum = int + /// .clone() + /// .foldl(just('+').ignore_then(int).repeated(), |a, b| a + b); + /// + /// assert_eq!(sum.parse("1+12+3+9").into_result(), Ok(25)); + /// assert_eq!(sum.parse("6").into_result(), Ok(6)); + /// ``` + #[cfg_attr(debug_assertions, track_caller)] + fn foldl(self, other: B, f: F) -> Foldl + where + F: Fn(O, OB) -> O, + B: IterParser<'src, I, OB, E>, + Self: Sized, + { + Foldl { + parser_a: self, + parser_b: other, + folder: f, + phantom: EmptyPhantom::new(), + } + } + + /// Left-fold the output of the parser into a single value, making use of the parser's state when doing so. + /// + /// The output of the original parser must be of type `(A, impl IntoIterator)`. + /// + /// The output type of this parser is `A`, the left-hand component of the original parser's output. + /// + /// # Examples + /// + /// ## General + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple, extra::SimpleState}; + /// let int = text::int::<_, extra::Full, SimpleState, ()>>(10) + /// .from_str() + /// .unwrapped(); + /// + /// let sum = int + /// .clone() + /// .foldl_with(just('+').ignore_then(int).repeated(), |a, b, e| (a + b) * **e.state()); + /// + /// let mut multiplier = SimpleState(2i32); + /// assert_eq!(sum.parse_with_state("1+12+3+9", &mut multiplier).into_result(), Ok(134)); + /// assert_eq!(sum.parse_with_state("6", &mut multiplier).into_result(), Ok(6)); + /// ``` + /// + /// ## Interning / Arena Allocation + /// + /// This example assumes use of the `slotmap` crate for arena allocation. + /// + /// ``` + /// # use chumsky::prelude::*; + /// use slotmap::{new_key_type, SlotMap}; + /// + /// // Metadata type for node Ids for extra type safety + /// new_key_type! { + /// pub struct NodeId; + /// } + /// + /// // AST nodes reference other nodes with `NodeId`s instead of containing boxed/owned values + /// #[derive(Copy, Clone, Debug, PartialEq)] + /// enum Expr { + /// Int(i32), + /// Add(NodeId, NodeId), + /// } + /// + /// type NodeArena = SlotMap; + /// + /// // Now, define our parser + /// let int = text::int::<&str, extra::Full, extra::SimpleState, ()>>(10) + /// .padded() + /// .map_with(|s, e| + /// // Return the ID of the new integer node + /// e.state().insert(Expr::Int(s.parse().unwrap())) + /// ); + /// + /// let sum = int.foldl_with( + /// just('+').padded().ignore_then(int).repeated(), + /// |a: NodeId, b: NodeId, e| { + /// // Inserting an item into the arena returns its ID + /// e.state().insert(Expr::Add(a, b)) + /// } + /// ); + /// + /// // Test our parser + /// let mut arena = extra::SimpleState(NodeArena::default()); + /// let four_plus_eight = sum.parse_with_state("4 + 8", &mut arena).unwrap(); + /// if let Expr::Add(a, b) = arena[four_plus_eight] { + /// assert_eq!(arena[a], Expr::Int(4)); + /// assert_eq!(arena[b], Expr::Int(8)); + /// } else { + /// panic!("Not an Expr::Add"); + /// } + /// ``` + #[cfg_attr(debug_assertions, track_caller)] + fn foldl_with(self, other: B, f: F) -> FoldlWith + where + F: Fn(O, OB, &mut MapExtra<'src, '_, I, E>) -> O, + B: IterParser<'src, I, OB, E>, + Self: Sized, + { + FoldlWith { + parser_a: self, + parser_b: other, + folder: f, + phantom: EmptyPhantom::new(), + } + } + + /// Parse a pattern. Afterwards, the input stream will be rewound to its original state, as if parsing had not + /// occurred. + /// + /// This combinator is useful for cases in which you wish to avoid a parser accidentally consuming too much input, + /// causing later parsers to fail as a result. A typical use-case of this is that you want to parse something that + /// is not followed by something else. + /// + /// The output type of this parser is `O`, the same as the original parser. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::prelude::*; + /// let just_numbers = text::digits::<_, extra::Err>>(10) + /// .to_slice() + /// .padded() + /// .then_ignore(none_of("+-*/").rewind()) + /// .separated_by(just(',')) + /// .collect::>(); + /// // 3 is not parsed because it's followed by '+'. + /// assert_eq!(just_numbers.lazy().parse("1, 2, 3 + 4").into_result(), Ok(vec!["1", "2"])); + /// ``` + fn rewind(self) -> Rewind + where + Self: Sized, + { + Rewind { parser: self } + } + + /// Make the parser lazy, such that it parses as much of the input as it can finishes successfully, leaving the trailing input untouched. + /// + /// The output type of this parser is `O`, the same as the original parser. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::prelude::*; + /// let digits = one_of::<_, _, extra::Err>>('0'..='9') + /// .repeated() + /// .collect::() + /// .lazy(); + /// + /// assert_eq!(digits.parse("12345abcde").into_result().as_deref(), Ok("12345")); + /// ``` + fn lazy(self) -> Lazy<'src, Self, I, E> + where + Self: Sized, + I: ValueInput<'src>, + { + self.then_ignore(any().repeated()) + } + + /// Parse a pattern, ignoring any amount of whitespace both before and after the pattern. + /// + /// The output type of this parser is `O`, the same as the original parser. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::prelude::*; + /// let ident = text::ascii::ident::<_, extra::Err>>().padded(); + /// + /// // A pattern with no whitespace surrounding it is accepted + /// assert_eq!(ident.parse("hello").into_result(), Ok("hello")); + /// // A pattern with arbitrary whitespace surrounding it is also accepted + /// assert_eq!(ident.parse(" \t \n \t world \t ").into_result(), Ok("world")); + /// ``` + fn padded(self) -> Padded + where + Self: Sized, + I: Input<'src>, + I::Token: Char, + { + Padded { parser: self } + } + + // /// Flatten a nested collection. + // /// + // /// This use-cases of this method are broadly similar to those of [`Iterator::flatten`]. + // /// + // /// The output type of this parser is `Vec`, where the original parser output was + // /// `impl IntoIterator>`. + // fn flatten(self) -> Map Vec> + // where + // Self: Sized, + // O: IntoIterator, + // Inner: IntoIterator, + // { + // self.map(|xs| xs.into_iter().flat_map(|xs| xs.into_iter()).collect()) + // } + + /// Apply a fallback recovery strategy to this parser should it fail. + /// + /// There is no silver bullet for error recovery, so this function allows you to specify one of several different + /// strategies at the location of your choice. Prefer an error recovery strategy that more precisely mirrors valid + /// syntax where possible to make error recovery more reliable. + /// + /// Because chumsky is a [PEG](https://en.m.wikipedia.org/wiki/Parsing_expression_grammar) parser, which always + /// take the first successful parsing route through a grammar, recovering from an error may cause the parser to + /// erroneously miss alternative valid routes through the grammar that do not generate recoverable errors. If you + /// run into cases where valid syntax fails to parse without errors, this might be happening: consider removing + /// error recovery or switching to a more specific error recovery strategy. + /// + /// The output type of this parser is `O`, the same as the original parser. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// #[derive(Debug, PartialEq)] + /// enum Expr<'src> { + /// Error, + /// Int(&'src str), + /// List(Vec>), + /// } + /// + /// let recovery = just::<_, _, extra::Err>>('[') + /// .then(none_of(']').repeated().then(just(']'))); + /// + /// let expr = recursive::<_, _, extra::Err>, _, _>(|expr| expr + /// .separated_by(just(',')) + /// .collect::>() + /// .delimited_by(just('['), just(']')) + /// .map(Expr::List) + /// // If parsing a list expression fails, recover at the next delimiter, generating an error AST node + /// .recover_with(via_parser(recovery.map(|_| Expr::Error))) + /// .or(text::int(10).map(Expr::Int)) + /// .padded()); + /// + /// assert!(expr.parse("five").has_errors()); // Text is not a valid expression in this language... + /// assert_eq!( + /// expr.parse("[1, 2, 3]").into_result(), + /// Ok(Expr::List(vec![Expr::Int("1"), Expr::Int("2"), Expr::Int("3")])), + /// ); // ...but lists and numbers are! + /// + /// // This input has two syntax errors... + /// let res = expr.parse("[[1, two], [3, four]]"); + /// // ...and error recovery allows us to catch both of them! + /// assert_eq!(res.errors().len(), 2); + /// // Additionally, the AST we get back still has useful information. + /// assert_eq!(res.output(), Some(&Expr::List(vec![Expr::Error, Expr::Error]))); + /// ``` + fn recover_with>(self, strategy: S) -> RecoverWith + where + Self: Sized, + { + RecoverWith { + parser: self, + strategy, + } + } + + /// Map the primary error of this parser to another value. + /// + /// This function is most useful when using a custom error type, allowing you to augment errors according to + /// context. + /// + /// The output type of this parser is `O`, the same as the original parser. + // TODO: Map E -> D, not E -> E + fn map_err(self, f: F) -> MapErr + where + Self: Sized, + F: Fn(E::Error) -> E::Error, + { + MapErr { + parser: self, + mapper: f, + } + } + + // /// Map the primary error of this parser to another value, making use of the span from the start of the attempted + // /// to the point at which the error was encountered. + // /// + // /// This function is useful for augmenting errors to allow them to display the span of the initial part of a + // /// pattern, for example to add a "while parsing" clause to your error messages. + // /// + // /// The output type of this parser is `O`, the same as the original parser. + // /// + // // TODO: Map E -> D, not E -> E + // fn map_err_with_span(self, f: F) -> MapErrWithSpan + // where + // Self: Sized, + // F: Fn(E::Error, I::Span) -> E::Error, + // { + // MapErrWithSpan { + // parser: self, + // mapper: f, + // } + // } + + /// Map the primary error of this parser to another value, making use of the parser state. + /// + /// This function is useful for augmenting errors to allow them to include context in non context-free + /// languages, or provide contextual notes on possible causes. + /// + /// The output type of this parser is `O`, the same as the original parser. + /// + // TODO: Map E -> D, not E -> E + fn map_err_with_state(self, f: F) -> MapErrWithState + where + Self: Sized, + F: Fn(E::Error, I::Span, &mut E::State) -> E::Error, + { + MapErrWithState { + parser: self, + mapper: f, + } + } + + /// Map the primary error of this parser to another value, making use of the parser state and + /// context. + /// + /// This function is useful for augmenting errors to allow them to include context in non context-free + /// languages, or provide contextual notes on possible causes. + /// + /// The output type of this parser is `O`, the same as the original parser. + /// + /// Note: Chumsky permits parsers to leave the input in an unspecified state after an error + /// occurs and backtracking begins. As such, the output of [`MapExtra::span`] and + /// [`MapExtra::slice`] is unspecified within this function. + /// + fn map_err_with(self, f: F) -> MapErrWith + where + Self: Sized, + F: Fn(E::Error, &mut MapExtra<'src, '_, I, E>) -> E::Error, + { + MapErrWith { + parser: self, + mapper: f, + } + } + + /// Validate an output, producing non-terminal errors if it does not fulfill certain criteria. + /// The errors will not immediately halt parsing on this path, but instead it will continue, + /// potentially emitting one or more other errors, only failing after the pattern has otherwise + /// successfully, or emitted another terminal error. + /// + /// This function also permits mapping the output to a value of another type, similar to [`Parser::map`]. + /// + /// If you wish parsing of this pattern to halt when an error is generated instead of continuing, consider using + /// [`Parser::try_map`] instead. + /// + /// The output type of this parser is `U`, the result of the validation closure. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::prelude::*; + /// let large_int = text::int::<_, extra::Err>>(10) + /// .from_str() + /// .unwrapped() + /// .validate(|x: u32, e, emitter| { + /// if x < 256 { emitter.emit(Rich::custom(e.span(), format!("{} must be 256 or higher.", x))) } + /// x + /// }); + /// + /// assert_eq!(large_int.parse("537").into_result(), Ok(537)); + /// assert!(large_int.parse("243").into_result().is_err()); + /// ``` + /// + /// To show the difference in behavior from [`Parser::try_map`]: + /// + /// ``` + /// # use chumsky::{text::TextExpected, util::MaybeRef, error::LabelError, prelude::*}; + /// + /// // Start with the same large_int validator + /// let large_int_val = text::int::<_, extra::Err>>(10) + /// .from_str() + /// .unwrapped() + /// .validate(|x: u32, e, emitter| { + /// if x < 256 { emitter.emit(Rich::custom(e.span(), format!("{} must be 256 or higher", x))) } + /// x + /// }); + /// + /// // A try_map version of the same parser + /// let large_int_tm = text::int::<_, extra::Err>>(10) + /// .from_str() + /// .unwrapped() + /// .try_map(|x: u32, span| { + /// if x < 256 { + /// Err(Rich::custom(span, format!("{} must be 256 or higher", x))) + /// } else { + /// Ok(x) + /// } + /// }); + /// + /// // Parser that uses the validation version + /// let multi_step_val = large_int_val.then(text::ascii::ident().padded()); + /// // Parser that uses the try_map version + /// let multi_step_tm = large_int_tm.then(text::ascii::ident().padded()); + /// + /// // On success, both parsers are equivalent + /// assert_eq!( + /// multi_step_val.parse("512 foo").into_result(), + /// Ok((512, "foo")) + /// ); + /// + /// assert_eq!( + /// multi_step_tm.parse("512 foo").into_result(), + /// Ok((512, "foo")) + /// ); + /// + /// // However, on failure, they may produce different errors: + /// assert_eq!( + /// multi_step_val.parse("100 2").into_result(), + /// Err(vec![ + /// Rich::::custom((0..3).into(), "100 must be 256 or higher"), + /// as LabelError<&str, _>>::expected_found([TextExpected::<&str>::AnyIdentifier], Some(MaybeRef::Val('2')), (4..5).into()), + /// ]) + /// ); + /// + /// assert_eq!( + /// multi_step_tm.parse("100 2").into_result(), + /// Err(vec![Rich::::custom((0..3).into(), "100 must be 256 or higher")]) + /// ); + /// ``` + /// + /// As is seen in the above example, validation doesn't prevent the emission of later errors in the + /// same parser, but still produces an error in the output. + /// + fn validate(self, f: F) -> Validate + where + Self: Sized, + F: Fn(O, &mut MapExtra<'src, '_, I, E>, &mut Emitter) -> U, + { + Validate { + parser: self, + validator: f, + phantom: EmptyPhantom::new(), + } + } + + // /// Map the primary error of this parser to a result. If the result is [`Ok`], the parser succeeds with that value. + // /// + // /// Note that, if the closure returns [`Err`], the parser will not consume any input. + // /// + // /// The output type of this parser is `U`, the [`Ok`] type of the result. + // fn or_else(self, f: F) -> OrElse + // where + // Self: Sized, + // F: Fn(E::Error) -> Result, + // { + // OrElse { + // parser: self, + // or_else: f, + // } + // } + + /// Attempt to convert the output of this parser into something else using Rust's [`FromStr`] trait. + /// + /// This is most useful when wanting to convert literal values into their corresponding Rust type, such as when + /// parsing integers. + /// + /// The output type of this parser is `Result`, the result of attempting to parse the output, `O`, into + /// the value `U`. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::prelude::*; + /// let uint64 = text::int::<_, extra::Err>>(10) + /// .from_str::() + /// .unwrapped(); + /// + /// assert_eq!(uint64.parse("7").into_result(), Ok(7)); + /// assert_eq!(uint64.parse("42").into_result(), Ok(42)); + /// ``` + #[allow(clippy::wrong_self_convention)] + fn from_str(self) -> Map Result> + where + Self: Sized, + U: FromStr, + O: AsRef, + { + self.map(|o| o.as_ref().parse()) + } + + /// For parsers that produce a [`Result`] as their output, unwrap the result (panicking if an [`Err`] is + /// encountered). + /// + /// In general, this method should be avoided except in cases where all possibilities that the parser might produce can + /// be parsed by using [`FromStr`] without producing an error. + /// + /// This combinator is not named `unwrap` to avoid confusion: it unwraps *during parsing*, not immediately. + /// + /// The output type of this parser is `U`, the [`Ok`] value of the [`Result`]. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::prelude::*; + /// let boolean = just::<_, _, extra::Err>>("true") + /// .or(just("false")) + /// .from_str::() + /// .unwrapped(); // Cannot panic: the only possible outputs generated by the parser are "true" or "false" + /// + /// assert_eq!(boolean.parse("true").into_result(), Ok(true)); + /// assert_eq!(boolean.parse("false").into_result(), Ok(false)); + /// // Does not panic, because the original parser only accepts "true" or "false" + /// assert!(boolean.parse("42").has_errors()); + /// ``` + #[track_caller] + fn unwrapped(self) -> Unwrapped + where + Self: Sized, + { + Unwrapped { + parser: self, + #[cfg(debug_assertions)] + location: *Location::caller(), + phantom: EmptyPhantom::new(), + } + } + + /// Turn this [`Parser`] into an [`IterParser`] if its output type implements [`IntoIterator`]. + /// + /// The resulting iterable parser will emit each element of the output type in turn. + /// + /// This is *broadly* analogous to functions like [`Vec::into_iter`], but operating at the level of parser outputs. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::prelude::*; + /// // Parses whole integers + /// let num = text::int::<&str, extra::Default>(10).padded().map(|x: &str| x.parse::().unwrap()); + /// // Parses a range like `0..4` into a vector like `[0, 1, 2, 3]` + /// let range = num.then_ignore(just("..")).then(num) + /// .map(|(x, y)| x..y) + /// .into_iter() + /// .collect::>(); + /// // Parses a list of numbers into a vector + /// let list = num.separated_by(just(',')).collect::>(); + /// let set = range.or(list); + /// assert_eq!(set.parse("0, 1, 2, 3").unwrap(), [0, 1, 2, 3]); + /// assert_eq!(set.parse("0..4").unwrap(), [0, 1, 2, 3]); + /// ``` + fn into_iter(self) -> IntoIter + where + Self: Sized, + O: IntoIterator, + { + IntoIter { + parser: self, + phantom: EmptyPhantom::new(), + } + } + + /// Box the parser, yielding a parser that performs parsing through dynamic dispatch. + /// + /// Boxing a parser might be useful for: + /// + /// - Dynamically building up parsers at run-time + /// + /// - Improving compilation times (Rust can struggle to compile code containing very long types) + /// + /// - Passing a parser over an FFI boundary + /// + /// - Getting around compiler implementation problems with long types such as + /// [this](https://github.com/rust-lang/rust/issues/54540). + /// + /// - Places where you need to name the type of a parser + /// + /// Boxing a parser is broadly equivalent to boxing other combinators via dynamic dispatch, such as [`Iterator`]. + /// + /// The output type of this parser is `O`, the same as the original parser. + /// + /// # Examples + /// + /// When not using `boxed`, the following patterns are either impossible or very difficult to express: + /// + /// ```compile_fail + /// # use chumsky::prelude::*; + /// + /// pub trait Parseable: Sized { + /// type Parser<'src>: Parser<'src, &'src str, Self>; + /// + /// fn parser<'src>() -> Self::Parser<'src>; + /// } + /// + /// impl Parseable for i32 { + /// // We *can* write this type, but it will be very impractical, and change on any alterations + /// // to the implementation + /// type Parser<'src> = ???; + /// + /// fn parser<'src>() -> Self::Parser<'src> { + /// todo() + /// } + /// } + /// ``` + /// + /// ```compile_fail + /// # use chumsky::prelude::*; + /// # fn user_input<'src>() -> impl IntoIterator> { [just('b')] } + /// + /// let user_input = user_input(); + /// + /// let mut parser = just('a'); + /// for i in user_input { + /// // Doesn't work due to type mismatch - since every combinator creates a unique type + /// parser = parser.or(i); + /// } + /// + /// let parser = parser.then(just('z')); + /// let _ = parser.parse("b").into_result(); + /// ``` + /// + /// However, with `boxed`, we can express them by making the parsers all share a common type: + /// + /// ``` + /// use chumsky::prelude::*; + /// + /// pub trait Parseable: Sized { + /// fn parser<'src>() -> Boxed<'src, 'src, &'src str, Self>; + /// } + /// + /// impl Parseable for i32 { + /// fn parser<'src>() -> Boxed<'src, 'src, &'src str, Self> { + /// todo().boxed() + /// } + /// } + /// ``` + /// + /// ``` + /// # use chumsky::prelude::*; + /// # fn user_input<'src>() -> impl IntoIterator> { [just('b'), just('c')] } + /// let user_input = user_input(); + /// let mut parser = just('a').boxed(); + /// for i in user_input { + /// // Doesn't work due to type mismatch - since every combinator creates a unique type + /// parser = parser.or(i).boxed(); + /// } + /// let parser = parser.then(just('z')); + /// parser.parse("az").into_result().unwrap(); + /// ``` + /// + fn boxed<'b>(self) -> Boxed<'src, 'b, I, O, E> + where + Self: Sized + 'b, + { + Boxed { + inner: Rc::new(self), + } + } + + /// Simplify the type of the parser using Rust's `impl Trait` syntax. + /// + /// The only reason for using this function is to make Rust's compiler errors easier to debug: it does not change + /// the behaviour of the parser at all, and is in fact just a simple identity function. + #[cfg(feature = "nightly")] + fn simplify(self) -> impl Parser<'src, I, O, E> + where + Self: Sized + 'src, + { + self + } + + /// Have this parser be enabled or disabled depending on context. + /// + /// This method, by itself, does nothing: you must use [`ConfigParser::configure`] to specify when the parser is + /// enabled. + /// + /// # Example + /// + /// ``` + /// # use chumsky::prelude::*; + /// + /// // Our parser can be in two modes depending on context: hexadecimal, or denary + /// #[derive(Clone)] + /// enum Mode { Hex, Dec } + /// + /// let digits = one_of::<_, _, extra::Context>("0123456789") + /// .or(one_of("abcdef").contextual().configure(|cfg, ctx| matches!(ctx, Mode::Hex))) + /// .repeated(); + /// + /// let num = just::<_, _, extra::Default>("0x").ignore_then(digits.with_ctx(Mode::Hex)) + /// // Fallback: when '0x' isn't present, parse using denary mode + /// .or(digits.with_ctx(Mode::Dec)) + /// .to_slice(); + /// + /// assert_eq!(num.parse("0x1a3f5b").into_result(), Ok("0x1a3f5b")); + /// assert_eq!(num.parse("12345").into_result(), Ok("12345")); + /// // Without the '0x' prefix, hexadecimal digits are invalid + /// assert!(num.parse("1a3f5b").has_errors()); + /// ``` + fn contextual(self) -> Contextual + where + Self: Sized, + { + Contextual { inner: self } + } + + /// Use [Pratt parsing](https://en.wikipedia.org/wiki/Operator-precedence_parser#Pratt_parsing) to ergonomically + /// parse this pattern separated by prefix, postfix, and infix operators of various associativites and precedence. + /// + /// Pratt parsing is a powerful technique and is recommended when writing parsers for expressions. + /// + /// # Example + /// + /// See the documentation in [`pratt`] for more extensive examples and details. + /// + /// ``` + /// # use chumsky::prelude::*; + /// use chumsky::pratt::*; + /// + /// let int = text::int::<_, extra::Err>>(10) + /// .from_str() + /// .unwrapped() + /// .padded(); + /// + /// let op = |c| just(c).padded(); + /// + /// let expr = int.pratt(( + /// prefix(2, op('-'), |_, x: i64, _| -x), + /// infix(left(1), op('*'), |x, _, y, _| x * y), + /// infix(left(1), op('/'), |x, _, y, _| x / y), + /// infix(left(0), op('+'), |x, _, y, _| x + y), + /// infix(left(0), op('-'), |x, _, y, _| x - y), + /// )); + /// + /// // Pratt parsing can handle unary operators... + /// assert_eq!(expr.parse("-7").into_result(), Ok(-7)); + /// // ...and infix binary operators... + /// assert_eq!(expr.parse("6 + 3").into_result(), Ok(9)); + /// // ...and arbitrary precedence levels between them. + /// assert_eq!(expr.parse("2 + 3 * -4").into_result(), Ok(-10)); + /// ``` + #[cfg(feature = "pratt")] + fn pratt(self, ops: Ops) -> pratt::Pratt + where + Self: Sized, + { + pratt::Pratt { atom: self, ops } + } +} + +#[cfg(feature = "nightly")] +impl<'src, I, O, E> Parser<'src, I, O, E> for ! +where + I: Input<'src>, + E: ParserExtra<'src, I>, +{ + fn go(&self, _inp: &mut InputRef<'src, '_, I, E>) -> PResult { + *self + } + + go_extra!(O); +} + +/// A [`Parser`] that can be configured with runtime context. +/// +/// This allows for context-sensitive parsing +/// of input. Note that chumsky only supports 'left'-sensitive parsing, where the context for a parser +/// is derived from earlier in the input. +/// +/// Chumsky distinguishes 'state' from 'context'. State is not able to change what input a parser +/// accepts, but may be used to change the contents of the type it emits. In this way state is expected +/// to be idempotent - combinators such as [`Parser::map_with`] are allowed to not call the +/// provided closure at all if they don't emit any output. Context and configuration, on the other hand, +/// is used to change what kind of input a parser may accept, and thus must always be evaluated. Context +/// isn't usable in any map combinator however - while it may affect accepted input, it is not expected +/// to change the final result outside of how it changes what the parser itself returns. +/// +/// Not all parsers currently support configuration. If you feel like you need a parser to be configurable +/// and it isn't currently, please open an issue on the issue tracker of the main repository. +pub trait ConfigParser<'src, I, O, E>: Parser<'src, I, O, E> +where + I: Input<'src>, + E: ParserExtra<'src, I>, +{ + /// A type describing the configurable aspects of the parser. + type Config: Default; + + #[doc(hidden)] + fn go_cfg( + &self, + inp: &mut InputRef<'src, '_, I, E>, + cfg: Self::Config, + ) -> PResult; + + #[doc(hidden)] + #[inline(always)] + fn go_emit_cfg( + &self, + inp: &mut InputRef<'src, '_, I, E>, + cfg: Self::Config, + ) -> PResult { + self.go_cfg::(inp, cfg) + } + #[doc(hidden)] + #[inline(always)] + fn go_check_cfg( + &self, + inp: &mut InputRef<'src, '_, I, E>, + cfg: Self::Config, + ) -> PResult { + self.go_cfg::(inp, cfg) + } + + /// A combinator that allows configuration of the parser from the current context. Context + /// is most often derived from [`Parser::ignore_with_ctx`], [`Parser::then_with_ctx`] or [`map_ctx`], + /// and is how chumsky supports parsing things such as indentation-sensitive grammars. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::prelude::*; + /// + /// let int = text::int::<_, extra::Err>>(10) + /// .from_str() + /// .unwrapped(); + /// + /// // By default, accepts any number of items + /// let item = text::ascii::ident() + /// .padded() + /// .repeated(); + /// + /// // With configuration, we can declare an exact number of items based on a prefix length + /// let len_prefixed_arr = int + /// .ignore_with_ctx(item.configure(|repeat, ctx| repeat.exactly(*ctx)).collect::>()); + /// + /// assert_eq!( + /// len_prefixed_arr.parse("2 foo bar").into_result(), + /// Ok(vec!["foo", "bar"]), + /// ); + /// + /// assert_eq!( + /// len_prefixed_arr.parse("0").into_result(), + /// Ok(vec![]), + /// ); + /// + /// len_prefixed_arr.parse("3 foo bar baz bam").into_result().unwrap_err(); + /// len_prefixed_arr.parse("3 foo bar").into_result().unwrap_err(); + /// ``` + fn configure(self, cfg: F) -> Configure + where + Self: Sized, + F: Fn(Self::Config, &E::Context) -> Self::Config, + { + Configure { parser: self, cfg } + } +} + +/// Data that is needed by IterParser when debug_assertions are enabled. +#[derive(Clone, Copy)] +pub struct IterParserDebug { + #[cfg(debug_assertions)] + pub(crate) nonconsumption_is_ok: bool, +} + +impl IterParserDebug { + #[inline(always)] + pub(crate) fn new(#[allow(unused_variables)] nonconsumption_is_ok: bool) -> Self { + Self { + #[cfg(debug_assertions)] + nonconsumption_is_ok, + } + } +} + +/// An iterator that wraps an iterable parser. See [`IterParser::parse_iter`]. +#[cfg(feature = "unstable")] +pub struct ParseIter< + 'a, + 'src, + 'iter, + P: IterParser<'src, I, O, E>, + I: Input<'src>, + O, + E: ParserExtra<'src, I>, +> { + parser: &'a mut P, + own: InputOwn<'src, 'iter, I, E>, + iter_state: Option>, + #[allow(dead_code)] + phantom: EmptyPhantom<(&'src (), O)>, +} + +#[cfg(feature = "unstable")] +impl<'a, 'src, P, I: Input<'src>, O, E: ParserExtra<'src, I>> Iterator + for ParseIter<'a, 'src, '_, P, I, O, E> +where + P: IterParser<'src, I, O, E>, +{ + type Item = O; + + fn next(&mut self) -> Option { + let mut inp = self.own.as_ref_start(); + let parser = &self.parser; + + let iter_state = match &mut self.iter_state { + Some(state) => state, + None => { + let state = parser.make_iter::(&mut inp).ok()?; + self.iter_state = Some(state); + self.iter_state.as_mut().unwrap() + } + }; + + let res = parser.next::(&mut inp, iter_state, IterParserDebug::new(true)); + // TODO: Avoid clone + self.own.start = inp.cursor().inner; + res.ok().and_then(|res| res) + } +} + +/// An iterable equivalent of [`Parser`], i.e: a parser that generates a sequence of outputs. +pub trait IterParser<'src, I, O, E = extra::Default> +where + I: Input<'src>, + E: ParserExtra<'src, I>, +{ + #[doc(hidden)] + type IterState + where + I: 'src; + + #[doc(hidden)] + fn make_iter( + &self, + inp: &mut InputRef<'src, '_, I, E>, + ) -> PResult>; + #[doc(hidden)] + fn next( + &self, + inp: &mut InputRef<'src, '_, I, E>, + state: &mut Self::IterState, + debug: IterParserDebug, + ) -> IPResult; + + #[doc(hidden)] + #[cfg(feature = "debug")] + fn node_info(&self, _scope: &mut debug::NodeScope) -> debug::NodeInfo { + let ty = core::any::type_name::(); + debug::NodeInfo::Unknown(ty.split_once('<').map_or(ty, |(ty, _)| ty).to_string()) + } + + /// Collect this iterable parser into a container that implements [`FromIterator`]. + /// + /// This is commonly useful for collecting parsers that output many values into containers of various kinds: + /// [`Vec`]s, [`String`]s, or even [`HashMap`]s. This method is analogous to [`Iterator::collect`]. + /// + /// The output type of this iterable parser is `C`, the type being collected into. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// let word = any::<_, extra::Err>>().filter(|c: &char| c.is_alphabetic()) // This parser produces an output of `char` + /// .repeated() // This parser is iterable (i.e: implements `IterParser`) + /// .collect::(); // We collect the `char`s into a `String` + /// + /// assert_eq!(word.parse("hello").into_result(), Ok("hello".to_string())); + /// ``` + #[cfg_attr(debug_assertions, track_caller)] + fn collect>(self) -> Collect + where + Self: Sized, + { + Collect { + parser: self, + phantom: EmptyPhantom::new(), + } + } + + /// Collect this iterable parser into a [`ContainerExactly`]. + /// + /// This is useful for situations where the number of items to consume is statically known. + /// A common use-case is collecting into an array. + /// + /// The output type of this iterable parser if `C`, the type being collected into. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// let three_digit = any::<_, extra::Err>>().filter(|c: &char| c.is_numeric()) + /// .repeated() + /// .collect_exactly::<[_; 3]>(); + /// + /// assert_eq!(three_digit.parse("123").into_result(), Ok(['1', '2', '3'])); + /// assert!(three_digit.parse("12").into_result().is_err()); + /// assert!(three_digit.parse("1234").into_result().is_err()); + /// ``` + fn collect_exactly>(self) -> CollectExactly + where + Self: Sized, + { + CollectExactly { + parser: self, + phantom: EmptyPhantom::new(), + } + } + + /// Collect this iterable parser into a [`usize`], outputting the number of elements that were parsed. + /// + /// This is sugar for [`.collect::()`](Self::collect). + /// + /// # Examples + /// + /// ``` + /// # use chumsky::prelude::*; + /// + /// // Counts how many chess squares are in the input. + /// let squares = one_of::<_, _, extra::Err>>('a'..='z').then(one_of('1'..='8')).padded().repeated().count(); + /// + /// assert_eq!(squares.parse("a1 b2 c3").into_result(), Ok(3)); + /// assert_eq!(squares.parse("e5 e7 c6 c7 f6 d5 e6 d7 e4 c5 d6 c4 b6 f5").into_result(), Ok(14)); + /// assert_eq!(squares.parse("").into_result(), Ok(0)); + /// ``` + fn count(self) -> Count + where + Self: Sized, + { + Count { + parser: self, + phantom: EmptyPhantom::new(), + } + } + + /// Enumerate outputs of this iterable parser. + /// + /// This function behaves in a similar way to [`Iterator::enumerate`]. + /// + /// The output type of this iterable parser is `(usize, O)`. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// let word = text::ascii::ident::<_, extra::Err>>() + /// .padded() + /// .repeated() // This parser is iterable (i.e: implements `IterParser`) + /// .enumerate() + /// .collect::>(); + /// + /// assert_eq!(word.parse("hello world").into_result(), Ok(vec![(0, "hello"), (1, "world")])); + /// ``` + fn enumerate(self) -> Enumerate + where + Self: Sized, + { + Enumerate { + parser: self, + phantom: EmptyPhantom::new(), + } + } + + /// Fold the output of the parser into the given accumulator. + /// + /// The output type of this iterable parser is `B`, the accumulator type. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// let int = text::int::<_, extra::Err>>(10) + /// .from_str::() + /// .unwrapped(); + /// + /// let sum = int + /// .padded() + /// .repeated() + /// .fold(0, |sum, x| sum + x); + /// + /// assert_eq!(sum.parse("3 7 2").into_result(), Ok(12)); + /// assert_eq!(sum.parse("").into_result(), Ok(0)); + /// assert_eq!(sum.parse("42 1").into_result(), Ok(43)); + /// ``` + #[cfg_attr(debug_assertions, track_caller)] + fn fold(self, init: B, f: F) -> Fold + where + B: Clone, + F: Fn(B, O) -> B, + Self: Sized, + { + Fold { + parser: self, + init, + folder: f, + phantom: EmptyPhantom::new(), + } + } + + /// Right-fold the output of the parser into a single value. + /// + /// The output type of this iterable parser is `B`, the right-hand component of the original parser's output. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple}; + /// let int = text::int::<_, extra::Err>>(10) + /// .from_str() + /// .unwrapped(); + /// + /// let signed = just('+').to(1) + /// .or(just('-').to(-1)) + /// .repeated() + /// .foldr(int, |a, b| a * b); + /// + /// assert_eq!(signed.parse("3").into_result(), Ok(3)); + /// assert_eq!(signed.parse("-17").into_result(), Ok(-17)); + /// assert_eq!(signed.parse("--+-+-5").into_result(), Ok(5)); + /// ``` + #[cfg_attr(debug_assertions, track_caller)] + fn foldr(self, other: B, f: F) -> Foldr + where + F: Fn(O, OA) -> OA, + B: Parser<'src, I, OA, E>, + Self: Sized, + { + Foldr { + parser_a: self, + parser_b: other, + folder: f, + phantom: EmptyPhantom::new(), + } + } + + /// Right-fold the output of the parser into a single value, making use of the parser's state when doing so. + /// + /// The output type of this parser is `B`, the right-hand component of the original parser's output. + /// + /// # Examples + /// + /// ``` + /// # use chumsky::{prelude::*, error::Simple, extra::SimpleState}; + /// let int = text::int::<_, extra::Full, SimpleState, ()>>(10) + /// .from_str() + /// .unwrapped(); + /// + /// let signed = just('+').to(1) + /// .or(just('-').to(-1)) + /// .repeated() + /// .foldr_with(int, |a, b, e| { + /// **e.state() += 1; + /// a * b + /// }); + /// + /// // Test our parser + /// let mut folds = SimpleState(0i32); + /// assert_eq!(signed.parse_with_state("3", &mut folds).into_result(), Ok(3)); + /// assert_eq!(signed.parse_with_state("-17", &mut folds).into_result(), Ok(-17)); + /// assert_eq!(signed.parse_with_state("--+-+-5", &mut folds).into_result(), Ok(5)); + /// ``` + /// + /// + #[cfg_attr(debug_assertions, track_caller)] + fn foldr_with(self, other: B, f: F) -> FoldrWith + where + F: Fn(O, OA, &mut MapExtra<'src, '_, I, E>) -> OA, + B: Parser<'src, I, OA, E>, + Self: Sized, + { + FoldrWith { + parser_a: self, + parser_b: other, + folder: f, + phantom: EmptyPhantom::new(), + } + } + + /// TODO + #[cfg(feature = "nightly")] + fn flatten(self) -> Flatten + where + O: IntoIterator, + Self: Sized, + { + Flatten { + parser: self, + phantom: EmptyPhantom::new(), + } + } + + /// Parse the given input with this [`IterParser`]. + /// + /// The provided closure gives access to an iterator, which may be used to iterate the parser's outputs. Once the closure has terminated, a [`ParseResult`] will be returned containing the output of the closure and any parse errors that were encountered during iteration. + #[cfg(feature = "unstable")] + fn parse_iter(&mut self, input: I, f: F) -> ParseResult + where + Self: IterParser<'src, I, O, E> + Sized, + I: Input<'src>, + E::State: Default, + E::Context: Default, + F: FnOnce(&mut ParseIter<'_, 'src, '_, Self, I, O, E>) -> R, + { + self.parse_iter_with_state(input, &mut Default::default(), f) + } + + /// Parse the given input with this [`IterParser`], using the given state. + /// + /// See [`IterParser::parse_iter`] for more information. + #[cfg(feature = "unstable")] + fn parse_iter_with_state( + &mut self, + input: I, + state: &mut E::State, + f: F, + ) -> ParseResult + where + Self: IterParser<'src, I, O, E> + Sized, + I: Input<'src>, + E::Context: Default, + F: FnOnce(&mut ParseIter<'_, 'src, '_, Self, I, O, E>) -> R, + { + let mut iter = ParseIter { + parser: self, + own: InputOwn::new_state(input, state), + iter_state: None, + phantom: EmptyPhantom::new(), + }; + let out = f(&mut iter); + let mut inp = iter.own.as_ref_start(); + let res = end().go::(&mut inp); + let alt = inp.take_alt().map(|alt| alt.err).unwrap_or_else(|| { + let fake_span = inp.span_since(&inp.cursor()); + // TODO: Why is this needed? + E::Error::expected_found([], None, fake_span) + }); + let mut errs = iter.own.into_errs(); + if res.is_err() { + errs.push(alt); + } + + ParseResult::new(Some(out), errs) + } +} + +/// An iterable equivalent of [`ConfigParser`], i.e: a parser that generates a sequence of outputs and +/// can be configured at runtime. +pub trait ConfigIterParser<'src, I, O, E = extra::Default>: IterParser<'src, I, O, E> +where + I: Input<'src>, + E: ParserExtra<'src, I>, +{ + /// A trait describing the configurable aspects of the iterable parser. + type Config: Default; + + #[doc(hidden)] + fn next_cfg( + &self, + inp: &mut InputRef<'src, '_, I, E>, + state: &mut Self::IterState, + cfg: &Self::Config, + debug: IterParserDebug, + ) -> IPResult; + + /// A combinator that allows configuration of the parser from the current context + fn configure(self, cfg: F) -> IterConfigure + where + Self: Sized, + F: Fn(Self::Config, &E::Context) -> Self::Config, + { + IterConfigure { + parser: self, + cfg, + phantom: EmptyPhantom::new(), + } + } + + /// A combinator that allows fallible configuration of the parser from the current context - + /// if an error is returned, parsing fails. + fn try_configure(self, cfg: F) -> TryIterConfigure + where + Self: Sized, + F: Fn(Self::Config, &E::Context, I::Span) -> Result, + { + TryIterConfigure { + parser: self, + cfg, + phantom: EmptyPhantom::new(), + } + } +} + +/// See [`Parser::boxed`]. +/// +/// Due to current implementation details, the inner value is not, in fact, a [`Box`], but is an [`Rc`] to facilitate +/// efficient cloning. This is likely to change in the future. Unlike [`Box`], [`Rc`] has no size guarantees: although +/// it is *currently* the same size as a raw pointer. +// TODO: Don't use an Rc (why?) +pub struct Boxed<'src, 'b, I: Input<'src>, O, E: ParserExtra<'src, I> = extra::Default> { + inner: Rc>, +} + +impl<'src, I: Input<'src>, O, E: ParserExtra<'src, I>> Clone for Boxed<'src, '_, I, O, E> { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + } + } +} + +impl<'src, I, O, E> Parser<'src, I, O, E> for Boxed<'src, '_, I, O, E> +where + I: Input<'src>, + E: ParserExtra<'src, I>, +{ + #[doc(hidden)] + #[cfg(feature = "debug")] + fn node_info(&self, scope: &mut debug::NodeScope) -> debug::NodeInfo { + self.inner.node_info(scope) + } + + #[inline] + fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult { + M::invoke(&*self.inner, inp) + } + + fn boxed<'c>(self) -> Boxed<'src, 'c, I, O, E> + where + Self: Sized + 'c, + { + // Never double-box parsers + self + } + + go_extra!(O); +} + +impl<'src, I, O, E, T> Parser<'src, I, O, E> for ::alloc::boxed::Box +where + I: Input<'src>, + E: ParserExtra<'src, I>, + T: Parser<'src, I, O, E>, +{ + #[inline] + fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult + where + Self: Sized, + { + T::go::(self, inp) + } + + go_extra!(O); +} + +impl<'src, I, O, E, T> Parser<'src, I, O, E> for ::alloc::rc::Rc +where + I: Input<'src>, + E: ParserExtra<'src, I>, + T: Parser<'src, I, O, E>, +{ + #[inline] + fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult + where + Self: Sized, + { + T::go::(self, inp) + } + + go_extra!(O); +} + +impl<'src, I, O, E, T> Parser<'src, I, O, E> for ::alloc::sync::Arc +where + I: Input<'src>, + E: ParserExtra<'src, I>, + T: Parser<'src, I, O, E>, +{ + #[inline] + fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult + where + Self: Sized, + { + T::go::(self, inp) + } + + go_extra!(O); +} + +/// Create a parser that selects one or more input patterns and map them to an output value. +/// +/// This is most useful when turning the tokens of a previous compilation pass (such as lexing) into data that can be +/// used for parsing, although it can also generally be used to select inputs and map them to outputs. Any unmapped +/// input patterns will become syntax errors, just as with [`Parser::filter`]. +/// +/// Internally, [`select!`] is very similar to a single-token [`Parser::filter`] and thinking of it as such might make +/// it less confusing. +/// +/// `select!` requires that tokens implement [`Clone`] and the input type implements [`ValueInput`]. If you're trying +/// to access tokens referentially (for the sake of nested parsing, or simply because you want to avoid cloning the +/// token), see [`select_ref!`]. +/// +/// # Examples +/// +/// `select!` is syntactically similar to a `match` expression and has support for +/// [pattern guards](https://doc.rust-lang.org/reference/expressions/match-expr.html#match-guards): +/// +/// ``` +/// # use chumsky::{prelude::*, error::Simple}; +/// #[derive(Clone)] +/// enum Token<'src> { Ident(&'src str) } +/// +/// enum Expr<'src> { Local(&'src str), Null, True, False } +/// +/// # let _: chumsky::primitive::Select<_, &[Token], Expr, extra::Default> = +/// select! { +/// Token::Ident(s) if s == "true" => Expr::True, +/// Token::Ident(s) if s == "false" => Expr::False, +/// Token::Ident(s) if s == "null" => Expr::Null, +/// Token::Ident(s) => Expr::Local(s), +/// } +/// # ; +/// ``` +/// +/// If you require access to the token's span or other metadata, you may add an argument after a pattern to gain access +/// to it (see the docs for [`Parser::map_with`] and [`MapExtra`]): +/// +/// ``` +/// # use chumsky::{prelude::*, error::Simple}; +/// #[derive(Clone)] +/// enum Token<'src> { Num(f64), Str(&'src str) } +/// +/// enum Expr<'src> { Num(f64), Str(&'src str) } +/// +/// type Span = SimpleSpan; +/// +/// impl<'src> Expr<'src> { +/// fn spanned(self, span: Span) -> (Self, Span) { (self, span) } +/// } +/// +/// # let _: chumsky::primitive::Select<_, &[Token], (Expr, Span), extra::Default> = +/// select! { +/// Token::Num(x) = e => Expr::Num(x).spanned(e.span()), +/// Token::Str(s) = e => Expr::Str(s).spanned(e.span()), +/// } +/// # ; +/// ``` +/// +/// ``` +/// # use chumsky::{prelude::*, error::Simple}; +/// // The type of our parser's input (tokens like this might be emitted by your compiler's lexer) +/// #[derive(Clone, Debug, PartialEq)] +/// enum Token { +/// Num(u64), +/// Bool(bool), +/// LParen, +/// RParen, +/// } +/// +/// // The type of our parser's output, a syntax tree +/// #[derive(Debug, PartialEq)] +/// enum Ast { +/// Num(u64), +/// Bool(bool), +/// List(Vec), +/// } +/// +/// // Our parser converts a stream of input tokens into an AST +/// // `select!` is used to deconstruct some of the tokens and turn them into AST nodes +/// let ast = recursive::<_, _, extra::Err>, _, _>(|ast| { +/// let literal = select! { +/// Token::Num(x) => Ast::Num(x), +/// Token::Bool(x) => Ast::Bool(x), +/// }; +/// +/// literal.or(ast +/// .repeated() +/// .collect() +/// .delimited_by(just(Token::LParen), just(Token::RParen)) +/// .map(Ast::List)) +/// }); +/// +/// use Token::*; +/// assert_eq!( +/// ast.parse(&[LParen, Num(5), LParen, Bool(false), Num(42), RParen, RParen]).into_result(), +/// Ok(Ast::List(vec![ +/// Ast::Num(5), +/// Ast::List(vec![ +/// Ast::Bool(false), +/// Ast::Num(42), +/// ]), +/// ])), +/// ); +/// ``` +#[macro_export] +macro_rules! select { + ($($(#[$attr:meta])? $p:pat $(= $extra:ident)? $(if $guard:expr)? $(=> $out:expr)?),+ $(,)?) => ({ + $crate::primitive::select( + move |x, extra| match (x, extra) { + $($(#[$attr])? ($p $(,$extra)?, ..) $(if $guard)? => ::core::option::Option::Some({ () $(;$out)? })),+, + _ => ::core::option::Option::None, + } + ) + }); +} + +/// A version of [`select!`] that selects on token by reference instead of by value. +/// +/// Useful if you want to extract elements from a token in a zero-copy manner. +/// +/// See the docs for [`select!`] for more information. +/// +/// Requires that the parser input implements [`BorrowInput`]. +#[macro_export] +macro_rules! select_ref { + ($($(#[$attr:meta])? $p:pat $(= $extra:ident)? $(if $guard:expr)? $(=> $out:expr)?),+ $(,)?) => ({ + $crate::primitive::select_ref( + move |x, extra| match (x, extra) { + $($(#[$attr])? ($p $(,$extra)?, ..) $(if $guard)? => ::core::option::Option::Some({ () $(;$out)? })),+, + _ => ::core::option::Option::None, + } + ) + }); +} + +#[cfg(test)] +mod tests { + use crate::prelude::*; + + #[test] + fn zero_copy() { + use crate::input::WithContext; + use crate::prelude::*; + + #[derive(PartialEq, Debug)] + enum Token<'src> { + Ident(&'src str), + String(&'src str), + } + + type FileId = u32; + type Span = SimpleSpan; + + fn parser<'src>( + ) -> impl Parser<'src, WithContext, [(Span, Token<'src>); 6]> { + let ident = any() + .filter(|c: &char| c.is_alphanumeric()) + .repeated() + .at_least(1) + .to_slice() + .map(Token::Ident); + + let string = just('"') + .then(any().filter(|c: &char| *c != '"').repeated()) + .then(just('"')) + .to_slice() + .map(Token::String); + + ident + .or(string) + .map_with(|token, e| (e.span(), token)) + .padded() + .repeated() + .collect_exactly() + } + + assert_eq!( + parser() + .parse(r#"hello "world" these are "test" tokens"#.with_context(42)) + .into_result(), + Ok([ + (Span::new(42, 0..5), Token::Ident("hello")), + (Span::new(42, 6..13), Token::String("\"world\"")), + (Span::new(42, 14..19), Token::Ident("these")), + (Span::new(42, 20..23), Token::Ident("are")), + (Span::new(42, 24..30), Token::String("\"test\"")), + (Span::new(42, 31..37), Token::Ident("tokens")), + ]), + ); + } + + #[test] + fn zero_copy_map_span() { + use crate::{ + input::{SliceInput, ValueInput}, + prelude::*, + }; + + #[derive(PartialEq, Debug)] + enum Token<'src> { + Ident(&'src str), + String(&'src str), + } + + type FileId<'src> = &'src str; + type Span<'src> = SimpleSpan>; + + fn parser<'src, I>() -> impl Parser<'src, I, [(Span<'src>, Token<'src>); 6]> + where + I: ValueInput<'src, Token = char, Span = Span<'src>> + + SliceInput<'src, Slice = &'src str>, + { + let ident = any() + .filter(|c: &char| c.is_alphanumeric()) + .repeated() + .at_least(1) + .to_slice() + .map(Token::Ident); + + let string = just('"') + .then(any().filter(|c: &char| *c != '"').repeated()) + .then(just('"')) + .to_slice() + .map(Token::String); + + ident + .or(string) + .map_with(|token, e| (e.span(), token)) + .padded() + .repeated() + .collect_exactly() + } + + let filename = "file.txt".to_string(); + let fstr = filename.as_str(); + + assert_eq!( + parser() + .parse( + r#"hello "world" these are "test" tokens"# + .map_span(|span| Span::new(fstr, span.start()..span.end())) + ) + .into_result(), + Ok([ + (Span::new("file.txt", 0..5), Token::Ident("hello")), + (Span::new("file.txt", 6..13), Token::String("\"world\"")), + (Span::new("file.txt", 14..19), Token::Ident("these")), + (Span::new("file.txt", 20..23), Token::Ident("are")), + (Span::new("file.txt", 24..30), Token::String("\"test\"")), + (Span::new("file.txt", 31..37), Token::Ident("tokens")), + ]), + ); + } + + #[test] + fn zero_copy_repetition() { + use crate::prelude::*; + + fn parser<'src>() -> impl Parser<'src, &'src str, Vec> { + any() + .filter(|c: &char| c.is_ascii_digit()) + .repeated() + .at_least(1) + .at_most(3) + .to_slice() + .map(|b: &str| b.parse::().unwrap()) + .padded() + .separated_by(just(',').padded()) + .allow_trailing() + .collect() + .delimited_by(just('['), just(']')) + } + + assert_eq!( + parser().parse("[122 , 23,43, 4, ]").into_result(), + Ok(vec![122, 23, 43, 4]), + ); + assert_eq!( + parser().parse("[0, 3, 6, 900,120]").into_result(), + Ok(vec![0, 3, 6, 900, 120]), + ); + assert_eq!( + parser().parse("[200,400,50 ,0,0, ]").into_result(), + Ok(vec![200, 400, 50, 0, 0]), + ); + + assert!(parser().parse("[1234,123,12,1]").has_errors()); + assert!(parser().parse("[,0, 1, 456]").has_errors()); + assert!(parser().parse("[3, 4, 5, 67 89,]").has_errors()); + } + + #[test] + fn zero_copy_group() { + use crate::prelude::*; + + fn parser<'src>() -> impl Parser<'src, &'src str, (&'src str, u64, char)> { + group(( + any() + .filter(|c: &char| c.is_ascii_alphabetic()) + .repeated() + .at_least(1) + .to_slice() + .padded(), + any() + .filter(|c: &char| c.is_ascii_digit()) + .repeated() + .at_least(1) + .to_slice() + .map(|s: &str| s.parse::().unwrap()) + .padded(), + any().filter(|c: &char| !c.is_whitespace()).padded(), + )) + } + + assert_eq!( + parser().parse("abc 123 [").into_result(), + Ok(("abc", 123, '[')), + ); + assert_eq!( + parser().parse("among3d").into_result(), + Ok(("among", 3, 'd')), + ); + assert_eq!( + parser().parse("cba321,").into_result(), + Ok(("cba", 321, ',')), + ); + + assert!(parser().parse("abc 123 ").has_errors()); + assert!(parser().parse("123abc ]").has_errors()); + assert!(parser().parse("and one &").has_errors()); + } + + #[test] + fn zero_copy_group_array() { + use crate::prelude::*; + + fn parser<'src>() -> impl Parser<'src, &'src str, [char; 3]> { + group([just('a'), just('b'), just('c')]) + } + + assert_eq!(parser().parse("abc").into_result(), Ok(['a', 'b', 'c'])); + assert!(parser().parse("abd").has_errors()); + } + + #[test] + fn unicode_str() { + let input = "🄯🄚🹠🴎ðŸ„ðŸ‹ðŸ°ðŸ„‚🬯🈦g🸵ðŸ©ðŸ•”🈳2🬙🨞🅢🭳🎅h🵚🧿ðŸ©ðŸ°¬k🠡🀔🈆ðŸ¹ðŸ¤ŸðŸ‰—🴟📵🰄🤿ðŸœðŸ™˜ðŸ¹„5🠻🡉🱖🠓"; + let mut own = crate::input::InputOwn::<_, extra::Default>::new(input); + let mut inp = own.as_ref_start(); + + while let Some(_c) = inp.next() {} + } + + #[test] + #[cfg(feature = "unstable")] + fn iter() { + use crate::prelude::*; + + fn many_letters<'src>() -> impl IterParser<'src, &'src str, char> { + any().filter(char::is_ascii_alphabetic).repeated() + } + + let res = many_letters().parse_iter("abcdef", |iter| iter.collect::()); + + assert_eq!(res.into_result().unwrap(), "abcdef"); + + let res = many_letters().parse_iter("123456", |iter| iter.collect::()); + + assert!(res.has_errors()); + } + + #[test] + #[cfg(feature = "memoization")] + fn exponential() { + use crate::prelude::*; + + fn parser<'src>() -> impl Parser<'src, &'src str, String> { + recursive(|expr| { + let atom = any() + .filter(|c: &char| c.is_alphabetic()) + .repeated() + .at_least(1) + .collect() + .or(expr.delimited_by(just('('), just(')'))); + + atom.clone() + .then_ignore(just('+')) + .then(atom.clone()) + .map(|(a, b)| format!("{a}{b}")) + .memoized() + .or(atom) + }) + .then_ignore(end()) + } + + parser() + .parse("((((((((((((((((((((((((((((((a+b))))))))))))))))))))))))))))))") + .into_result() + .unwrap(); + } + + #[test] + #[cfg(feature = "memoization")] + fn left_recursive() { + use crate::prelude::*; + + fn parser<'src>() -> impl Parser<'src, &'src str, String> { + recursive(|expr| { + let atom = any() + .filter(|c: &char| c.is_alphabetic()) + .repeated() + .at_least(1) + .collect(); + + let sum = expr + .clone() + .then_ignore(just('+')) + .then(expr) + .map(|(a, b)| format!("{a}{b}")) + .memoized(); + + sum.or(atom) + }) + .then_ignore(end()) + } + + assert_eq!(parser().parse("a+b+c").into_result().unwrap(), "abc"); + } + + #[cfg(debug_assertions)] + mod debug_asserts { + use crate::prelude::*; + + // TODO panic when left recursive parser is detected + // #[test] + // #[should_panic] + // fn debug_assert_left_recursive() { + // recursive(|expr| { + // let atom = any::<&str, extra::Default>() + // .filter(|c: &char| c.is_alphabetic()) + // .repeated() + // .at_least(1) + // .collect(); + + // let sum = expr + // .clone() + // .then_ignore(just('+')) + // .then(expr) + // .map(|(a, b)| format!("{a}{b}")); + + // sum.or(atom) + // }) + // .then_ignore(end()) + // .parse("a+b+c"); + // } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn debug_assert_collect() { + empty::<&str, extra::Default>() + .repeated() + .collect::<()>() + .parse("a+b+c") + .unwrap(); + } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn debug_assert_separated_by() { + empty::<&str, extra::Default>() + .to(()) + .separated_by(empty()) + .collect::<()>() + .parse("a+b+c"); + } + + #[test] + fn debug_assert_separated_by2() { + assert_eq!( + empty::<&str, extra::Default>() + .separated_by(just(',')) + .count() + .parse(",") + .unwrap(), + 2 + ); + } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn debug_assert_foldl() { + assert_eq!( + empty::<&str, extra::Default>() + .to(1) + .foldl(empty().repeated(), |n, ()| n + 1) + .parse("a+b+c") + .unwrap(), + 3 + ); + } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn debug_assert_foldl_with() { + use extra::SimpleState; + + let state = 100; + empty::<&str, extra::Full, ()>>() + .foldl_with(empty().to(()).repeated(), |_, _, _| ()) + .parse_with_state("a+b+c", &mut state.into()); + } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn debug_assert_foldr() { + empty::<&str, extra::Default>() + .to(()) + .repeated() + .foldr(empty(), |_, _| ()) + .parse("a+b+c"); + } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn debug_assert_foldr_with_state() { + empty::<&str, extra::Default>() + .to(()) + .repeated() + .foldr_with(empty(), |_, _, _| ()) + .parse_with_state("a+b+c", &mut ()); + } + + #[test] + #[should_panic] + #[cfg(debug_assertions)] + fn debug_assert_repeated() { + empty::<&str, extra::Default>() + .to(()) + .repeated() + .parse("a+b+c"); + } + + // TODO what about IterConfigure and TryIterConfigure? + } + + #[test] + #[should_panic] + fn recursive_define_twice() { + let mut expr = Recursive::declare(); + expr.define({ + let atom = any::<&str, extra::Default>() + .filter(|c: &char| c.is_alphabetic()) + .repeated() + .at_least(1) + .collect(); + let sum = expr + .clone() + .then_ignore(just('+')) + .then(expr.clone()) + .map(|(a, b)| format!("{a}{b}")); + + sum.or(atom) + }); + expr.define(expr.clone()); + + expr.then_ignore(end()).parse("a+b+c"); + } + + #[test] + #[should_panic] + fn todo_err() { + let expr = todo::<&str, String, extra::Default>(); + expr.then_ignore(end()).parse("a+b+c"); + } + + #[test] + fn box_impl() { + fn parser<'src>() -> impl Parser<'src, &'src str, Vec> { + Box::new( + any() + .filter(|c: &char| c.is_ascii_digit()) + .repeated() + .at_least(1) + .at_most(3) + .to_slice() + .map(|b: &str| b.parse::().unwrap()) + .padded() + .separated_by(just(',').padded()) + .allow_trailing() + .collect() + .delimited_by(just('['), just(']')), + ) + } + + assert_eq!( + parser().parse("[122 , 23,43, 4, ]").into_result(), + Ok(vec![122, 23, 43, 4]), + ); + assert_eq!( + parser().parse("[0, 3, 6, 900,120]").into_result(), + Ok(vec![0, 3, 6, 900, 120]), + ); + assert_eq!( + parser().parse("[200,400,50 ,0,0, ]").into_result(), + Ok(vec![200, 400, 50, 0, 0]), + ); + } + + #[test] + fn rc_impl() { + use alloc::rc::Rc; + + fn parser<'src>() -> impl Parser<'src, &'src str, Vec> { + Rc::new( + any() + .filter(|c: &char| c.is_ascii_digit()) + .repeated() + .at_least(1) + .at_most(3) + .to_slice() + .map(|b: &str| b.parse::().unwrap()) + .padded() + .separated_by(just(',').padded()) + .allow_trailing() + .collect() + .delimited_by(just('['), just(']')), + ) + } + + assert_eq!( + parser().parse("[122 , 23,43, 4, ]").into_result(), + Ok(vec![122, 23, 43, 4]), + ); + assert_eq!( + parser().parse("[0, 3, 6, 900,120]").into_result(), + Ok(vec![0, 3, 6, 900, 120]), + ); + assert_eq!( + parser().parse("[200,400,50 ,0,0, ]").into_result(), + Ok(vec![200, 400, 50, 0, 0]), + ); + } + + #[derive(Copy, Clone, Debug, PartialEq, Eq)] + struct MyErr(&'static str); + + impl<'src, I: Input<'src>> crate::Error<'src, I> for MyErr { + fn merge(self, other: Self) -> Self { + if other == MyErr("special") { + MyErr("special") + } else { + self + } + } + } + + impl<'src, I> crate::LabelError<'src, I, crate::DefaultExpected<'src, I::Token>> for MyErr + where + I: Input<'src>, + { + fn expected_found>>( + _expected: E, + _found: Option>, + _span: I::Span, + ) -> Self { + MyErr("expected found") + } + } + + #[test] + fn err_prio_0() { + #[allow(dead_code)] + fn always_err<'src>() -> impl Parser<'src, &'src str, (), extra::Err> { + empty().try_map(|_, _| Err(MyErr("special"))) + } + + assert_eq!( + always_err().parse("test").into_result().unwrap_err(), + vec![MyErr("special")] + ) + } + + #[test] + fn err_prio_1() { + #[allow(dead_code)] + fn always_err_choice<'src>() -> impl Parser<'src, &'src str, (), extra::Err> { + choice((just("something").ignored(), empty())).try_map(|_, _| Err(MyErr("special"))) + } + + assert_eq!( + always_err_choice().parse("test").into_result().unwrap_err(), + vec![MyErr("special")] + ) + } + + #[test] + fn into_iter_no_error() { + fn parser<'src>() -> impl Parser<'src, &'src str, (), extra::Err> { + let many_as = just('a') + .ignored() + .repeated() + .at_least(1) + .collect::>(); + + many_as.into_iter().collect() + } + + assert_eq!(parser().parse("aaa").into_result(), Ok(())); + } + + #[cfg(feature = "nightly")] + #[test] + fn flatten() { + fn parser<'src>() -> impl Parser<'src, &'src str, Vec, extra::Err> { + let many_as = just('a') + .map(Some) + .or(any().to(None)) + .repeated() + .flatten() + .collect::>(); + + many_as.into_iter().collect() + } + + assert_eq!( + parser().parse("abracadabra").into_result(), + Ok(vec!['a', 'a', 'a', 'a', 'a']) + ); + } + + #[test] + fn iterable_then() { + fn parser<'src>() -> impl Parser<'src, &'src str, Vec> { + just('a') + .map(Some) + .into_iter() + .then(just('b').repeated()) + .then(just('c').repeated()) + .collect() + } + + assert_eq!( + parser().parse("abbcc").into_result(), + Ok(vec!['a', 'b', 'b', 'c', 'c']) + ); + assert_eq!(parser().parse("acc").into_result(), Ok(vec!['a', 'c', 'c'])); + assert!(parser().parse("bbc").has_errors()); + } + + #[test] + #[allow(dead_code)] + fn map_with_compiles() { + enum Token {} + enum Expr {} + + fn expr<'src, I>() -> impl Parser<'src, I, (Expr, SimpleSpan)> + 'src + where + I: Input<'src, Token = Token, Span = SimpleSpan> + 'src, + { + todo().map_with(|expr, e| (expr, e.span())) + } + } + + #[test] + fn label() { + use crate::label::LabelError; + + fn parser<'src>() -> impl Parser<'src, &'src str, (), extra::Err>> { + just("hello").labelled("greeting").as_context().ignored() + } + + let mut err = as crate::LabelError<&str, char>>::expected_found( + ['h'], + Some('b'.into()), + (0..1).into(), + ); + as LabelError<&str, _>>::label_with(&mut err, "greeting"); + assert_eq!(parser().parse("bye").into_errors(), vec![err]); + + let mut err = as crate::LabelError<&str, char>>::expected_found( + ['l'], + Some('p'.into()), + (3..4).into(), + ); + as LabelError<&str, _>>::in_context(&mut err, "greeting", (0..3).into()); + assert_eq!(parser().parse("help").into_errors(), vec![err]); + + fn parser2<'src>() -> impl Parser<'src, &'src str, (), extra::Err>> { + text::keyword("hello") + .labelled("greeting") + .as_context() + .ignored() + } + + let mut err = + as crate::LabelError<&str, char>>::expected_found(['h'], None, (0..7).into()); + as LabelError<&str, _>>::label_with(&mut err, "greeting"); + assert_eq!(parser2().parse("goodbye").into_errors(), vec![err]); + } + + #[test] + fn labelled_with() { + use crate::label::LabelError; + + fn parser<'src>() -> impl Parser<'src, &'src str, (), extra::Err>> { + just("hello") + .ignored() + .recover_with(via_parser(empty())) + .labelled_with(|| "greeting") + .as_context() + } + + let mut err = + as LabelError<&str, char>>::expected_found(['h'], None, (0..0).into()); + as LabelError<&str, _>>::in_context(&mut err, "greeting", (0..0).into()); + assert_eq!(parser().parse("").into_errors(), vec![err]); + } + + #[test] + #[allow(dead_code)] + fn invalid_escape() { + use crate::LabelError; + + fn string<'src>() -> impl Parser<'src, &'src str, &'src str, extra::Err>> { + let quote = just("\""); + let escaped = just("\\").then(just("n")); + let unescaped = none_of("\\\""); + + unescaped + .ignored() + .or(escaped.ignored()) + .repeated() + .to_slice() + .delimited_by(quote, quote) + } + + assert_eq!( + string().parse(r#""Hello\m""#).into_result(), + Err(vec![ + as LabelError::<&str, char>>::expected_found( + ['n'], + Some('m'.into()), + (7..8).into(), + ) + ]), + ); + } + + #[test] + #[allow(dead_code)] + fn map_err_missed_info() { + use crate::{extra::Err, LabelError}; + + fn erroneous_map_err<'src>() -> impl Parser<'src, &'src str, (), Err>> { + group(( + just("a").or_not(), + just("b").map_err(|mut err| { + LabelError::<&str, _>::label_with(&mut err, 'l'); + err + }), + )) + .ignored() + } + + assert_eq!( + erroneous_map_err().parse("_").into_output_errors(), + ( + None, + vec![LabelError::<&str, _>::expected_found( + ['a', 'l'], + Some('_'.into()), + SimpleSpan::new((), 0..1), + )] + ), + ); + + fn erroneous_then<'src>() -> impl Parser<'src, &'src str, (), Err>> { + group(( + just("a").or_not(), + empty().map_err(|mut err| { + LabelError::<&str, _>::label_with(&mut err, 'l'); + err + }), + just("c"), + )) + .ignored() + } + + assert_eq!( + erroneous_then().parse("_").into_output_errors(), + ( + None, + vec![LabelError::<&str, _>::expected_found( + ['a', 'c'], + Some('_'.into()), + SimpleSpan::new((), 0..1), + )] + ), + ); + } + + #[test] + fn map_err() { + use crate::LabelError; + + let parser = just::>('"').map_err(move |e: Rich| { + println!("Found = {:?}", e.found()); + println!("Expected = {:?}", e.expected().collect::>()); + println!("Span = {:?}", e.span()); + LabelError::<&str, char>::expected_found( + ['"'], + e.found().copied().map(Into::into), + *e.span(), + ) + }); + + assert_eq!( + parser.parse(r#"H"#).into_result(), + Err(vec![LabelError::<&str, char>::expected_found( + ['"'], + Some('H'.into()), + (0..1).into() + )]) + ); + } + + #[test] + fn map_err_with() { + use crate::LabelError; + + let parser = just::>('#') + .repeated() + .count() + .ignore_with_ctx(just('"').map_err_with(move |e: Rich, extras| { + println!("Found = {:?}", e.found()); + println!("Expected = {:?}", e.expected().collect::>()); + println!("Span = {:?}", e.span()); + println!("Context = {:?}", extras.ctx()); + LabelError::<&str, String>::expected_found( + [format!("after {} hashes", extras.ctx())], + e.found().copied().map(Into::into), + *e.span(), + ) + })); + + let mut err: Rich<_> = + LabelError::<&str, char>::expected_found(['#'], Some('H'.into()), (3..4).into()); + err = LabelError::<&str, String>::merge_expected_found( + err, + ["after 3 hashes".into()], + Some('H'.into()), + (3..4).into(), + ); + assert_eq!(parser.parse("###H").into_result(), Err(vec![err])); + } + + #[test] + fn try_map() { + use crate::{DefaultExpected, LabelError}; + + let parser = group(( + just("a").or_not(), + just("b").try_map(|_, _| Ok(())).or_not(), + just::<_, &str, extra::Err>>("c"), + )) + .ignored(); + + assert_eq!( + parser.parse("").into_output_errors(), + ( + None, + vec![LabelError::<&str, _>::expected_found( + vec![ + DefaultExpected::Token('a'.into()), + DefaultExpected::Token('b'.into()), + DefaultExpected::Token('c'.into()), + ], + None, + SimpleSpan::new((), 0..0) + )] + ) + ); + } + + #[test] + fn try_map_with() { + use crate::{DefaultExpected, LabelError}; + + let parser = group(( + just("a").or_not(), + just("b").try_map_with(|_, _| Ok(())).or_not(), + just::<_, &str, extra::Err>>("c"), + )) + .ignored(); + + assert_eq!( + parser.parse("").into_output_errors(), + ( + None, + vec![LabelError::<&str, _>::expected_found( + vec![ + DefaultExpected::Token('a'.into()), + DefaultExpected::Token('b'.into()), + DefaultExpected::Token('c'.into()), + ], + None, + SimpleSpan::new((), 0..0) + )] + ) + ); + } + + #[test] + fn filter() { + use crate::{DefaultExpected, LabelError}; + + let parser = just::<_, _, extra::Err>>("a").filter(|_| false); + + assert_eq!( + parser.parse("a").into_result(), + Err(vec![LabelError::<&str, _>::expected_found( + [DefaultExpected::SomethingElse], + Some('a'.into()), + SimpleSpan::new((), 0..1) + ),]) + ); + + let parser = group(( + just("a").or_not(), + just("b").filter(|_| false).or_not(), + just::<_, &str, extra::Err>>("c"), + )); + + assert_eq!( + parser.parse("b").into_output_errors(), + ( + None, + vec![LabelError::<&str, _>::expected_found( + vec![ + DefaultExpected::Token('a'.into()), + DefaultExpected::SomethingElse, + DefaultExpected::Token('c'.into()), + ], + Some('b'.into()), + SimpleSpan::new((), 0..1) + )] + ) + ); + } + + #[test] + fn rewind() { + use crate::{DefaultExpected, LabelError}; + + let parser = group((just("a"), any(), just("b").or_not())) + .rewind() + .then(just::<_, _, extra::Err>>("ac")); + + assert_eq!( + parser.parse("ad").into_output_errors(), + ( + None, + vec![LabelError::<&str, _>::expected_found( + [DefaultExpected::Token('c'.into())], + Some('d'.into()), + SimpleSpan::new((), 1..2) + )] + ) + ) + } + + #[test] + fn separated_by() { + use crate::{error::Simple, extra}; + + let parser = just::<_, &str, extra::Err>>("a") + .or_not() + .separated_by(just("b")); + + assert_eq!(parser.parse("bba").into_result(), Ok(())); + } + + #[test] + fn zero_size_custom_failure() { + fn my_custom<'src>() -> impl Parser<'src, &'src str, ()> { + custom(|inp| { + let check = inp.save(); + if inp.parse(just("foo")).is_err() { + inp.rewind(check); + } + Ok(()) + }) + } + + assert!(my_custom().parse("not foo").has_errors()); + } + + #[test] + fn labels() { + use crate::{DefaultExpected, Error, LabelError, TextExpected}; + + let parser = just("a") + .or_not() + .then(text::whitespace::<&str, extra::Err>>()); + + assert_eq!( + parser.parse("b").into_output_errors(), + ( + None, + vec![Error::<&str>::merge( + Error::<&str>::merge( + LabelError::<&str, _>::expected_found( + vec![DefaultExpected::Token('a'.into())], + Some('b'.into()), + SimpleSpan::new((), 0..1) + ), + LabelError::<&str, _>::expected_found( + vec![TextExpected::<&str>::Whitespace], + Some('b'.into()), + SimpleSpan::new((), 0..1) + ), + ), + LabelError::<&str, _>::expected_found( + vec![DefaultExpected::EndOfInput], + Some('b'.into()), + SimpleSpan::new((), 0..1) + ), + )] + ) + ); + } + + #[test] + fn labelled_not() { + use crate::{DefaultExpected, LabelError}; + + let parser = any::<_, extra::Err>>().not().labelled("label"); + + let mut err = LabelError::<&str, _>::expected_found( + [DefaultExpected::SomethingElse], + Some('b'.into()), + SimpleSpan::new((), 0..1), + ); + LabelError::<&str, _>::label_with(&mut err, "label"); + assert_eq!(parser.parse("b").into_output_errors(), (None, vec![err])); + } + + #[test] + fn state_rewind() { + use crate::{extra::Full, inspector::TruncateState}; + + let parser = any::<_, Full, ()>>() + .map_with(|out, extra| { + extra.state().0.push(out); + extra.state().0.len() - 1 + }) + .rewind() + .then_ignore(any()); + + let mut state = TruncateState::default(); + let res = parser.parse_with_state("a", &mut state).unwrap(); + assert_eq!(res, 0); + assert_eq!(state.0.as_slice(), ['a']); + } + + #[test] + fn error_rewind() { + let parser = any::<_, extra::Default>() + .validate(|out, _, emitter| { + emitter.emit(EmptyErr::default()); + out + }) + .rewind() + .then_ignore(any()); + + assert_eq!( + parser.parse("a").into_output_errors(), + (Some('a'), vec![EmptyErr::default()]) + ); + } + + #[test] + fn secondary_error_choice() { + let secondary_error = any::<_, extra::Default>() + .validate(|out, _, emitter| { + emitter.emit(EmptyErr::default()); + out + }) + .then(just('c')); + let parser = choice((just('a').then(just('b')), secondary_error)); + + assert_eq!( + parser.parse("aa").into_output_errors(), + (None, vec![EmptyErr::default(), EmptyErr::default()]) + ); + } + + /* + #[test] + fn label_sets() { + use crate::{DefaultExpected, Error, LabelError, TextExpected, text::whitespace}; + + fn tuple<'input>() -> impl Parser<'input, &'input str, (), extra::Err>> { + just("a") + .repeated() + .then_ignore(whitespace()) + .separated_by(just(",")) + .then_ignore(just(")")) + } + + assert_eq!( + tuple().parse("a").into_output_errors(), + ( + None, + vec![Error::<&str>::merge( + LabelError::<&str, _>::expected_found( + vec![TextExpected::<&str>::Whitespace], + None, + SimpleSpan::new((), 1..1) + ), + LabelError::<&str, _>::expected_found( + vec![ + DefaultExpected::Token('a'.into()), + DefaultExpected::Token(','.into()), + DefaultExpected::Token(')'.into()), + ], + None, + SimpleSpan::new((), 1..1) + ) + )] + ) + ); + } + */ + + // Prevent a regression + #[test] + fn labelled_recovery_dont_panic() { + fn parser<'i>() -> impl Parser<'i, &'i str, SimpleSpan> { + choice((choice((just("true"), just("false"))) + .labelled("boolean") + .to_span(),)) + .recover_with(via_parser(any().and_is(text::newline().not()).to_span())) + } + + let _ = parser().parse("tru"); + } + + #[test] + fn expected_nothing() { + fn parser<'i>() -> impl Parser<'i, &'i str, &'i str, extra::Err>> { + just("foo").contextual().configure(|_, _| false) + } + + let mut errs = parser().parse("foo").into_errors(); + assert_eq!(errs.len(), 1); + // The parser is unsatisifiable, so nothing should be expected + assert_eq!(errs.remove(0).expected().len(), 0); + } + + // Regression test for https://codeberg.org/zesterer/chumsky/issues/985 + #[test] + fn labelled_context_validate() { + use crate::input::MappedInput; + + type Span = SimpleSpan; + type Token = Spanned; + type ParserInput<'tokens> = MappedInput<'tokens, char, Span, &'tokens [Token]>; + type ParseError<'tokens> = extra::Err>; + + fn parser<'tokens>( + ) -> impl Parser<'tokens, ParserInput<'tokens>, Vec, ParseError<'tokens>> { + let single = just("a") + .ignore_then(one_of("bc").spanned()) + .validate(|b_or_c, _, emitter| { + let Spanned { inner, span } = b_or_c; + match inner { + 'b' => true, + 'c' => { + emitter.emit(Rich::custom(span, "expected 'b' but found 'c'")); + false + } + _ => unreachable!(), + } + }) + .labelled("SINGLE PARSER") + .as_context(); + single.repeated().collect() + } + + // Conceptually represents the source string "ac ab ac ad" + let tokens = vec![ + Token { + inner: 'a', + span: Span::from(0..1), + }, + Token { + inner: 'c', + span: Span::from(1..2), + }, + Token { + inner: 'a', + span: Span::from(3..4), + }, + Token { + inner: 'b', + span: Span::from(4..5), + }, + Token { + inner: 'a', + span: Span::from(6..7), + }, + Token { + inner: 'c', + span: Span::from(7..8), + }, + ]; + let eoi = Span::from(8..8); + + let (output, errors) = parser() + .parse(tokens.split_spanned(eoi)) + .into_output_errors(); + + assert_eq!(output, Some(vec![false, true, false])); + assert_eq!(errors.len(), 2); + if let [custom_1, custom_2] = &errors[..] { + let custom_1_contexts: Vec<_> = custom_1.contexts().collect(); + assert_eq!(*custom_1.span(), Span::from(1..2)); + assert_eq!(custom_1_contexts.len(), 1); + assert_eq!(*custom_1_contexts[0].1, Span::from(0..2)); + + let custom_2_contexts: Vec<_> = custom_2.contexts().collect(); + assert_eq!(*custom_2.span(), Span::from(7..8)); + assert_eq!(custom_2_contexts.len(), 1); + assert_eq!(*custom_2_contexts[0].1, Span::from(6..8)); + } else { + unreachable!(); + } + } +} diff --git a/chumsky-0.12.0/src/number.rs b/chumsky-0.13.0/src/number.rs similarity index 100% rename from chumsky-0.12.0/src/number.rs rename to chumsky-0.13.0/src/number.rs diff --git a/chumsky-0.12.0/src/pratt.rs b/chumsky-0.13.0/src/pratt.rs similarity index 99% rename from chumsky-0.12.0/src/pratt.rs rename to chumsky-0.13.0/src/pratt.rs index ffd1925324..f7488f9fa4 100644 --- a/chumsky-0.12.0/src/pratt.rs +++ b/chumsky-0.13.0/src/pratt.rs @@ -87,6 +87,8 @@ //! Ok("(((*1) + (-(2!))) - (-(3 ^ 2)))".to_string()), //! ); //! ``` +//! +//! `left`, `right` and `none` are shorthand versions of `Associativity` variants. use super::*; diff --git a/chumsky-0.12.0/src/primitive.rs b/chumsky-0.13.0/src/primitive.rs similarity index 97% rename from chumsky-0.12.0/src/primitive.rs rename to chumsky-0.13.0/src/primitive.rs index 93f55c6616..aecf3fcba2 100644 --- a/chumsky-0.12.0/src/primitive.rs +++ b/chumsky-0.13.0/src/primitive.rs @@ -211,6 +211,27 @@ where } } +/// Configuration for [`one_of`], used in [`ConfigParser::configure`] +pub struct OneOfCfg { + seq: Option, +} + +impl OneOfCfg { + /// Set the sequence to be used while parsing + #[inline] + pub fn seq(mut self, new_seq: T) -> Self { + self.seq = Some(new_seq); + self + } +} + +impl Default for OneOfCfg { + #[inline] + fn default() -> Self { + OneOfCfg { seq: None } + } +} + /// See [`one_of`]. pub struct OneOf { seq: T, @@ -272,16 +293,37 @@ where #[inline] fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult { + Self::go_cfg::(self, inp, OneOfCfg::default()) + } + + go_extra!(I::Token); +} + +impl<'src, I, E, T> ConfigParser<'src, I, I::Token, E> for OneOf +where + I: ValueInput<'src>, + E: ParserExtra<'src, I>, + I::Token: PartialEq, + T: Seq<'src, I::Token>, +{ + type Config = OneOfCfg; + + #[inline] + fn go_cfg( + &self, + inp: &mut InputRef<'src, '_, I, E>, + cfg: Self::Config, + ) -> PResult { + let seq = cfg.seq.as_ref().unwrap_or(&self.seq); let before = inp.save(); match inp.next_inner() { #[allow(suspicious_double_ref_op)] // Is this a clippy bug? - Some(tok) if self.seq.contains(tok.borrow()) => Ok(M::bind(|| tok)), + Some(tok) if seq.contains(tok.borrow()) => Ok(M::bind(|| tok)), found => { let err_span = inp.span_since(before.cursor()); inp.rewind(before); inp.add_alt( - self.seq - .seq_iter() + seq.seq_iter() .map(|e| DefaultExpected::Token(T::to_maybe_ref(e))), found.map(|f| f.into()), err_span, @@ -290,8 +332,6 @@ where } } } - - go_extra!(I::Token); } /// See [`none_of`]. @@ -952,15 +992,14 @@ macro_rules! impl_choice_for_tuple { let Choice { parsers: ($Head, $($X,)*), .. } = self; - match $Head.go::(inp) { - Ok(out) => return Ok(out), - Err(()) => inp.rewind(before.clone()), + if let Ok(out) = $Head.go::(inp) { + return Ok(out); } $( - match $X.go::(inp) { - Ok(out) => return Ok(out), - Err(()) => inp.rewind(before.clone()), + inp.rewind(before.clone()); + if let Ok(out) = $X.go::(inp) { + return Ok(out); } )* diff --git a/chumsky-0.13.0/src/private.rs b/chumsky-0.13.0/src/private.rs new file mode 100644 index 0000000000..6c9426888f --- /dev/null +++ b/chumsky-0.13.0/src/private.rs @@ -0,0 +1,392 @@ +use super::*; + +#[derive(Clone)] +pub(crate) struct Located { + pub(crate) pos: T, + pub(crate) err: E, +} + +impl Located { + #[inline] + pub fn at(pos: T, err: E) -> Self { + Self { pos, err } + } +} + +/// The result of calling [`Parser::go`] +pub(crate) type PResult = Result<::Output, ()>; +/// The result of calling [`IterParser::next`] +pub(crate) type IPResult = Result::Output>, ()>; + +/// An abstract parse mode - can be [`Emit`] or [`Check`] in practice, and represents the +/// common interface for handling both in the same method. +pub trait Mode { + /// The output of this mode for a given type + type Output; + + /// Bind the result of a closure into an output + fn bind T>(f: F) -> Self::Output; + + /// Given an [`Output`](Self::Output), takes its value and return a newly generated output + fn map U>(x: Self::Output, f: F) -> Self::Output; + + /// Choose between two fallible functions to execute depending on the mode. + fn choose Result, G: FnOnce(A) -> Result<(), E>>( + arg: A, + f: F, + g: G, + ) -> Result, E>; + + /// Given two [`Output`](Self::Output)s, take their values and combine them into a new + /// output value + fn combine V>( + x: Self::Output, + y: Self::Output, + f: F, + ) -> Self::Output; + /// By-reference version of [`Mode::combine`]. + fn combine_mut(x: &mut Self::Output, y: Self::Output, f: F); + + /// Given an array of outputs, bind them into an output of arrays + fn array(x: [Self::Output; N]) -> Self::Output<[T; N]>; + + /// Given an iterator of outputs, collect them into a container. The iterator will always be + /// consumed. + fn from_iter>( + iter: impl Iterator>, + ) -> Self::Output; + + fn from_mut(r: &mut Self::Output) -> Self::Output<&mut T>; + + fn get_or T>(r: Self::Output, f: F) -> T; + + /// Invoke a parser user the current mode. This is normally equivalent to + /// [`parser.go::(inp)`](Parser::go), but it can be called on unsized values such as + /// `dyn Parser`. + fn invoke<'a, I, O, E, P>(parser: &P, inp: &mut InputRef<'a, '_, I, E>) -> PResult + where + I: Input<'a>, + E: ParserExtra<'a, I>, + P: Parser<'a, I, O, E> + ?Sized; + + /// Invoke a parser with configuration using the current mode. This is normally equivalent + /// to [`parser.go::(inp)`](ConfigParser::go_cfg), but it can be called on unsized values + /// such as `dyn Parser`. + fn invoke_cfg<'a, I, O, E, P>( + parser: &P, + inp: &mut InputRef<'a, '_, I, E>, + cfg: P::Config, + ) -> PResult + where + I: Input<'a>, + E: ParserExtra<'a, I>, + P: ConfigParser<'a, I, O, E> + ?Sized; + + #[cfg(feature = "pratt")] + fn invoke_pratt_op_prefix<'src, 'parse, Op, I, O, E>( + op: &Op, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, + ) -> pratt::OperatorResult, ()> + where + Op: pratt::Operator<'src, I, O, E>, + I: Input<'src>, + E: ParserExtra<'src, I>; + #[cfg(feature = "pratt")] + fn invoke_pratt_op_postfix<'src, 'parse, Op, I, O, E>( + op: &Op, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: Self::Output, + min_power: i32, + ) -> pratt::OperatorResult, Self::Output> + where + Op: pratt::Operator<'src, I, O, E>, + I: Input<'src>, + E: ParserExtra<'src, I>; + #[cfg(feature = "pratt")] + fn invoke_pratt_op_infix<'src, 'parse, Op, I, O, E>( + op: &Op, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: Self::Output, + min_power: i32, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, + ) -> pratt::OperatorResult, Self::Output> + where + Op: pratt::Operator<'src, I, O, E>, + I: Input<'src>, + E: ParserExtra<'src, I>; +} + +/// Emit mode - generates parser output +pub struct Emit; + +impl Mode for Emit { + type Output = T; + #[inline(always)] + fn bind T>(f: F) -> Self::Output { + f() + } + #[inline(always)] + fn map U>(x: Self::Output, f: F) -> Self::Output { + f(x) + } + #[inline(always)] + fn choose Result, G: FnOnce(A) -> Result<(), E>>( + arg: A, + f: F, + _: G, + ) -> Result, E> { + f(arg) + } + #[inline(always)] + fn combine V>( + x: Self::Output, + y: Self::Output, + f: F, + ) -> Self::Output { + f(x, y) + } + #[inline(always)] + fn combine_mut(x: &mut Self::Output, y: Self::Output, f: F) { + f(x, y) + } + #[inline(always)] + fn array(x: [Self::Output; N]) -> Self::Output<[T; N]> { + x + } + #[inline(always)] + fn from_iter>( + iter: impl Iterator>, + ) -> Self::Output { + iter.collect() + } + + #[inline(always)] + fn from_mut(r: &mut Self::Output) -> Self::Output<&mut T> { + r + } + #[inline(always)] + fn get_or T>(r: Self::Output, _f: F) -> T { + r + } + + #[inline(always)] + fn invoke<'a, I, O, E, P>(parser: &P, inp: &mut InputRef<'a, '_, I, E>) -> PResult + where + I: Input<'a>, + E: ParserExtra<'a, I>, + P: Parser<'a, I, O, E> + ?Sized, + { + parser.go_emit(inp) + } + + #[inline(always)] + fn invoke_cfg<'a, I, O, E, P>( + parser: &P, + inp: &mut InputRef<'a, '_, I, E>, + cfg: P::Config, + ) -> PResult + where + I: Input<'a>, + E: ParserExtra<'a, I>, + P: ConfigParser<'a, I, O, E> + ?Sized, + { + parser.go_emit_cfg(inp, cfg) + } + + #[cfg(feature = "pratt")] + #[inline(always)] + fn invoke_pratt_op_prefix<'src, 'parse, Op, I, O, E>( + op: &Op, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, + ) -> pratt::OperatorResult, ()> + where + Op: pratt::Operator<'src, I, O, E>, + I: Input<'src>, + E: ParserExtra<'src, I>, + { + op.do_parse_prefix_emit(inp, pre_expr, &f) + } + #[cfg(feature = "pratt")] + #[inline(always)] + fn invoke_pratt_op_postfix<'src, 'parse, Op, I, O, E>( + op: &Op, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: Self::Output, + min_power: i32, + ) -> pratt::OperatorResult, Self::Output> + where + Op: pratt::Operator<'src, I, O, E>, + I: Input<'src>, + E: ParserExtra<'src, I>, + { + op.do_parse_postfix_emit(inp, pre_expr, pre_op, lhs, min_power) + } + #[cfg(feature = "pratt")] + #[inline(always)] + fn invoke_pratt_op_infix<'src, 'parse, Op, I, O, E>( + op: &Op, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: Self::Output, + min_power: i32, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, + ) -> pratt::OperatorResult, Self::Output> + where + Op: pratt::Operator<'src, I, O, E>, + I: Input<'src>, + E: ParserExtra<'src, I>, + { + op.do_parse_infix_emit(inp, pre_expr, pre_op, lhs, min_power, &f) + } +} + +/// Check mode - all output is discarded, and only uses parsers to check validity +pub struct Check; + +impl Mode for Check { + type Output = (); + #[inline(always)] + fn bind T>(_: F) -> Self::Output {} + #[inline(always)] + fn map U>(_: Self::Output, _: F) -> Self::Output {} + #[inline(always)] + fn choose Result, G: FnOnce(A) -> Result<(), E>>( + arg: A, + _: F, + g: G, + ) -> Result, E> { + g(arg) + } + #[inline(always)] + fn combine V>( + _: Self::Output, + _: Self::Output, + _: F, + ) -> Self::Output { + } + #[inline(always)] + fn combine_mut(_: &mut Self::Output, _: Self::Output, _: F) {} + #[inline(always)] + fn array(_: [Self::Output; N]) -> Self::Output<[T; N]> {} + #[inline(always)] + fn from_iter>( + iter: impl Iterator>, + ) -> Self::Output { + iter.for_each(drop); + } + #[inline(always)] + fn from_mut(_r: &mut Self::Output) -> Self::Output<&mut T> {} + #[inline(always)] + fn get_or T>(_r: Self::Output, f: F) -> T { + f() + } + + #[inline(always)] + fn invoke<'a, I, O, E, P>(parser: &P, inp: &mut InputRef<'a, '_, I, E>) -> PResult + where + I: Input<'a>, + E: ParserExtra<'a, I>, + P: Parser<'a, I, O, E> + ?Sized, + { + parser.go_check(inp) + } + + #[inline(always)] + fn invoke_cfg<'a, I, O, E, P>( + parser: &P, + inp: &mut InputRef<'a, '_, I, E>, + cfg: P::Config, + ) -> PResult + where + I: Input<'a>, + E: ParserExtra<'a, I>, + P: ConfigParser<'a, I, O, E> + ?Sized, + { + parser.go_check_cfg(inp, cfg) + } + + #[cfg(feature = "pratt")] + #[inline(always)] + fn invoke_pratt_op_prefix<'src, 'parse, Op, I, O, E>( + op: &Op, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, + ) -> pratt::OperatorResult, ()> + where + Op: pratt::Operator<'src, I, O, E>, + I: Input<'src>, + E: ParserExtra<'src, I>, + { + op.do_parse_prefix_check(inp, pre_expr, &f) + } + #[cfg(feature = "pratt")] + #[inline(always)] + fn invoke_pratt_op_postfix<'src, 'parse, Op, I, O, E>( + op: &Op, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: Self::Output, + min_power: i32, + ) -> pratt::OperatorResult, Self::Output> + where + Op: pratt::Operator<'src, I, O, E>, + I: Input<'src>, + E: ParserExtra<'src, I>, + { + op.do_parse_postfix_check(inp, pre_expr, pre_op, lhs, min_power) + } + #[cfg(feature = "pratt")] + #[inline(always)] + fn invoke_pratt_op_infix<'src, 'parse, Op, I, O, E>( + op: &Op, + inp: &mut InputRef<'src, 'parse, I, E>, + pre_expr: &input::Cursor<'src, 'parse, I>, + pre_op: &input::Checkpoint<'src, 'parse, I, >::Checkpoint>, + lhs: Self::Output, + min_power: i32, + f: &impl Fn(&mut InputRef<'src, 'parse, I, E>, i32) -> PResult, + ) -> pratt::OperatorResult, Self::Output> + where + Op: pratt::Operator<'src, I, O, E>, + I: Input<'src>, + E: ParserExtra<'src, I>, + { + op.do_parse_infix_check(inp, pre_expr, pre_op, lhs, min_power, &f) + } +} + +// TODO: Remove this when MaybeUninit transforms to/from arrays stabilize in any form +pub(crate) trait MaybeUninitExt: Sized { + /// Identical to the unstable [`MaybeUninit::uninit_array`] + fn uninit_array() -> [Self; N]; + + /// Identical to the unstable [`MaybeUninit::array_assume_init`] + unsafe fn array_assume_init(uninit: [Self; N]) -> [T; N]; +} + +impl MaybeUninitExt for MaybeUninit { + #[allow(clippy::uninit_assumed_init)] + fn uninit_array() -> [Self; N] { + // SAFETY: Output type is entirely uninhabited - IE, it's made up entirely of `MaybeUninit` + unsafe { MaybeUninit::uninit().assume_init() } + } + + unsafe fn array_assume_init(uninit: [Self; N]) -> [T; N] { + (&uninit as *const [Self; N] as *const [T; N]).read() + } +} + +pub trait Sealed {} diff --git a/chumsky-0.12.0/src/recovery.rs b/chumsky-0.13.0/src/recovery.rs similarity index 99% rename from chumsky-0.12.0/src/recovery.rs rename to chumsky-0.13.0/src/recovery.rs index 94b62847d6..7c1a8bda51 100644 --- a/chumsky-0.12.0/src/recovery.rs +++ b/chumsky-0.13.0/src/recovery.rs @@ -22,7 +22,7 @@ //! levels of the parser to find a configuration that you are happy with. If none of the provided error recovery //! strategies cover the specific pattern you wish to catch, you can even create your own by digging into Chumsky's //! internals and implementing your own strategies! If you come up with a useful strategy, feel free to open a PR -//! against the [main repository](https://github.com/zesterer/chumsky/)! +//! against the [main repository](https://codeberg.org/zesterer/chumsky/)! use super::*; diff --git a/chumsky-0.12.0/src/recursive.rs b/chumsky-0.13.0/src/recursive.rs similarity index 100% rename from chumsky-0.12.0/src/recursive.rs rename to chumsky-0.13.0/src/recursive.rs diff --git a/chumsky-0.12.0/src/regex.rs b/chumsky-0.13.0/src/regex.rs similarity index 100% rename from chumsky-0.12.0/src/regex.rs rename to chumsky-0.13.0/src/regex.rs diff --git a/chumsky-0.12.0/src/span.rs b/chumsky-0.13.0/src/span.rs similarity index 100% rename from chumsky-0.12.0/src/span.rs rename to chumsky-0.13.0/src/span.rs diff --git a/chumsky-0.12.0/src/stream.rs b/chumsky-0.13.0/src/stream.rs similarity index 100% rename from chumsky-0.12.0/src/stream.rs rename to chumsky-0.13.0/src/stream.rs diff --git a/chumsky-0.13.0/src/text.rs b/chumsky-0.13.0/src/text.rs new file mode 100644 index 0000000000..d17a607eb9 --- /dev/null +++ b/chumsky-0.13.0/src/text.rs @@ -0,0 +1,1209 @@ +//! Text-specific parsers and utilities. +//! +//! *“Ford!" he said, "there's an infinite number of monkeys outside who want to talk to us about this script for +//! Hamlet they've worked out.â€* +//! +//! The parsers in this module are generic over both Unicode ([`char`]) and ASCII ([`u8`]) characters. Most parsers take +//! a type parameter, `C`, that can be either [`u8`] or [`char`] in order to handle either case. + +use crate::prelude::*; +use alloc::string::ToString; + +use super::*; + +/// A trait implemented by textual character types (currently, [`u8`] and [`char`]). +/// +/// This trait is currently sealed to minimize the impact of breaking changes. If you find a type that you think should +/// implement this trait, please [open an issue/PR](https://codeberg.org/zesterer/chumsky/issues/new). +pub trait Char: Copy + PartialEq + Sealed { + /// Returns true if the character is canonically considered to be inline whitespace (i.e: not part of a newline). + fn is_inline_whitespace(&self) -> bool; + + /// Returns true if the character is canonically considered to be whitespace. + fn is_whitespace(&self) -> bool; + + /// Returns true if the character is canonically considered to be newline. + fn is_newline(&self) -> bool; + + /// Return the '0' digit of the character. + fn digit_zero() -> Self; + + /// Returns true if the character is canonically considered to be a numeric digit. + fn is_digit(&self, radix: u32) -> bool; + + /// Returns true if the character is canonically considered to be valid for starting an identifier. + fn is_ident_start(&self) -> bool; + + /// Returns true if the character is canonically considered to be a valid within an identifier. + fn is_ident_continue(&self) -> bool; + + /// Returns this character as a [`char`]. + fn to_ascii(&self) -> Option; +} + +impl Sealed for &Grapheme {} +impl Char for &Grapheme { + fn is_inline_whitespace(&self) -> bool { + self.as_str() == " " || self.as_str() == "\t" + } + + fn is_whitespace(&self) -> bool { + let mut iter = self.as_str().chars(); + iter.all(unicode::is_whitespace) + } + + fn is_newline(&self) -> bool { + [ + "\r\n", // CR LF + "\n", // Newline + "\r", // Carriage return + "\x0B", // Vertical tab + "\x0C", // Form feed + "\u{0085}", // Next line + "\u{2028}", // Line separator + "\u{2029}", // Paragraph separator + ] + .as_slice() + .contains(&self.as_str()) + } + + fn digit_zero() -> Self { + Grapheme::digit_zero() + } + + fn is_digit(&self, radix: u32) -> bool { + let mut iter = self.as_str().chars(); + match (iter.next(), iter.next()) { + (Some(i), None) => i.is_digit(radix), + _ => false, + } + } + + fn to_ascii(&self) -> Option { + let mut iter = self.as_bytes().iter(); + match (iter.next(), iter.next()) { + (Some(i), None) if i.is_ascii() => Some(*i), + _ => None, + } + } + + fn is_ident_start(&self) -> bool { + let (first, rest) = self.split(); + let is_start = unicode_ident::is_xid_start(first) || first == '_'; + is_start && rest.chars().all(unicode_ident::is_xid_continue) + } + + fn is_ident_continue(&self) -> bool { + let mut iter = self.as_str().chars(); + iter.all(unicode_ident::is_xid_continue) + } +} + +impl Sealed for char {} +impl Char for char { + fn is_inline_whitespace(&self) -> bool { + *self == ' ' || *self == '\t' + } + + fn is_whitespace(&self) -> bool { + unicode::is_whitespace(*self) + } + + fn is_newline(&self) -> bool { + [ + '\n', // Newline + '\r', // Carriage return + '\x0B', // Vertical tab + '\x0C', // Form feed + '\u{0085}', // Next line + '\u{2028}', // Line separator + '\u{2029}', // Paragraph separator + ] + .as_slice() + .contains(self) + } + + fn digit_zero() -> Self { + '0' + } + + fn is_digit(&self, radix: u32) -> bool { + char::is_digit(*self, radix) + } + + fn to_ascii(&self) -> Option { + self.is_ascii().then_some(*self as u8) + } + + fn is_ident_start(&self) -> bool { + unicode_ident::is_xid_start(*self) || *self == '_' + } + + fn is_ident_continue(&self) -> bool { + unicode_ident::is_xid_continue(*self) + } +} + +impl Sealed for u8 {} +impl Char for u8 { + fn is_inline_whitespace(&self) -> bool { + *self == b' ' || *self == b'\t' + } + + fn is_whitespace(&self) -> bool { + self.is_ascii_whitespace() + } + + fn is_newline(&self) -> bool { + [ + b'\n', // Newline + b'\r', // Carriage return + b'\x0B', // Vertical tab + b'\x0C', // Form feed + ] + .as_slice() + .contains(self) + } + + fn digit_zero() -> Self { + b'0' + } + + fn is_digit(&self, radix: u32) -> bool { + (*self as char).is_digit(radix) + } + + fn to_ascii(&self) -> Option { + Some(*self) + } + + fn is_ident_start(&self) -> bool { + (*self as char).is_ident_start() + } + + fn is_ident_continue(&self) -> bool { + (*self as char).is_ident_continue() + } +} + +/// A parser that accepts (and ignores) any number of whitespace characters before or after another pattern. +#[derive(Copy, Clone)] +pub struct Padded
{ + pub(crate) parser: A, +} + +impl<'src, I, O, E, A> Parser<'src, I, O, E> for Padded +where + I: Input<'src>, + E: ParserExtra<'src, I>, + I::Token: Char, + A: Parser<'src, I, O, E>, +{ + #[doc(hidden)] + #[cfg(feature = "debug")] + fn node_info(&self, scope: &mut debug::NodeScope) -> debug::NodeInfo { + debug::NodeInfo::Padded(Box::new(self.parser.node_info(scope))) + } + + fn go(&self, inp: &mut InputRef<'src, '_, I, E>) -> PResult { + inp.skip_while(|c| c.is_whitespace()); + let out = self.parser.go::(inp)?; + inp.skip_while(|c| c.is_whitespace()); + Ok(out) + } + + go_extra!(O); +} + +/// Labels denoting a variety of text-related patterns. +#[derive(Clone, Debug)] +#[non_exhaustive] +pub enum TextExpected { + /// Whitespace (for example: spaces, tabs, or newlines). + Whitespace, + /// Inline whitespace (for example: spaces or tabs). + InlineWhitespace, + /// A newline character or sequence. + Newline, + /// A numeric digit within the given radix range. + /// + /// For example: + /// + /// - `Digit(0, 10)` implies any base-10 digit + /// - `Digit(1, 16)` implies any non-zero hexadecimal digit + Digit(u32, u32), + /// Any identifier. + AnyIdentifier, + /// A specific identifier. + Identifier(Slice), + /// An integer was expected + Int, +} + +impl Copy for TextExpected {} + +/// A parser that accepts (and ignores) any number of whitespace characters. +/// +/// This parser is a `Parser::Repeated` and so methods such as `at_least()` can be called on it. +/// +/// The output type of this parser is `()`. +/// +/// # Examples +/// +/// ``` +/// # use chumsky::prelude::*; +/// let whitespace = text::whitespace::<_, extra::Err>>(); +/// +/// // Any amount of whitespace is parsed... +/// assert_eq!(whitespace.parse("\t \n \r ").into_result(), Ok(())); +/// // ...including none at all! +/// assert_eq!(whitespace.parse("").into_result(), Ok(())); +/// ``` +pub fn whitespace<'src, I, E>() -> Repeated + Copy, (), I, E> +where + I: StrInput<'src>, + I::Token: Char + 'src, + E: ParserExtra<'src, I>, + E::Error: LabelError<'src, I, TextExpected<()>>, +{ + any() + .filter(|c: &I::Token| c.is_whitespace()) + .labelled_with(|| TextExpected::Whitespace) + .as_builtin() + .ignored() + .repeated() +} + +/// A parser that accepts (and ignores) any number of inline whitespace characters. +/// +/// This parser is a `Parser::Repeated` and so methods such as `at_least()` can be called on it. +/// +/// The output type of this parser is `()`. +/// +/// # Examples +/// +/// ``` +/// # use chumsky::prelude::*; +/// let inline_whitespace = text::inline_whitespace::<_, extra::Err>>(); +/// +/// // Any amount of inline whitespace is parsed... +/// assert_eq!(inline_whitespace.parse("\t ").into_result(), Ok(())); +/// // ...including none at all! +/// assert_eq!(inline_whitespace.parse("").into_result(), Ok(())); +/// // ... but not newlines +/// assert!(inline_whitespace.at_least(1).parse("\n\r").has_errors()); +/// ``` +pub fn inline_whitespace<'src, I, E>() -> Repeated + Copy, (), I, E> +where + I: StrInput<'src>, + I::Token: Char + 'src, + E: ParserExtra<'src, I>, + E::Error: LabelError<'src, I, TextExpected<()>>, +{ + any() + .filter(|c: &I::Token| c.is_inline_whitespace()) + .labelled_with(|| TextExpected::InlineWhitespace) + .as_builtin() + .ignored() + .repeated() +} + +/// A parser that accepts (and ignores) any newline characters or character sequences. +/// +/// The output type of this parser is `()`. +/// +/// This parser is quite extensive, recognizing: +/// +/// - Line feed (`\n`) +/// - Carriage return (`\r`) +/// - Carriage return + line feed (`\r\n`) +/// - Vertical tab (`\x0B`) +/// - Form feed (`\x0C`) +/// - Next line (`\u{0085}`) +/// - Line separator (`\u{2028}`) +/// - Paragraph separator (`\u{2029}`) +/// +/// # Examples +/// +/// ``` +/// # use chumsky::prelude::*; +/// let newline = text::newline::<_, extra::Err>>(); +/// +/// assert_eq!(newline.parse("\n").into_result(), Ok(())); +/// assert_eq!(newline.parse("\r").into_result(), Ok(())); +/// assert_eq!(newline.parse("\r\n").into_result(), Ok(())); +/// assert_eq!(newline.parse("\x0B").into_result(), Ok(())); +/// assert_eq!(newline.parse("\x0C").into_result(), Ok(())); +/// assert_eq!(newline.parse("\u{0085}").into_result(), Ok(())); +/// assert_eq!(newline.parse("\u{2028}").into_result(), Ok(())); +/// assert_eq!(newline.parse("\u{2029}").into_result(), Ok(())); +/// ``` +#[must_use] +pub fn newline<'src, I, E>() -> impl Parser<'src, I, (), E> + Copy +where + I: StrInput<'src>, + I::Token: Char + 'src, + E: ParserExtra<'src, I>, + &'src str: OrderedSeq<'src, I::Token>, + E::Error: LabelError<'src, I, TextExpected<()>>, +{ + custom(|inp| { + let before = inp.cursor(); + + if inp + .peek() + .map_or(false, |c: I::Token| c.to_ascii() == Some(b'\r')) + { + inp.skip(); + if inp + .peek() + .map_or(false, |c: I::Token| c.to_ascii() == Some(b'\n')) + { + inp.skip(); + } + Ok(()) + } else { + let c = inp.next(); + if c.map_or(false, |c: I::Token| c.is_newline()) { + Ok(()) + } else { + let span = inp.span_since(&before); + Err(LabelError::expected_found( + [TextExpected::Newline], + c.map(MaybeRef::Val), + span, + )) + } + } + }) + .labelled_with(|| TextExpected::Newline) + .as_builtin() +} + +/// A parser that accepts one or more ASCII digits. +/// +/// The output type of this parser is `I::Slice` (i.e: [`&str`] when `I` is [`&str`], and [`&[u8]`] +/// when `I::Slice` is [`&[u8]`]). +/// +/// The `radix` parameter functions identically to [`char::is_digit`]. If in doubt, choose `10`. +/// +/// # Examples +/// +/// ``` +/// # use chumsky::prelude::*; +/// let digits = text::digits::<_, extra::Err>>(10).to_slice(); +/// +/// assert_eq!(digits.parse("0").into_result(), Ok("0")); +/// assert_eq!(digits.parse("1").into_result(), Ok("1")); +/// assert_eq!(digits.parse("01234").into_result(), Ok("01234")); +/// assert_eq!(digits.parse("98345").into_result(), Ok("98345")); +/// // A string of zeroes is still valid. Use `int` if this is not desirable. +/// assert_eq!(digits.parse("0000").into_result(), Ok("0000")); +/// assert!(digits.parse("").has_errors()); +/// ``` +#[must_use] +pub fn digits<'src, I, E>( + radix: u32, +) -> Repeated>::Token, E> + Copy, I::Token, I, E> +where + I: StrInput<'src>, + I::Token: Char + 'src, + E: ParserExtra<'src, I>, + E::Error: LabelError<'src, I, TextExpected<()>>, +{ + any() + .filter(move |c: &I::Token| c.is_digit(radix)) + .labelled_with(move || TextExpected::Digit(0, radix)) + .as_builtin() + .map_err(move |mut err: E::Error| { + err.label_with(TextExpected::Digit(0, radix)); + err + }) + .repeated() + .at_least(1) +} + +/// A parser that accepts a non-negative integer. +/// +/// An integer is defined as a non-empty sequence of ASCII digits, where the first digit is non-zero or the sequence +/// has length one. +/// +/// The output type of this parser is `I::Slice` (i.e: [`&str`] when `I` is [`&str`], and [`&[u8]`] +/// when `I::Slice` is [`&[u8]`]). +/// +/// The `radix` parameter functions identically to [`char::is_digit`]. If in doubt, choose `10`. +/// +/// # Examples +/// +/// ``` +/// # use chumsky::prelude::*; +/// let dec = text::int::<_, extra::Err>>(10); +/// +/// assert_eq!(dec.parse("0").into_result(), Ok("0")); +/// assert_eq!(dec.parse("1").into_result(), Ok("1")); +/// assert_eq!(dec.parse("1452").into_result(), Ok("1452")); +/// // No leading zeroes are permitted! +/// assert!(dec.parse("04").has_errors()); +/// +/// let hex = text::int::<_, extra::Err>>(16); +/// +/// assert_eq!(hex.parse("2A").into_result(), Ok("2A")); +/// assert_eq!(hex.parse("d").into_result(), Ok("d")); +/// assert_eq!(hex.parse("b4").into_result(), Ok("b4")); +/// assert!(hex.parse("0B").has_errors()); +/// ``` +/// +#[must_use] +pub fn int<'src, I, E>(radix: u32) -> impl Parser<'src, I, >::Slice, E> + Copy +where + I: StrInput<'src>, + I::Token: Char + 'src, + E: ParserExtra<'src, I>, + E::Error: LabelError<'src, I, TextExpected<()>> + LabelError<'src, I, MaybeRef<'src, I::Token>>, +{ + any() + .filter(move |c: &I::Token| c.is_digit(radix) && c != &I::Token::digit_zero()) + .then( + any() + .filter(move |c: &I::Token| c.is_digit(radix)) + .repeated(), + ) + .ignored() + .or(just(I::Token::digit_zero()).ignored()) + .to_slice() + .labelled_with(|| TextExpected::Int) + .as_builtin() +} + +/// Parsers and utilities for working with ASCII inputs. +pub mod ascii { + use super::*; + + /// A parser that accepts a C-style identifier. + /// + /// The output type of this parser is [`SliceInput::Slice`] (i.e: [`&str`] when `I` is [`&str`], and [`&[u8]`] when `I` is + /// [`&[u8]`]). + /// + /// An identifier is defined as an ASCII alphabetic character or an underscore followed by any number of alphanumeric + /// characters or underscores. The regex pattern for it is `[a-zA-Z_][a-zA-Z0-9_]*`. + #[must_use] + pub fn ident<'src, I, E>() -> impl Parser<'src, I, >::Slice, E> + Copy + where + I: StrInput<'src>, + I::Token: Char + 'src, + E: ParserExtra<'src, I>, + E::Error: LabelError<'src, I, TextExpected<()>>, + { + any() + .filter(|c: &I::Token| { + c.to_ascii() + .map_or(false, |i| i.is_ascii_alphabetic() || i == b'_') + }) + .then( + any() + .filter(|c: &I::Token| { + c.to_ascii() + .map_or(false, |i| i.is_ascii_alphanumeric() || i == b'_') + }) + .repeated(), + ) + .to_slice() + .labelled_with(|| TextExpected::AnyIdentifier) + .as_builtin() + } + + /// Like [`ident`], but only accepts a specific identifier while rejecting trailing identifier characters. + /// + /// The output type of this parser is `I::Slice` (i.e: [`&str`] when `I` is [`&str`], and [`&[u8]`] + /// when `I::Slice` is [`&[u8]`]). + /// + /// # Examples + /// + /// ``` + /// # use chumsky::prelude::*; + /// let def = text::ascii::keyword::<_, _, extra::Err>>("def"); + /// + /// // Exactly 'def' was found + /// assert_eq!(def.parse("def").into_result(), Ok("def")); + /// // Exactly 'def' was found, with non-identifier trailing characters + /// // This works because we made the parser lazy: it parses 'def' and ignores the rest + /// assert_eq!(def.clone().lazy().parse("def(foo, bar)").into_result(), Ok("def")); + /// // 'def' was found, but only as part of a larger identifier, so this fails to parse + /// assert!(def.lazy().parse("define").has_errors()); + /// ``` + #[track_caller] + pub fn keyword<'src, I, S, E>( + keyword: S, + ) -> impl Parser<'src, I, >::Slice, E> + Clone + 'src + where + I: StrInput<'src>, + I::Token: Char + fmt::Debug + 'src, + S: PartialEq + Clone + 'src, + E: ParserExtra<'src, I> + 'src, + E::Error: LabelError<'src, I, TextExpected<()>> + LabelError<'src, I, TextExpected>, + { + /* + #[cfg(debug_assertions)] + { + let mut cs = keyword.seq_iter(); + if let Some(c) = cs.next() { + let c = c.borrow().to_char(); + assert!(c.is_ascii_alphabetic() || c == '_', "The first character of a keyword must be ASCII alphabetic or an underscore, not {:?}", c); + } else { + panic!("Keyword must have at least one character"); + } + for c in cs { + let c = c.borrow().to_char(); + assert!(c.is_ascii_alphanumeric() || c == '_', "Trailing characters of a keyword must be ASCII alphanumeric or an underscore, not {:?}", c); + } + } + */ + ident() + .try_map({ + let keyword = keyword.clone(); + move |s: I::Slice, span| { + if keyword == s { + Ok(()) + } else { + Err(LabelError::expected_found( + [TextExpected::Identifier(keyword.clone())], + None, + span, + )) + } + } + }) + .to_slice() + .labelled(TextExpected::Identifier(keyword)) + .as_builtin() + } +} + +// Unicode is the default +pub use unicode::*; + +/// Parsers and utilities for working with unicode inputs. +pub mod unicode { + use super::*; + + use core::str::{Bytes, Chars}; + use unicode_segmentation::UnicodeSegmentation; + + /// A type containing one extended Unicode grapheme cluster. + #[derive(PartialEq, Eq)] + #[repr(transparent)] + pub struct Grapheme { + inner: str, + } + + impl Grapheme { + fn new(inner: &str) -> &Self { + // SAFETY: This is ok because Grapheme is #[repr(transparent)] + unsafe { &*(inner as *const str as *const Self) } + } + + /// Creates a new grapheme with the character `'0'` inside it. + pub fn digit_zero() -> &'static Self { + Self::new("0") + } + + /// Gets an iterator over code points. + pub fn code_points(&self) -> Chars<'_> { + self.inner.chars() + } + + /// Gets an iterator over bytes. + pub fn bytes(&self) -> Bytes<'_> { + self.inner.bytes() + } + + /// Gets the slice of code points that are contained in the grapheme cluster. + pub fn as_str(&self) -> &str { + &self.inner + } + + /// Gets the slice of bytes that are contained in the grapheme cluster. + pub fn as_bytes(&self) -> &[u8] { + self.inner.as_bytes() + } + + /// Splits the grapheme into the first code point and the remaining code points. + pub fn split(&self) -> (char, &str) { + let mut iter = self.inner.chars(); + // The operation never falls because the grapheme always contains at least one code point. + let first = iter.next().unwrap(); + (first, iter.as_str()) + } + } + + impl fmt::Debug for Grapheme { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("g'")?; + for i in self.as_str().chars() { + write!(f, "{}", i.escape_debug())?; + } + f.write_str("'")?; + Ok(()) + } + } + + impl fmt::Display for Grapheme { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.inner, f) + } + } + + impl AsRef for Grapheme { + fn as_ref(&self) -> &str { + self.as_str() + } + } + + impl AsRef<[u8]> for Grapheme { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } + } + + impl AsRef for Grapheme { + fn as_ref(&self) -> &Grapheme { + self + } + } + + impl Borrow for Grapheme { + fn borrow(&self) -> &str { + self.as_str() + } + } + + impl Borrow<[u8]> for Grapheme { + fn borrow(&self) -> &[u8] { + self.as_bytes() + } + } + + impl<'src> From<&'src Grapheme> for Box { + fn from(value: &'src Grapheme) -> Self { + let value: Box = Box::from(value.as_str()); + // SAFETY: This is ok because Grapheme is #[repr(transparent)] + unsafe { Box::from_raw(Box::into_raw(value) as *mut Grapheme) } + } + } + + impl From> for Box { + fn from(value: Box) -> Self { + // SAFETY: This is ok because Grapheme is #[repr(transparent)] + unsafe { Box::from_raw(Box::into_raw(value) as *mut str) } + } + } + + impl From> for Box<[u8]> { + fn from(value: Box) -> Self { + Box::::from(value).into() + } + } + + /// A type containing any number of extended Unicode grapheme clusters. + #[derive(PartialEq, Eq)] + #[repr(transparent)] + pub struct Graphemes { + inner: str, + } + + impl Graphemes { + /// Create a new graphemes. + pub fn new(inner: &str) -> &Self { + // SAFETY: This is ok because Graphemes is #[repr(transparent)] + unsafe { &*(inner as *const str as *const Self) } + } + + /// Gets an iterator over graphemes. + pub fn iter(&self) -> GraphemesIter<'_> { + self.into_iter() + } + + /// Gets an iterator over code points. + pub fn code_points(&self) -> Chars<'_> { + self.inner.chars() + } + + /// Gets an iterator over bytes. + pub fn bytes(&self) -> Bytes<'_> { + self.inner.bytes() + } + + /// Gets the slice of code points that are contained in the string. + pub fn as_str(&self) -> &str { + &self.inner + } + + /// Gets the slice of bytes that are contained in the string. + pub fn as_bytes(&self) -> &[u8] { + self.inner.as_bytes() + } + } + + impl fmt::Debug for Graphemes { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("g")?; + fmt::Debug::fmt(&self.inner, f) + } + } + + impl fmt::Display for Graphemes { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.inner, f) + } + } + + impl AsRef for Graphemes { + fn as_ref(&self) -> &str { + self.as_str() + } + } + + impl AsRef<[u8]> for Graphemes { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } + } + + impl AsRef for Graphemes { + fn as_ref(&self) -> &Graphemes { + self + } + } + + impl Borrow for Graphemes { + fn borrow(&self) -> &str { + self.as_str() + } + } + + impl Borrow<[u8]> for Graphemes { + fn borrow(&self) -> &[u8] { + self.as_bytes() + } + } + + impl<'src> From<&'src str> for &'src Graphemes { + fn from(value: &'src str) -> Self { + Graphemes::new(value) + } + } + + impl<'src> From<&'src Graphemes> for &'src str { + fn from(value: &'src Graphemes) -> Self { + value.as_str() + } + } + + impl<'src> From<&'src Graphemes> for Box { + fn from(value: &'src Graphemes) -> Self { + value.as_str().into() + } + } + + impl<'src> From<&'src str> for Box { + fn from(value: &'src str) -> Self { + Box::::from(value).into() + } + } + + impl From> for Box { + fn from(value: Box) -> Self { + // SAFETY: This is ok because Grapheme is #[repr(transparent)] + unsafe { Box::from_raw(Box::into_raw(value) as *mut Graphemes) } + } + } + + impl From> for Box { + fn from(value: Box) -> Self { + // SAFETY: This is ok because Grapheme is #[repr(transparent)] + unsafe { Box::from_raw(Box::into_raw(value) as *mut str) } + } + } + + impl From> for Box<[u8]> { + fn from(value: Box) -> Self { + Box::::from(value).into() + } + } + + impl<'src> IntoIterator for &'src Graphemes { + type Item = &'src Grapheme; + + type IntoIter = GraphemesIter<'src>; + + fn into_iter(self) -> Self::IntoIter { + GraphemesIter::new(self) + } + } + + impl Sealed for &'_ Graphemes {} + impl<'src> StrInput<'src> for &'src Graphemes { + #[doc(hidden)] + fn stringify(slice: Self::Slice) -> String { + slice.to_string() + } + } + + impl<'src> Input<'src> for &'src Graphemes { + type Cursor = usize; + type Span = SimpleSpan; + + type Token = &'src Grapheme; + type MaybeToken = &'src Grapheme; + + type Cache = Self; + + #[inline] + fn begin(self) -> (Self::Cursor, Self::Cache) { + (0, self) + } + + #[inline] + fn cursor_location(cursor: &Self::Cursor) -> usize { + *cursor + } + + #[inline(always)] + unsafe fn next_maybe( + this: &mut Self::Cache, + cursor: &mut Self::Cursor, + ) -> Option { + if *cursor < this.as_str().len() { + // SAFETY: `cursor < self.len()` above guarantees cursor is in-bounds + // We only ever return cursors that are at a code point boundary. + // The `next()` implementation returns `None`, only in the + // situation of zero length of the remaining part of the string. + // And the Unicode standard guarantees that any sequence of code + // points is a valid sequence of grapheme clusters, so the + // behaviour of the `next()` function should not change. + let c = this + .as_str() + .get_unchecked(*cursor..) + .graphemes(true) + .next() + .unwrap_unchecked(); + *cursor += c.len(); + Some(Grapheme::new(c)) + } else { + None + } + } + + #[inline(always)] + unsafe fn span(_this: &mut Self::Cache, range: Range<&Self::Cursor>) -> Self::Span { + (*range.start..*range.end).into() + } + } + + impl<'src> ExactSizeInput<'src> for &'src Graphemes { + #[inline(always)] + unsafe fn span_from(this: &mut Self::Cache, range: RangeFrom<&Self::Cursor>) -> Self::Span { + (*range.start..this.as_str().len()).into() + } + } + + impl<'src> ValueInput<'src> for &'src Graphemes { + #[inline(always)] + unsafe fn next(this: &mut Self::Cache, cursor: &mut Self::Cursor) -> Option { + Self::next_maybe(this, cursor) + } + } + + impl<'src> SliceInput<'src> for &'src Graphemes { + type Slice = Self; + + #[inline(always)] + fn full_slice(this: &mut Self::Cache) -> Self::Slice { + *this + } + + #[inline(always)] + unsafe fn slice(this: &mut Self::Cache, range: Range<&Self::Cursor>) -> Self::Slice { + Graphemes::new(&this.as_str()[*range.start..*range.end]) + } + + #[inline(always)] + unsafe fn slice_from( + this: &mut Self::Cache, + from: RangeFrom<&Self::Cursor>, + ) -> Self::Slice { + Graphemes::new(&this.as_str()[*from.start..]) + } + } + + /// Grapheme iterator type. + #[derive(Debug, Clone)] + pub struct GraphemesIter<'src> { + iter: unicode_segmentation::Graphemes<'src>, + } + + impl<'src> GraphemesIter<'src> { + /// Create a new grapheme iterator. + pub fn new(graphemes: &'src Graphemes) -> Self { + Self { + iter: graphemes.as_str().graphemes(true), + } + } + + /// Gets the slice of code points that are contained in the grapheme cluster. + pub fn as_str(self) -> &'src str { + self.iter.as_str() + } + } + + impl<'src> Iterator for GraphemesIter<'src> { + type Item = &'src Grapheme; + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + #[inline] + fn next(&mut self) -> Option { + self.iter.next().map(Grapheme::new) + } + } + + impl DoubleEndedIterator for GraphemesIter<'_> { + #[inline] + fn next_back(&mut self) -> Option { + self.iter.next_back().map(Grapheme::new) + } + } + + /// A parser that accepts an identifier. + /// + /// The output type of this parser is [`SliceInput::Slice`] (i.e: [`&str`] when `I` is [`&str`], and [`&[u8]`] when `I` is + /// [`&[u8]`]). + /// + /// An identifier is defined as per "Default Identifiers" in [Unicode Standard Annex #31](https://www.unicode.org/reports/tr31/). + #[must_use] + pub fn ident<'src, I, E>() -> impl Parser<'src, I, >::Slice, E> + Copy + where + I: StrInput<'src>, + I::Token: Char + 'src, + E: ParserExtra<'src, I>, + E::Error: LabelError<'src, I, TextExpected<()>>, + { + any() + .filter(|c: &I::Token| c.is_ident_start()) + .then( + any() + .filter(|c: &I::Token| c.is_ident_continue()) + .repeated(), + ) + .to_slice() + .labelled(TextExpected::AnyIdentifier) + .as_builtin() + } + + /// Like [`ident`], but only accepts a specific identifier while rejecting trailing identifier characters. + /// + /// The output type of this parser is `I::Slice` (i.e: [`&str`] when `I` is [`&str`], and [`&[u8]`] + /// when `I::Slice` is [`&[u8]`]). + /// + /// # Examples + /// + /// ``` + /// # use chumsky::prelude::*; + /// let def = text::ascii::keyword::<_, _, extra::Err>>("def"); + /// + /// // Exactly 'def' was found + /// assert_eq!(def.parse("def").into_result(), Ok("def")); + /// // Exactly 'def' was found, with non-identifier trailing characters + /// // This works because we made the parser lazy: it parses 'def' and ignores the rest + /// assert_eq!(def.clone().lazy().parse("def(foo, bar)").into_result(), Ok("def")); + /// // 'def' was found, but only as part of a larger identifier, so this fails to parse + /// assert!(def.lazy().parse("define").has_errors()); + /// ``` + #[track_caller] + pub fn keyword<'src, I, S, E>( + keyword: S, + ) -> impl Parser<'src, I, >::Slice, E> + Clone + 'src + where + I: StrInput<'src>, + I::Slice: PartialEq, + I::Token: Char + fmt::Debug + 'src, + S: PartialEq + Clone + 'src, + E: ParserExtra<'src, I> + 'src, + E::Error: LabelError<'src, I, TextExpected<()>> + LabelError<'src, I, TextExpected>, + { + /* + #[cfg(debug_assertions)] + { + let mut cs = keyword.seq_iter(); + if let Some(c) = cs.next() { + let c = c.borrow(); + assert!( + c.is_ident_start(), + "The first character of a keyword must be a valid unicode XID_START, not {:?}", + c + ); + } else { + panic!("Keyword must have at least one character"); + } + for c in cs { + let c = c.borrow(); + assert!(c.is_ident_continue(), "Trailing characters of a keyword must be valid as unicode XID_CONTINUE, not {:?}", c); + } + } + */ + ident() + .try_map({ + let keyword = keyword.clone(); + move |s: I::Slice, span| { + if keyword == s { + Ok(()) + } else { + Err(LabelError::expected_found( + [TextExpected::Identifier(keyword.clone())], + None, + span, + )) + } + } + }) + .to_slice() + .labelled(TextExpected::Identifier(keyword.clone())) + .as_builtin() + } + + /// Like [`char::is_whitespace`], but rejects the characters U+202A, U+202B, U+202C, U+202D, U+202E, U+2066, U+2067, U+2068, U+2069 + /// to mitigate against [CVE-2021-42574](https://nvd.nist.gov/vuln/detail/CVE-2021-42574) + pub fn is_whitespace(c: char) -> bool { + c.is_whitespace() + && !matches!( + c, + '\u{202A}' + | '\u{202B}' + | '\u{202C}' + | '\u{202D}' + | '\u{202E}' + | '\u{2066}' + | '\u{2067}' + | '\u{2068}' + | '\u{2069}' + ) + } +} + +#[cfg(test)] +mod tests { + use crate::prelude::*; + use std::fmt; + + fn make_ascii_kw_parser<'src, I>(s: I::Slice) -> impl Parser<'src, I, ()> + where + I: crate::StrInput<'src>, + I::Slice: PartialEq, + I::Token: crate::Char + fmt::Debug + 'src, + { + text::ascii::keyword(s).ignored() + } + + fn make_unicode_kw_parser<'src, I>(s: I::Slice) -> impl Parser<'src, I, ()> + where + I: crate::StrInput<'src>, + I::Slice: PartialEq, + I::Token: crate::Char + fmt::Debug + 'src, + { + text::unicode::keyword(s).ignored() + } + + fn test_ok<'src, P: Parser<'src, &'src str, &'src str>>(parser: P, input: &'src str) { + assert_eq!( + parser.parse(input), + ParseResult { + output: Some(input), + errs: vec![] + } + ); + } + + fn test_err<'src, P: Parser<'src, &'src str, &'src str>>(parser: P, input: &'src str) { + assert_eq!( + parser.parse(input), + ParseResult { + output: None, + errs: vec![EmptyErr::default()] + } + ); + } + + #[test] + fn keyword_good() { + make_ascii_kw_parser::<&str>("hello"); + make_ascii_kw_parser::<&str>("_42"); + make_ascii_kw_parser::<&str>("_42"); + + make_unicode_kw_parser::<&str>("שלו×"); + make_unicode_kw_parser::<&str>("привет"); + make_unicode_kw_parser::<&str>("你好"); + } + + #[test] + fn ident() { + let ident = text::ident::<&str, extra::Default>(); + test_ok(ident, "foo"); + test_ok(ident, "foo_bar"); + test_ok(ident, "foo_"); + test_ok(ident, "_foo"); + test_ok(ident, "_"); + test_ok(ident, "__"); + test_ok(ident, "__init__"); + test_err(ident, ""); + test_err(ident, "."); + test_err(ident, "123"); + } + + #[test] + fn whitespace() { + use crate::{whitespace, LabelError, TextExpected}; + + let parser = whitespace::<&str, extra::Err>>().exactly(1); + + assert_eq!( + parser.parse("").into_output_errors(), + ( + None, + vec![LabelError::<&str, _>::expected_found( + vec![TextExpected::<&str>::Whitespace], + None, + SimpleSpan::new((), 0..0) + )] + ) + ); + } + + /* + #[test] + #[should_panic] + fn keyword_numeric() { + make_ascii_kw_parser::<&str>("42"); + } + + #[test] + #[should_panic] + fn keyword_empty() { + make_ascii_kw_parser::<&str>(""); + } + + #[test] + #[should_panic] + fn keyword_not_alphanum() { + make_ascii_kw_parser::<&str>("hi\n"); + } + + #[test] + #[should_panic] + fn keyword_unicode_in_ascii() { + make_ascii_kw_parser::<&str>("שלו×"); + } + */ +} diff --git a/chumsky-0.12.0/src/tokio.rs b/chumsky-0.13.0/src/tokio.rs similarity index 100% rename from chumsky-0.12.0/src/tokio.rs rename to chumsky-0.13.0/src/tokio.rs diff --git a/chumsky-0.12.0/src/util.rs b/chumsky-0.13.0/src/util.rs similarity index 100% rename from chumsky-0.12.0/src/util.rs rename to chumsky-0.13.0/src/util.rs diff --git a/clickhouse-rs-1.1.0-alpha.1/.cargo-checksum.json b/clickhouse-rs-1.1.0-alpha.1/.cargo-checksum.json index 42b4a18a1e..a4f12c29f6 100644 --- a/clickhouse-rs-1.1.0-alpha.1/.cargo-checksum.json +++ b/clickhouse-rs-1.1.0-alpha.1/.cargo-checksum.json @@ -1 +1 @@ -{"files":{".github/workflows/test.yml":"a44ba3a53fe08ae3e67c2b7d5faf2782804e6f673876e74d7d57691afe437b2a","Cargo.toml":"db7a1d22873b58340f491d9a1f35f50bd1bde02cf10ff1995036c1f843590204","LICENSE":"7e74fd1b7c265e38ff3d76cca8490bf1b027ab1c84ef66cd7c3ae9ce2d2cb6bb","README.md":"606f21de7f15bb09e24f490a9eccca11075de01f18308a862a74eb712d6dd716","docker-compose.yml":"7405e9496cdfff2fca7e8e3ef476b5e335311c5e719d65d6c6300e6e4a544472","extras/ci/generate_certs.sh":"8aecf704f5d0478aea873ead62f2132108adbc0692338d6a3846a0e6296a81a1","extras/ci/overrides.xml":"43f6aba4302a0ce754c4315f067881c8a7f1b67a0a332dc76d434ffd57a3cd0f","extras/ci/users-overrides.yaml":"bd650d278e75b511c7e28b563a3ec6c44aa5110ddc479b398f226209f229cb5e","src/binary/encoder.rs":"018984e142e9e72b23400c62b7a449b081de5d13801ab99f240d54b817b8d7fd","src/binary/mod.rs":"d1830cbb412f6ccdf3e3331f547c03971c676eb355e0e1ed5da55b20f4b36818","src/binary/parser.rs":"74c5424b2dc0d412a3f32a56662ecf609cc1de09101617dc6f06518cf86ad51c","src/binary/protocol.rs":"7581dede0263642f1a83d4bea76a13831ad235561d38e955c35bf1d97bee3faa","src/binary/read_ex.rs":"b4fbaf7fdb29aa21c3f3b9e19d8748fc74d8ec5a7c2b9cf6b39741e0f86ce8bf","src/binary/uvarint.rs":"2bcb96792bd6dc55babc4318c360c5dbb5f51c432499bc508b56ba16d72230dd","src/client_info.rs":"d6803f3df873b29aa9a5cf083f386c1aeef19818708d5da81c91de717b7049d1","src/connecting_stream.rs":"8ca62d3c759389872cfcf959390296a21bc228e60cd0620428ac34e9dcfe840b","src/errors/codes.rs":"3f1044cebc749845a89f6bfe3585dc7c6a1c7d0ca5fa9ce3370bf25a67877455","src/errors/mod.rs":"71467e667bbb077061444a116361691545aa5641880fe54a34ffd71b9ea5de39","src/io/mod.rs":"1edee1b891d380597d57b47fa1baba988e140adcd016066aaa05d7a32c0b3453","src/io/read_to_end.rs":"85bdb7e73b751d979339aa2102705e760a7cdee439ba60523d97e1f0fc73820b","src/io/stream.rs":"c8543f8dee54cf74325e0667963b16b03b90a0512bf95f660ddf9b005e59fa3a","src/io/transport.rs":"4af793d966ce21d154565e0e34909e8d2ad0b6dedd78419be247721c51fbbddf","src/lib.rs":"8a367351575c64cae9f4f15c5a3b4a50d1611f34f022666088a7ae31aa3b6099","src/pool/futures/get_handle.rs":"c7ec470fcfe82ce8ce9be742e4fbd930e30634f859af86d3563f6ae8049cbd31","src/pool/futures/mod.rs":"cc57dcb80efe68df853b879c2e399f60cd084bbb6cfaf441c21ac895b9784962","src/pool/mod.rs":"8694b8d47c71acc8376ed11c8133be64c4edada63df4c368d4d2bd0d76de024c","src/retry_guard.rs":"14adde5cf00b002dbb994e2f196621facbba0de8c37595b8116d02d5e490d919","src/types/block/block_info.rs":"9c46680b6ec2c3ce961a218202412090788d8dab262845b0697341eb8cdbddf2","src/types/block/builder.rs":"27580c2a0eea756e62369357c9302c2a12a27618e36907ff8c7ad86308fe8a9e","src/types/block/chunk_iterator.rs":"e4d518b87d41f2deb6bf100841053028ee4adb332c216b0cb57eff812f80596f","src/types/block/compressed.rs":"93084a294a1b59a9124f51cc19c4babd33431bcb1d789974fab0a94e46b19a1f","src/types/block/mod.rs":"903f9ca82ec67048b93e3a5002516ddd0bbe9262c572d0a403c1b3feac8a006d","src/types/block/row.rs":"137a6b26d536522eec550ebe4394a2e3485b9046903eee3ad74685c23b68c3aa","src/types/cmd.rs":"9d6ef159c3658a516d0c8e8afa05c8dc179db4f88ff0a306f503f24943dbf575","src/types/column/array.rs":"df0a339ca8ec9d9e27c8ccbc34499760948068d8779e4c9982552f95dcc2a026","src/types/column/chrono_datetime.rs":"9dccad8b0daa04f953210c7d95623a4a44fc2f726e706c7357b2fb54917c3b40","src/types/column/chunk.rs":"f97e8ef5552556906a09d341f7801f9db52ae5802447c0b25d0f8528dc139eaf","src/types/column/column_data.rs":"b43afaf6ed5dadc433f5e70afac98e08ef59d70aa9c6f07cbfce23b1448ca7f4","src/types/column/concat.rs":"f747164beac642cd8d460934aa43e69e6b789afad9f1dcf44f498cca3295e482","src/types/column/date.rs":"5ac20098e1a239bc12a43f6ef1a01ce7d183493dffb8930cecd9910cb326b99d","src/types/column/datetime64.rs":"b2656dbd3e328fcdafca6142f4b42b3326b1915b8b3cc77be9f3fc94b746f1e6","src/types/column/decimal.rs":"23f3e84604da0b7cbcb5b26f9087cfe471eb92e4692daa9d0c538daded7f91d3","src/types/column/enums.rs":"c3ece1b27296333a7d7656cb0bd2bde777b8e2cddcfc1cd4d923aecf7f37f0e4","src/types/column/factory.rs":"e66c5822736dc8a538ac1d98e850f2d06e7e948d88992ceb90c92eaede6e5b45","src/types/column/fixed_string.rs":"21ea7e833d7293a4f1347bf9f49a79d663cd0a81140d00e647a2c00f66997b94","src/types/column/ip.rs":"e4736a9314eb8eaac22a64cf5ad53d072d6652c27c1a29bc364ea700a80ef89d","src/types/column/iter/mod.rs":"e4b065729e2262ee6c2e879a437c8b0e33897a7a12842b612da76ed0ce9c4340","src/types/column/list.rs":"af7daf8628c6efa417c17c99a3a5da47c5f956674acfbd3baa7694ae6043feef","src/types/column/low_cardinality.rs":"f298c24c7f15f226c42a3d7ad4d6ecbabd0b59f33f86c42ca238069106710c53","src/types/column/map.rs":"aad9f179836346a5eaf1e1a2225ce08867b442608b9514861dcf6348f1907c1d","src/types/column/mod.rs":"e03d4d10122720315919103c5b984604195fcd8091350698795055e5f98bce34","src/types/column/nullable.rs":"75200e8cdc48b7dea323f5b8a86bfc9528e68ec231ac60b33afbeef5a1d481f4","src/types/column/numeric.rs":"40c44c556128a2979e78860cce0d15954b5e8219bf6fbb89a39c6eb367b65a6c","src/types/column/simple_agg_func.rs":"0e1f2137640cdaf66880f16fa1b836592992b7f12752e29465d1c26734d2b5f6","src/types/column/string.rs":"77c24092f9d064877e4ea95f71bcbe0259be0863681daafb0770559d665a6586","src/types/column/string_pool.rs":"c0c78ba447e081c436a4f369a2c72784c0ac7dd5185cab07c4196bcd48d8886f","src/types/column/util.rs":"fabc2e5f0f28c4ebd051041b4aac45807c0f50b54c59dfc9109d27cd073d9015","src/types/date_converter.rs":"e1d749c8db4066483c10084a16c59709003380e3223401e4067d635f299c5ee8","src/types/decimal.rs":"0de3050026cad1376d540dcb09bc767357d16111b150d5cd65bdb43039a5ec4b","src/types/enums.rs":"2cc7c2d63acb553ec32e7d615b40c1bfebeb5c6e4b2e9a27d1a35a1d84acf066","src/types/from_sql.rs":"d10f7bd54ea625dcac95333ea0e3c885bc56a2abc68309000923f1e057dd8af7","src/types/marshal.rs":"2e162969b0a04d9794227484e0c54815dc756e7aa942c1c4c6cc73254812712e","src/types/mod.rs":"3d32d217f40710fe9db463aa75ed0d8c09c8218d9fa559f2bc2f68cf4e0404ca","src/types/options.rs":"f33a3b0f26240ccfdd1931484ecd4960a50f70481204177d492181f0e5713de0","src/types/query.rs":"4ed13a178a05ca9ba7ce3908dfa116c37605c827a885f7bdaabc4aa9610f3a48","src/types/query_result/mod.rs":"dcd3a36d55809f5bbe3ee38625b72d8e9e21926f4f9e3708c997c8bdda4b14a9","src/types/query_result/stream_blocks.rs":"db07e98676e131336101db5ca76f1c49508f4bd762ca9596f18c55742fe09a5a","src/types/stat_buffer.rs":"09430114929c31fba82cad4c0774bc40d4e733d0d15af43d5bdcc70df57365b9","src/types/unmarshal.rs":"3b7e8cd456fcaf10171ca2c845d65b6e1f2207c671c10a53c9a7b781a086139c","src/types/value.rs":"00a3c2a34031427c8db85fbb2f26fe6f67d92c0aae1cc93a529cfb7800c3745f","src/types/value_ref.rs":"be8d6f20affa58f16a60db3f8895ade3e5901bc328a9b2f44b9d7f0ded18cbd0"},"package":null} \ No newline at end of file +{"files":{".github/workflows/test.yml":"a44ba3a53fe08ae3e67c2b7d5faf2782804e6f673876e74d7d57691afe437b2a","Cargo.toml":"a770c2f08bec0c2aa9a923e074cc32b0e41c7d6ec3d06077ad8125f2994b12ce","LICENSE":"7e74fd1b7c265e38ff3d76cca8490bf1b027ab1c84ef66cd7c3ae9ce2d2cb6bb","README.md":"606f21de7f15bb09e24f490a9eccca11075de01f18308a862a74eb712d6dd716","docker-compose.yml":"7405e9496cdfff2fca7e8e3ef476b5e335311c5e719d65d6c6300e6e4a544472","extras/ci/generate_certs.sh":"8aecf704f5d0478aea873ead62f2132108adbc0692338d6a3846a0e6296a81a1","extras/ci/overrides.xml":"43f6aba4302a0ce754c4315f067881c8a7f1b67a0a332dc76d434ffd57a3cd0f","extras/ci/users-overrides.yaml":"bd650d278e75b511c7e28b563a3ec6c44aa5110ddc479b398f226209f229cb5e","src/binary/encoder.rs":"018984e142e9e72b23400c62b7a449b081de5d13801ab99f240d54b817b8d7fd","src/binary/mod.rs":"d1830cbb412f6ccdf3e3331f547c03971c676eb355e0e1ed5da55b20f4b36818","src/binary/parser.rs":"74c5424b2dc0d412a3f32a56662ecf609cc1de09101617dc6f06518cf86ad51c","src/binary/protocol.rs":"7581dede0263642f1a83d4bea76a13831ad235561d38e955c35bf1d97bee3faa","src/binary/read_ex.rs":"b4fbaf7fdb29aa21c3f3b9e19d8748fc74d8ec5a7c2b9cf6b39741e0f86ce8bf","src/binary/uvarint.rs":"2bcb96792bd6dc55babc4318c360c5dbb5f51c432499bc508b56ba16d72230dd","src/client_info.rs":"d6803f3df873b29aa9a5cf083f386c1aeef19818708d5da81c91de717b7049d1","src/connecting_stream.rs":"084b22eb11cb8bc08352dc8edea801f76d88ded28db6f32557918d450c14787c","src/errors/codes.rs":"3f1044cebc749845a89f6bfe3585dc7c6a1c7d0ca5fa9ce3370bf25a67877455","src/errors/mod.rs":"71467e667bbb077061444a116361691545aa5641880fe54a34ffd71b9ea5de39","src/io/mod.rs":"1edee1b891d380597d57b47fa1baba988e140adcd016066aaa05d7a32c0b3453","src/io/read_to_end.rs":"85bdb7e73b751d979339aa2102705e760a7cdee439ba60523d97e1f0fc73820b","src/io/stream.rs":"c8543f8dee54cf74325e0667963b16b03b90a0512bf95f660ddf9b005e59fa3a","src/io/transport.rs":"4af793d966ce21d154565e0e34909e8d2ad0b6dedd78419be247721c51fbbddf","src/lib.rs":"8a367351575c64cae9f4f15c5a3b4a50d1611f34f022666088a7ae31aa3b6099","src/pool/futures/get_handle.rs":"c7ec470fcfe82ce8ce9be742e4fbd930e30634f859af86d3563f6ae8049cbd31","src/pool/futures/mod.rs":"cc57dcb80efe68df853b879c2e399f60cd084bbb6cfaf441c21ac895b9784962","src/pool/mod.rs":"8694b8d47c71acc8376ed11c8133be64c4edada63df4c368d4d2bd0d76de024c","src/retry_guard.rs":"14adde5cf00b002dbb994e2f196621facbba0de8c37595b8116d02d5e490d919","src/types/block/block_info.rs":"9c46680b6ec2c3ce961a218202412090788d8dab262845b0697341eb8cdbddf2","src/types/block/builder.rs":"27580c2a0eea756e62369357c9302c2a12a27618e36907ff8c7ad86308fe8a9e","src/types/block/chunk_iterator.rs":"e4d518b87d41f2deb6bf100841053028ee4adb332c216b0cb57eff812f80596f","src/types/block/compressed.rs":"93084a294a1b59a9124f51cc19c4babd33431bcb1d789974fab0a94e46b19a1f","src/types/block/mod.rs":"903f9ca82ec67048b93e3a5002516ddd0bbe9262c572d0a403c1b3feac8a006d","src/types/block/row.rs":"137a6b26d536522eec550ebe4394a2e3485b9046903eee3ad74685c23b68c3aa","src/types/cmd.rs":"9d6ef159c3658a516d0c8e8afa05c8dc179db4f88ff0a306f503f24943dbf575","src/types/column/array.rs":"df0a339ca8ec9d9e27c8ccbc34499760948068d8779e4c9982552f95dcc2a026","src/types/column/chrono_datetime.rs":"9dccad8b0daa04f953210c7d95623a4a44fc2f726e706c7357b2fb54917c3b40","src/types/column/chunk.rs":"f97e8ef5552556906a09d341f7801f9db52ae5802447c0b25d0f8528dc139eaf","src/types/column/column_data.rs":"b43afaf6ed5dadc433f5e70afac98e08ef59d70aa9c6f07cbfce23b1448ca7f4","src/types/column/concat.rs":"f747164beac642cd8d460934aa43e69e6b789afad9f1dcf44f498cca3295e482","src/types/column/date.rs":"5ac20098e1a239bc12a43f6ef1a01ce7d183493dffb8930cecd9910cb326b99d","src/types/column/datetime64.rs":"b2656dbd3e328fcdafca6142f4b42b3326b1915b8b3cc77be9f3fc94b746f1e6","src/types/column/decimal.rs":"23f3e84604da0b7cbcb5b26f9087cfe471eb92e4692daa9d0c538daded7f91d3","src/types/column/enums.rs":"c3ece1b27296333a7d7656cb0bd2bde777b8e2cddcfc1cd4d923aecf7f37f0e4","src/types/column/factory.rs":"e66c5822736dc8a538ac1d98e850f2d06e7e948d88992ceb90c92eaede6e5b45","src/types/column/fixed_string.rs":"21ea7e833d7293a4f1347bf9f49a79d663cd0a81140d00e647a2c00f66997b94","src/types/column/ip.rs":"e4736a9314eb8eaac22a64cf5ad53d072d6652c27c1a29bc364ea700a80ef89d","src/types/column/iter/mod.rs":"e4b065729e2262ee6c2e879a437c8b0e33897a7a12842b612da76ed0ce9c4340","src/types/column/list.rs":"af7daf8628c6efa417c17c99a3a5da47c5f956674acfbd3baa7694ae6043feef","src/types/column/low_cardinality.rs":"f298c24c7f15f226c42a3d7ad4d6ecbabd0b59f33f86c42ca238069106710c53","src/types/column/map.rs":"aad9f179836346a5eaf1e1a2225ce08867b442608b9514861dcf6348f1907c1d","src/types/column/mod.rs":"e03d4d10122720315919103c5b984604195fcd8091350698795055e5f98bce34","src/types/column/nullable.rs":"75200e8cdc48b7dea323f5b8a86bfc9528e68ec231ac60b33afbeef5a1d481f4","src/types/column/numeric.rs":"40c44c556128a2979e78860cce0d15954b5e8219bf6fbb89a39c6eb367b65a6c","src/types/column/simple_agg_func.rs":"0e1f2137640cdaf66880f16fa1b836592992b7f12752e29465d1c26734d2b5f6","src/types/column/string.rs":"77c24092f9d064877e4ea95f71bcbe0259be0863681daafb0770559d665a6586","src/types/column/string_pool.rs":"c0c78ba447e081c436a4f369a2c72784c0ac7dd5185cab07c4196bcd48d8886f","src/types/column/util.rs":"fabc2e5f0f28c4ebd051041b4aac45807c0f50b54c59dfc9109d27cd073d9015","src/types/date_converter.rs":"e1d749c8db4066483c10084a16c59709003380e3223401e4067d635f299c5ee8","src/types/decimal.rs":"0de3050026cad1376d540dcb09bc767357d16111b150d5cd65bdb43039a5ec4b","src/types/enums.rs":"2cc7c2d63acb553ec32e7d615b40c1bfebeb5c6e4b2e9a27d1a35a1d84acf066","src/types/from_sql.rs":"d10f7bd54ea625dcac95333ea0e3c885bc56a2abc68309000923f1e057dd8af7","src/types/marshal.rs":"2e162969b0a04d9794227484e0c54815dc756e7aa942c1c4c6cc73254812712e","src/types/mod.rs":"3d32d217f40710fe9db463aa75ed0d8c09c8218d9fa559f2bc2f68cf4e0404ca","src/types/options.rs":"f33a3b0f26240ccfdd1931484ecd4960a50f70481204177d492181f0e5713de0","src/types/query.rs":"4ed13a178a05ca9ba7ce3908dfa116c37605c827a885f7bdaabc4aa9610f3a48","src/types/query_result/mod.rs":"dcd3a36d55809f5bbe3ee38625b72d8e9e21926f4f9e3708c997c8bdda4b14a9","src/types/query_result/stream_blocks.rs":"db07e98676e131336101db5ca76f1c49508f4bd762ca9596f18c55742fe09a5a","src/types/stat_buffer.rs":"09430114929c31fba82cad4c0774bc40d4e733d0d15af43d5bdcc70df57365b9","src/types/unmarshal.rs":"3b7e8cd456fcaf10171ca2c845d65b6e1f2207c671c10a53c9a7b781a086139c","src/types/value.rs":"00a3c2a34031427c8db85fbb2f26fe6f67d92c0aae1cc93a529cfb7800c3745f","src/types/value_ref.rs":"be8d6f20affa58f16a60db3f8895ade3e5901bc328a9b2f44b9d7f0ded18cbd0"},"package":null} \ No newline at end of file diff --git a/clickhouse-rs-1.1.0-alpha.1/Cargo.toml b/clickhouse-rs-1.1.0-alpha.1/Cargo.toml index 7eea98002f..86d048eac9 100644 --- a/clickhouse-rs-1.1.0-alpha.1/Cargo.toml +++ b/clickhouse-rs-1.1.0-alpha.1/Cargo.toml @@ -107,11 +107,18 @@ version = "0.2" optional = true [dependencies.rustls] -version = "0.22.1" +version = "0.23" +features = [ + "ring", + "logging", + "std", + "tls12", +] optional = true +default-features = false [dependencies.rustls-pemfile] -version = "2.0" +version = "2" optional = true [dependencies.tokio] @@ -131,11 +138,17 @@ version = "^0.3" optional = true [dependencies.tokio-rustls] -version = "0.25.0" +version = "0.26" +features = [ + "ring", + "logging", + "tls12", +] optional = true +default-features = false [dependencies.webpki-roots] -version = "*" +version = "1" optional = true [dev-dependencies] diff --git a/clickhouse-rs-1.1.0-alpha.1/src/connecting_stream.rs b/clickhouse-rs-1.1.0-alpha.1/src/connecting_stream.rs index de06b0baf7..f173ad4ee2 100644 --- a/clickhouse-rs-1.1.0-alpha.1/src/connecting_stream.rs +++ b/clickhouse-rs-1.1.0-alpha.1/src/connecting_stream.rs @@ -273,8 +273,23 @@ impl ConnectingStream { state: State::tls_host_err(), }, Some(host) => { + // builder_with_provider() instead of builder(): the latter panics if + // more than one crypto provider feature is enabled in the final + // dependency graph (e.g. a downstream crate enables aws-lc-rs). + let config_builder = match ClientConfig::builder_with_provider(Arc::new( + rustls::crypto::ring::default_provider(), + )) + .with_safe_default_protocol_versions() + { + Ok(config_builder) => config_builder, + Err(err) => { + return Self { + state: State::tls_err(err), + }; + } + }; let builder = if options.skip_verify { - ClientConfig::builder() + config_builder .dangerous() .with_custom_certificate_verifier(Arc::new(DummyTlsVerifier)) } else { @@ -302,8 +317,7 @@ impl ConnectingStream { } } } - ClientConfig::builder() - .with_root_certificates(cert_store) + config_builder.with_root_certificates(cert_store) }; let config = if let Some(identity) = &options.client_tls_identity { let ClientTlsIdentity::Pem { key, certs } = identity; diff --git a/cursive-0.21.1/.cargo-checksum.json b/cursive-0.21.1/.cargo-checksum.json index ce8d8e7808..0f80777efb 100644 --- a/cursive-0.21.1/.cargo-checksum.json +++ b/cursive-0.21.1/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"050341c32308e754568f633d3b0b6117eff9b90eb126bfd5d8b4c625899b128b","LICENSE":"34732cfadbccc51ae3660f332b609c491005993d21735c9b949a0b6821b34c1d","README.md":"5762bbec909f7fb91f70de0eedb2b68596e1af99b58d6161dfbc05cb9b1c265a","src/backends/blt.rs":"feb9f350e8ac42ae91b630a69cce2ecf5e5aa856e9f5af71ecf8f8fa3e08d36c","src/backends/crossterm.rs":"23929dcf261e8059c42e97438b7f8e5c5e89232f10f55b5ea3c97af18e349cab","src/backends/curses/mod.rs":"866fc50e9c5f636d093a45634069262269f561e3b7861b6b74b98a6f2bdf8805","src/backends/curses/n.rs":"09cedc3c66b00f1758975dea3fdc2466199647129003634c33c5df6c54db7834","src/backends/curses/pan.rs":"dd8f611205854e3f831e65cfef32be009402c26e5a6f7c646ca37c4a8cc4ac3b","src/backends/mod.rs":"32c233f21c2e7bafa68befb47465b900384cb726edcb1caae844b21363d10731","src/backends/puppet/mod.rs":"d528b3c3e284563b8dbeb876422e2507109c286c14efc9cf475c9a78d9593a86","src/backends/puppet/observed.rs":"7f26dc759976b6804fa186b04802b429b49647f09792dfe19ed58c0250fd940c","src/backends/puppet/observed_screen_view.rs":"ba6729537a95fa2859643f1997bb49a02204a58a8f89d25f599256eb1546bd21","src/backends/puppet/static_values.rs":"3772f881116b1696fa37662a4263242bb05cfe7083063226e23a2ba3439e1891","src/backends/resize.rs":"511cb513dfaca004b7b5d84012c84ecdc3733c36336b51180ac0475472404d5d","src/backends/termion.rs":"9f5e378655d956df8262f162a1c9a858d3cbb98f109253163aa7d309b33b540d","src/cursive_ext.rs":"8bbc4c6cd8c3c56e14bc8ce36b9a1c98ac0e173e3172c0d28ff8402119e526f8","src/cursive_runnable.rs":"e9799238d482fd062948c21d40756de7c8ab3f57b1e66dba6001f6a9f6032333","src/lib.rs":"6f32ddf2ccd6860b7cc5e64bdbc3497ba2d4781600d831a1945d39b143b12a64","src/utf8.rs":"02810ac852ec19f08d620db29e2dffa813514ee34ed46dd94920421138be41a8"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"3301873ff5e1e364ca9c679aa6b81d06c39819afa11884591458915043778d0c","LICENSE":"34732cfadbccc51ae3660f332b609c491005993d21735c9b949a0b6821b34c1d","README.md":"acc65cb3b33217d537b621233126b2e388eb51221078abf7d7fd4030f5b1ff44","src/backends/blt.rs":"feb9f350e8ac42ae91b630a69cce2ecf5e5aa856e9f5af71ecf8f8fa3e08d36c","src/backends/crossterm.rs":"f385bd55bcf8eb31b506b1db330911a52ffd5215e54b6d428ae8d26698ae6311","src/backends/curses/mod.rs":"866fc50e9c5f636d093a45634069262269f561e3b7861b6b74b98a6f2bdf8805","src/backends/curses/n.rs":"09cedc3c66b00f1758975dea3fdc2466199647129003634c33c5df6c54db7834","src/backends/curses/pan.rs":"dd8f611205854e3f831e65cfef32be009402c26e5a6f7c646ca37c4a8cc4ac3b","src/backends/mod.rs":"32c233f21c2e7bafa68befb47465b900384cb726edcb1caae844b21363d10731","src/backends/puppet/mod.rs":"d528b3c3e284563b8dbeb876422e2507109c286c14efc9cf475c9a78d9593a86","src/backends/puppet/observed.rs":"115625d197da5885bd252da44988255b117a6092434fb74ddcf68a8da2810e9f","src/backends/puppet/observed_screen_view.rs":"ba6729537a95fa2859643f1997bb49a02204a58a8f89d25f599256eb1546bd21","src/backends/puppet/static_values.rs":"3772f881116b1696fa37662a4263242bb05cfe7083063226e23a2ba3439e1891","src/backends/resize.rs":"511cb513dfaca004b7b5d84012c84ecdc3733c36336b51180ac0475472404d5d","src/backends/termion.rs":"9f5e378655d956df8262f162a1c9a858d3cbb98f109253163aa7d309b33b540d","src/cursive_ext.rs":"8bbc4c6cd8c3c56e14bc8ce36b9a1c98ac0e173e3172c0d28ff8402119e526f8","src/cursive_runnable.rs":"e9799238d482fd062948c21d40756de7c8ab3f57b1e66dba6001f6a9f6032333","src/lib.rs":"6f32ddf2ccd6860b7cc5e64bdbc3497ba2d4781600d831a1945d39b143b12a64","src/utf8.rs":"02810ac852ec19f08d620db29e2dffa813514ee34ed46dd94920421138be41a8"},"package":null} \ No newline at end of file diff --git a/cursive-0.21.1/Cargo.toml b/cursive-0.21.1/Cargo.toml index f50a0b0d69..c3416545c1 100644 --- a/cursive-0.21.1/Cargo.toml +++ b/cursive-0.21.1/Cargo.toml @@ -118,7 +118,7 @@ optional = true [dev-dependencies] parking_lot = "0.12" pretty-bytes = "0.2" -rand = "0.9" +rand = "0.10" serde_json = "1.0.85" serde_yaml = "0.9.13" diff --git a/cursive-0.21.1/README.md b/cursive-0.21.1/README.md index 03b478c5fa..d0d5c6c79c 100644 --- a/cursive-0.21.1/README.md +++ b/cursive-0.21.1/README.md @@ -72,18 +72,20 @@ These tutorials may help you get started with cursive: Here are a few crates implementing new views for you to use: -* [cursive-aligned-view](https://github.com/deinstapel/cursive-aligned-view): A view wrapper for gyscos/cursive views which aligns child views. +* [cursive-aligned-view](https://github.com/deinstapel/cursive-aligned-view): A view wrapper which aligns child views. * [cursive-async-view](https://github.com/deinstapel/cursive-async-view): A loading-screen wrapper. -* [cursive-flexi-logger-view](https://github.com/deinstapel/cursive-flexi-logger-view): An alternative debug view using `emabee/flexi_logger`. +* [cursive_calendar_view](https://github.com/BonsaiDen/cursive_calendar_view): A basic calendar view implementation. +* [cursive-flexi-logger-view](https://github.com/deinstapel/cursive-flexi-logger-view): An alternative debug view using [flexi_logger](https://github.com/emabee/flexi_logger). +* [cursive_hexview](https://github.com/hellow554/cursive_hexview): A simple hexview. +* [cursive-hjkl](https://github.com/gamma-delta/cursive-hjkl): Wraps any view to use Vim-like `hjkl` controls. +* [cursive-image](https://codeberg.org/tliron/cursive-image): A graphical image view. Supported formats include PNG, GIF, JPEG, SVG, and PDF. * [cursive-markup](https://sr.ht/~ireas/cursive-markup-rs): A view that renders HTML or other markup. * [cursive-multiplex](https://github.com/deinstapel/cursive-multiplex): A tmux like multiplexer. * [cursive-spinner-view](https://github.com/otov4its/cursive-spinner-view): A spinner view. -* [cursive-tabs](https://github.com/deinstapel/cursive-tabs): Tabs. -* [cursive_calendar_view](https://github.com/BonsaiDen/cursive_calendar_view): A basic calendar view implementation. -* [cursive_hexview](https://github.com/hellow554/cursive_hexview): A simple hexview. +* [cursive-split-panel](https://codeberg.org/tliron/cursive-split-panel): A split panel view with a movable divider. * [cursive_table_view](https://github.com/BonsaiDen/cursive_table_view): A basic table view component. -* [cursive_tree_view](https://github.com/BonsaiDen/cursive_tree_view): A tree view implementation. -* [cursive-hjkl](https://github.com/gamma-delta/cursive-hjkl): Wraps any view to use Vim-like `hjkl` controls. +* [cursive-tabs](https://github.com/deinstapel/cursive-tabs): Tabs. +* [cursive-tree](https://codeberg.org/tliron/cursive-tree): A tree view implementation. ## Showcases diff --git a/cursive-0.21.1/src/backends/crossterm.rs b/cursive-0.21.1/src/backends/crossterm.rs index d3c3a2d025..9236e7da0c 100644 --- a/cursive-0.21.1/src/backends/crossterm.rs +++ b/cursive-0.21.1/src/backends/crossterm.rs @@ -258,7 +258,7 @@ impl Backend { }); } - fn stdout_mut(&self) -> RefMut> { + fn stdout_mut(&self) -> RefMut<'_, BufWriter> { self.stdout.borrow_mut() } @@ -289,10 +289,8 @@ impl Backend { } MouseEventKind::ScrollDown => MouseEvent::WheelDown, MouseEventKind::ScrollUp => MouseEvent::WheelUp, - MouseEventKind::ScrollLeft | MouseEventKind::ScrollRight => { - // TODO: Currently unsupported. - return None; - } + MouseEventKind::ScrollLeft => MouseEvent::WheelLeft, + MouseEventKind::ScrollRight => MouseEvent::WheelRight, }; Event::Mouse { diff --git a/cursive-0.21.1/src/backends/puppet/observed.rs b/cursive-0.21.1/src/backends/puppet/observed.rs index cee4de1be6..8b75f3d3a7 100644 --- a/cursive-0.21.1/src/backends/puppet/observed.rs +++ b/cursive-0.21.1/src/backends/puppet/observed.rs @@ -169,7 +169,7 @@ impl ObservedScreen { } /// Returns a rectangular subset of observed screen. - pub fn piece(&self, min: Vec2, max: Vec2) -> ObservedPiece { + pub fn piece(&self, min: Vec2, max: Vec2) -> ObservedPiece<'_> { ObservedPiece::new(self, min, max) } @@ -179,7 +179,7 @@ impl ObservedScreen { } /// Returns occurrences of given string pattern - pub fn find_occurences(&self, pattern: &str) -> Vec { + pub fn find_occurences(&self, pattern: &str) -> Vec> { // TODO(njskalski): test for two-cell letters. // TODO(njskalski): fails with whitespaces like "\t". @@ -282,7 +282,7 @@ pub trait ObservedPieceInterface { /// Returns expanded sibling of this piece /// /// Asserts if request can be satisfied. - fn expanded(&self, up_left: Vec2, down_right: Vec2) -> ObservedPiece { + fn expanded(&self, up_left: Vec2, down_right: Vec2) -> ObservedPiece<'_> { assert!(self.min().x >= up_left.x); assert!(self.min().y >= up_left.y); assert!(self.max().x + down_right.x <= self.parent().size.x); diff --git a/cursive-macros-0.1.0/.cargo-checksum.json b/cursive-macros-0.1.0/.cargo-checksum.json index 4bcbb2efec..cbf2250ae7 100644 --- a/cursive-macros-0.1.0/.cargo-checksum.json +++ b/cursive-macros-0.1.0/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"d3b8b2d47ff0d26eac0f848bd652e35cdf4c27706fdc32ff87fd42f42f4e85e5","LICENSE":"34732cfadbccc51ae3660f332b609c491005993d21735c9b949a0b6821b34c1d","src/builder/blueprint.rs":"5fef537657890c64bf4bf1d9aeb4e6e9d5064e2b1b591844b044cf33828facf6","src/builder/callback_helper.rs":"6bc011f6ab60f8d158523a0c12835fc1e379ca07846a58cf8e36cb4dee3369da","src/builder/dummy_mod.rs":"061107acdc56259523999565b50bbd2914f85ca1ebff34405a8552f57c2dcd40","src/builder/mod.rs":"fb257f4b5a9f5bf9a801ecac3ce041409f582b544da743ba24feeb84af22da43","src/builder/real_mod.rs":"c2e79e53717c46452a655b2f48b3fd86830fefcd45f28efc1572ee2fff3daaff","src/lib.rs":"2b6782f48c400e20418bff75fcbb3fc913afc2373bd3bbaf4092e390052a0034"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"b116577eab099b822d1aa4bcb2eb032c36d3eeccac03812f27ff80cd718d74ff","LICENSE":"34732cfadbccc51ae3660f332b609c491005993d21735c9b949a0b6821b34c1d","src/builder/blueprint.rs":"5fef537657890c64bf4bf1d9aeb4e6e9d5064e2b1b591844b044cf33828facf6","src/builder/callback_helper.rs":"6bc011f6ab60f8d158523a0c12835fc1e379ca07846a58cf8e36cb4dee3369da","src/builder/dummy_mod.rs":"061107acdc56259523999565b50bbd2914f85ca1ebff34405a8552f57c2dcd40","src/builder/mod.rs":"fb257f4b5a9f5bf9a801ecac3ce041409f582b544da743ba24feeb84af22da43","src/builder/real_mod.rs":"c2e79e53717c46452a655b2f48b3fd86830fefcd45f28efc1572ee2fff3daaff","src/lib.rs":"2b6782f48c400e20418bff75fcbb3fc913afc2373bd3bbaf4092e390052a0034"},"package":null} \ No newline at end of file diff --git a/cursive-macros-0.1.0/Cargo.toml b/cursive-macros-0.1.0/Cargo.toml index ed8c2f9f0a..ecc1031de6 100644 --- a/cursive-macros-0.1.0/Cargo.toml +++ b/cursive-macros-0.1.0/Cargo.toml @@ -55,7 +55,7 @@ proc-macro = true proc-macro2 = "1.0.47" [dependencies.find-crate] -version = "0.6.3" +version = "0.7.1" optional = true [dependencies.quote] diff --git a/cursive_core-0.4.6/.cargo-checksum.json b/cursive_core-0.4.6/.cargo-checksum.json index 746ab127ab..2843fb5782 100644 --- a/cursive_core-0.4.6/.cargo-checksum.json +++ b/cursive_core-0.4.6/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"634e558f0211e8c84a8a11b561a820515d0192d9f2348cb5e70f53d6f4b737e4","LICENSE":"34732cfadbccc51ae3660f332b609c491005993d21735c9b949a0b6821b34c1d","README.md":"1ce27134320158c9df070b90c82e0dfd0b473a0db7521ce53780d699897938dc","src/align.rs":"acc1241aabbde51665a408330f30d21b87c2ca53027a46ce8c25b301e43166df","src/backend.rs":"ba01add5f407671d9cf7575e9162d69a735f0e918e23632d0714756c65f52454","src/buffer.rs":"bdf6a064737a867ba98725349fe383f390b7cb44540d0ad2060876ebd71dbf50","src/builder.rs":"df6b110480cbf0f9cf6797e16a42026bae6904bf147b8a07a6d3b323db368319","src/builder/resolvable.rs":"f50e36634ec7a729743af57d2adee9310b545fbbfc824524de2cae5149f0d740","src/cursive_root.rs":"1cf4f4b156dc2d8bb237035a02cd9c3153ca5a91cd81dd942f2e4c9b9f9209dd","src/cursive_run.rs":"9093ab351438f07006eab79a750d4ab4be0efb20f8dcc3de8b8e8d70bace9bc4","src/direction.rs":"f30cd3380b806662ef9991f354bedf03a2a06e8354b2941b57126a851d93a852","src/div.rs":"7746ed8f3cd73dd7ca9cd8b48abec0d98c166832d031820ce9da091f2d11ff11","src/dump.rs":"e989b6cbb2545f2ac685562417cbef24c5d94d3a5432c897213eecb927c3cf63","src/event.rs":"b0c527e4e12578d8e6b9ea2ccbc78c71733cd4a0df412646b8898cc432c3d422","src/lib.rs":"f743e0d3ac0a84d02166797b29782ad7efbf9efe470dfc23b6c94812ebc8bef5","src/logger.rs":"77be95d2eb15d3ea77c39d2626fa90e7d95aaabb10529d50f210405862089484","src/menu.rs":"465f5cb33168192aa9e2bd14b0bfbeddf383215f0e0f8a96160ea41d913e85ab","src/printer.rs":"808c86c4b8cb1164ca9da5f9308237eca4ecb744b0afde4a5d163f5c3de3bcdb","src/rect.rs":"b0820bb417fd767aaeed8d5621aa3b144c92680b09cc164a289d6883d7ef45af","src/style/border_style.rs":"ecb47c1dd46f8dfa8539d77a8b256c6ee93d9c32879d8d0e01fe9162b8a5aa80","src/style/color.rs":"68470ef8d27151df73d2262b9c6d44113c15038c2f3cd45b4d53f4d82dea39a5","src/style/color_pair.rs":"a9127c1475dfe93097ed8b4fa65f21b091c384ea07f46719b74d274a13cef131","src/style/color_style.rs":"86eeaaf47305be63d130468b913934dd6e1016941714487b62bb16da00fc7e68","src/style/effect.rs":"4a0831b6a621f0215a7e1c7bc7c8808c8f3270d45fb420f94d156f478a192b22","src/style/gradient/mod.rs":"9a406f0c51541628da14c47dad44b5ca18a64aabe323aa3cb2538c4dd591f543","src/style/mod.rs":"dc6f966d8a0790601632301989007247ddd108876766cd3a0bc535ed83d06b64","src/style/palette.rs":"d7210658b0d40b1a965141f02b517a744e5bcfacc229abb0ac151e89232adc48","src/style/style_types.rs":"c1efedddf7d42e40450803c3fb3da4f8b2ffba523aa10513bf43c26ef59489d8","src/theme.rs":"e18d580486497e7c02a769b729885fac6ad842ee06a13df687fadbb187f59964","src/traits.rs":"70d3cd55a34e8e25673a7bd08c4a15b1b89683eca5f8e8426f3edced51859e71","src/utils/counter.rs":"4e8363289075758aff080b5891a879fc91654524b8b2d6a3d9d41a1c40c09e7d","src/utils/immutify.rs":"c9841123156d04342d364068036a084db7755cf5c3028d2b19c06a6853549a56","src/utils/lines/mod.rs":"57b7fc239776dc432d62cac140a57018801c79646f7f925c2021e4943fe29273","src/utils/lines/simple/lines_iterator.rs":"beda089b64e918a84cac555092d827c75071137eef299e4a6616019d93f62502","src/utils/lines/simple/mod.rs":"28d52b0c7230622519717c47fb822f1fa55502a252f5e51870ddc52bea6b54b1","src/utils/lines/simple/row.rs":"f290bdb78174abeb0d3c1c200b2bf44134958f533184342c3f8fc808f980164e","src/utils/lines/simple/tests.rs":"34cd24140abec6d7a9350a4bfa15edaa79ff6519d22a6f850ed0e328628e873d","src/utils/lines/spans/chunk.rs":"4d9cab8bc6adeb7a9c08e4d793bf227f432bcc6882b76ce3a87b68002adc4cf3","src/utils/lines/spans/chunk_iterator.rs":"a1f84da07034601ffb730344356056828200caf2e69a7d0f72e4223bafe1e4a9","src/utils/lines/spans/lines_iterator.rs":"54a50a65c252e51f2656792d5c8420c5bcecd416495b627313a0d90536d799f5","src/utils/lines/spans/mod.rs":"f23da445e0d81ed41c6c38ed87820df7b60e5cca5e0c56d9d0135f943e6df3e5","src/utils/lines/spans/prefix.rs":"5a66e3ab40aa8d5bf9a8e1cdc763f1f24c7edcec8c657f5c413791c2d0c939c3","src/utils/lines/spans/row.rs":"8951bf2c7cfb5241a9f379be8fd8f683eba122f2249aed6f1df2ef003ca66e05","src/utils/lines/spans/segment.rs":"cee93d64e470f11b8472540d7475388d10a013882149d65ad6b08c97c1dafca3","src/utils/lines/spans/segment_merge_iterator.rs":"62572b59908e32b64dfc541f9f62f186e7cc97cc97696e936481413637c72fc4","src/utils/lines/spans/tests.rs":"32522f12a738559bc10bd998dd071d55978b739cf80c2158909d5cb58b9e8c70","src/utils/markup/ansi.rs":"db91ce45e30f7883c1d4caa60f5b51b4fc2ff5a2e6dfb7941733651e45217641","src/utils/markup/cursup.rs":"257bd992876c479f2bd75640c52e287fc8c495cc56b0f4502d7c6d0885bf77c2","src/utils/markup/gradient.rs":"e73d1f5e95df39486ad07425835bdde25d1a933bfd19e6a4aef1a6e5b35b40f6","src/utils/markup/markdown.rs":"45e4d2fb41b7120ebfd2a1297fc9e0ffb0956bc935478e76b875849f5c195de8","src/utils/markup/mod.rs":"1ed139b7bc843655c6b53837384f77078c62bac685bd2fe661d5af9aa4e6bd1b","src/utils/mod.rs":"aafc0a5a07c3772dbdcc93ce09aa0b0d3617ec180c932df7f7784ec9fef37c7f","src/utils/reader.rs":"abcd7cd33c7f124a2749c8e313fd47b60bf8dbf198a629d1735c23ae8a226e2d","src/utils/span.rs":"2a5fc58fab3897cc8e070050596bb0ee035d60e1c6e5cee8aa515fe00b7c5d0d","src/vec.rs":"9f4a72185b5c6d377bcd7c0cc0bf02ac74fabaa946a253d880e6075191f1dd86","src/view/any.rs":"8174c180d35fdd8c517593295094c423d02f8ebbb0b2e217c65773b87ca9aac5","src/view/finder.rs":"f4d2939eead7181a626ffb196991f33d59109bf1e2e242c6a7c6dcc61ce8c606","src/view/into_boxed_view.rs":"ea5b322a7cde0d23c9dbd8db89747d39c2b7dd02074d8a90b7debb3e0ff3bb2d","src/view/margins.rs":"1e28dc66c2c07dc5e37cc4d4313ac7ee4827b82e4745c5d2cbb4eba70b4201c1","src/view/mod.rs":"934926f10ae6392b3e4a166e3cf089103ad56d54fc0072ac2a1cdd974efbd236","src/view/nameable.rs":"6aa80e55d2cfe5ef58c1f2082efadcd90c801728a38c59f60c8d60b0aa29d7cf","src/view/position.rs":"88ca0ea4eda48fc486b71297206b032a0da4a5b7c058d7dedbde11b1d3e21472","src/view/resizable.rs":"f4d35eba281cb12cb076da1212932cc58c7bb8d43dd0e54eac5258b64d24bc09","src/view/scroll/core.rs":"9da2f58f2bc5d47c86bf69b7dc41d0b36b820b717b02aa0c4e98cabb29b051c1","src/view/scroll/mod.rs":"e948211e8186d440b31e725655b6b3bf97e8c4cd93dbbc79379f6c0747a5a645","src/view/scroll/raw.rs":"8aa3f33726b8aaf77c04a2c485d0f71897898a81215d5b76bd8df25875a00906","src/view/scroll_base.rs":"4f11332f81fbb8b5fd629702fca89907cdcc3109cb6dcd618fcc8532db97b999","src/view/scrollable.rs":"a0d43242cc97cc160f4c1fd4aeca25293977ebec365e76fe258d461cd897cac8","src/view/size_cache.rs":"0ed5f49422661650ed3d55351acf73dbba7356cc4f78c52749a18e0cc1c68bb7","src/view/size_constraint.rs":"483fe967c5fca5e96ed884d38db00c5de985a000cf0ee1cb896e76e421d6825e","src/view/view_path.rs":"6c14dbe2063c41b64bd371ec64827dd3f2b0f7a636f86333aa1ddbfa4e3d9477","src/view/view_trait.rs":"77562327df911adaa3f254cf2ddb942c304bbb52bd202575b857a9d9f8d74272","src/view/view_wrapper.rs":"bbe92995adf66d095700d54dec2c977a6ec042616a65a0d04d2b4b0d702057c1","src/views/boxed_view.rs":"2e0424b3db3600f8bb65ae3518aeba3c99f413c0a874621b705f731be2333ac9","src/views/button.rs":"b7abaa599f3ae37a46accf75b0072dc5f0ccecd840cb7b8ef421f06b2a194b9d","src/views/canvas.rs":"61bee6ca56dc6e30c246507cac872232c234f3cb53689d691faf0924cd70a248","src/views/checkbox.rs":"77b1e9da909a77e6c0afa0a3bb0d98ef0631299bfd0a9f850ca0949511e40677","src/views/circular_focus.rs":"cde756431a1de4a2a7802d0da74c2fb5c44cbcb68cbe778d83c8491b47efba87","src/views/debug_view.rs":"147303099fcd745821281955c1976b0a4ffc305c3b6840c269a2d77f887fc35f","src/views/dialog.rs":"3bf7f2af4bfa4d7f9292b229e65983089efc17fae22fa092f6c6fb0f8b169c31","src/views/dummy.rs":"f2ee599a6096d6038fe5c1cf425164735588fcc353375d73e16f10e75d3afc0d","src/views/edit_view.rs":"c675513016cd8fb94c0526b8481aecaa51269fbf2dbe2f363b2f4446bd847cff","src/views/enableable_view.rs":"e24d601673e629fe1b29b2f574cd799f07c21a450d6ad456ca1f586135e9808e","src/views/fixed_layout.rs":"0686ad35d9d1a4f1215ea2318908d5aa90abf80fb7d3c7e4c9de376bc4d38eac","src/views/focus_tracker.rs":"a2d15d18a50397361250d0a9169d05305e7ac39f9507c1b3db717e7d828a199f","src/views/gradient_view.rs":"2a4eb69c97484502417503603b0059202e9d8d52c706601e47d9030ba91e023b","src/views/hideable_view.rs":"aa417ccc6adad4de57d9035988038b406eb42aed995d7b679832273683b5f4e6","src/views/last_size_view.rs":"d579984c60a87e8b2a9b6a1f1e0df1bbfe69e55034e0711826e6818c5ab41300","src/views/layer.rs":"840c497606c6faf11ddc65aa4f30a12047f1e1fabbda590766127efef932c6a0","src/views/linear_layout.rs":"960931c20efaedec88b4a2d236aa50c49f81ae92b2074c6dd34bcc2453b2df25","src/views/list_view.rs":"6de5cede4572ada3e390fc719a226588ddcce049a4e53f1e0fc53b861a6e66ea","src/views/menu_popup.rs":"3335cfd68dd96facd75a0a2392dadde2b05e4c169933daf9ce9aac3906eab485","src/views/menubar.rs":"0b43c42f2c997b5ada185aa579cad41c9c9efa947363bd34270bdbf9b78e2174","src/views/mod.rs":"d6e7e735fcff4b8e34db5862f08aed567ee0f623d7e5bd93a2ecde6532e7e1b5","src/views/named_view.rs":"831c78c6b71c6aa49eec7430717cbc82efa5d9d5ef323552bc45033b2d64f3ed","src/views/on_event_view.rs":"bb12e4ea8c018cca17af439ffbe7882588fe3d6aba66fedc4e989d5ee679fa93","src/views/on_layout_view.rs":"9481d75196c7aa4772baead21c8fb65c1e0d3f13d7d8a6085d785a1b8304e3c3","src/views/padded_view.rs":"27c950b73ed7f0576ef8e5ad61ed8049c669362cd3982c48fb758ca8b8a79edc","src/views/panel.rs":"9a35e0e7c2d3c5c981f49a3e28fd74943c17e592bc89c1026ce359a77605f323","src/views/progress_bar.rs":"bce8930ef386c9faf4dad3b2d258a58ce33362f76349e41c9c5ed355d0b80493","src/views/radio.rs":"e247ccfc3cf1d52c2ae1c064a94462f012b9ed5e334130a7339ed6b0e30784b5","src/views/resized_view.rs":"9be27f0e419eae338c8018b7163f19c6120da16fa3b564985f08375ddfd2311c","src/views/screens_view.rs":"8487a84802809231f80ef38c35adcc95fc09a884d37205a1661a9ffe3e9428cf","src/views/scroll_view.rs":"aebd8e1b50cee436fe1c54e07b24557da6eb717238498238a0e4351499d04588","src/views/select_view.rs":"7799d00b144c9c497ad8d3f7fb463fd15eac7cfab3092a3305accae9f5543ca6","src/views/shadow_view.rs":"af10fb0f70939f1fcdf36fdabe59cd7ca96396f5b3e23590b5e90feea1939d9a","src/views/slider_view.rs":"624412fdef2bfae0d5574099fb3baebebcc606b298538fda2999ef50de6e9da3","src/views/stack_view.rs":"ad0d2d7ce3aae83bbf0d9ddce0ab8f611f0290b53503d3e58ac60b65b6aa6aeb","src/views/text_area.rs":"233fbfb7557b11c38af7ce975aa77a76700642fd5530fee804c28dda3b4e7394","src/views/text_view.rs":"f1dbec0beb6b5760de73276150815ed236ebf2e95c2c591dc1a0b5b7aef916b2","src/views/themed_view.rs":"41a7316b2eae210936e07500c38e97cabc162c33fd8eb8c5dde86c2eb23048d4","src/views/tracked_view.rs":"8190bbd831febcbc71c62d505f852efa81d7cbef24103fded9419993d3fc7812","src/with.rs":"13e96f72839d0ef7ff757f8098134cdf2e00a90f90194d411631760dd429ffbf","src/xy.rs":"9e21f7a0d4f1d2873162223fd4f50d63ca44ab631e53dd58bcb9fae32fd1fcad"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"45ff17ef6cdc3b135126324ee8038b1383ad2025061cf835d1e8f482d22c1618","LICENSE":"34732cfadbccc51ae3660f332b609c491005993d21735c9b949a0b6821b34c1d","README.md":"1ce27134320158c9df070b90c82e0dfd0b473a0db7521ce53780d699897938dc","src/align.rs":"acc1241aabbde51665a408330f30d21b87c2ca53027a46ce8c25b301e43166df","src/backend.rs":"ba01add5f407671d9cf7575e9162d69a735f0e918e23632d0714756c65f52454","src/buffer.rs":"a47c8641b18f706fcd4c97d3f0101a014a25f03dd47f8c79dd850009e6bcdcf5","src/builder.rs":"df6b110480cbf0f9cf6797e16a42026bae6904bf147b8a07a6d3b323db368319","src/builder/resolvable.rs":"f50e36634ec7a729743af57d2adee9310b545fbbfc824524de2cae5149f0d740","src/cursive_root.rs":"1cf4f4b156dc2d8bb237035a02cd9c3153ca5a91cd81dd942f2e4c9b9f9209dd","src/cursive_run.rs":"9093ab351438f07006eab79a750d4ab4be0efb20f8dcc3de8b8e8d70bace9bc4","src/direction.rs":"f30cd3380b806662ef9991f354bedf03a2a06e8354b2941b57126a851d93a852","src/div.rs":"7746ed8f3cd73dd7ca9cd8b48abec0d98c166832d031820ce9da091f2d11ff11","src/dump.rs":"e989b6cbb2545f2ac685562417cbef24c5d94d3a5432c897213eecb927c3cf63","src/event.rs":"53d7d9dbc4cc288e092ea29f599b6a688f2e246d9052a4dabab0733b8e42efbc","src/lib.rs":"f743e0d3ac0a84d02166797b29782ad7efbf9efe470dfc23b6c94812ebc8bef5","src/logger.rs":"77be95d2eb15d3ea77c39d2626fa90e7d95aaabb10529d50f210405862089484","src/menu.rs":"651b7875e3201f7d1f598a7df3b4103f2a0fb1e45d0fadf4f1480261e3670deb","src/printer.rs":"808c86c4b8cb1164ca9da5f9308237eca4ecb744b0afde4a5d163f5c3de3bcdb","src/rect.rs":"b0820bb417fd767aaeed8d5621aa3b144c92680b09cc164a289d6883d7ef45af","src/style/border_style.rs":"ecb47c1dd46f8dfa8539d77a8b256c6ee93d9c32879d8d0e01fe9162b8a5aa80","src/style/color.rs":"68470ef8d27151df73d2262b9c6d44113c15038c2f3cd45b4d53f4d82dea39a5","src/style/color_pair.rs":"a9127c1475dfe93097ed8b4fa65f21b091c384ea07f46719b74d274a13cef131","src/style/color_style.rs":"b7c89ca81e53e0f56c4ce236cc8a806737304e483824c4501e92d48f5776666b","src/style/effect.rs":"4a0831b6a621f0215a7e1c7bc7c8808c8f3270d45fb420f94d156f478a192b22","src/style/gradient/mod.rs":"9a406f0c51541628da14c47dad44b5ca18a64aabe323aa3cb2538c4dd591f543","src/style/mod.rs":"dc6f966d8a0790601632301989007247ddd108876766cd3a0bc535ed83d06b64","src/style/palette.rs":"d7210658b0d40b1a965141f02b517a744e5bcfacc229abb0ac151e89232adc48","src/style/style_types.rs":"c1efedddf7d42e40450803c3fb3da4f8b2ffba523aa10513bf43c26ef59489d8","src/theme.rs":"e18d580486497e7c02a769b729885fac6ad842ee06a13df687fadbb187f59964","src/traits.rs":"70d3cd55a34e8e25673a7bd08c4a15b1b89683eca5f8e8426f3edced51859e71","src/utils/counter.rs":"4e8363289075758aff080b5891a879fc91654524b8b2d6a3d9d41a1c40c09e7d","src/utils/immutify.rs":"c9841123156d04342d364068036a084db7755cf5c3028d2b19c06a6853549a56","src/utils/lines/mod.rs":"57b7fc239776dc432d62cac140a57018801c79646f7f925c2021e4943fe29273","src/utils/lines/simple/lines_iterator.rs":"beda089b64e918a84cac555092d827c75071137eef299e4a6616019d93f62502","src/utils/lines/simple/mod.rs":"28d52b0c7230622519717c47fb822f1fa55502a252f5e51870ddc52bea6b54b1","src/utils/lines/simple/row.rs":"f290bdb78174abeb0d3c1c200b2bf44134958f533184342c3f8fc808f980164e","src/utils/lines/simple/tests.rs":"34cd24140abec6d7a9350a4bfa15edaa79ff6519d22a6f850ed0e328628e873d","src/utils/lines/spans/chunk.rs":"4d9cab8bc6adeb7a9c08e4d793bf227f432bcc6882b76ce3a87b68002adc4cf3","src/utils/lines/spans/chunk_iterator.rs":"a1f84da07034601ffb730344356056828200caf2e69a7d0f72e4223bafe1e4a9","src/utils/lines/spans/lines_iterator.rs":"54a50a65c252e51f2656792d5c8420c5bcecd416495b627313a0d90536d799f5","src/utils/lines/spans/mod.rs":"f23da445e0d81ed41c6c38ed87820df7b60e5cca5e0c56d9d0135f943e6df3e5","src/utils/lines/spans/prefix.rs":"5a66e3ab40aa8d5bf9a8e1cdc763f1f24c7edcec8c657f5c413791c2d0c939c3","src/utils/lines/spans/row.rs":"8951bf2c7cfb5241a9f379be8fd8f683eba122f2249aed6f1df2ef003ca66e05","src/utils/lines/spans/segment.rs":"cee93d64e470f11b8472540d7475388d10a013882149d65ad6b08c97c1dafca3","src/utils/lines/spans/segment_merge_iterator.rs":"62572b59908e32b64dfc541f9f62f186e7cc97cc97696e936481413637c72fc4","src/utils/lines/spans/tests.rs":"32522f12a738559bc10bd998dd071d55978b739cf80c2158909d5cb58b9e8c70","src/utils/markup/ansi.rs":"db91ce45e30f7883c1d4caa60f5b51b4fc2ff5a2e6dfb7941733651e45217641","src/utils/markup/cursup.rs":"257bd992876c479f2bd75640c52e287fc8c495cc56b0f4502d7c6d0885bf77c2","src/utils/markup/gradient.rs":"e73d1f5e95df39486ad07425835bdde25d1a933bfd19e6a4aef1a6e5b35b40f6","src/utils/markup/markdown.rs":"45e4d2fb41b7120ebfd2a1297fc9e0ffb0956bc935478e76b875849f5c195de8","src/utils/markup/mod.rs":"15b7bf123b546b84edef0d8b7c26ad341d7dfeb9fac2e20b663f9cda43cdd154","src/utils/mod.rs":"aafc0a5a07c3772dbdcc93ce09aa0b0d3617ec180c932df7f7784ec9fef37c7f","src/utils/reader.rs":"abcd7cd33c7f124a2749c8e313fd47b60bf8dbf198a629d1735c23ae8a226e2d","src/utils/span.rs":"2a5fc58fab3897cc8e070050596bb0ee035d60e1c6e5cee8aa515fe00b7c5d0d","src/vec.rs":"938ea763a46c18a1520369f76d51f4e57911e027c87b8f7f67e978684bb74ef4","src/view/any.rs":"8174c180d35fdd8c517593295094c423d02f8ebbb0b2e217c65773b87ca9aac5","src/view/finder.rs":"f4d2939eead7181a626ffb196991f33d59109bf1e2e242c6a7c6dcc61ce8c606","src/view/into_boxed_view.rs":"ea5b322a7cde0d23c9dbd8db89747d39c2b7dd02074d8a90b7debb3e0ff3bb2d","src/view/margins.rs":"1e28dc66c2c07dc5e37cc4d4313ac7ee4827b82e4745c5d2cbb4eba70b4201c1","src/view/mod.rs":"934926f10ae6392b3e4a166e3cf089103ad56d54fc0072ac2a1cdd974efbd236","src/view/nameable.rs":"6aa80e55d2cfe5ef58c1f2082efadcd90c801728a38c59f60c8d60b0aa29d7cf","src/view/position.rs":"88ca0ea4eda48fc486b71297206b032a0da4a5b7c058d7dedbde11b1d3e21472","src/view/resizable.rs":"f4d35eba281cb12cb076da1212932cc58c7bb8d43dd0e54eac5258b64d24bc09","src/view/scroll/core.rs":"9da2f58f2bc5d47c86bf69b7dc41d0b36b820b717b02aa0c4e98cabb29b051c1","src/view/scroll/mod.rs":"2436e1585d8f93a8a1f77b579f0368012cba8e11a0f936bd3c62aa87a1e670a7","src/view/scroll/raw.rs":"3c97e902126af16c5f2df56cd1afc22ad7f233cc4f947075c8a515361614998f","src/view/scroll_base.rs":"4f11332f81fbb8b5fd629702fca89907cdcc3109cb6dcd618fcc8532db97b999","src/view/scrollable.rs":"a0d43242cc97cc160f4c1fd4aeca25293977ebec365e76fe258d461cd897cac8","src/view/size_cache.rs":"0ed5f49422661650ed3d55351acf73dbba7356cc4f78c52749a18e0cc1c68bb7","src/view/size_constraint.rs":"483fe967c5fca5e96ed884d38db00c5de985a000cf0ee1cb896e76e421d6825e","src/view/view_path.rs":"6c14dbe2063c41b64bd371ec64827dd3f2b0f7a636f86333aa1ddbfa4e3d9477","src/view/view_trait.rs":"373c2f472a074bf998256c57c57a9629678b31b3ff22ff4e279ac8efa41376aa","src/view/view_wrapper.rs":"bbe92995adf66d095700d54dec2c977a6ec042616a65a0d04d2b4b0d702057c1","src/views/boxed_view.rs":"2e0424b3db3600f8bb65ae3518aeba3c99f413c0a874621b705f731be2333ac9","src/views/button.rs":"b7abaa599f3ae37a46accf75b0072dc5f0ccecd840cb7b8ef421f06b2a194b9d","src/views/canvas.rs":"61bee6ca56dc6e30c246507cac872232c234f3cb53689d691faf0924cd70a248","src/views/checkbox.rs":"2e477bb8242e441bcbb0da0eea98e906d6ae44d89aad84503103fb61e55b3c05","src/views/circular_focus.rs":"cde756431a1de4a2a7802d0da74c2fb5c44cbcb68cbe778d83c8491b47efba87","src/views/debug_view.rs":"147303099fcd745821281955c1976b0a4ffc305c3b6840c269a2d77f887fc35f","src/views/dialog.rs":"9acd3605c1c15c17c287b3d4f6b6cc7da27a22571afc845ce18fdee91e18a0dd","src/views/dummy.rs":"f2ee599a6096d6038fe5c1cf425164735588fcc353375d73e16f10e75d3afc0d","src/views/edit_view.rs":"82d44f5c35d450ee69e6c492790c8c0eda40444f656d53c918f5b2d0c530b399","src/views/enableable_view.rs":"e24d601673e629fe1b29b2f574cd799f07c21a450d6ad456ca1f586135e9808e","src/views/fixed_layout.rs":"0686ad35d9d1a4f1215ea2318908d5aa90abf80fb7d3c7e4c9de376bc4d38eac","src/views/focus_tracker.rs":"a2d15d18a50397361250d0a9169d05305e7ac39f9507c1b3db717e7d828a199f","src/views/gradient_view.rs":"2a4eb69c97484502417503603b0059202e9d8d52c706601e47d9030ba91e023b","src/views/hideable_view.rs":"aa417ccc6adad4de57d9035988038b406eb42aed995d7b679832273683b5f4e6","src/views/last_size_view.rs":"d579984c60a87e8b2a9b6a1f1e0df1bbfe69e55034e0711826e6818c5ab41300","src/views/layer.rs":"840c497606c6faf11ddc65aa4f30a12047f1e1fabbda590766127efef932c6a0","src/views/linear_layout.rs":"88592cc8e6e755fb7667b4b5b7874110671f4d1b0c58aed77fda7150525154c1","src/views/list_view.rs":"6de5cede4572ada3e390fc719a226588ddcce049a4e53f1e0fc53b861a6e66ea","src/views/menu_popup.rs":"4c9a4dcd5ebdd71af5d99b06a276aa83e284b5de71c4ba41b1644d53a904b1ea","src/views/menubar.rs":"0b43c42f2c997b5ada185aa579cad41c9c9efa947363bd34270bdbf9b78e2174","src/views/mod.rs":"d6e7e735fcff4b8e34db5862f08aed567ee0f623d7e5bd93a2ecde6532e7e1b5","src/views/named_view.rs":"831c78c6b71c6aa49eec7430717cbc82efa5d9d5ef323552bc45033b2d64f3ed","src/views/on_event_view.rs":"bb12e4ea8c018cca17af439ffbe7882588fe3d6aba66fedc4e989d5ee679fa93","src/views/on_layout_view.rs":"9481d75196c7aa4772baead21c8fb65c1e0d3f13d7d8a6085d785a1b8304e3c3","src/views/padded_view.rs":"27c950b73ed7f0576ef8e5ad61ed8049c669362cd3982c48fb758ca8b8a79edc","src/views/panel.rs":"9a35e0e7c2d3c5c981f49a3e28fd74943c17e592bc89c1026ce359a77605f323","src/views/progress_bar.rs":"bce8930ef386c9faf4dad3b2d258a58ce33362f76349e41c9c5ed355d0b80493","src/views/radio.rs":"e247ccfc3cf1d52c2ae1c064a94462f012b9ed5e334130a7339ed6b0e30784b5","src/views/resized_view.rs":"9be27f0e419eae338c8018b7163f19c6120da16fa3b564985f08375ddfd2311c","src/views/screens_view.rs":"8487a84802809231f80ef38c35adcc95fc09a884d37205a1661a9ffe3e9428cf","src/views/scroll_view.rs":"aebd8e1b50cee436fe1c54e07b24557da6eb717238498238a0e4351499d04588","src/views/select_view.rs":"84f5054e430747f067ca70ab173c8d512440ec1d81a3dba506c2f055fe272570","src/views/shadow_view.rs":"af10fb0f70939f1fcdf36fdabe59cd7ca96396f5b3e23590b5e90feea1939d9a","src/views/slider_view.rs":"624412fdef2bfae0d5574099fb3baebebcc606b298538fda2999ef50de6e9da3","src/views/stack_view.rs":"ad0d2d7ce3aae83bbf0d9ddce0ab8f611f0290b53503d3e58ac60b65b6aa6aeb","src/views/text_area.rs":"fab705804d166b858bfccfcdbadb4dd3ae5ae78f016cd39dfce58ce05113072a","src/views/text_view.rs":"f1dbec0beb6b5760de73276150815ed236ebf2e95c2c591dc1a0b5b7aef916b2","src/views/themed_view.rs":"41a7316b2eae210936e07500c38e97cabc162c33fd8eb8c5dde86c2eb23048d4","src/views/tracked_view.rs":"8190bbd831febcbc71c62d505f852efa81d7cbef24103fded9419993d3fc7812","src/with.rs":"13e96f72839d0ef7ff757f8098134cdf2e00a90f90194d411631760dd429ffbf","src/xy.rs":"9e21f7a0d4f1d2873162223fd4f50d63ca44ab631e53dd58bcb9fae32fd1fcad"},"package":null} \ No newline at end of file diff --git a/cursive_core-0.4.6/Cargo.toml b/cursive_core-0.4.6/Cargo.toml index 2328cd1e00..93ab124a50 100644 --- a/cursive_core-0.4.6/Cargo.toml +++ b/cursive_core-0.4.6/Cargo.toml @@ -104,7 +104,7 @@ features = [ ] [dependencies.toml] -version = "0.9" +version = "1.0" optional = true [dev-dependencies] diff --git a/cursive_core-0.4.6/src/buffer.rs b/cursive_core-0.4.6/src/buffer.rs index 887dd02908..80db9c196b 100644 --- a/cursive_core-0.4.6/src/buffer.rs +++ b/cursive_core-0.4.6/src/buffer.rs @@ -10,9 +10,10 @@ use unicode_width::UnicodeWidthStr; /// The width of a cell. /// /// Most characters are single-width. Some asian characters and emojis are double-width. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] pub enum CellWidth { /// This character takes a single cell in the grid. + #[default] Single, /// This character takes 2 cells in the grid (mostly for emojis and asian characters). @@ -28,12 +29,6 @@ pub enum CellWidth { Quintuple, } -impl Default for CellWidth { - fn default() -> Self { - CellWidth::Single - } -} - impl CellWidth { /// Convert the width as returned from `UnicodeWidthStr::width()` into a `CellWidth`. /// diff --git a/cursive_core-0.4.6/src/event.rs b/cursive_core-0.4.6/src/event.rs index cadb725c4a..eb840dad91 100644 --- a/cursive_core-0.4.6/src/event.rs +++ b/cursive_core-0.4.6/src/event.rs @@ -494,12 +494,16 @@ pub enum MouseEvent { WheelUp, /// The wheel was moved down. WheelDown, + /// The wheel was moved to the left. + WheelLeft, + /// The wheel was moved to the right. + WheelRight, } impl MouseEvent { /// Returns the button used by this event, if any. /// - /// Returns `None` if `self` is `WheelUp` or `WheelDown`. + /// Returns `None` if `self` is `WheelUp`, `WheelDown`, `WheelLeft` or `WheelRight`. pub fn button(self) -> Option { match self { MouseEvent::Press(btn) | MouseEvent::Release(btn) | MouseEvent::Hold(btn) => Some(btn), @@ -509,7 +513,7 @@ impl MouseEvent { /// Returns `true` if `self` is an event that can grab focus. /// - /// This includes `Press`, `WheelUp` and `WheelDown`. + /// This includes `Press`, `WheelUp`, `WheelDown`, `WheelLeft` and `WheelRight`. /// /// It does _not_ include `Release` or `Hold`. /// @@ -518,7 +522,11 @@ impl MouseEvent { pub fn grabs_focus(self) -> bool { matches!( self, - MouseEvent::Press(_) | MouseEvent::WheelUp | MouseEvent::WheelDown + MouseEvent::Press(_) + | MouseEvent::WheelUp + | MouseEvent::WheelDown + | MouseEvent::WheelLeft + | MouseEvent::WheelRight ) } } diff --git a/cursive_core-0.4.6/src/menu.rs b/cursive_core-0.4.6/src/menu.rs index 14628f79ab..0fb6bf24a0 100644 --- a/cursive_core-0.4.6/src/menu.rs +++ b/cursive_core-0.4.6/src/menu.rs @@ -99,7 +99,7 @@ impl Item { /// Returns the styled lable for this item /// /// Returns a vertical bar string if `self` is a delimiter. - pub fn styled_label(&self) -> SpannedStr