mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
Bug 1520166 - Part 2: revendor dependencies. r=jcj
Differential Revision: https://phabricator.services.mozilla.com/D32222 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
d24b02b94c
commit
85c6ac0805
1
third_party/rust/authenticator/.cargo-checksum.json
vendored
Normal file
1
third_party/rust/authenticator/.cargo-checksum.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"files":{"Cargo.toml":"5af2217a89aa4af5db14fd48c05144e264b53e067a8cf0433638e5196ad85f25","LICENSE":"e147f11fd8c5b7d151db93eabbb02ddb8b481082047008062025afd1ff7e2a27","README.md":"1c291ddb3f131b55e706f13a98eec871055ceb17ef502ce75f608b67136cc7c3","examples/main.rs":"7af9e288b1836fb9362589b6bf54c1f1d277bdf64df60c1caccef98c1bfe792c","rustfmt.toml":"de4e1daab481c1572805aed3e51e72c5dc1b3e5af757bc675e1717b251c6e922","src/capi.rs":"33b91e8e43003fb11b0fbb4ea72e4a5dba8f4dcc11e2503a8186eaea020c13c3","src/consts.rs":"4c34980f94d1017e5e75e29b26750c1678e0609c9227296951ffbb9e180a5adf","src/freebsd/device.rs":"914ac446ff24cc3fc050732372e286e1fedf8341a8d4754e392bc9f38393b142","src/freebsd/mod.rs":"42dcb57fbeb00140003a8ad39acac9b547062b8f281a3fa5deb5f92a6169dde6","src/freebsd/monitor.rs":"3683370931b15f05403d240a788a87b1bea801284ee74b849e1f330a4a231dd0","src/freebsd/transaction.rs":"e3615cfdd7f23e9a80a53c32e0fa2a5ae290a432b009bed2b2e74e0df77f8266","src/freebsd/uhid.rs":"d19ade6e808e63981ba5f93d482d676ffa9dff29cb2b7011486591f69ddbbdcd","src/hidproto.rs":"966ee74341e2ecff262e5490f99dcc9b9f9427451f3ded1b9cb9698a6984a3cc","src/lib.rs":"1dc04a7d7af14e9f1172a3839522386492a47218df14dfb0f24f98fe314c3d06","src/linux/device.rs":"2271fbb8d176ec01f83a899c0c8194a954465fdd8670a9b136ab88baaba2ee48","src/linux/hidraw.rs":"179cf38a1bd86c191c625a814d16e7ac00a774df9b3828ac4f735313a030f1e5","src/linux/mod.rs":"1056db0c258ccd9975066ffa8407ded689ecf5ece67fc17e732c487b69557b2c","src/linux/monitor.rs":"876d4e095d77858412c61d35560a3b85855a9c00beea6950b5f50796d8fd3eab","src/linux/transaction.rs":"e3615cfdd7f23e9a80a53c32e0fa2a5ae290a432b009bed2b2e74e0df77f8266","src/macos/device.rs":"57186092fd937124f96afacef57d0493efce86553ac50d527e6365b93eaabddf","src/macos/iokit.rs":"a0fd818224718e96ad5d106dfc235f4bc9218a59f5114b9f9825abe3ee62bce7","src/macos/mod.rs":"333e561554fc901d4f6092f6e4c85823e2b0c4ff31c9188d0e6d542b71a0a07c","src/macos/monitor.rs":"1f132a8566858b22309b33f8ae96d5596c42f515785be841bb51db80e20471d1","src/macos/transaction.rs":"b8e362575b2bcc54232022131bfa073d605969119326dcb549f4d87037a31541","src/manager.rs":"7428fc23038b004841936d8f27f8dc33234d5c06361efa75b73c7a5c035dae75","src/statemachine.rs":"28477fba601f5086b85e911da1e4f04af3f0060329e6e0e1172b960f9c52fd41","src/stub/device.rs":"32e134fc8826667d16b02fe32831fc29f4d52d0a7a065d7d649c4a1c5faa0dcc","src/stub/mod.rs":"6a7fec504a52d403b0241b18cd8b95088a31807571f4c0a67e4055afc74f4453","src/stub/transaction.rs":"8655bc37b69c318ff0bc69a62fcb31820eb6ad7921a53e0cecffa15e80d97630","src/u2fhid-capi.h":"fc2575f720ab8f6bc0c523c57310f35c464576bd5150c3c2b1873d3abde5909b","src/u2fprotocol.rs":"77b6d5005d8b3d98cd96e480013c1e97155da5df3cf5e19819ee82ac8e3b6c7d","src/u2ftypes.rs":"a28c07956a339d97d37f91b7257d1a5d1ea3b34f5c37bb9da0e17490115e5d8d","src/util.rs":"70c8b9b8d90e6d581a7d2568aeff3bbde6c2d9f3864cee9fbc2486c1f3002a13","src/windows/device.rs":"86a6ecc239608977a963f375336780746e90e95c3eb9ff303347beb983c40ab6","src/windows/mod.rs":"218e7f2fe91ecb390c12bba5a5ffdad2c1f0b22861c937f4d386262e5b3dd617","src/windows/monitor.rs":"d8e8316e5bd9fc6ebed737bd8d6e0713c99287aca04f392f6319cdfd8576f754","src/windows/transaction.rs":"1b9a5af866048911ccaec8c94b698b28ae1b80e3d4842f9d6ed38462f459c796","src/windows/winapi.rs":"a4286fd5e8dcb178e37df512ba7752b2a3c38fe30e1176022767d2c05c242bf8"},"package":"ec149e5d5d4caa2c9ead53a8ce1ea9c4204c388c65bf3b96c2d1dc0fcf4aeb66"}
|
53
third_party/rust/authenticator/Cargo.toml
vendored
Normal file
53
third_party/rust/authenticator/Cargo.toml
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
name = "authenticator"
|
||||
version = "0.2.6"
|
||||
authors = ["J.C. Jones <jc@mozilla.com>", "Tim Taubert <ttaubert@mozilla.com>", "Kyle Machulis <kyle@nonpolynomial.com>"]
|
||||
description = "Library for interacting with CTAP1/2 security keys for Web Authentication. Used by Firefox."
|
||||
license = "MPL-2.0"
|
||||
repository = "https://github.com/mozilla/authenticator-rs/"
|
||||
[dependencies.bitflags]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.boxfnonce]
|
||||
version = "0.0.3"
|
||||
|
||||
[dependencies.libc]
|
||||
version = "0.2"
|
||||
|
||||
[dependencies.log]
|
||||
version = "0.4"
|
||||
|
||||
[dependencies.rand]
|
||||
version = "0.6"
|
||||
|
||||
[dependencies.runloop]
|
||||
version = "0.1.0"
|
||||
[dev-dependencies.base64]
|
||||
version = "^0.10"
|
||||
|
||||
[dev-dependencies.env_logger]
|
||||
version = "0.6"
|
||||
|
||||
[dev-dependencies.sha2]
|
||||
version = "^0.8"
|
||||
[target."cfg(target_os = \"freebsd\")".dependencies.devd-rs]
|
||||
version = "0.3"
|
||||
[target."cfg(target_os = \"linux\")".dependencies.libudev]
|
||||
version = "^0.2"
|
||||
[target."cfg(target_os = \"macos\")".dependencies.core-foundation]
|
||||
version = "0.6.2"
|
||||
[target."cfg(target_os = \"windows\")".dependencies.winapi]
|
||||
version = "0.3"
|
||||
features = ["handleapi", "hidclass", "hidpi", "hidusage", "setupapi"]
|
374
third_party/rust/authenticator/LICENSE
vendored
Normal file
374
third_party/rust/authenticator/LICENSE
vendored
Normal file
@ -0,0 +1,374 @@
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
|
50
third_party/rust/authenticator/README.md
vendored
Normal file
50
third_party/rust/authenticator/README.md
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
# A Rust library for interacting with CTAP1/CTAP2 Security Keys
|
||||
|
||||
[![Build Status](https://travis-ci.org/mozilla/authenticator-rs.svg?branch=master)](https://travis-ci.org/mozilla/authenticator-rs)
|
||||
![Maturity Level](https://img.shields.io/badge/maturity-release-green.svg)
|
||||
|
||||
This is a cross-platform library for interacting with Security Key-type devices via Rust.
|
||||
|
||||
* **Supported Platforms**: Windows, Linux, FreeBSD, and macOS.
|
||||
* **Supported Transports**: USB HID.
|
||||
* **Supported Protocols**: [FIDO U2F over USB](https://fidoalliance.org/specs/fido-u2f-v1.1-id-20160915/fido-u2f-raw-message-formats-v1.1-id-20160915.html). [CTAP2 support](https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html) is forthcoming, with work being done in the **unstable** [`ctap2` branch](https://github.com/mozilla/authenticator-rs/tree/ctap2).
|
||||
|
||||
This library currently focuses on USB security keys, but is expected to be extended to
|
||||
support additional transports.
|
||||
|
||||
## Usage
|
||||
|
||||
There's only a simple example function that tries to register and sign right now. It uses
|
||||
[env_logger](http://rust-lang-nursery.github.io/log/env_logger/) for logging, which you
|
||||
configure with the `RUST_LOG` environment variable:
|
||||
|
||||
```
|
||||
cargo build --example main
|
||||
RUST_LOG=debug cargo run --example main
|
||||
```
|
||||
|
||||
Proper usage should be to call into this library from something else - e.g., Firefox. There are
|
||||
some [C headers exposed for the purpose](authenticator/blob/master/src/u2fhid-capi.h).
|
||||
|
||||
## Tests
|
||||
|
||||
There are some tests of the cross-platform runloop logic and the protocol decoder:
|
||||
|
||||
```
|
||||
cargo test
|
||||
```
|
||||
|
||||
## Fuzzing
|
||||
|
||||
There are fuzzers for the USB protocol reader, basically fuzzing inputs from the HID layer.
|
||||
There are not (yet) fuzzers for the C API used by callers (such as Gecko).
|
||||
|
||||
To fuzz, you will need cargo-fuzz (the latest version from GitHub) as well as Rust Nightly.
|
||||
|
||||
```
|
||||
rustup install nightly
|
||||
cargo install --git https://github.com/rust-fuzz/cargo-fuzz/
|
||||
|
||||
cargo +nightly fuzz run u2f_read -- -max_len=512
|
||||
cargo +nightly fuzz run u2f_read_write -- -max_len=512
|
||||
```
|
109
third_party/rust/authenticator/examples/main.rs
vendored
Normal file
109
third_party/rust/authenticator/examples/main.rs
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
extern crate authenticator;
|
||||
extern crate base64;
|
||||
extern crate sha2;
|
||||
use authenticator::{AuthenticatorTransports, KeyHandle, RegisterFlags, SignFlags, U2FManager};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::io;
|
||||
use std::sync::mpsc::channel;
|
||||
|
||||
extern crate env_logger;
|
||||
extern crate log;
|
||||
|
||||
macro_rules! try_or {
|
||||
($val:expr, $or:expr) => {
|
||||
match $val {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
return $or(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn u2f_get_key_handle_from_register_response(register_response: &[u8]) -> io::Result<Vec<u8>> {
|
||||
if register_response[0] != 0x05 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"Reserved byte not set correctly",
|
||||
));
|
||||
}
|
||||
|
||||
let key_handle_len = register_response[66] as usize;
|
||||
let mut public_key = register_response.to_owned();
|
||||
let mut key_handle = public_key.split_off(67);
|
||||
let _attestation = key_handle.split_off(key_handle_len);
|
||||
|
||||
Ok(key_handle)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
|
||||
println!("Asking a security key to register now...");
|
||||
let challenge_str = format!(
|
||||
"{}{}",
|
||||
r#"{"challenge": "1vQ9mxionq0ngCnjD-wTsv1zUSrGRtFqG2xP09SbZ70","#,
|
||||
r#" "version": "U2F_V2", "appId": "http://demo.yubico.com"}"#
|
||||
);
|
||||
let mut challenge = Sha256::default();
|
||||
challenge.input(challenge_str.as_bytes());
|
||||
let chall_bytes = challenge.result().to_vec();
|
||||
|
||||
let mut application = Sha256::default();
|
||||
application.input(b"http://demo.yubico.com");
|
||||
let app_bytes = application.result().to_vec();
|
||||
|
||||
let manager = U2FManager::new().unwrap();
|
||||
let flags = RegisterFlags::empty();
|
||||
|
||||
let (tx, rx) = channel();
|
||||
manager
|
||||
.register(
|
||||
flags,
|
||||
15_000,
|
||||
chall_bytes.clone(),
|
||||
app_bytes.clone(),
|
||||
vec![],
|
||||
move |rv| {
|
||||
tx.send(rv.unwrap()).unwrap();
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let register_data = try_or!(rx.recv(), |_| {
|
||||
panic!("Problem receiving, unable to continue");
|
||||
});
|
||||
println!("Register result: {}", base64::encode(®ister_data));
|
||||
println!("Asking a security key to sign now, with the data from the register...");
|
||||
let credential = u2f_get_key_handle_from_register_response(®ister_data).unwrap();
|
||||
let key_handle = KeyHandle {
|
||||
credential,
|
||||
transports: AuthenticatorTransports::empty(),
|
||||
};
|
||||
|
||||
let flags = SignFlags::empty();
|
||||
let (tx, rx) = channel();
|
||||
manager
|
||||
.sign(
|
||||
flags,
|
||||
15_000,
|
||||
chall_bytes,
|
||||
vec![app_bytes],
|
||||
vec![key_handle],
|
||||
move |rv| {
|
||||
tx.send(rv.unwrap()).unwrap();
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let (_, handle_used, sign_data) = try_or!(rx.recv(), |_| {
|
||||
println!("Problem receiving");
|
||||
});
|
||||
println!("Sign result: {}", base64::encode(&sign_data));
|
||||
println!("Key handle used: {}", base64::encode(&handle_used));
|
||||
println!("Done.");
|
||||
}
|
2
third_party/rust/authenticator/rustfmt.toml
vendored
Normal file
2
third_party/rust/authenticator/rustfmt.toml
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
comment_width = 200
|
||||
wrap_comments = true
|
270
third_party/rust/authenticator/src/capi.rs
vendored
Normal file
270
third_party/rust/authenticator/src/capi.rs
vendored
Normal file
@ -0,0 +1,270 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use libc::size_t;
|
||||
use rand::{thread_rng, Rng};
|
||||
use std::collections::HashMap;
|
||||
use std::{ptr, slice};
|
||||
|
||||
use U2FManager;
|
||||
|
||||
type U2FAppIds = Vec<::AppId>;
|
||||
type U2FKeyHandles = Vec<::KeyHandle>;
|
||||
type U2FCallback = extern "C" fn(u64, *mut U2FResult);
|
||||
|
||||
pub enum U2FResult {
|
||||
Success(HashMap<u8, Vec<u8>>),
|
||||
Error(::Error),
|
||||
}
|
||||
|
||||
const RESBUF_ID_REGISTRATION: u8 = 0;
|
||||
const RESBUF_ID_KEYHANDLE: u8 = 1;
|
||||
const RESBUF_ID_SIGNATURE: u8 = 2;
|
||||
const RESBUF_ID_APPID: u8 = 3;
|
||||
|
||||
// Generates a new 64-bit transaction id with collision probability 2^-32.
|
||||
fn new_tid() -> u64 {
|
||||
thread_rng().gen::<u64>()
|
||||
}
|
||||
|
||||
unsafe fn from_raw(ptr: *const u8, len: usize) -> Vec<u8> {
|
||||
slice::from_raw_parts(ptr, len).to_vec()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_u2f_mgr_new() -> *mut U2FManager {
|
||||
if let Ok(mgr) = U2FManager::new() {
|
||||
Box::into_raw(Box::new(mgr))
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_u2f_mgr_free(mgr: *mut U2FManager) {
|
||||
if !mgr.is_null() {
|
||||
Box::from_raw(mgr);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_u2f_app_ids_new() -> *mut U2FAppIds {
|
||||
Box::into_raw(Box::new(vec![]))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_u2f_app_ids_add(
|
||||
ids: *mut U2FAppIds,
|
||||
id_ptr: *const u8,
|
||||
id_len: usize,
|
||||
) {
|
||||
(*ids).push(from_raw(id_ptr, id_len));
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_u2f_app_ids_free(ids: *mut U2FAppIds) {
|
||||
if !ids.is_null() {
|
||||
Box::from_raw(ids);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_u2f_khs_new() -> *mut U2FKeyHandles {
|
||||
Box::into_raw(Box::new(vec![]))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_u2f_khs_add(
|
||||
khs: *mut U2FKeyHandles,
|
||||
key_handle_ptr: *const u8,
|
||||
key_handle_len: usize,
|
||||
transports: u8,
|
||||
) {
|
||||
(*khs).push(::KeyHandle {
|
||||
credential: from_raw(key_handle_ptr, key_handle_len),
|
||||
transports: ::AuthenticatorTransports::from_bits_truncate(transports),
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_u2f_khs_free(khs: *mut U2FKeyHandles) {
|
||||
if !khs.is_null() {
|
||||
Box::from_raw(khs);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_u2f_result_error(res: *const U2FResult) -> u8 {
|
||||
if res.is_null() {
|
||||
return ::Error::Unknown as u8;
|
||||
}
|
||||
|
||||
if let U2FResult::Error(ref err) = *res {
|
||||
return *err as u8;
|
||||
}
|
||||
|
||||
0 /* No error, the request succeeded. */
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_u2f_resbuf_length(
|
||||
res: *const U2FResult,
|
||||
bid: u8,
|
||||
len: *mut size_t,
|
||||
) -> bool {
|
||||
if res.is_null() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let U2FResult::Success(ref bufs) = *res {
|
||||
if let Some(buf) = bufs.get(&bid) {
|
||||
*len = buf.len();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_u2f_resbuf_copy(
|
||||
res: *const U2FResult,
|
||||
bid: u8,
|
||||
dst: *mut u8,
|
||||
) -> bool {
|
||||
if res.is_null() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let U2FResult::Success(ref bufs) = *res {
|
||||
if let Some(buf) = bufs.get(&bid) {
|
||||
ptr::copy_nonoverlapping(buf.as_ptr(), dst, buf.len());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_u2f_res_free(res: *mut U2FResult) {
|
||||
if !res.is_null() {
|
||||
Box::from_raw(res);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_u2f_mgr_register(
|
||||
mgr: *mut U2FManager,
|
||||
flags: u64,
|
||||
timeout: u64,
|
||||
callback: U2FCallback,
|
||||
challenge_ptr: *const u8,
|
||||
challenge_len: usize,
|
||||
application_ptr: *const u8,
|
||||
application_len: usize,
|
||||
khs: *const U2FKeyHandles,
|
||||
) -> u64 {
|
||||
if mgr.is_null() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check buffers.
|
||||
if challenge_ptr.is_null() || application_ptr.is_null() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let flags = ::RegisterFlags::from_bits_truncate(flags);
|
||||
let challenge = from_raw(challenge_ptr, challenge_len);
|
||||
let application = from_raw(application_ptr, application_len);
|
||||
let key_handles = (*khs).clone();
|
||||
|
||||
let tid = new_tid();
|
||||
let res = (*mgr).register(
|
||||
flags,
|
||||
timeout,
|
||||
challenge,
|
||||
application,
|
||||
key_handles,
|
||||
move |rv| {
|
||||
let result = match rv {
|
||||
Ok(registration) => {
|
||||
let mut bufs = HashMap::new();
|
||||
bufs.insert(RESBUF_ID_REGISTRATION, registration);
|
||||
U2FResult::Success(bufs)
|
||||
}
|
||||
Err(e) => U2FResult::Error(e),
|
||||
};
|
||||
|
||||
callback(tid, Box::into_raw(Box::new(result)));
|
||||
},
|
||||
);
|
||||
|
||||
if res.is_ok() {
|
||||
tid
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_u2f_mgr_sign(
|
||||
mgr: *mut U2FManager,
|
||||
flags: u64,
|
||||
timeout: u64,
|
||||
callback: U2FCallback,
|
||||
challenge_ptr: *const u8,
|
||||
challenge_len: usize,
|
||||
app_ids: *const U2FAppIds,
|
||||
khs: *const U2FKeyHandles,
|
||||
) -> u64 {
|
||||
if mgr.is_null() || khs.is_null() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check buffers.
|
||||
if challenge_ptr.is_null() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Need at least one app_id.
|
||||
if (*app_ids).is_empty() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let flags = ::SignFlags::from_bits_truncate(flags);
|
||||
let challenge = from_raw(challenge_ptr, challenge_len);
|
||||
let app_ids = (*app_ids).clone();
|
||||
let key_handles = (*khs).clone();
|
||||
|
||||
let tid = new_tid();
|
||||
let res = (*mgr).sign(flags, timeout, challenge, app_ids, key_handles, move |rv| {
|
||||
let result = match rv {
|
||||
Ok((app_id, key_handle, signature)) => {
|
||||
let mut bufs = HashMap::new();
|
||||
bufs.insert(RESBUF_ID_KEYHANDLE, key_handle);
|
||||
bufs.insert(RESBUF_ID_SIGNATURE, signature);
|
||||
bufs.insert(RESBUF_ID_APPID, app_id);
|
||||
U2FResult::Success(bufs)
|
||||
}
|
||||
Err(e) => U2FResult::Error(e),
|
||||
};
|
||||
|
||||
callback(tid, Box::into_raw(Box::new(result)));
|
||||
});
|
||||
|
||||
if res.is_ok() {
|
||||
tid
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn rust_u2f_mgr_cancel(mgr: *mut U2FManager) {
|
||||
if !mgr.is_null() {
|
||||
// Ignore return value.
|
||||
let _ = (*mgr).cancel();
|
||||
}
|
||||
}
|
78
third_party/rust/authenticator/src/consts.rs
vendored
Normal file
78
third_party/rust/authenticator/src/consts.rs
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Allow dead code in this module, since it's all packet consts anyways.
|
||||
#![allow(dead_code)]
|
||||
|
||||
pub const HID_RPT_SIZE: usize = 64;
|
||||
pub const U2FAPDUHEADER_SIZE: usize = 7;
|
||||
pub const CID_BROADCAST: [u8; 4] = [0xff, 0xff, 0xff, 0xff];
|
||||
pub const TYPE_MASK: u8 = 0x80;
|
||||
pub const TYPE_INIT: u8 = 0x80;
|
||||
pub const TYPE_CONT: u8 = 0x80;
|
||||
|
||||
// Size of data chunk expected in U2F Init USB HID Packets
|
||||
pub const INIT_DATA_SIZE: usize = HID_RPT_SIZE - 7;
|
||||
// Size of data chunk expected in U2F Cont USB HID Packets
|
||||
pub const CONT_DATA_SIZE: usize = HID_RPT_SIZE - 5;
|
||||
|
||||
pub const PARAMETER_SIZE: usize = 32;
|
||||
|
||||
pub const FIDO_USAGE_PAGE: u16 = 0xf1d0; // FIDO alliance HID usage page
|
||||
pub const FIDO_USAGE_U2FHID: u16 = 0x01; // U2FHID usage for top-level collection
|
||||
pub const FIDO_USAGE_DATA_IN: u8 = 0x20; // Raw IN data report
|
||||
pub const FIDO_USAGE_DATA_OUT: u8 = 0x21; // Raw OUT data report
|
||||
|
||||
// General pub constants
|
||||
|
||||
pub const U2FHID_IF_VERSION: u32 = 2; // Current interface implementation version
|
||||
pub const U2FHID_FRAME_TIMEOUT: u32 = 500; // Default frame timeout in ms
|
||||
pub const U2FHID_TRANS_TIMEOUT: u32 = 3000; // Default message timeout in ms
|
||||
|
||||
// U2FHID native commands
|
||||
pub const U2FHID_PING: u8 = (TYPE_INIT | 0x01); // Echo data through local processor only
|
||||
pub const U2FHID_MSG: u8 = (TYPE_INIT | 0x03); // Send U2F message frame
|
||||
pub const U2FHID_LOCK: u8 = (TYPE_INIT | 0x04); // Send lock channel command
|
||||
pub const U2FHID_INIT: u8 = (TYPE_INIT | 0x06); // Channel initialization
|
||||
pub const U2FHID_WINK: u8 = (TYPE_INIT | 0x08); // Send device identification wink
|
||||
pub const U2FHID_ERROR: u8 = (TYPE_INIT | 0x3f); // Error response
|
||||
|
||||
// U2FHID_MSG commands
|
||||
pub const U2F_VENDOR_FIRST: u8 = (TYPE_INIT | 0x40); // First vendor defined command
|
||||
pub const U2F_VENDOR_LAST: u8 = (TYPE_INIT | 0x7f); // Last vendor defined command
|
||||
pub const U2F_REGISTER: u8 = 0x01; // Registration command
|
||||
pub const U2F_AUTHENTICATE: u8 = 0x02; // Authenticate/sign command
|
||||
pub const U2F_VERSION: u8 = 0x03; // Read version string command
|
||||
|
||||
// U2F_REGISTER command defines
|
||||
pub const U2F_REGISTER_ID: u8 = 0x05; // Version 2 registration identifier
|
||||
pub const U2F_REGISTER_HASH_ID: u8 = 0x00; // Version 2 hash identintifier
|
||||
|
||||
// U2F_AUTHENTICATE command defines
|
||||
pub const U2F_REQUEST_USER_PRESENCE: u8 = 0x03; // Verify user presence and sign
|
||||
pub const U2F_CHECK_IS_REGISTERED: u8 = 0x07; // Check if the key handle is registered
|
||||
|
||||
// U2FHID_INIT command defines
|
||||
pub const INIT_NONCE_SIZE: usize = 8; // Size of channel initialization challenge
|
||||
pub const CAPFLAG_WINK: u8 = 0x01; // Device supports WINK command
|
||||
pub const CAPFLAG_LOCK: u8 = 0x02; // Device supports LOCK command
|
||||
|
||||
// Low-level error codes. Return as negatives.
|
||||
|
||||
pub const ERR_NONE: u8 = 0x00; // No error
|
||||
pub const ERR_INVALID_CMD: u8 = 0x01; // Invalid command
|
||||
pub const ERR_INVALID_PAR: u8 = 0x02; // Invalid parameter
|
||||
pub const ERR_INVALID_LEN: u8 = 0x03; // Invalid message length
|
||||
pub const ERR_INVALID_SEQ: u8 = 0x04; // Invalid message sequencing
|
||||
pub const ERR_MSG_TIMEOUT: u8 = 0x05; // Message has timed out
|
||||
pub const ERR_CHANNEL_BUSY: u8 = 0x06; // Channel busy
|
||||
pub const ERR_LOCK_REQUIRED: u8 = 0x0a; // Command requires channel lock
|
||||
pub const ERR_INVALID_CID: u8 = 0x0b; // Command not allowed on this cid
|
||||
pub const ERR_OTHER: u8 = 0x7f; // Other unspecified error
|
||||
|
||||
// These are ISO 7816-4 defined response status words.
|
||||
pub const SW_NO_ERROR: [u8; 2] = [0x90, 0x00];
|
||||
pub const SW_CONDITIONS_NOT_SATISFIED: [u8; 2] = [0x69, 0x85];
|
||||
pub const SW_WRONG_DATA: [u8; 2] = [0x6A, 0x80];
|
||||
pub const SW_WRONG_LENGTH: [u8; 2] = [0x67, 0x00];
|
88
third_party/rust/authenticator/src/freebsd/device.rs
vendored
Normal file
88
third_party/rust/authenticator/src/freebsd/device.rs
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
extern crate libc;
|
||||
|
||||
use std::ffi::{CString, OsString};
|
||||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
use std::os::unix::prelude::*;
|
||||
|
||||
use consts::CID_BROADCAST;
|
||||
use platform::uhid;
|
||||
use u2ftypes::U2FDevice;
|
||||
use util::from_unix_result;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Device {
|
||||
path: OsString,
|
||||
fd: libc::c_int,
|
||||
cid: [u8; 4],
|
||||
}
|
||||
|
||||
impl Device {
|
||||
pub fn new(path: OsString) -> io::Result<Self> {
|
||||
let cstr = CString::new(path.as_bytes())?;
|
||||
let fd = unsafe { libc::open(cstr.as_ptr(), libc::O_RDWR) };
|
||||
let fd = from_unix_result(fd)?;
|
||||
Ok(Self {
|
||||
path,
|
||||
fd,
|
||||
cid: CID_BROADCAST,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_u2f(&self) -> bool {
|
||||
uhid::is_u2f_device(self.fd)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Device {
|
||||
fn drop(&mut self) {
|
||||
// Close the fd, ignore any errors.
|
||||
let _ = unsafe { libc::close(self.fd) };
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Device {
|
||||
fn eq(&self, other: &Device) -> bool {
|
||||
self.path == other.path
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Device {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let bufp = buf.as_mut_ptr() as *mut libc::c_void;
|
||||
let rv = unsafe { libc::read(self.fd, bufp, buf.len()) };
|
||||
from_unix_result(rv as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Device {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let report_id = buf[0] as i64;
|
||||
// Skip report number when not using numbered reports.
|
||||
let start = if report_id == 0x0 { 1 } else { 0 };
|
||||
let data = &buf[start..];
|
||||
|
||||
let data_ptr = data.as_ptr() as *const libc::c_void;
|
||||
let rv = unsafe { libc::write(self.fd, data_ptr, data.len()) };
|
||||
from_unix_result(rv as usize + 1)
|
||||
}
|
||||
|
||||
// USB HID writes don't buffer, so this will be a nop.
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl U2FDevice for Device {
|
||||
fn get_cid<'a>(&'a self) -> &'a [u8; 4] {
|
||||
&self.cid
|
||||
}
|
||||
|
||||
fn set_cid(&mut self, cid: [u8; 4]) {
|
||||
self.cid = cid;
|
||||
}
|
||||
}
|
9
third_party/rust/authenticator/src/freebsd/mod.rs
vendored
Normal file
9
third_party/rust/authenticator/src/freebsd/mod.rs
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
pub mod device;
|
||||
pub mod transaction;
|
||||
|
||||
mod monitor;
|
||||
mod uhid;
|
133
third_party/rust/authenticator/src/freebsd/monitor.rs
vendored
Normal file
133
third_party/rust/authenticator/src/freebsd/monitor.rs
vendored
Normal file
@ -0,0 +1,133 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use devd_rs;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::OsString;
|
||||
use std::sync::Arc;
|
||||
use std::{fs, io};
|
||||
|
||||
use runloop::RunLoop;
|
||||
|
||||
const POLL_TIMEOUT: usize = 100;
|
||||
|
||||
pub enum Event {
|
||||
Add(OsString),
|
||||
Remove(OsString),
|
||||
}
|
||||
|
||||
impl Event {
|
||||
fn from_devd(event: devd_rs::Event) -> Option<Self> {
|
||||
match event {
|
||||
devd_rs::Event::Attach {
|
||||
ref dev,
|
||||
parent: _,
|
||||
location: _,
|
||||
} if dev.starts_with("uhid") => Some(Event::Add(("/dev/".to_owned() + dev).into())),
|
||||
devd_rs::Event::Detach {
|
||||
ref dev,
|
||||
parent: _,
|
||||
location: _,
|
||||
} if dev.starts_with("uhid") => Some(Event::Remove(("/dev/".to_owned() + dev).into())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_error(e: devd_rs::Error) -> io::Error {
|
||||
e.into()
|
||||
}
|
||||
|
||||
pub struct Monitor<F>
|
||||
where
|
||||
F: Fn(OsString, &Fn() -> bool) + Sync,
|
||||
{
|
||||
runloops: HashMap<OsString, RunLoop>,
|
||||
new_device_cb: Arc<F>,
|
||||
}
|
||||
|
||||
impl<F> Monitor<F>
|
||||
where
|
||||
F: Fn(OsString, &Fn() -> bool) + Send + Sync + 'static,
|
||||
{
|
||||
pub fn new(new_device_cb: F) -> Self {
|
||||
Self {
|
||||
runloops: HashMap::new(),
|
||||
new_device_cb: Arc::new(new_device_cb),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self, alive: &Fn() -> bool) -> io::Result<()> {
|
||||
let mut ctx = devd_rs::Context::new().map_err(convert_error)?;
|
||||
|
||||
// Iterate all existing devices.
|
||||
for dev in fs::read_dir("/dev")? {
|
||||
if let Ok(dev) = dev {
|
||||
let filename_ = dev.file_name();
|
||||
let filename = filename_.to_str().unwrap_or("");
|
||||
if filename.starts_with("uhid") {
|
||||
self.add_device(("/dev/".to_owned() + filename).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop until we're stopped by the controlling thread, or fail.
|
||||
while alive() {
|
||||
// Wait for new events, break on failure.
|
||||
match ctx.wait_for_event(POLL_TIMEOUT) {
|
||||
Err(devd_rs::Error::Timeout) => (),
|
||||
Err(e) => return Err(e.into()),
|
||||
Ok(event) => {
|
||||
if let Some(event) = Event::from_devd(event) {
|
||||
self.process_event(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all tracked devices.
|
||||
self.remove_all_devices();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_event(&mut self, event: Event) {
|
||||
match event {
|
||||
Event::Add(path) => {
|
||||
self.add_device(path);
|
||||
}
|
||||
Event::Remove(path) => {
|
||||
self.remove_device(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_device(&mut self, path: OsString) {
|
||||
let f = self.new_device_cb.clone();
|
||||
let key = path.clone();
|
||||
|
||||
let runloop = RunLoop::new(move |alive| {
|
||||
if alive() {
|
||||
f(path, alive);
|
||||
}
|
||||
});
|
||||
|
||||
if let Ok(runloop) = runloop {
|
||||
self.runloops.insert(key, runloop);
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_device(&mut self, path: OsString) {
|
||||
if let Some(runloop) = self.runloops.remove(&path) {
|
||||
runloop.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_all_devices(&mut self) {
|
||||
while !self.runloops.is_empty() {
|
||||
let path = self.runloops.keys().next().unwrap().clone();
|
||||
self.remove_device(path);
|
||||
}
|
||||
}
|
||||
}
|
49
third_party/rust/authenticator/src/freebsd/transaction.rs
vendored
Normal file
49
third_party/rust/authenticator/src/freebsd/transaction.rs
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use platform::monitor::Monitor;
|
||||
use runloop::RunLoop;
|
||||
use std::ffi::OsString;
|
||||
use util::OnceCallback;
|
||||
|
||||
pub struct Transaction {
|
||||
// Handle to the thread loop.
|
||||
thread: Option<RunLoop>,
|
||||
}
|
||||
|
||||
impl Transaction {
|
||||
pub fn new<F, T>(
|
||||
timeout: u64,
|
||||
callback: OnceCallback<T>,
|
||||
new_device_cb: F,
|
||||
) -> Result<Self, ::Error>
|
||||
where
|
||||
F: Fn(OsString, &Fn() -> bool) + Sync + Send + 'static,
|
||||
T: 'static,
|
||||
{
|
||||
let thread = RunLoop::new_with_timeout(
|
||||
move |alive| {
|
||||
// Create a new device monitor.
|
||||
let mut monitor = Monitor::new(new_device_cb);
|
||||
|
||||
// Start polling for new devices.
|
||||
try_or!(monitor.run(alive), |_| callback.call(Err(::Error::Unknown)));
|
||||
|
||||
// Send an error, if the callback wasn't called already.
|
||||
callback.call(Err(::Error::NotAllowed));
|
||||
},
|
||||
timeout,
|
||||
)
|
||||
.map_err(|_| ::Error::Unknown)?;
|
||||
|
||||
Ok(Self {
|
||||
thread: Some(thread),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn cancel(&mut self) {
|
||||
// This must never be None.
|
||||
self.thread.take().unwrap().cancel();
|
||||
}
|
||||
}
|
92
third_party/rust/authenticator/src/freebsd/uhid.rs
vendored
Normal file
92
third_party/rust/authenticator/src/freebsd/uhid.rs
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
extern crate libc;
|
||||
|
||||
use std::io;
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::ptr;
|
||||
|
||||
use hidproto::*;
|
||||
use util::from_unix_result;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct GenDescriptor {
|
||||
ugd_data: *mut u8,
|
||||
ugd_lang_id: u16,
|
||||
ugd_maxlen: u16,
|
||||
ugd_actlen: u16,
|
||||
ugd_offset: u16,
|
||||
ugd_config_index: u8,
|
||||
ugd_string_index: u8,
|
||||
ugd_iface_index: u8,
|
||||
ugd_altif_index: u8,
|
||||
ugd_endpt_index: u8,
|
||||
ugd_report_index: u8,
|
||||
reserved: [u8; 16],
|
||||
}
|
||||
|
||||
impl Default for GenDescriptor {
|
||||
fn default() -> GenDescriptor {
|
||||
GenDescriptor {
|
||||
ugd_data: ptr::null_mut(),
|
||||
ugd_lang_id: 0,
|
||||
ugd_maxlen: 65535,
|
||||
ugd_actlen: 0,
|
||||
ugd_offset: 0,
|
||||
ugd_config_index: 0,
|
||||
ugd_string_index: 0,
|
||||
ugd_iface_index: 0,
|
||||
ugd_altif_index: 0,
|
||||
ugd_endpt_index: 0,
|
||||
ugd_report_index: 0,
|
||||
reserved: [0; 16],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const IOWR: u32 = 0x40000000 | 0x80000000;
|
||||
|
||||
const IOCPARM_SHIFT: u32 = 13;
|
||||
const IOCPARM_MASK: u32 = ((1 << IOCPARM_SHIFT) - 1);
|
||||
|
||||
const TYPESHIFT: u32 = 8;
|
||||
const SIZESHIFT: u32 = 16;
|
||||
|
||||
macro_rules! ioctl {
|
||||
($dir:expr, $name:ident, $ioty:expr, $nr:expr, $size:expr; $ty:ty) => {
|
||||
pub unsafe fn $name(fd: libc::c_int, val: *mut $ty) -> io::Result<libc::c_int> {
|
||||
let ioc = ($dir as u32)
|
||||
| (($size as u32 & IOCPARM_MASK) << SIZESHIFT)
|
||||
| (($ioty as u32) << TYPESHIFT)
|
||||
| ($nr as u32);
|
||||
from_unix_result(libc::ioctl(fd, ioc as libc::c_ulong, val))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// https://github.com/freebsd/freebsd/blob/master/sys/dev/usb/usb_ioctl.h
|
||||
ioctl!(IOWR, usb_get_report_desc, b'U', 21, 32; /*struct*/ GenDescriptor);
|
||||
|
||||
fn read_report_descriptor(fd: RawFd) -> io::Result<ReportDescriptor> {
|
||||
let mut desc = GenDescriptor::default();
|
||||
let _ = unsafe { usb_get_report_desc(fd, &mut desc)? };
|
||||
desc.ugd_maxlen = desc.ugd_actlen;
|
||||
let mut value = Vec::with_capacity(desc.ugd_actlen as usize);
|
||||
unsafe {
|
||||
value.set_len(desc.ugd_actlen as usize);
|
||||
}
|
||||
desc.ugd_data = value.as_mut_ptr();
|
||||
let _ = unsafe { usb_get_report_desc(fd, &mut desc)? };
|
||||
Ok(ReportDescriptor { value })
|
||||
}
|
||||
|
||||
pub fn is_u2f_device(fd: RawFd) -> bool {
|
||||
match read_report_descriptor(fd) {
|
||||
Ok(desc) => has_fido_usage(desc),
|
||||
Err(_) => false, // Upon failure, just say it's not a U2F device.
|
||||
}
|
||||
}
|
163
third_party/rust/authenticator/src/hidproto.rs
vendored
Normal file
163
third_party/rust/authenticator/src/hidproto.rs
vendored
Normal file
@ -0,0 +1,163 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Shared code for platforms that use raw HID access (Linux, FreeBSD, etc.)
|
||||
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(cast_lossless, needless_lifetimes))]
|
||||
|
||||
use std::mem;
|
||||
|
||||
use consts::{FIDO_USAGE_PAGE, FIDO_USAGE_U2FHID};
|
||||
|
||||
// The 4 MSBs (the tag) are set when it's a long item.
|
||||
const HID_MASK_LONG_ITEM_TAG: u8 = 0b1111_0000;
|
||||
// The 2 LSBs denote the size of a short item.
|
||||
const HID_MASK_SHORT_ITEM_SIZE: u8 = 0b0000_0011;
|
||||
// The 6 MSBs denote the tag (4) and type (2).
|
||||
const HID_MASK_ITEM_TAGTYPE: u8 = 0b1111_1100;
|
||||
// tag=0000, type=10 (local)
|
||||
const HID_ITEM_TAGTYPE_USAGE: u8 = 0b0000_1000;
|
||||
// tag=0000, type=01 (global)
|
||||
const HID_ITEM_TAGTYPE_USAGE_PAGE: u8 = 0b0000_0100;
|
||||
|
||||
pub struct ReportDescriptor {
|
||||
pub value: Vec<u8>,
|
||||
}
|
||||
|
||||
impl ReportDescriptor {
|
||||
fn iter(self) -> ReportDescriptorIterator {
|
||||
ReportDescriptorIterator::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Data {
|
||||
UsagePage { data: u32 },
|
||||
Usage { data: u32 },
|
||||
}
|
||||
|
||||
pub struct ReportDescriptorIterator {
|
||||
desc: ReportDescriptor,
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
impl ReportDescriptorIterator {
|
||||
fn new(desc: ReportDescriptor) -> Self {
|
||||
Self { desc, pos: 0 }
|
||||
}
|
||||
|
||||
fn next_item(&mut self) -> Option<Data> {
|
||||
let item = get_hid_item(&self.desc.value[self.pos..]);
|
||||
if item.is_none() {
|
||||
self.pos = self.desc.value.len(); // Close, invalid data.
|
||||
return None;
|
||||
}
|
||||
|
||||
let (tag_type, key_len, data) = item.unwrap();
|
||||
|
||||
// Advance if we have a valid item.
|
||||
self.pos += key_len + data.len();
|
||||
|
||||
// We only check short items.
|
||||
if key_len > 1 {
|
||||
return None; // Check next item.
|
||||
}
|
||||
|
||||
// Short items have max. length of 4 bytes.
|
||||
assert!(data.len() <= mem::size_of::<u32>());
|
||||
|
||||
// Convert data bytes to a uint.
|
||||
let data = read_uint_le(data);
|
||||
|
||||
match tag_type {
|
||||
HID_ITEM_TAGTYPE_USAGE_PAGE => Some(Data::UsagePage { data }),
|
||||
HID_ITEM_TAGTYPE_USAGE => Some(Data::Usage { data }),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for ReportDescriptorIterator {
|
||||
type Item = Data;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.pos >= self.desc.value.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.next_item().or_else(|| self.next())
|
||||
}
|
||||
}
|
||||
|
||||
fn get_hid_item<'a>(buf: &'a [u8]) -> Option<(u8, usize, &'a [u8])> {
|
||||
if (buf[0] & HID_MASK_LONG_ITEM_TAG) == HID_MASK_LONG_ITEM_TAG {
|
||||
get_hid_long_item(buf)
|
||||
} else {
|
||||
get_hid_short_item(buf)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_hid_long_item<'a>(buf: &'a [u8]) -> Option<(u8, usize, &'a [u8])> {
|
||||
// A valid long item has at least three bytes.
|
||||
if buf.len() < 3 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let len = buf[1] as usize;
|
||||
|
||||
// Ensure that there are enough bytes left in the buffer.
|
||||
if len > buf.len() - 3 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some((buf[2], 3 /* key length */, &buf[3..]))
|
||||
}
|
||||
|
||||
fn get_hid_short_item<'a>(buf: &'a [u8]) -> Option<(u8, usize, &'a [u8])> {
|
||||
// This is a short item. The bottom two bits of the key
|
||||
// contain the length of the data section (value) for this key.
|
||||
let len = match buf[0] & HID_MASK_SHORT_ITEM_SIZE {
|
||||
s @ 0...2 => s as usize,
|
||||
_ => 4, /* _ == 3 */
|
||||
};
|
||||
|
||||
// Ensure that there are enough bytes left in the buffer.
|
||||
if len > buf.len() - 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some((
|
||||
buf[0] & HID_MASK_ITEM_TAGTYPE,
|
||||
1, /* key length */
|
||||
&buf[1..=len],
|
||||
))
|
||||
}
|
||||
|
||||
fn read_uint_le(buf: &[u8]) -> u32 {
|
||||
assert!(buf.len() <= 4);
|
||||
// Parse the number in little endian byte order.
|
||||
buf.iter()
|
||||
.rev()
|
||||
.fold(0, |num, b| (num << 8) | (u32::from(*b)))
|
||||
}
|
||||
|
||||
pub fn has_fido_usage(desc: ReportDescriptor) -> bool {
|
||||
let mut usage_page = None;
|
||||
let mut usage = None;
|
||||
|
||||
for data in desc.iter() {
|
||||
match data {
|
||||
Data::UsagePage { data } => usage_page = Some(data),
|
||||
Data::Usage { data } => usage = Some(data),
|
||||
}
|
||||
|
||||
// Check the values we found.
|
||||
if let (Some(usage_page), Some(usage)) = (usage_page, usage) {
|
||||
return usage_page == u32::from(FIDO_USAGE_PAGE)
|
||||
&& usage == u32::from(FIDO_USAGE_U2FHID);
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
111
third_party/rust/authenticator/src/lib.rs
vendored
Normal file
111
third_party/rust/authenticator/src/lib.rs
vendored
Normal file
@ -0,0 +1,111 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#[macro_use]
|
||||
mod util;
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||
pub mod hidproto;
|
||||
|
||||
#[cfg(any(target_os = "linux"))]
|
||||
extern crate libudev;
|
||||
|
||||
#[cfg(any(target_os = "linux"))]
|
||||
#[path = "linux/mod.rs"]
|
||||
pub mod platform;
|
||||
|
||||
#[cfg(any(target_os = "freebsd"))]
|
||||
extern crate devd_rs;
|
||||
|
||||
#[cfg(any(target_os = "freebsd"))]
|
||||
#[path = "freebsd/mod.rs"]
|
||||
pub mod platform;
|
||||
|
||||
#[cfg(any(target_os = "macos"))]
|
||||
extern crate core_foundation;
|
||||
|
||||
#[cfg(any(target_os = "macos"))]
|
||||
#[path = "macos/mod.rs"]
|
||||
pub mod platform;
|
||||
|
||||
#[cfg(any(target_os = "windows"))]
|
||||
#[path = "windows/mod.rs"]
|
||||
pub mod platform;
|
||||
|
||||
#[cfg(not(any(
|
||||
target_os = "linux",
|
||||
target_os = "freebsd",
|
||||
target_os = "macos",
|
||||
target_os = "windows"
|
||||
)))]
|
||||
#[path = "stub/mod.rs"]
|
||||
pub mod platform;
|
||||
|
||||
extern crate boxfnonce;
|
||||
extern crate libc;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate rand;
|
||||
extern crate runloop;
|
||||
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
|
||||
mod consts;
|
||||
mod statemachine;
|
||||
mod u2fprotocol;
|
||||
mod u2ftypes;
|
||||
|
||||
mod manager;
|
||||
pub use manager::U2FManager;
|
||||
|
||||
mod capi;
|
||||
pub use capi::*;
|
||||
|
||||
// Keep this in sync with the constants in u2fhid-capi.h.
|
||||
bitflags! {
|
||||
pub struct RegisterFlags: u64 {
|
||||
const REQUIRE_RESIDENT_KEY = 1;
|
||||
const REQUIRE_USER_VERIFICATION = 2;
|
||||
const REQUIRE_PLATFORM_ATTACHMENT = 4;
|
||||
}
|
||||
}
|
||||
bitflags! {
|
||||
pub struct SignFlags: u64 {
|
||||
const REQUIRE_USER_VERIFICATION = 1;
|
||||
}
|
||||
}
|
||||
bitflags! {
|
||||
pub struct AuthenticatorTransports: u8 {
|
||||
const USB = 1;
|
||||
const NFC = 2;
|
||||
const BLE = 4;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct KeyHandle {
|
||||
pub credential: Vec<u8>,
|
||||
pub transports: AuthenticatorTransports,
|
||||
}
|
||||
|
||||
pub type AppId = Vec<u8>;
|
||||
pub type RegisterResult = Vec<u8>;
|
||||
pub type SignResult = (AppId, Vec<u8>, Vec<u8>);
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Error {
|
||||
Unknown = 1,
|
||||
NotSupported = 2,
|
||||
InvalidState = 3,
|
||||
ConstraintError = 4,
|
||||
NotAllowed = 5,
|
||||
}
|
||||
|
||||
#[cfg(fuzzing)]
|
||||
pub use consts::*;
|
||||
#[cfg(fuzzing)]
|
||||
pub use u2fprotocol::*;
|
||||
#[cfg(fuzzing)]
|
||||
pub use u2ftypes::*;
|
83
third_party/rust/authenticator/src/linux/device.rs
vendored
Normal file
83
third_party/rust/authenticator/src/linux/device.rs
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
extern crate libc;
|
||||
|
||||
use std::ffi::{CString, OsString};
|
||||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
use std::os::unix::prelude::*;
|
||||
|
||||
use consts::CID_BROADCAST;
|
||||
use platform::hidraw;
|
||||
use u2ftypes::U2FDevice;
|
||||
use util::from_unix_result;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Device {
|
||||
path: OsString,
|
||||
fd: libc::c_int,
|
||||
cid: [u8; 4],
|
||||
}
|
||||
|
||||
impl Device {
|
||||
pub fn new(path: OsString) -> io::Result<Self> {
|
||||
let cstr = CString::new(path.as_bytes())?;
|
||||
let fd = unsafe { libc::open(cstr.as_ptr(), libc::O_RDWR) };
|
||||
let fd = from_unix_result(fd)?;
|
||||
Ok(Self {
|
||||
path,
|
||||
fd,
|
||||
cid: CID_BROADCAST,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_u2f(&self) -> bool {
|
||||
hidraw::is_u2f_device(self.fd)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Device {
|
||||
fn drop(&mut self) {
|
||||
// Close the fd, ignore any errors.
|
||||
let _ = unsafe { libc::close(self.fd) };
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Device {
|
||||
fn eq(&self, other: &Device) -> bool {
|
||||
self.path == other.path
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Device {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let bufp = buf.as_mut_ptr() as *mut libc::c_void;
|
||||
let rv = unsafe { libc::read(self.fd, bufp, buf.len()) };
|
||||
from_unix_result(rv as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Device {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let bufp = buf.as_ptr() as *const libc::c_void;
|
||||
let rv = unsafe { libc::write(self.fd, bufp, buf.len()) };
|
||||
from_unix_result(rv as usize)
|
||||
}
|
||||
|
||||
// USB HID writes don't buffer, so this will be a nop.
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl U2FDevice for Device {
|
||||
fn get_cid(&self) -> &[u8; 4] {
|
||||
&self.cid
|
||||
}
|
||||
|
||||
fn set_cid(&mut self, cid: [u8; 4]) {
|
||||
self.cid = cid;
|
||||
}
|
||||
}
|
81
third_party/rust/authenticator/src/linux/hidraw.rs
vendored
Normal file
81
third_party/rust/authenticator/src/linux/hidraw.rs
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]
|
||||
|
||||
extern crate libc;
|
||||
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::os::unix::io::RawFd;
|
||||
|
||||
use hidproto::*;
|
||||
use util::{from_unix_result, io_err};
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[repr(C)]
|
||||
pub struct LinuxReportDescriptor {
|
||||
size: ::libc::c_int,
|
||||
value: [u8; 4096],
|
||||
}
|
||||
|
||||
const NRBITS: u32 = 8;
|
||||
const TYPEBITS: u32 = 8;
|
||||
|
||||
const READ: u8 = 2;
|
||||
const SIZEBITS: u8 = 14;
|
||||
|
||||
const NRSHIFT: u32 = 0;
|
||||
const TYPESHIFT: u32 = NRSHIFT + NRBITS as u32;
|
||||
const SIZESHIFT: u32 = TYPESHIFT + TYPEBITS as u32;
|
||||
const DIRSHIFT: u32 = SIZESHIFT + SIZEBITS as u32;
|
||||
|
||||
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/hid.h
|
||||
const HID_MAX_DESCRIPTOR_SIZE: usize = 4096;
|
||||
|
||||
macro_rules! ioctl {
|
||||
($dir:expr, $name:ident, $ioty:expr, $nr:expr; $ty:ty) => {
|
||||
pub unsafe fn $name(fd: libc::c_int, val: *mut $ty) -> io::Result<libc::c_int> {
|
||||
let size = mem::size_of::<$ty>();
|
||||
let ioc = (($dir as u32) << DIRSHIFT)
|
||||
| (($ioty as u32) << TYPESHIFT)
|
||||
| (($nr as u32) << NRSHIFT)
|
||||
| ((size as u32) << SIZESHIFT);
|
||||
|
||||
#[cfg(not(target_env = "musl"))]
|
||||
type IocType = libc::c_ulong;
|
||||
#[cfg(target_env = "musl")]
|
||||
type IocType = libc::c_int;
|
||||
|
||||
from_unix_result(libc::ioctl(fd, ioc as IocType, val))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/hidraw.h
|
||||
ioctl!(READ, hidiocgrdescsize, b'H', 0x01; ::libc::c_int);
|
||||
ioctl!(READ, hidiocgrdesc, b'H', 0x02; /*struct*/ LinuxReportDescriptor);
|
||||
|
||||
pub fn is_u2f_device(fd: RawFd) -> bool {
|
||||
match read_report_descriptor(fd) {
|
||||
Ok(desc) => has_fido_usage(desc),
|
||||
Err(_) => false, // Upon failure, just say it's not a U2F device.
|
||||
}
|
||||
}
|
||||
|
||||
fn read_report_descriptor(fd: RawFd) -> io::Result<ReportDescriptor> {
|
||||
let mut desc = LinuxReportDescriptor {
|
||||
size: 0,
|
||||
value: [0; HID_MAX_DESCRIPTOR_SIZE],
|
||||
};
|
||||
|
||||
let _ = unsafe { hidiocgrdescsize(fd, &mut desc.size)? };
|
||||
if desc.size == 0 || desc.size as usize > desc.value.len() {
|
||||
return Err(io_err("unexpected hidiocgrdescsize() result"));
|
||||
}
|
||||
|
||||
let _ = unsafe { hidiocgrdesc(fd, &mut desc)? };
|
||||
let mut value = Vec::from(&desc.value[..]);
|
||||
value.truncate(desc.size as usize);
|
||||
Ok(ReportDescriptor { value })
|
||||
}
|
9
third_party/rust/authenticator/src/linux/mod.rs
vendored
Normal file
9
third_party/rust/authenticator/src/linux/mod.rs
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
pub mod device;
|
||||
pub mod transaction;
|
||||
|
||||
mod hidraw;
|
||||
mod monitor;
|
133
third_party/rust/authenticator/src/linux/monitor.rs
vendored
Normal file
133
third_party/rust/authenticator/src/linux/monitor.rs
vendored
Normal file
@ -0,0 +1,133 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use libc::{c_int, c_short, c_ulong};
|
||||
use libudev;
|
||||
use libudev::EventType;
|
||||
use runloop::RunLoop;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::OsString;
|
||||
use std::io;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::sync::Arc;
|
||||
|
||||
const UDEV_SUBSYSTEM: &str = "hidraw";
|
||||
const POLLIN: c_short = 0x0001;
|
||||
const POLL_TIMEOUT: c_int = 100;
|
||||
|
||||
fn poll(fds: &mut Vec<::libc::pollfd>) -> io::Result<()> {
|
||||
let nfds = fds.len() as c_ulong;
|
||||
|
||||
let rv = unsafe { ::libc::poll((&mut fds[..]).as_mut_ptr(), nfds, POLL_TIMEOUT) };
|
||||
|
||||
if rv < 0 {
|
||||
Err(io::Error::from_raw_os_error(rv))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Monitor<F>
|
||||
where
|
||||
F: Fn(OsString, &Fn() -> bool) + Sync,
|
||||
{
|
||||
runloops: HashMap<OsString, RunLoop>,
|
||||
new_device_cb: Arc<F>,
|
||||
}
|
||||
|
||||
impl<F> Monitor<F>
|
||||
where
|
||||
F: Fn(OsString, &Fn() -> bool) + Send + Sync + 'static,
|
||||
{
|
||||
pub fn new(new_device_cb: F) -> Self {
|
||||
Self {
|
||||
runloops: HashMap::new(),
|
||||
new_device_cb: Arc::new(new_device_cb),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self, alive: &Fn() -> bool) -> io::Result<()> {
|
||||
let ctx = libudev::Context::new()?;
|
||||
|
||||
let mut enumerator = libudev::Enumerator::new(&ctx)?;
|
||||
enumerator.match_subsystem(UDEV_SUBSYSTEM)?;
|
||||
|
||||
// Iterate all existing devices.
|
||||
for dev in enumerator.scan_devices()? {
|
||||
if let Some(path) = dev.devnode().map(|p| p.to_owned().into_os_string()) {
|
||||
self.add_device(path);
|
||||
}
|
||||
}
|
||||
|
||||
let mut monitor = libudev::Monitor::new(&ctx)?;
|
||||
monitor.match_subsystem(UDEV_SUBSYSTEM)?;
|
||||
|
||||
// Start listening for new devices.
|
||||
let mut socket = monitor.listen()?;
|
||||
let mut fds = vec![::libc::pollfd {
|
||||
fd: socket.as_raw_fd(),
|
||||
events: POLLIN,
|
||||
revents: 0,
|
||||
}];
|
||||
|
||||
while alive() {
|
||||
// Wait for new events, break on failure.
|
||||
poll(&mut fds)?;
|
||||
|
||||
if let Some(event) = socket.receive_event() {
|
||||
self.process_event(&event);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all tracked devices.
|
||||
self.remove_all_devices();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_event(&mut self, event: &libudev::Event) {
|
||||
let path = event
|
||||
.device()
|
||||
.devnode()
|
||||
.map(|dn| dn.to_owned().into_os_string());
|
||||
|
||||
match (event.event_type(), path) {
|
||||
(EventType::Add, Some(path)) => {
|
||||
self.add_device(path);
|
||||
}
|
||||
(EventType::Remove, Some(path)) => {
|
||||
self.remove_device(&path);
|
||||
}
|
||||
_ => { /* ignore other types and failures */ }
|
||||
}
|
||||
}
|
||||
|
||||
fn add_device(&mut self, path: OsString) {
|
||||
let f = self.new_device_cb.clone();
|
||||
let key = path.clone();
|
||||
|
||||
let runloop = RunLoop::new(move |alive| {
|
||||
if alive() {
|
||||
f(path, alive);
|
||||
}
|
||||
});
|
||||
|
||||
if let Ok(runloop) = runloop {
|
||||
self.runloops.insert(key, runloop);
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_device(&mut self, path: &OsString) {
|
||||
if let Some(runloop) = self.runloops.remove(path) {
|
||||
runloop.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_all_devices(&mut self) {
|
||||
while !self.runloops.is_empty() {
|
||||
let path = self.runloops.keys().next().unwrap().clone();
|
||||
self.remove_device(&path);
|
||||
}
|
||||
}
|
||||
}
|
49
third_party/rust/authenticator/src/linux/transaction.rs
vendored
Normal file
49
third_party/rust/authenticator/src/linux/transaction.rs
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use platform::monitor::Monitor;
|
||||
use runloop::RunLoop;
|
||||
use std::ffi::OsString;
|
||||
use util::OnceCallback;
|
||||
|
||||
pub struct Transaction {
|
||||
// Handle to the thread loop.
|
||||
thread: Option<RunLoop>,
|
||||
}
|
||||
|
||||
impl Transaction {
|
||||
pub fn new<F, T>(
|
||||
timeout: u64,
|
||||
callback: OnceCallback<T>,
|
||||
new_device_cb: F,
|
||||
) -> Result<Self, ::Error>
|
||||
where
|
||||
F: Fn(OsString, &Fn() -> bool) + Sync + Send + 'static,
|
||||
T: 'static,
|
||||
{
|
||||
let thread = RunLoop::new_with_timeout(
|
||||
move |alive| {
|
||||
// Create a new device monitor.
|
||||
let mut monitor = Monitor::new(new_device_cb);
|
||||
|
||||
// Start polling for new devices.
|
||||
try_or!(monitor.run(alive), |_| callback.call(Err(::Error::Unknown)));
|
||||
|
||||
// Send an error, if the callback wasn't called already.
|
||||
callback.call(Err(::Error::NotAllowed));
|
||||
},
|
||||
timeout,
|
||||
)
|
||||
.map_err(|_| ::Error::Unknown)?;
|
||||
|
||||
Ok(Self {
|
||||
thread: Some(thread),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn cancel(&mut self) {
|
||||
// This must never be None.
|
||||
self.thread.take().unwrap().cancel();
|
||||
}
|
||||
}
|
102
third_party/rust/authenticator/src/macos/device.rs
vendored
Normal file
102
third_party/rust/authenticator/src/macos/device.rs
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
extern crate log;
|
||||
|
||||
use consts::{CID_BROADCAST, HID_RPT_SIZE};
|
||||
use core_foundation::base::*;
|
||||
use platform::iokit::*;
|
||||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
use std::sync::mpsc::{Receiver, RecvTimeoutError};
|
||||
use std::time::Duration;
|
||||
use u2ftypes::U2FDevice;
|
||||
|
||||
const READ_TIMEOUT: u64 = 15;
|
||||
|
||||
pub struct Device {
|
||||
device_ref: IOHIDDeviceRef,
|
||||
cid: [u8; 4],
|
||||
report_rx: Receiver<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
pub fn new(dev_info: (IOHIDDeviceRef, Receiver<Vec<u8>>)) -> io::Result<Self> {
|
||||
let (device_ref, report_rx) = dev_info;
|
||||
Ok(Self {
|
||||
device_ref,
|
||||
cid: CID_BROADCAST,
|
||||
report_rx,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_u2f(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Device {
|
||||
fn eq(&self, other_device: &Device) -> bool {
|
||||
self.device_ref == other_device.device_ref
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Device {
|
||||
fn read(&mut self, mut bytes: &mut [u8]) -> io::Result<usize> {
|
||||
let timeout = Duration::from_secs(READ_TIMEOUT);
|
||||
let data = match self.report_rx.recv_timeout(timeout) {
|
||||
Ok(v) => v,
|
||||
Err(e) if e == RecvTimeoutError::Timeout => {
|
||||
return Err(io::Error::new(io::ErrorKind::TimedOut, e));
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(io::Error::new(io::ErrorKind::UnexpectedEof, e));
|
||||
}
|
||||
};
|
||||
bytes.write(&data)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Device {
|
||||
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
|
||||
assert_eq!(bytes.len(), HID_RPT_SIZE + 1);
|
||||
|
||||
let report_id = i64::from(bytes[0]);
|
||||
// Skip report number when not using numbered reports.
|
||||
let start = if report_id == 0x0 { 1 } else { 0 };
|
||||
let data = &bytes[start..];
|
||||
|
||||
let result = unsafe {
|
||||
IOHIDDeviceSetReport(
|
||||
self.device_ref,
|
||||
kIOHIDReportTypeOutput,
|
||||
report_id,
|
||||
data.as_ptr(),
|
||||
data.len() as CFIndex,
|
||||
)
|
||||
};
|
||||
if result != 0 {
|
||||
warn!("set_report sending failure = {0:X}", result);
|
||||
return Err(io::Error::from_raw_os_error(result));
|
||||
}
|
||||
trace!("set_report sending success = {0:X}", result);
|
||||
|
||||
Ok(bytes.len())
|
||||
}
|
||||
|
||||
// USB HID writes don't buffer, so this will be a nop.
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl U2FDevice for Device {
|
||||
fn get_cid(&self) -> &[u8; 4] {
|
||||
&self.cid
|
||||
}
|
||||
|
||||
fn set_cid(&mut self, cid: [u8; 4]) {
|
||||
self.cid = cid;
|
||||
}
|
||||
}
|
291
third_party/rust/authenticator/src/macos/iokit.rs
vendored
Normal file
291
third_party/rust/authenticator/src/macos/iokit.rs
vendored
Normal file
@ -0,0 +1,291 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]
|
||||
|
||||
extern crate libc;
|
||||
|
||||
use consts::{FIDO_USAGE_PAGE, FIDO_USAGE_U2FHID};
|
||||
use core_foundation::array::*;
|
||||
use core_foundation::base::*;
|
||||
use core_foundation::dictionary::*;
|
||||
use core_foundation::number::*;
|
||||
use core_foundation::runloop::*;
|
||||
use core_foundation::string::*;
|
||||
use std::ops::Deref;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
type IOOptionBits = u32;
|
||||
|
||||
pub type IOReturn = libc::c_int;
|
||||
|
||||
pub type IOHIDManagerRef = *mut __IOHIDManager;
|
||||
pub type IOHIDManagerOptions = IOOptionBits;
|
||||
|
||||
pub type IOHIDDeviceCallback = extern "C" fn(
|
||||
context: *mut c_void,
|
||||
result: IOReturn,
|
||||
sender: *mut c_void,
|
||||
device: IOHIDDeviceRef,
|
||||
);
|
||||
|
||||
pub type IOHIDReportType = IOOptionBits;
|
||||
pub type IOHIDReportCallback = extern "C" fn(
|
||||
context: *mut c_void,
|
||||
result: IOReturn,
|
||||
sender: IOHIDDeviceRef,
|
||||
report_type: IOHIDReportType,
|
||||
report_id: u32,
|
||||
report: *mut u8,
|
||||
report_len: CFIndex,
|
||||
);
|
||||
|
||||
pub const kIOHIDManagerOptionNone: IOHIDManagerOptions = 0;
|
||||
|
||||
pub const kIOHIDReportTypeOutput: IOHIDReportType = 1;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct __IOHIDManager {
|
||||
__private: c_void,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct IOHIDDeviceRef(*const c_void);
|
||||
|
||||
unsafe impl Send for IOHIDDeviceRef {}
|
||||
unsafe impl Sync for IOHIDDeviceRef {}
|
||||
|
||||
pub struct SendableRunLoop(CFRunLoopRef);
|
||||
|
||||
impl SendableRunLoop {
|
||||
pub fn new(runloop: CFRunLoopRef) -> Self {
|
||||
// Keep the CFRunLoop alive for as long as we are.
|
||||
unsafe { CFRetain(runloop as *mut c_void) };
|
||||
|
||||
SendableRunLoop(runloop)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for SendableRunLoop {}
|
||||
|
||||
impl Deref for SendableRunLoop {
|
||||
type Target = CFRunLoopRef;
|
||||
|
||||
fn deref(&self) -> &CFRunLoopRef {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SendableRunLoop {
|
||||
fn drop(&mut self) {
|
||||
unsafe { CFRelease(self.0 as *mut c_void) };
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct CFRunLoopObserverContext {
|
||||
pub version: CFIndex,
|
||||
pub info: *mut c_void,
|
||||
pub retain: Option<extern "C" fn(info: *const c_void) -> *const c_void>,
|
||||
pub release: Option<extern "C" fn(info: *const c_void)>,
|
||||
pub copyDescription: Option<extern "C" fn(info: *const c_void) -> CFStringRef>,
|
||||
}
|
||||
|
||||
impl CFRunLoopObserverContext {
|
||||
pub fn new(context: *mut c_void) -> Self {
|
||||
Self {
|
||||
version: 0 as CFIndex,
|
||||
info: context,
|
||||
retain: None,
|
||||
release: None,
|
||||
copyDescription: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CFRunLoopEntryObserver {
|
||||
observer: CFRunLoopObserverRef,
|
||||
// Keep alive until the observer goes away.
|
||||
context_ptr: *mut CFRunLoopObserverContext,
|
||||
}
|
||||
|
||||
impl CFRunLoopEntryObserver {
|
||||
pub fn new(callback: CFRunLoopObserverCallBack, context: *mut c_void) -> Self {
|
||||
let context = CFRunLoopObserverContext::new(context);
|
||||
let context_ptr = Box::into_raw(Box::new(context));
|
||||
|
||||
let observer = unsafe {
|
||||
CFRunLoopObserverCreate(
|
||||
kCFAllocatorDefault,
|
||||
kCFRunLoopEntry,
|
||||
false as Boolean,
|
||||
0,
|
||||
callback,
|
||||
context_ptr,
|
||||
)
|
||||
};
|
||||
|
||||
Self {
|
||||
observer,
|
||||
context_ptr,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_to_current_runloop(&self) {
|
||||
unsafe {
|
||||
CFRunLoopAddObserver(CFRunLoopGetCurrent(), self.observer, kCFRunLoopDefaultMode)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for CFRunLoopEntryObserver {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
CFRelease(self.observer as *mut c_void);
|
||||
|
||||
// Drop the CFRunLoopObserverContext.
|
||||
let _ = Box::from_raw(self.context_ptr);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IOHIDDeviceMatcher {
|
||||
pub dict: CFDictionary<CFString, CFNumber>,
|
||||
}
|
||||
|
||||
impl IOHIDDeviceMatcher {
|
||||
pub fn new() -> Self {
|
||||
let dict = CFDictionary::<CFString, CFNumber>::from_CFType_pairs(&[
|
||||
(
|
||||
CFString::from_static_string("DeviceUsage"),
|
||||
CFNumber::from(i32::from(FIDO_USAGE_U2FHID)),
|
||||
),
|
||||
(
|
||||
CFString::from_static_string("DeviceUsagePage"),
|
||||
CFNumber::from(i32::from(FIDO_USAGE_PAGE)),
|
||||
),
|
||||
]);
|
||||
Self { dict }
|
||||
}
|
||||
}
|
||||
|
||||
#[link(name = "IOKit", kind = "framework")]
|
||||
extern "C" {
|
||||
// CFRunLoop
|
||||
pub fn CFRunLoopObserverCreate(
|
||||
allocator: CFAllocatorRef,
|
||||
activities: CFOptionFlags,
|
||||
repeats: Boolean,
|
||||
order: CFIndex,
|
||||
callout: CFRunLoopObserverCallBack,
|
||||
context: *mut CFRunLoopObserverContext,
|
||||
) -> CFRunLoopObserverRef;
|
||||
|
||||
// IOHIDManager
|
||||
pub fn IOHIDManagerCreate(
|
||||
allocator: CFAllocatorRef,
|
||||
options: IOHIDManagerOptions,
|
||||
) -> IOHIDManagerRef;
|
||||
pub fn IOHIDManagerSetDeviceMatching(manager: IOHIDManagerRef, matching: CFDictionaryRef);
|
||||
pub fn IOHIDManagerRegisterDeviceMatchingCallback(
|
||||
manager: IOHIDManagerRef,
|
||||
callback: IOHIDDeviceCallback,
|
||||
context: *mut c_void,
|
||||
);
|
||||
pub fn IOHIDManagerRegisterDeviceRemovalCallback(
|
||||
manager: IOHIDManagerRef,
|
||||
callback: IOHIDDeviceCallback,
|
||||
context: *mut c_void,
|
||||
);
|
||||
pub fn IOHIDManagerRegisterInputReportCallback(
|
||||
manager: IOHIDManagerRef,
|
||||
callback: IOHIDReportCallback,
|
||||
context: *mut c_void,
|
||||
);
|
||||
pub fn IOHIDManagerOpen(manager: IOHIDManagerRef, options: IOHIDManagerOptions) -> IOReturn;
|
||||
pub fn IOHIDManagerClose(manager: IOHIDManagerRef, options: IOHIDManagerOptions) -> IOReturn;
|
||||
pub fn IOHIDManagerScheduleWithRunLoop(
|
||||
manager: IOHIDManagerRef,
|
||||
runLoop: CFRunLoopRef,
|
||||
runLoopMode: CFStringRef,
|
||||
);
|
||||
|
||||
// IOHIDDevice
|
||||
pub fn IOHIDDeviceSetReport(
|
||||
device: IOHIDDeviceRef,
|
||||
reportType: IOHIDReportType,
|
||||
reportID: CFIndex,
|
||||
report: *const u8,
|
||||
reportLength: CFIndex,
|
||||
) -> IOReturn;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Tests
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
use std::sync::mpsc::{channel, Sender};
|
||||
use std::thread;
|
||||
|
||||
extern "C" fn observe(_: CFRunLoopObserverRef, _: CFRunLoopActivity, context: *mut c_void) {
|
||||
let tx: &Sender<SendableRunLoop> = unsafe { &*(context as *mut _) };
|
||||
|
||||
// Send the current runloop to the receiver to unblock it.
|
||||
let _ = tx.send(SendableRunLoop::new(unsafe { CFRunLoopGetCurrent() }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sendable_runloop() {
|
||||
let (tx, rx) = channel();
|
||||
|
||||
let thread = thread::spawn(move || {
|
||||
// Send the runloop to the owning thread.
|
||||
let context = &tx as *const _ as *mut c_void;
|
||||
let obs = CFRunLoopEntryObserver::new(observe, context);
|
||||
obs.add_to_current_runloop();
|
||||
|
||||
unsafe {
|
||||
// We need some source for the runloop to run.
|
||||
let manager = IOHIDManagerCreate(kCFAllocatorDefault, 0);
|
||||
assert!(!manager.is_null());
|
||||
|
||||
IOHIDManagerScheduleWithRunLoop(
|
||||
manager,
|
||||
CFRunLoopGetCurrent(),
|
||||
kCFRunLoopDefaultMode,
|
||||
);
|
||||
IOHIDManagerSetDeviceMatching(manager, ptr::null_mut());
|
||||
|
||||
let rv = IOHIDManagerOpen(manager, 0);
|
||||
assert_eq!(rv, 0);
|
||||
|
||||
// This will run until `CFRunLoopStop()` is called.
|
||||
CFRunLoopRun();
|
||||
|
||||
let rv = IOHIDManagerClose(manager, 0);
|
||||
assert_eq!(rv, 0);
|
||||
|
||||
CFRelease(manager as *mut c_void);
|
||||
}
|
||||
});
|
||||
|
||||
// Block until we enter the CFRunLoop.
|
||||
let runloop: SendableRunLoop = rx.recv().expect("failed to receive runloop");
|
||||
|
||||
// Stop the runloop.
|
||||
unsafe { CFRunLoopStop(*runloop) };
|
||||
|
||||
// Stop the thread.
|
||||
thread.join().expect("failed to join the thread");
|
||||
|
||||
// Try to stop the runloop again (without crashing).
|
||||
unsafe { CFRunLoopStop(*runloop) };
|
||||
}
|
||||
}
|
9
third_party/rust/authenticator/src/macos/mod.rs
vendored
Normal file
9
third_party/rust/authenticator/src/macos/mod.rs
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
pub mod device;
|
||||
pub mod transaction;
|
||||
|
||||
mod iokit;
|
||||
mod monitor;
|
175
third_party/rust/authenticator/src/macos/monitor.rs
vendored
Normal file
175
third_party/rust/authenticator/src/macos/monitor.rs
vendored
Normal file
@ -0,0 +1,175 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
extern crate libc;
|
||||
extern crate log;
|
||||
|
||||
use core_foundation::base::*;
|
||||
use core_foundation::runloop::*;
|
||||
use platform::iokit::*;
|
||||
use runloop::RunLoop;
|
||||
use std::collections::HashMap;
|
||||
use std::os::raw::c_void;
|
||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||
use std::{io, slice};
|
||||
use util::io_err;
|
||||
|
||||
struct DeviceData {
|
||||
tx: Sender<Vec<u8>>,
|
||||
runloop: RunLoop,
|
||||
}
|
||||
|
||||
pub struct Monitor<F>
|
||||
where
|
||||
F: Fn((IOHIDDeviceRef, Receiver<Vec<u8>>), &Fn() -> bool) + Sync,
|
||||
{
|
||||
manager: IOHIDManagerRef,
|
||||
// Keep alive until the monitor goes away.
|
||||
_matcher: IOHIDDeviceMatcher,
|
||||
map: HashMap<IOHIDDeviceRef, DeviceData>,
|
||||
new_device_cb: F,
|
||||
}
|
||||
|
||||
impl<F> Monitor<F>
|
||||
where
|
||||
F: Fn((IOHIDDeviceRef, Receiver<Vec<u8>>), &Fn() -> bool) + Sync + 'static,
|
||||
{
|
||||
pub fn new(new_device_cb: F) -> Self {
|
||||
let manager = unsafe { IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDManagerOptionNone) };
|
||||
|
||||
// Match FIDO devices only.
|
||||
let _matcher = IOHIDDeviceMatcher::new();
|
||||
unsafe { IOHIDManagerSetDeviceMatching(manager, _matcher.dict.as_concrete_TypeRef()) };
|
||||
|
||||
Self {
|
||||
manager,
|
||||
_matcher,
|
||||
new_device_cb,
|
||||
map: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(&mut self) -> io::Result<()> {
|
||||
let context = self as *mut Self as *mut c_void;
|
||||
|
||||
unsafe {
|
||||
IOHIDManagerRegisterDeviceMatchingCallback(
|
||||
self.manager,
|
||||
Monitor::<F>::on_device_matching,
|
||||
context,
|
||||
);
|
||||
IOHIDManagerRegisterDeviceRemovalCallback(
|
||||
self.manager,
|
||||
Monitor::<F>::on_device_removal,
|
||||
context,
|
||||
);
|
||||
IOHIDManagerRegisterInputReportCallback(
|
||||
self.manager,
|
||||
Monitor::<F>::on_input_report,
|
||||
context,
|
||||
);
|
||||
|
||||
IOHIDManagerScheduleWithRunLoop(
|
||||
self.manager,
|
||||
CFRunLoopGetCurrent(),
|
||||
kCFRunLoopDefaultMode,
|
||||
);
|
||||
|
||||
let rv = IOHIDManagerOpen(self.manager, kIOHIDManagerOptionNone);
|
||||
if rv == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io_err(&format!("Couldn't open HID Manager, rv={}", rv)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stop(&mut self) {
|
||||
// Remove all devices.
|
||||
while !self.map.is_empty() {
|
||||
let device_ref = *self.map.keys().next().unwrap();
|
||||
self.remove_device(device_ref);
|
||||
}
|
||||
|
||||
// Close the manager and its devices.
|
||||
unsafe { IOHIDManagerClose(self.manager, kIOHIDManagerOptionNone) };
|
||||
}
|
||||
|
||||
fn remove_device(&mut self, device_ref: IOHIDDeviceRef) {
|
||||
if let Some(DeviceData { tx, runloop }) = self.map.remove(&device_ref) {
|
||||
// Dropping `tx` will make Device::read() fail eventually.
|
||||
drop(tx);
|
||||
|
||||
// Wait until the runloop stopped.
|
||||
runloop.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn on_input_report(
|
||||
context: *mut c_void,
|
||||
_: IOReturn,
|
||||
device_ref: IOHIDDeviceRef,
|
||||
_: IOHIDReportType,
|
||||
_: u32,
|
||||
report: *mut u8,
|
||||
report_len: CFIndex,
|
||||
) {
|
||||
let this = unsafe { &mut *(context as *mut Self) };
|
||||
let mut send_failed = false;
|
||||
|
||||
// Ignore the report if we can't find a device for it.
|
||||
if let Some(&DeviceData { ref tx, .. }) = this.map.get(&device_ref) {
|
||||
let data = unsafe { slice::from_raw_parts(report, report_len as usize).to_vec() };
|
||||
send_failed = tx.send(data).is_err();
|
||||
}
|
||||
|
||||
// Remove the device if sending fails.
|
||||
if send_failed {
|
||||
this.remove_device(device_ref);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn on_device_matching(
|
||||
context: *mut c_void,
|
||||
_: IOReturn,
|
||||
_: *mut c_void,
|
||||
device_ref: IOHIDDeviceRef,
|
||||
) {
|
||||
let this = unsafe { &mut *(context as *mut Self) };
|
||||
|
||||
let (tx, rx) = channel();
|
||||
let f = &this.new_device_cb;
|
||||
|
||||
// Create a new per-device runloop.
|
||||
let runloop = RunLoop::new(move |alive| {
|
||||
// Ensure that the runloop is still alive.
|
||||
if alive() {
|
||||
f((device_ref, rx), alive);
|
||||
}
|
||||
});
|
||||
|
||||
if let Ok(runloop) = runloop {
|
||||
this.map.insert(device_ref, DeviceData { tx, runloop });
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn on_device_removal(
|
||||
context: *mut c_void,
|
||||
_: IOReturn,
|
||||
_: *mut c_void,
|
||||
device_ref: IOHIDDeviceRef,
|
||||
) {
|
||||
let this = unsafe { &mut *(context as *mut Self) };
|
||||
this.remove_device(device_ref);
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Drop for Monitor<F>
|
||||
where
|
||||
F: Fn((IOHIDDeviceRef, Receiver<Vec<u8>>), &Fn() -> bool) + Sync,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
unsafe { CFRelease(self.manager as *mut c_void) };
|
||||
}
|
||||
}
|
86
third_party/rust/authenticator/src/macos/transaction.rs
vendored
Normal file
86
third_party/rust/authenticator/src/macos/transaction.rs
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
extern crate libc;
|
||||
|
||||
use core_foundation::runloop::*;
|
||||
use platform::iokit::{CFRunLoopEntryObserver, IOHIDDeviceRef, SendableRunLoop};
|
||||
use platform::monitor::Monitor;
|
||||
use std::os::raw::c_void;
|
||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||
use std::thread;
|
||||
use util::OnceCallback;
|
||||
|
||||
// A transaction will run the given closure in a new thread, thereby using a
|
||||
// separate per-thread state machine for each HID. It will either complete or
|
||||
// fail through user action, timeout, or be cancelled when overridden by a new
|
||||
// transaction.
|
||||
pub struct Transaction {
|
||||
runloop: Option<SendableRunLoop>,
|
||||
thread: Option<thread::JoinHandle<()>>,
|
||||
}
|
||||
|
||||
impl Transaction {
|
||||
pub fn new<F, T>(
|
||||
timeout: u64,
|
||||
callback: OnceCallback<T>,
|
||||
new_device_cb: F,
|
||||
) -> Result<Self, ::Error>
|
||||
where
|
||||
F: Fn((IOHIDDeviceRef, Receiver<Vec<u8>>), &Fn() -> bool) + Sync + Send + 'static,
|
||||
T: 'static,
|
||||
{
|
||||
let (tx, rx) = channel();
|
||||
let timeout = (timeout as f64) / 1000.0;
|
||||
|
||||
let builder = thread::Builder::new();
|
||||
let thread = builder
|
||||
.spawn(move || {
|
||||
// Add a runloop observer that will be notified when we enter the
|
||||
// runloop and tx.send() the current runloop to the owning thread.
|
||||
// We need to ensure the runloop was entered before unblocking
|
||||
// Transaction::new(), so we can always properly cancel.
|
||||
let context = &tx as *const _ as *mut c_void;
|
||||
let obs = CFRunLoopEntryObserver::new(Transaction::observe, context);
|
||||
obs.add_to_current_runloop();
|
||||
|
||||
// Create a new HID device monitor and start polling.
|
||||
let mut monitor = Monitor::new(new_device_cb);
|
||||
try_or!(monitor.start(), |_| callback.call(Err(::Error::Unknown)));
|
||||
|
||||
// This will block until completion, abortion, or timeout.
|
||||
unsafe { CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeout, 0) };
|
||||
|
||||
// Close the monitor and its devices.
|
||||
monitor.stop();
|
||||
|
||||
// Send an error, if the callback wasn't called already.
|
||||
callback.call(Err(::Error::NotAllowed));
|
||||
})
|
||||
.map_err(|_| ::Error::Unknown)?;
|
||||
|
||||
// Block until we enter the CFRunLoop.
|
||||
let runloop = rx.recv().map_err(|_| ::Error::Unknown)?;
|
||||
|
||||
Ok(Self {
|
||||
runloop: Some(runloop),
|
||||
thread: Some(thread),
|
||||
})
|
||||
}
|
||||
|
||||
extern "C" fn observe(_: CFRunLoopObserverRef, _: CFRunLoopActivity, context: *mut c_void) {
|
||||
let tx: &Sender<SendableRunLoop> = unsafe { &*(context as *mut _) };
|
||||
|
||||
// Send the current runloop to the receiver to unblock it.
|
||||
let _ = tx.send(SendableRunLoop::new(unsafe { CFRunLoopGetCurrent() }));
|
||||
}
|
||||
|
||||
pub fn cancel(&mut self) {
|
||||
// This must never be None. This won't block.
|
||||
unsafe { CFRunLoopStop(*self.runloop.take().unwrap()) };
|
||||
|
||||
// This must never be None. Ignore return value.
|
||||
let _ = self.thread.take().unwrap().join();
|
||||
}
|
||||
}
|
188
third_party/rust/authenticator/src/manager.rs
vendored
Normal file
188
third_party/rust/authenticator/src/manager.rs
vendored
Normal file
@ -0,0 +1,188 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::io;
|
||||
use std::sync::mpsc::{channel, RecvTimeoutError, Sender};
|
||||
use std::time::Duration;
|
||||
|
||||
use consts::PARAMETER_SIZE;
|
||||
use runloop::RunLoop;
|
||||
use statemachine::StateMachine;
|
||||
use util::OnceCallback;
|
||||
|
||||
enum QueueAction {
|
||||
Register {
|
||||
flags: ::RegisterFlags,
|
||||
timeout: u64,
|
||||
challenge: Vec<u8>,
|
||||
application: ::AppId,
|
||||
key_handles: Vec<::KeyHandle>,
|
||||
callback: OnceCallback<::RegisterResult>,
|
||||
},
|
||||
Sign {
|
||||
flags: ::SignFlags,
|
||||
timeout: u64,
|
||||
challenge: Vec<u8>,
|
||||
app_ids: Vec<::AppId>,
|
||||
key_handles: Vec<::KeyHandle>,
|
||||
callback: OnceCallback<::SignResult>,
|
||||
},
|
||||
Cancel,
|
||||
}
|
||||
|
||||
pub struct U2FManager {
|
||||
queue: RunLoop,
|
||||
tx: Sender<QueueAction>,
|
||||
}
|
||||
|
||||
impl U2FManager {
|
||||
pub fn new() -> io::Result<Self> {
|
||||
let (tx, rx) = channel();
|
||||
|
||||
// Start a new work queue thread.
|
||||
let queue = RunLoop::new(move |alive| {
|
||||
let mut sm = StateMachine::new();
|
||||
|
||||
while alive() {
|
||||
match rx.recv_timeout(Duration::from_millis(50)) {
|
||||
Ok(QueueAction::Register {
|
||||
flags,
|
||||
timeout,
|
||||
challenge,
|
||||
application,
|
||||
key_handles,
|
||||
callback,
|
||||
}) => {
|
||||
// This must not block, otherwise we can't cancel.
|
||||
sm.register(
|
||||
flags,
|
||||
timeout,
|
||||
challenge,
|
||||
application,
|
||||
key_handles,
|
||||
callback,
|
||||
);
|
||||
}
|
||||
Ok(QueueAction::Sign {
|
||||
flags,
|
||||
timeout,
|
||||
challenge,
|
||||
app_ids,
|
||||
key_handles,
|
||||
callback,
|
||||
}) => {
|
||||
// This must not block, otherwise we can't cancel.
|
||||
sm.sign(flags, timeout, challenge, app_ids, key_handles, callback);
|
||||
}
|
||||
Ok(QueueAction::Cancel) => {
|
||||
// Cancelling must block so that we don't start a new
|
||||
// polling thread before the old one has shut down.
|
||||
sm.cancel();
|
||||
}
|
||||
Err(RecvTimeoutError::Disconnected) => {
|
||||
break;
|
||||
}
|
||||
_ => { /* continue */ }
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel any ongoing activity.
|
||||
sm.cancel();
|
||||
})?;
|
||||
|
||||
Ok(Self { queue, tx })
|
||||
}
|
||||
|
||||
pub fn register<F>(
|
||||
&self,
|
||||
flags: ::RegisterFlags,
|
||||
timeout: u64,
|
||||
challenge: Vec<u8>,
|
||||
application: ::AppId,
|
||||
key_handles: Vec<::KeyHandle>,
|
||||
callback: F,
|
||||
) -> Result<(), ::Error>
|
||||
where
|
||||
F: FnOnce(Result<::RegisterResult, ::Error>),
|
||||
F: Send + 'static,
|
||||
{
|
||||
if challenge.len() != PARAMETER_SIZE || application.len() != PARAMETER_SIZE {
|
||||
return Err(::Error::Unknown);
|
||||
}
|
||||
|
||||
for key_handle in &key_handles {
|
||||
if key_handle.credential.len() > 256 {
|
||||
return Err(::Error::Unknown);
|
||||
}
|
||||
}
|
||||
|
||||
let callback = OnceCallback::new(callback);
|
||||
let action = QueueAction::Register {
|
||||
flags,
|
||||
timeout,
|
||||
challenge,
|
||||
application,
|
||||
key_handles,
|
||||
callback,
|
||||
};
|
||||
self.tx.send(action).map_err(|_| ::Error::Unknown)
|
||||
}
|
||||
|
||||
pub fn sign<F>(
|
||||
&self,
|
||||
flags: ::SignFlags,
|
||||
timeout: u64,
|
||||
challenge: Vec<u8>,
|
||||
app_ids: Vec<::AppId>,
|
||||
key_handles: Vec<::KeyHandle>,
|
||||
callback: F,
|
||||
) -> Result<(), ::Error>
|
||||
where
|
||||
F: FnOnce(Result<::SignResult, ::Error>),
|
||||
F: Send + 'static,
|
||||
{
|
||||
if challenge.len() != PARAMETER_SIZE {
|
||||
return Err(::Error::Unknown);
|
||||
}
|
||||
|
||||
if app_ids.is_empty() {
|
||||
return Err(::Error::Unknown);
|
||||
}
|
||||
|
||||
for app_id in &app_ids {
|
||||
if app_id.len() != PARAMETER_SIZE {
|
||||
return Err(::Error::Unknown);
|
||||
}
|
||||
}
|
||||
|
||||
for key_handle in &key_handles {
|
||||
if key_handle.credential.len() > 256 {
|
||||
return Err(::Error::Unknown);
|
||||
}
|
||||
}
|
||||
|
||||
let callback = OnceCallback::new(callback);
|
||||
let action = QueueAction::Sign {
|
||||
flags,
|
||||
timeout,
|
||||
challenge,
|
||||
app_ids,
|
||||
key_handles,
|
||||
callback,
|
||||
};
|
||||
self.tx.send(action).map_err(|_| ::Error::Unknown)
|
||||
}
|
||||
|
||||
pub fn cancel(&self) -> Result<(), ::Error> {
|
||||
self.tx
|
||||
.send(QueueAction::Cancel)
|
||||
.map_err(|_| ::Error::Unknown)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for U2FManager {
|
||||
fn drop(&mut self) {
|
||||
self.queue.cancel();
|
||||
}
|
||||
}
|
212
third_party/rust/authenticator/src/statemachine.rs
vendored
Normal file
212
third_party/rust/authenticator/src/statemachine.rs
vendored
Normal file
@ -0,0 +1,212 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use consts::PARAMETER_SIZE;
|
||||
use platform::device::Device;
|
||||
use platform::transaction::Transaction;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use u2fprotocol::{u2f_init_device, u2f_is_keyhandle_valid, u2f_register, u2f_sign};
|
||||
use util::OnceCallback;
|
||||
|
||||
fn is_valid_transport(transports: ::AuthenticatorTransports) -> bool {
|
||||
transports.is_empty() || transports.contains(::AuthenticatorTransports::USB)
|
||||
}
|
||||
|
||||
fn find_valid_key_handles<'a, F>(
|
||||
app_ids: &'a [::AppId],
|
||||
key_handles: &'a [::KeyHandle],
|
||||
mut is_valid: F,
|
||||
) -> (&'a ::AppId, Vec<&'a ::KeyHandle>)
|
||||
where
|
||||
F: FnMut(&Vec<u8>, &::KeyHandle) -> bool,
|
||||
{
|
||||
// Try all given app_ids in order.
|
||||
for app_id in app_ids {
|
||||
// Find all valid key handles for the current app_id.
|
||||
let valid_handles = key_handles
|
||||
.iter()
|
||||
.filter(|key_handle| is_valid(app_id, key_handle))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// If there's at least one, stop.
|
||||
if !valid_handles.is_empty() {
|
||||
return (app_id, valid_handles);
|
||||
}
|
||||
}
|
||||
|
||||
(&app_ids[0], vec![])
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct StateMachine {
|
||||
transaction: Option<Transaction>,
|
||||
}
|
||||
|
||||
impl StateMachine {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn register(
|
||||
&mut self,
|
||||
flags: ::RegisterFlags,
|
||||
timeout: u64,
|
||||
challenge: Vec<u8>,
|
||||
application: ::AppId,
|
||||
key_handles: Vec<::KeyHandle>,
|
||||
callback: OnceCallback<::RegisterResult>,
|
||||
) {
|
||||
// Abort any prior register/sign calls.
|
||||
self.cancel();
|
||||
|
||||
let cbc = callback.clone();
|
||||
|
||||
let transaction = Transaction::new(timeout, cbc.clone(), move |info, alive| {
|
||||
// Create a new device.
|
||||
let dev = &mut match Device::new(info) {
|
||||
Ok(dev) => dev,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// Try initializing it.
|
||||
if !dev.is_u2f() || !u2f_init_device(dev) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We currently support none of the authenticator selection
|
||||
// criteria because we can't ask tokens whether they do support
|
||||
// those features. If flags are set, ignore all tokens for now.
|
||||
//
|
||||
// Technically, this is a ConstraintError because we shouldn't talk
|
||||
// to this authenticator in the first place. But the result is the
|
||||
// same anyway.
|
||||
if !flags.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate the exclude list and see if there are any matches.
|
||||
// If so, we'll keep polling the device anyway to test for user
|
||||
// consent, to be consistent with CTAP2 device behavior.
|
||||
let excluded = key_handles.iter().any(|key_handle| {
|
||||
is_valid_transport(key_handle.transports)
|
||||
&& u2f_is_keyhandle_valid(dev, &challenge, &application, &key_handle.credential)
|
||||
.unwrap_or(false) /* no match on failure */
|
||||
});
|
||||
|
||||
while alive() {
|
||||
if excluded {
|
||||
let blank = vec![0u8; PARAMETER_SIZE];
|
||||
if u2f_register(dev, &blank, &blank).is_ok() {
|
||||
callback.call(Err(::Error::InvalidState));
|
||||
break;
|
||||
}
|
||||
} else if let Ok(bytes) = u2f_register(dev, &challenge, &application) {
|
||||
callback.call(Ok(bytes));
|
||||
break;
|
||||
}
|
||||
|
||||
// Sleep a bit before trying again.
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
});
|
||||
|
||||
self.transaction = Some(try_or!(transaction, |e| cbc.call(Err(e))));
|
||||
}
|
||||
|
||||
pub fn sign(
|
||||
&mut self,
|
||||
flags: ::SignFlags,
|
||||
timeout: u64,
|
||||
challenge: Vec<u8>,
|
||||
app_ids: Vec<::AppId>,
|
||||
key_handles: Vec<::KeyHandle>,
|
||||
callback: OnceCallback<::SignResult>,
|
||||
) {
|
||||
// Abort any prior register/sign calls.
|
||||
self.cancel();
|
||||
|
||||
let cbc = callback.clone();
|
||||
|
||||
let transaction = Transaction::new(timeout, cbc.clone(), move |info, alive| {
|
||||
// Create a new device.
|
||||
let dev = &mut match Device::new(info) {
|
||||
Ok(dev) => dev,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// Try initializing it.
|
||||
if !dev.is_u2f() || !u2f_init_device(dev) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We currently don't support user verification because we can't
|
||||
// ask tokens whether they do support that. If the flag is set,
|
||||
// ignore all tokens for now.
|
||||
//
|
||||
// Technically, this is a ConstraintError because we shouldn't talk
|
||||
// to this authenticator in the first place. But the result is the
|
||||
// same anyway.
|
||||
if !flags.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// For each appId, try all key handles. If there's at least one
|
||||
// valid key handle for an appId, we'll use that appId below.
|
||||
let (app_id, valid_handles) =
|
||||
find_valid_key_handles(&app_ids, &key_handles, |app_id, key_handle| {
|
||||
u2f_is_keyhandle_valid(dev, &challenge, app_id, &key_handle.credential)
|
||||
.unwrap_or(false) /* no match on failure */
|
||||
});
|
||||
|
||||
// Aggregate distinct transports from all given credentials.
|
||||
let transports = key_handles
|
||||
.iter()
|
||||
.fold(::AuthenticatorTransports::empty(), |t, k| t | k.transports);
|
||||
|
||||
// We currently only support USB. If the RP specifies transports
|
||||
// and doesn't include USB it's probably lying.
|
||||
if !is_valid_transport(transports) {
|
||||
return;
|
||||
}
|
||||
|
||||
while alive() {
|
||||
// If the device matches none of the given key handles
|
||||
// then just make it blink with bogus data.
|
||||
if valid_handles.is_empty() {
|
||||
let blank = vec![0u8; PARAMETER_SIZE];
|
||||
if u2f_register(dev, &blank, &blank).is_ok() {
|
||||
callback.call(Err(::Error::InvalidState));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Otherwise, try to sign.
|
||||
for key_handle in &valid_handles {
|
||||
if let Ok(bytes) = u2f_sign(dev, &challenge, app_id, &key_handle.credential)
|
||||
{
|
||||
callback.call(Ok((
|
||||
app_id.clone(),
|
||||
key_handle.credential.clone(),
|
||||
bytes,
|
||||
)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sleep a bit before trying again.
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
});
|
||||
|
||||
self.transaction = Some(try_or!(transaction, |e| cbc.call(Err(e))));
|
||||
}
|
||||
|
||||
// This blocks.
|
||||
pub fn cancel(&mut self) {
|
||||
if let Some(mut transaction) = self.transaction.take() {
|
||||
transaction.cancel();
|
||||
}
|
||||
}
|
||||
}
|
45
third_party/rust/authenticator/src/stub/device.rs
vendored
Normal file
45
third_party/rust/authenticator/src/stub/device.rs
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
use u2ftypes::U2FDevice;
|
||||
|
||||
pub struct Device {}
|
||||
|
||||
impl Device {
|
||||
pub fn new(path: String) -> io::Result<Self> {
|
||||
panic!("not implemented");
|
||||
}
|
||||
|
||||
pub fn is_u2f(&self) -> bool {
|
||||
panic!("not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Device {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
panic!("not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Device {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
panic!("not implemented");
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
panic!("not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
impl U2FDevice for Device {
|
||||
fn get_cid<'a>(&'a self) -> &'a [u8; 4] {
|
||||
panic!("not implemented");
|
||||
}
|
||||
|
||||
fn set_cid(&mut self, cid: [u8; 4]) {
|
||||
panic!("not implemented");
|
||||
}
|
||||
}
|
11
third_party/rust/authenticator/src/stub/mod.rs
vendored
Normal file
11
third_party/rust/authenticator/src/stub/mod.rs
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// No-op module to permit compiling token HID support for Android, where
|
||||
// no results are returned.
|
||||
|
||||
#![allow(unused_variables)]
|
||||
|
||||
pub mod device;
|
||||
pub mod transaction;
|
25
third_party/rust/authenticator/src/stub/transaction.rs
vendored
Normal file
25
third_party/rust/authenticator/src/stub/transaction.rs
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use util::OnceCallback;
|
||||
|
||||
pub struct Transaction {}
|
||||
|
||||
impl Transaction {
|
||||
pub fn new<F, T>(
|
||||
timeout: u64,
|
||||
callback: OnceCallback<T>,
|
||||
new_device_cb: F,
|
||||
) -> Result<Self, ::Error>
|
||||
where
|
||||
F: Fn(String, &Fn() -> bool),
|
||||
{
|
||||
callback.call(Err(::Error::NotSupported));
|
||||
Err(::Error::NotSupported)
|
||||
}
|
||||
|
||||
pub fn cancel(&mut self) {
|
||||
/* No-op. */
|
||||
}
|
||||
}
|
106
third_party/rust/authenticator/src/u2fhid-capi.h
vendored
Normal file
106
third_party/rust/authenticator/src/u2fhid-capi.h
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __U2FHID_CAPI
|
||||
#define __U2FHID_CAPI
|
||||
#include <stdlib.h>
|
||||
#include "nsString.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
const uint8_t U2F_RESBUF_ID_REGISTRATION = 0;
|
||||
const uint8_t U2F_RESBUF_ID_KEYHANDLE = 1;
|
||||
const uint8_t U2F_RESBUF_ID_SIGNATURE = 2;
|
||||
const uint8_t U2F_RESBUF_ID_APPID = 3;
|
||||
|
||||
const uint64_t U2F_FLAG_REQUIRE_RESIDENT_KEY = 1;
|
||||
const uint64_t U2F_FLAG_REQUIRE_USER_VERIFICATION = 2;
|
||||
const uint64_t U2F_FLAG_REQUIRE_PLATFORM_ATTACHMENT = 4;
|
||||
|
||||
const uint8_t U2F_AUTHENTICATOR_TRANSPORT_USB = 1;
|
||||
const uint8_t U2F_AUTHENTICATOR_TRANSPORT_NFC = 2;
|
||||
const uint8_t U2F_AUTHENTICATOR_TRANSPORT_BLE = 4;
|
||||
const uint8_t CTAP_AUTHENTICATOR_TRANSPORT_INTERNAL = 8;
|
||||
|
||||
const uint8_t U2F_ERROR_UKNOWN = 1;
|
||||
const uint8_t U2F_ERROR_NOT_SUPPORTED = 2;
|
||||
const uint8_t U2F_ERROR_INVALID_STATE = 3;
|
||||
const uint8_t U2F_ERROR_CONSTRAINT = 4;
|
||||
const uint8_t U2F_ERROR_NOT_ALLOWED = 5;
|
||||
|
||||
// NOTE: Preconditions
|
||||
// * All rust_u2f_mgr* pointers must refer to pointers which are returned
|
||||
// by rust_u2f_mgr_new, and must be freed with rust_u2f_mgr_free.
|
||||
// * All rust_u2f_khs* pointers must refer to pointers which are returned
|
||||
// by rust_u2f_khs_new, and must be freed with rust_u2f_khs_free.
|
||||
// * All rust_u2f_res* pointers must refer to pointers passed to the
|
||||
// register() and sign() callbacks. They can be null on failure.
|
||||
|
||||
// The `rust_u2f_mgr` opaque type is equivalent to the rust type `U2FManager`
|
||||
struct rust_u2f_manager;
|
||||
|
||||
// The `rust_u2f_app_ids` opaque type is equivalent to the rust type `U2FAppIds`
|
||||
struct rust_u2f_app_ids;
|
||||
|
||||
// The `rust_u2f_key_handles` opaque type is equivalent to the rust type
|
||||
// `U2FKeyHandles`
|
||||
struct rust_u2f_key_handles;
|
||||
|
||||
// The `rust_u2f_res` opaque type is equivalent to the rust type `U2FResult`
|
||||
struct rust_u2f_result;
|
||||
|
||||
// The callback passed to register() and sign().
|
||||
typedef void (*rust_u2f_callback)(uint64_t, rust_u2f_result*);
|
||||
|
||||
/// U2FManager functions.
|
||||
|
||||
rust_u2f_manager* rust_u2f_mgr_new();
|
||||
/* unsafe */ void rust_u2f_mgr_free(rust_u2f_manager* mgr);
|
||||
|
||||
uint64_t rust_u2f_mgr_register(rust_u2f_manager* mgr, uint64_t flags,
|
||||
uint64_t timeout, rust_u2f_callback,
|
||||
const uint8_t* challenge_ptr,
|
||||
size_t challenge_len,
|
||||
const uint8_t* application_ptr,
|
||||
size_t application_len,
|
||||
const rust_u2f_key_handles* khs);
|
||||
|
||||
uint64_t rust_u2f_mgr_sign(rust_u2f_manager* mgr, uint64_t flags,
|
||||
uint64_t timeout, rust_u2f_callback,
|
||||
const uint8_t* challenge_ptr, size_t challenge_len,
|
||||
const rust_u2f_app_ids* app_ids,
|
||||
const rust_u2f_key_handles* khs);
|
||||
|
||||
void rust_u2f_mgr_cancel(rust_u2f_manager* mgr);
|
||||
|
||||
/// U2FAppIds functions.
|
||||
|
||||
rust_u2f_app_ids* rust_u2f_app_ids_new();
|
||||
void rust_u2f_app_ids_add(rust_u2f_app_ids* ids, const uint8_t* id,
|
||||
size_t id_len);
|
||||
/* unsafe */ void rust_u2f_app_ids_free(rust_u2f_app_ids* ids);
|
||||
|
||||
/// U2FKeyHandles functions.
|
||||
|
||||
rust_u2f_key_handles* rust_u2f_khs_new();
|
||||
void rust_u2f_khs_add(rust_u2f_key_handles* khs, const uint8_t* key_handle,
|
||||
size_t key_handle_len, uint8_t transports);
|
||||
/* unsafe */ void rust_u2f_khs_free(rust_u2f_key_handles* khs);
|
||||
|
||||
/// U2FResult functions.
|
||||
|
||||
// Returns 0 for success, or the U2F_ERROR error code >= 1.
|
||||
uint8_t rust_u2f_result_error(const rust_u2f_result* res);
|
||||
|
||||
// Call this before `[..]_copy()` to allocate enough space.
|
||||
bool rust_u2f_resbuf_length(const rust_u2f_result* res, uint8_t bid,
|
||||
size_t* len);
|
||||
bool rust_u2f_resbuf_copy(const rust_u2f_result* res, uint8_t bid,
|
||||
uint8_t* dst);
|
||||
/* unsafe */ void rust_u2f_res_free(rust_u2f_result* res);
|
||||
}
|
||||
|
||||
#endif // __U2FHID_CAPI
|
401
third_party/rust/authenticator/src/u2fprotocol.rs
vendored
Normal file
401
third_party/rust/authenticator/src/u2fprotocol.rs
vendored
Normal file
@ -0,0 +1,401 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(needless_lifetimes))]
|
||||
|
||||
extern crate std;
|
||||
|
||||
use rand::{thread_rng, RngCore};
|
||||
use std::ffi::CString;
|
||||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use consts::*;
|
||||
use u2ftypes::*;
|
||||
use util::io_err;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Device Commands
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub fn u2f_init_device<T>(dev: &mut T) -> bool
|
||||
where
|
||||
T: U2FDevice + Read + Write,
|
||||
{
|
||||
let mut nonce = [0u8; 8];
|
||||
thread_rng().fill_bytes(&mut nonce);
|
||||
|
||||
// Initialize the device and check its version.
|
||||
init_device(dev, &nonce).is_ok() && is_v2_device(dev).unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn u2f_register<T>(dev: &mut T, challenge: &[u8], application: &[u8]) -> io::Result<Vec<u8>>
|
||||
where
|
||||
T: U2FDevice + Read + Write,
|
||||
{
|
||||
if challenge.len() != PARAMETER_SIZE || application.len() != PARAMETER_SIZE {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"Invalid parameter sizes",
|
||||
));
|
||||
}
|
||||
|
||||
let mut register_data = Vec::with_capacity(2 * PARAMETER_SIZE);
|
||||
register_data.extend(challenge);
|
||||
register_data.extend(application);
|
||||
|
||||
let flags = U2F_REQUEST_USER_PRESENCE;
|
||||
let (resp, status) = send_apdu(dev, U2F_REGISTER, flags, ®ister_data)?;
|
||||
status_word_to_result(status, resp)
|
||||
}
|
||||
|
||||
pub fn u2f_sign<T>(
|
||||
dev: &mut T,
|
||||
challenge: &[u8],
|
||||
application: &[u8],
|
||||
key_handle: &[u8],
|
||||
) -> io::Result<Vec<u8>>
|
||||
where
|
||||
T: U2FDevice + Read + Write,
|
||||
{
|
||||
if challenge.len() != PARAMETER_SIZE || application.len() != PARAMETER_SIZE {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"Invalid parameter sizes",
|
||||
));
|
||||
}
|
||||
|
||||
if key_handle.len() > 256 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"Key handle too large",
|
||||
));
|
||||
}
|
||||
|
||||
let mut sign_data = Vec::with_capacity(2 * PARAMETER_SIZE + 1 + key_handle.len());
|
||||
sign_data.extend(challenge);
|
||||
sign_data.extend(application);
|
||||
sign_data.push(key_handle.len() as u8);
|
||||
sign_data.extend(key_handle);
|
||||
|
||||
let flags = U2F_REQUEST_USER_PRESENCE;
|
||||
let (resp, status) = send_apdu(dev, U2F_AUTHENTICATE, flags, &sign_data)?;
|
||||
status_word_to_result(status, resp)
|
||||
}
|
||||
|
||||
pub fn u2f_is_keyhandle_valid<T>(
|
||||
dev: &mut T,
|
||||
challenge: &[u8],
|
||||
application: &[u8],
|
||||
key_handle: &[u8],
|
||||
) -> io::Result<bool>
|
||||
where
|
||||
T: U2FDevice + Read + Write,
|
||||
{
|
||||
if challenge.len() != PARAMETER_SIZE || application.len() != PARAMETER_SIZE {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"Invalid parameter sizes",
|
||||
));
|
||||
}
|
||||
|
||||
if key_handle.len() > 256 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"Key handle too large",
|
||||
));
|
||||
}
|
||||
|
||||
let mut sign_data = Vec::with_capacity(2 * PARAMETER_SIZE + 1 + key_handle.len());
|
||||
sign_data.extend(challenge);
|
||||
sign_data.extend(application);
|
||||
sign_data.push(key_handle.len() as u8);
|
||||
sign_data.extend(key_handle);
|
||||
|
||||
let flags = U2F_CHECK_IS_REGISTERED;
|
||||
let (_, status) = send_apdu(dev, U2F_AUTHENTICATE, flags, &sign_data)?;
|
||||
Ok(status == SW_CONDITIONS_NOT_SATISFIED)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Internal Device Commands
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
fn init_device<T>(dev: &mut T, nonce: &[u8]) -> io::Result<()>
|
||||
where
|
||||
T: U2FDevice + Read + Write,
|
||||
{
|
||||
assert_eq!(nonce.len(), INIT_NONCE_SIZE);
|
||||
let raw = sendrecv(dev, U2FHID_INIT, nonce)?;
|
||||
dev.set_cid(U2FHIDInitResp::read(&raw, nonce)?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_v2_device<T>(dev: &mut T) -> io::Result<bool>
|
||||
where
|
||||
T: U2FDevice + Read + Write,
|
||||
{
|
||||
let (data, status) = send_apdu(dev, U2F_VERSION, 0x00, &[])?;
|
||||
let actual = CString::new(data)?;
|
||||
let expected = CString::new("U2F_V2")?;
|
||||
status_word_to_result(status, actual == expected)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Error Handling
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
fn status_word_to_result<T>(status: [u8; 2], val: T) -> io::Result<T> {
|
||||
use self::io::ErrorKind::{InvalidData, InvalidInput};
|
||||
|
||||
match status {
|
||||
SW_NO_ERROR => Ok(val),
|
||||
SW_WRONG_DATA => Err(io::Error::new(InvalidData, "wrong data")),
|
||||
SW_WRONG_LENGTH => Err(io::Error::new(InvalidInput, "wrong length")),
|
||||
SW_CONDITIONS_NOT_SATISFIED => Err(io_err("conditions not satisfied")),
|
||||
_ => Err(io_err(&format!("failed with status {:?}", status))),
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Device Communication Functions
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub fn sendrecv<T>(dev: &mut T, cmd: u8, send: &[u8]) -> io::Result<Vec<u8>>
|
||||
where
|
||||
T: U2FDevice + Read + Write,
|
||||
{
|
||||
// Send initialization packet.
|
||||
let mut count = U2FHIDInit::write(dev, cmd, send)?;
|
||||
|
||||
// Send continuation packets.
|
||||
let mut sequence = 0u8;
|
||||
while count < send.len() {
|
||||
count += U2FHIDCont::write(dev, sequence, &send[count..])?;
|
||||
sequence += 1;
|
||||
}
|
||||
|
||||
// Now we read. This happens in 2 chunks: The initial packet, which has the
|
||||
// size we expect overall, then continuation packets, which will fill in
|
||||
// data until we have everything.
|
||||
let mut data = U2FHIDInit::read(dev)?;
|
||||
|
||||
let mut sequence = 0u8;
|
||||
while data.len() < data.capacity() {
|
||||
let max = data.capacity() - data.len();
|
||||
data.extend_from_slice(&U2FHIDCont::read(dev, sequence, max)?);
|
||||
sequence += 1;
|
||||
}
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
fn send_apdu<T>(dev: &mut T, cmd: u8, p1: u8, send: &[u8]) -> io::Result<(Vec<u8>, [u8; 2])>
|
||||
where
|
||||
T: U2FDevice + Read + Write,
|
||||
{
|
||||
let apdu = U2FAPDUHeader::serialize(cmd, p1, send)?;
|
||||
let mut data = sendrecv(dev, U2FHID_MSG, &apdu)?;
|
||||
|
||||
if data.len() < 2 {
|
||||
return Err(io_err("unexpected response"));
|
||||
}
|
||||
|
||||
let split_at = data.len() - 2;
|
||||
let status = data.split_off(split_at);
|
||||
Ok((data, [status[0], status[1]]))
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Tests
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rand::{thread_rng, RngCore};
|
||||
|
||||
use super::{init_device, send_apdu, sendrecv, U2FDevice};
|
||||
use consts::{CID_BROADCAST, SW_NO_ERROR, U2FHID_INIT, U2FHID_MSG, U2FHID_PING};
|
||||
|
||||
mod platform {
|
||||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use consts::{CID_BROADCAST, HID_RPT_SIZE};
|
||||
use u2ftypes::U2FDevice;
|
||||
|
||||
pub struct TestDevice {
|
||||
cid: [u8; 4],
|
||||
reads: Vec<[u8; HID_RPT_SIZE]>,
|
||||
writes: Vec<[u8; HID_RPT_SIZE + 1]>,
|
||||
}
|
||||
|
||||
impl TestDevice {
|
||||
pub fn new() -> TestDevice {
|
||||
TestDevice {
|
||||
cid: CID_BROADCAST,
|
||||
reads: vec![],
|
||||
writes: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_write(&mut self, packet: &[u8], fill_value: u8) {
|
||||
// Add one to deal with record index check
|
||||
let mut write = [fill_value; HID_RPT_SIZE + 1];
|
||||
// Make sure we start with a 0, for HID record index
|
||||
write[0] = 0;
|
||||
// Clone packet data in at 1, since front is padded with HID record index
|
||||
write[1..=packet.len()].clone_from_slice(packet);
|
||||
self.writes.push(write);
|
||||
}
|
||||
|
||||
pub fn add_read(&mut self, packet: &[u8], fill_value: u8) {
|
||||
let mut read = [fill_value; HID_RPT_SIZE];
|
||||
read[..packet.len()].clone_from_slice(packet);
|
||||
self.reads.push(read);
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for TestDevice {
|
||||
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
|
||||
// Pop a vector from the expected writes, check for quality
|
||||
// against bytes array.
|
||||
assert!(!self.writes.is_empty(), "Ran out of expected write values!");
|
||||
let check = self.writes.remove(0);
|
||||
assert_eq!(check.len(), bytes.len());
|
||||
assert_eq!(&check[..], bytes);
|
||||
Ok(bytes.len())
|
||||
}
|
||||
|
||||
// nop
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for TestDevice {
|
||||
fn read(&mut self, bytes: &mut [u8]) -> io::Result<usize> {
|
||||
assert!(!self.reads.is_empty(), "Ran out of read values!");
|
||||
let check = self.reads.remove(0);
|
||||
assert_eq!(check.len(), bytes.len());
|
||||
bytes.clone_from_slice(&check[..]);
|
||||
Ok(check.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TestDevice {
|
||||
fn drop(&mut self) {
|
||||
assert!(self.reads.is_empty());
|
||||
assert!(self.writes.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
impl U2FDevice for TestDevice {
|
||||
fn get_cid<'a>(&'a self) -> &'a [u8; 4] {
|
||||
&self.cid
|
||||
}
|
||||
|
||||
fn set_cid(&mut self, cid: [u8; 4]) {
|
||||
self.cid = cid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_init_device() {
|
||||
let mut device = platform::TestDevice::new();
|
||||
let nonce = vec![0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01];
|
||||
|
||||
// channel id
|
||||
let mut cid = [0u8; 4];
|
||||
thread_rng().fill_bytes(&mut cid);
|
||||
|
||||
// init packet
|
||||
let mut msg = CID_BROADCAST.to_vec();
|
||||
msg.extend(vec![U2FHID_INIT, 0x00, 0x08]); // cmd + bcnt
|
||||
msg.extend_from_slice(&nonce);
|
||||
device.add_write(&msg, 0);
|
||||
|
||||
// init_resp packet
|
||||
let mut msg = CID_BROADCAST.to_vec();
|
||||
msg.extend(vec![U2FHID_INIT, 0x00, 0x11]); // cmd + bcnt
|
||||
msg.extend_from_slice(&nonce);
|
||||
msg.extend_from_slice(&cid); // new channel id
|
||||
msg.extend(vec![0x02, 0x04, 0x01, 0x08, 0x01]); // versions + flags
|
||||
device.add_read(&msg, 0);
|
||||
|
||||
init_device(&mut device, &nonce).unwrap();
|
||||
assert_eq!(device.get_cid(), &cid);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sendrecv_multiple() {
|
||||
let mut device = platform::TestDevice::new();
|
||||
let cid = [0x01, 0x02, 0x03, 0x04];
|
||||
device.set_cid(cid);
|
||||
|
||||
// init packet
|
||||
let mut msg = cid.to_vec();
|
||||
msg.extend(vec![U2FHID_PING, 0x00, 0xe4]); // cmd + length = 228
|
||||
// write msg, append [1u8; 57], 171 bytes remain
|
||||
device.add_write(&msg, 1);
|
||||
device.add_read(&msg, 1);
|
||||
|
||||
// cont packet
|
||||
let mut msg = cid.to_vec();
|
||||
msg.push(0x00); // seq = 0
|
||||
// write msg, append [1u8; 59], 112 bytes remaining
|
||||
device.add_write(&msg, 1);
|
||||
device.add_read(&msg, 1);
|
||||
|
||||
// cont packet
|
||||
let mut msg = cid.to_vec();
|
||||
msg.push(0x01); // seq = 1
|
||||
// write msg, append [1u8; 59], 53 bytes remaining
|
||||
device.add_write(&msg, 1);
|
||||
device.add_read(&msg, 1);
|
||||
|
||||
// cont packet
|
||||
let mut msg = cid.to_vec();
|
||||
msg.push(0x02); // seq = 2
|
||||
msg.extend_from_slice(&[1u8; 53]);
|
||||
// write msg, append remaining 53 bytes.
|
||||
device.add_write(&msg, 0);
|
||||
device.add_read(&msg, 0);
|
||||
|
||||
let data = [1u8; 228];
|
||||
let d = sendrecv(&mut device, U2FHID_PING, &data).unwrap();
|
||||
assert_eq!(d.len(), 228);
|
||||
assert_eq!(d, &data[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sendapdu() {
|
||||
let cid = [0x01, 0x02, 0x03, 0x04];
|
||||
let data = [0x01, 0x02, 0x03, 0x04, 0x05];
|
||||
let mut device = platform::TestDevice::new();
|
||||
device.set_cid(cid);
|
||||
|
||||
let mut msg = cid.to_vec();
|
||||
// sendrecv header
|
||||
msg.extend(vec![U2FHID_MSG, 0x00, 0x0e]); // len = 14
|
||||
// apdu header
|
||||
msg.extend(vec![0x00, U2FHID_PING, 0xaa, 0x00, 0x00, 0x00, 0x05]);
|
||||
// apdu data
|
||||
msg.extend_from_slice(&data);
|
||||
device.add_write(&msg, 0);
|
||||
|
||||
// Send data back
|
||||
let mut msg = cid.to_vec();
|
||||
msg.extend(vec![U2FHID_MSG, 0x00, 0x07]);
|
||||
msg.extend_from_slice(&data);
|
||||
msg.extend_from_slice(&SW_NO_ERROR);
|
||||
device.add_read(&msg, 0);
|
||||
|
||||
let (result, status) = send_apdu(&mut device, U2FHID_PING, 0xaa, &data).unwrap();
|
||||
assert_eq!(result, &data);
|
||||
assert_eq!(status, SW_NO_ERROR);
|
||||
}
|
||||
}
|
187
third_party/rust/authenticator/src/u2ftypes.rs
vendored
Normal file
187
third_party/rust/authenticator/src/u2ftypes.rs
vendored
Normal file
@ -0,0 +1,187 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::{cmp, io};
|
||||
|
||||
use consts::*;
|
||||
use util::io_err;
|
||||
|
||||
use log;
|
||||
|
||||
fn trace_hex(data: &[u8]) {
|
||||
if log_enabled!(log::Level::Trace) {
|
||||
let parts: Vec<String> = data.iter().map(|byte| format!("{:02x}", byte)).collect();
|
||||
trace!("USB send: {}", parts.join(""));
|
||||
}
|
||||
}
|
||||
|
||||
// Trait for representing U2F HID Devices. Requires getters/setters for the
|
||||
// channel ID, created during device initialization.
|
||||
pub trait U2FDevice {
|
||||
fn get_cid(&self) -> &[u8; 4];
|
||||
fn set_cid(&mut self, cid: [u8; 4]);
|
||||
}
|
||||
|
||||
// Init structure for U2F Communications. Tells the receiver what channel
|
||||
// communication is happening on, what command is running, and how much data to
|
||||
// expect to receive over all.
|
||||
//
|
||||
// Spec at https://fidoalliance.org/specs/fido-u2f-v1.
|
||||
// 0-nfc-bt-amendment-20150514/fido-u2f-hid-protocol.html#message--and-packet-structure
|
||||
pub struct U2FHIDInit {}
|
||||
|
||||
impl U2FHIDInit {
|
||||
pub fn read<T>(dev: &mut T) -> io::Result<Vec<u8>>
|
||||
where
|
||||
T: U2FDevice + io::Read,
|
||||
{
|
||||
let mut frame = [0u8; HID_RPT_SIZE];
|
||||
let count = dev.read(&mut frame)?;
|
||||
|
||||
if count != HID_RPT_SIZE {
|
||||
return Err(io_err("invalid init packet"));
|
||||
}
|
||||
|
||||
if dev.get_cid() != &frame[..4] {
|
||||
return Err(io_err("invalid channel id"));
|
||||
}
|
||||
|
||||
let cap = (frame[5] as usize) << 8 | (frame[6] as usize);
|
||||
let mut data = Vec::with_capacity(cap);
|
||||
|
||||
let len = cmp::min(cap, INIT_DATA_SIZE);
|
||||
data.extend_from_slice(&frame[7..7 + len]);
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub fn write<T>(dev: &mut T, cmd: u8, data: &[u8]) -> io::Result<usize>
|
||||
where
|
||||
T: U2FDevice + io::Write,
|
||||
{
|
||||
if data.len() > 0xffff {
|
||||
return Err(io_err("payload length > 2^16"));
|
||||
}
|
||||
|
||||
let mut frame = [0; HID_RPT_SIZE + 1];
|
||||
frame[1..5].copy_from_slice(dev.get_cid());
|
||||
frame[5] = cmd;
|
||||
frame[6] = (data.len() >> 8) as u8;
|
||||
frame[7] = data.len() as u8;
|
||||
|
||||
let count = cmp::min(data.len(), INIT_DATA_SIZE);
|
||||
frame[8..8 + count].copy_from_slice(&data[..count]);
|
||||
trace_hex(&frame);
|
||||
|
||||
if dev.write(&frame)? != frame.len() {
|
||||
return Err(io_err("device write failed"));
|
||||
}
|
||||
|
||||
Ok(count)
|
||||
}
|
||||
}
|
||||
|
||||
// Continuation structure for U2F Communications. After an Init structure is
|
||||
// sent, continuation structures are used to transmit all extra data that
|
||||
// wouldn't fit in the initial packet. The sequence number increases with every
|
||||
// packet, until all data is received.
|
||||
//
|
||||
// https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-u2f-hid-protocol.
|
||||
// html#message--and-packet-structure
|
||||
pub struct U2FHIDCont {}
|
||||
|
||||
impl U2FHIDCont {
|
||||
pub fn read<T>(dev: &mut T, seq: u8, max: usize) -> io::Result<Vec<u8>>
|
||||
where
|
||||
T: U2FDevice + io::Read,
|
||||
{
|
||||
let mut frame = [0u8; HID_RPT_SIZE];
|
||||
let count = dev.read(&mut frame)?;
|
||||
|
||||
if count != HID_RPT_SIZE {
|
||||
return Err(io_err("invalid cont packet"));
|
||||
}
|
||||
|
||||
if dev.get_cid() != &frame[..4] {
|
||||
return Err(io_err("invalid channel id"));
|
||||
}
|
||||
|
||||
if seq != frame[4] {
|
||||
return Err(io_err("invalid sequence number"));
|
||||
}
|
||||
|
||||
let max = cmp::min(max, CONT_DATA_SIZE);
|
||||
Ok(frame[5..5 + max].to_vec())
|
||||
}
|
||||
|
||||
pub fn write<T>(dev: &mut T, seq: u8, data: &[u8]) -> io::Result<usize>
|
||||
where
|
||||
T: U2FDevice + io::Write,
|
||||
{
|
||||
let mut frame = [0; HID_RPT_SIZE + 1];
|
||||
frame[1..5].copy_from_slice(dev.get_cid());
|
||||
frame[5] = seq;
|
||||
|
||||
let count = cmp::min(data.len(), CONT_DATA_SIZE);
|
||||
frame[6..6 + count].copy_from_slice(&data[..count]);
|
||||
trace_hex(&frame);
|
||||
|
||||
if dev.write(&frame)? != frame.len() {
|
||||
return Err(io_err("device write failed"));
|
||||
}
|
||||
|
||||
Ok(count)
|
||||
}
|
||||
}
|
||||
|
||||
// Reply sent after initialization command. Contains information about U2F USB
|
||||
// Key versioning, as well as the communication channel to be used for all
|
||||
// further requests.
|
||||
//
|
||||
// https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment-20150514/fido-u2f-hid-protocol.
|
||||
// html#u2fhid_init
|
||||
pub struct U2FHIDInitResp {}
|
||||
|
||||
impl U2FHIDInitResp {
|
||||
pub fn read(data: &[u8], nonce: &[u8]) -> io::Result<[u8; 4]> {
|
||||
assert_eq!(nonce.len(), INIT_NONCE_SIZE);
|
||||
|
||||
if data.len() != INIT_NONCE_SIZE + 9 {
|
||||
return Err(io_err("invalid init response"));
|
||||
}
|
||||
|
||||
if nonce != &data[..INIT_NONCE_SIZE] {
|
||||
return Err(io_err("invalid nonce"));
|
||||
}
|
||||
|
||||
let mut cid = [0u8; 4];
|
||||
cid.copy_from_slice(&data[INIT_NONCE_SIZE..INIT_NONCE_SIZE + 4]);
|
||||
Ok(cid)
|
||||
}
|
||||
}
|
||||
|
||||
// https://en.wikipedia.org/wiki/Smart_card_application_protocol_data_unit
|
||||
// https://fidoalliance.org/specs/fido-u2f-v1.
|
||||
// 0-nfc-bt-amendment-20150514/fido-u2f-raw-message-formats.html#u2f-message-framing
|
||||
pub struct U2FAPDUHeader {}
|
||||
|
||||
impl U2FAPDUHeader {
|
||||
pub fn serialize(ins: u8, p1: u8, data: &[u8]) -> io::Result<Vec<u8>> {
|
||||
if data.len() > 0xffff {
|
||||
return Err(io_err("payload length > 2^16"));
|
||||
}
|
||||
|
||||
// Size of header + data + 2 zero bytes for maximum return size.
|
||||
let mut bytes = vec![0u8; U2FAPDUHEADER_SIZE + data.len() + 2];
|
||||
bytes[1] = ins;
|
||||
bytes[2] = p1;
|
||||
// p2 is always 0, at least, for our requirements.
|
||||
// lc[0] should always be 0.
|
||||
bytes[5] = (data.len() >> 8) as u8;
|
||||
bytes[6] = data.len() as u8;
|
||||
bytes[7..7 + data.len()].copy_from_slice(data);
|
||||
|
||||
Ok(bytes)
|
||||
}
|
||||
}
|
94
third_party/rust/authenticator/src/util.rs
vendored
Normal file
94
third_party/rust/authenticator/src/util.rs
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
extern crate libc;
|
||||
|
||||
use std::io;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use boxfnonce::SendBoxFnOnce;
|
||||
|
||||
macro_rules! try_or {
|
||||
($val:expr, $or:expr) => {
|
||||
match $val {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
return $or(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub trait Signed {
|
||||
fn is_negative(&self) -> bool;
|
||||
}
|
||||
|
||||
impl Signed for i32 {
|
||||
fn is_negative(&self) -> bool {
|
||||
*self < (0 as i32)
|
||||
}
|
||||
}
|
||||
|
||||
impl Signed for usize {
|
||||
fn is_negative(&self) -> bool {
|
||||
(*self as isize) < (0 as isize)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux"))]
|
||||
pub fn from_unix_result<T: Signed>(rv: T) -> io::Result<T> {
|
||||
if rv.is_negative() {
|
||||
let errno = unsafe { *libc::__errno_location() };
|
||||
Err(io::Error::from_raw_os_error(errno))
|
||||
} else {
|
||||
Ok(rv)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "freebsd"))]
|
||||
pub fn from_unix_result<T: Signed>(rv: T) -> io::Result<T> {
|
||||
if rv.is_negative() {
|
||||
let errno = unsafe { *libc::__error() };
|
||||
Err(io::Error::from_raw_os_error(errno))
|
||||
} else {
|
||||
Ok(rv)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn io_err(msg: &str) -> io::Error {
|
||||
io::Error::new(io::ErrorKind::Other, msg)
|
||||
}
|
||||
|
||||
pub struct OnceCallback<T> {
|
||||
callback: Arc<Mutex<Option<SendBoxFnOnce<(Result<T, ::Error>,)>>>>,
|
||||
}
|
||||
|
||||
impl<T> OnceCallback<T> {
|
||||
pub fn new<F>(cb: F) -> Self
|
||||
where
|
||||
F: FnOnce(Result<T, ::Error>),
|
||||
F: Send + 'static,
|
||||
{
|
||||
let cb = Some(SendBoxFnOnce::from(cb));
|
||||
Self {
|
||||
callback: Arc::new(Mutex::new(cb)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call(&self, rv: Result<T, ::Error>) {
|
||||
if let Ok(mut cb) = self.callback.lock() {
|
||||
if let Some(cb) = cb.take() {
|
||||
cb.call(rv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for OnceCallback<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
callback: self.callback.clone(),
|
||||
}
|
||||
}
|
||||
}
|
74
third_party/rust/authenticator/src/windows/device.rs
vendored
Normal file
74
third_party/rust/authenticator/src/windows/device.rs
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
use std::os::windows::io::AsRawHandle;
|
||||
|
||||
use super::winapi::DeviceCapabilities;
|
||||
use consts::{CID_BROADCAST, FIDO_USAGE_PAGE, FIDO_USAGE_U2FHID, HID_RPT_SIZE};
|
||||
|
||||
use u2ftypes::U2FDevice;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Device {
|
||||
path: String,
|
||||
file: File,
|
||||
cid: [u8; 4],
|
||||
}
|
||||
|
||||
impl Device {
|
||||
pub fn new(path: String) -> io::Result<Self> {
|
||||
let file = OpenOptions::new().read(true).write(true).open(&path)?;
|
||||
Ok(Self {
|
||||
path: path,
|
||||
file: file,
|
||||
cid: CID_BROADCAST,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_u2f(&self) -> bool {
|
||||
match DeviceCapabilities::new(self.file.as_raw_handle()) {
|
||||
Ok(caps) => caps.usage() == FIDO_USAGE_U2FHID && caps.usage_page() == FIDO_USAGE_PAGE,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Device {
|
||||
fn eq(&self, other: &Device) -> bool {
|
||||
self.path == other.path
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Device {
|
||||
fn read(&mut self, bytes: &mut [u8]) -> io::Result<usize> {
|
||||
// Windows always includes the report ID.
|
||||
let mut input = [0u8; HID_RPT_SIZE + 1];
|
||||
let _ = self.file.read(&mut input)?;
|
||||
bytes.clone_from_slice(&input[1..]);
|
||||
Ok(bytes.len() as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Device {
|
||||
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
|
||||
self.file.write(bytes)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.file.flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl U2FDevice for Device {
|
||||
fn get_cid<'a>(&'a self) -> &'a [u8; 4] {
|
||||
&self.cid
|
||||
}
|
||||
|
||||
fn set_cid(&mut self, cid: [u8; 4]) {
|
||||
self.cid = cid;
|
||||
}
|
||||
}
|
9
third_party/rust/authenticator/src/windows/mod.rs
vendored
Normal file
9
third_party/rust/authenticator/src/windows/mod.rs
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
pub mod device;
|
||||
pub mod transaction;
|
||||
|
||||
mod monitor;
|
||||
mod winapi;
|
91
third_party/rust/authenticator/src/windows/monitor.rs
vendored
Normal file
91
third_party/rust/authenticator/src/windows/monitor.rs
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use platform::winapi::DeviceInfoSet;
|
||||
use runloop::RunLoop;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::io;
|
||||
use std::iter::FromIterator;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
pub struct Monitor<F>
|
||||
where
|
||||
F: Fn(String, &Fn() -> bool) + Sync,
|
||||
{
|
||||
runloops: HashMap<String, RunLoop>,
|
||||
new_device_cb: Arc<F>,
|
||||
}
|
||||
|
||||
impl<F> Monitor<F>
|
||||
where
|
||||
F: Fn(String, &Fn() -> bool) + Send + Sync + 'static,
|
||||
{
|
||||
pub fn new(new_device_cb: F) -> Self {
|
||||
Self {
|
||||
runloops: HashMap::new(),
|
||||
new_device_cb: Arc::new(new_device_cb),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self, alive: &Fn() -> bool) -> io::Result<()> {
|
||||
let mut stored = HashSet::new();
|
||||
|
||||
while alive() {
|
||||
let device_info_set = DeviceInfoSet::new()?;
|
||||
let devices = HashSet::from_iter(device_info_set.devices());
|
||||
|
||||
// Remove devices that are gone.
|
||||
for path in stored.difference(&devices) {
|
||||
self.remove_device(path);
|
||||
}
|
||||
|
||||
// Add devices that were plugged in.
|
||||
for path in devices.difference(&stored) {
|
||||
self.add_device(path);
|
||||
}
|
||||
|
||||
// Remember the new set.
|
||||
stored = devices;
|
||||
|
||||
// Wait a little before looking for devices again.
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
|
||||
// Remove all tracked devices.
|
||||
self.remove_all_devices();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_device(&mut self, path: &String) {
|
||||
let f = self.new_device_cb.clone();
|
||||
let path = path.clone();
|
||||
let key = path.clone();
|
||||
|
||||
let runloop = RunLoop::new(move |alive| {
|
||||
if alive() {
|
||||
f(path, alive);
|
||||
}
|
||||
});
|
||||
|
||||
if let Ok(runloop) = runloop {
|
||||
self.runloops.insert(key, runloop);
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_device(&mut self, path: &String) {
|
||||
if let Some(runloop) = self.runloops.remove(path) {
|
||||
runloop.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_all_devices(&mut self) {
|
||||
while !self.runloops.is_empty() {
|
||||
let path = self.runloops.keys().next().unwrap().clone();
|
||||
self.remove_device(&path);
|
||||
}
|
||||
}
|
||||
}
|
48
third_party/rust/authenticator/src/windows/transaction.rs
vendored
Normal file
48
third_party/rust/authenticator/src/windows/transaction.rs
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use platform::monitor::Monitor;
|
||||
use runloop::RunLoop;
|
||||
use util::OnceCallback;
|
||||
|
||||
pub struct Transaction {
|
||||
// Handle to the thread loop.
|
||||
thread: Option<RunLoop>,
|
||||
}
|
||||
|
||||
impl Transaction {
|
||||
pub fn new<F, T>(
|
||||
timeout: u64,
|
||||
callback: OnceCallback<T>,
|
||||
new_device_cb: F,
|
||||
) -> Result<Self, ::Error>
|
||||
where
|
||||
F: Fn(String, &Fn() -> bool) + Sync + Send + 'static,
|
||||
T: 'static,
|
||||
{
|
||||
let thread = RunLoop::new_with_timeout(
|
||||
move |alive| {
|
||||
// Create a new device monitor.
|
||||
let mut monitor = Monitor::new(new_device_cb);
|
||||
|
||||
// Start polling for new devices.
|
||||
try_or!(monitor.run(alive), |_| callback.call(Err(::Error::Unknown)));
|
||||
|
||||
// Send an error, if the callback wasn't called already.
|
||||
callback.call(Err(::Error::NotAllowed));
|
||||
},
|
||||
timeout,
|
||||
)
|
||||
.map_err(|_| ::Error::Unknown)?;
|
||||
|
||||
Ok(Self {
|
||||
thread: Some(thread),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn cancel(&mut self) {
|
||||
// This must never be None.
|
||||
self.thread.take().unwrap().cancel();
|
||||
}
|
||||
}
|
271
third_party/rust/authenticator/src/windows/winapi.rs
vendored
Normal file
271
third_party/rust/authenticator/src/windows/winapi.rs
vendored
Normal file
@ -0,0 +1,271 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
|
||||
use std::ffi::OsString;
|
||||
use std::os::windows::ffi::OsStringExt;
|
||||
|
||||
use util::io_err;
|
||||
|
||||
extern crate libc;
|
||||
extern crate winapi;
|
||||
|
||||
use platform::winapi::winapi::shared::{guiddef, minwindef, ntdef, windef};
|
||||
use platform::winapi::winapi::shared::{hidclass, hidpi, hidusage};
|
||||
use platform::winapi::winapi::um::{handleapi, setupapi};
|
||||
|
||||
#[link(name = "setupapi")]
|
||||
extern "system" {
|
||||
fn SetupDiGetClassDevsW(
|
||||
ClassGuid: *const guiddef::GUID,
|
||||
Enumerator: ntdef::PCSTR,
|
||||
hwndParent: windef::HWND,
|
||||
flags: minwindef::DWORD,
|
||||
) -> setupapi::HDEVINFO;
|
||||
|
||||
fn SetupDiDestroyDeviceInfoList(DeviceInfoSet: setupapi::HDEVINFO) -> minwindef::BOOL;
|
||||
|
||||
fn SetupDiEnumDeviceInterfaces(
|
||||
DeviceInfoSet: setupapi::HDEVINFO,
|
||||
DeviceInfoData: setupapi::PSP_DEVINFO_DATA,
|
||||
InterfaceClassGuid: *const guiddef::GUID,
|
||||
MemberIndex: minwindef::DWORD,
|
||||
DeviceInterfaceData: setupapi::PSP_DEVICE_INTERFACE_DATA,
|
||||
) -> minwindef::BOOL;
|
||||
|
||||
fn SetupDiGetDeviceInterfaceDetailW(
|
||||
DeviceInfoSet: setupapi::HDEVINFO,
|
||||
DeviceInterfaceData: setupapi::PSP_DEVICE_INTERFACE_DATA,
|
||||
DeviceInterfaceDetailData: setupapi::PSP_DEVICE_INTERFACE_DETAIL_DATA_W,
|
||||
DeviceInterfaceDetailDataSize: minwindef::DWORD,
|
||||
RequiredSize: minwindef::PDWORD,
|
||||
DeviceInfoData: setupapi::PSP_DEVINFO_DATA,
|
||||
) -> minwindef::BOOL;
|
||||
}
|
||||
|
||||
#[link(name = "hid")]
|
||||
extern "system" {
|
||||
fn HidD_GetPreparsedData(
|
||||
HidDeviceObject: ntdef::HANDLE,
|
||||
PreparsedData: *mut hidpi::PHIDP_PREPARSED_DATA,
|
||||
) -> ntdef::BOOLEAN;
|
||||
|
||||
fn HidD_FreePreparsedData(PreparsedData: hidpi::PHIDP_PREPARSED_DATA) -> ntdef::BOOLEAN;
|
||||
|
||||
fn HidP_GetCaps(
|
||||
PreparsedData: hidpi::PHIDP_PREPARSED_DATA,
|
||||
Capabilities: hidpi::PHIDP_CAPS,
|
||||
) -> ntdef::NTSTATUS;
|
||||
}
|
||||
|
||||
macro_rules! offset_of {
|
||||
($ty:ty, $field:ident) => {
|
||||
unsafe { &(*(0 as *const $ty)).$field as *const _ as usize }
|
||||
};
|
||||
}
|
||||
|
||||
fn from_wide_ptr(ptr: *const u16, len: usize) -> String {
|
||||
assert!(!ptr.is_null() && len % 2 == 0);
|
||||
let slice = unsafe { slice::from_raw_parts(ptr, len / 2) };
|
||||
OsString::from_wide(slice).to_string_lossy().into_owned()
|
||||
}
|
||||
|
||||
pub struct DeviceInfoSet {
|
||||
set: setupapi::HDEVINFO,
|
||||
}
|
||||
|
||||
impl DeviceInfoSet {
|
||||
pub fn new() -> io::Result<Self> {
|
||||
let flags = setupapi::DIGCF_PRESENT | setupapi::DIGCF_DEVICEINTERFACE;
|
||||
let set = unsafe {
|
||||
SetupDiGetClassDevsW(
|
||||
&hidclass::GUID_DEVINTERFACE_HID,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
flags,
|
||||
)
|
||||
};
|
||||
if set == handleapi::INVALID_HANDLE_VALUE {
|
||||
return Err(io_err("SetupDiGetClassDevsW failed!"));
|
||||
}
|
||||
|
||||
Ok(Self { set })
|
||||
}
|
||||
|
||||
pub fn get(&self) -> setupapi::HDEVINFO {
|
||||
self.set
|
||||
}
|
||||
|
||||
pub fn devices(&self) -> DeviceInfoSetIter {
|
||||
DeviceInfoSetIter::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DeviceInfoSet {
|
||||
fn drop(&mut self) {
|
||||
let _ = unsafe { SetupDiDestroyDeviceInfoList(self.set) };
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DeviceInfoSetIter<'a> {
|
||||
set: &'a DeviceInfoSet,
|
||||
index: minwindef::DWORD,
|
||||
}
|
||||
|
||||
impl<'a> DeviceInfoSetIter<'a> {
|
||||
fn new(set: &'a DeviceInfoSet) -> Self {
|
||||
Self { set, index: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for DeviceInfoSetIter<'a> {
|
||||
type Item = String;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut device_interface_data =
|
||||
unsafe { mem::uninitialized::<setupapi::SP_DEVICE_INTERFACE_DATA>() };
|
||||
device_interface_data.cbSize =
|
||||
mem::size_of::<setupapi::SP_DEVICE_INTERFACE_DATA>() as minwindef::UINT;
|
||||
|
||||
let rv = unsafe {
|
||||
SetupDiEnumDeviceInterfaces(
|
||||
self.set.get(),
|
||||
ptr::null_mut(),
|
||||
&hidclass::GUID_DEVINTERFACE_HID,
|
||||
self.index,
|
||||
&mut device_interface_data,
|
||||
)
|
||||
};
|
||||
if rv == 0 {
|
||||
return None; // We're past the last device index.
|
||||
}
|
||||
|
||||
// Determine the size required to hold a detail struct.
|
||||
let mut required_size = 0;
|
||||
unsafe {
|
||||
SetupDiGetDeviceInterfaceDetailW(
|
||||
self.set.get(),
|
||||
&mut device_interface_data,
|
||||
ptr::null_mut(),
|
||||
required_size,
|
||||
&mut required_size,
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
if required_size == 0 {
|
||||
return None; // An error occurred.
|
||||
}
|
||||
|
||||
let detail = DeviceInterfaceDetailData::new(required_size as usize);
|
||||
if detail.is_none() {
|
||||
return None; // malloc() failed.
|
||||
}
|
||||
|
||||
let detail = detail.unwrap();
|
||||
let rv = unsafe {
|
||||
SetupDiGetDeviceInterfaceDetailW(
|
||||
self.set.get(),
|
||||
&mut device_interface_data,
|
||||
detail.get(),
|
||||
required_size,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
if rv == 0 {
|
||||
return None; // An error occurred.
|
||||
}
|
||||
|
||||
self.index += 1;
|
||||
Some(detail.path())
|
||||
}
|
||||
}
|
||||
|
||||
struct DeviceInterfaceDetailData {
|
||||
data: setupapi::PSP_DEVICE_INTERFACE_DETAIL_DATA_W,
|
||||
path_len: usize,
|
||||
}
|
||||
|
||||
impl DeviceInterfaceDetailData {
|
||||
fn new(size: usize) -> Option<Self> {
|
||||
let mut cb_size = mem::size_of::<setupapi::SP_DEVICE_INTERFACE_DETAIL_DATA_W>();
|
||||
if cfg!(target_pointer_width = "32") {
|
||||
cb_size = 4 + 2; // 4-byte uint + default TCHAR size. size_of is inaccurate.
|
||||
}
|
||||
|
||||
if size < cb_size {
|
||||
warn!("DeviceInterfaceDetailData is too small. {}", size);
|
||||
return None;
|
||||
}
|
||||
|
||||
let data = unsafe { libc::malloc(size) as setupapi::PSP_DEVICE_INTERFACE_DETAIL_DATA_W };
|
||||
if data.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Set total size of the structure.
|
||||
unsafe { (*data).cbSize = cb_size as minwindef::UINT };
|
||||
|
||||
// Compute offset of `SP_DEVICE_INTERFACE_DETAIL_DATA_W.DevicePath`.
|
||||
let offset = offset_of!(setupapi::SP_DEVICE_INTERFACE_DETAIL_DATA_W, DevicePath);
|
||||
|
||||
Some(Self {
|
||||
data,
|
||||
path_len: size - offset,
|
||||
})
|
||||
}
|
||||
|
||||
fn get(&self) -> setupapi::PSP_DEVICE_INTERFACE_DETAIL_DATA_W {
|
||||
self.data
|
||||
}
|
||||
|
||||
fn path(&self) -> String {
|
||||
unsafe { from_wide_ptr((*self.data).DevicePath.as_ptr(), self.path_len - 2) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DeviceInterfaceDetailData {
|
||||
fn drop(&mut self) {
|
||||
unsafe { libc::free(self.data as *mut libc::c_void) };
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DeviceCapabilities {
|
||||
caps: hidpi::HIDP_CAPS,
|
||||
}
|
||||
|
||||
impl DeviceCapabilities {
|
||||
pub fn new(handle: ntdef::HANDLE) -> io::Result<Self> {
|
||||
let mut preparsed_data = ptr::null_mut();
|
||||
let rv = unsafe { HidD_GetPreparsedData(handle, &mut preparsed_data) };
|
||||
if rv == 0 || preparsed_data.is_null() {
|
||||
return Err(io_err("HidD_GetPreparsedData failed!"));
|
||||
}
|
||||
|
||||
let mut caps: hidpi::HIDP_CAPS = unsafe { mem::uninitialized() };
|
||||
|
||||
unsafe {
|
||||
let rv = HidP_GetCaps(preparsed_data, &mut caps);
|
||||
HidD_FreePreparsedData(preparsed_data);
|
||||
|
||||
if rv != hidpi::HIDP_STATUS_SUCCESS {
|
||||
return Err(io_err("HidP_GetCaps failed!"));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self { caps })
|
||||
}
|
||||
|
||||
pub fn usage(&self) -> hidusage::USAGE {
|
||||
self.caps.Usage
|
||||
}
|
||||
|
||||
pub fn usage_page(&self) -> hidusage::USAGE {
|
||||
self.caps.UsagePage
|
||||
}
|
||||
}
|
@ -1 +1 @@
|
||||
{"files":{"CODE_OF_CONDUCT.md":"62f073941a34756006851cef8d5d081f6332a986063e87deafeb621f3f6ff554","Cargo.toml":"82c3a9280afb5f4ac916fbca17ca4913b9f66f90c28eb48be1b66f5efe363e87","README.md":"27a78f684d46d92d64bdda18e8b55f132960836347a654d4024ede000e980bec","UNLICENSE":"7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c","examples/main.rs":"734a87846b61d09d2aaca444c69dc61765f66df34602f3a4acf1255f95404226","src/data.rs":"677b52a636deb1f0ffc623dbdc5ed7acd78d915117825ced7031c6fa6f0c861e","src/lib.rs":"5e1539f2e197214f90cdcb5835c9b082773b0cd18f6c18e03067ebe04f18a6b7","src/parser.rs":"8459eed676eb9190f592b159d099d542bbcc447d6fb19b46f7a61c60a1ef8a8e","src/result.rs":"4088fc879652c115a13d8a6e6a71fab8571a7982e740af6a91115f3a82aef236"},"package":"e7c9ac481c38baf400d3b732e4a06850dfaa491d1b6379a249d9d40d14c2434c"}
|
||||
{"files":{"CODE_OF_CONDUCT.md":"62f073941a34756006851cef8d5d081f6332a986063e87deafeb621f3f6ff554","Cargo.toml":"5b63e110cc58510911b48f6ccfc5748d56d1b88bb9eefc1fb1806fa14213b030","README.md":"88550411d0440cc5931cff6e4265ec676291b2b18989f4fb5779c493e40392ae","UNLICENSE":"7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c","examples/main.rs":"734a87846b61d09d2aaca444c69dc61765f66df34602f3a4acf1255f95404226","src/data.rs":"677b52a636deb1f0ffc623dbdc5ed7acd78d915117825ced7031c6fa6f0c861e","src/lib.rs":"b485b0fcc73deaad31c9b6e5762dd2d325ecebca100a127432596afeb9058d0e","src/parser.rs":"4d6fedcdc976e9d305e737ef23c643af3754cb3bb3e36ad3330fd8eee4923d69","src/result.rs":"4088fc879652c115a13d8a6e6a71fab8571a7982e740af6a91115f3a82aef236"},"package":"0d009f166c0d9e9f9909dc751630b3a6411ab7f85a153d32d01deb364ffe52a7"}
|
4
third_party/rust/devd-rs/Cargo.toml
vendored
4
third_party/rust/devd-rs/Cargo.toml
vendored
@ -12,7 +12,7 @@
|
||||
|
||||
[package]
|
||||
name = "devd-rs"
|
||||
version = "0.2.1"
|
||||
version = "0.3.0"
|
||||
authors = ["Greg V <greg@unrelenting.technology>"]
|
||||
description = "An interface to devd, the device hotplug daemon on FreeBSD and DragonFlyBSD"
|
||||
homepage = "https://github.com/myfreeweb/devd-rs"
|
||||
@ -25,4 +25,4 @@ repository = "https://github.com/myfreeweb/devd-rs"
|
||||
version = "0"
|
||||
|
||||
[dependencies.nom]
|
||||
version = "3.2"
|
||||
version = "4"
|
||||
|
4
third_party/rust/devd-rs/README.md
vendored
4
third_party/rust/devd-rs/README.md
vendored
@ -1,4 +1,4 @@
|
||||
[![crates.io](https://img.shields.io/crates/v/systemstat.svg)](https://crates.io/crates/systemstat)
|
||||
[![crates.io](https://img.shields.io/crates/v/devd-rs.svg)](https://crates.io/crates/devd-rs)
|
||||
[![unlicense](https://img.shields.io/badge/un-license-green.svg?style=flat)](http://unlicense.org)
|
||||
|
||||
# devd-rs
|
||||
@ -23,3 +23,5 @@ By participating in this project you agree to follow the [Contributor Code of Co
|
||||
|
||||
This is free and unencumbered software released into the public domain.
|
||||
For more information, please refer to the `UNLICENSE` file or [unlicense.org](http://unlicense.org).
|
||||
|
||||
It is also available under the MIT License.
|
||||
|
5
third_party/rust/devd-rs/src/lib.rs
vendored
5
third_party/rust/devd-rs/src/lib.rs
vendored
@ -18,12 +18,13 @@ use io::{BufRead, BufReader};
|
||||
|
||||
pub use result::*;
|
||||
pub use data::*;
|
||||
use nom::types::CompleteStr;
|
||||
|
||||
const SOCKET_PATH: &'static str = "/var/run/devd.seqpacket.pipe";
|
||||
|
||||
pub fn parse_devd_event(e: String) -> Result<Event> {
|
||||
match parser::event(e.as_bytes()) {
|
||||
parser::IResult::Done(_, x) => Ok(x),
|
||||
match parser::event(CompleteStr(e.as_str())) {
|
||||
Ok((_, x)) => Ok(x),
|
||||
_ => Err(Error::Parse),
|
||||
}
|
||||
}
|
||||
|
171
third_party/rust/devd-rs/src/parser.rs
vendored
171
third_party/rust/devd-rs/src/parser.rs
vendored
@ -1,88 +1,88 @@
|
||||
use std::str;
|
||||
use nom::{alphanumeric, multispace};
|
||||
pub use nom::IResult;
|
||||
use nom::types::CompleteStr;
|
||||
use data::*;
|
||||
|
||||
named!(key<&str>, map_res!(alphanumeric, str::from_utf8));
|
||||
|
||||
named!(
|
||||
val<&str>,
|
||||
alt!(delimited!(char!('"'), map_res!(take_while!(call!(|c| c != '"' as u8)), str::from_utf8), char!('"')) | map_res!(take_while!(call!(|c| c != '\n' as u8 && c != ' ' as u8)), str::from_utf8))
|
||||
val<CompleteStr, CompleteStr>,
|
||||
alt!(
|
||||
delimited!(char!('"'), take_while!(call!(|c| c != '"')), char!('"'))
|
||||
|
|
||||
take_while!(call!(|c| c != '\n' && c != ' '))
|
||||
)
|
||||
);
|
||||
|
||||
named!(keyval <&[u8], (String, String)>,
|
||||
named!(keyval <CompleteStr, (CompleteStr, CompleteStr)>,
|
||||
do_parse!(
|
||||
key: key
|
||||
key: alphanumeric
|
||||
>> char!('=')
|
||||
>> val: val
|
||||
>> (key.to_owned(), val.to_owned())
|
||||
>> (key, val)
|
||||
)
|
||||
);
|
||||
|
||||
named!(keyvals <&[u8], BTreeMap<String, String> >,
|
||||
named!(keyvals <CompleteStr, BTreeMap<String, String> >,
|
||||
map!(
|
||||
many0!(terminated!(keyval, opt!(multispace))),
|
||||
|vec: Vec<_>| vec.into_iter().collect()
|
||||
|vec: Vec<_>| vec.into_iter().map(|(k, v)| (k.to_string(), v.to_string())).collect()
|
||||
)
|
||||
);
|
||||
|
||||
named!(pub event <&[u8], Event>,
|
||||
alt!(
|
||||
do_parse!(
|
||||
tag!("!") >>
|
||||
tag!("system=") >>
|
||||
sys: val >>
|
||||
multispace >>
|
||||
tag!("subsystem=") >>
|
||||
subsys: val >>
|
||||
multispace >>
|
||||
tag!("type=") >>
|
||||
kind: val >>
|
||||
multispace >>
|
||||
data: keyvals >>
|
||||
(Event::Notify { system: sys.to_owned(), subsystem: subsys.to_owned(), kind: kind.to_owned(), data: data })
|
||||
) |
|
||||
do_parse!(
|
||||
tag!("+") >>
|
||||
dev: key >>
|
||||
multispace >>
|
||||
tag!("at") >>
|
||||
multispace >>
|
||||
parent: keyvals >>
|
||||
tag!("on") >>
|
||||
multispace >>
|
||||
loc: val >>
|
||||
(Event::Attach { dev: dev.to_owned(), parent: parent, location: loc.to_owned() })
|
||||
) |
|
||||
do_parse!(
|
||||
tag!("-") >>
|
||||
dev: key >>
|
||||
multispace >>
|
||||
tag!("at") >>
|
||||
multispace >>
|
||||
parent: keyvals >>
|
||||
tag!("on") >>
|
||||
multispace >>
|
||||
loc: val >>
|
||||
(Event::Detach { dev: dev.to_owned(), parent: parent, location: loc.to_owned() })
|
||||
) |
|
||||
do_parse!(
|
||||
tag!("?") >>
|
||||
multispace >>
|
||||
tag!("at") >>
|
||||
multispace >>
|
||||
parent: keyvals >>
|
||||
tag!("on") >>
|
||||
multispace >>
|
||||
loc: val >>
|
||||
(Event::Nomatch { parent: parent, location: loc.to_owned() })
|
||||
named!(pub event <CompleteStr, Event>,
|
||||
alt!(
|
||||
do_parse!(
|
||||
tag!("!") >>
|
||||
tag!("system=") >>
|
||||
sys: val >>
|
||||
multispace >>
|
||||
tag!("subsystem=") >>
|
||||
subsys: val >>
|
||||
multispace >>
|
||||
tag!("type=") >>
|
||||
kind: val >>
|
||||
multispace >>
|
||||
data: keyvals >>
|
||||
(Event::Notify { system: sys.to_string(), subsystem: subsys.to_string(), kind: kind.to_string(), data: data })
|
||||
)
|
||||
|
|
||||
do_parse!(
|
||||
tag!("+") >>
|
||||
dev: alphanumeric >>
|
||||
multispace >>
|
||||
tag!("at") >>
|
||||
multispace >>
|
||||
parent: keyvals >>
|
||||
tag!("on") >>
|
||||
multispace >>
|
||||
loc: val >>
|
||||
(Event::Attach { dev: dev.to_string(), parent: parent, location: loc.to_string() })
|
||||
)
|
||||
|
|
||||
do_parse!(
|
||||
tag!("-") >>
|
||||
dev: alphanumeric >>
|
||||
multispace >>
|
||||
tag!("at") >>
|
||||
multispace >>
|
||||
parent: keyvals >>
|
||||
tag!("on") >>
|
||||
multispace >>
|
||||
loc: val >>
|
||||
(Event::Detach { dev: dev.to_string(), parent: parent, location: loc.to_string() })
|
||||
)
|
||||
|
|
||||
do_parse!(
|
||||
tag!("?") >>
|
||||
multispace >>
|
||||
tag!("at") >>
|
||||
multispace >>
|
||||
parent: keyvals >>
|
||||
tag!("on") >>
|
||||
multispace >>
|
||||
loc: val >>
|
||||
(Event::Nomatch { parent: parent, location: loc.to_string() })
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
)
|
||||
);
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
@ -90,8 +90,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_notify() {
|
||||
let txt = b"!system=USB subsystem=INTERFACE type=ATTACH ugen=ugen0.2 vendor=0x1050 sernum=\"\" mode=host\n";
|
||||
let res = event(txt);
|
||||
let txt = "!system=USB subsystem=INTERFACE type=ATTACH ugen=ugen0.2 vendor=0x1050 sernum=\"\" mode=host\n";
|
||||
let res = event(CompleteStr(txt));
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert("ugen".to_owned(), "ugen0.2".to_owned());
|
||||
data.insert("vendor".to_owned(), "0x1050".to_owned());
|
||||
@ -99,65 +99,66 @@ mod tests {
|
||||
data.insert("mode".to_owned(), "host".to_owned());
|
||||
assert_eq!(
|
||||
res,
|
||||
IResult::Done(
|
||||
&b""[..],
|
||||
Ok((
|
||||
CompleteStr(""),
|
||||
Event::Notify {
|
||||
system: "USB".to_owned(),
|
||||
subsystem: "INTERFACE".to_owned(),
|
||||
kind: "ATTACH".to_owned(),
|
||||
data: data,
|
||||
}
|
||||
)
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_attach() {
|
||||
let txt = b"+uhid1 at bus=0 sernum=\"\" on uhub1";
|
||||
let res = event(txt);
|
||||
let txt = "+uhid1 at bus=0 sernum=\"\" on uhub1";
|
||||
let res = event(CompleteStr(txt));
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert("bus".to_owned(), "0".to_owned());
|
||||
data.insert("sernum".to_owned(), "".to_owned());
|
||||
assert_eq!(
|
||||
res,
|
||||
IResult::Done(
|
||||
&b""[..],
|
||||
Ok((
|
||||
CompleteStr(""),
|
||||
Event::Attach {
|
||||
dev: "uhid1".to_owned(),
|
||||
parent: data,
|
||||
location: "uhub1".to_owned(),
|
||||
}
|
||||
)
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_detach() {
|
||||
let txt = b"-uhid1 at on uhub1";
|
||||
let res = event(txt);
|
||||
let txt = "-uhid1 at on uhub1";
|
||||
let res = event(CompleteStr(txt));
|
||||
let data = BTreeMap::new();
|
||||
assert_eq!(
|
||||
res,
|
||||
IResult::Done(
|
||||
&b""[..],
|
||||
Ok((
|
||||
CompleteStr(""),
|
||||
Event::Detach {
|
||||
dev: "uhid1".to_owned(),
|
||||
parent: data.to_owned(),
|
||||
location: "uhub1".to_owned(),
|
||||
}
|
||||
)
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nomatch() {
|
||||
let txt = b"? at bus=0 on uhub1";
|
||||
let res = event(txt);
|
||||
let txt = "? at bus=0 on uhub1";
|
||||
let res = event(CompleteStr(txt));
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert("bus".to_owned(), "0".to_owned());
|
||||
|
||||
assert_eq!(
|
||||
res,
|
||||
IResult::Done(&b""[..], Event::Nomatch { parent: data, location: "uhub1".to_owned() })
|
||||
Ok((CompleteStr(""), Event::Nomatch { parent: data, location: "uhub1".to_owned() }))
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
{"files":{"CHANGELOG.md":"0ca452f3de1955e58ac7e6814077b430c0ee1dbf7e64c5348b6d7b0b997ffb27","Cargo.toml":"69edf4fdee4c17415625f77366f55cf6b3fd7410222c392a2b4bc262bf2e2bc9","LICENSE":"568d0ae12e18bf9dda98fcd838732321852abdb557c24900d474e71f8fd29f4e","src/bits.rs":"8cd8786191145b5365d381ffdf3aedf69002b6a8d40520c7b752c3e6adb83cc9","src/branch.rs":"e298f1d370a25e0a85f34186d38427f5805e745808f98586c2e8c51581e40480","src/bytes.rs":"bf0fe97e34f47d1f5d4ef93d3a00bd7553ae87ea248162cbce8872ea64c44b36","src/character.rs":"3891258d893c09833f6577dee5b9c7af3e12dc2e60458df542a8fcfb8516e068","src/internal.rs":"7be274fd578166404e0b8236417efeaa8a6cda4a8b10ab637083b1d6cba70f1c","src/lib.rs":"14300d7984ea66e052923e0db31b6c99840e5e234cc76042ce94645d2779c7ea","src/macros.rs":"58cde368c072e219ac19a83b7cb6eb9942d57030b2f74426110e8d91e7968145","src/methods.rs":"e6438ac91baec05fb898765879722d55bb54d9e96e29ab823ed34fe571802063","src/multi.rs":"318b4a345b185252515e1373e3fb1302d74ba3a0c65d44c55b4da39bf74a4c29","src/nom.rs":"48acec4a5dd92222823272e38b63e0decf33a31bdd3e97d22ed5d02ae4c6696c","src/regexp.rs":"8a780a8d328b31012a083ca763b8326e7126320a9071ea011629ddb9d82d178a","src/sequence.rs":"20055b97349f135fae182ba3755261439784b3b83572938ed2d9e4eda3c04758","src/simple_errors.rs":"0a37b042eba1c41da6d18b9249f3b7360f1732a5ed43150c39f36877b40594df","src/str.rs":"4cc81376b1d8c48709d73af357141554bbabb4e0546c1bb4606cfdd2ad8082cb","src/stream.rs":"f80895c621aae949a655576fc1082b65235d3e3af5d6a1cafcc9c0d2398264c9","src/traits.rs":"15db8ac6d5f698d9f55d23fbd025dd10f0e461533b34166e6234bf00e443f42e","src/util.rs":"5c8af3f73dff0efe3bcff59ff0c9cdbfeeecc4bafed14763c229a37bf26f7c99","src/verbose_errors.rs":"f640709d9bd8ffffb8baebab8252bc3d55e247a4c4ee814007b7fbda29856233","src/whitespace.rs":"0117cc62c288acd3ba53903fc14fd650934039919039ba5f83195e0435e12535","tests/arithmetic.rs":"aac143de5c80179f9cb71bf3c79268aa892876622b6f08d5e360ab9308f21341","tests/arithmetic_ast.rs":"bb8995cf726ac382e87dd9665f168cba0d25c8cc040372321027d23499ddea99","tests/blockbuf-arithmetic.rs":"485605360f68fc301390eff084657c27cb30347b8eaaed6d92a5830767d50ce6","tests/cross_function_backtracking.rs":"ea847fa762954e1ff8d8e4fbd130e453d57f41132735e4745286a2fbaf10dd6e","tests/float.rs":"7e380464cd8c2c315eaa2bcd3bb7407a4ba558ee5d6adde3ec72492c7987f784","tests/ini.rs":"c3b2b3ccd6b854c36dc89afc8d68877ff7f5cb8b2cb9d8f138e143deb8ee6ddc","tests/ini_str.rs":"7c5db358330be22e1c64900a8182f26881f7e5d38f1c78d827c60de0af68612f","tests/issues.rs":"d9af6f28be33a70732bec51213a4811910c656a1c38c4ffb5b3202efc188b4b6","tests/json.rs":"5d2c2f3f6ebb9f9c188f5b0cfad723a421ce161caceb3d8517d02fc6546fece6","tests/mp4.rs":"0e5f248dc9e27182ff82b3153481df529600bc4418b84f1f0554e1ada6cc3e08","tests/multiline.rs":"5165e95ba471f77dc0e614a716828d71bbcebcd42fe0fb882b7da1d9633222af","tests/named_args.rs":"3954a031e17e55e12e2bbb107c36a29e2204969ee2e4afb9cbd604decfe3f81a","tests/omnom.rs":"ff749d621b51df8aa3db289b1b626c706190fa15e29061dd5653de83635976d4","tests/overflow.rs":"aec98fc65bf102ae934625aa70e2b48cfcce70f4af38d6520716d79df3e95335","tests/reborrow_fold.rs":"8f140330dd0bfb3bbdab8286b4be6d6f698cadb7b064fac586dcca97e3d397fe","tests/test1.rs":"3e0c187bad91d822ebc113eb5cf30fc6585e53a961728304ac24e05ab2123d10"},"package":"05aec50c70fd288702bcd93284a8444607f3292dbdf2a30de5ea5dcdbe72287b"}
|
825
third_party/rust/nom-3.2.1/CHANGELOG.md
vendored
825
third_party/rust/nom-3.2.1/CHANGELOG.md
vendored
@ -1,825 +0,0 @@
|
||||
# Change Log
|
||||
|
||||
## [Unreleased][unreleased]
|
||||
|
||||
### Changed
|
||||
|
||||
## 3.2.1 - 2017-10-27
|
||||
|
||||
### Thanks
|
||||
|
||||
- @ordian for `alt_complete` fixes
|
||||
- @friedm for documentation fixes
|
||||
- @kali for improving error management
|
||||
|
||||
### Fixed
|
||||
|
||||
- there were cases where `alt_complete` could return `Incomplete`
|
||||
|
||||
### Added
|
||||
|
||||
- an `into_error_kind` method can be used to transform any error to a common value. This helps when the library is included multiple times as dependency with different feature sets
|
||||
|
||||
|
||||
## 3.2.0 - 2017-07-24
|
||||
|
||||
### Thanks
|
||||
|
||||
- @jedireza for documentation fixes
|
||||
- @gmorenz for the `bytes` combinator
|
||||
- @meh for character combinator fixes for UTF-8
|
||||
- @jethrogb for avoiding move issues in `separated_list`
|
||||
|
||||
### Changed
|
||||
|
||||
- new layout for the main page of documentation
|
||||
- `anychar` can now work on any input type
|
||||
- `length_bytes` is now an alias for `length_data`
|
||||
|
||||
### Fixed
|
||||
|
||||
- `one_of`, `none_of` and `char` will now index correctly UTF-8 characters
|
||||
- the `compiler_error` macro is now correctly exported
|
||||
|
||||
|
||||
### Added
|
||||
|
||||
- the `bytes` combinator transforms a bit stream back to a byte slice for child parsers
|
||||
|
||||
## 3.1.0 - 2017-06-16
|
||||
|
||||
### Thanks
|
||||
|
||||
- @sdroege: implementing be_i24 and le_i24
|
||||
- @Hywan: integrating faster substring search using memchr
|
||||
- @nizox: fixing type issues in bit stream parsing
|
||||
- @grissiom: documentation fixes
|
||||
- @doomrobo: implementing separated_list_complete and separated_nonempty_list_complete
|
||||
- @CWood1: fixing memchr integration in no_std
|
||||
- @lu_zero: integrating the compiler_error crate
|
||||
- @dtolnay: helping debug a type inference issue in map
|
||||
|
||||
### Changed
|
||||
|
||||
- memchr is used for substring search if possible
|
||||
- if building on nightly, some common syntax errors will display a specific error message. If building no stable, display the documentation to activate those messages
|
||||
- `count` no longer preallocates its vector
|
||||
|
||||
### Fixed
|
||||
|
||||
- better type inference in alt_complete
|
||||
- `alt` should now work with whitespace parsing
|
||||
- `map` should not make type inference errors anymore
|
||||
|
||||
### Added
|
||||
|
||||
- be_i24 and le_i24, parsing big endian and little endian signed 24 bit integers
|
||||
- `separated_list_complete` and `separated_nonempty_list_complete` will treat incomplete from sub parsers as error
|
||||
|
||||
## 3.0.0 - 2017-05-12
|
||||
|
||||
### Thanks
|
||||
|
||||
- Chris Pick for some `Incomplete` related refactors
|
||||
- @drbgn for documentation fixes
|
||||
- @valarauca for adding `be_u24`
|
||||
- @ithinuel for usability fixes
|
||||
- @evuez for README readability fixes and improvements to `IResult`
|
||||
- @s3bk for allowing non-`Copy` types as input
|
||||
- @keruspe for documentation fixes
|
||||
- @0xd34d10cc for trait fixes on `InputIter`
|
||||
- @sdleffler for lifetime shenanigans on `named_args`
|
||||
- @chengsun for type inference fixes in `alt`
|
||||
- @iBelieve for adding str to no_std
|
||||
- @Hywan for simplifying code in input traits
|
||||
- @azerupi for extensive documentation of `alt` and `alt_complete`
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- `escaped`, `separated_list` and `separated_nonempty_list` can now return `Incomplete` when necessary
|
||||
- `InputIter` does not require `AsChar` on its `Item` type anymore
|
||||
- the `core` feature that was putting nom in `no_std` mode has been removed. There is now a `std` feature, activated by default. If it is not activated, nom is in `no_std`
|
||||
- in `verbose-errors` mode, the error list is now stored in a `Vec` instead of a box based linked list
|
||||
- `chain!` has finally been removed
|
||||
|
||||
### Changed
|
||||
|
||||
- `Endianness` now implements `Debug`, `PartialEq`, `Eq`, `Clone` and `Copy`
|
||||
- custom input types can now be cloned if they're not `Copy`
|
||||
- the infamous 'Cannot infer type for E' error should happen less often now
|
||||
- `str` is now available in `no_std` mode
|
||||
|
||||
### Fixed
|
||||
|
||||
- `FileProducer` will be marked as `Eof` on full buffer
|
||||
- `named_args!` now has lifetimes that cannot conflict with the lifetimes from other arguments
|
||||
|
||||
### Added
|
||||
|
||||
- `be_u24`: big endian 24 bit unsigned integer parsing
|
||||
- `IResult` now has a `unwrap_or` method
|
||||
|
||||
|
||||
## 2.2.1 - 2017-04-03
|
||||
|
||||
### Thanks
|
||||
|
||||
- @Victor-Savu for formatting fixes in the README
|
||||
- @chifflier for detecting and fixing integer overflows
|
||||
- @utkarshkukreti for some performance improvements in benchmarks
|
||||
|
||||
### Changed
|
||||
|
||||
- when calculating how much data is needed in `IResult::Incomplete`, the addition could overflow (it is stored as a usize). This would apparently not result in any security vulnerability on release code
|
||||
|
||||
## 2.2.0 - 2017-03-20
|
||||
|
||||
### Thanks
|
||||
|
||||
- @seppo0010 for fixing `named_args`
|
||||
- @keruspe for implementing or() on `IResult`, adding the option of default cases in `switch!`, adding support for `cargo-travis`
|
||||
- @timlyo for documentation fixes
|
||||
- @JayKickliter for extending `hex_u32`
|
||||
- @1011X for fixing regex integration
|
||||
- @Kerollmops for actually marking `chain!` as deprecated
|
||||
- @joliss for documentation fixes
|
||||
- @utkarshkukreti for tests refactoring and performance improvement
|
||||
- @tmccombs for documentation fixes
|
||||
|
||||
### Added
|
||||
|
||||
- `IResult` gets an `or()` method
|
||||
- `take_until1`, `take_until_and_consume1`, `take_till1!` and `take_till1_s!` require at least 1 character
|
||||
|
||||
### Changed
|
||||
|
||||
- `hex_u32` accepts uppercase digits as well
|
||||
- the character based combinators leverage the input traits
|
||||
- the whitespace parsers now work on &str and other types
|
||||
- `take_while1` returns `Incomplete` on empty input
|
||||
- `switch!` can now take a default case
|
||||
|
||||
### Fixed
|
||||
|
||||
- `named_args!` now imports `IResult` directly
|
||||
- the upgrade to regex 0.2 broke the regex combinators, they work now
|
||||
|
||||
## 2.1.0 - 2017-01-27
|
||||
|
||||
### Thanks
|
||||
|
||||
- @nickbabcock for documentation fixes
|
||||
- @derekdreery for documentation fixes
|
||||
- @DirkyJerky for documentation fixes
|
||||
- @saschagrunert for documentation fixes
|
||||
- @lucab for documentation fixes
|
||||
- @hyone for documentation fixes
|
||||
- @tstorch for factoring `Slice`
|
||||
- @shepmaster for adding crate categories
|
||||
- @antoyo for adding `named_args!`
|
||||
|
||||
### Added
|
||||
|
||||
- `verify!` uses a first parser, then applies a function to check that its result satisfies some conditions
|
||||
- `named_args!` creates a parser function that can accept other arguments along with the input
|
||||
- `parse_to!` will use the `parse` method from `FromStr` to parse a value. It will automatically translate the input to a string if necessary
|
||||
- `float`, `float_s`, `double`, `double_s` can recognize floating point numbers in text
|
||||
|
||||
### Changed
|
||||
|
||||
- `escaped!` will now return `Incomplete` if needed
|
||||
- `permutation!` supports up to 20 child parsers
|
||||
|
||||
## 2.0.1 - 2016-12-10
|
||||
|
||||
Bugfix release
|
||||
|
||||
*Warning*: there is a small breaking change, `add_error!` is renamed to `add_return_error!`. This was planned for the 2.0 release but was forgotten. This is a small change in a feature that not many people use, for a release that is not yet widely in use, so there will be no 3.0 release for that change.
|
||||
|
||||
### Thanks
|
||||
|
||||
- @nickbabcock for catching and fixing the `add_error!` mixup
|
||||
- @lucab for documentation fixes
|
||||
- @jtdowney for noticing that `tag_no_case!` was not working at all for byte slices
|
||||
|
||||
### Fixed
|
||||
|
||||
- `add_error!` has been renamed to `add_return_error!`
|
||||
- the `not!` combinator now accepts functions
|
||||
- `tag_no_case!` is now working as accepted (before, it accepted everything)
|
||||
|
||||
|
||||
## 2.0 - 2016-11-25
|
||||
|
||||
The 2.0 release is one of the biggest yet. It was a good opportunity to clean up some badly named combinators and fix invalid behaviours.
|
||||
|
||||
Since this version introduces a few breaking changes, an [upgrade documentation](https://github.com/Geal/nom/blob/master/doc/upgrading_to_nom_2.md) is available, detailing the steps to fix the most common migration issues. After testing on a set of 30 crates, most of them will build directly, a large part will just need to activate the "verbose-errors" compilation feature. The remaining fixes are documented.
|
||||
|
||||
This version also adds a lot of interesting features, like the permutation combinator or whitespace separated formats support.
|
||||
|
||||
### Thanks
|
||||
|
||||
- @lu-zero for license help
|
||||
- @adamgreig for type inference fixes
|
||||
- @keruspe for documentation and example fixes, for the `IResult => Result` conversion work, making `AsChar`'s method more consistent, and adding `many_till!`
|
||||
- @jdeeny for implementing `Offset` on `&str`
|
||||
- @vickenty for documentation fixes and his refactoring of `length_value!` and `length_bytes!`
|
||||
- @overdrivenpotato for refactoring some combinators
|
||||
- @taralx for documentation fixes
|
||||
- @keeperofdakeys for fixing eol behaviour, writing documentation and adding `named_attr!`
|
||||
- @jturner314 for writing documentation
|
||||
- @bozaro for fixing compilation errors
|
||||
- @uniphil for adding a `crates.io` badge
|
||||
- @badboy for documentation fixes
|
||||
- @jugglerchris for fixing `take_s!`
|
||||
- @AndyShiue for implementing `Error` and `Display` on `ErrorKind` and detecting incorrect UTF-8 string indexing
|
||||
|
||||
### Added
|
||||
|
||||
- the "simple" error management system does not accumulates errors when backtracking. This is a big perf gain, and is activated by default in nom 2.0
|
||||
- nom can now work on any type that implement the traits defined in `src/traits.rs`: `InputLength`, `InputIter`, `InputTake`, `Compare`, `FindToken`, `FindSubstring`, `Slice`
|
||||
- the documentation from Github's wiki has been moved to the `doc/` directory. They are markdown files that you can build with [cargo-external-doc](https://crates.io/crates/cargo-external-doc)
|
||||
- whitespace separated format support: with the `ws!` combinator, you can automatically introduce whitespace parsers between all parsers and combinators
|
||||
- the `permutation!` combinator applies its child parsers in any order, as long as they all succeed once, and return a tuple of the results
|
||||
- `do_parse!` is a simpler alternative to `chain!`, which is now deprecated
|
||||
- you can now transform an `IResult` in a `std::result::Result`
|
||||
- `length_data!` parses a length, and returns a subslice of that length
|
||||
- `tag_no_case!` provides case independent comparison. It works nicely, without any allocation, for ASCII strings, but for UTF-8 strings, it defaults to an unsatisfying (and incorrect) comparison by lowercasing both strings
|
||||
- `named_attr!` creates functions like `named!` but can add attributes like documentation
|
||||
- `many_till!` applies repeatedly its first child parser until the second succeeds
|
||||
|
||||
### Changed
|
||||
|
||||
- the "verbose" error management that was available in previous versions is now activated by the "verbose-errors" compilation feature
|
||||
- code reorganization: most of the parsers were moved in separate files to make the source easier to navigate
|
||||
- most of the combinators are now independent from the input type
|
||||
- the `eof` function was replaced with the `eof!` macro
|
||||
- `error!` and `add_error!` were replaced with `return_error!` and `add_return_error!` to fix the name conflict with the log crate
|
||||
- the `offset()` method is now in the `Offset` trait
|
||||
- `length_value!` has been renamed to `length_count!`. The new `length_value!` selects a slice and applies the second parser once on that slice
|
||||
- `AsChar::is_0_to_9` is now `AsChar::is_dec_digit`
|
||||
- the combinators with configurable endianness now take an enum instead of a boolean as parameter
|
||||
|
||||
### Fixed
|
||||
- the `count!`, `count_fixed!` and `length_*!` combinator calculate incomplete data needs correctly
|
||||
- `eol`, `line_ending` and `not_line_ending` now have a consistent behaviour that works correctly with incomplete data
|
||||
- `take_s!` didn't correctly handle the case when the slice is exactly the right length
|
||||
|
||||
## 1.2.4 - 2016-07-20
|
||||
|
||||
### Thanks
|
||||
- @Phlosioneer for documentation fixes
|
||||
- @sourrust for fixing offsets in `take_bits!`
|
||||
- @ChrisMacNaughton for the XFS crate
|
||||
- @pwoolcoc for `rest_s`
|
||||
- @fitzgen for more `IResult` methods
|
||||
- @gtors for the negative lookahead feature
|
||||
- @frk1 and @jeandudey for little endian float parsing
|
||||
- @jethrogb for fixing input usage in `many1`
|
||||
- @acatton for beating me at nom golf :D
|
||||
|
||||
### Added
|
||||
- the `rest_s` method on `IResult` returns the remaining `&str` input
|
||||
- `unwrap_err` and `unwrap_inc` methods on `IResult`
|
||||
- `not!` will peek at the input and return `Done` if the underlying parser returned `Error` or `Incomplete`, without consuming the input
|
||||
- `le_f32` and `le_f64` parse little endian floating point numbers (IEEE 754)
|
||||
-
|
||||
|
||||
### Fixed
|
||||
- documentation fixes
|
||||
- `take_bits!` is now more precise
|
||||
- `many1` inccorectly used the `len` function instead of `input_len`
|
||||
- the INI parser is simpler
|
||||
- `recognize!` had an early `return` that is removed now
|
||||
|
||||
## 1.2.3 - 2016-05-10
|
||||
|
||||
### Thanks
|
||||
- @lu-zero for the contribution guidelines
|
||||
- @GuillaumeGomez for fixes on `length_bytes` and some documentation
|
||||
- @Hywan for documentation and test fixes
|
||||
- @Xirdus for correct trait import issues
|
||||
- @mspiegel for the new AST example
|
||||
- @cholcombe973 for adding the `cond_with_error!` combinator
|
||||
- @tstorch for refactoring `many0!`
|
||||
- @panicbit for the folding combinators
|
||||
- @evestera for `separated_list!` fixes
|
||||
- @DanielKeep for correcting some enum imports
|
||||
|
||||
### Added
|
||||
- Regular expression combinators starting with `re_bytes_` work on byte slices
|
||||
- example parsing arithmetic expressions to an AST
|
||||
- `cond_with_error!` works like `cond!` but will return `None` if the condition is false, and `Some(value)` if the underlying parser succeeded
|
||||
- `fold_many0!`, `fold_many1!` and `fold_many_m_n!` will take a parser, an initial value and a combining function, and fold over the successful applications of the parser
|
||||
|
||||
### Fixed
|
||||
- `length_bytes!` converts the result of its child parser to usize
|
||||
- `take_till!` now imports `InputLength` instead of assuming it's in scope
|
||||
- `separated_list!` and `separated_nonempty_list!` will not consume the separator if there's no following successfully parsed value
|
||||
- no more warnings on build
|
||||
|
||||
### Changed
|
||||
- simpler implementation of `many0!`
|
||||
|
||||
## 1.2.2 - 2016-03-09
|
||||
|
||||
### Thanks
|
||||
- @conradev for fixing `take_until_s!`
|
||||
- @GuillaumeGomez for some documentation fixes
|
||||
- @frewsxcv for some documentation fixes
|
||||
- @tstorch for some test refactorings
|
||||
|
||||
### Added
|
||||
- `nom::Err` now implements `std::error::Error`
|
||||
|
||||
### Fixed
|
||||
- `hex_u32` does not parses more than 8 chars now
|
||||
- `take_while!` and `take_while1!` will not perturb the behaviour of `recognize!` anymore
|
||||
|
||||
## 1.2.1 - 2016-02-23
|
||||
|
||||
### Thanks
|
||||
- @sourrust for adding methods to `IResult`
|
||||
- @tstorch for the test refactoring, and for adding methods to `IResult` and `Needed`
|
||||
- @joelself for fixing the method system
|
||||
|
||||
### Added
|
||||
|
||||
- mapping methods over `IResult` and `Needed`
|
||||
|
||||
### Changed
|
||||
|
||||
- `apply_rf` is renamed to `apply_m`. This will not warrant a major version, since it is part missing from the methods feture added in the 1.2.0 release
|
||||
- the `regexp_macros` feature that used `regex!` to precompile regular expressions has been replaced by the normal regex engine combined with `lazy_static`
|
||||
|
||||
### Fixed
|
||||
|
||||
- when a parser or combinator was returning an empty buffer as remaining part, it was generating one from a static empty string. This was messing with buffer offset calculation. Now, that empty slice is taken like this: `&input[input.len()..]`.
|
||||
- The `regexp_macros` and `no_std` feature build again and are now tested with Travis CI
|
||||
|
||||
## 1.2.0 - 2016-02-08
|
||||
|
||||
### Thanks
|
||||
- @zentner-kyle for type inference fixes
|
||||
- @joelself for his work on `&str` parsing and method parsers
|
||||
- @GuillaumeGomez for implementing methods on `IResult`
|
||||
- @dirk for the `alt_complete!` combinator
|
||||
- @tstorch for a lot of refactoring work and unit tests additions
|
||||
- @jansegre for the hex digit parsers
|
||||
- @belgum for some documentation fixes
|
||||
- @lwandrebeck for some documentation fixes and code fixes in `hex_digit`
|
||||
|
||||
### Added
|
||||
- `take_until_and_consume_s!` for consumption of string data until a tag
|
||||
- more function patterns in `named!`. The error type can now be specified
|
||||
- `alt_complete!` works like the `alt!` combinator, but tries the next branch if the current one returned `Incomplete`, instead of returning directly
|
||||
- more unit tests for a lot of combinators
|
||||
- hexadecimal digit parsers
|
||||
- the `tuple!` combinator takes a list of parsers as argument, and applies them serially on the input. If all of them are successful, it willr eturn a tuple accumulating all the values. This combinator will (hopefully) replace most uses of `chain!`
|
||||
- parsers can now be implemented as a method for a struct thanks to the `method!`, `call_m!` and `apply_rf!` combinators
|
||||
|
||||
### Fixed
|
||||
- there were type inference issues in a few combinators. They will now be easier to compile
|
||||
- `peek!` compilation with bare functions
|
||||
- `&str` parsers were splitting data at the byte level, not at the char level, which can result in inconsistencies in parsing UTF-8 characters. They now use character indexes
|
||||
- some method implementations were missing on `IResult<I,O,E>` (with specified error type instead of implicit)
|
||||
|
||||
## 1.1.0 - 2016-01-01
|
||||
|
||||
This release adds a lot of features related to `&str` parsing. The previous versions
|
||||
were focused on `&[u8]` and bit streams parsing, but there's a need for more text
|
||||
parsing with nom. The parsing functions like `alpha`, `digit` and others will now
|
||||
accept either a `&[u8]` or a `&str`, so there is no breaking change on that part.
|
||||
|
||||
There are also a few performance improvements and documentation fixes.
|
||||
|
||||
### Thanks
|
||||
- @Binero for pushing the work on `&str` parsing
|
||||
- @meh for fixing `Option` and `Vec` imports
|
||||
- @hoodie for a documentation fix
|
||||
- @joelself for some documentation fixes
|
||||
- @vberger for his traits magic making nom functions more generic
|
||||
|
||||
### Added
|
||||
|
||||
- string related parsers: `tag_s!`, `take_s!`, `is_a_s!`, `is_not_s!`, `take_while_s!`, `take_while1_s!`, `take_till_s!`
|
||||
- `value!` is a combinator that always returns the same value. If a child parser is passed as second argument, that value is returned when the child parser succeeds
|
||||
|
||||
### Changed
|
||||
|
||||
- `tag!` will now compare even on partial input. If it expects "abcd" but receives "ef", it will now return an `Error` instead of `Incomplete`
|
||||
- `many0!` and others will preallocate a larger vector to avoid some copies and reallocations
|
||||
- `alpha`, `digit`, `alphanumeric`, `space` and `multispace` now accept as input a `&[u8]` or a `&str`. Additionally, they return an error if they receive an empty input
|
||||
- `take_while!`, `take_while1!`, `take_while_s!`, `take_while1_s!` wilreturn an error on empty input
|
||||
|
||||
### Fixed
|
||||
|
||||
- if the child parser of `many0!` or `many1!` returns `Incomplete`, it will return `Incomplete` too, possibly updating the needed size
|
||||
- `Option,` `Some`, `None` and `Vec` are now used with full path imports
|
||||
|
||||
## 1.0.1 - 2015-11-22
|
||||
|
||||
This releases makes the 1.0 version compatible with Rust 1.2 and 1.3
|
||||
|
||||
### Thanks
|
||||
- @steveklabnik for fixing lifetime issues in Producers and Consumers
|
||||
|
||||
## 1.0.0 - 2015-11-16
|
||||
|
||||
Stable release for nom. A lot of new features, a few breaking changes
|
||||
|
||||
### Thanks
|
||||
- @ahenry for macro fixes
|
||||
- @bluss for fixing documentation
|
||||
- @sourrust for cleaning code and debugging the new streaming utilities
|
||||
- @meh for inline optimizations
|
||||
- @ccmtaylor for fixing function imports
|
||||
- @soro for improvements to the streaming utilities
|
||||
- @breard-r for catching my typos
|
||||
- @nelsonjchen for catching my typos too
|
||||
- @divarvel for hex string parsers
|
||||
- @mrordinaire for the `length_bytes!` combinator
|
||||
|
||||
### Breaking changes
|
||||
- `IResult::Error` can now use custom error types, and is generic over the input type
|
||||
- Producers and consumers have been replaced. The new implementation uses less memory and integrates more with parsers
|
||||
- `nom::ErrorCode` is now `nom::ErrorKind`
|
||||
- `filter!` has been renamed to `take_while!`
|
||||
- `chain!` will count how much data is consumed and use that number to calculate how much data is needed if a parser returned `Incomplete`
|
||||
- `alt!` returns `Incomplete` if a child parser returned `Incomplete`, instead of skipping to the next parser
|
||||
- `IResult` does not require a lifetime tag anymore, yay!
|
||||
|
||||
### Added
|
||||
|
||||
- `complete!` will return an error if the child parser returned `Incomplete`
|
||||
- `add_error!` will wrap an error, but allow backtracking
|
||||
- `hex_u32` parser
|
||||
|
||||
### Fixed
|
||||
- the behaviour around `Incomplete` is better for most parsers now
|
||||
|
||||
## 0.5.0 - 2015-10-16
|
||||
|
||||
This release fixes a few issues and stabilizes the code.
|
||||
|
||||
### Thanks
|
||||
- @nox for documentation fixes
|
||||
- @daboross for linting fixes
|
||||
- @ahenry for fixing `tap!` and extending `dbg!` and `dbg_dmp!`
|
||||
- @bluss for tracking down and fixing issues with unsafe code
|
||||
- @meh for inlining parser functions
|
||||
- @ccmtaylor for fixing import of `str::from_utf8`
|
||||
|
||||
### Fixed
|
||||
- `tap!`, `dbg!` and `dbg_dmp!` now accept function parameters
|
||||
|
||||
### Changed
|
||||
- the type used in `count_fixed!` must be `Copy`
|
||||
- `chain!` calculates how much data is needed if one of the parsers returns `Incomplete
|
||||
- optional parsers in `chain!` can return `Incomplete`
|
||||
|
||||
## 0.4.0 - 2015-09-08
|
||||
|
||||
Considering the number of changes since the last release, this version can contain breaking changes, so the version number becomes 0.4.0. A lot of new features and performance improvements!
|
||||
|
||||
### Thanks
|
||||
- @frewsxcv for documentation fixes
|
||||
- @ngrewe for his work on producers and consumers
|
||||
- @meh for fixes on `chain!` and for the `rest` parser
|
||||
- @daboross for refactoring `many0!` and `many1!`
|
||||
- @aleksander for the `switch!` combinator idea
|
||||
- @TechnoMancer for his help with bit level parsing
|
||||
- @sxeraverx for pointing out a bug in `is_a!`
|
||||
|
||||
### Fixed
|
||||
- `count_fixed!` must take an explicit type as argument to generate the fixed-size array
|
||||
- optional parsing behaviour in `chain!`
|
||||
- `count!` can take 0 elements
|
||||
- `is_a!` and `is_not!` can now consume the whole input
|
||||
|
||||
### Added
|
||||
- it is now possible to seek to the end of a `MemProducer`
|
||||
- `opt!` returns `Done(input, None)` if `the child parser returned `Incomplete`
|
||||
- `rest` will return the remaining input
|
||||
- consumers can now seek to and from the end of input
|
||||
- `switch!` applies a first parser then matches on its result to choose the next parser
|
||||
- bit-level parsers
|
||||
- character-level parsers
|
||||
- regular expression parsers
|
||||
- implementation of `take_till!`, `take_while!` and `take_while1!`
|
||||
|
||||
### Changed
|
||||
- `alt!` can return `Incomplete`
|
||||
- the error analysis functions will now take references to functions instead of moving them
|
||||
- performance improvements on producers
|
||||
- performance improvement for `filter!`
|
||||
- performance improvement for `count!`: a `Vec` of the right size is directly allocated
|
||||
|
||||
## 0.3.11 - 2015-08-04
|
||||
|
||||
### Thanks
|
||||
- @bluss for remarking that the crate included random junk lying non commited in my local repository
|
||||
|
||||
### Fixed
|
||||
- cleanup of my local repository will ship less files in the crates, resulting in a smaller download
|
||||
|
||||
## 0.3.10 - 2015-08-03
|
||||
|
||||
### Added
|
||||
|
||||
- `bits!` for bit level parsing. It indicates that all child parsers will take a `(&[u8], usize)`as input, with the second parameter indicating the bit offset in the first byte. This allows viewing a byte slice as a bit stream. Most combinators can be used directly under `bits!`
|
||||
- `take_bits!` takes an integer type and a number of bits, consumes that number of bits and updates the offset, possibly by crossing byte boundaries
|
||||
- bit level parsers are all written in `src/bits.rs`
|
||||
|
||||
### Changed
|
||||
|
||||
- Parsers that specifically handle bytes have been moved to src/bytes.rs`. This applies to `tag!`, `is_not!`, `is_a!`, `filter!`, `take!`, `take_str!`, `take_until_and_consume!`, `take_until!`, `take_until_either_and_consume!`, `take_until_either!`
|
||||
|
||||
## 0.3.9 - 2015-07-20
|
||||
|
||||
### Thanks
|
||||
- @badboy for fixing `filter!`
|
||||
- @idmit for some documentation fixes
|
||||
|
||||
### Added
|
||||
- `opt_res!` applies a parser and transform its result in a Result. This parser never fails
|
||||
- `cond_reduce!` takes an expression as parameter, applies the parser if the expression is true, and returns an error if the expression is false
|
||||
- `tap!` pass the result of a parser to a block to manipulate it, but do not affect the parser's result
|
||||
- `AccReader` is a Read+BufRead that supports data accumulation and partial consumption. The `consume` method must be called afterwardsto indicate how much was consumed
|
||||
- Arithmetic expression evaluation and parsing example
|
||||
- `u16!`, `u32!`, `u64!`, `i16!`, `i32!`, `i64!` take an expression as parameter, if the expression is true, apply the big endian integer parser, if false, the little endian version
|
||||
- type information for combinators. This will make the documentation a bit easier to navigate
|
||||
|
||||
### Fixed
|
||||
- `map_opt!` and `map_res!` had issues with argument order due to bad macros
|
||||
- `delimited!` did not compile for certain combinations of arguments
|
||||
- `filter!` did not return a byte slice but a fixed array
|
||||
|
||||
## 0.3.8 - 2015-07-03
|
||||
|
||||
### Added
|
||||
- code coverage is now calculated automatically on Travis CI
|
||||
- `Stepper`: wrap a `Producer`, and call the method `step` with a parser. This method will buffer data if there is not enough, apply the parser if there is, and keep the rest of the input in memory for the next call
|
||||
- `ReadProducer`: takes something implementing `Read`, and makes a `Producer` out of it
|
||||
|
||||
### Fixed
|
||||
- the combinators `separated_pair!` and `delimited!` did not work because an implementation macro was not exported
|
||||
- if a `MemProducer` reached its end, it should always return `Eof`
|
||||
- `map!` had issues with argument matching
|
||||
|
||||
## 0.3.7 - 2015-06-24
|
||||
|
||||
### Added
|
||||
- `expr_res!` and `expr_opt!` evaluate an expression returning a Result or Opt and convert it to IResult
|
||||
- `AsBytes` is implemented for fixed size arrays. This allows `tag!([41u8, 42u8])`
|
||||
|
||||
### Fixed
|
||||
- `count_fixed!` argument parsing works again
|
||||
|
||||
## 0.3.6 - 2015-06-15
|
||||
|
||||
### Added
|
||||
- documentation for a few functions
|
||||
- the consumer trait now requires the `failed(&self, error_code)` method in case of parsing error
|
||||
- `named!` now handles thge alternative `named!(pub fun_name<OutputType>, ...)`
|
||||
|
||||
### Fixed
|
||||
- `filter!` now returns the whole input if the filter function never returned false
|
||||
- `take!` casts its argument as usize, so it can accepts any integer type now
|
||||
|
||||
## 0.3.5 - 2015-06-10
|
||||
|
||||
### Thanks
|
||||
- @cmr for some documentation fixes
|
||||
|
||||
### Added
|
||||
- `count_fixed!` returns a fixed array
|
||||
|
||||
### Fixed
|
||||
- `count!` is back to the previous behaviour, returning a `Vec` for sizes known at runtime
|
||||
|
||||
### Changed
|
||||
- functions and traits exported from `nom::util` are now directly in `nom::`
|
||||
|
||||
## 0.3.4 - 2015-06-09
|
||||
|
||||
### Thanks
|
||||
- @andrew-d for fixes on `cond!`
|
||||
- @keruspe for features in `chain!`
|
||||
|
||||
### Added
|
||||
- `chain!` can now have mutable fields
|
||||
|
||||
### Fixed
|
||||
- `cond!` had an infinite macro recursion
|
||||
|
||||
### Changed
|
||||
- `chain!` generates less code now. No apprent compilation time improvement
|
||||
|
||||
## 0.3.3 - 2015-06-09
|
||||
|
||||
### Thanks
|
||||
- @andrew-d for the little endian signed integer parsers
|
||||
- @keruspe for fixes on `count!`
|
||||
|
||||
### Added
|
||||
- `le_i8`, `le_i16`, `le_i32`, `le_i64`: little endian signed integer parsers
|
||||
|
||||
### Changed
|
||||
- the `alt!` parser compiles much faster, even with more than 8 branches
|
||||
- `count!` can now return a fixed size array instead of a growable vector
|
||||
|
||||
## 0.3.2 - 2015-05-31
|
||||
|
||||
### Thanks
|
||||
- @keruspe for the `take_str` parser and the function application combinator
|
||||
|
||||
### Added
|
||||
- `take_str!`: takes the specified number of bytes and return a UTF-8 string
|
||||
- `apply!`: do partial application on the parameters of a function
|
||||
|
||||
### Changed
|
||||
- `Needed::Size` now contains a `usize` instead of a `u32`
|
||||
|
||||
## 0.3.1 - 2015-05-21
|
||||
|
||||
### Thanks
|
||||
- @divarvel for the big endian signed integer parsers
|
||||
|
||||
### Added
|
||||
- `be_i8`, `be_i16`, `be_i32`, `be_i64`: big endian signed integer parsers
|
||||
- the `core` feature can be passed to cargo to build with `no_std`
|
||||
- colored hexdump can be generated from error chains
|
||||
|
||||
## 0.3.0 - 2015-05-07
|
||||
|
||||
### Thanks
|
||||
- @filipegoncalves for some documentation and the new eof parser
|
||||
- @CrimsonVoid for putting fully qualified types in the macros
|
||||
- @lu_zero for some documentation fixes
|
||||
|
||||
### Added
|
||||
- new error types that can contain an error code, an input slice, and a list of following errors
|
||||
- `error!` will cut backtracking and return directly from the parser, with a specified error code
|
||||
- `eof` parser, successful if there is no more input
|
||||
- specific error codes for the parsers provided by nom
|
||||
|
||||
### Changed
|
||||
- fully qualified types in macros. A lot of imports are not needed anymore
|
||||
|
||||
### Removed
|
||||
- `FlatMap`, `FlatpMapOpt` and `Functor` traits (replaced by `map!`, `map_opt!` and `map_res!`)
|
||||
|
||||
## 0.2.2 - 2015-04-12
|
||||
|
||||
### Thanks
|
||||
- @filipegoncalves and @thehydroimpulse for debugging an infinite loop in many0 and many1
|
||||
- @thehydroimpulse for suggesting public named parsers
|
||||
- @skade for removing the dependency on the collections gate
|
||||
|
||||
### Added
|
||||
- `named!` can now declare public functions like this: `named!(pub tst, tag!("abcd"));`
|
||||
- `pair!(X,Y)` returns a tuple `(x, y)`
|
||||
- `separated_pair!(X, sep, Y)` returns a tuple `(x, y)`
|
||||
- `preceded!(opening, X)` returns `x`
|
||||
- `terminated!(X, closing)` returns `x`
|
||||
- `delimited(opening, X, closing)` returns `x`
|
||||
- `separated_list(sep, X)` returns a `Vec<X>`
|
||||
- `separated_nonempty_list(sep, X)` returns a `Vec<X>` of at list one element
|
||||
|
||||
### Changed
|
||||
- `many0!` and `many1!` forbid parsers that do not consume input
|
||||
- `is_a!`, `is_not!`, `alpha`, `digit`, `space`, `multispace` will now return an error if they do not consume at least one byte
|
||||
|
||||
## 0.2.1 - 2015-04-04
|
||||
|
||||
### Thanks
|
||||
- @mtsr for catching the remaining debug println!
|
||||
- @jag426 who killed a lot of warnings
|
||||
- @skade for removing the dependency on the core feature gate
|
||||
|
||||
|
||||
### Added
|
||||
- little endian unsigned int parsers le_u8, le_u16, le_u32, le_u64
|
||||
- `count!` to apply a parser a specified number of times
|
||||
- `cond!` applies a parser if the condition is met
|
||||
- more parser development tools in `util::*`
|
||||
|
||||
### Fixed
|
||||
- in one case, `opt!` would not compile
|
||||
|
||||
### Removed
|
||||
- most of the feature gates are now removed. The only one still needed is `collections`
|
||||
|
||||
## 0.2.0 - 2015-03-24
|
||||
*works with `rustc 1.0.0-dev (81e2396c7 2015-03-19) (built 2015-03-19)`*
|
||||
|
||||
### Thanks
|
||||
- Ryman for the AsBytes implementation
|
||||
- jag426 and jaredly for documentation fixes
|
||||
- eternaleye on #rust IRC for his help on the new macro syntax
|
||||
|
||||
### Changed
|
||||
- the AsBytes trait improves readability, no more b"...", but "..." instead
|
||||
- Incomplete will now hold either Needed;;Unknown, or Needed::Size(u32). Matching on Incomplete without caring for the value is done with `Incomplete(_)`, but if more granularity is mandatory, `Needed` can be matched too
|
||||
- `alt!` can pass the result of the parser to a closure
|
||||
- the `take_*` macros changed behaviour, the default case is now not to consume the separator. The macros have been renamed as follows: `take_until!` -> `take_until_and_consume!`, `take_until_and_leave!` -> `take_until!`, `take_until_either_and_leave!` -> `take_until_either!`, `take_until_either!` -> `take_until_either_and_consume!`
|
||||
|
||||
### Added
|
||||
- `peek!` macro: matches the future input but does not consume it
|
||||
- `length_value!` macro: the first argument is a parser returning a `n` that can cast to usize, then applies the second parser `n` times. The macro has a variant with a third argument indicating the expected input size for the second parser
|
||||
- benchmarks are available at https://github.com/Geal/nom_benchmarks
|
||||
- more documentation
|
||||
- **Unnamed parser syntax**: warning, this is a breaking change. With this new syntax, the macro combinators do not generate functions anymore, they create blocks. That way, they can be nested, for better readability. The `named!` macro is provided to create functions from parsers. Please be aware that nesting parsers comes with a small cost of compilation time, negligible in most cases, but can quickly get to the minutes scale if not careful. If this happens, separate your parsers in multiple subfunctions.
|
||||
- `named!`, `closure!` and `call!` macros used to support the unnamed syntax
|
||||
- `map!`, `map_opt!` and `map_res!` to combine a parser with a normal function, transforming the input directly, or returning an `Option` or `Result`
|
||||
|
||||
### Fixed
|
||||
- `is_a!` is now working properly
|
||||
|
||||
### Removed
|
||||
- the `o!` macro does less than `chain!`, so it has been removed
|
||||
- the `fold0!` and `fold1!` macros were too complex and awkward to use, the `many*` combinators will be useful for most uses for now
|
||||
|
||||
## 0.1.6 - 2015-02-24
|
||||
### Changed
|
||||
- consumers must have an end method that will be called after parsing
|
||||
|
||||
### Added
|
||||
- big endian unsigned int and float parsers: be_u8, be_u16, be_u32, be_u64, be_f32, be_f64
|
||||
- producers can seek
|
||||
- function and macros documentation
|
||||
- README documentation
|
||||
### Fixed
|
||||
- lifetime declarations
|
||||
- tag! can return Incomplete
|
||||
|
||||
## 0.1.5 - 2015-02-17
|
||||
### Changed
|
||||
- traits were renamed: FlatMapper -> FlatMap, Mapper -> FlatMapOpt, Mapper2 -> Functor
|
||||
|
||||
### Fixed
|
||||
- woeks with rustc f1bb6c2f4
|
||||
|
||||
## 0.1.4 - 2015-02-17
|
||||
### Changed
|
||||
- the chaining macro can take optional arguments with '?'
|
||||
|
||||
## 0.1.3 - 2015-02-16
|
||||
### Changed
|
||||
- the chaining macro now takes the closure at the end of the argument list
|
||||
|
||||
## 0.1.2 - 2015-02-16
|
||||
### Added
|
||||
- flat_map implementation for <&[u8], &[u8]>
|
||||
- chaining macro
|
||||
- partial MP4 parser example
|
||||
|
||||
|
||||
## 0.1.1 - 2015-02-06
|
||||
### Fixed
|
||||
- closure syntax change
|
||||
|
||||
## Compare code
|
||||
|
||||
* [unreleased]: https://github.com/Geal/nom/compare/3.2.1...HEAD
|
||||
* [3.2.1]: https://github.com/Geal/nom/compare/3.2.0...3.2.1
|
||||
* [3.2.0]: https://github.com/Geal/nom/compare/3.1.0...3.2.0
|
||||
* [3.1.0]: https://github.com/Geal/nom/compare/3.0.0...3.1.0
|
||||
* [3.0.0]: https://github.com/Geal/nom/compare/2.2.1...3.0.0
|
||||
* [2.2.1]: https://github.com/Geal/nom/compare/2.2.0...2.2.1
|
||||
* [2.2.0]: https://github.com/Geal/nom/compare/2.1.0...2.2.0
|
||||
* [2.1.0]: https://github.com/Geal/nom/compare/2.0.1...2.1.0
|
||||
* [2.0.1]: https://github.com/Geal/nom/compare/2.0.0...2.0.1
|
||||
* [2.0.0]: https://github.com/Geal/nom/compare/1.2.4...2.0.0
|
||||
* [1.2.4]: https://github.com/Geal/nom/compare/1.2.3...1.2.4
|
||||
* [1.2.3]: https://github.com/Geal/nom/compare/1.2.2...1.2.3
|
||||
* [1.2.2]: https://github.com/Geal/nom/compare/1.2.1...1.2.2
|
||||
* [1.2.1]: https://github.com/Geal/nom/compare/1.2.0...1.2.1
|
||||
* [1.2.0]: https://github.com/Geal/nom/compare/1.1.0...1.2.0
|
||||
* [1.1.0]: https://github.com/Geal/nom/compare/1.0.1...1.1.0
|
||||
* [1.0.1]: https://github.com/Geal/nom/compare/1.0.0...1.0.1
|
||||
* [1.0.0]: https://github.com/Geal/nom/compare/0.5.0...1.0.0
|
||||
* [0.5.0]: https://github.com/geal/nom/compare/0.4.0...0.5.0
|
||||
* [0.4.0]: https://github.com/geal/nom/compare/0.3.11...0.4.0
|
||||
* [0.3.11]: https://github.com/geal/nom/compare/0.3.10...0.3.11
|
||||
* [0.3.10]: https://github.com/geal/nom/compare/0.3.9...0.3.10
|
||||
* [0.3.9]: https://github.com/geal/nom/compare/0.3.8...0.3.9
|
||||
* [0.3.8]: https://github.com/Geal/nom/compare/0.3.7...0.3.8
|
||||
* [0.3.7]: https://github.com/Geal/nom/compare/0.3.6...0.3.7
|
||||
* [0.3.6]: https://github.com/Geal/nom/compare/0.3.5...0.3.6
|
||||
* [0.3.5]: https://github.com/Geal/nom/compare/0.3.4...0.3.5
|
||||
* [0.3.4]: https://github.com/Geal/nom/compare/0.3.3...0.3.4
|
||||
* [0.3.3]: https://github.com/Geal/nom/compare/0.3.2...0.3.3
|
||||
* [0.3.2]: https://github.com/Geal/nom/compare/0.3.1...0.3.2
|
||||
* [0.3.1]: https://github.com/Geal/nom/compare/0.3.0...0.3.1
|
||||
* [0.3.0]: https://github.com/Geal/nom/compare/0.2.2...0.3.0
|
||||
* [0.2.2]: https://github.com/Geal/nom/compare/0.2.1...0.2.2
|
||||
* [0.2.1]: https://github.com/Geal/nom/compare/0.2.0...0.2.1
|
||||
* [0.2.0]: https://github.com/Geal/nom/compare/0.1.6...0.2.0
|
||||
* [0.1.6]: https://github.com/Geal/nom/compare/0.1.5...0.1.6
|
||||
* [0.1.5]: https://github.com/Geal/nom/compare/0.1.4...0.1.5
|
||||
* [0.1.4]: https://github.com/Geal/nom/compare/0.1.3...0.1.4
|
||||
* [0.1.3]: https://github.com/Geal/nom/compare/0.1.2...0.1.3
|
||||
* [0.1.2]: https://github.com/Geal/nom/compare/0.1.1...0.1.2
|
||||
* [0.1.1]: https://github.com/Geal/nom/compare/0.1.0...0.1.1
|
50
third_party/rust/nom-3.2.1/Cargo.toml
vendored
50
third_party/rust/nom-3.2.1/Cargo.toml
vendored
@ -1,50 +0,0 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
name = "nom"
|
||||
version = "3.2.1"
|
||||
authors = ["contact@geoffroycouprie.com"]
|
||||
include = ["CHANGELOG.md", "LICENSE", ".gitignore", ".travis.yml", "Cargo.toml", "src/*.rs", "tests/*.rs"]
|
||||
description = "A byte-oriented, zero-copy, parser combinators library"
|
||||
documentation = "http://rust.unhandledexpression.com/nom/"
|
||||
readme = "README.md"
|
||||
keywords = ["parser", "parser-combinators", "parsing", "streaming", "bit"]
|
||||
categories = ["parsing"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/Geal/nom"
|
||||
[dependencies.memchr]
|
||||
version = "^1.0.1"
|
||||
default-features = false
|
||||
|
||||
[dependencies.compiler_error]
|
||||
version = "0.1.1"
|
||||
optional = true
|
||||
|
||||
[dependencies.regex]
|
||||
version = "^0.2"
|
||||
optional = true
|
||||
|
||||
[dependencies.lazy_static]
|
||||
version = "^0.2.2"
|
||||
optional = true
|
||||
|
||||
[features]
|
||||
default = ["std", "stream"]
|
||||
regexp = ["regex"]
|
||||
verbose-errors = []
|
||||
stream = []
|
||||
nightly = ["compiler_error"]
|
||||
regexp_macros = ["regexp", "lazy_static"]
|
||||
std = ["memchr/use_std"]
|
||||
[badges.travis-ci]
|
||||
repository = "Geal/nom"
|
20
third_party/rust/nom-3.2.1/LICENSE
vendored
20
third_party/rust/nom-3.2.1/LICENSE
vendored
@ -1,20 +0,0 @@
|
||||
Copyright (c) 2015-2016 Geoffroy Couprie
|
||||
|
||||
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.
|
394
third_party/rust/nom-3.2.1/src/bits.rs
vendored
394
third_party/rust/nom-3.2.1/src/bits.rs
vendored
@ -1,394 +0,0 @@
|
||||
//! Bit level parsers and combinators
|
||||
//!
|
||||
//! Bit parsing is handled by tweaking the input in most macros.
|
||||
//! In byte level parsing, the input is generally a `&[u8]` passed from combinator
|
||||
//! to combinator until the slices are manipulated.
|
||||
//!
|
||||
//! Bit parsers take a `(&[u8], usize)` as input. The first part of the tuple is an byte slice,
|
||||
//! the second part is a bit offset in the first byte of the slice.
|
||||
//!
|
||||
//! By passing a pair like this, we can leverage most of the combinators, and avoid
|
||||
//! transforming the whole slice to a vector of booleans. This should make it easy
|
||||
//! to see a byte slice as a bit stream, and parse code points of arbitrary bit length.
|
||||
|
||||
|
||||
/// `bits!( parser ) => ( &[u8], (&[u8], usize) -> IResult<(&[u8], usize), T> ) -> IResult<&[u8], T>`
|
||||
/// transforms its byte slice input into a bit stream for the underlying parsers
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # fn main() {
|
||||
/// named!( take_3_bits<u8>, bits!( take_bits!( u8, 3 ) ) );
|
||||
///
|
||||
/// let input = vec![0b10101010, 0b11110000, 0b00110011];
|
||||
/// let sl = &input[..];
|
||||
///
|
||||
/// assert_eq!(take_3_bits( sl ), Done(&sl[1..], 5) );
|
||||
/// # }
|
||||
#[macro_export]
|
||||
macro_rules! bits (
|
||||
($i:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
bits_impl!($i, $submac!($($args)*));
|
||||
);
|
||||
($i:expr, $f:expr) => (
|
||||
bits_impl!($i, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
/// Internal parser, do not use directly
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! bits_impl (
|
||||
($i:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
let input = ($i, 0usize);
|
||||
match $submac!(input, $($args)*) {
|
||||
$crate::IResult::Error(e) => {
|
||||
let err = match e {
|
||||
$crate::Err::Code(k) | $crate::Err::Node(k, _) => $crate::Err::Code(k),
|
||||
$crate::Err::Position(k, (i,b)) | $crate::Err::NodePosition(k, (i,b), _) => {
|
||||
$crate::Err::Position(k, &i[b/8..])
|
||||
}
|
||||
};
|
||||
$crate::IResult::Error(err)
|
||||
}
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown) => $crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(i)) => {
|
||||
//println!("bits parser returned Needed::Size({})", i);
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(i / 8 + 1))
|
||||
},
|
||||
$crate::IResult::Done((i, bit_index), o) => {
|
||||
let byte_index = bit_index / 8 + if bit_index % 8 == 0 { 0 } else { 1 } ;
|
||||
//println!("bit index=={} => byte index=={}", bit_index, byte_index);
|
||||
$crate::IResult::Done(&i[byte_index..], o)
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "verbose-errors"))]
|
||||
/// Internal parser, do not use directly
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! bits_impl (
|
||||
($i:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
let input = ($i, 0usize);
|
||||
match $submac!(input, $($args)*) {
|
||||
$crate::IResult::Error(e) => {
|
||||
$crate::IResult::Error(e)
|
||||
}
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown) => $crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(i)) => {
|
||||
//println!("bits parser returned Needed::Size({})", i);
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(i / 8 + 1))
|
||||
},
|
||||
$crate::IResult::Done((i, bit_index), o) => {
|
||||
let byte_index = bit_index / 8 + if bit_index % 8 == 0 { 0 } else { 1 } ;
|
||||
//println!("bit index=={} => byte index=={}", bit_index, byte_index);
|
||||
$crate::IResult::Done(&i[byte_index..], o)
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// Counterpart to bits,
|
||||
/// `bytes!( parser ) => ( (&[u8], usize), &[u8] -> IResult<&[u8], T> ) -> IResult<(&[u8], usize), T>`,
|
||||
/// transforms its bits stream input into a byte slice for the underlying parsers. If we start in the
|
||||
/// middle of a byte throws away the bits until the end of the byte.
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # use nom::rest;
|
||||
/// # fn main() {
|
||||
/// named!( parse<(u8, u8, &[u8])>, bits!( tuple!(
|
||||
/// take_bits!(u8, 4),
|
||||
/// take_bits!(u8, 8),
|
||||
/// bytes!(rest)
|
||||
/// )));
|
||||
///
|
||||
/// let input = &[0xde, 0xad, 0xbe, 0xaf];
|
||||
///
|
||||
/// assert_eq!(parse( input ), Done(&[][..], (0xd, 0xea, &[0xbe, 0xaf][..])));
|
||||
/// # }
|
||||
#[macro_export]
|
||||
macro_rules! bytes (
|
||||
($i:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
bytes_impl!($i, $submac!($($args)*));
|
||||
);
|
||||
($i:expr, $f:expr) => (
|
||||
bytes_impl!($i, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
/// Internal parser, do not use directly
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! bytes_impl (
|
||||
($macro_i:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
let inp;
|
||||
if $macro_i.1 % 8 != 0 {
|
||||
inp = & $macro_i.0[1 + $macro_i.1 / 8 ..];
|
||||
}
|
||||
else {
|
||||
inp = & $macro_i.0[$macro_i.1 / 8 ..];
|
||||
}
|
||||
|
||||
match $submac!(inp, $($args)*) {
|
||||
$crate::IResult::Error(e) => {
|
||||
let err = match e {
|
||||
$crate::Err::Code(k) | $crate::Err::Node(k, _) => $crate::Err::Code(k),
|
||||
$crate::Err::Position(k, i) | $crate::Err::NodePosition(k, i, _) => {
|
||||
$crate::Err::Position(k, (i, 0))
|
||||
}
|
||||
};
|
||||
$crate::IResult::Error(err)
|
||||
}
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown) => $crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(i)) => {
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(i * 8))
|
||||
},
|
||||
$crate::IResult::Done(i, o) => {
|
||||
$crate::IResult::Done((i, 0), o)
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "verbose-errors"))]
|
||||
/// Internal parser, do not use directly
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! bytes_impl (
|
||||
($macro_i:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
let inp;
|
||||
if $macro_i.1 % 8 != 0 {
|
||||
inp = & $macro_i.0[1 + $macro_i.1 / 8 ..];
|
||||
}
|
||||
else {
|
||||
inp = & $macro_i.0[$macro_i.1 / 8 ..];
|
||||
}
|
||||
|
||||
match $submac!(inp, $($args)*) {
|
||||
$crate::IResult::Error(e) => {
|
||||
$crate::IResult::Error(e)
|
||||
}
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown) => $crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(i)) => {
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(i * 8))
|
||||
},
|
||||
$crate::IResult::Done(i, o) => {
|
||||
$crate::IResult::Done((i, 0), o)
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// `take_bits!(type, nb) => ( (&[T], usize), U, usize) -> IResult<(&[T], usize), U>`
|
||||
/// generates a parser consuming the specified number of bits.
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # fn main() {
|
||||
/// named!( take_pair<(u8, u8)>, bits!( pair!( take_bits!( u8, 3 ), take_bits!(u8, 5) ) ) );
|
||||
///
|
||||
/// let input = vec![0b10101010, 0b11110000, 0b00110011];
|
||||
/// let sl = &input[..];
|
||||
///
|
||||
/// assert_eq!(take_pair( sl ), Done(&sl[1..], (5, 10)) );
|
||||
/// assert_eq!(take_pair( &sl[1..] ), Done(&sl[2..], (7, 16)) );
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! take_bits (
|
||||
($i:expr, $t:ty, $count:expr) => (
|
||||
{
|
||||
use std::ops::Div;
|
||||
use std::convert::Into;
|
||||
//println!("taking {} bits from {:?}", $count, $i);
|
||||
let (input, bit_offset) = $i;
|
||||
let res : $crate::IResult<(&[u8],usize), $t> = if $count == 0 {
|
||||
$crate::IResult::Done( (input, bit_offset), (0 as u8).into())
|
||||
} else {
|
||||
let cnt = ($count as usize + bit_offset).div(8);
|
||||
if input.len() * 8 < $count as usize + bit_offset {
|
||||
//println!("returning incomplete: {}", $count as usize + bit_offset);
|
||||
$crate::IResult::Incomplete($crate::Needed::Size($count as usize))
|
||||
} else {
|
||||
let mut acc:$t = (0 as u8).into();
|
||||
let mut offset: usize = bit_offset;
|
||||
let mut remaining: usize = $count;
|
||||
let mut end_offset: usize = 0;
|
||||
|
||||
for byte in input.iter().take(cnt + 1) {
|
||||
if remaining == 0 {
|
||||
break;
|
||||
}
|
||||
let val: $t = if offset == 0 {
|
||||
(*byte as u8).into()
|
||||
} else {
|
||||
(((*byte as u8) << offset) as u8 >> offset).into()
|
||||
};
|
||||
|
||||
if remaining < 8 - offset {
|
||||
acc += val >> (8 - offset - remaining);
|
||||
end_offset = remaining + offset;
|
||||
break;
|
||||
} else {
|
||||
acc += val << (remaining - (8 - offset));
|
||||
remaining -= 8 - offset;
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
$crate::IResult::Done( (&input[cnt..], end_offset) , acc)
|
||||
}
|
||||
};
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// matches an integer pattern to a bitstream. The number of bits of the input to compare must be specified
|
||||
#[macro_export]
|
||||
macro_rules! tag_bits (
|
||||
($i:expr, $t:ty, $count:expr, $p: pat) => (
|
||||
{
|
||||
match take_bits!($i, $t, $count) {
|
||||
$crate::IResult::Incomplete(i) => $crate::IResult::Incomplete(i),
|
||||
$crate::IResult::Done(i, o) => {
|
||||
if let $p = o {
|
||||
let res: $crate::IResult<(&[u8],usize),$t> = $crate::IResult::Done(i, o);
|
||||
res
|
||||
} else {
|
||||
$crate::IResult::Error(error_position!($crate::ErrorKind::TagBits, $i))
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
$crate::IResult::Error(error_position!($crate::ErrorKind::TagBits, $i))
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::ops::{Shr,Shl,AddAssign};
|
||||
use internal::{IResult,Needed};
|
||||
use ErrorKind;
|
||||
|
||||
#[test]
|
||||
fn take_bits() {
|
||||
let input = [0b10101010, 0b11110000, 0b00110011];
|
||||
let sl = &input[..];
|
||||
|
||||
assert_eq!(take_bits!( (sl, 0), u8, 0 ), IResult::Done((sl, 0), 0));
|
||||
assert_eq!(take_bits!( (sl, 0), u8, 8 ), IResult::Done((&sl[1..], 0), 170));
|
||||
assert_eq!(take_bits!( (sl, 0), u8, 3 ), IResult::Done((&sl[0..], 3), 5));
|
||||
assert_eq!(take_bits!( (sl, 0), u8, 6 ), IResult::Done((&sl[0..], 6), 42));
|
||||
assert_eq!(take_bits!( (sl, 1), u8, 1 ), IResult::Done((&sl[0..], 2), 0));
|
||||
assert_eq!(take_bits!( (sl, 1), u8, 2 ), IResult::Done((&sl[0..], 3), 1));
|
||||
assert_eq!(take_bits!( (sl, 1), u8, 3 ), IResult::Done((&sl[0..], 4), 2));
|
||||
assert_eq!(take_bits!( (sl, 6), u8, 3 ), IResult::Done((&sl[1..], 1), 5));
|
||||
assert_eq!(take_bits!( (sl, 0), u16, 10 ), IResult::Done((&sl[1..], 2), 683));
|
||||
assert_eq!(take_bits!( (sl, 0), u16, 8 ), IResult::Done((&sl[1..], 0), 170));
|
||||
assert_eq!(take_bits!( (sl, 6), u16, 10 ), IResult::Done((&sl[2..], 0), 752));
|
||||
assert_eq!(take_bits!( (sl, 6), u16, 11 ), IResult::Done((&sl[2..], 1), 1504));
|
||||
assert_eq!(take_bits!( (sl, 0), u32, 20 ), IResult::Done((&sl[2..], 4), 700163));
|
||||
assert_eq!(take_bits!( (sl, 4), u32, 20 ), IResult::Done((&sl[3..], 0), 716851));
|
||||
assert_eq!(take_bits!( (sl, 4), u32, 22 ), IResult::Incomplete(Needed::Size(22)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tag_bits() {
|
||||
let input = [0b10101010, 0b11110000, 0b00110011];
|
||||
let sl = &input[..];
|
||||
|
||||
assert_eq!(tag_bits!( (sl, 0), u8, 3, 0b101), IResult::Done((&sl[0..], 3), 5));
|
||||
assert_eq!(tag_bits!( (sl, 0), u8, 4, 0b1010), IResult::Done((&sl[0..], 4), 10));
|
||||
}
|
||||
|
||||
named!(ch<(&[u8],usize),(u8,u8)>,
|
||||
do_parse!(
|
||||
tag_bits!(u8, 3, 0b101) >>
|
||||
x: take_bits!(u8, 4) >>
|
||||
y: take_bits!(u8, 5) >>
|
||||
(x,y)
|
||||
)
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn chain_bits() {
|
||||
let input = [0b10101010, 0b11110000, 0b00110011];
|
||||
let sl = &input[..];
|
||||
assert_eq!(ch((&input[..],0)), IResult::Done((&sl[1..], 4), (5,15)));
|
||||
assert_eq!(ch((&input[..],4)), IResult::Done((&sl[2..], 0), (7,16)));
|
||||
assert_eq!(ch((&input[..1],0)), IResult::Incomplete(Needed::Size(12)));
|
||||
}
|
||||
|
||||
named!(ch_bytes<(u8,u8)>, bits!(ch));
|
||||
#[test]
|
||||
fn bits_to_bytes() {
|
||||
let input = [0b10101010, 0b11110000, 0b00110011];
|
||||
assert_eq!(ch_bytes(&input[..]), IResult::Done(&input[2..], (5,15)));
|
||||
assert_eq!(ch_bytes(&input[..1]), IResult::Incomplete(Needed::Size(2)));
|
||||
assert_eq!(ch_bytes(&input[1..]), IResult::Error(error_position!(ErrorKind::TagBits, &input[1..])));
|
||||
}
|
||||
|
||||
#[derive(PartialEq,Debug)]
|
||||
struct FakeUint(u32);
|
||||
|
||||
impl AddAssign for FakeUint {
|
||||
|
||||
fn add_assign(&mut self, other: FakeUint) {
|
||||
*self = FakeUint(&self.0 + other.0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Shr<usize> for FakeUint {
|
||||
type Output = FakeUint;
|
||||
|
||||
fn shr(self, shift: usize) -> FakeUint {
|
||||
FakeUint(&self.0 >> shift)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Shl<usize> for FakeUint {
|
||||
type Output = FakeUint;
|
||||
|
||||
fn shl(self, shift: usize) -> FakeUint {
|
||||
FakeUint(&self.0 << shift)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl From<u8> for FakeUint {
|
||||
|
||||
fn from(i: u8) -> FakeUint {
|
||||
FakeUint(u32::from(i))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_privitive_type() {
|
||||
let input = [0b10101010, 0b11110000, 0b00110011];
|
||||
let sl = &input[..];
|
||||
|
||||
assert_eq!(take_bits!( (sl, 0), FakeUint, 20 ), IResult::Done((&sl[2..], 4), FakeUint(700163)));
|
||||
assert_eq!(take_bits!( (sl, 4), FakeUint, 20 ), IResult::Done((&sl[3..], 0), FakeUint(716851)));
|
||||
assert_eq!(take_bits!( (sl, 4), FakeUint, 22 ), IResult::Incomplete(Needed::Size(22)));
|
||||
}
|
||||
}
|
871
third_party/rust/nom-3.2.1/src/branch.rs
vendored
871
third_party/rust/nom-3.2.1/src/branch.rs
vendored
@ -1,871 +0,0 @@
|
||||
/// Try a list of parsers and return the result of the first successful one
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// alt!(I -> IResult<I,O> | I -> IResult<I,O> | ... | I -> IResult<I,O> ) => I -> IResult<I, O>
|
||||
/// ```
|
||||
/// All the parsers must have the same return type.
|
||||
///
|
||||
/// If one of the parsers returns `Incomplete`, `alt!` will return `Incomplete`, to retry
|
||||
/// once you get more input. Note that it is better for performance to know the
|
||||
/// minimum size of data you need before you get into `alt!`.
|
||||
///
|
||||
/// The `alt!` combinator is used in the following way:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// alt!(parser_1 | parser_2 | ... | parser_n)
|
||||
/// ```
|
||||
///
|
||||
/// # Basic example
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # fn main() {
|
||||
/// // Create a parser that will match either "dragon" or "beast"
|
||||
/// named!( dragon_or_beast, alt!( tag!( "dragon" ) | tag!( "beast" ) ) );
|
||||
///
|
||||
/// // Given the input "dragon slayer", the parser will match "dragon"
|
||||
/// // and the rest will be " slayer"
|
||||
/// let (rest, result) = dragon_or_beast(b"dragon slayer").unwrap();
|
||||
/// assert_eq!(result, b"dragon");
|
||||
/// assert_eq!(rest, b" slayer");
|
||||
///
|
||||
/// // Given the input "beast of Gevaudan", the parser will match "beast"
|
||||
/// // and the rest will be " of Gevaudan"
|
||||
/// let (rest, result) = dragon_or_beast(&b"beast of Gevaudan"[..]).unwrap();
|
||||
/// assert_eq!(result, b"beast");
|
||||
/// assert_eq!(rest, b" of Gevaudan");
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Manipulate results
|
||||
///
|
||||
/// There exists another syntax for `alt!` that gives you the ability to
|
||||
/// manipulate the result from each parser:
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # fn main() {
|
||||
/// #
|
||||
/// // We create an enum to represent our creatures
|
||||
/// #[derive(Debug,PartialEq,Eq)]
|
||||
/// enum Creature {
|
||||
/// Dragon,
|
||||
/// Beast,
|
||||
/// Unknown(usize)
|
||||
/// }
|
||||
///
|
||||
/// // Let's make a helper function that returns true when not a space
|
||||
/// // we are required to do this because the `take_while!` macro is limited
|
||||
/// // to idents, so we can't negate `ìs_space` at the call site
|
||||
/// fn is_not_space(c: u8) -> bool { ! nom::is_space(c) }
|
||||
///
|
||||
/// // Our parser will return the `Dragon` variant when matching "dragon",
|
||||
/// // the `Beast` variant when matching "beast" and otherwise it will consume
|
||||
/// // the input until a space is found and return an `Unknown` creature with
|
||||
/// // the size of it's name.
|
||||
/// named!(creature<Creature>, alt!(
|
||||
/// tag!("dragon") => { |_| Creature::Dragon } |
|
||||
/// tag!("beast") => { |_| Creature::Beast } |
|
||||
/// take_while!(is_not_space) => { |r: &[u8]| Creature::Unknown(r.len()) }
|
||||
/// // the closure takes the result as argument if the parser is successful
|
||||
/// ));
|
||||
///
|
||||
/// // Given the input "dragon slayer" the parser will return `Creature::Dragon`
|
||||
/// // and the rest will be " slayer"
|
||||
/// let (rest, result) = creature(b"dragon slayer").unwrap();
|
||||
/// assert_eq!(result, Creature::Dragon);
|
||||
/// assert_eq!(rest, b" slayer");
|
||||
///
|
||||
/// // Given the input "beast of Gevaudan" the parser will return `Creature::Beast`
|
||||
/// // and the rest will be " of Gevaudan"
|
||||
/// let (rest, result) = creature(b"beast of Gevaudan").unwrap();
|
||||
/// assert_eq!(result, Creature::Beast);
|
||||
/// assert_eq!(rest, b" of Gevaudan");
|
||||
///
|
||||
/// // Given the input "demon hunter" the parser will return `Creature::Unkown(5)`
|
||||
/// // and the rest will be " hunter"
|
||||
/// let (rest, result) = creature(b"demon hunter").unwrap();
|
||||
/// assert_eq!(result, Creature::Unknown(5));
|
||||
/// assert_eq!(rest, b" hunter");
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// # Behaviour of `alt!`
|
||||
///
|
||||
/// **BE CAREFUL** there is a case where the behaviour of `alt!` can be confusing:
|
||||
///
|
||||
/// when the alternatives have different lengths, like this case:
|
||||
///
|
||||
/// ```ignore
|
||||
/// named!( test, alt!( tag!( "abcd" ) | tag!( "ef" ) | tag!( "ghi" ) | tag!( "kl" ) ) );
|
||||
/// ```
|
||||
///
|
||||
/// With this parser, if you pass `"abcd"` as input, the first alternative parses it correctly,
|
||||
/// but if you pass `"efg"`, the first alternative will return `Incomplete`, since it needs an input
|
||||
/// of 4 bytes. This behaviour of `alt!` is expected: if you get a partial input that isn't matched
|
||||
/// by the first alternative, but would match if the input was complete, you want `alt!` to indicate
|
||||
/// that it cannot decide with limited information.
|
||||
///
|
||||
/// There are two ways to fix this behaviour. The first one consists in ordering the alternatives
|
||||
/// by size, like this:
|
||||
///
|
||||
/// ```ignore
|
||||
/// named!( test, alt!( tag!( "ef" ) | tag!( "kl") | tag!( "ghi" ) | tag!( "abcd" ) ) );
|
||||
/// ```
|
||||
///
|
||||
/// With this solution, the largest alternative will be tested last.
|
||||
///
|
||||
/// The other solution uses the `complete!` combinator, which transforms an `Incomplete` in an
|
||||
/// `Error`. If one of the alternatives returns `Incomplete` but is wrapped by `complete!`,
|
||||
/// `alt!` will try the next alternative. This is useful when you know that
|
||||
/// you will not get partial input:
|
||||
///
|
||||
/// ```ignore
|
||||
/// named!( test,
|
||||
/// alt!(
|
||||
/// complete!( tag!( "abcd" ) ) |
|
||||
/// complete!( tag!( "ef" ) ) |
|
||||
/// complete!( tag!( "ghi" ) ) |
|
||||
/// complete!( tag!( "kl" ) )
|
||||
/// )
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// If you want the `complete!` combinator to be applied to all rules then use the convenience
|
||||
/// `alt_complete!` macro (see below).
|
||||
///
|
||||
/// This behaviour of `alt!` can get especially confusing if multiple alternatives have different
|
||||
/// sizes but a common prefix, like this:
|
||||
///
|
||||
/// ```ignore
|
||||
/// named!( test, alt!( tag!( "abcd" ) | tag!( "ab" ) | tag!( "ef" ) ) );
|
||||
/// ```
|
||||
///
|
||||
/// in that case, if you order by size, passing `"abcd"` as input will always be matched by the
|
||||
/// smallest parser, so the solution using `complete!` is better suited.
|
||||
///
|
||||
/// You can also nest multiple `alt!`, like this:
|
||||
///
|
||||
/// ```ignore
|
||||
/// named!( test,
|
||||
/// alt!(
|
||||
/// preceded!(
|
||||
/// tag!("ab"),
|
||||
/// alt!(
|
||||
/// tag!( "cd" ) |
|
||||
/// eof!()
|
||||
/// )
|
||||
/// )
|
||||
/// | tag!( "ef" )
|
||||
/// )
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// `preceded!` will first parse `"ab"` then, if successful, try the alternatives "cd",
|
||||
/// or empty input (End Of File). If none of them work, `preceded!` will fail and
|
||||
/// "ef" will be tested.
|
||||
///
|
||||
#[macro_export]
|
||||
macro_rules! alt (
|
||||
(__impl $i:expr, $submac:ident!( $($args:tt)* ), $($rest:tt)* ) => (
|
||||
compiler_error!("alt uses '|' as separator, not ',':
|
||||
|
||||
alt!(
|
||||
tag!(\"abcd\") |
|
||||
tag!(\"efgh\") |
|
||||
tag!(\"ijkl\")
|
||||
)
|
||||
");
|
||||
);
|
||||
(__impl $i:expr, $e:ident, $($rest:tt)* ) => (
|
||||
alt!(__impl $i, call!($e) , $($rest)*);
|
||||
);
|
||||
(__impl $i:expr, $e:ident | $($rest:tt)*) => (
|
||||
alt!(__impl $i, call!($e) | $($rest)*);
|
||||
);
|
||||
|
||||
(__impl $i:expr, $subrule:ident!( $($args:tt)*) | $($rest:tt)*) => (
|
||||
{
|
||||
let i_ = $i.clone();
|
||||
let res = $subrule!(i_, $($args)*);
|
||||
match res {
|
||||
$crate::IResult::Done(_,_) => res,
|
||||
$crate::IResult::Incomplete(_) => res,
|
||||
$crate::IResult::Error(e) => {
|
||||
let out = alt!(__impl $i, $($rest)*);
|
||||
|
||||
// Compile-time hack to ensure that res's E type is not under-specified.
|
||||
// This all has no effect at runtime.
|
||||
fn unify_types<T>(_: &T, _: &T) {}
|
||||
if let $crate::IResult::Error(ref e2) = out {
|
||||
unify_types(&e, e2);
|
||||
}
|
||||
|
||||
out
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
(__impl $i:expr, $subrule:ident!( $($args:tt)* ) => { $gen:expr } | $($rest:tt)*) => (
|
||||
{
|
||||
let i_ = $i.clone();
|
||||
match $subrule!(i_, $($args)* ) {
|
||||
$crate::IResult::Done(i,o) => $crate::IResult::Done(i,$gen(o)),
|
||||
$crate::IResult::Incomplete(x) => $crate::IResult::Incomplete(x),
|
||||
$crate::IResult::Error(e) => {
|
||||
let out = alt!(__impl $i, $($rest)*);
|
||||
|
||||
// Compile-time hack to ensure that res's E type is not under-specified.
|
||||
// This all has no effect at runtime.
|
||||
fn unify_types<T>(_: &T, _: &T) {}
|
||||
if let $crate::IResult::Error(ref e2) = out {
|
||||
unify_types(&e, e2);
|
||||
}
|
||||
|
||||
out
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
(__impl $i:expr, $e:ident => { $gen:expr } | $($rest:tt)*) => (
|
||||
alt!(__impl $i, call!($e) => { $gen } | $($rest)*);
|
||||
);
|
||||
|
||||
(__impl $i:expr, __end) => (
|
||||
$crate::IResult::Error(error_position!($crate::ErrorKind::Alt,$i))
|
||||
);
|
||||
|
||||
($i:expr, $($rest:tt)*) => (
|
||||
{
|
||||
alt!(__impl $i, $($rest)* | __end)
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// Is equivalent to the `alt!` combinator, except that it will not return `Incomplete`
|
||||
/// when one of the constituting parsers returns `Incomplete`. Instead, it will try the
|
||||
/// next alternative in the chain.
|
||||
///
|
||||
/// You should use this combinator only if you know you
|
||||
/// will not receive partial input for the rules you're trying to match (this
|
||||
/// is almost always the case for parsing programming languages).
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// alt_complete!(I -> IResult<I,O> | I -> IResult<I,O> | ... | I -> IResult<I,O> ) => I -> IResult<I, O>
|
||||
/// ```
|
||||
/// All the parsers must have the same return type.
|
||||
///
|
||||
/// If one of the parsers return `Incomplete`, `alt_complete!` will try the next alternative.
|
||||
/// If there is no other parser left to try, an `Error` will be returned.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// alt_complete!(parser_1 | parser_2 | ... | parser_n)
|
||||
/// ```
|
||||
/// **For more in depth examples, refer to the documentation of `alt!`**
|
||||
#[macro_export]
|
||||
macro_rules! alt_complete (
|
||||
// Recursive rules (must include `complete!` around the head)
|
||||
|
||||
($i:expr, $e:ident | $($rest:tt)*) => (
|
||||
alt_complete!($i, complete!(call!($e)) | $($rest)*);
|
||||
);
|
||||
|
||||
($i:expr, $subrule:ident!( $($args:tt)*) | $($rest:tt)*) => (
|
||||
{
|
||||
let i_ = $i.clone();
|
||||
let res = complete!(i_, $subrule!($($args)*));
|
||||
match res {
|
||||
$crate::IResult::Done(_,_) => res,
|
||||
e => {
|
||||
let out = alt_complete!($i, $($rest)*);
|
||||
|
||||
if let (&$crate::IResult::Error(ref e1), &$crate::IResult::Error(ref e2)) = (&e, &out) {
|
||||
// Compile-time hack to ensure that res's E type is not under-specified.
|
||||
// This all has no effect at runtime.
|
||||
fn unify_types<T>(_: &T, _: &T) {}
|
||||
unify_types(e1, e2);
|
||||
}
|
||||
|
||||
out
|
||||
},
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
($i:expr, $subrule:ident!( $($args:tt)* ) => { $gen:expr } | $($rest:tt)+) => (
|
||||
{
|
||||
let i_ = $i.clone();
|
||||
match complete!(i_, $subrule!($($args)*)) {
|
||||
$crate::IResult::Done(i,o) => $crate::IResult::Done(i,$gen(o)),
|
||||
e => {
|
||||
let out = alt_complete!($i, $($rest)*);
|
||||
|
||||
if let (&$crate::IResult::Error(ref e1), &$crate::IResult::Error(ref e2)) = (&e, &out) {
|
||||
// Compile-time hack to ensure that res's E type is not under-specified.
|
||||
// This all has no effect at runtime.
|
||||
fn unify_types<T>(_: &T, _: &T) {}
|
||||
unify_types(e1, e2);
|
||||
}
|
||||
|
||||
out
|
||||
},
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
($i:expr, $e:ident => { $gen:expr } | $($rest:tt)*) => (
|
||||
alt_complete!($i, complete!(call!($e)) => { $gen } | $($rest)*);
|
||||
);
|
||||
|
||||
// Tail (non-recursive) rules
|
||||
|
||||
($i:expr, $e:ident => { $gen:expr }) => (
|
||||
alt_complete!($i, call!($e) => { $gen });
|
||||
);
|
||||
|
||||
($i:expr, $subrule:ident!( $($args:tt)* ) => { $gen:expr }) => (
|
||||
alt!(__impl $i, complete!($subrule!($($args)*)) => { $gen } | __end)
|
||||
);
|
||||
|
||||
($i:expr, $e:ident) => (
|
||||
alt_complete!($i, call!($e));
|
||||
);
|
||||
|
||||
($i:expr, $subrule:ident!( $($args:tt)*)) => (
|
||||
alt!(__impl $i, complete!($subrule!($($args)*)) | __end)
|
||||
);
|
||||
);
|
||||
|
||||
/// `switch!(I -> IResult<I,P>, P => I -> IResult<I,O> | ... | P => I -> IResult<I,O> ) => I -> IResult<I, O>`
|
||||
/// choose the next parser depending on the result of the first one, if successful,
|
||||
/// and returns the result of the second parser
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::{Done,Error};
|
||||
/// # #[cfg(feature = "verbose-errors")]
|
||||
/// # use nom::Err::{Position, NodePosition};
|
||||
/// # use nom::ErrorKind;
|
||||
/// # fn main() {
|
||||
/// named!(sw,
|
||||
/// switch!(take!(4),
|
||||
/// b"abcd" => tag!("XYZ") |
|
||||
/// b"efgh" => tag!("123")
|
||||
/// )
|
||||
/// );
|
||||
///
|
||||
/// let a = b"abcdXYZ123";
|
||||
/// let b = b"abcdef";
|
||||
/// let c = b"efgh123";
|
||||
/// let d = b"blah";
|
||||
///
|
||||
/// assert_eq!(sw(&a[..]), Done(&b"123"[..], &b"XYZ"[..]));
|
||||
/// assert_eq!(sw(&b[..]), Error(error_node_position!(ErrorKind::Switch, &b"abcdef"[..],
|
||||
/// error_position!(ErrorKind::Tag, &b"ef"[..]))));
|
||||
/// assert_eq!(sw(&c[..]), Done(&b""[..], &b"123"[..]));
|
||||
/// assert_eq!(sw(&d[..]), Error(error_position!(ErrorKind::Switch, &b"blah"[..])));
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// You can specify a default case like with a normal match, using `_`
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # fn main() {
|
||||
/// named!(sw,
|
||||
/// switch!(take!(4),
|
||||
/// b"abcd" => tag!("XYZ") |
|
||||
/// _ => value!(&b"default"[..])
|
||||
/// )
|
||||
/// );
|
||||
///
|
||||
/// let a = b"abcdXYZ123";
|
||||
/// let b = b"blah";
|
||||
///
|
||||
/// assert_eq!(sw(&a[..]), Done(&b"123"[..], &b"XYZ"[..]));
|
||||
/// assert_eq!(sw(&b[..]), Done(&b""[..], &b"default"[..]));
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Due to limitations in Rust macros, it is not possible to have simple functions on the right hand
|
||||
/// side of pattern, like this:
|
||||
///
|
||||
/// ```ignore
|
||||
/// named!(sw,
|
||||
/// switch!(take!(4),
|
||||
/// b"abcd" => tag!("XYZ") |
|
||||
/// b"efgh" => tag!("123")
|
||||
/// )
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// If you want to pass your own functions instead, you can use the `call!` combinator as follows:
|
||||
///
|
||||
/// ```ignore
|
||||
/// named!(xyz, tag!("XYZ"));
|
||||
/// named!(num, tag!("123"));
|
||||
/// named!(sw,
|
||||
/// switch!(take!(4),
|
||||
/// b"abcd" => call!(xyz) |
|
||||
/// b"efgh" => call!(num)
|
||||
/// )
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
#[macro_export]
|
||||
macro_rules! switch (
|
||||
(__impl $i:expr, $submac:ident!( $($args:tt)* ), $($p:pat => $subrule:ident!( $($args2:tt)* ))|* ) => (
|
||||
{
|
||||
let i_ = $i.clone();
|
||||
match map!(i_, $submac!($($args)*), |o| Some(o)) {
|
||||
$crate::IResult::Error(e) => $crate::IResult::Error(error_node_position!(
|
||||
$crate::ErrorKind::Switch, $i, e
|
||||
)),
|
||||
$crate::IResult::Incomplete(i) => $crate::IResult::Incomplete(i),
|
||||
$crate::IResult::Done(i, o) => {
|
||||
match o {
|
||||
$(Some($p) => match $subrule!(i, $($args2)*) {
|
||||
$crate::IResult::Error(e) => $crate::IResult::Error(error_node_position!(
|
||||
$crate::ErrorKind::Switch, $i, e
|
||||
)),
|
||||
a => a,
|
||||
}),*,
|
||||
_ => $crate::IResult::Error(error_position!($crate::ErrorKind::Switch,$i))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
($i:expr, $submac:ident!( $($args:tt)*), $($rest:tt)*) => (
|
||||
{
|
||||
switch!(__impl $i, $submac!($($args)*), $($rest)*)
|
||||
}
|
||||
);
|
||||
($i:expr, $e:ident, $($rest:tt)*) => (
|
||||
{
|
||||
switch!(__impl $i, call!($e), $($rest)*)
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
///
|
||||
///
|
||||
/// `permutation!(I -> IResult<I,A>, I -> IResult<I,B>, ... I -> IResult<I,X> ) => I -> IResult<I, (A,B,...X)>`
|
||||
/// applies its sub parsers in a sequence, but independent from their order
|
||||
/// this parser will only succeed if all of its sub parsers succeed
|
||||
///
|
||||
/// the tuple of results is in the same order as the parsers are declared
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::{Done,Error,Incomplete};
|
||||
/// # use nom::{ErrorKind,Needed};
|
||||
/// # fn main() {
|
||||
/// named!(perm<(&[u8], &[u8], &[u8])>,
|
||||
/// permutation!(tag!("abcd"), tag!("efg"), tag!("hi"))
|
||||
/// );
|
||||
///
|
||||
/// // whatever the order, if the parser succeeds, each
|
||||
/// // tag should have matched correctly
|
||||
/// let expected = (&b"abcd"[..], &b"efg"[..], &b"hi"[..]);
|
||||
///
|
||||
/// let a = &b"abcdefghijk"[..];
|
||||
/// assert_eq!(perm(a), Done(&b"jk"[..], expected));
|
||||
/// let b = &b"efgabcdhijkl"[..];
|
||||
/// assert_eq!(perm(b), Done(&b"jkl"[..], expected));
|
||||
/// let c = &b"hiefgabcdjklm"[..];
|
||||
/// assert_eq!(perm(c), Done(&b"jklm"[..], expected));
|
||||
///
|
||||
/// let d = &b"efgxyzabcdefghi"[..];
|
||||
/// assert_eq!(perm(d), Error(error_position!(ErrorKind::Permutation, &b"xyzabcdefghi"[..])));
|
||||
///
|
||||
/// let e = &b"efgabc"[..];
|
||||
/// assert_eq!(perm(e), Incomplete(Needed::Size(7)));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! permutation (
|
||||
($i:expr, $($rest:tt)*) => (
|
||||
{
|
||||
let mut res = permutation_init!((), $($rest)*);
|
||||
let mut input = $i;
|
||||
let mut error = ::std::option::Option::None;
|
||||
let mut needed = ::std::option::Option::None;
|
||||
|
||||
loop {
|
||||
let mut all_done = true;
|
||||
permutation_iterator!(0, input, all_done, needed, res, $($rest)*);
|
||||
|
||||
//if we reach that part, it means none of the parsers were able to read anything
|
||||
if !all_done {
|
||||
//FIXME: should wrap the error returned by the child parser
|
||||
error = ::std::option::Option::Some(error_position!($crate::ErrorKind::Permutation, input));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if let ::std::option::Option::Some(need) = needed {
|
||||
if let $crate::Needed::Size(sz) = need {
|
||||
$crate::IResult::Incomplete(
|
||||
$crate::Needed::Size(
|
||||
$crate::InputLength::input_len(&($i)) -
|
||||
$crate::InputLength::input_len(&input) +
|
||||
sz
|
||||
)
|
||||
)
|
||||
} else {
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown)
|
||||
}
|
||||
} else if let ::std::option::Option::Some(e) = error {
|
||||
$crate::IResult::Error(e)
|
||||
} else {
|
||||
let unwrapped_res = permutation_unwrap!(0, (), res, $($rest)*);
|
||||
$crate::IResult::Done(input, unwrapped_res)
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! permutation_init (
|
||||
((), $e:ident, $($rest:tt)*) => (
|
||||
permutation_init!((::std::option::Option::None), $($rest)*)
|
||||
);
|
||||
((), $submac:ident!( $($args:tt)* ), $($rest:tt)*) => (
|
||||
permutation_init!((::std::option::Option::None), $($rest)*)
|
||||
);
|
||||
(($($parsed:expr),*), $e:ident, $($rest:tt)*) => (
|
||||
permutation_init!(($($parsed),* , ::std::option::Option::None), $($rest)*);
|
||||
);
|
||||
(($($parsed:expr),*), $submac:ident!( $($args:tt)* ), $($rest:tt)*) => (
|
||||
permutation_init!(($($parsed),* , ::std::option::Option::None), $($rest)*);
|
||||
);
|
||||
(($($parsed:expr),*), $e:ident) => (
|
||||
($($parsed),* , ::std::option::Option::None)
|
||||
);
|
||||
(($($parsed:expr),*), $submac:ident!( $($args:tt)* )) => (
|
||||
($($parsed),* , ::std::option::Option::None)
|
||||
);
|
||||
(($($parsed:expr),*),) => (
|
||||
($($parsed),*)
|
||||
);
|
||||
);
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! succ (
|
||||
(0, $submac:ident ! ($($rest:tt)*)) => ($submac!(1, $($rest)*));
|
||||
(1, $submac:ident ! ($($rest:tt)*)) => ($submac!(2, $($rest)*));
|
||||
(2, $submac:ident ! ($($rest:tt)*)) => ($submac!(3, $($rest)*));
|
||||
(3, $submac:ident ! ($($rest:tt)*)) => ($submac!(4, $($rest)*));
|
||||
(4, $submac:ident ! ($($rest:tt)*)) => ($submac!(5, $($rest)*));
|
||||
(5, $submac:ident ! ($($rest:tt)*)) => ($submac!(6, $($rest)*));
|
||||
(6, $submac:ident ! ($($rest:tt)*)) => ($submac!(7, $($rest)*));
|
||||
(7, $submac:ident ! ($($rest:tt)*)) => ($submac!(8, $($rest)*));
|
||||
(8, $submac:ident ! ($($rest:tt)*)) => ($submac!(9, $($rest)*));
|
||||
(9, $submac:ident ! ($($rest:tt)*)) => ($submac!(10, $($rest)*));
|
||||
(10, $submac:ident ! ($($rest:tt)*)) => ($submac!(11, $($rest)*));
|
||||
(11, $submac:ident ! ($($rest:tt)*)) => ($submac!(12, $($rest)*));
|
||||
(12, $submac:ident ! ($($rest:tt)*)) => ($submac!(13, $($rest)*));
|
||||
(13, $submac:ident ! ($($rest:tt)*)) => ($submac!(14, $($rest)*));
|
||||
(14, $submac:ident ! ($($rest:tt)*)) => ($submac!(15, $($rest)*));
|
||||
(15, $submac:ident ! ($($rest:tt)*)) => ($submac!(16, $($rest)*));
|
||||
(16, $submac:ident ! ($($rest:tt)*)) => ($submac!(17, $($rest)*));
|
||||
(17, $submac:ident ! ($($rest:tt)*)) => ($submac!(18, $($rest)*));
|
||||
(18, $submac:ident ! ($($rest:tt)*)) => ($submac!(19, $($rest)*));
|
||||
(19, $submac:ident ! ($($rest:tt)*)) => ($submac!(20, $($rest)*));
|
||||
);
|
||||
|
||||
// HACK: for some reason, Rust 1.11 does not accept $res.$it in
|
||||
// permutation_unwrap. This is a bit ugly, but it will have no
|
||||
// impact on the generated code
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! acc (
|
||||
(0, $tup:expr) => ($tup.0);
|
||||
(1, $tup:expr) => ($tup.1);
|
||||
(2, $tup:expr) => ($tup.2);
|
||||
(3, $tup:expr) => ($tup.3);
|
||||
(4, $tup:expr) => ($tup.4);
|
||||
(5, $tup:expr) => ($tup.5);
|
||||
(6, $tup:expr) => ($tup.6);
|
||||
(7, $tup:expr) => ($tup.7);
|
||||
(8, $tup:expr) => ($tup.8);
|
||||
(9, $tup:expr) => ($tup.9);
|
||||
(10, $tup:expr) => ($tup.10);
|
||||
(11, $tup:expr) => ($tup.11);
|
||||
(12, $tup:expr) => ($tup.12);
|
||||
(13, $tup:expr) => ($tup.13);
|
||||
(14, $tup:expr) => ($tup.14);
|
||||
(15, $tup:expr) => ($tup.15);
|
||||
(16, $tup:expr) => ($tup.16);
|
||||
(17, $tup:expr) => ($tup.17);
|
||||
(18, $tup:expr) => ($tup.18);
|
||||
(19, $tup:expr) => ($tup.19);
|
||||
(20, $tup:expr) => ($tup.20);
|
||||
);
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! permutation_unwrap (
|
||||
($it:tt, (), $res:ident, $submac:ident!( $($args:tt)* ), $($rest:tt)*) => (
|
||||
succ!($it, permutation_unwrap!((acc!($it, $res).unwrap()), $res, $($rest)*));
|
||||
);
|
||||
($it:tt, ($($parsed:expr),*), $res:ident, $e:ident, $($rest:tt)*) => (
|
||||
succ!($it, permutation_unwrap!(($($parsed),* , acc!($it, $res).unwrap()), $res, $($rest)*));
|
||||
);
|
||||
($it:tt, ($($parsed:expr),*), $res:ident, $submac:ident!( $($args:tt)* ), $($rest:tt)*) => (
|
||||
succ!($it, permutation_unwrap!(($($parsed),* , acc!($it, $res).unwrap()), $res, $($rest)*));
|
||||
);
|
||||
($it:tt, ($($parsed:expr),*), $res:ident, $e:ident) => (
|
||||
($($parsed),* , { acc!($it, $res).unwrap() })
|
||||
);
|
||||
($it:tt, ($($parsed:expr),*), $res:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
($($parsed),* , acc!($it, $res).unwrap() )
|
||||
);
|
||||
);
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! permutation_iterator (
|
||||
($it:tt,$i:expr, $all_done:expr, $needed:expr, $res:expr, $e:ident, $($rest:tt)*) => (
|
||||
permutation_iterator!($it, $i, $all_done, $needed, $res, call!($e), $($rest)*);
|
||||
);
|
||||
($it:tt, $i:expr, $all_done:expr, $needed:expr, $res:expr, $submac:ident!( $($args:tt)* ), $($rest:tt)*) => {
|
||||
if acc!($it, $res) == ::std::option::Option::None {
|
||||
match $submac!($i, $($args)*) {
|
||||
$crate::IResult::Done(i,o) => {
|
||||
$i = i;
|
||||
acc!($it, $res) = ::std::option::Option::Some(o);
|
||||
continue;
|
||||
},
|
||||
$crate::IResult::Error(_) => {
|
||||
$all_done = false;
|
||||
},
|
||||
$crate::IResult::Incomplete(i) => {
|
||||
$needed = ::std::option::Option::Some(i);
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
succ!($it, permutation_iterator!($i, $all_done, $needed, $res, $($rest)*));
|
||||
};
|
||||
($it:tt,$i:expr, $all_done:expr, $needed:expr, $res:expr, $e:ident) => (
|
||||
permutation_iterator!($it, $i, $all_done, $res, call!($e));
|
||||
);
|
||||
($it:tt, $i:expr, $all_done:expr, $needed:expr, $res:expr, $submac:ident!( $($args:tt)* )) => {
|
||||
if acc!($it, $res) == ::std::option::Option::None {
|
||||
match $submac!($i, $($args)*) {
|
||||
$crate::IResult::Done(i,o) => {
|
||||
$i = i;
|
||||
acc!($it, $res) = ::std::option::Option::Some(o);
|
||||
continue;
|
||||
},
|
||||
$crate::IResult::Error(_) => {
|
||||
$all_done = false;
|
||||
},
|
||||
$crate::IResult::Incomplete(i) => {
|
||||
$needed = ::std::option::Option::Some(i);
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use internal::{Needed,IResult};
|
||||
use internal::IResult::*;
|
||||
use util::ErrorKind;
|
||||
|
||||
// reproduce the tag and take macros, because of module import order
|
||||
macro_rules! tag (
|
||||
($i:expr, $inp: expr) => (
|
||||
{
|
||||
#[inline(always)]
|
||||
fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
|
||||
b.as_bytes()
|
||||
}
|
||||
|
||||
let expected = $inp;
|
||||
let bytes = as_bytes(&expected);
|
||||
|
||||
tag_bytes!($i,bytes)
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
macro_rules! tag_bytes (
|
||||
($i:expr, $bytes: expr) => (
|
||||
{
|
||||
use std::cmp::min;
|
||||
let len = $i.len();
|
||||
let blen = $bytes.len();
|
||||
let m = min(len, blen);
|
||||
let reduced = &$i[..m];
|
||||
let b = &$bytes[..m];
|
||||
|
||||
let res: $crate::IResult<_,_> = if reduced != b {
|
||||
$crate::IResult::Error(error_position!($crate::ErrorKind::Tag, $i))
|
||||
} else if m < blen {
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(blen))
|
||||
} else {
|
||||
$crate::IResult::Done(&$i[blen..], reduced)
|
||||
};
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
macro_rules! take(
|
||||
($i:expr, $count:expr) => (
|
||||
{
|
||||
let cnt = $count as usize;
|
||||
let res:$crate::IResult<&[u8],&[u8]> = if $i.len() < cnt {
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(cnt))
|
||||
} else {
|
||||
$crate::IResult::Done(&$i[cnt..],&$i[0..cnt])
|
||||
};
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn alt() {
|
||||
fn work(input: &[u8]) -> IResult<&[u8],&[u8], &'static str> {
|
||||
Done(&b""[..], input)
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn dont_work(input: &[u8]) -> IResult<&[u8],&[u8],&'static str> {
|
||||
Error(error_code!(ErrorKind::Custom("abcd")))
|
||||
}
|
||||
|
||||
fn work2(input: &[u8]) -> IResult<&[u8],&[u8], &'static str> {
|
||||
Done(input, &b""[..])
|
||||
}
|
||||
|
||||
fn alt1(i:&[u8]) -> IResult<&[u8],&[u8], &'static str> {
|
||||
alt!(i, dont_work | dont_work)
|
||||
}
|
||||
fn alt2(i:&[u8]) -> IResult<&[u8],&[u8], &'static str> {
|
||||
alt!(i, dont_work | work)
|
||||
}
|
||||
fn alt3(i:&[u8]) -> IResult<&[u8],&[u8], &'static str> {
|
||||
alt!(i, dont_work | dont_work | work2 | dont_work)
|
||||
}
|
||||
//named!(alt1, alt!(dont_work | dont_work));
|
||||
//named!(alt2, alt!(dont_work | work));
|
||||
//named!(alt3, alt!(dont_work | dont_work | work2 | dont_work));
|
||||
|
||||
let a = &b"abcd"[..];
|
||||
assert_eq!(alt1(a), Error(error_position!(ErrorKind::Alt, a)));
|
||||
assert_eq!(alt2(a), Done(&b""[..], a));
|
||||
assert_eq!(alt3(a), Done(a, &b""[..]));
|
||||
|
||||
named!(alt4, alt!(tag!("abcd") | tag!("efgh")));
|
||||
let b = &b"efgh"[..];
|
||||
assert_eq!(alt4(a), Done(&b""[..], a));
|
||||
assert_eq!(alt4(b), Done(&b""[..], b));
|
||||
|
||||
// test the alternative syntax
|
||||
named!(alt5<bool>, alt!(tag!("abcd") => { |_| false } | tag!("efgh") => { |_| true }));
|
||||
assert_eq!(alt5(a), Done(&b""[..], false));
|
||||
assert_eq!(alt5(b), Done(&b""[..], true));
|
||||
|
||||
// compile-time test guarding against an underspecified E generic type (#474)
|
||||
named!(alt_eof1, alt!(eof!() | eof!()));
|
||||
named!(alt_eof2, alt!(eof!() => {|x| x} | eof!() => {|x| x}));
|
||||
let _ = (alt_eof1, alt_eof2);
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alt_incomplete() {
|
||||
named!(alt1, alt!(tag!("a") | tag!("bc") | tag!("def")));
|
||||
|
||||
let a = &b""[..];
|
||||
assert_eq!(alt1(a), Incomplete(Needed::Size(1)));
|
||||
let a = &b"b"[..];
|
||||
assert_eq!(alt1(a), Incomplete(Needed::Size(2)));
|
||||
let a = &b"bcd"[..];
|
||||
assert_eq!(alt1(a), Done(&b"d"[..], &b"bc"[..]));
|
||||
let a = &b"cde"[..];
|
||||
assert_eq!(alt1(a), Error(error_position!(ErrorKind::Alt, a)));
|
||||
let a = &b"de"[..];
|
||||
assert_eq!(alt1(a), Incomplete(Needed::Size(3)));
|
||||
let a = &b"defg"[..];
|
||||
assert_eq!(alt1(a), Done(&b"g"[..], &b"def"[..]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alt_complete() {
|
||||
named!(ac<&[u8], &[u8]>,
|
||||
alt_complete!(tag!("abcd") | tag!("ef") | tag!("ghi") | tag!("kl"))
|
||||
);
|
||||
|
||||
let a = &b""[..];
|
||||
assert_eq!(ac(a), Error(error_position!(ErrorKind::Alt, a)));
|
||||
let a = &b"ef"[..];
|
||||
assert_eq!(ac(a), Done(&b""[..], &b"ef"[..]));
|
||||
let a = &b"cde"[..];
|
||||
assert_eq!(ac(a), Error(error_position!(ErrorKind::Alt, a)));
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
#[test]
|
||||
fn switch() {
|
||||
named!(sw,
|
||||
switch!(take!(4),
|
||||
b"abcd" => take!(2) |
|
||||
b"efgh" => take!(4)
|
||||
)
|
||||
);
|
||||
|
||||
let a = &b"abcdefgh"[..];
|
||||
assert_eq!(sw(a), Done(&b"gh"[..], &b"ef"[..]));
|
||||
|
||||
let b = &b"efghijkl"[..];
|
||||
assert_eq!(sw(b), Done(&b""[..], &b"ijkl"[..]));
|
||||
let c = &b"afghijkl"[..];
|
||||
assert_eq!(sw(c), Error(error_position!(ErrorKind::Switch, &b"afghijkl"[..])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn permutation() {
|
||||
//trace_macros!(true);
|
||||
named!(perm<(&[u8], &[u8], &[u8])>,
|
||||
permutation!(tag!("abcd"), tag!("efg"), tag!("hi"))
|
||||
);
|
||||
//trace_macros!(false);
|
||||
|
||||
let expected = (&b"abcd"[..], &b"efg"[..], &b"hi"[..]);
|
||||
|
||||
let a = &b"abcdefghijk"[..];
|
||||
assert_eq!(perm(a), Done(&b"jk"[..], expected));
|
||||
let b = &b"efgabcdhijk"[..];
|
||||
assert_eq!(perm(b), Done(&b"jk"[..], expected));
|
||||
let c = &b"hiefgabcdjk"[..];
|
||||
assert_eq!(perm(c), Done(&b"jk"[..], expected));
|
||||
|
||||
let d = &b"efgxyzabcdefghi"[..];
|
||||
assert_eq!(perm(d), Error(error_position!(ErrorKind::Permutation, &b"xyzabcdefghi"[..])));
|
||||
|
||||
let e = &b"efgabc"[..];
|
||||
assert_eq!(perm(e), Incomplete(Needed::Size(7)));
|
||||
}
|
||||
|
||||
/*
|
||||
named!(does_not_compile,
|
||||
alt!(tag!("abcd"), tag!("efgh"))
|
||||
);
|
||||
*/
|
||||
}
|
1119
third_party/rust/nom-3.2.1/src/bytes.rs
vendored
1119
third_party/rust/nom-3.2.1/src/bytes.rs
vendored
File diff suppressed because it is too large
Load Diff
143
third_party/rust/nom-3.2.1/src/character.rs
vendored
143
third_party/rust/nom-3.2.1/src/character.rs
vendored
@ -1,143 +0,0 @@
|
||||
/// Character level parsers
|
||||
|
||||
use internal::{IResult,Needed};
|
||||
use traits::{AsChar,InputIter,InputLength,Slice};
|
||||
use std::ops::RangeFrom;
|
||||
|
||||
/// matches one of the provided characters
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult;
|
||||
/// # fn main() {
|
||||
/// named!(simple<char>, one_of!(&b"abc"[..]));
|
||||
/// assert_eq!(simple(b"a123"), IResult::Done(&b"123"[..], 'a'));
|
||||
///
|
||||
/// named!(a_or_b<&str, char>, one_of!("ab汉"));
|
||||
/// assert_eq!(a_or_b("汉jiosfe"), IResult::Done("jiosfe", '汉'));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! one_of (
|
||||
($i:expr, $inp: expr) => (
|
||||
{
|
||||
use $crate::Slice;
|
||||
use $crate::AsChar;
|
||||
use $crate::FindToken;
|
||||
use $crate::InputIter;
|
||||
|
||||
match ($i).iter_elements().next().map(|c| {
|
||||
(c, c.find_token($inp))
|
||||
}) {
|
||||
None => $crate::IResult::Incomplete::<_, _>($crate::Needed::Size(1)),
|
||||
Some((_, false)) => $crate::IResult::Error(error_position!($crate::ErrorKind::OneOf, $i)),
|
||||
//the unwrap should be safe here
|
||||
Some((c, true)) => $crate::IResult::Done($i.slice(c.len()..), $i.iter_elements().next().unwrap().as_char())
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// matches anything but the provided characters
|
||||
#[macro_export]
|
||||
macro_rules! none_of (
|
||||
($i:expr, $inp: expr) => (
|
||||
{
|
||||
use $crate::Slice;
|
||||
use $crate::AsChar;
|
||||
use $crate::FindToken;
|
||||
use $crate::InputIter;
|
||||
|
||||
match ($i).iter_elements().next().map(|c| {
|
||||
(c, !c.find_token($inp))
|
||||
}) {
|
||||
None => $crate::IResult::Incomplete::<_, _>($crate::Needed::Size(1)),
|
||||
Some((_, false)) => $crate::IResult::Error(error_position!($crate::ErrorKind::NoneOf, $i)),
|
||||
//the unwrap should be safe here
|
||||
Some((c, true)) => $crate::IResult::Done($i.slice(c.len()..), $i.iter_elements().next().unwrap().as_char())
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// matches one character: `char!(char) => &[u8] -> IResult<&[u8], char>
|
||||
#[macro_export]
|
||||
macro_rules! char (
|
||||
($i:expr, $c: expr) => (
|
||||
{
|
||||
use $crate::Slice;
|
||||
use $crate::AsChar;
|
||||
use $crate::InputIter;
|
||||
|
||||
match ($i).iter_elements().next().map(|c| {
|
||||
(c, c.as_char() == $c)
|
||||
}) {
|
||||
None => $crate::IResult::Incomplete::<_, _>($crate::Needed::Size(1)),
|
||||
Some((_, false)) => $crate::IResult::Error(error_position!($crate::ErrorKind::Char, $i)),
|
||||
//the unwrap should be safe here
|
||||
Some((c, true)) => $crate::IResult::Done($i.slice(c.len()..), $i.iter_elements().next().unwrap().as_char())
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
named!(#[doc="Matches a newline character '\\n'"], pub newline<char>, char!('\n'));
|
||||
|
||||
named!(#[doc="Matches a tab character '\\t'"], pub tab<char>, char!('\t'));
|
||||
|
||||
pub fn anychar<T>(input: T) -> IResult<T, char> where
|
||||
T: InputIter+InputLength+Slice<RangeFrom<usize>>,
|
||||
<T as InputIter>::Item: AsChar {
|
||||
if input.input_len() == 0 {
|
||||
IResult::Incomplete(Needed::Size(1))
|
||||
} else {
|
||||
IResult::Done(input.slice(1..), input.iter_elements().next().expect("slice should contain at least one element").as_char())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use internal::IResult::*;
|
||||
use util::ErrorKind;
|
||||
|
||||
#[test]
|
||||
fn one_of() {
|
||||
named!(f<char>, one_of!("ab"));
|
||||
|
||||
let a = &b"abcd"[..];
|
||||
assert_eq!(f(a), Done(&b"bcd"[..], 'a'));
|
||||
|
||||
let b = &b"cde"[..];
|
||||
assert_eq!(f(b), Error(error_position!(ErrorKind::OneOf, b)));
|
||||
|
||||
named!(utf8(&str) -> char,
|
||||
one_of!("+\u{FF0B}"));
|
||||
|
||||
assert!(utf8("+").is_done());
|
||||
assert!(utf8("\u{FF0B}").is_done());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn none_of() {
|
||||
named!(f<char>, none_of!("ab"));
|
||||
|
||||
let a = &b"abcd"[..];
|
||||
assert_eq!(f(a), Error(error_position!(ErrorKind::NoneOf, a)));
|
||||
|
||||
let b = &b"cde"[..];
|
||||
assert_eq!(f(b), Done(&b"de"[..], 'c'));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn char() {
|
||||
named!(f<char>, char!('c'));
|
||||
|
||||
let a = &b"abcd"[..];
|
||||
assert_eq!(f(a), Error(error_position!(ErrorKind::Char, a)));
|
||||
|
||||
let b = &b"cde"[..];
|
||||
assert_eq!(f(b), Done(&b"de"[..], 'c'));
|
||||
}
|
||||
|
||||
}
|
515
third_party/rust/nom-3.2.1/src/internal.rs
vendored
515
third_party/rust/nom-3.2.1/src/internal.rs
vendored
@ -1,515 +0,0 @@
|
||||
//! Basic types to build the parsers
|
||||
|
||||
use self::IResult::*;
|
||||
use self::Needed::*;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
use verbose_errors::Err;
|
||||
|
||||
#[cfg(not(feature = "verbose-errors"))]
|
||||
use simple_errors::Err;
|
||||
|
||||
/// Contains information on needed data if a parser returned `Incomplete`
|
||||
#[derive(Debug,PartialEq,Eq,Clone,Copy)]
|
||||
pub enum Needed {
|
||||
/// needs more data, but we do not know how much
|
||||
Unknown,
|
||||
/// contains the required data size
|
||||
Size(usize)
|
||||
}
|
||||
|
||||
impl Needed {
|
||||
pub fn is_known(&self) -> bool {
|
||||
*self != Unknown
|
||||
}
|
||||
|
||||
/// Maps a `Needed` to `Needed` by appling a function to a contained `Size` value.
|
||||
#[inline]
|
||||
pub fn map<F: FnOnce(usize) -> usize>(self, f: F) -> Needed {
|
||||
match self {
|
||||
Unknown => Unknown,
|
||||
Size(n) => Size(f(n)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
/// Holds the result of parsing functions
|
||||
///
|
||||
/// It depends on I, the input type, O, the output type, and E, the error type (by default u32)
|
||||
///
|
||||
/// Depending on a compilation flag, the content of the `Error` variant
|
||||
/// can change. By default, it will be a `ErrorKind<E=u32>` (with `E` configurable).
|
||||
///
|
||||
/// If you activate the `verbose-errors` compilation flags, it will be an
|
||||
/// enum that contains an error code, optionally, an input position,
|
||||
/// and an error sent by child parsers.
|
||||
///
|
||||
/// The verbose errors feature allows very flexible error management:
|
||||
/// you can know precisely which parser got to which part of the input.
|
||||
/// The main drawback is that it is a lot slower than default error
|
||||
/// management.
|
||||
///
|
||||
#[derive(Debug,PartialEq,Eq,Clone)]
|
||||
pub enum IResult<I,O,E=u32> {
|
||||
/// indicates a correct parsing, the first field containing the rest of the unparsed data, the second field contains the parsed data
|
||||
Done(I,O),
|
||||
/// contains a Err, an enum that can indicate an error code, a position in the input, and a pointer to another error, making a list of errors in the parsing tree
|
||||
Error(Err<I,E>),
|
||||
/// Incomplete contains a Needed, an enum than can represent a known quantity of input data, or unknown
|
||||
Incomplete(Needed)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "verbose-errors"))]
|
||||
/// Holds the result of parsing functions
|
||||
///
|
||||
/// It depends on I, the input type, O, the output type, and E, the error type (by default u32)
|
||||
///
|
||||
#[derive(Debug,PartialEq,Eq,Clone)]
|
||||
pub enum IResult<I,O,E=u32> {
|
||||
/// indicates a correct parsing, the first field containing the rest of the unparsed data, the second field contains the parsed data
|
||||
Done(I,O),
|
||||
/// contains a Err, an enum that can indicate an error code, a position in the input, and a pointer to another error, making a list of errors in the parsing tree
|
||||
Error(Err<E>),
|
||||
/// Incomplete contains a Needed, an enum than can represent a known quantity of input data, or unknown
|
||||
Incomplete(Needed)
|
||||
}
|
||||
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
/// This is the same as IResult, but without Done
|
||||
///
|
||||
/// This is used as the Error type when converting to std::result::Result
|
||||
#[derive(Debug,PartialEq,Eq,Clone)]
|
||||
pub enum IError<I,E=u32> {
|
||||
Error(Err<I,E>),
|
||||
Incomplete(Needed)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "verbose-errors"))]
|
||||
/// This is the same as IResult, but without Done
|
||||
///
|
||||
/// This is used as the Error type when converting to std::result::Result
|
||||
#[derive(Debug,PartialEq,Eq,Clone)]
|
||||
pub enum IError<E=u32> {
|
||||
Error(Err<E>),
|
||||
Incomplete(Needed)
|
||||
}
|
||||
|
||||
impl<I,O,E> IResult<I,O,E> {
|
||||
pub fn is_done(&self) -> bool {
|
||||
match *self {
|
||||
Done(_,_) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_err(&self) -> bool {
|
||||
match *self {
|
||||
Error(_) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_incomplete(&self) -> bool {
|
||||
match *self {
|
||||
Incomplete(_) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn or(self, other: IResult<I, O, E>) -> IResult<I, O, E> {
|
||||
if self.is_done() {
|
||||
self
|
||||
} else {
|
||||
other
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps a `IResult<I, O, E>` to `IResult<I, N, E>` by appling a function
|
||||
/// to a contained `Done` value, leaving `Error` and `Incomplete` value
|
||||
/// untouched.
|
||||
#[inline]
|
||||
pub fn map<N, F: FnOnce(O) -> N>(self, f: F) -> IResult<I, N, E> {
|
||||
match self {
|
||||
Done(i, o) => Done(i, f(o)),
|
||||
Error(e) => Error(e),
|
||||
Incomplete(n) => Incomplete(n),
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps a `IResult<I, O, E>` to `IResult<I, O, E>` by appling a function
|
||||
/// to a contained `Incomplete` value, leaving `Done` and `Error` value
|
||||
/// untouched.
|
||||
#[inline]
|
||||
pub fn map_inc<F>(self, f: F) -> IResult<I, O, E>
|
||||
where F: FnOnce(Needed) -> Needed {
|
||||
match self {
|
||||
Error(e) => Error(e),
|
||||
Incomplete(n) => Incomplete(f(n)),
|
||||
Done(i, o) => Done(i, o),
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwrap the contained `Done(I, O)` value, or panic if the `IResult` is not
|
||||
/// `Done`.
|
||||
pub fn unwrap(self) -> (I, O) {
|
||||
match self {
|
||||
Done(i, o) => (i, o),
|
||||
Incomplete(_) => panic!("unwrap() called on an IResult that is Incomplete"),
|
||||
Error(_) => panic!("unwrap() called on an IResult that is Error")
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwrap the contained `Done(I, O)` value or a default if the `IResult` is not
|
||||
/// `Done`.
|
||||
pub fn unwrap_or(self, default: (I, O)) -> (I, O) {
|
||||
match self {
|
||||
Done(i, o) => (i, o),
|
||||
Incomplete(_) => default,
|
||||
Error(_) => default
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwrap the contained `Incomplete(n)` value, or panic if the `IResult` is not
|
||||
/// `Incomplete`.
|
||||
pub fn unwrap_inc(self) -> Needed {
|
||||
match self {
|
||||
Incomplete(n) => n,
|
||||
Done(_, _) => panic!("unwrap_inc() called on an IResult that is Done"),
|
||||
Error(_) => panic!("unwrap_inc() called on an IResult that is Error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GetInput<I> {
|
||||
fn remaining_input(&self) -> Option<I>;
|
||||
}
|
||||
|
||||
pub trait GetOutput<O> {
|
||||
fn output(&self) -> Option<O>;
|
||||
}
|
||||
|
||||
impl<'a,I,O,E> GetInput<&'a[I]> for IResult<&'a[I],O,E> {
|
||||
fn remaining_input(&self) -> Option<&'a[I]> {
|
||||
match *self {
|
||||
Done(ref i,_) => Some(*i),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<O,E> GetInput<()> for IResult<(),O,E> {
|
||||
fn remaining_input(&self) -> Option<()> {
|
||||
match *self {
|
||||
Done((),_) => Some(()),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,O,E> GetInput<&'a str> for IResult<&'a str,O,E> {
|
||||
fn remaining_input(&self) -> Option<&'a str> {
|
||||
match *self {
|
||||
Done(ref i,_) => Some(*i),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,I,O,E> GetOutput<&'a[O]> for IResult<I,&'a[O],E> {
|
||||
fn output(&self) -> Option<&'a[O]> {
|
||||
match *self {
|
||||
Done(_, ref o) => Some(*o),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I,E> GetOutput<()> for IResult<I,(),E> {
|
||||
fn output(&self) -> Option<()> {
|
||||
match *self {
|
||||
Done(_,()) => Some(()),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,I,E> GetOutput<&'a str> for IResult<I,&'a str,E> {
|
||||
fn output(&self) -> Option<&'a str> {
|
||||
match *self {
|
||||
Done(_,ref o) => Some(*o),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
/// creates a parse error from a `nom::ErrorKind`
|
||||
#[macro_export]
|
||||
macro_rules! error_code(
|
||||
($code:expr) => ($crate::Err::Code($code));
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "verbose-errors"))]
|
||||
/// creates a parse error from a `nom::ErrorKind`
|
||||
#[macro_export]
|
||||
macro_rules! error_code(
|
||||
($code:expr) => ($code);
|
||||
);
|
||||
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
/// creates a parse error from a `nom::ErrorKind`
|
||||
/// and the next error in the parsing tree.
|
||||
/// if "verbose-errors" is not activated,
|
||||
/// it default to only the error code
|
||||
#[macro_export]
|
||||
macro_rules! error_node(
|
||||
($code:expr, $next:expr) => {
|
||||
let next_errors = match $next {
|
||||
$crate::Err::Code(e) => {
|
||||
let mut v = ::std::vec::Vec::new();
|
||||
v.push($crate::Err::Code(e));
|
||||
v
|
||||
},
|
||||
$crate::Err::Position(e, p) => {
|
||||
let mut v = ::std::vec::Vec::new();
|
||||
v.push($crate::Err::Position(e,p));
|
||||
v
|
||||
},
|
||||
$crate::Err::Node(e, mut next) => {
|
||||
next.push($crate::Err::Code(e));
|
||||
next
|
||||
},
|
||||
$crate::Err::NodePosition(e, p, mut next) => {
|
||||
next.push($crate::Err::Position(e,p));
|
||||
next
|
||||
},
|
||||
};
|
||||
$crate::Err::Node($code, next_errors)
|
||||
};
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "verbose-errors"))]
|
||||
/// creates a parse error from a `nom::ErrorKind`
|
||||
/// and the next error in the parsing tree.
|
||||
/// if "verbose-errors" is not activated,
|
||||
/// it default to only the error code
|
||||
#[allow(unused_variables)]
|
||||
#[macro_export]
|
||||
macro_rules! error_node(
|
||||
($code:expr, $next:expr) => ($code);
|
||||
);
|
||||
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
/// creates a parse error from a `nom::ErrorKind`
|
||||
/// and the position in the input
|
||||
/// if "verbose-errors" is not activated,
|
||||
/// it default to only the error code
|
||||
#[macro_export]
|
||||
macro_rules! error_position(
|
||||
($code:expr, $input:expr) => ($crate::Err::Position($code, $input));
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "verbose-errors"))]
|
||||
/// creates a parse error from a `nom::ErrorKind`
|
||||
/// and the position in the input
|
||||
/// if "verbose-errors" is not activated,
|
||||
/// it default to only the error code
|
||||
#[allow(unused_variables)]
|
||||
#[macro_export]
|
||||
macro_rules! error_position(
|
||||
($code:expr, $input:expr) => ($code);
|
||||
);
|
||||
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
/// creates a parse error from a `nom::ErrorKind`,
|
||||
/// the position in the input and the next error in
|
||||
/// the parsing tree.
|
||||
/// if "verbose-errors" is not activated,
|
||||
/// it default to only the error code
|
||||
#[macro_export]
|
||||
macro_rules! error_node_position(
|
||||
($code:expr, $input:expr, $next:expr) => {
|
||||
{
|
||||
let next_errors = match $next {
|
||||
$crate::Err::Code(e) => {
|
||||
let mut v = ::std::vec::Vec::new();
|
||||
v.push($crate::Err::Code(e));
|
||||
v
|
||||
},
|
||||
$crate::Err::Position(e, p) => {
|
||||
let mut v = ::std::vec::Vec::new();
|
||||
v.push($crate::Err::Position(e,p));
|
||||
v
|
||||
},
|
||||
$crate::Err::Node(e, mut next) => {
|
||||
next.push($crate::Err::Code(e));
|
||||
next
|
||||
},
|
||||
$crate::Err::NodePosition(e, p, mut next) => {
|
||||
next.push($crate::Err::Position(e,p));
|
||||
next
|
||||
}
|
||||
};
|
||||
$crate::Err::NodePosition($code, $input, next_errors)
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "verbose-errors"))]
|
||||
/// creates a parse error from a `nom::ErrorKind`,
|
||||
/// the position in the input and the next error in
|
||||
/// the parsing tree.
|
||||
/// if "verbose-errors" is not activated,
|
||||
/// it default to only the error code
|
||||
#[allow(unused_variables)]
|
||||
#[macro_export]
|
||||
macro_rules! error_node_position(
|
||||
($code:expr, $input: expr, $next:expr) => ($code);
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use util::ErrorKind;
|
||||
|
||||
const REST: [u8; 0] = [];
|
||||
const DONE: IResult<&'static [u8], u32> = IResult::Done(&REST, 5);
|
||||
const ERROR: IResult<&'static [u8], u32> = IResult::Error(error_code!(ErrorKind::Tag));
|
||||
const INCOMPLETE: IResult<&'static [u8], u32> = IResult::Incomplete(Needed::Unknown);
|
||||
|
||||
#[test]
|
||||
fn iresult_or() {
|
||||
assert_eq!(DONE.or(ERROR), DONE);
|
||||
assert_eq!(ERROR.or(DONE), DONE);
|
||||
assert_eq!(INCOMPLETE.or(ERROR), ERROR);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn needed_map() {
|
||||
let unknown = Needed::Unknown;
|
||||
let size = Needed::Size(5);
|
||||
|
||||
assert_eq!(size.map(|x| x * 2), Needed::Size(10));
|
||||
assert_eq!(unknown.map(|x| x * 2), Needed::Unknown);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iresult_map() {
|
||||
assert_eq!(DONE.map(|x| x * 2), IResult::Done(&b""[..], 10));
|
||||
assert_eq!(ERROR.map(|x| x * 2), IResult::Error(error_code!(ErrorKind::Tag)));
|
||||
assert_eq!(INCOMPLETE.map(|x| x * 2), IResult::Incomplete(Needed::Unknown));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iresult_map_inc() {
|
||||
let inc_unknown: IResult<&[u8], u32> = IResult::Incomplete(Needed::Unknown);
|
||||
let inc_size: IResult<&[u8], u32> = IResult::Incomplete(Needed::Size(5));
|
||||
|
||||
assert_eq!(DONE.map_inc(|n| if let Needed::Size(i) = n {Needed::Size(i+1)} else {n}), IResult::Done(&b""[..], 5));
|
||||
assert_eq!(ERROR.map_inc(|n| if let Needed::Size(i) = n {Needed::Size(i+1)} else {n}), IResult::Error(error_code!(ErrorKind::Tag)));
|
||||
assert_eq!(inc_unknown.map_inc(|n| if let Needed::Size(i) = n {Needed::Size(i+1)} else {n}), IResult::Incomplete(Needed::Unknown));
|
||||
assert_eq!(inc_size.map_inc(|n| if let Needed::Size(i) = n {Needed::Size(i+1)} else {n}), IResult::Incomplete(Needed::Size(6)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "std")]
|
||||
fn iresult_map_err() {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
struct Error(u32);
|
||||
|
||||
let error_kind = error_code!(ErrorKind::Custom(Error(5)));
|
||||
|
||||
assert_eq!(DONE.map_err(|_| error_kind.clone()), IResult::Done(&b""[..], 5));
|
||||
assert_eq!(ERROR.map_err(|x| {println!("err: {:?}", x); error_kind.clone()}), IResult::Error(error_kind.clone()));
|
||||
assert_eq!(INCOMPLETE.map_err(|x| {println!("err: {:?}", x); error_kind.clone()}), IResult::Incomplete(Needed::Unknown));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iresult_unwrap_on_done() {
|
||||
assert_eq!(DONE.unwrap(), (&b""[..], 5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn iresult_unwrap_on_err() {
|
||||
ERROR.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn iresult_unwrap_on_inc() {
|
||||
INCOMPLETE.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iresult_unwrap_or_on_done() {
|
||||
assert_eq!(DONE.unwrap_or((&b""[..], 2)), (&b""[..], 5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iresult_unwrap_or_on_err() {
|
||||
assert_eq!(ERROR.unwrap_or((&b""[..], 2)), (&b""[..], 2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iresult_unwrap_or_on_inc() {
|
||||
assert_eq!(INCOMPLETE.unwrap_or((&b""[..], 2)), (&b""[..], 2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn iresult_unwrap_err_on_done() {
|
||||
DONE.unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iresult_unwrap_err_on_err() {
|
||||
assert_eq!(ERROR.unwrap_err(), error_code!(ErrorKind::Tag));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn iresult_unwrap_err_on_inc() {
|
||||
INCOMPLETE.unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn iresult_unwrap_inc_on_done() {
|
||||
DONE.unwrap_inc();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn iresult_unwrap_inc_on_err() {
|
||||
ERROR.unwrap_inc();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iresult_unwrap_inc_on_inc() {
|
||||
assert_eq!(INCOMPLETE.unwrap_inc(), Needed::Unknown);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iresult_to_result() {
|
||||
assert_eq!(DONE.to_result(), Ok(5));
|
||||
assert_eq!(ERROR.to_result(), Err(error_code!(ErrorKind::Tag)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn iresult_to_result_on_incomplete() {
|
||||
INCOMPLETE.to_result().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iresult_to_full_result() {
|
||||
assert_eq!(DONE.to_full_result(), Ok(5));
|
||||
assert_eq!(INCOMPLETE.to_full_result(), Err(IError::Incomplete(Needed::Unknown)));
|
||||
assert_eq!(ERROR.to_full_result(), Err(IError::Error(error_code!(ErrorKind::Tag))));
|
||||
}
|
||||
}
|
491
third_party/rust/nom-3.2.1/src/lib.rs
vendored
491
third_party/rust/nom-3.2.1/src/lib.rs
vendored
@ -1,491 +0,0 @@
|
||||
//! nom, eating data byte by byte
|
||||
//!
|
||||
//! nom is a parser combinator library with a focus on safe parsing,
|
||||
//! streaming patterns, and as much as possible zero copy.
|
||||
//!
|
||||
//! The code is available on [Github](https://github.com/Geal/nom)
|
||||
//!
|
||||
//! There are a few [guides](http://rust.unhandledexpression.com/nom/home.html) with more details
|
||||
//! about [the design of nom](http://rust.unhandledexpression.com/nom/how_nom_macros_work.html),
|
||||
//! [how to write parsers](http://rust.unhandledexpression.com/nom/making_a_new_parser_from_scratch.html),
|
||||
//! or the [error management system](http://rust.unhandledexpression.com/nom/error_management.html).
|
||||
//!
|
||||
//! If you are upgrading to nom 2.0, please read the
|
||||
//! [migration document](http://rust.unhandledexpression.com/nom/upgrading_to_nom_2.html).
|
||||
//!
|
||||
//! See also the [FAQ](http://rust.unhandledexpression.com/nom/FAQ.html).
|
||||
//!
|
||||
//! # What are parser combinators?
|
||||
//!
|
||||
//! Parser combinators are a way to build parsers out of small functions. instead of
|
||||
//! writing a huge grammar file then generaing code, like you would do with lex and yacc,
|
||||
//! you write small functions, to parse small things like a character, or a number,
|
||||
//! and then you assemble them in larger and larger functions, that can parse larger
|
||||
//! parts of your formats.
|
||||
//!
|
||||
//! You end up with a list of small functions that you can reuse everywhere you need. Each
|
||||
//! of them can be unit tested anf fuzzed separately.
|
||||
//!
|
||||
//! # nom parser design
|
||||
//!
|
||||
//! All nom parsers follow the same convention. They are all functions with the following signature:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! fn parser(input: I) -> IResult<I,O> { ... }
|
||||
//! ```
|
||||
//!
|
||||
//! Here is the definition of that `IResult` type:
|
||||
//!
|
||||
//! ```
|
||||
//! # #[macro_use] extern crate nom;
|
||||
//! # use nom::{Err,Needed};
|
||||
//! # fn main() {}
|
||||
//! pub enum IResult<I,O,E=u32> {
|
||||
//! Done(I,O),
|
||||
//! Error(Err<E>), // indicates the parser encountered an error. E is a custom error type you can redefine
|
||||
//! /// Incomplete contains a Needed, an enum than can represent a known quantity of input data, or unknown
|
||||
//! Incomplete(Needed) // if the parser did not have enough data to decide
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! What it means:
|
||||
//!
|
||||
//! * `Done(i,o)` means the parser was successful. `i` is the remaining part of the input, `o` is the correctly parsed value
|
||||
//! The remaining part can then be used as input for other parsers called in a sequence
|
||||
//! * `Error(e)` indicates the parser encountered an error. The `Err<E>` type is an enum of possible parser errors,
|
||||
//! that can also contain a custom error that you'd specify, by redefining the `E` error type
|
||||
//! * `Incomplete(i)` means the parser did not have enough information to decide, and tells you, if possible,
|
||||
//! how much data it needs
|
||||
//!
|
||||
//! That way, you could write your own parser that recognizes the letter 'a' like this:
|
||||
//!
|
||||
//! ```
|
||||
//! #[macro_use] extern crate nom;
|
||||
//! use nom::{IResult,Needed,Err,ErrorKind};
|
||||
//! # fn main() {}
|
||||
//!
|
||||
//! fn a(input: &[u8]) -> IResult<&[u8], char> {
|
||||
//! // if there is not enough data, we return Ìncomplete
|
||||
//! if input.len() == 0 {
|
||||
//! IResult::Incomplete(Needed::Size(1))
|
||||
//! } else {
|
||||
//! if input[0] == 'a' as u8 {
|
||||
//! // the first part of the returned value is the remaining slice
|
||||
//! IResult::Done(&input[1..], 'a')
|
||||
//! } else {
|
||||
//! IResult::Error(error_code!(ErrorKind::Custom(42)))
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Writing all the parsers manually, like this, is dangerous, despite Rust's safety features. There
|
||||
//! are still a lot of mistakes one can make. That's why nom provides a list of macros to help in
|
||||
//! developing parsers. As an example, here is a parser that would recognize the phrase
|
||||
//! "Hello <someone>" and return the name of the person we hail:
|
||||
//!
|
||||
//! ```
|
||||
//! #[macro_use] extern crate nom;
|
||||
//! use nom::alpha;
|
||||
//!
|
||||
//! named!(hello, preceded!(tag!("Hello "), alpha));
|
||||
//! # use nom::IResult;
|
||||
//! # fn main() {
|
||||
//! # assert_eq!(hello(b"Hello nom."), IResult::Done(&b"."[..], &b"nom"[..]));
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! Let's deconstruct it:
|
||||
//!
|
||||
//! * `named!` generates a function with the correct type. Without `named` here, we could write the parser
|
||||
//! as follows:
|
||||
//!
|
||||
//! ```
|
||||
//! #[macro_use] extern crate nom;
|
||||
//! use nom::{alpha,IResult};
|
||||
//!
|
||||
//! fn hello(input: &[u8]) -> IResult<&[u8], &[u8]> {
|
||||
//! preceded!(input,
|
||||
//! tag!("Hello "), alpha)
|
||||
//! }
|
||||
//! # fn main() {
|
||||
//! # assert_eq!(hello(b"Hello nom."), IResult::Done(&b"."[..], &b"nom"[..]));
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! By default, `named` makes a function that takes `&[u8]` as input type, and returns `&[u8]` as output type.
|
||||
//! You can override it like this:
|
||||
//!
|
||||
//! * `named!(hello<&str>, ...):` would take `&[u8]` as input type, and return `&str` as output type.
|
||||
//! * `named!(hello<&str, &str>, ...):` would take `&str` as input type, and return `&str` as output type.
|
||||
//!
|
||||
//! *Note* : when we don't use `named!`, we must pass the input as first argument of the top
|
||||
//! level combinator (see the line `preceded!(input,` in the preceding code example). This is a macro trick
|
||||
//! in nom to pass input from one combinator to the next by rewriting the call.
|
||||
//!
|
||||
//! Next part of the parser: `preceded!(tag!("Hello "), alpha))`. Here, `tag!` is a combinator that recognizes
|
||||
//! a specific serie of bytes or characters. `alpha` is a function that recognizes alphabetical characters.
|
||||
//! The `preceded!` combinator assembles them in a more complex parser: if both parsers are successful,
|
||||
//! it returns the result of the second one (`alpha` is preceded by `tag!`).
|
||||
//!
|
||||
//! *Note* : combinators can assemble other combinators (macros), or parser functions, as long as they follow
|
||||
//! the same interface. Here, `alpha` is a parser function already implemented in nom.
|
||||
//!
|
||||
//! # List of parsers and combinators
|
||||
//!
|
||||
//! ## Basic elements
|
||||
//!
|
||||
//! Those are used to recognize the lowest level elements of your grammar, like, "here is a dot", or
|
||||
//! "here is an big endian integer".
|
||||
//!
|
||||
//! * **char!**: matches one character: `char!('a')` will make a parser that recognizes the letter 'a' (works with non ASCII chars too)
|
||||
//! * **eof!**: `eof!()` returns its input if it is at the end of input data
|
||||
//! * **is_a!, is_a_s!**: matches a sequence of any of the characters passed as arguments. `is_a!("ab1")` could recognize `ababa` or `1bb`. `is_a_s!` is a legacy combinator, it does exactly the same thing as `is_a`
|
||||
//! * **is_not!, is_not_s!**: matches a sequence of none of the characters passed as arguments
|
||||
//! * **one_of!**: matches one of the provided characters. `one_of!("abc")` could recognize 'a', 'b', or 'c'. It also works with non ASCII characters
|
||||
//! * **none_of!**: matches anything but the provided characters
|
||||
//! * **tag!, tag_s!**: recognizes a specific suite of characters or bytes. `tag!("hello")` matches "hello"
|
||||
//! * **tag_no_case!**: recognizes a suite of ASCII characters, case insensitive. `tag_no_case!("hello")` could match "hello", "Hello" or even "HeLlO"
|
||||
//! * **tag_no_case_s!** works like `tag_no_case` but on UTF-8 characters too (uses `&str` as input). Note that case insensitive comparison is not well defined for unicode, and that you might have bad surprises. Also, this combinator allocates a new string for the comparison. Ponder for a bit before using this combinator
|
||||
//! * **take!, take_s!**: takes a specific number of bytes or characters. `take!(5)` would return "hello" from the string "hello world"
|
||||
//! * **take_str!**: same as `take!` but returning a `&str`
|
||||
//! * **take_till!, take_till_s!**: returns the longest list of bytes until the provided function succeeds. `take_till!(is_alphabetic)` with input "123abc" would return "123"
|
||||
//! * **take_till1!, take_till1_s!**: same as `take_till!`, but the result must not be empty: `take_till1!(is_alphabetic)` would fail on "abc"
|
||||
//! * **take_until!, take_until_s!**: returns the longest list of bytes until the provided tag is found. `take_until!("world")` with input "Hello world!" would return "Hello " and leave "world!" as remaining input
|
||||
//! * **take_until1!**: same as `take_until!`, but cannot return an empty result
|
||||
//! * **take_until_and_consume!, take_until_and_consume_s!**: same as `take_until!` but consumes the tag. `take_until_and_consume!("world")` with input "Hello world!" would return "Hello " and leave "!" as remaining input
|
||||
//! * **take_until_and_consume1!**: same as `take_until_and_consume!`, but cannot return an empty result
|
||||
//! * **take_until_either!**: returns the longest list of bytes until any of the provided characters are found
|
||||
//! * **take_until_either_and_consume!**: same as `take_until_either!`, but consumes the terminating character
|
||||
//! * **take_while!, take_while_s!**: returns the longest list of bytes for which the function is true. `take_while!(is_alphabetic)` with input "abc123" would return "abc"
|
||||
//! * **take_while1!, take_while1_s!**: same as `take_while!`, but cannot return an empty result
|
||||
//! * **value!**: you can use `value!` to always return the same result value without consuming input, like this: `value!(42)`. Or you can replace the result of a child parser with a predefined value, like this: `value!(42, tag!("abcd"))` which would replace, if successful, the return value from "abcd", to 42
|
||||
//!
|
||||
//! Parsing integers from binary formats can be done in two ways: with parser functions, or combinators with configurable endianness:
|
||||
//!
|
||||
//! * configurable endianness: **i16!, i32!, i64!, u16!, u32!, u64!** are combinators that take as argument a `nom::Endianness`,
|
||||
//! like this: `i16!(endianness)`. If the parameter is nom::Endianness::Big, parse a big endian i16 integer, otherwise a little endian i16 integer
|
||||
//! * fixed endianness: the functions are prefixed by "be_" for big endian numbers, and by "le_" for little endian numbers, and the suffix is the type they parse to. As an example, "be_u32" parses a big endian unsigned integer stored in 32 bits.
|
||||
//! * **be_f32, be_f64, le_f32, le_f64**: recognize floating point numbers
|
||||
//! * **be_i8, be_i16, be_i32, be_i24, be_i32, be_i64**: big endian signed integers
|
||||
//! * **be_u8, be_u16, be_u32, be_u24, be_u32, be_u64**: big endian unsigned integers
|
||||
//! * **le_i8, le_i16, le_i32, le_i24, le_i32, le_i64**: little endian signed integers
|
||||
//! * **le_u8, le_u16, le_u32, le_u24, le_u32, le_u64**: little endian unsigned integers
|
||||
//!
|
||||
//! ## Modifiers
|
||||
//!
|
||||
//! * **complete!**: replaces a Incomplete returned by the child parser with an Error
|
||||
//! * **cond!**: conditional combinator
|
||||
//! * **cond_reduce!**: Conditional combinator with error
|
||||
//! * **cond_with_error!**: Conditional combinator
|
||||
//! * **expr_opt!**: evaluates an expression that returns a Option and returns a IResult::Done(I,T) if Some
|
||||
//! * **expr_res!**: evaluates an expression that returns a Result and returns a IResult::Done(I,T) if Ok
|
||||
//! * **flat_map!**:
|
||||
//! * **map!**: maps a function on the result of a parser
|
||||
//! * **map_opt!**: maps a function returning an Option on the output of a parser
|
||||
//! * **map_res!**: maps a function returning a Result on the output of a parser
|
||||
//! * **not!**: returns a result only if the embedded parser returns Error or Incomplete does not consume the input
|
||||
//! * **opt!**: make the underlying parser optional
|
||||
//! * **opt_res!**: make the underlying parser optional
|
||||
//! * **parse_to!**: uses the parse method from std::str::FromStr to convert the current input to the specified type
|
||||
//! * **peek!**: returns a result without consuming the input
|
||||
//! * **recognize!**: if the child parser was successful, return the consumed input as produced value
|
||||
//! * **return_error!**: prevents backtracking if the child parser fails
|
||||
//! * **tap!**: allows access to the parser's result without affecting it
|
||||
//! * **verify!**: returns the result of the child parser if it satisfies a verification function
|
||||
//!
|
||||
//! ## Error management and debugging
|
||||
//!
|
||||
//! * **add_return_error!**: Add an error if the child parser fails
|
||||
//! * **dbg!**: Prints a message if the parser fails
|
||||
//! * **dbg_dmp!**: Prints a message and the input if the parser fails
|
||||
//! * **error_code!**: creates a parse error from a nom::ErrorKind
|
||||
//! * **error_node!**: creates a parse error from a nom::ErrorKind and the next error in the parsing tree. if "verbose-errors" is not activated, it default to only the error code
|
||||
//! * **error_node_position!**: creates a parse error from a nom::ErrorKind, the position in the input and the next error in the parsing tree. if "verbose-errors" is not activated, it default to only the error code
|
||||
//! * **error_position!**: creates a parse error from a nom::ErrorKind and the position in the input if "verbose-errors" is not activated, it default to only the error code
|
||||
//! * **fix_error!**: translate parser result from IResult to IResult with a custom type
|
||||
//!
|
||||
//! ## Choice combinators
|
||||
//!
|
||||
//! * **alt!**: try a list of parsers and return the result of the first successful one
|
||||
//! * **alt_complete!**: is equivalent to the alt! combinator, except that it will not return Incomplete when one of the constituting parsers returns Incomplete. Instead, it will try the next alternative in the chain.
|
||||
//! * **switch!**: choose the next parser depending on the result of the first one, if successful, and returns the result of the second parser
|
||||
//!
|
||||
//! # Sequence combinators
|
||||
//!
|
||||
//! * **delimited!**: delimited(opening, X, closing) returns X
|
||||
//! * **do_parse!**: do_parse applies sub parsers in a sequence. it can store intermediary results and make them available for later parsers
|
||||
//! * **pair!**: pair(X,Y), returns (x,y)
|
||||
//! * **permutation!**: applies its sub parsers in a sequence, but independent from their order this parser will only succeed if all of its sub parsers succeed
|
||||
//! * **preceded!**: preceded(opening, X) returns X
|
||||
//! * **separated_pair!**: separated_pair(X,sep,Y) returns (x,y)
|
||||
//! * **terminated!**: terminated(X, closing) returns X
|
||||
//! * **tuple!**: chains parsers and assemble the sub results in a tuple.
|
||||
//!
|
||||
//! ## Applying a parser multiple times
|
||||
//!
|
||||
//! * **count!**: Applies the child parser a specified number of times
|
||||
//! * **count_fixed!**: Applies the child parser a fixed number of times and returns a fixed size array The type must be specified and it must be Copy
|
||||
//! * **fold_many0!**: Applies the parser 0 or more times and folds the list of return values
|
||||
//! * **fold_many1!**: Applies the parser 1 or more times and folds the list of return values
|
||||
//! * **fold_many_m_n!**: Applies the parser between m and n times (n included) and folds the list of return value
|
||||
//! * **length_count!**: gets a number from the first parser, then applies the second parser that many times
|
||||
//! * **many0!**: Applies the parser 0 or more times and returns the list of results in a Vec
|
||||
//! * **many1!**: Applies the parser 1 or more times and returns the list of results in a Vec
|
||||
//! * **many_m_n!**: Applies the parser between m and n times (n included) and returns the list of results in a Vec
|
||||
//! * **many_till!**: Applies the first parser until the second applies. Returns a tuple containing the list of results from the first in a Vec and the result of the second.
|
||||
//! * **separated_list!**: separated_list(sep, X) returns Vec will return Incomplete if there may be more elements
|
||||
//! * **separated_list_complete!**: This is equivalent to the separated_list! combinator, except that it will return Error when either the separator or element subparser returns Incomplete.
|
||||
//! * **separated_nonempty_list!**: separated_nonempty_list(sep, X) returns Vec will return Incomplete if there may be more elements
|
||||
//! * **separated_nonempty_list_complete!**: This is equivalent to the separated_nonempty_list! combinator, except that it will return Error when either the separator or element subparser returns Incomplete.
|
||||
//!
|
||||
//! ## Text parsing
|
||||
//!
|
||||
//! * **escaped!**: matches a byte string with escaped characters.
|
||||
//! * **escaped_transform!**: matches a byte string with escaped characters, and returns a new string with the escaped characters replaced
|
||||
//!
|
||||
//! ## Binary format parsing
|
||||
//!
|
||||
//! * **length_data!**: gets a number from the first parser, than takes a subslice of the input of that size, and returns that subslice
|
||||
//! * **length_bytes!**: alias for `length_data`
|
||||
//! * **length_value!**: gets a number from the first parser, takes a subslice of the input of that size, then applies the second parser on that subslice. If the second parser returns Incomplete, length_value will return an error
|
||||
//!
|
||||
//! ## Bit stream parsing
|
||||
//!
|
||||
//! * **bits!**: transforms the current input type (byte slice `&[u8]`) to a bit stream on which bit specific parsers and more general combinators can be applied
|
||||
//! * **bytes!**: transforms its bits stream input back into a byte slice for the underlying parsers.
|
||||
//! * **tag_bits!**: matches an integer pattern to a bitstream. The number of bits of the input to compare must be specified
|
||||
//! * **take_bits!**: generates a parser consuming the specified number of bits
|
||||
//!
|
||||
//! ## Whitespace delimited formats parsing
|
||||
//!
|
||||
//! * **eat_separator!**: helper macros to build a separator parser
|
||||
//! * **sep!**: sep is the parser rewriting macro for whitespace separated formats
|
||||
//! * **wrap_sep!**:
|
||||
//! * **ws!**:
|
||||
//!
|
||||
//! ## Remaining combinators
|
||||
//!
|
||||
//! * **apply!**: emulate function currying: apply!(my_function, arg1, arg2, ...) becomes my_function(input, arg1, arg2, ...)
|
||||
//! * **apply_m!**: emulate function currying for method calls on structs apply_m!(self.my_function, arg1, arg2, ...) becomes self.my_function(input, arg1, arg2, ...)
|
||||
//! * **call!**: Used to wrap common expressions and function as macros
|
||||
//! * **call_m!**: Used to called methods then move self back into self
|
||||
//! * **closure!**: Wraps a parser in a closure
|
||||
//! * **method!**: Makes a method from a parser combination
|
||||
//! * **named!**: Makes a function from a parser combination
|
||||
//! * **named_args!**: Makes a function from a parser combination with arguments.
|
||||
//! * **named_attr!**: Makes a function from a parser combination, with attributes
|
||||
//! * **try_parse!**: A bit like std::try!, this macro will return the remaining input and parsed value if the child parser returned Done, and will do an early return for Error and Incomplete this can provide more flexibility than do_parse! if needed
|
||||
//!
|
||||
//! ## Character test functions
|
||||
//!
|
||||
//! use those functions with a combinator like `take_while!`:
|
||||
//!
|
||||
//! * **is_alphabetic**: Tests if byte is ASCII alphabetic: A-Z, a-z
|
||||
//! * **is_alphanumeric**: Tests if byte is ASCII alphanumeric: A-Z, a-z, 0-9
|
||||
//! * **is_digit**: Tests if byte is ASCII digit: 0-9
|
||||
//! * **is_hex_digit**: Tests if byte is ASCII hex digit: 0-9, A-F, a-f
|
||||
//! * **is_oct_digit**: Tests if byte is ASCII octal digit: 0-7
|
||||
//! * **is_space**: Tests if byte is ASCII space or tab
|
||||
//!
|
||||
//! ## Remaining functions (sort those out in the other categories)
|
||||
//!
|
||||
//! * **alpha**: Recognizes one or more lowercase and uppercase alphabetic characters: a-zA-Z
|
||||
//! * **alphanumeric**: Recognizes one or more numerical and alphabetic characters: 0-9a-zA-Z
|
||||
//! * **anychar**:
|
||||
//! * **begin**:
|
||||
//! * **crlf**:
|
||||
//! * **digit**: Recognizes one or more numerical characters: 0-9
|
||||
//! * **double**: Recognizes floating point number in a byte string and returns a f64
|
||||
//! * **double_s**: Recognizes floating point number in a string and returns a f64
|
||||
//! * **eol**:
|
||||
//! * **float**: Recognizes floating point number in a byte string and returns a f32
|
||||
//! * **float_s**: Recognizes floating point number in a string and returns a f32
|
||||
//! * **hex_digit**: Recognizes one or more hexadecimal numerical characters: 0-9, A-F, a-f
|
||||
//! * **hex_u32**: Recognizes a hex-encoded integer
|
||||
//! * **line_ending**: Recognizes an end of line (both '\n' and "\r\n")
|
||||
//! * **multispace**: Recognizes one or more spaces, tabs, carriage returns and line feeds
|
||||
//! * **newline**: Matches a newline character '\n'
|
||||
//! * **non_empty**: Recognizes non empty buffers
|
||||
//! * **not_line_ending**:
|
||||
//! * **oct_digit**: Recognizes one or more octal characters: 0-7
|
||||
//! * **rest**: Return the remaining input.
|
||||
//! * **rest_s**: Return the remaining input, for strings.
|
||||
//! * **shift**:
|
||||
//! * **sized_buffer**:
|
||||
//! * **space**: Recognizes one or more spaces and tabs
|
||||
//! * **tab**: Matches a tab character '\t'
|
||||
//! * **tag_cl**:
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! #[macro_use]
|
||||
//! extern crate nom;
|
||||
//!
|
||||
//! use nom::{IResult,digit};
|
||||
//!
|
||||
//! // Parser definition
|
||||
//!
|
||||
//! use std::str;
|
||||
//! use std::str::FromStr;
|
||||
//!
|
||||
//! // We parse any expr surrounded by parens, ignoring all whitespaces around those
|
||||
//! named!(parens<i64>, ws!(delimited!( tag!("("), expr, tag!(")") )) );
|
||||
//!
|
||||
//! // We transform an integer string into a i64, ignoring surrounding whitespaces
|
||||
//! // We look for a digit suite, and try to convert it.
|
||||
//! // If either str::from_utf8 or FromStr::from_str fail,
|
||||
//! // we fallback to the parens parser defined above
|
||||
//! named!(factor<i64>, alt!(
|
||||
//! map_res!(
|
||||
//! map_res!(
|
||||
//! ws!(digit),
|
||||
//! str::from_utf8
|
||||
//! ),
|
||||
//! FromStr::from_str
|
||||
//! )
|
||||
//! | parens
|
||||
//! )
|
||||
//! );
|
||||
//!
|
||||
//! // We read an initial factor and for each time we find
|
||||
//! // a * or / operator followed by another factor, we do
|
||||
//! // the math by folding everything
|
||||
//! named!(term <i64>, do_parse!(
|
||||
//! init: factor >>
|
||||
//! res: fold_many0!(
|
||||
//! pair!(alt!(tag!("*") | tag!("/")), factor),
|
||||
//! init,
|
||||
//! |acc, (op, val): (&[u8], i64)| {
|
||||
//! if (op[0] as char) == '*' { acc * val } else { acc / val }
|
||||
//! }
|
||||
//! ) >>
|
||||
//! (res)
|
||||
//! )
|
||||
//! );
|
||||
//!
|
||||
//! named!(expr <i64>, do_parse!(
|
||||
//! init: term >>
|
||||
//! res: fold_many0!(
|
||||
//! pair!(alt!(tag!("+") | tag!("-")), term),
|
||||
//! init,
|
||||
//! |acc, (op, val): (&[u8], i64)| {
|
||||
//! if (op[0] as char) == '+' { acc + val } else { acc - val }
|
||||
//! }
|
||||
//! ) >>
|
||||
//! (res)
|
||||
//! )
|
||||
//! );
|
||||
//!
|
||||
//! fn main() {
|
||||
//! assert_eq!(expr(b"1+2"), IResult::Done(&b""[..], 3));
|
||||
//! assert_eq!(expr(b"12+6-4+3"), IResult::Done(&b""[..], 17));
|
||||
//! assert_eq!(expr(b"1+2*3+4"), IResult::Done(&b""[..], 11));
|
||||
//!
|
||||
//! assert_eq!(expr(b"(2)"), IResult::Done(&b""[..], 2));
|
||||
//! assert_eq!(expr(b"2*(3+4)"), IResult::Done(&b""[..], 14));
|
||||
//! assert_eq!(expr(b"2*2/(5-1)+3"), IResult::Done(&b""[..], 4));
|
||||
//! }
|
||||
//! ```
|
||||
#![cfg_attr(not(feature = "std"), feature(no_std))]
|
||||
#![cfg_attr(not(feature = "std"), feature(collections))]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
#![cfg_attr(feature = "nightly", feature(test))]
|
||||
#![cfg_attr(feature = "nightly", feature(const_fn))]
|
||||
#![cfg_attr(feature = "nightly", feature(plugin))]
|
||||
#![cfg_attr(feature = "nightly", plugin(compiler_error))]
|
||||
//#![warn(missing_docs)]
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
extern crate collections;
|
||||
#[cfg(feature = "regexp")]
|
||||
extern crate regex;
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[macro_use] extern crate lazy_static;
|
||||
extern crate memchr;
|
||||
#[cfg(feature = "nightly")]
|
||||
extern crate test;
|
||||
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
#[allow(unused_macros)]
|
||||
#[macro_export]
|
||||
macro_rules! compiler_error {
|
||||
($e:expr) => {
|
||||
INVALID_NOM_SYNTAX_PLEASE_SEE_FAQ //https://github.com/Geal/nom/blob/master/doc/FAQ.md#using-nightly-to-get-better-error-messages
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
mod std {
|
||||
#[macro_use]
|
||||
pub use core::{fmt, cmp, iter, option, result, ops, slice, str, mem, convert};
|
||||
pub use collections::{boxed, vec, string};
|
||||
pub mod prelude {
|
||||
pub use core::prelude as v1;
|
||||
}
|
||||
}
|
||||
|
||||
pub use self::util::*;
|
||||
pub use self::traits::*;
|
||||
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
pub use self::verbose_errors::*;
|
||||
|
||||
#[cfg(not(feature = "verbose-errors"))]
|
||||
pub use self::simple_errors::*;
|
||||
|
||||
pub use self::internal::*;
|
||||
pub use self::macros::*;
|
||||
pub use self::branch::*;
|
||||
pub use self::sequence::*;
|
||||
pub use self::multi::*;
|
||||
pub use self::methods::*;
|
||||
pub use self::bytes::*;
|
||||
pub use self::bits::*;
|
||||
|
||||
pub use self::nom::*;
|
||||
pub use self::character::*;
|
||||
|
||||
pub use self::whitespace::*;
|
||||
|
||||
#[cfg(feature = "regexp")]
|
||||
pub use self::regexp::*;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(feature = "stream")]
|
||||
pub use self::stream::*;
|
||||
|
||||
pub use self::str::*;
|
||||
|
||||
#[macro_use] mod util;
|
||||
mod traits;
|
||||
|
||||
#[cfg(feature = "verbose-errors")] #[macro_use] pub mod verbose_errors;
|
||||
|
||||
#[cfg(not(feature = "verbose-errors"))] #[macro_use] pub mod simple_errors;
|
||||
|
||||
#[macro_use] mod internal;
|
||||
#[macro_use] mod macros;
|
||||
#[macro_use] mod branch;
|
||||
#[macro_use] mod sequence;
|
||||
#[macro_use] mod multi;
|
||||
#[macro_use] pub mod methods;
|
||||
#[macro_use] mod bytes;
|
||||
#[macro_use] pub mod bits;
|
||||
|
||||
#[macro_use] mod nom;
|
||||
#[macro_use] mod character;
|
||||
|
||||
#[macro_use]
|
||||
pub mod whitespace;
|
||||
|
||||
#[cfg(feature = "regexp")]
|
||||
#[macro_use] mod regexp;
|
||||
|
||||
#[macro_use]
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(feature = "stream")]
|
||||
mod stream;
|
||||
|
||||
mod str;
|
1342
third_party/rust/nom-3.2.1/src/macros.rs
vendored
1342
third_party/rust/nom-3.2.1/src/macros.rs
vendored
File diff suppressed because it is too large
Load Diff
500
third_party/rust/nom-3.2.1/src/methods.rs
vendored
500
third_party/rust/nom-3.2.1/src/methods.rs
vendored
@ -1,500 +0,0 @@
|
||||
//! Method macro combinators
|
||||
//!
|
||||
//! These macros make parsers as methods of structs
|
||||
//! and that can take methods of structs to call
|
||||
//! as parsers.
|
||||
//!
|
||||
//! There is a trick to make them easier to assemble,
|
||||
//! combinators are defined like this:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! macro_rules! tag (
|
||||
//! ($i:expr, $inp: expr) => (
|
||||
//! {
|
||||
//! ...
|
||||
//! }
|
||||
//! );
|
||||
//! );
|
||||
//! ```
|
||||
//!
|
||||
//! But when used as methods in other combinators, are used
|
||||
//! like this:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! method!(my_function<Parser<'a> >, self, tag!("abcd"));
|
||||
//! ```
|
||||
//!
|
||||
//! Internally, other combinators will rewrite
|
||||
//! that call to pass the input as second argument:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! macro_rules! method (
|
||||
//! ($name:ident<$a:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
//! fn $name( $self_: $a, i: &[u8] ) -> $crate::IResult<&[u8], &[u8]> {
|
||||
//! $submac!(i, $($args)*)
|
||||
//! }
|
||||
//! );
|
||||
//! );
|
||||
//! ```
|
||||
//!
|
||||
//! The `method!` macro is similar to the `named!` macro in the macros module.
|
||||
//! While `named!` will create a parser function, `method!` will create a parser
|
||||
//! method on the struct it is defined in.
|
||||
//!
|
||||
//! Compared to the `named!` macro there are a few differences in how they are
|
||||
//! invoked. A `method!` invocation always has to have the type of `self`
|
||||
//! declared and it can't be a reference due to Rust's borrow lifetime
|
||||
//! restrictions:
|
||||
//! ```ignore
|
||||
//! // -`self`'s type-
|
||||
//! method!(method_name< Parser<'a> >, ...);
|
||||
//! ```
|
||||
//! `self`'s type always comes first.
|
||||
//! The next difference is you have to input the self struct. Due to Rust's
|
||||
//! macro hygiene the macro can't declare it on it's own.
|
||||
//! ```ignore
|
||||
//! // -self-
|
||||
//! method!(method_name<Parser<'a>, &'a str, &'a str>, self, ...);
|
||||
//! ```
|
||||
//! When making a parsing struct with parsing methods, due to the static borrow
|
||||
//! checker,calling any parsing methods on self (or any other parsing struct)
|
||||
//! will cause self to be moved for the rest of the method.To get around this
|
||||
//! restriction all self is moved into the called method and then the called
|
||||
//! method will return self to the caller.
|
||||
//!
|
||||
//! To call a method on self you need to use the `call_m!` macro. For example:
|
||||
//! ```ignore
|
||||
//! struct<'a> Parser<'a> {
|
||||
//! parsed: &'a str,
|
||||
//! }
|
||||
//! impl<'a> Parser<'a> {
|
||||
//! // Constructor omitted for brevity
|
||||
//! method!(take4<Parser<'a>, &'a str, &'a str>, self, take!(4));
|
||||
//! method!(caller<Parser<'a>, &'a str, &'a str>, self, call_m!(self.take4));
|
||||
//! }
|
||||
//! ```
|
||||
//! More complicated combinations still mostly look the same as their `named!`
|
||||
//! counterparts:
|
||||
//! ```ignore
|
||||
//! method!(pub simple_chain<&mut Parser<'a>, &'a str, &'a str>, self,
|
||||
//! do_parse!(
|
||||
//! call_m!(self.tag_abc) >>
|
||||
//! call_m!(self.tag_def) >>
|
||||
//! call_m!(self.tag_ghi) >>
|
||||
//! last: map!(call_m!(self.simple_peek), |parsed| sb.parsed = parsed) >>
|
||||
//! (last)
|
||||
//! )
|
||||
//! );
|
||||
//! ```
|
||||
//! The three additions to method definitions to remember are:
|
||||
//! 1. Specify `self`'s type
|
||||
//! 2. Pass `self` to the macro
|
||||
//! 4. Call parser methods using the `call_m!` macro.
|
||||
|
||||
/// Makes a method from a parser combination
|
||||
///
|
||||
/// The must be set up because the compiler needs
|
||||
/// the information
|
||||
///
|
||||
/// ```ignore
|
||||
/// method!(my_function<Parser<'a> >( &[u8] ) -> &[u8], tag!("abcd"));
|
||||
/// // first type parameter is `self`'s type, second is input, third is output
|
||||
/// method!(my_function<Parser<'a>, &[u8], &[u8]>, tag!("abcd"));
|
||||
/// //prefix them with 'pub' to make the methods public
|
||||
/// method!(pub my_function<Parser<'a>,&[u8], &[u8]>, tag!("abcd"));
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! method (
|
||||
// Non-public immutable self
|
||||
($name:ident<$a:ty>( $i:ty ) -> $o:ty, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
#[allow(unused_variables)]
|
||||
fn $name( $self_: $a, i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty,$i:ty,$o:ty,$e:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
#[allow(unused_variables)]
|
||||
fn $name( $self_: $a, i: $i ) -> ($a, $crate::IResult<$i, $o, $e>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty,$i:ty,$o:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
#[allow(unused_variables)]
|
||||
fn $name( $self_: $a, i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty,$o:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
#[allow(unused_variables)]
|
||||
fn $name( $self_: $a, i: &[u8] ) -> ($a, $crate::IResult<&[u8], $o, u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
#[allow(unused_variables)]
|
||||
fn $name( $self_: $a, i: &[u8] ) -> ($a, $crate::IResult<&[u8], &[u8], u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
// Public immutable self
|
||||
(pub $name:ident<$a:ty>( $i:ty ) -> $o:ty, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
#[allow(unused_variables)]
|
||||
pub fn $name( $self_: $a, i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty,$i:ty,$o:ty,$e:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
#[allow(unused_variables)]
|
||||
fn $name( $self_: $a, i: $i ) -> ($a, $crate::IResult<$i, $o, $e>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty,$i:ty,$o:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
#[allow(unused_variables)]
|
||||
pub fn $name( $self_: $a,i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty,$o:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
#[allow(unused_variables)]
|
||||
pub fn $name( $self_: $a, i: &[u8] ) -> ($a, $crate::IResult<&[u8], $o, u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty>, $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
#[allow(unused_variables)]
|
||||
pub fn $name( $self_: $a, i: &[u8] ) -> ($a, $crate::IResult<&[u8], &[u8], u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
// Non-public mutable self
|
||||
($name:ident<$a:ty>( $i:ty ) -> $o:ty, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
#[allow(unused_variables)]
|
||||
fn $name( mut $self_: $a, i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty,$i:ty,$o:ty,$e:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
#[allow(unused_variables)]
|
||||
fn $name( mut $self_: $a, i: $i ) -> ($a, $crate::IResult<$i, $o, $e>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty,$i:ty,$o:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
#[allow(unused_variables)]
|
||||
fn $name( mut $self_: $a, i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty,$o:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
#[allow(unused_variables)]
|
||||
fn $name( mut $self_: $a, i: &[u8] ) -> ($a, $crate::IResult<&[u8], $o, u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
($name:ident<$a:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
#[allow(unused_variables)]
|
||||
fn $name( mut $self_: $a, i: &[u8] ) -> ($a, $crate::IResult<&[u8], &[u8], u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
// Public mutable self
|
||||
(pub $name:ident<$a:ty>( $i:ty ) -> $o:ty, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
#[allow(unused_variables)]
|
||||
pub fn $name( mut $self_: $a, i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty,$i:ty,$o:ty,$e:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
#[allow(unused_variables)]
|
||||
fn $name( mut $self_: $a, i: $i ) -> ($a, $crate::IResult<$i, $o, $e>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty,$i:ty,$o:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
#[allow(unused_variables)]
|
||||
pub fn $name( mut $self_: $a,i: $i ) -> ($a, $crate::IResult<$i,$o,u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty,$o:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
#[allow(unused_variables)]
|
||||
pub fn $name( mut $self_: $a, i: &[u8] ) -> ($a, $crate::IResult<&[u8], $o, u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$a:ty>, mut $self_:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
#[allow(unused_variables)]
|
||||
pub fn $name( mut $self_: $a, i: &[u8] ) -> ($a, $crate::IResult<&[u8], &[u8], u32>) {
|
||||
let result = $submac!(i, $($args)*);
|
||||
($self_, result)
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// Used to called methods then move self back into self
|
||||
#[macro_export]
|
||||
macro_rules! call_m (
|
||||
($i:expr, $self_:ident.$method:ident) => (
|
||||
{
|
||||
let (tmp, res) = $self_.$method($i);
|
||||
$self_ = tmp;
|
||||
res
|
||||
}
|
||||
);
|
||||
($i:expr, $self_:ident.$method:ident, $($args:expr),* ) => (
|
||||
{
|
||||
let (tmp, res) = $self_.$method($i, $($args),*);
|
||||
$self_ = tmp;
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
/// emulate function currying for method calls on structs
|
||||
/// `apply_m!(self.my_function, arg1, arg2, ...)` becomes `self.my_function(input, arg1, arg2, ...)`
|
||||
///
|
||||
/// Supports up to 6 arguments
|
||||
#[macro_export]
|
||||
macro_rules! apply_m (
|
||||
($i:expr, $self_:ident.$method:ident, $($args:expr),* ) => ( { let (tmp, res) = $self_.$method( $i, $($args),* ); $self_ = tmp; res } );
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use internal::IResult::*;
|
||||
|
||||
// reproduce the tag_s and take_s macros, because of module import order
|
||||
macro_rules! tag_s (
|
||||
($i:expr, $tag: expr) => (
|
||||
{
|
||||
let res: $crate::IResult<_,_> = if $tag.len() > $i.len() {
|
||||
$crate::IResult::Incomplete($crate::Needed::Size($tag.len()))
|
||||
//} else if &$i[0..$tag.len()] == $tag {
|
||||
} else if ($i).starts_with($tag) {
|
||||
$crate::IResult::Done(&$i[$tag.len()..], &$i[0..$tag.len()])
|
||||
} else {
|
||||
$crate::IResult::Error(error_position!($crate::ErrorKind::TagStr, $i))
|
||||
};
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
macro_rules! take_s (
|
||||
($i:expr, $count:expr) => (
|
||||
{
|
||||
let cnt = $count as usize;
|
||||
let res: $crate::IResult<_,_> = if $i.chars().count() < cnt {
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(cnt))
|
||||
} else {
|
||||
let mut offset = $i.len();
|
||||
let mut count = 0;
|
||||
for (o, _) in $i.char_indices() {
|
||||
if count == cnt {
|
||||
offset = o;
|
||||
break;
|
||||
}
|
||||
count += 1;
|
||||
}
|
||||
$crate::IResult::Done(&$i[offset..], &$i[..offset])
|
||||
};
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
struct Parser<'a> {
|
||||
bcd: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
pub fn new() -> Parser<'a> {
|
||||
Parser{bcd: ""}
|
||||
}
|
||||
|
||||
method!(tag_abc<Parser<'a>, &'a str, &'a str>, self, tag_s!("áβç"));
|
||||
method!(tag_bcd<Parser<'a> >(&'a str) -> &'a str, self, tag_s!("βçδ"));
|
||||
method!(pub tag_hij<Parser<'a> >(&'a str) -> &'a str, self, tag_s!("λïJ"));
|
||||
method!(pub tag_ijk<Parser<'a>, &'a str, &'a str>, self, tag_s!("ïJƙ"));
|
||||
method!(take3<Parser<'a>, &'a str, &'a str>, self, take_s!(3));
|
||||
method!(pub simple_call<Parser<'a>, &'a str, &'a str>, mut self,
|
||||
call_m!(self.tag_abc)
|
||||
);
|
||||
method!(pub simple_peek<Parser<'a>, &'a str, &'a str>, mut self,
|
||||
peek!(call_m!(self.take3))
|
||||
);
|
||||
method!(pub simple_chain<Parser<'a>, &'a str, &'a str>, mut self,
|
||||
do_parse!(
|
||||
map!(call_m!(self.tag_bcd), |bcd| self.bcd = bcd) >>
|
||||
last: call_m!(self.simple_peek) >>
|
||||
(last)
|
||||
)
|
||||
);
|
||||
fn tag_stuff(mut self: Parser<'a>, input: &'a str, something: &'a str) -> (Parser<'a>, ::IResult<&'a str, &'a str>) {
|
||||
self.bcd = something;
|
||||
let(tmp, res) = self.tag_abc(input);
|
||||
self = tmp;
|
||||
(self, res)
|
||||
}
|
||||
method!(use_apply<Parser<'a>, &'a str, &'a str>, mut self, apply_m!(self.tag_stuff, "βçδ"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_method_call_abc() {
|
||||
let p = Parser::new();
|
||||
let input: &str = "áβçδèƒϱλïJƙ";
|
||||
let consumed: &str = "áβç";
|
||||
let leftover: &str = "δèƒϱλïJƙ";
|
||||
let(_, res) = p.tag_abc(input);
|
||||
match res {
|
||||
Done(extra, output) => { assert!(extra == leftover, "`Parser.tag_abc` consumed leftover input. leftover: {}", extra);
|
||||
assert!(output == consumed, "`Parser.tag_abc` doesnt return the string it consumed \
|
||||
on success. Expected `{}`, got `{}`.", consumed, output);
|
||||
},
|
||||
other => panic!("`Parser.tag_abc` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_method_call_bcd() {
|
||||
let p = Parser::new();
|
||||
let input: &str = "βçδèƒϱλïJƙ";
|
||||
let consumed: &str = "βçδ";
|
||||
let leftover: &str = "èƒϱλïJƙ";
|
||||
let(_, res) = p.tag_bcd(input);
|
||||
match res {
|
||||
Done(extra, output) => { assert!(extra == leftover, "`Parser.tag_bcd` consumed leftover input. leftover: {}", extra);
|
||||
assert!(output == consumed, "`Parser.tag_bcd` doesn't return the string it consumed \
|
||||
on success. Expected `{}`, got `{}`.", consumed, output);
|
||||
},
|
||||
other => panic!("`Parser.tag_bcd` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_method_call_hij() {
|
||||
let p = Parser::new();
|
||||
let input: &str = "λïJƙℓ₥ñôƥ9řƨ";
|
||||
let consumed: &str = "λïJ";
|
||||
let leftover: &str = "ƙℓ₥ñôƥ9řƨ";
|
||||
let(_, res) = p.tag_hij(input);
|
||||
match res {
|
||||
Done(extra, output) => { assert!(extra == leftover, "`Parser.tag_hij` consumed leftover input. leftover: {}", extra);
|
||||
assert!(output == consumed, "`Parser.tag_hij` doesn't return the string it consumed \
|
||||
on success. Expected `{}`, got `{}`.", consumed, output);
|
||||
},
|
||||
other => panic!("`Parser.tag_hij` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_method_call_ijk() {
|
||||
let p = Parser::new();
|
||||
let input: &str = "ïJƙℓ₥ñôƥ9řƨ";
|
||||
let consumed: &str = "ïJƙ";
|
||||
let leftover: &str = "ℓ₥ñôƥ9řƨ";
|
||||
let(_, res) = p.tag_ijk(input);
|
||||
match res {
|
||||
Done(extra, output) => { assert!(extra == leftover, "`Parser.tag_ijk` consumed leftover input. leftover: {}", extra);
|
||||
assert!(output == consumed, "`Parser.tag_ijk` doesn't return the string it consumed \
|
||||
on success. Expected `{}`, got `{}`.", consumed, output);
|
||||
},
|
||||
other => panic!("`Parser.tag_ijk` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_method_simple_call() {
|
||||
let p = Parser::new();
|
||||
let input: &str = "áβçδèƒϱλïJƙ";
|
||||
let consumed: &str = "áβç";
|
||||
let leftover: &str = "δèƒϱλïJƙ";
|
||||
let(_, res) = p.simple_call(input);
|
||||
match res {
|
||||
Done(extra, output) => { assert!(extra == leftover, "`Parser.simple_call` consumed leftover input. leftover: {}", extra);
|
||||
assert!(output == consumed, "`Parser.simple_call` doesn't return the string it consumed \
|
||||
on success. Expected `{}`, got `{}`.", consumed, output);
|
||||
},
|
||||
other => panic!("`Parser.simple_call` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_apply_m() {
|
||||
let mut p = Parser::new();
|
||||
let input: &str = "áβçδèƒϱλïJƙ";
|
||||
let consumed: &str = "áβç";
|
||||
let leftover: &str = "δèƒϱλïJƙ";
|
||||
let(tmp, res) = p.use_apply(input);
|
||||
p = tmp;
|
||||
match res {
|
||||
Done(extra, output) => { assert!(extra == leftover, "`Parser.use_apply` consumed leftover input. leftover: {}", extra);
|
||||
assert!(output == consumed, "`Parser.use_apply` doesn't return the string it was supposed to \
|
||||
on success. Expected `{}`, got `{}`.", leftover, output);
|
||||
assert!(p.bcd == "βçδ", "Parser.use_apply didn't modify the parser field correctly: {}", p.bcd);
|
||||
},
|
||||
other => panic!("`Parser.use_apply` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_method_call_peek() {
|
||||
let p = Parser::new();
|
||||
let input: &str = "ж¥ƺáβçδèƒϱλïJƙ";
|
||||
let consumed: &str = "ж¥ƺ";
|
||||
let(_, res) = p.simple_peek(input);
|
||||
match res {
|
||||
Done(extra, output) => { assert!(extra == input, "`Parser.simple_peek` consumed leftover input. leftover: {}", extra);
|
||||
assert!(output == consumed, "`Parser.simple_peek` doesn't return the string it consumed \
|
||||
on success. Expected `{}`, got `{}`.", consumed, output);
|
||||
},
|
||||
other => panic!("`Parser.simple_peek` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_method_call_chain() {
|
||||
let mut p = Parser::new();
|
||||
let input : &str = "βçδδèƒϱλïJƙℓ";
|
||||
let leftover : &str = "δèƒϱλïJƙℓ";
|
||||
let output : &str = "늟";
|
||||
let(tmp, res) = p.simple_chain(input);
|
||||
p = tmp;
|
||||
match res {
|
||||
Done(extra, out) => { assert!(extra == leftover, "`Parser.simple_chain` consumed leftover input. leftover: {}", extra);
|
||||
assert!(out == output, "`Parser.simple_chain` doesn't return the string it was supposed to \
|
||||
on success. Expected `{}`, got `{}`.", output, out);
|
||||
assert!(p.bcd == "βçδ", "Parser.simple_chain didn't modify the parser field correctly: {}", p.bcd);
|
||||
},
|
||||
other => panic!("`Parser.simple_chain` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
}
|
1614
third_party/rust/nom-3.2.1/src/multi.rs
vendored
1614
third_party/rust/nom-3.2.1/src/multi.rs
vendored
File diff suppressed because it is too large
Load Diff
1225
third_party/rust/nom-3.2.1/src/nom.rs
vendored
1225
third_party/rust/nom-3.2.1/src/nom.rs
vendored
File diff suppressed because it is too large
Load Diff
683
third_party/rust/nom-3.2.1/src/regexp.rs
vendored
683
third_party/rust/nom-3.2.1/src/regexp.rs
vendored
@ -1,683 +0,0 @@
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! regex (
|
||||
($re: ident, $s:expr) => (
|
||||
lazy_static! {
|
||||
static ref $re: ::regex::Regex = ::regex::Regex::new($s).unwrap();
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! regex_bytes (
|
||||
($re: ident, $s:expr) => (
|
||||
lazy_static! {
|
||||
static ref $re: ::regex::bytes::Regex = ::regex::bytes::Regex::new($s).unwrap();
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
/// `re_match!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the whole input if a match is found
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_match (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::InputLength;
|
||||
use $crate::Slice;
|
||||
let re = ::regex::Regex::new($re).unwrap();
|
||||
if re.is_match($i) {
|
||||
$crate::IResult::Done($i.slice($i.input_len()..), $i)
|
||||
} else {
|
||||
let res: $crate::IResult<_,_> = $crate::IResult::Error(error_code!($crate::ErrorKind::RegexpMatch));
|
||||
res
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_match_static!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the whole input if a match is found. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_match_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::InputLength;
|
||||
use $crate::Slice;
|
||||
regex!(RE, $re);
|
||||
if RE.is_match($i) {
|
||||
$crate::IResult::Done($i.slice($i.input_len()..), $i)
|
||||
} else {
|
||||
let res: $crate::IResult<_,_> = $crate::IResult::Error(error_code!($crate::ErrorKind::RegexpMatch));
|
||||
res
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_bytes_match!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the whole input if a match is found
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_match (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::InputLength;
|
||||
use $crate::Slice;
|
||||
let re = ::regex::bytes::Regex::new($re).unwrap();
|
||||
if re.is_match($i) {
|
||||
$crate::IResult::Done($i.slice($i.input_len()..), $i)
|
||||
} else {
|
||||
let res: $crate::IResult<_,_> = $crate::IResult::Error(error_code!($crate::ErrorKind::RegexpMatch));
|
||||
res
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_bytes_match_static!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the whole input if a match is found. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_match_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::InputLength;
|
||||
use $crate::Slice;
|
||||
regex_bytes!(RE, $re);
|
||||
if RE.is_match($i) {
|
||||
$crate::IResult::Done($i.slice($i.input_len()..), $i)
|
||||
} else {
|
||||
let res: $crate::IResult<_,_> = $crate::IResult::Error(error_code!($crate::ErrorKind::RegexpMatch));
|
||||
res
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_find!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the first match
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_find (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::Slice;
|
||||
let re = ::regex::Regex::new($re).unwrap();
|
||||
if let Some(m) = re.find($i) {
|
||||
$crate::IResult::Done($i.slice(m.end()..), $i.slice(m.start()..m.end()))
|
||||
} else {
|
||||
let res: $crate::IResult<_,_> = $crate::IResult::Error(error_code!($crate::ErrorKind::RegexpFind));
|
||||
res
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_find_static!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the first match. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_find_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::Slice;
|
||||
regex!(RE, $re);
|
||||
if let Some(m) = RE.find($i) {
|
||||
$crate::IResult::Done($i.slice(m.end()..), $i.slice(m.start()..m.end()))
|
||||
} else {
|
||||
let res: $crate::IResult<_,_> = $crate::IResult::Error(error_code!($crate::ErrorKind::RegexpFind));
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_bytes_find!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the first match
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_find (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::Slice;
|
||||
let re = ::regex::bytes::Regex::new($re).unwrap();
|
||||
if let Some(m) = re.find($i) {
|
||||
$crate::IResult::Done($i.slice(m.end()..), $i.slice(m.start()..m.end()))
|
||||
} else {
|
||||
let res: $crate::IResult<_,_> = $crate::IResult::Error(error_code!($crate::ErrorKind::RegexpFind));
|
||||
res
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_bytes_find!(regexp) => &[T] -> IResult<&[T], &[T]>`
|
||||
/// Returns the first match. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_find_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::Slice;
|
||||
regex_bytes!(RE, $re);
|
||||
if let Some(m) = RE.find($i) {
|
||||
$crate::IResult::Done($i.slice(m.end()..), $i.slice(m.start()..m.end()))
|
||||
} else {
|
||||
let res: $crate::IResult<_,_> = $crate::IResult::Error(error_code!($crate::ErrorKind::RegexpFind));
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_matches!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns all the matched parts
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_matches (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::Slice;
|
||||
let re = ::regex::Regex::new($re).unwrap();
|
||||
let v: Vec<&str> = re.find_iter($i).map(|m| $i.slice(m.start()..m.end())).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done($i.slice(offset..), v)
|
||||
} else {
|
||||
let res: $crate::IResult<_,_> = $crate::IResult::Error(error_code!($crate::ErrorKind::RegexpMatches));
|
||||
res
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_matches_static!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns all the matched parts. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_matches_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::Slice;
|
||||
regex!(RE, $re);
|
||||
let v: Vec<&str> = RE.find_iter($i).map(|m| $i.slice(m.start()..m.end())).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done($i.slice(offset..), v)
|
||||
} else {
|
||||
let res: $crate::IResult<_,_> = $crate::IResult::Error(error_code!($crate::ErrorKind::RegexpMatches));
|
||||
res
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_bytes_matches!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns all the matched parts
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_matches (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::Slice;
|
||||
let re = ::regex::bytes::Regex::new($re).unwrap();
|
||||
let v: Vec<&[u8]> = re.find_iter($i).map(|m| $i.slice(m.start()..m.end())).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done($i.slice(offset..), v)
|
||||
} else {
|
||||
let res: $crate::IResult<_,_> = $crate::IResult::Error(error_code!($crate::ErrorKind::RegexpMatches));
|
||||
res
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_bytes_matches_static!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns all the matched parts. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_matches_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::Slice;
|
||||
regex_bytes!(RE, $re);
|
||||
let v: Vec<&[u8]> = RE.find_iter($i).map(|m| $i.slice(m.start()..m.end())).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done($i.slice(offset..), v)
|
||||
} else {
|
||||
let res: $crate::IResult<_,_> = $crate::IResult::Error(error_code!($crate::ErrorKind::RegexpMatches));
|
||||
res
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_capture!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns the first capture group
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_capture (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::Slice;
|
||||
let re = ::regex::Regex::new($re).unwrap();
|
||||
if let Some(c) = re.captures($i) {
|
||||
let v:Vec<&str> = c.iter().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|m| $i.slice(m.start()..m.end())).collect();
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done($i.slice(offset..), v)
|
||||
} else {
|
||||
let res: $crate::IResult<_,_> = $crate::IResult::Error(error_code!($crate::ErrorKind::RegexpCapture));
|
||||
res
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_capture_static!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns the first capture group. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_capture_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::Slice;
|
||||
regex!(RE, $re);
|
||||
if let Some(c) = RE.captures($i) {
|
||||
let v:Vec<&str> = c.iter().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|m| $i.slice(m.start()..m.end())).collect();
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done($i.slice(offset..), v)
|
||||
} else {
|
||||
let res: $crate::IResult<_,_> = $crate::IResult::Error(error_code!($crate::ErrorKind::RegexpCapture));
|
||||
res
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_bytes_capture!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns the first capture group
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_capture (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::Slice;
|
||||
let re = ::regex::bytes::Regex::new($re).unwrap();
|
||||
if let Some(c) = re.captures($i) {
|
||||
let v:Vec<&[u8]> = c.iter().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|m| $i.slice(m.start()..m.end())).collect();
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done($i.slice(offset..), v)
|
||||
} else {
|
||||
let res: $crate::IResult<_,_> = $crate::IResult::Error(error_code!($crate::ErrorKind::RegexpCapture));
|
||||
res
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_bytes_capture_static!(regexp) => &[T] -> IResult<&[T], Vec<&[T]>>`
|
||||
/// Returns the first capture group. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_capture_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::Slice;
|
||||
regex_bytes!(RE, $re);
|
||||
if let Some(c) = RE.captures($i) {
|
||||
let v:Vec<&[u8]> = c.iter().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|m| $i.slice(m.start()..m.end())).collect();
|
||||
let offset = {
|
||||
let end = v.last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done($i.slice(offset..), v)
|
||||
} else {
|
||||
let res: $crate::IResult<_,_> = $crate::IResult::Error(error_code!($crate::ErrorKind::RegexpCapture));
|
||||
res
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_captures!(regexp) => &[T] -> IResult<&[T], Vec<Vec<&[T]>>>`
|
||||
/// Returns all the capture groups
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_captures (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::Slice;
|
||||
let re = ::regex::Regex::new($re).unwrap();
|
||||
let v:Vec<Vec<&str>> = re.captures_iter($i).map(|c| c.iter().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|m| $i.slice(m.start()..m.end())).collect()).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap().last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done($i.slice(offset..), v)
|
||||
} else {
|
||||
let res: $crate::IResult<_,_> = $crate::IResult::Error(error_code!($crate::ErrorKind::RegexpCapture));
|
||||
res
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_captures_static!(regexp) => &[T] -> IResult<&[T], Vec<Vec<&[T]>>>`
|
||||
/// Returns all the capture groups. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_captures_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::Slice;
|
||||
regex!(RE, $re);
|
||||
let v:Vec<Vec<&str>> = RE.captures_iter($i).map(|c| c.iter().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|m| $i.slice(m.start()..m.end())).collect()).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap().last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done($i.slice(offset..), v)
|
||||
} else {
|
||||
let res: $crate::IResult<_,_> = $crate::IResult::Error(error_code!($crate::ErrorKind::RegexpCapture));
|
||||
res
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/// `re_bytes_captures!(regexp) => &[T] -> IResult<&[T], Vec<Vec<&[T]>>>`
|
||||
/// Returns all the capture groups
|
||||
///
|
||||
/// requires the `regexp` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_captures (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::Slice;
|
||||
let re = ::regex::bytes::Regex::new($re).unwrap();
|
||||
let v:Vec<Vec<&[u8]>> = re.captures_iter($i).map(|c| c.iter().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|m| $i.slice(m.start()..m.end())).collect()).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap().last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done($i.slice(offset..), v)
|
||||
} else {
|
||||
let res: $crate::IResult<_,_> = $crate::IResult::Error(error_code!($crate::ErrorKind::RegexpCapture));
|
||||
res
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
/// `re_bytes_captures_static!(regexp) => &[T] -> IResult<&[T], Vec<Vec<&[T]>>>`
|
||||
/// Returns all the capture groups. Regular expression calculated at compile time
|
||||
///
|
||||
/// requires the `regexp_macros` feature
|
||||
#[macro_export]
|
||||
macro_rules! re_bytes_captures_static (
|
||||
($i:expr, $re:expr) => (
|
||||
{
|
||||
use $crate::Slice;
|
||||
regex_bytes!(RE, $re);
|
||||
let v:Vec<Vec<&[u8]>> = RE.captures_iter($i).map(|c| c.iter().filter(|el| el.is_some()).map(|el| el.unwrap()).map(|m| $i.slice(m.start()..m.end())).collect()).collect();
|
||||
if v.len() != 0 {
|
||||
let offset = {
|
||||
let end = v.last().unwrap().last().unwrap();
|
||||
end.as_ptr() as usize + end.len() - $i.as_ptr() as usize
|
||||
};
|
||||
$crate::IResult::Done($i.slice(offset..), v)
|
||||
} else {
|
||||
let res: $crate::IResult<_,_> = $crate::IResult::Error(error_code!($crate::ErrorKind::RegexpCapture));
|
||||
res
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use internal::IResult::*;
|
||||
use util::ErrorKind;
|
||||
|
||||
#[test]
|
||||
fn re_match() {
|
||||
named!(rm<&str,&str>, re_match!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm("2015-09-07"), Done("", "2015-09-07"));
|
||||
assert_eq!(rm("blah"), Error(error_code!(ErrorKind::RegexpMatch)));
|
||||
assert_eq!(rm("2015-09-07blah"), Done("", "2015-09-07blah"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_match_static() {
|
||||
named!(rm<&str,&str>, re_match_static!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm("2015-09-07"), Done("", "2015-09-07"));
|
||||
assert_eq!(rm("blah"), Error(error_code!(ErrorKind::RegexpMatch)));
|
||||
assert_eq!(rm("2015-09-07blah"), Done("", "2015-09-07blah"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_find() {
|
||||
named!(rm<&str,&str>, re_find!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm("2015-09-07"), Done("", "2015-09-07"));
|
||||
assert_eq!(rm("blah"), Error(error_code!(ErrorKind::RegexpFind)));
|
||||
assert_eq!(rm("2015-09-07blah"), Done("blah", "2015-09-07"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_find_static() {
|
||||
named!(rm<&str,&str>, re_find_static!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm("2015-09-07"), Done("", "2015-09-07"));
|
||||
assert_eq!(rm("blah"), Error(error_code!(ErrorKind::RegexpFind)));
|
||||
assert_eq!(rm("2015-09-07blah"), Done("blah", "2015-09-07"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_matches() {
|
||||
named!(rm< &str,Vec<&str> >, re_matches!(r"\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm("2015-09-07"), Done("", vec!["2015-09-07"]));
|
||||
assert_eq!(rm("blah"), Error(error_code!(ErrorKind::RegexpMatches)));
|
||||
assert_eq!(rm("aaa2015-09-07blah2015-09-09pouet"), Done("pouet", vec!["2015-09-07", "2015-09-09"]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_matches_static() {
|
||||
named!(rm< &str,Vec<&str> >, re_matches_static!(r"\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm("2015-09-07"), Done("", vec!["2015-09-07"]));
|
||||
assert_eq!(rm("blah"), Error(error_code!(ErrorKind::RegexpMatches)));
|
||||
assert_eq!(rm("aaa2015-09-07blah2015-09-09pouet"), Done("pouet", vec!["2015-09-07", "2015-09-09"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_capture() {
|
||||
named!(rm< &str,Vec<&str> >, re_capture!(r"([[:alpha:]]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm("blah nom 0.3.11pouet"), Done("pouet", vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"]));
|
||||
assert_eq!(rm("blah"), Error(error_code!(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm("hello nom 0.3.11 world regex 0.1.41"), Done(" world regex 0.1.41", vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_capture_static() {
|
||||
named!(rm< &str,Vec<&str> >, re_capture_static!(r"([[:alpha:]]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm("blah nom 0.3.11pouet"), Done("pouet", vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"]));
|
||||
assert_eq!(rm("blah"), Error(error_code!(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm("hello nom 0.3.11 world regex 0.1.41"), Done(" world regex 0.1.41", vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_captures() {
|
||||
named!(rm< &str,Vec<Vec<&str>> >, re_captures!(r"([[:alpha:]]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm("blah nom 0.3.11pouet"), Done("pouet", vec![vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"]]));
|
||||
assert_eq!(rm("blah"), Error(error_code!(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm("hello nom 0.3.11 world regex 0.1.41 aaa"), Done(" aaa", vec![
|
||||
vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"],
|
||||
vec!["regex 0.1.41", "regex", "0.1.41", "0", "1", "41"],
|
||||
]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_captures_static() {
|
||||
named!(rm< &str,Vec<Vec<&str>> >, re_captures_static!(r"([[:alpha:]]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm("blah nom 0.3.11pouet"), Done("pouet", vec![vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"]]));
|
||||
assert_eq!(rm("blah"), Error(error_code!(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm("hello nom 0.3.11 world regex 0.1.41 aaa"), Done(" aaa", vec![
|
||||
vec!["nom 0.3.11", "nom", "0.3.11", "0", "3", "11"],
|
||||
vec!["regex 0.1.41", "regex", "0.1.41", "0", "1", "41"],
|
||||
]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_bytes_match() {
|
||||
named!(rm, re_bytes_match!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm(&b"2015-09-07"[..]), Done(&b""[..], &b"2015-09-07"[..]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(error_code!(ErrorKind::RegexpMatch)));
|
||||
assert_eq!(rm(&b"2015-09-07blah"[..]), Done(&b""[..], &b"2015-09-07blah"[..]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_bytes_match_static() {
|
||||
named!(rm, re_bytes_match_static!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm(&b"2015-09-07"[..]), Done(&b""[..], &b"2015-09-07"[..]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(error_code!(ErrorKind::RegexpMatch)));
|
||||
assert_eq!(rm(&b"2015-09-07blah"[..]), Done(&b""[..], &b"2015-09-07blah"[..]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_bytes_find() {
|
||||
named!(rm, re_bytes_find!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm(&b"2015-09-07"[..]), Done(&b""[..], &b"2015-09-07"[..]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(error_code!(ErrorKind::RegexpFind)));
|
||||
assert_eq!(rm(&b"2015-09-07blah"[..]), Done(&b"blah"[..], &b"2015-09-07"[..]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_bytes_find_static() {
|
||||
named!(rm, re_bytes_find_static!(r"^\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm(&b"2015-09-07"[..]), Done(&b""[..], &b"2015-09-07"[..]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(error_code!(ErrorKind::RegexpFind)));
|
||||
assert_eq!(rm(&b"2015-09-07blah"[..]), Done(&b"blah"[..], &b"2015-09-07"[..]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_bytes_matches() {
|
||||
named!(rm<Vec<&[u8]> >, re_bytes_matches!(r"\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm(&b"2015-09-07"[..]), Done(&b""[..], vec![&b"2015-09-07"[..]]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(error_code!(ErrorKind::RegexpMatches)));
|
||||
assert_eq!(rm(&b"aaa2015-09-07blah2015-09-09pouet"[..]), Done(&b"pouet"[..], vec![&b"2015-09-07"[..], &b"2015-09-09"[..]]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_bytes_matches_static() {
|
||||
named!(rm<Vec<&[u8]> >, re_bytes_matches_static!(r"\d{4}-\d{2}-\d{2}"));
|
||||
assert_eq!(rm(&b"2015-09-07"[..]), Done(&b""[..], vec![&b"2015-09-07"[..]]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(error_code!(ErrorKind::RegexpMatches)));
|
||||
assert_eq!(rm(&b"aaa2015-09-07blah2015-09-09pouet"[..]), Done(&b"pouet"[..], vec![&b"2015-09-07"[..], &b"2015-09-09"[..]]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_bytes_capture() {
|
||||
named!(rm<Vec<&[u8]> >, re_bytes_capture!(r"([[:alpha:]]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm(&b"blah nom 0.3.11pouet"[..]), Done(&b"pouet"[..], vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(error_code!(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm(&b"hello nom 0.3.11 world regex 0.1.41"[..]), Done(&b" world regex 0.1.41"[..], vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_bytes_capture_static() {
|
||||
named!(rm< Vec<&[u8]> >, re_bytes_capture_static!(r"([[:alpha:]]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm(&b"blah nom 0.3.11pouet"[..]), Done(&b"pouet"[..], vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(error_code!(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm(&b"hello nom 0.3.11 world regex 0.1.41"[..]), Done(&b" world regex 0.1.41"[..], vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn re_bytes_captures() {
|
||||
named!(rm< Vec<Vec<&[u8]>> >, re_bytes_captures!(r"([[:alpha:]]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm(&b"blah nom 0.3.11pouet"[..]), Done(&b"pouet"[..], vec![vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]]]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(error_code!(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm(&b"hello nom 0.3.11 world regex 0.1.41 aaa"[..]), Done(&b" aaa"[..], vec![
|
||||
vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]],
|
||||
vec![&b"regex 0.1.41"[..], &b"regex"[..], &b"0.1.41"[..], &b"0"[..], &b"1"[..], &b"41"[..]],
|
||||
]));
|
||||
}
|
||||
|
||||
#[cfg(feature = "regexp_macros")]
|
||||
#[test]
|
||||
fn re_bytes_captures_static() {
|
||||
named!(rm< Vec<Vec<&[u8]>> >, re_bytes_captures_static!(r"([[:alpha:]]+)\s+((\d+).(\d+).(\d+))"));
|
||||
assert_eq!(rm(&b"blah nom 0.3.11pouet"[..]), Done(&b"pouet"[..], vec![vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]]]));
|
||||
assert_eq!(rm(&b"blah"[..]), Error(error_code!(ErrorKind::RegexpCapture)));
|
||||
assert_eq!(rm(&b"hello nom 0.3.11 world regex 0.1.41 aaa"[..]), Done(&b" aaa"[..], vec![
|
||||
vec![&b"nom 0.3.11"[..], &b"nom"[..], &b"0.3.11"[..], &b"0"[..], &b"3"[..], &b"11"[..]],
|
||||
vec![&b"regex 0.1.41"[..], &b"regex"[..], &b"0.1.41"[..], &b"0"[..], &b"1"[..], &b"41"[..]],
|
||||
]));
|
||||
}
|
||||
}
|
889
third_party/rust/nom-3.2.1/src/sequence.rs
vendored
889
third_party/rust/nom-3.2.1/src/sequence.rs
vendored
@ -1,889 +0,0 @@
|
||||
/// `tuple!(I->IResult<I,A>, I->IResult<I,B>, ... I->IResult<I,X>) => I -> IResult<I, (A, B, ..., X)>`
|
||||
/// chains parsers and assemble the sub results in a tuple.
|
||||
///
|
||||
/// The input type `I` must implement `nom::InputLength`.
|
||||
///
|
||||
/// This combinator will count how much data is consumed by every child parser
|
||||
/// and take it into account if there is not enough data
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::{self, Done, Error};
|
||||
/// # #[cfg(feature = "verbose-errors")]
|
||||
/// # use nom::Err::Position;
|
||||
/// # use nom::ErrorKind;
|
||||
/// # use nom::be_u16;
|
||||
/// // the return type depends of the children parsers
|
||||
/// named!(parser<&[u8], (u16, &[u8], &[u8]) >,
|
||||
/// tuple!(
|
||||
/// be_u16 ,
|
||||
/// take!(3),
|
||||
/// tag!("fg")
|
||||
/// )
|
||||
/// );
|
||||
///
|
||||
/// # fn main() {
|
||||
/// assert_eq!(
|
||||
/// parser(&b"abcdefgh"[..]),
|
||||
/// Done(
|
||||
/// &b"h"[..],
|
||||
/// (0x6162u16, &b"cde"[..], &b"fg"[..])
|
||||
/// )
|
||||
/// );
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! tuple (
|
||||
($i:expr, $($rest:tt)*) => (
|
||||
{
|
||||
tuple_parser!($i, 0usize, (), $($rest)*)
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// Internal parser, do not use directly
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! tuple_parser (
|
||||
($i:expr, $consumed:expr, ($($parsed:tt),*), $e:ident, $($rest:tt)*) => (
|
||||
tuple_parser!($i, $consumed, ($($parsed),*), call!($e), $($rest)*);
|
||||
);
|
||||
($i:expr, $consumed:expr, (), $submac:ident!( $($args:tt)* ), $($rest:tt)*) => (
|
||||
{
|
||||
let i_ = $i.clone();
|
||||
match $submac!(i_, $($args)*) {
|
||||
$crate::IResult::Error(e) =>
|
||||
$crate::IResult::Error(e),
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown) =>
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(i)) => {
|
||||
let (needed,overflowed) = $consumed.overflowing_add(i);
|
||||
match overflowed {
|
||||
true => $crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
false => $crate::IResult::Incomplete($crate::Needed::Size(needed)),
|
||||
}
|
||||
},
|
||||
$crate::IResult::Done(i,o) => {
|
||||
let i_ = i.clone();
|
||||
tuple_parser!(i_,
|
||||
$consumed + ($crate::InputLength::input_len(&($i)) -
|
||||
$crate::InputLength::input_len(&i)), (o), $($rest)*)
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
($i:expr, $consumed:expr, ($($parsed:tt)*), $submac:ident!( $($args:tt)* ), $($rest:tt)*) => (
|
||||
{
|
||||
let i_ = $i.clone();
|
||||
match $submac!(i_, $($args)*) {
|
||||
$crate::IResult::Error(e) =>
|
||||
$crate::IResult::Error(e),
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown) =>
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(i)) => {
|
||||
let (needed,overflowed) = $consumed.overflowing_add(i);
|
||||
match overflowed {
|
||||
true => $crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
false => $crate::IResult::Incomplete($crate::Needed::Size(needed)),
|
||||
}
|
||||
},
|
||||
$crate::IResult::Done(i,o) => {
|
||||
let i_ = i.clone();
|
||||
tuple_parser!(i_,
|
||||
$consumed + ($crate::InputLength::input_len(&($i)) -
|
||||
$crate::InputLength::input_len(&i)), ($($parsed)* , o), $($rest)*)
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
($i:expr, $consumed:expr, ($($parsed:tt),*), $e:ident) => (
|
||||
tuple_parser!($i, $consumed, ($($parsed),*), call!($e));
|
||||
);
|
||||
($i:expr, $consumed:expr, (), $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
let i_ = $i.clone();
|
||||
match $submac!(i_, $($args)*) {
|
||||
$crate::IResult::Error(e) =>
|
||||
$crate::IResult::Error(e),
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown) =>
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(i)) => {
|
||||
let (needed,overflowed) = $consumed.overflowing_add(i);
|
||||
match overflowed {
|
||||
true => $crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
false => $crate::IResult::Incomplete($crate::Needed::Size(needed)),
|
||||
}
|
||||
},
|
||||
$crate::IResult::Done(i,o) => {
|
||||
$crate::IResult::Done(i, (o))
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
($i:expr, $consumed:expr, ($($parsed:expr),*), $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
match $submac!($i, $($args)*) {
|
||||
$crate::IResult::Error(e) =>
|
||||
$crate::IResult::Error(e),
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown) =>
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(i)) => {
|
||||
let (needed,overflowed) = $consumed.overflowing_add(i);
|
||||
match overflowed {
|
||||
true => $crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
false => $crate::IResult::Incomplete($crate::Needed::Size(needed)),
|
||||
}
|
||||
},
|
||||
$crate::IResult::Done(i,o) => {
|
||||
$crate::IResult::Done(i, ($($parsed),* , o))
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
($i:expr, $consumed:expr, ($($parsed:expr),*)) => (
|
||||
{
|
||||
$crate::IResult::Done($i, ($($parsed),*))
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// `pair!(I -> IResult<I,O>, I -> IResult<I,P>) => I -> IResult<I, (O,P)>`
|
||||
/// pair(X,Y), returns (x,y)
|
||||
///
|
||||
#[macro_export]
|
||||
macro_rules! pair(
|
||||
($i:expr, $submac:ident!( $($args:tt)* ), $submac2:ident!( $($args2:tt)* )) => (
|
||||
{
|
||||
tuple!($i, $submac!($($args)*), $submac2!($($args2)*))
|
||||
}
|
||||
);
|
||||
|
||||
($i:expr, $submac:ident!( $($args:tt)* ), $g:expr) => (
|
||||
pair!($i, $submac!($($args)*), call!($g));
|
||||
);
|
||||
|
||||
($i:expr, $f:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
pair!($i, call!($f), $submac!($($args)*));
|
||||
);
|
||||
|
||||
($i:expr, $f:expr, $g:expr) => (
|
||||
pair!($i, call!($f), call!($g));
|
||||
);
|
||||
);
|
||||
|
||||
/// `separated_pair!(I -> IResult<I,O>, I -> IResult<I, T>, I -> IResult<I,P>) => I -> IResult<I, (O,P)>`
|
||||
/// separated_pair(X,sep,Y) returns (x,y)
|
||||
#[macro_export]
|
||||
macro_rules! separated_pair(
|
||||
($i:expr, $submac:ident!( $($args:tt)* ), $($rest:tt)+) => (
|
||||
{
|
||||
match tuple_parser!($i, 0usize, (), $submac!($($args)*), $($rest)*) {
|
||||
$crate::IResult::Error(a) => $crate::IResult::Error(a),
|
||||
$crate::IResult::Incomplete(i) => $crate::IResult::Incomplete(i),
|
||||
$crate::IResult::Done(i1, (o1, _, o2)) => {
|
||||
$crate::IResult::Done(i1, (o1, o2))
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
($i:expr, $f:expr, $($rest:tt)+) => (
|
||||
separated_pair!($i, call!($f), $($rest)*);
|
||||
);
|
||||
);
|
||||
|
||||
/// `preceded!(I -> IResult<I,T>, I -> IResult<I,O>) => I -> IResult<I, O>`
|
||||
/// preceded(opening, X) returns X
|
||||
#[macro_export]
|
||||
macro_rules! preceded(
|
||||
($i:expr, $submac:ident!( $($args:tt)* ), $submac2:ident!( $($args2:tt)* )) => (
|
||||
{
|
||||
match tuple!($i, $submac!($($args)*), $submac2!($($args2)*)) {
|
||||
$crate::IResult::Error(a) => $crate::IResult::Error(a),
|
||||
$crate::IResult::Incomplete(i) => $crate::IResult::Incomplete(i),
|
||||
$crate::IResult::Done(remaining, (_,o)) => {
|
||||
$crate::IResult::Done(remaining, o)
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
($i:expr, $submac:ident!( $($args:tt)* ), $g:expr) => (
|
||||
preceded!($i, $submac!($($args)*), call!($g));
|
||||
);
|
||||
|
||||
($i:expr, $f:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
preceded!($i, call!($f), $submac!($($args)*));
|
||||
);
|
||||
|
||||
($i:expr, $f:expr, $g:expr) => (
|
||||
preceded!($i, call!($f), call!($g));
|
||||
);
|
||||
);
|
||||
|
||||
/// `terminated!(I -> IResult<I,O>, I -> IResult<I,T>) => I -> IResult<I, O>`
|
||||
/// terminated(X, closing) returns X
|
||||
#[macro_export]
|
||||
macro_rules! terminated(
|
||||
($i:expr, $submac:ident!( $($args:tt)* ), $submac2:ident!( $($args2:tt)* )) => (
|
||||
{
|
||||
match tuple!($i, $submac!($($args)*), $submac2!($($args2)*)) {
|
||||
$crate::IResult::Error(a) => $crate::IResult::Error(a),
|
||||
$crate::IResult::Incomplete(i) => $crate::IResult::Incomplete(i),
|
||||
$crate::IResult::Done(remaining, (o,_)) => {
|
||||
$crate::IResult::Done(remaining, o)
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
($i:expr, $submac:ident!( $($args:tt)* ), $g:expr) => (
|
||||
terminated!($i, $submac!($($args)*), call!($g));
|
||||
);
|
||||
|
||||
($i:expr, $f:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
terminated!($i, call!($f), $submac!($($args)*));
|
||||
);
|
||||
|
||||
($i:expr, $f:expr, $g:expr) => (
|
||||
terminated!($i, call!($f), call!($g));
|
||||
);
|
||||
);
|
||||
|
||||
/// `delimited!(I -> IResult<I,T>, I -> IResult<I,O>, I -> IResult<I,U>) => I -> IResult<I, O>`
|
||||
/// delimited(opening, X, closing) returns X
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::{self, Done};
|
||||
/// named!(bracketed,
|
||||
/// delimited!(
|
||||
/// tag!("("),
|
||||
/// take_until!(")"),
|
||||
/// tag!(")")
|
||||
/// )
|
||||
/// );
|
||||
///
|
||||
/// # fn main() {
|
||||
/// let input = &b"(test)"[..];
|
||||
/// assert_eq!(bracketed(input), Done(&b""[..], &b"test"[..]));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! delimited(
|
||||
($i:expr, $submac:ident!( $($args:tt)* ), $($rest:tt)+) => (
|
||||
{
|
||||
match tuple_parser!($i, 0usize, (), $submac!($($args)*), $($rest)*) {
|
||||
$crate::IResult::Error(a) => $crate::IResult::Error(a),
|
||||
$crate::IResult::Incomplete(i) => $crate::IResult::Incomplete(i),
|
||||
$crate::IResult::Done(i1, (_, o, _)) => {
|
||||
$crate::IResult::Done(i1, o)
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
($i:expr, $f:expr, $($rest:tt)+) => (
|
||||
delimited!($i, call!($f), $($rest)*);
|
||||
);
|
||||
);
|
||||
|
||||
/// `do_parse!(I->IResult<I,A> >> I->IResult<I,B> >> ... I->IResult<I,X> , ( O ) ) => I -> IResult<I, O>`
|
||||
/// do_parse applies sub parsers in a sequence.
|
||||
/// it can store intermediary results and make them available
|
||||
/// for later parsers
|
||||
///
|
||||
/// The input type `I` must implement `nom::InputLength`.
|
||||
///
|
||||
/// This combinator will count how much data is consumed by every child parser
|
||||
/// and take it into account if there is not enough data
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::{self, Done, Incomplete};
|
||||
/// # use nom::Needed;
|
||||
/// use nom::be_u8;
|
||||
///
|
||||
/// // this parser implements a common pattern in binary formats,
|
||||
/// // the TAG-LENGTH-VALUE, where you first recognize a specific
|
||||
/// // byte slice, then the next bytes indicate the length of
|
||||
/// // the data, then you take that slice and return it
|
||||
/// //
|
||||
/// // here, we match the tag 42, take the length in the next byte
|
||||
/// // and store it in `length`, then use `take!` with `length`
|
||||
/// // to obtain the subslice that we store in `bytes`, then return
|
||||
/// // `bytes`
|
||||
/// named!(tag_length_value,
|
||||
/// do_parse!(
|
||||
/// tag!( &[ 42u8 ][..] ) >>
|
||||
/// length: be_u8 >>
|
||||
/// bytes: take!(length) >>
|
||||
/// (bytes)
|
||||
/// )
|
||||
/// );
|
||||
///
|
||||
/// # fn main() {
|
||||
/// let a: Vec<u8> = vec!(42, 2, 3, 4, 5);
|
||||
/// let result_a: Vec<u8> = vec!(3, 4);
|
||||
/// let rest_a: Vec<u8> = vec!(5);
|
||||
/// assert_eq!(tag_length_value(&a[..]), Done(&rest_a[..], &result_a[..]));
|
||||
///
|
||||
/// // here, the length is 5, but there are only 3 bytes afterwards (3, 4 and 5),
|
||||
/// // so the parser will tell you that you need 7 bytes: one for the tag,
|
||||
/// // one for the length, then 5 bytes
|
||||
/// let b: Vec<u8> = vec!(42, 5, 3, 4, 5);
|
||||
/// assert_eq!(tag_length_value(&b[..]), Incomplete(Needed::Size(7)));
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// the result is a tuple, so you can return multiple sub results, like
|
||||
/// this:
|
||||
/// `do_parse!(I->IResult<I,A> >> I->IResult<I,B> >> ... I->IResult<I,X> , ( O, P ) ) => I -> IResult<I, (O,P)>`
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::{self, Done, Incomplete};
|
||||
/// # use nom::Needed;
|
||||
/// use nom::be_u8;
|
||||
/// named!(tag_length_value<(u8, &[u8])>,
|
||||
/// do_parse!(
|
||||
/// tag!( &[ 42u8 ][..] ) >>
|
||||
/// length: be_u8 >>
|
||||
/// bytes: take!(length) >>
|
||||
/// (length, bytes)
|
||||
/// )
|
||||
/// );
|
||||
///
|
||||
/// # fn main() {
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
#[macro_export]
|
||||
macro_rules! do_parse (
|
||||
(__impl $i:expr, $consumed:expr, ( $($rest:expr),* )) => (
|
||||
$crate::IResult::Done($i, ( $($rest),* ))
|
||||
);
|
||||
|
||||
(__impl $i:expr, $consumed:expr, $field:ident : $submac:ident!( $($args:tt)* ) ) => (
|
||||
do_parse!(__impl $i, $consumed, $submac!( $($args)* ))
|
||||
);
|
||||
|
||||
(__impl $i:expr, $consumed:expr, $submac:ident!( $($args:tt)* ) ) => (
|
||||
compiler_error!("do_parse is missing the return value. A do_parse call must end
|
||||
with a return value between parenthesis, as follows:
|
||||
|
||||
do_parse!(
|
||||
a: tag!(\"abcd\") >>
|
||||
b: tag!(\"efgh\") >>
|
||||
|
||||
( Value { a: a, b: b } )
|
||||
");
|
||||
);
|
||||
|
||||
(__impl $i:expr, $consumed:expr, $field:ident : $submac:ident!( $($args:tt)* ) ~ $($rest:tt)* ) => (
|
||||
compiler_error!("do_parse uses >> as separator, not ~");
|
||||
);
|
||||
(__impl $i:expr, $consumed:expr, $submac:ident!( $($args:tt)* ) ~ $($rest:tt)* ) => (
|
||||
compiler_error!("do_parse uses >> as separator, not ~");
|
||||
);
|
||||
(__impl $i:expr, $consumed:expr, $field:ident : $e:ident ~ $($rest:tt)*) => (
|
||||
do_parse!(__impl $i, $consumed, $field: call!($e) ~ $($rest)*);
|
||||
);
|
||||
(__impl $i:expr, $consumed:expr, $e:ident ~ $($rest:tt)*) => (
|
||||
do_parse!(__impl $i, $consumed, call!($e) ~ $($rest)*);
|
||||
);
|
||||
|
||||
(__impl $i:expr, $consumed:expr, $e:ident >> $($rest:tt)*) => (
|
||||
do_parse!(__impl $i, $consumed, call!($e) >> $($rest)*);
|
||||
);
|
||||
(__impl $i:expr, $consumed:expr, $submac:ident!( $($args:tt)* ) >> $($rest:tt)*) => (
|
||||
{
|
||||
let i_ = $i.clone();
|
||||
match $submac!(i_, $($args)*) {
|
||||
$crate::IResult::Error(e) => $crate::IResult::Error(e),
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown) =>
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(i)) => {
|
||||
let (needed,overflowed) = $consumed.overflowing_add(i);
|
||||
match overflowed {
|
||||
true => $crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
false => $crate::IResult::Incomplete($crate::Needed::Size(needed)),
|
||||
}
|
||||
},
|
||||
$crate::IResult::Done(i,_) => {
|
||||
let i_ = i.clone();
|
||||
do_parse!(__impl i_,
|
||||
$consumed + ($crate::InputLength::input_len(&($i)) -
|
||||
$crate::InputLength::input_len(&i)), $($rest)*)
|
||||
},
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
(__impl $i:expr, $consumed:expr, $field:ident : $e:ident >> $($rest:tt)*) => (
|
||||
do_parse!(__impl $i, $consumed, $field: call!($e) >> $($rest)*);
|
||||
);
|
||||
|
||||
(__impl $i:expr, $consumed:expr, $field:ident : $submac:ident!( $($args:tt)* ) >> $($rest:tt)*) => (
|
||||
{
|
||||
let i_ = $i.clone();
|
||||
match $submac!(i_, $($args)*) {
|
||||
$crate::IResult::Error(e) => $crate::IResult::Error(e),
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown) =>
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(i)) => {
|
||||
let (needed,overflowed) = $consumed.overflowing_add(i);
|
||||
match overflowed {
|
||||
true => $crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
false => $crate::IResult::Incomplete($crate::Needed::Size(needed)),
|
||||
}
|
||||
},
|
||||
$crate::IResult::Done(i,o) => {
|
||||
let $field = o;
|
||||
let i_ = i.clone();
|
||||
do_parse!(__impl i_,
|
||||
$consumed + ($crate::InputLength::input_len(&($i)) -
|
||||
$crate::InputLength::input_len(&i)), $($rest)*)
|
||||
},
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// ending the chain
|
||||
(__impl $i:expr, $consumed:expr, $e:ident >> ( $($rest:tt)* )) => (
|
||||
do_parse!(__impl $i, $consumed, call!($e) >> ( $($rest)* ));
|
||||
);
|
||||
|
||||
(__impl $i:expr, $consumed:expr, $submac:ident!( $($args:tt)* ) >> ( $($rest:tt)* )) => (
|
||||
match $submac!($i, $($args)*) {
|
||||
$crate::IResult::Error(e) => $crate::IResult::Error(e),
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown) =>
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(i)) => {
|
||||
let (needed,overflowed) = $consumed.overflowing_add(i);
|
||||
match overflowed {
|
||||
true => $crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
false => $crate::IResult::Incomplete($crate::Needed::Size(needed)),
|
||||
}
|
||||
},
|
||||
$crate::IResult::Done(i,_) => {
|
||||
$crate::IResult::Done(i, ( $($rest)* ))
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
(__impl $i:expr, $consumed:expr, $field:ident : $e:ident >> ( $($rest:tt)* )) => (
|
||||
do_parse!(__impl $i, $consumed, $field: call!($e) >> ( $($rest)* ) );
|
||||
);
|
||||
|
||||
(__impl $i:expr, $consumed:expr, $field:ident : $submac:ident!( $($args:tt)* ) >> ( $($rest:tt)* )) => (
|
||||
match $submac!($i, $($args)*) {
|
||||
$crate::IResult::Error(e) => $crate::IResult::Error(e),
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown) =>
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(i)) => {
|
||||
let (needed,overflowed) = $consumed.overflowing_add(i);
|
||||
match overflowed {
|
||||
true => $crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
false => $crate::IResult::Incomplete($crate::Needed::Size(needed)),
|
||||
}
|
||||
},
|
||||
$crate::IResult::Done(i,o) => {
|
||||
let $field = o;
|
||||
$crate::IResult::Done(i, ( $($rest)* ))
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
($i:expr, $($rest:tt)*) => (
|
||||
{
|
||||
do_parse!(__impl $i, 0usize, $($rest)*)
|
||||
}
|
||||
);
|
||||
($submac:ident!( $($args:tt)* ) >> $($rest:tt)* ) => (
|
||||
compiler_error!("if you are using do_parse outside of a named! macro, you must
|
||||
pass the input data as first argument, like this:
|
||||
|
||||
let res = do_parse!(input,
|
||||
a: tag!(\"abcd\") >>
|
||||
b: tag!(\"efgh\") >>
|
||||
( Value { a: a, b: b } )
|
||||
);");
|
||||
);
|
||||
($e:ident! >> $($rest:tt)* ) => (
|
||||
do_parse!( call!($e) >> $($rest)*);
|
||||
);
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use internal::{Needed,IResult};
|
||||
use internal::IResult::*;
|
||||
use util::ErrorKind;
|
||||
use nom::be_u16;
|
||||
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
use verbose_errors::Err;
|
||||
|
||||
// reproduce the tag and take macros, because of module import order
|
||||
macro_rules! tag (
|
||||
($i:expr, $inp: expr) => (
|
||||
{
|
||||
#[inline(always)]
|
||||
fn as_bytes<T: $crate::AsBytes>(b: &T) -> &[u8] {
|
||||
b.as_bytes()
|
||||
}
|
||||
|
||||
let expected = $inp;
|
||||
let bytes = as_bytes(&expected);
|
||||
|
||||
tag_bytes!($i,bytes)
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
macro_rules! tag_bytes (
|
||||
($i:expr, $bytes: expr) => (
|
||||
{
|
||||
use std::cmp::min;
|
||||
let len = $i.len();
|
||||
let blen = $bytes.len();
|
||||
let m = min(len, blen);
|
||||
let reduced = &$i[..m];
|
||||
let b = &$bytes[..m];
|
||||
|
||||
let res: $crate::IResult<_,_> = if reduced != b {
|
||||
$crate::IResult::Error(error_position!($crate::ErrorKind::Tag, $i))
|
||||
} else if m < blen {
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(blen))
|
||||
} else {
|
||||
$crate::IResult::Done(&$i[blen..], reduced)
|
||||
};
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
macro_rules! take (
|
||||
($i:expr, $count:expr) => (
|
||||
{
|
||||
let cnt = $count as usize;
|
||||
let res:$crate::IResult<&[u8],&[u8]> = if $i.len() < cnt {
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(cnt))
|
||||
} else {
|
||||
$crate::IResult::Done(&$i[cnt..],&$i[0..cnt])
|
||||
};
|
||||
res
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
#[derive(PartialEq,Eq,Debug)]
|
||||
struct B {
|
||||
a: u8,
|
||||
b: u8
|
||||
}
|
||||
|
||||
#[derive(PartialEq,Eq,Debug)]
|
||||
struct C {
|
||||
a: u8,
|
||||
b: Option<u8>
|
||||
}
|
||||
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
use util::{error_to_list, add_error_pattern, print_error};
|
||||
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
fn error_to_string<P>(e: &Err<P>) -> &'static str {
|
||||
let v:Vec<ErrorKind> = error_to_list(e);
|
||||
// do it this way if you can use slice patterns
|
||||
/*
|
||||
match &v[..] {
|
||||
[ErrorKind::Custom(42), ErrorKind::Tag] => "missing `ijkl` tag",
|
||||
[ErrorKind::Custom(42), ErrorKind::Custom(128), ErrorKind::Tag] => "missing `mnop` tag after `ijkl`",
|
||||
_ => "unrecognized error"
|
||||
}
|
||||
*/
|
||||
if &v[..] == [ErrorKind::Custom(42),ErrorKind::Tag] {
|
||||
"missing `ijkl` tag"
|
||||
} else if &v[..] == [ErrorKind::Custom(42), ErrorKind::Custom(128), ErrorKind::Tag] {
|
||||
"missing `mnop` tag after `ijkl`"
|
||||
} else {
|
||||
"unrecognized error"
|
||||
}
|
||||
}
|
||||
|
||||
// do it this way if you can use box patterns
|
||||
/*use std::str;
|
||||
fn error_to_string(e:Err) -> String
|
||||
match e {
|
||||
NodePosition(ErrorKind::Custom(42), i1, box Position(ErrorKind::Tag, i2)) => {
|
||||
format!("missing `ijkl` tag, found '{}' instead", str::from_utf8(i2).unwrap())
|
||||
},
|
||||
NodePosition(ErrorKind::Custom(42), i1, box NodePosition(ErrorKind::Custom(128), i2, box Position(ErrorKind::Tag, i3))) => {
|
||||
format!("missing `mnop` tag after `ijkl`, found '{}' instead", str::from_utf8(i3).unwrap())
|
||||
},
|
||||
_ => "unrecognized error".to_string()
|
||||
}
|
||||
}*/
|
||||
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
use std::collections;
|
||||
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
#[test]
|
||||
fn err() {
|
||||
named!(err_test, alt!(
|
||||
tag!("abcd") |
|
||||
preceded!(tag!("efgh"), return_error!(ErrorKind::Custom(42),
|
||||
do_parse!(
|
||||
tag!("ijkl") >>
|
||||
res: return_error!(ErrorKind::Custom(128), tag!("mnop")) >>
|
||||
(res)
|
||||
)
|
||||
)
|
||||
)
|
||||
));
|
||||
let a = &b"efghblah"[..];
|
||||
let b = &b"efghijklblah"[..];
|
||||
let c = &b"efghijklmnop"[..];
|
||||
|
||||
let blah = &b"blah"[..];
|
||||
|
||||
let res_a = err_test(a);
|
||||
let res_b = err_test(b);
|
||||
let res_c = err_test(c);
|
||||
assert_eq!(res_a, Error(error_node_position!(ErrorKind::Custom(42), blah, error_position!(ErrorKind::Tag, blah))));
|
||||
assert_eq!(res_b, Error(error_node_position!(ErrorKind::Custom(42), &b"ijklblah"[..], error_node_position!(ErrorKind::Custom(128), blah, error_position!(ErrorKind::Tag, blah)))));
|
||||
assert_eq!(res_c, Done(&b""[..], &b"mnop"[..]));
|
||||
|
||||
// Merr-like error matching
|
||||
let mut err_map = collections::HashMap::new();
|
||||
assert!(add_error_pattern(&mut err_map, err_test(&b"efghpouet"[..]), "missing `ijkl` tag"));
|
||||
assert!(add_error_pattern(&mut err_map, err_test(&b"efghijklpouet"[..]), "missing `mnop` tag after `ijkl`"));
|
||||
|
||||
let res_a2 = res_a.clone();
|
||||
match res_a {
|
||||
Error(e) => {
|
||||
assert_eq!(error_to_list(&e), [ErrorKind::Custom(42), ErrorKind::Tag]);
|
||||
assert_eq!(error_to_string(&e), "missing `ijkl` tag");
|
||||
assert_eq!(err_map.get(&error_to_list(&e)), Some(&"missing `ijkl` tag"));
|
||||
},
|
||||
_ => panic!()
|
||||
};
|
||||
|
||||
let res_b2 = res_b.clone();
|
||||
match res_b {
|
||||
Error(e) => {
|
||||
assert_eq!(error_to_list(&e), [ErrorKind::Custom(42), ErrorKind::Custom(128), ErrorKind::Tag]);
|
||||
assert_eq!(error_to_string(&e), "missing `mnop` tag after `ijkl`");
|
||||
assert_eq!(err_map.get(&error_to_list(&e)), Some(&"missing `mnop` tag after `ijkl`"));
|
||||
},
|
||||
_ => panic!()
|
||||
};
|
||||
|
||||
print_error(a, res_a2);
|
||||
print_error(b, res_b2);
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
#[test]
|
||||
fn add_err() {
|
||||
named!(err_test,
|
||||
preceded!(tag!("efgh"), add_return_error!(ErrorKind::Custom(42),
|
||||
do_parse!(
|
||||
tag!("ijkl") >>
|
||||
res: add_return_error!(ErrorKind::Custom(128), tag!("mnop")) >>
|
||||
(res)
|
||||
)
|
||||
)
|
||||
));
|
||||
let a = &b"efghblah"[..];
|
||||
let b = &b"efghijklblah"[..];
|
||||
let c = &b"efghijklmnop"[..];
|
||||
|
||||
let blah = &b"blah"[..];
|
||||
|
||||
let res_a = err_test(a);
|
||||
let res_b = err_test(b);
|
||||
let res_c = err_test(c);
|
||||
assert_eq!(res_a, Error(error_node_position!(ErrorKind::Custom(42), blah, error_position!(ErrorKind::Tag, blah))));
|
||||
assert_eq!(res_b, Error(error_node_position!(ErrorKind::Custom(42), &b"ijklblah"[..], error_node_position!(ErrorKind::Custom(128), blah, error_position!(ErrorKind::Tag, blah)))));
|
||||
assert_eq!(res_c, Done(&b""[..], &b"mnop"[..]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn complete() {
|
||||
named!(err_test,
|
||||
do_parse!(
|
||||
tag!("ijkl") >>
|
||||
res: complete!(tag!("mnop")) >>
|
||||
(res)
|
||||
)
|
||||
);
|
||||
let a = &b"ijklmn"[..];
|
||||
|
||||
let res_a = err_test(a);
|
||||
assert_eq!(res_a, Error(error_position!(ErrorKind::Complete, &b"mn"[..])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pair() {
|
||||
named!( tag_abc, tag!("abc") );
|
||||
named!( tag_def, tag!("def") );
|
||||
named!( pair_abc_def<&[u8],(&[u8], &[u8])>, pair!(tag_abc, tag_def) );
|
||||
|
||||
assert_eq!(pair_abc_def(&b"abcdefghijkl"[..]), Done(&b"ghijkl"[..], (&b"abc"[..], &b"def"[..])));
|
||||
assert_eq!(pair_abc_def(&b"ab"[..]), Incomplete(Needed::Size(3)));
|
||||
assert_eq!(pair_abc_def(&b"abcd"[..]), Incomplete(Needed::Size(6)));
|
||||
assert_eq!(pair_abc_def(&b"xxx"[..]), Error(error_position!(ErrorKind::Tag, &b"xxx"[..])));
|
||||
assert_eq!(pair_abc_def(&b"xxxdef"[..]), Error(error_position!(ErrorKind::Tag, &b"xxxdef"[..])));
|
||||
assert_eq!(pair_abc_def(&b"abcxxx"[..]), Error(error_position!(ErrorKind::Tag, &b"xxx"[..])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn separated_pair() {
|
||||
named!( tag_abc, tag!("abc") );
|
||||
named!( tag_def, tag!("def") );
|
||||
named!( tag_separator, tag!(",") );
|
||||
named!( sep_pair_abc_def<&[u8],(&[u8], &[u8])>, separated_pair!(tag_abc, tag_separator, tag_def) );
|
||||
|
||||
assert_eq!(sep_pair_abc_def(&b"abc,defghijkl"[..]), Done(&b"ghijkl"[..], (&b"abc"[..], &b"def"[..])));
|
||||
assert_eq!(sep_pair_abc_def(&b"ab"[..]), Incomplete(Needed::Size(3)));
|
||||
assert_eq!(sep_pair_abc_def(&b"abc,d"[..]), Incomplete(Needed::Size(7)));
|
||||
assert_eq!(sep_pair_abc_def(&b"xxx"[..]), Error(error_position!(ErrorKind::Tag, &b"xxx"[..])));
|
||||
assert_eq!(sep_pair_abc_def(&b"xxx,def"[..]), Error(error_position!(ErrorKind::Tag, &b"xxx,def"[..])));
|
||||
assert_eq!(sep_pair_abc_def(&b"abc,xxx"[..]), Error(error_position!(ErrorKind::Tag, &b"xxx"[..])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preceded() {
|
||||
named!( tag_abcd, tag!("abcd") );
|
||||
named!( tag_efgh, tag!("efgh") );
|
||||
named!( preceded_abcd_efgh<&[u8], &[u8]>, preceded!(tag_abcd, tag_efgh) );
|
||||
|
||||
assert_eq!(preceded_abcd_efgh(&b"abcdefghijkl"[..]), Done(&b"ijkl"[..], &b"efgh"[..]));
|
||||
assert_eq!(preceded_abcd_efgh(&b"ab"[..]), Incomplete(Needed::Size(4)));
|
||||
assert_eq!(preceded_abcd_efgh(&b"abcde"[..]), Incomplete(Needed::Size(8)));
|
||||
assert_eq!(preceded_abcd_efgh(&b"xxx"[..]), Error(error_position!(ErrorKind::Tag, &b"xxx"[..])));
|
||||
assert_eq!(preceded_abcd_efgh(&b"xxxxdef"[..]), Error(error_position!(ErrorKind::Tag, &b"xxxxdef"[..])));
|
||||
assert_eq!(preceded_abcd_efgh(&b"abcdxxx"[..]), Error(error_position!(ErrorKind::Tag, &b"xxx"[..])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn terminated() {
|
||||
named!( tag_abcd, tag!("abcd") );
|
||||
named!( tag_efgh, tag!("efgh") );
|
||||
named!( terminated_abcd_efgh<&[u8], &[u8]>, terminated!(tag_abcd, tag_efgh) );
|
||||
|
||||
assert_eq!(terminated_abcd_efgh(&b"abcdefghijkl"[..]), Done(&b"ijkl"[..], &b"abcd"[..]));
|
||||
assert_eq!(terminated_abcd_efgh(&b"ab"[..]), Incomplete(Needed::Size(4)));
|
||||
assert_eq!(terminated_abcd_efgh(&b"abcde"[..]), Incomplete(Needed::Size(8)));
|
||||
assert_eq!(terminated_abcd_efgh(&b"xxx"[..]), Error(error_position!(ErrorKind::Tag, &b"xxx"[..])));
|
||||
assert_eq!(terminated_abcd_efgh(&b"xxxxdef"[..]), Error(error_position!(ErrorKind::Tag, &b"xxxxdef"[..])));
|
||||
assert_eq!(terminated_abcd_efgh(&b"abcdxxxx"[..]), Error(error_position!(ErrorKind::Tag, &b"xxxx"[..])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delimited() {
|
||||
named!( tag_abc, tag!("abc") );
|
||||
named!( tag_def, tag!("def") );
|
||||
named!( tag_ghi, tag!("ghi") );
|
||||
named!( delimited_abc_def_ghi<&[u8], &[u8]>, delimited!(tag_abc, tag_def, tag_ghi) );
|
||||
|
||||
assert_eq!(delimited_abc_def_ghi(&b"abcdefghijkl"[..]), Done(&b"jkl"[..], &b"def"[..]));
|
||||
assert_eq!(delimited_abc_def_ghi(&b"ab"[..]), Incomplete(Needed::Size(3)));
|
||||
assert_eq!(delimited_abc_def_ghi(&b"abcde"[..]), Incomplete(Needed::Size(6)));
|
||||
assert_eq!(delimited_abc_def_ghi(&b"abcdefgh"[..]), Incomplete(Needed::Size(9)));
|
||||
assert_eq!(delimited_abc_def_ghi(&b"xxx"[..]), Error(error_position!(ErrorKind::Tag, &b"xxx"[..])));
|
||||
assert_eq!(delimited_abc_def_ghi(&b"xxxdefghi"[..]), Error(error_position!(ErrorKind::Tag, &b"xxxdefghi"[..])));
|
||||
assert_eq!(delimited_abc_def_ghi(&b"abcxxxghi"[..]), Error(error_position!(ErrorKind::Tag, &b"xxxghi"[..])));
|
||||
assert_eq!(delimited_abc_def_ghi(&b"abcdefxxx"[..]), Error(error_position!(ErrorKind::Tag, &b"xxx"[..])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tuple_test() {
|
||||
named!(tuple_3<&[u8], (u16, &[u8], &[u8]) >,
|
||||
tuple!( be_u16 , take!(3), tag!("fg") ) );
|
||||
|
||||
assert_eq!(tuple_3(&b"abcdefgh"[..]), Done(&b"h"[..], (0x6162u16, &b"cde"[..], &b"fg"[..])));
|
||||
assert_eq!(tuple_3(&b"abcd"[..]), Incomplete(Needed::Size(5)));
|
||||
assert_eq!(tuple_3(&b"abcde"[..]), Incomplete(Needed::Size(7)));
|
||||
assert_eq!(tuple_3(&b"abcdejk"[..]), Error(error_position!(ErrorKind::Tag, &b"jk"[..])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn do_parse() {
|
||||
fn ret_int1(i:&[u8]) -> IResult<&[u8], u8> { Done(i,1) };
|
||||
fn ret_int2(i:&[u8]) -> IResult<&[u8], u8> { Done(i,2) };
|
||||
|
||||
//trace_macros!(true);
|
||||
named!(do_parser<&[u8], (u8, u8)>,
|
||||
do_parse!(
|
||||
tag!("abcd") >>
|
||||
opt!(tag!("abcd")) >>
|
||||
aa: ret_int1 >>
|
||||
tag!("efgh") >>
|
||||
bb: ret_int2 >>
|
||||
tag!("efgh") >>
|
||||
(aa, bb)
|
||||
)
|
||||
);
|
||||
//named!(do_parser<&[u8], (u8, u8)>,
|
||||
// do_parse!(
|
||||
// tag!("abcd") >> aa: ret_int1 >> tag!("efgh") >> bb: ret_int2 >> tag!("efgh") >> (aa, bb)
|
||||
// )
|
||||
//);
|
||||
|
||||
//trace_macros!(false);
|
||||
|
||||
assert_eq!(do_parser(&b"abcdabcdefghefghX"[..]), Done(&b"X"[..], (1, 2)));
|
||||
assert_eq!(do_parser(&b"abcdefghefghX"[..]), Done(&b"X"[..], (1, 2)));
|
||||
assert_eq!(do_parser(&b"abcdab"[..]), Incomplete(Needed::Size(8)));
|
||||
assert_eq!(do_parser(&b"abcdefghef"[..]), Incomplete(Needed::Size(12)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn do_parse_dependency() {
|
||||
use nom::be_u8;
|
||||
|
||||
named!(length_value,
|
||||
do_parse!(
|
||||
length: be_u8 >>
|
||||
bytes: take!(length) >>
|
||||
(bytes)
|
||||
)
|
||||
);
|
||||
|
||||
let a = [2u8, 3, 4, 5];
|
||||
let res_a = [3u8, 4];
|
||||
assert_eq!(length_value(&a[..]), Done(&a[3..], &res_a[..]));
|
||||
let b = [5u8, 3, 4, 5];
|
||||
assert_eq!(length_value(&b[..]), Incomplete(Needed::Size(6)));
|
||||
}
|
||||
|
||||
/*
|
||||
named!(does_not_compile,
|
||||
do_parse!(
|
||||
length: be_u8 >>
|
||||
bytes: take!(length)
|
||||
)
|
||||
);
|
||||
named!(does_not_compile_either,
|
||||
do_parse!(
|
||||
length: be_u8 ~
|
||||
bytes: take!(length) ~
|
||||
( () )
|
||||
)
|
||||
);
|
||||
fn still_does_not_compile() {
|
||||
let data = b"abcd";
|
||||
|
||||
let res = do_parse!(
|
||||
tag!("abcd") >>
|
||||
tag!("efgh") >>
|
||||
( () )
|
||||
);
|
||||
}
|
||||
*/
|
||||
}
|
153
third_party/rust/nom-3.2.1/src/simple_errors.rs
vendored
153
third_party/rust/nom-3.2.1/src/simple_errors.rs
vendored
@ -1,153 +0,0 @@
|
||||
//! Error management
|
||||
//!
|
||||
//! there are two ways to handle errors in nom. The first one,
|
||||
//! activated by default, uses the `nom::ErrorKind<E=u32>` enum
|
||||
//! in the error branch of `IResult`. This enum can hold either
|
||||
//! a parser specific error code, or a custom error type you
|
||||
//! specify.
|
||||
//!
|
||||
//! If you need more advanced error management, you can activate
|
||||
//! the "verbose-errors" compilation feature, which will give you
|
||||
//! the error system available in nom 1.0. The verbose errors
|
||||
//! accumulate error codes and positions as you backtrack through
|
||||
//! the parser tree. From there, you can precisely identify which
|
||||
//! parts of the input triggered the error case.
|
||||
//!
|
||||
//! Please note that the verbose error management is a bit slower
|
||||
//! than the simple one.
|
||||
use util::ErrorKind;
|
||||
use internal::{IResult, IError};
|
||||
use internal::IResult::*;
|
||||
|
||||
pub type Err<E=u32> = ErrorKind<E>;
|
||||
|
||||
impl<I,O,E> IResult<I,O,E> {
|
||||
/// Maps a `IResult<I, O, E>` to `IResult<I, O, N>` by appling a function
|
||||
/// to a contained `Error` value, leaving `Done` and `Incomplete` value
|
||||
/// untouched.
|
||||
#[inline]
|
||||
pub fn map_err<N, F>(self, f: F) -> IResult<I, O, N>
|
||||
where F: FnOnce(Err<E>) -> Err<N> {
|
||||
match self {
|
||||
Error(e) => Error(f(e)),
|
||||
Incomplete(n) => Incomplete(n),
|
||||
Done(i, o) => Done(i, o),
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwrap the contained `Error(E)` value, or panic if the `IResult` is not
|
||||
/// `Error`.
|
||||
pub fn unwrap_err(self) -> Err<E> {
|
||||
match self {
|
||||
Error(e) => e,
|
||||
Done(_, _) => panic!("unwrap_err() called on an IResult that is Done"),
|
||||
Incomplete(_) => panic!("unwrap_err() called on an IResult that is Incomplete"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the IResult to a std::result::Result
|
||||
pub fn to_full_result(self) -> Result<O, IError<E>> {
|
||||
match self {
|
||||
Done(_, o) => Ok(o),
|
||||
Incomplete(n) => Err(IError::Incomplete(n)),
|
||||
Error(e) => Err(IError::Error(e))
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the IResult to a std::result::Result, or panic if the `IResult` is `Incomplete`
|
||||
pub fn to_result(self) -> Result<O, Err<E>> {
|
||||
match self {
|
||||
Done(_, o) => Ok(o),
|
||||
Error(e) => Err(e),
|
||||
Incomplete(_) => panic!("to_result() called on an IResult that is Incomplete")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::any::Any;
|
||||
#[cfg(feature = "std")]
|
||||
use std::{error,fmt};
|
||||
#[cfg(feature = "std")]
|
||||
impl<E: fmt::Debug+Any> error::Error for Err<E> {
|
||||
fn description(&self) -> &str {
|
||||
self.description()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<E: fmt::Debug> fmt::Display for Err<E> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.description())
|
||||
}
|
||||
}
|
||||
|
||||
/// translate parser result from IResult<I,O,u32> to IResult<I,O,E> with a custom type
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use std::collections;
|
||||
/// # use nom::IResult::Error;
|
||||
/// # use nom::ErrorKind;
|
||||
/// # fn main() {
|
||||
/// // will add a Custom(42) error to the error chain
|
||||
/// named!(err_test, add_return_error!(ErrorKind::Custom(42), tag!("abcd")));
|
||||
/// // Convert to IREsult<&[u8], &[u8], &str>
|
||||
/// named!(parser<&[u8], &[u8], &str>, add_return_error!(ErrorKind::Custom("custom error message"), fix_error!(&str, err_test)));
|
||||
///
|
||||
/// let a = &b"efghblah"[..];
|
||||
/// let res_a = parser(a);
|
||||
/// assert_eq!(res_a, Error(error_node_position!( ErrorKind::Custom("custom error message"), a, Position(ErrorKind::Fix, a))));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! fix_error (
|
||||
($i:expr, $t:ty, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
match $submac!($i, $($args)*) {
|
||||
$crate::IResult::Incomplete(x) => $crate::IResult::Incomplete(x),
|
||||
$crate::IResult::Done(i, o) => $crate::IResult::Done(i, o),
|
||||
$crate::IResult::Error(_) => {
|
||||
let e: $crate::ErrorKind<$t> = $crate::ErrorKind::Fix;
|
||||
$crate::IResult::Error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
($i:expr, $t:ty, $f:expr) => (
|
||||
fix_error!($i, $t, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
/// `flat_map!(R -> IResult<R,S>, S -> IResult<S,T>) => R -> IResult<R, T>`
|
||||
///
|
||||
/// combines a parser R -> IResult<R,S> and
|
||||
/// a parser S -> IResult<S,T> to return another
|
||||
/// parser R -> IResult<R,T>
|
||||
#[macro_export]
|
||||
macro_rules! flat_map(
|
||||
($i:expr, $submac:ident!( $($args:tt)* ), $submac2:ident!( $($args2:tt)* )) => (
|
||||
{
|
||||
match $submac!($i, $($args)*) {
|
||||
$crate::IResult::Error(e) => $crate::IResult::Error(e),
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown) => $crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(i)) => $crate::IResult::Incomplete($crate::Needed::Size(i)),
|
||||
$crate::IResult::Done(i, o) => match $submac2!(o, $($args2)*) {
|
||||
$crate::IResult::Error(e) => $crate::IResult::Error(e),
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown) => $crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(ref i2)) => $crate::IResult::Incomplete($crate::Needed::Size(*i2)),
|
||||
$crate::IResult::Done(_, o2) => $crate::IResult::Done(i, o2)
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
($i:expr, $submac:ident!( $($args:tt)* ), $g:expr) => (
|
||||
flat_map!($i, $submac!($($args)*), call!($g));
|
||||
);
|
||||
($i:expr, $f:expr, $g:expr) => (
|
||||
flat_map!($i, call!($f), call!($g));
|
||||
);
|
||||
($i:expr, $f:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
flat_map!($i, call!($f), $submac!($($args)*));
|
||||
);
|
||||
);
|
658
third_party/rust/nom-3.2.1/src/str.rs
vendored
658
third_party/rust/nom-3.2.1/src/str.rs
vendored
@ -1,658 +0,0 @@
|
||||
//! Parsers and helper functions operating on strings, especially useful when writing parsers for
|
||||
//! text-based formats.
|
||||
|
||||
/// `tag_s!(&str) => &str -> IResult<&str, &str>`
|
||||
/// declares a string as a suite to recognize
|
||||
///
|
||||
/// consumes the recognized characters
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::{self,Done};
|
||||
/// # fn main() {
|
||||
/// fn test(input: &str) -> IResult<&str, &str> {
|
||||
/// tag_s!(input, "abcd")
|
||||
/// }
|
||||
/// let r = test("abcdefgh");
|
||||
/// assert_eq!(r, Done("efgh", "abcd"));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! tag_s (
|
||||
($i:expr, $tag: expr) => (
|
||||
{
|
||||
tag!($i, $tag)
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// `tag_no_case_s!(&str) => &str -> IResult<&str, &str>`
|
||||
/// declares a case-insensitive string as a suite to recognize
|
||||
///
|
||||
/// consumes the recognized characters
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::{self,Done};
|
||||
/// # fn main() {
|
||||
/// fn test(input: &str) -> IResult<&str, &str> {
|
||||
/// tag_no_case_s!(input, "ABcd")
|
||||
/// }
|
||||
/// let r = test("aBCdefgh");
|
||||
/// assert_eq!(r, Done("efgh", "aBCd"));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! tag_no_case_s (
|
||||
($i:expr, $tag: expr) => (
|
||||
{
|
||||
tag_no_case!($i, $tag)
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// `take_s!(nb) => &str -> IResult<&str, &str>`
|
||||
/// generates a parser consuming the specified number of characters
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # fn main() {
|
||||
/// // Desmond parser
|
||||
/// named!(take5<&str,&str>, take_s!( 5 ) );
|
||||
///
|
||||
/// let a = "abcdefgh";
|
||||
///
|
||||
/// assert_eq!(take5(a), Done("fgh", "abcde"));
|
||||
///
|
||||
/// let b = "12345";
|
||||
///
|
||||
/// assert_eq!(take5(b), Done("", "12345"));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! take_s (
|
||||
($i:expr, $count:expr) => (
|
||||
{
|
||||
let input = $i;
|
||||
let cnt = $count as usize;
|
||||
take!(input, cnt)
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
/// `is_not_s!(&str) => &str -> IResult<&str, &str>`
|
||||
/// returns the longest list of characters that do not appear in the provided array
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # fn main() {
|
||||
/// named!( not_space<&str,&str>, is_not_s!( " \t\r\n" ) );
|
||||
///
|
||||
/// let r = not_space("abcdefgh\nijkl");
|
||||
/// assert_eq!(r, Done("\nijkl", "abcdefgh"));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! is_not_s (
|
||||
($input:expr, $arr:expr) => (
|
||||
{
|
||||
is_not!($input, $arr)
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// `is_a_s!(&str) => &str -> IResult<&str, &str>`
|
||||
/// returns the longest list of characters that appear in the provided array
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # fn main() {
|
||||
/// named!(abcd<&str, &str>, is_a_s!( "abcd" ));
|
||||
///
|
||||
/// let r1 = abcd("aaaaefgh");
|
||||
/// assert_eq!(r1, Done("efgh", "aaaa"));
|
||||
///
|
||||
/// let r2 = abcd("dcbaefgh");
|
||||
/// assert_eq!(r2, Done("efgh", "dcba"));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! is_a_s (
|
||||
($input:expr, $arr:expr) => (
|
||||
{
|
||||
is_a!($input, $arr)
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
/// `take_while_s!(char -> bool) => &str -> IResult<&str, &str>`
|
||||
/// returns the longest list of characters until the provided function fails.
|
||||
///
|
||||
/// The argument is either a function `char -> bool` or a macro returning a `bool
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # use nom::is_alphanumeric;
|
||||
/// # fn main() {
|
||||
/// fn alphabetic(chr: char) -> bool { (chr >= 0x41 as char && chr <= 0x5A as char) || (chr >= 0x61 as char && chr <= 0x7A as char) }
|
||||
/// named!( alpha<&str,&str>, take_while_s!( alphabetic ) );
|
||||
///
|
||||
/// let r = alpha("abcd\nefgh");
|
||||
/// assert_eq!(r, Done("\nefgh", "abcd"));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! take_while_s (
|
||||
($input:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
take_while!($input, $submac!($($args)*))
|
||||
}
|
||||
);
|
||||
($input:expr, $f:expr) => (
|
||||
take_while_s!($input, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
/// `take_while1_s!(char -> bool) => &str -> IResult<&str, &str>`
|
||||
/// returns the longest (non empty) list of characters until the provided function fails.
|
||||
///
|
||||
/// The argument is either a function `char -> bool` or a macro returning a `bool`
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use nom::IResult::Done;
|
||||
/// # use nom::is_alphanumeric;
|
||||
/// # fn main() {
|
||||
/// fn alphabetic(chr: char) -> bool { (chr >= 0x41 as char && chr <= 0x5A as char) || (chr >= 0x61 as char && chr <= 0x7A as char) }
|
||||
/// named!( alpha<&str,&str>, take_while1_s!( alphabetic ) );
|
||||
///
|
||||
/// let r = alpha("abcd\nefgh");
|
||||
/// assert_eq!(r, Done("\nefgh", "abcd"));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! take_while1_s (
|
||||
($input:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
take_while1!($input, $submac!($($args)*))
|
||||
);
|
||||
($input:expr, $f:expr) => (
|
||||
take_while1_s!($input, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
/// `take_till_s!(char -> bool) => &str -> IResult<&str, &str>`
|
||||
/// returns the longest list of characters until the provided function succeeds
|
||||
///
|
||||
/// The argument is either a function `char -> bool` or a macro returning a `bool
|
||||
#[macro_export]
|
||||
macro_rules! take_till_s (
|
||||
($input:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
take_till!($input, $submac!($($args)*))
|
||||
}
|
||||
);
|
||||
($input:expr, $f:expr) => (
|
||||
take_till_s!($input, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
/// `take_till1_s!(char -> bool) => &str -> IResult<&str, &str>`
|
||||
/// returns the longest non empty list of characters until the provided function succeeds
|
||||
///
|
||||
/// The argument is either a function `char -> bool` or a macro returning a `bool
|
||||
#[macro_export]
|
||||
macro_rules! take_till1_s (
|
||||
($input:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
take_till1!($input, $submac!($($args)*))
|
||||
}
|
||||
);
|
||||
($input:expr, $f:expr) => (
|
||||
take_till1_s!($input, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
/// `take_until_and_consume_s!(&str) => &str -> IResult<&str, &str>`
|
||||
/// generates a parser consuming all chars until the specified string is found and consumes it
|
||||
#[macro_export]
|
||||
macro_rules! take_until_and_consume_s (
|
||||
($input:expr, $substr:expr) => (
|
||||
{
|
||||
take_until_and_consume!($input, $substr)
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
/// `take_until_s!(&str) => &str -> IResult<&str, &str>`
|
||||
/// generates a parser consuming all chars until the specified string is found and leaves it in the remaining input
|
||||
#[macro_export]
|
||||
macro_rules! take_until_s (
|
||||
($input:expr, $substr:expr) => (
|
||||
{
|
||||
take_until!($input, $substr)
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use ::IResult;
|
||||
|
||||
#[test]
|
||||
fn tag_str_succeed() {
|
||||
const INPUT: &'static str = "Hello World!";
|
||||
const TAG: &'static str = "Hello";
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
tag_s!(input, TAG)
|
||||
}
|
||||
|
||||
match test(INPUT) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == " World!", "Parser `tag_s` consumed leftover input.");
|
||||
assert!(output == TAG,
|
||||
"Parser `tag_s` doesn't return the tag it matched on success. \
|
||||
Expected `{}`, got `{}`.", TAG, output);
|
||||
},
|
||||
other => panic!("Parser `tag_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tag_str_incomplete() {
|
||||
const INPUT: &'static str = "Hello";
|
||||
const TAG: &'static str = "Hello World!";
|
||||
|
||||
match tag_s!(INPUT, TAG) {
|
||||
IResult::Incomplete(_) => (),
|
||||
other => {
|
||||
panic!("Parser `tag_s` didn't require more input when it should have. \
|
||||
Got `{:?}`.", other);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tag_str_error() {
|
||||
const INPUT: &'static str = "Hello World!";
|
||||
const TAG: &'static str = "Random"; // TAG must be closer than INPUT.
|
||||
|
||||
match tag_s!(INPUT, TAG) {
|
||||
IResult::Error(_) => (),
|
||||
other => {
|
||||
panic!("Parser `tag_s` didn't fail when it should have. Got `{:?}`.`", other);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_s_succeed() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const CONSUMED: &'static str = "βèƒôřèÂßÇ";
|
||||
const LEFTOVER: &'static str = "áƒƭèř";
|
||||
|
||||
match take_s!(INPUT, 9) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `take_s` consumed leftover input. Leftover `{}`.", extra);
|
||||
assert!(output == CONSUMED,
|
||||
"Parser `take_s` doens't return the string it consumed on success. Expected `{}`, got `{}`.",
|
||||
CONSUMED, output);
|
||||
},
|
||||
other => panic!("Parser `take_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_until_s_succeed() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇ∂áƒƭèř";
|
||||
const FIND: &'static str = "ÂßÇ∂";
|
||||
const CONSUMED: &'static str = "βèƒôřè";
|
||||
const LEFTOVER: &'static str = "ÂßÇ∂áƒƭèř";
|
||||
|
||||
match take_until_s!(INPUT, FIND) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `take_until_s`\
|
||||
consumed leftover input. Leftover `{}`.", extra);
|
||||
assert!(output == CONSUMED, "Parser `take_until_s`\
|
||||
doens't return the string it consumed on success. Expected `{}`, got `{}`.",
|
||||
CONSUMED, output);
|
||||
}
|
||||
other => panic!("Parser `take_until_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_s_incomplete() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇá";
|
||||
|
||||
match take_s!(INPUT, 13) {
|
||||
IResult::Incomplete(_) => (),
|
||||
other => panic!("Parser `take_s` didn't require more input when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
}
|
||||
}
|
||||
|
||||
use internal::IResult::{Done, Error, Incomplete};
|
||||
use internal::Needed;
|
||||
use util::ErrorKind;
|
||||
|
||||
pub fn is_alphabetic(c:char) -> bool {
|
||||
(c as u8 >= 0x41 && c as u8 <= 0x5A) || (c as u8 >= 0x61 && c as u8 <= 0x7A)
|
||||
}
|
||||
#[test]
|
||||
fn take_while_s() {
|
||||
named!(f<&str,&str>, take_while_s!(is_alphabetic));
|
||||
let a = "";
|
||||
let b = "abcd";
|
||||
let c = "abcd123";
|
||||
let d = "123";
|
||||
|
||||
assert_eq!(f(&a[..]), Done(&a[..], &a[..]));
|
||||
assert_eq!(f(&b[..]), Done(&a[..], &b[..]));
|
||||
assert_eq!(f(&c[..]), Done(&d[..], &b[..]));
|
||||
assert_eq!(f(&d[..]), Done(&d[..], &a[..]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_while1_s() {
|
||||
named!(f<&str,&str>, take_while1_s!(is_alphabetic));
|
||||
let a = "";
|
||||
let b = "abcd";
|
||||
let c = "abcd123";
|
||||
let d = "123";
|
||||
|
||||
assert_eq!(f(&a[..]), Incomplete(Needed::Size(1)));
|
||||
assert_eq!(f(&b[..]), Done(&a[..], &b[..]));
|
||||
assert_eq!(f(&c[..]), Done(&"123"[..], &b[..]));
|
||||
assert_eq!(f(&d[..]), Error(error_position!(ErrorKind::TakeWhile1, &d[..])));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_till_s_succeed() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const CONSUMED: &'static str = "βèƒôřèÂßÇ";
|
||||
const LEFTOVER: &'static str = "áƒƭèř";
|
||||
fn till_s(c: char) -> bool {
|
||||
c == 'á'
|
||||
}
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
take_till_s!(input, till_s)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `take_till_s` consumed leftover input.");
|
||||
assert!(output == CONSUMED,
|
||||
"Parser `take_till_s` doesn't return the string it consumed on success. \
|
||||
Expected `{}`, got `{}`.", CONSUMED, output);
|
||||
},
|
||||
other => panic!("Parser `take_till_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_while_s_succeed_none() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const CONSUMED: &'static str = "";
|
||||
const LEFTOVER: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
fn while_s(c: char) -> bool {
|
||||
c == '9'
|
||||
}
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
take_while_s!(input, while_s)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `take_while_s` consumed leftover input.");
|
||||
assert!(output == CONSUMED,
|
||||
"Parser `take_while_s` doesn't return the string it consumed on success. \
|
||||
Expected `{}`, got `{}`.", CONSUMED, output);
|
||||
},
|
||||
other => panic!("Parser `take_while_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_not_s_succeed() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const AVOID: &'static str = "£úçƙ¥á";
|
||||
const CONSUMED: &'static str = "βèƒôřèÂßÇ";
|
||||
const LEFTOVER: &'static str = "áƒƭèř";
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
is_not_s!(input, AVOID)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `is_not_s` consumed leftover input. Leftover `{}`.", extra);
|
||||
assert!(output == CONSUMED,
|
||||
"Parser `is_not_s` doens't return the string it consumed on success. Expected `{}`, got `{}`.",
|
||||
CONSUMED, output);
|
||||
},
|
||||
other => panic!("Parser `is_not_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_until_and_consume_s_succeed() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const FIND: &'static str = "姂";
|
||||
const OUTPUT: &'static str = "βèƒôřè";
|
||||
const LEFTOVER: &'static str = "áƒƭèř";
|
||||
|
||||
match take_until_and_consume_s!(INPUT, FIND) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `take_until_and_consume_s`\
|
||||
consumed leftover input. Leftover `{}`.", extra);
|
||||
assert!(output == OUTPUT, "Parser `take_until_and_consume_s`\
|
||||
doens't return the string it selected on success. Expected `{}`, got `{}`.",
|
||||
OUTPUT, output);
|
||||
}
|
||||
other => panic!("Parser `take_until_and_consume_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_while_s_succeed_some() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const CONSUMED: &'static str = "βèƒôřèÂßÇ";
|
||||
const LEFTOVER: &'static str = "áƒƭèř";
|
||||
fn while_s(c: char) -> bool {
|
||||
c == 'β' || c == 'è' || c == 'ƒ' || c == 'ô' || c == 'ř' ||
|
||||
c == 'è' || c == 'Â' || c == 'ß' || c == 'Ç'
|
||||
}
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
take_while_s!(input, while_s)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `take_while_s` consumed leftover input.");
|
||||
assert!(output == CONSUMED,
|
||||
"Parser `take_while_s` doesn't return the string it consumed on success. \
|
||||
Expected `{}`, got `{}`.", CONSUMED, output);
|
||||
},
|
||||
other => panic!("Parser `take_while_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_not_s_fail() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const AVOID: &'static str = "βúçƙ¥";
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
is_not_s!(input, AVOID)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Error(_) => (),
|
||||
other => panic!("Parser `is_not_s` didn't fail when it should have. Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_while1_s_succeed() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const CONSUMED: &'static str = "βèƒôřèÂßÇ";
|
||||
const LEFTOVER: &'static str = "áƒƭèř";
|
||||
fn while1_s(c: char) -> bool {
|
||||
c == 'β' || c == 'è' || c == 'ƒ' || c == 'ô' || c == 'ř' ||
|
||||
c == 'è' || c == 'Â' || c == 'ß' || c == 'Ç'
|
||||
}
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
take_while1_s!(input, while1_s)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `take_while1_s` consumed leftover input.");
|
||||
assert!(output == CONSUMED,
|
||||
"Parser `take_while1_s` doesn't return the string it consumed on success. \
|
||||
Expected `{}`, got `{}`.", CONSUMED, output);
|
||||
},
|
||||
other => panic!("Parser `take_while1_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_until_and_consume_s_incomplete() {
|
||||
const INPUT: &'static str = "βèƒôřè";
|
||||
const FIND: &'static str = "βèƒôřèÂßÇ";
|
||||
|
||||
match take_until_and_consume_s!(INPUT, FIND) {
|
||||
IResult::Incomplete(_) => (),
|
||||
other => panic!("Parser `take_until_and_consume_s` didn't require more input when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_until_s_incomplete() {
|
||||
const INPUT: &'static str = "βèƒôřè";
|
||||
const FIND: &'static str = "βèƒôřèÂßÇ";
|
||||
|
||||
match take_until_s!(INPUT, FIND) {
|
||||
IResult::Incomplete(_) => (),
|
||||
other => panic!("Parser `take_until_s` didn't require more input when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_a_s_succeed() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const MATCH: &'static str = "βèƒôřèÂßÇ";
|
||||
const CONSUMED: &'static str = "βèƒôřèÂßÇ";
|
||||
const LEFTOVER: &'static str = "áƒƭèř";
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
is_a_s!(input, MATCH)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Done(extra, output) => {
|
||||
assert!(extra == LEFTOVER, "Parser `is_a_s` consumed leftover input. Leftover `{}`.", extra);
|
||||
assert!(output == CONSUMED,
|
||||
"Parser `is_a_s` doens't return the string it consumed on success. Expected `{}`, got `{}`.",
|
||||
CONSUMED, output);
|
||||
},
|
||||
other => panic!("Parser `is_a_s` didn't succeed when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_while1_s_fail() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
fn while1_s(c: char) -> bool {
|
||||
c == '9'
|
||||
}
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
take_while1_s!(input, while1_s)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Error(_) => (),
|
||||
other => panic!("Parser `take_while1_s` didn't fail when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_a_s_fail() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const MATCH: &'static str = "Ûñℓúçƙ¥";
|
||||
fn test(input: &str) -> IResult<&str, &str> {
|
||||
is_a_s!(input, MATCH)
|
||||
}
|
||||
match test(INPUT) {
|
||||
IResult::Error(_) => (),
|
||||
other => panic!("Parser `is_a_s` didn't fail when it should have. Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_until_and_consume_s_error() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const FIND: &'static str = "Ráñδô₥";
|
||||
|
||||
match take_until_and_consume_s!(INPUT, FIND) {
|
||||
IResult::Error(_) => (),
|
||||
other => panic!("Parser `take_until_and_consume_s` didn't fail when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_until_s_error() {
|
||||
const INPUT: &'static str = "βèƒôřèÂßÇáƒƭèř";
|
||||
const FIND: &'static str = "Ráñδô₥";
|
||||
|
||||
match take_until_s!(INPUT, FIND) {
|
||||
IResult::Error(_) => (),
|
||||
other => panic!("Parser `take_until_and_consume_s` didn't fail when it should have. \
|
||||
Got `{:?}`.", other),
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "std")]
|
||||
fn recognize_is_a_s() {
|
||||
let a = "aabbab";
|
||||
let b = "ababcd";
|
||||
|
||||
named!(f <&str,&str>, recognize!(many1!(alt!( tag_s!("a") | tag_s!("b") ))));
|
||||
|
||||
assert_eq!(f(&a[..]), Done(&a[6..], &a[..]));
|
||||
assert_eq!(f(&b[..]), Done(&b[4..], &b[..4]));
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn utf8_indexing() {
|
||||
named!(dot(&str) -> &str,
|
||||
tag_s!(".")
|
||||
);
|
||||
|
||||
dot("點");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn case_insensitive() {
|
||||
named!(test<&str,&str>, tag_no_case!("ABcd"));
|
||||
assert_eq!(test("aBCdefgh"), Done("efgh", "aBCd"));
|
||||
assert_eq!(test("abcdefgh"), Done("efgh", "abcd"));
|
||||
assert_eq!(test("ABCDefgh"), Done("efgh", "ABCD"));
|
||||
|
||||
named!(test2<&str,&str>, tag_no_case!("ABcd"));
|
||||
assert_eq!(test2("aBCdefgh"), Done("efgh", "aBCd"));
|
||||
assert_eq!(test2("abcdefgh"), Done("efgh", "abcd"));
|
||||
assert_eq!(test2("ABCDefgh"), Done("efgh", "ABCD"));
|
||||
}
|
||||
}
|
1062
third_party/rust/nom-3.2.1/src/stream.rs
vendored
1062
third_party/rust/nom-3.2.1/src/stream.rs
vendored
File diff suppressed because it is too large
Load Diff
623
third_party/rust/nom-3.2.1/src/traits.rs
vendored
623
third_party/rust/nom-3.2.1/src/traits.rs
vendored
@ -1,623 +0,0 @@
|
||||
//! Traits input types have to implement to work with nom combinators
|
||||
//!
|
||||
use std::ops::{Range,RangeTo,RangeFrom,RangeFull};
|
||||
use std::iter::Enumerate;
|
||||
use std::slice::Iter;
|
||||
|
||||
use std::str::Chars;
|
||||
use std::str::CharIndices;
|
||||
use std::str::FromStr;
|
||||
use std::str::from_utf8;
|
||||
|
||||
use memchr;
|
||||
|
||||
|
||||
/// abstract method to calculate the input length
|
||||
pub trait InputLength {
|
||||
/// calculates the input length, as indicated by its name,
|
||||
/// and the name of the trait itself
|
||||
#[inline]
|
||||
fn input_len(&self) -> usize;
|
||||
}
|
||||
|
||||
impl<'a, T> InputLength for &'a[T] {
|
||||
#[inline]
|
||||
fn input_len(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> InputLength for &'a str {
|
||||
#[inline]
|
||||
fn input_len(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> InputLength for (&'a [u8], usize) {
|
||||
#[inline]
|
||||
fn input_len(&self) -> usize {
|
||||
//println!("bit input length for ({:?}, {}):", self.0, self.1);
|
||||
let res = self.0.len() * 8 - self.1;
|
||||
//println!("-> {}", res);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
/// transforms common types to a char for basic token parsing
|
||||
pub trait AsChar {
|
||||
/// makes a char from self
|
||||
#[inline]
|
||||
fn as_char(self) -> char;
|
||||
|
||||
/// tests that self is an alphabetic character
|
||||
///
|
||||
/// warning: for `&str` it recognizes alphabetic
|
||||
/// characters outside of the 52 ASCII letters
|
||||
#[inline]
|
||||
fn is_alpha(self) -> bool;
|
||||
|
||||
/// tests that self is an alphabetic character
|
||||
/// or a decimal digit
|
||||
#[inline]
|
||||
fn is_alphanum(self) -> bool;
|
||||
/// tests that self is a decimal digit
|
||||
#[inline]
|
||||
fn is_dec_digit(self) -> bool;
|
||||
/// tests that self is an hex digit
|
||||
#[inline]
|
||||
fn is_hex_digit(self) -> bool;
|
||||
/// tests that self is an octal digit
|
||||
#[inline]
|
||||
fn is_oct_digit(self) -> bool;
|
||||
/// gets the len in bytes for self
|
||||
#[inline]
|
||||
fn len(self) -> usize;
|
||||
}
|
||||
|
||||
impl AsChar for u8 {
|
||||
#[inline]
|
||||
fn as_char(self) -> char { self as char }
|
||||
#[inline]
|
||||
fn is_alpha(self) -> bool {
|
||||
(self >= 0x41 && self <= 0x5A) || (self >= 0x61 && self <= 0x7A)
|
||||
}
|
||||
#[inline]
|
||||
fn is_alphanum(self) -> bool { self.is_alpha() || self.is_dec_digit() }
|
||||
#[inline]
|
||||
fn is_dec_digit(self) -> bool {
|
||||
self >= 0x30 && self <= 0x39
|
||||
}
|
||||
#[inline]
|
||||
fn is_hex_digit(self) -> bool {
|
||||
(self >= 0x30 && self <= 0x39) ||
|
||||
(self >= 0x41 && self <= 0x46) ||
|
||||
(self >= 0x61 && self <= 0x66)
|
||||
}
|
||||
#[inline]
|
||||
fn is_oct_digit(self) -> bool {
|
||||
self >= 0x30 && self <= 0x37
|
||||
}
|
||||
#[inline]
|
||||
fn len(self) -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
impl<'a> AsChar for &'a u8 {
|
||||
#[inline]
|
||||
fn as_char(self) -> char { *self as char }
|
||||
#[inline]
|
||||
fn is_alpha(self) -> bool {
|
||||
(*self >= 0x41 && *self <= 0x5A) || (*self >= 0x61 && *self <= 0x7A)
|
||||
}
|
||||
#[inline]
|
||||
fn is_alphanum(self) -> bool { self.is_alpha() || self.is_dec_digit() }
|
||||
#[inline]
|
||||
fn is_dec_digit(self) -> bool {
|
||||
*self >= 0x30 && *self <= 0x39
|
||||
}
|
||||
#[inline]
|
||||
fn is_hex_digit(self) -> bool {
|
||||
(*self >= 0x30 && *self <= 0x39) ||
|
||||
(*self >= 0x41 && *self <= 0x46) ||
|
||||
(*self >= 0x61 && *self <= 0x66)
|
||||
}
|
||||
#[inline]
|
||||
fn is_oct_digit(self) -> bool {
|
||||
*self >= 0x30 && *self <= 0x37
|
||||
}
|
||||
#[inline]
|
||||
fn len(self) -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
impl AsChar for char {
|
||||
#[inline]
|
||||
fn as_char(self) -> char { self }
|
||||
#[inline]
|
||||
fn is_alpha(self) -> bool { self.is_alphabetic() }
|
||||
#[inline]
|
||||
fn is_alphanum(self) -> bool { self.is_alpha() || self.is_dec_digit() }
|
||||
#[inline]
|
||||
fn is_dec_digit(self) -> bool { self.is_digit(10) }
|
||||
#[inline]
|
||||
fn is_hex_digit(self) -> bool { self.is_digit(16) }
|
||||
#[inline]
|
||||
fn is_oct_digit(self) -> bool { self.is_digit(8) }
|
||||
#[inline]
|
||||
fn len(self) -> usize { self.len_utf8() }
|
||||
}
|
||||
|
||||
impl<'a> AsChar for &'a char {
|
||||
#[inline]
|
||||
fn as_char(self) -> char { self.clone() }
|
||||
#[inline]
|
||||
fn is_alpha(self) -> bool { self.is_alphabetic() }
|
||||
#[inline]
|
||||
fn is_alphanum(self) -> bool { self.is_alpha() || self.is_dec_digit() }
|
||||
#[inline]
|
||||
fn is_dec_digit(self) -> bool { self.is_digit(10) }
|
||||
#[inline]
|
||||
fn is_hex_digit(self) -> bool { self.is_digit(16) }
|
||||
#[inline]
|
||||
fn is_oct_digit(self) -> bool { self.is_digit(8) }
|
||||
#[inline]
|
||||
fn len(self) -> usize { self.len_utf8() }
|
||||
}
|
||||
|
||||
/// abstracts common iteration operations on the input type
|
||||
///
|
||||
/// it needs a distinction between `Item` and `RawItem` because
|
||||
/// `&[T]` iterates on references
|
||||
pub trait InputIter {
|
||||
type Item;
|
||||
type RawItem;
|
||||
type Iter : Iterator<Item=(usize, Self::Item)>;
|
||||
type IterElem : Iterator<Item=Self::Item>;
|
||||
|
||||
/// returns an iterator over the elements and their byte offsets
|
||||
fn iter_indices(&self) -> Self::Iter;
|
||||
/// returns an iterator over the elements
|
||||
fn iter_elements(&self) -> Self::IterElem;
|
||||
/// finds the byte position of the element
|
||||
fn position<P>(&self, predicate: P) -> Option<usize> where P: Fn(Self::RawItem) -> bool;
|
||||
/// get the byte offset from the element's position in the stream
|
||||
fn slice_index(&self, count:usize) -> Option<usize>;
|
||||
}
|
||||
|
||||
/// abstracts slicing operations
|
||||
pub trait InputTake {
|
||||
/// returns a slice of `count` bytes
|
||||
fn take<P>(&self, count: usize) -> Option<&Self>;
|
||||
/// split the stream at the `count` byte offset
|
||||
fn take_split<P>(&self, count: usize) -> Option<(&Self,&Self)>;
|
||||
}
|
||||
|
||||
impl<'a> InputIter for &'a [u8] {
|
||||
type Item = &'a u8;
|
||||
type RawItem = u8;
|
||||
type Iter = Enumerate<Iter<'a, Self::RawItem>>;
|
||||
type IterElem = Iter<'a, Self::RawItem>;
|
||||
|
||||
#[inline]
|
||||
fn iter_indices(&self) -> Self::Iter {
|
||||
self.iter().enumerate()
|
||||
}
|
||||
#[inline]
|
||||
fn iter_elements(&self) -> Self::IterElem {
|
||||
self.iter()
|
||||
}
|
||||
#[inline]
|
||||
fn position<P>(&self, predicate: P) -> Option<usize> where P: Fn(Self::RawItem) -> bool {
|
||||
self.iter().position(|b| predicate(*b))
|
||||
}
|
||||
#[inline]
|
||||
fn slice_index(&self, count:usize) -> Option<usize> {
|
||||
if self.len() >= count {
|
||||
Some(count)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InputTake for [u8] {
|
||||
#[inline]
|
||||
fn take<P>(&self, count: usize) -> Option<&Self> {
|
||||
if self.len() >= count {
|
||||
Some(&self[0..count])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn take_split<P>(&self, count: usize) -> Option<(&Self,&Self)> {
|
||||
if self.len() >= count {
|
||||
Some((&self[count..],&self[..count]))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> InputIter for &'a str {
|
||||
type Item = char;
|
||||
type RawItem = char;
|
||||
type Iter = CharIndices<'a>;
|
||||
type IterElem = Chars<'a>;
|
||||
#[inline]
|
||||
fn iter_indices(&self) -> Self::Iter {
|
||||
self.char_indices()
|
||||
}
|
||||
#[inline]
|
||||
fn iter_elements(&self) -> Self::IterElem {
|
||||
self.chars()
|
||||
}
|
||||
fn position<P>(&self, predicate: P) -> Option<usize> where P: Fn(Self::RawItem) -> bool {
|
||||
for (o,c) in self.char_indices() {
|
||||
if predicate(c) {
|
||||
return Some(o)
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
#[inline]
|
||||
fn slice_index(&self, count:usize) -> Option<usize> {
|
||||
let mut cnt = 0;
|
||||
for (index, _) in self.char_indices() {
|
||||
if cnt == count {
|
||||
return Some(index)
|
||||
}
|
||||
cnt += 1;
|
||||
}
|
||||
if cnt == count {
|
||||
return Some(self.len())
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl InputTake for str {
|
||||
#[inline]
|
||||
fn take<P>(&self, count: usize) -> Option<&Self> {
|
||||
let mut cnt = 0;
|
||||
for (index, _) in self.char_indices() {
|
||||
if cnt == count {
|
||||
return Some(&self[..index])
|
||||
}
|
||||
cnt += 1;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// return byte index
|
||||
#[inline]
|
||||
fn take_split<P>(&self, count: usize) -> Option<(&Self,&Self)> {
|
||||
let mut cnt = 0;
|
||||
for (index, _) in self.char_indices() {
|
||||
if cnt == count {
|
||||
return Some((&self[index..],&self[..index]))
|
||||
}
|
||||
cnt += 1;
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// indicates wether a comparison was successful, an error, or
|
||||
/// if more data was needed
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub enum CompareResult {
|
||||
Ok,
|
||||
Incomplete,
|
||||
Error
|
||||
}
|
||||
|
||||
/// abstracts comparison operations
|
||||
pub trait Compare<T> {
|
||||
/// compares self to another value for equality
|
||||
fn compare(&self, t:T) -> CompareResult;
|
||||
/// compares self to another value for equality
|
||||
/// independently of the case.
|
||||
///
|
||||
/// warning: for `&str`, the comparison is done
|
||||
/// by lowercasing both strings and comparing
|
||||
/// the result. This is a temporary solution until
|
||||
/// a better one appears
|
||||
fn compare_no_case(&self, t:T) -> CompareResult;
|
||||
}
|
||||
|
||||
impl<'a,'b> Compare<&'b[u8]> for &'a [u8] {
|
||||
#[inline(always)]
|
||||
fn compare(&self, t: &'b[u8]) -> CompareResult {
|
||||
let len = self.len();
|
||||
let blen = t.len();
|
||||
let m = if len < blen { len } else { blen };
|
||||
let reduced = &self[..m];
|
||||
let b = &t[..m];
|
||||
|
||||
if reduced != b {
|
||||
CompareResult::Error
|
||||
} else if m < blen {
|
||||
CompareResult::Incomplete
|
||||
} else {
|
||||
CompareResult::Ok
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn compare_no_case(&self, t: &'b[u8]) -> CompareResult {
|
||||
let len = self.len();
|
||||
let blen = t.len();
|
||||
let m = if len < blen { len } else { blen };
|
||||
let reduced = &self[..m];
|
||||
let other = &t[..m];
|
||||
|
||||
if !reduced.iter().zip(other).all(|(a, b)| {
|
||||
match (*a,*b) {
|
||||
(0...64, 0...64) | (91...96, 91...96) | (123...255, 123...255) => a == b,
|
||||
(65...90, 65...90) | (97...122, 97...122) | (65...90, 97...122 ) |(97...122, 65...90) => {
|
||||
*a | 0b00100000 == *b | 0b00100000
|
||||
}
|
||||
_ => false
|
||||
}
|
||||
}) {
|
||||
CompareResult::Error
|
||||
} else if m < blen {
|
||||
CompareResult::Incomplete
|
||||
} else {
|
||||
CompareResult::Ok
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,'b> Compare<&'b str> for &'a [u8] {
|
||||
#[inline(always)]
|
||||
fn compare(&self, t: &'b str) -> CompareResult {
|
||||
self.compare(str::as_bytes(t))
|
||||
}
|
||||
#[inline(always)]
|
||||
fn compare_no_case(&self, t: &'b str) -> CompareResult {
|
||||
self.compare_no_case(str::as_bytes(t))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,'b> Compare<&'b str> for &'a str {
|
||||
#[inline(always)]
|
||||
fn compare(&self, t: &'b str) -> CompareResult {
|
||||
let pos = self.chars().zip(t.chars()).position(|(a,b)| a != b);
|
||||
|
||||
match pos {
|
||||
Some(_) => CompareResult::Error,
|
||||
None => if self.len() >= t.len() {
|
||||
CompareResult::Ok
|
||||
} else {
|
||||
CompareResult::Incomplete
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//FIXME: this version is too simple and does not use the current locale
|
||||
#[inline(always)]
|
||||
fn compare_no_case(&self, t: &'b str) -> CompareResult {
|
||||
let pos = self.to_lowercase().chars().zip(t.to_lowercase().chars()).position(|(a,b)| a != b);
|
||||
|
||||
match pos {
|
||||
Some(_) => CompareResult::Error,
|
||||
None => if self.len() >= t.len() {
|
||||
CompareResult::Ok
|
||||
} else {
|
||||
CompareResult::Incomplete
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// look for self in the given input stream
|
||||
pub trait FindToken<T> {
|
||||
fn find_token(&self, input: T) -> bool;
|
||||
}
|
||||
|
||||
impl<'a> FindToken<&'a[u8]> for u8 {
|
||||
fn find_token(&self, input: &[u8]) -> bool {
|
||||
memchr::memchr(*self, input).is_some()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FindToken<&'a str> for u8 {
|
||||
fn find_token(&self, input: &str) -> bool {
|
||||
self.find_token(str::as_bytes(input))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,'b> FindToken<&'a[u8]> for &'b u8 {
|
||||
fn find_token(&self, input: &[u8]) -> bool {
|
||||
memchr::memchr(**self, input).is_some()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,'b> FindToken<&'a str> for &'b u8 {
|
||||
fn find_token(&self, input: &str) -> bool {
|
||||
self.find_token(str::as_bytes(input))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FindToken<&'a str> for char {
|
||||
fn find_token(&self, input: &str) -> bool {
|
||||
for i in input.chars() {
|
||||
if *self == i { return true }
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// look for a substring in self
|
||||
pub trait FindSubstring<T> {
|
||||
fn find_substring(&self, substr: T) -> Option<usize>;
|
||||
}
|
||||
|
||||
impl<'a,'b> FindSubstring<&'b [u8]> for &'a[u8] {
|
||||
fn find_substring(&self, substr: &'b[u8]) -> Option<usize> {
|
||||
let substr_len = substr.len();
|
||||
|
||||
if substr_len == 0 {
|
||||
None
|
||||
} else if substr_len == 1 {
|
||||
memchr::memchr(substr[0], self)
|
||||
} else {
|
||||
let max = self.len() - substr_len;
|
||||
let mut offset = 0;
|
||||
let mut haystack = &self[..];
|
||||
|
||||
while let Some(position) = memchr::memchr(substr[0], haystack) {
|
||||
offset += position;
|
||||
|
||||
if offset > max {
|
||||
return None
|
||||
}
|
||||
|
||||
if &haystack[position..position + substr_len] == substr {
|
||||
return Some(offset)
|
||||
}
|
||||
|
||||
haystack = &haystack[position + 1..];
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,'b> FindSubstring<&'b str> for &'a[u8] {
|
||||
fn find_substring(&self, substr: &'b str) -> Option<usize> {
|
||||
self.find_substring(str::as_bytes(substr))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,'b> FindSubstring<&'b str> for &'a str {
|
||||
//returns byte index
|
||||
fn find_substring(&self, substr: &'b str) -> Option<usize> {
|
||||
self.find(substr)
|
||||
}
|
||||
}
|
||||
|
||||
/// used to integrate str's parse() method
|
||||
pub trait ParseTo<R> {
|
||||
fn parse_to(&self) -> Option<R>;
|
||||
}
|
||||
|
||||
impl<'a,R: FromStr> ParseTo<R> for &'a[u8] {
|
||||
fn parse_to(&self) -> Option<R> {
|
||||
from_utf8(self).ok().and_then(|s| s.parse().ok())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,R:FromStr> ParseTo<R> for &'a str {
|
||||
fn parse_to(&self) -> Option<R> {
|
||||
self.parse().ok()
|
||||
}
|
||||
}
|
||||
|
||||
/// slicing operations using ranges
|
||||
///
|
||||
/// this trait is loosely based on
|
||||
/// `Index`, but can actually return
|
||||
/// something else than a `&[T]` or `&str`
|
||||
pub trait Slice<R> {
|
||||
#[inline(always)]
|
||||
fn slice(&self, range: R) -> Self;
|
||||
}
|
||||
|
||||
macro_rules! impl_fn_slice {
|
||||
( $ty:ty ) => {
|
||||
fn slice(&self, range:$ty) -> Self {
|
||||
&self[range]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! slice_range_impl {
|
||||
( [ $for_type:ident ], $ty:ty ) => {
|
||||
impl<'a, $for_type> Slice<$ty> for &'a [$for_type] {
|
||||
impl_fn_slice!( $ty );
|
||||
}
|
||||
};
|
||||
( $for_type:ty, $ty:ty ) => {
|
||||
impl<'a> Slice<$ty> for &'a $for_type {
|
||||
impl_fn_slice!( $ty );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! slice_ranges_impl {
|
||||
( [ $for_type:ident ] ) => {
|
||||
slice_range_impl! {[$for_type], Range<usize>}
|
||||
slice_range_impl! {[$for_type], RangeTo<usize>}
|
||||
slice_range_impl! {[$for_type], RangeFrom<usize>}
|
||||
slice_range_impl! {[$for_type], RangeFull}
|
||||
};
|
||||
( $for_type:ty ) => {
|
||||
slice_range_impl! {$for_type, Range<usize>}
|
||||
slice_range_impl! {$for_type, RangeTo<usize>}
|
||||
slice_range_impl! {$for_type, RangeFrom<usize>}
|
||||
slice_range_impl! {$for_type, RangeFull}
|
||||
}
|
||||
}
|
||||
|
||||
slice_ranges_impl! {str}
|
||||
slice_ranges_impl! {[T]}
|
||||
|
||||
|
||||
macro_rules! array_impls {
|
||||
($($N:expr)+) => {
|
||||
$(
|
||||
impl InputLength for [u8; $N] {
|
||||
#[inline]
|
||||
fn input_len(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> InputLength for &'a [u8; $N] {
|
||||
#[inline]
|
||||
fn input_len(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Compare<[u8; $N]> for &'a [u8] {
|
||||
#[inline(always)]
|
||||
fn compare(&self, t: [u8; $N]) -> CompareResult {
|
||||
self.compare(&t[..])
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn compare_no_case(&self, t: [u8;$N]) -> CompareResult {
|
||||
self.compare_no_case(&t[..])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,'b> Compare<&'b [u8; $N]> for &'a [u8] {
|
||||
#[inline(always)]
|
||||
fn compare(&self, t: &'b [u8; $N]) -> CompareResult {
|
||||
self.compare(&t[..])
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn compare_no_case(&self, t: &'b [u8;$N]) -> CompareResult {
|
||||
self.compare_no_case(&t[..])
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
array_impls! {
|
||||
0 1 2 3 4 5 6 7 8 9
|
||||
10 11 12 13 14 15 16 17 18 19
|
||||
20 21 22 23 24 25 26 27 28 29
|
||||
30 31 32
|
||||
}
|
738
third_party/rust/nom-3.2.1/src/util.rs
vendored
738
third_party/rust/nom-3.2.1/src/util.rs
vendored
@ -1,738 +0,0 @@
|
||||
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
use internal::IResult;
|
||||
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
use verbose_errors::Err;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
use std::vec::Vec;
|
||||
use std::string::ToString;
|
||||
|
||||
/// useful functions to calculate the offset between slices and show a hexdump of a slice
|
||||
pub trait Offset {
|
||||
/// offset between the first byte of self and the first byte of the argument
|
||||
fn offset(&self, second:&Self) -> usize;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub trait HexDisplay {
|
||||
/// Converts the value of `self` to a hex dump, returning the owned
|
||||
/// string.
|
||||
fn to_hex(&self, chunk_size: usize) -> String;
|
||||
|
||||
/// Converts the value of `self` to a hex dump beginning at `from` address, returning the owned
|
||||
/// string.
|
||||
fn to_hex_from(&self, chunk_size: usize, from: usize) -> String;
|
||||
}
|
||||
|
||||
static CHARS: &'static[u8] = b"0123456789abcdef";
|
||||
|
||||
impl Offset for [u8] {
|
||||
fn offset(&self, second:&[u8]) -> usize {
|
||||
let fst = self.as_ptr();
|
||||
let snd = second.as_ptr();
|
||||
|
||||
snd as usize - fst as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl Offset for str {
|
||||
fn offset(&self, second: &Self) -> usize {
|
||||
let fst = self.as_ptr();
|
||||
let snd = second.as_ptr();
|
||||
|
||||
snd as usize - fst as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl HexDisplay for [u8] {
|
||||
#[allow(unused_variables)]
|
||||
fn to_hex(&self, chunk_size: usize) -> String {
|
||||
self.to_hex_from(chunk_size, 0)
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn to_hex_from(&self, chunk_size: usize, from: usize) -> String {
|
||||
let mut v = Vec::with_capacity(self.len() * 3);
|
||||
let mut i = from;
|
||||
for chunk in self.chunks(chunk_size) {
|
||||
let s = format!("{:08x}", i);
|
||||
for &ch in s.as_bytes().iter() {
|
||||
v.push(ch);
|
||||
}
|
||||
v.push('\t' as u8);
|
||||
|
||||
i = i + chunk_size;
|
||||
|
||||
for &byte in chunk {
|
||||
v.push(CHARS[(byte >> 4) as usize]);
|
||||
v.push(CHARS[(byte & 0xf) as usize]);
|
||||
v.push(' ' as u8);
|
||||
}
|
||||
if chunk_size > chunk.len() {
|
||||
for j in 0..(chunk_size - chunk.len()) {
|
||||
v.push(' ' as u8);
|
||||
v.push(' ' as u8);
|
||||
v.push(' ' as u8);
|
||||
}
|
||||
}
|
||||
v.push('\t' as u8);
|
||||
|
||||
for &byte in chunk {
|
||||
if (byte >=32 && byte <= 126) || byte >= 128 {
|
||||
v.push(byte);
|
||||
} else {
|
||||
v.push('.' as u8);
|
||||
}
|
||||
}
|
||||
v.push('\n' as u8);
|
||||
}
|
||||
|
||||
String::from_utf8_lossy(&v[..]).into_owned()
|
||||
}
|
||||
}
|
||||
|
||||
/// Prints a message if the parser fails
|
||||
///
|
||||
/// The message prints the `Error` or `Incomplete`
|
||||
/// and the parser's calling code
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # fn main() {
|
||||
/// named!(f, dbg!( tag!( "abcd" ) ) );
|
||||
///
|
||||
/// let a = &b"efgh"[..];
|
||||
///
|
||||
/// // Will print the following message:
|
||||
/// // Error(Position(0, [101, 102, 103, 104])) at l.5 by ' tag ! ( "abcd" ) '
|
||||
/// f(a);
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! dbg (
|
||||
($i: expr, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
let l = line!();
|
||||
match $submac!($i, $($args)*) {
|
||||
$crate::IResult::Error(a) => {
|
||||
println!("Error({:?}) at l.{} by ' {} '", a, l, stringify!($submac!($($args)*)));
|
||||
$crate::IResult::Error(a)
|
||||
},
|
||||
$crate::IResult::Incomplete(a) => {
|
||||
println!("Incomplete({:?}) at {} by ' {} '", a, l, stringify!($submac!($($args)*)));
|
||||
$crate::IResult::Incomplete(a)
|
||||
},
|
||||
a => a
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
($i:expr, $f:ident) => (
|
||||
dbg!($i, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
/// Prints a message and the input if the parser fails
|
||||
///
|
||||
/// The message prints the `Error` or `Incomplete`
|
||||
/// and the parser's calling code.
|
||||
///
|
||||
/// It also displays the input in hexdump format
|
||||
///
|
||||
/// ```ignore
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # fn main() {
|
||||
/// named!(f, dbg_dmp!( tag!( "abcd" ) ) );
|
||||
///
|
||||
/// let a = &b"efghijkl"[..];
|
||||
///
|
||||
/// // Will print the following message:
|
||||
/// // Error(Position(0, [101, 102, 103, 104, 105, 106, 107, 108])) at l.5 by ' tag ! ( "abcd" ) '
|
||||
/// // 00000000 65 66 67 68 69 6a 6b 6c efghijkl
|
||||
/// f(a);
|
||||
/// # }
|
||||
#[macro_export]
|
||||
macro_rules! dbg_dmp (
|
||||
($i: expr, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
use $crate::HexDisplay;
|
||||
let l = line!();
|
||||
match $submac!($i, $($args)*) {
|
||||
$crate::IResult::Error(a) => {
|
||||
println!("Error({:?}) at l.{} by ' {} '\n{}", a, l, stringify!($submac!($($args)*)), $i.to_hex(8));
|
||||
$crate::IResult::Error(a)
|
||||
},
|
||||
$crate::IResult::Incomplete(a) => {
|
||||
println!("Incomplete({:?}) at {} by ' {} '\n{}", a, l, stringify!($submac!($($args)*)), $i.to_hex(8));
|
||||
$crate::IResult::Incomplete(a)
|
||||
},
|
||||
a => a
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
($i:expr, $f:ident) => (
|
||||
dbg_dmp!($i, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
pub fn error_to_list<P,E:Clone>(e:&Err<P,E>) -> Vec<ErrorKind<E>> {
|
||||
let mut v:Vec<ErrorKind<E>> = Vec::new();
|
||||
match e {
|
||||
&Err::Code(ref i) | &Err::Position(ref i,_) => {
|
||||
v.push(i.clone());
|
||||
return v;
|
||||
},
|
||||
&Err::Node(ref i, ref next) | &Err::NodePosition(ref i, _, ref next) => {
|
||||
//v.push(i.clone());
|
||||
for error in next.iter() {
|
||||
if let &Err::Code(ref i2) = error {
|
||||
v.push(i2.clone());
|
||||
}
|
||||
if let &Err::Position(ref i2,_) = error {
|
||||
v.push(i2.clone());
|
||||
}
|
||||
}
|
||||
v.push(i.clone());
|
||||
v.reverse()
|
||||
}
|
||||
}
|
||||
|
||||
v
|
||||
}
|
||||
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
pub fn compare_error_paths<P,E:Clone+PartialEq>(e1:&Err<P,E>, e2:&Err<P,E>) -> bool {
|
||||
error_to_list(e1) == error_to_list(e2)
|
||||
}
|
||||
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
use std::hash::Hash;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
pub fn add_error_pattern<'a,I,O,E: Clone+Hash+Eq>(h: &mut HashMap<Vec<ErrorKind<E>>, &'a str>, res: IResult<I,O,E>, message: &'a str) -> bool {
|
||||
if let IResult::Error(e) = res {
|
||||
h.insert(error_to_list(&e), message);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn slice_to_offsets(input: &[u8], s: &[u8]) -> (usize, usize) {
|
||||
let start = input.as_ptr();
|
||||
let off1 = s.as_ptr() as usize - start as usize;
|
||||
let off2 = off1 + s.len();
|
||||
(off1, off2)
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
pub fn prepare_errors<O,E: Clone>(input: &[u8], res: IResult<&[u8],O,E>) -> Option<Vec<(ErrorKind<E>, usize, usize)> > {
|
||||
if let IResult::Error(e) = res {
|
||||
let mut v:Vec<(ErrorKind<E>, usize, usize)> = Vec::new();
|
||||
|
||||
match e {
|
||||
Err::Code(_) => {},
|
||||
Err::Position(i, p) => {
|
||||
let (o1, o2) = slice_to_offsets(input, p);
|
||||
v.push((i, o1, o2));
|
||||
},
|
||||
Err::Node(_, _) => {},
|
||||
Err::NodePosition(i, p, next) => {
|
||||
//v.push(i.clone());
|
||||
for error in next.iter() {
|
||||
if let &Err::Position(ref i2, ref p2) = error {
|
||||
let (o1, o2) = slice_to_offsets(input, p2);
|
||||
v.push((i2.clone(), o1, o2));
|
||||
}
|
||||
}
|
||||
let (o1, o2) = slice_to_offsets(input, p);
|
||||
v.push((i, o1, o2));
|
||||
v.reverse()
|
||||
}
|
||||
}
|
||||
|
||||
v.sort_by(|a, b| a.1.cmp(&b.1));
|
||||
Some(v)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
pub fn print_error<O,E:Clone>(input: &[u8], res: IResult<&[u8],O,E>) {
|
||||
if let Some(v) = prepare_errors(input, res) {
|
||||
let colors = generate_colors(&v);
|
||||
println!("parser codes: {}", print_codes(colors, HashMap::new()));
|
||||
println!("{}", print_offsets(input, 0, &v));
|
||||
|
||||
} else {
|
||||
println!("not an error");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
pub fn generate_colors<E>(v: &[(ErrorKind<E>, usize, usize)]) -> HashMap<u32, u8> {
|
||||
let mut h: HashMap<u32, u8> = HashMap::new();
|
||||
let mut color = 0;
|
||||
|
||||
for &(ref c,_,_) in v.iter() {
|
||||
h.insert(error_to_u32(c), color + 31);
|
||||
color = color + 1 % 7;
|
||||
}
|
||||
|
||||
h
|
||||
}
|
||||
|
||||
pub fn code_from_offset<E>(v: &[(ErrorKind<E>, usize, usize)], offset: usize) -> Option<u32> {
|
||||
let mut acc: Option<(u32, usize, usize)> = None;
|
||||
for &(ref ek, s, e) in v.iter() {
|
||||
let c = error_to_u32(ek);
|
||||
if s <= offset && offset <=e {
|
||||
if let Some((_, start, end)) = acc {
|
||||
if start <= s && e <= end {
|
||||
acc = Some((c, s, e));
|
||||
}
|
||||
} else {
|
||||
acc = Some((c, s, e));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some((code, _, _)) = acc {
|
||||
return Some(code);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_color(v: &mut Vec<u8>) {
|
||||
v.push(0x1B);
|
||||
v.push('[' as u8);
|
||||
v.push(0);
|
||||
v.push('m' as u8);
|
||||
}
|
||||
|
||||
pub fn write_color(v: &mut Vec<u8>, color: u8) {
|
||||
v.push(0x1B);
|
||||
v.push('[' as u8);
|
||||
v.push(1);
|
||||
v.push(';' as u8);
|
||||
let s = color.to_string();
|
||||
let bytes = s.as_bytes();
|
||||
v.extend(bytes.iter().cloned());
|
||||
v.push('m' as u8);
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub fn print_codes(colors: HashMap<u32, u8>, names: HashMap<u32, &str>) -> String {
|
||||
let mut v = Vec::new();
|
||||
for (code, &color) in &colors {
|
||||
if let Some(&s) = names.get(&code) {
|
||||
let bytes = s.as_bytes();
|
||||
write_color(&mut v, color);
|
||||
v.extend(bytes.iter().cloned());
|
||||
} else {
|
||||
let s = code.to_string();
|
||||
let bytes = s.as_bytes();
|
||||
write_color(&mut v, color);
|
||||
v.extend(bytes.iter().cloned());
|
||||
}
|
||||
reset_color(&mut v);
|
||||
v.push(' ' as u8);
|
||||
}
|
||||
reset_color(&mut v);
|
||||
|
||||
String::from_utf8_lossy(&v[..]).into_owned()
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(feature = "verbose-errors")]
|
||||
pub fn print_offsets<E>(input: &[u8], from: usize, offsets: &[(ErrorKind<E>, usize, usize)]) -> String {
|
||||
let mut v = Vec::with_capacity(input.len() * 3);
|
||||
let mut i = from;
|
||||
let chunk_size = 8;
|
||||
let mut current_code: Option<u32> = None;
|
||||
let mut current_code2: Option<u32> = None;
|
||||
|
||||
let colors = generate_colors(&offsets);
|
||||
|
||||
for chunk in input.chunks(chunk_size) {
|
||||
let s = format!("{:08x}", i);
|
||||
for &ch in s.as_bytes().iter() {
|
||||
v.push(ch);
|
||||
}
|
||||
v.push('\t' as u8);
|
||||
|
||||
let mut k = i;
|
||||
let mut l = i;
|
||||
for &byte in chunk {
|
||||
if let Some(code) = code_from_offset(&offsets, k) {
|
||||
if let Some(current) = current_code {
|
||||
if current != code {
|
||||
reset_color(&mut v);
|
||||
current_code = Some(code);
|
||||
if let Some(&color) = colors.get(&code) {
|
||||
write_color(&mut v, color);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
current_code = Some(code);
|
||||
if let Some(&color) = colors.get(&code) {
|
||||
write_color(&mut v, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
v.push(CHARS[(byte >> 4) as usize]);
|
||||
v.push(CHARS[(byte & 0xf) as usize]);
|
||||
v.push(' ' as u8);
|
||||
k = k + 1;
|
||||
}
|
||||
|
||||
reset_color(&mut v);
|
||||
|
||||
if chunk_size > chunk.len() {
|
||||
for _ in 0..(chunk_size - chunk.len()) {
|
||||
v.push(' ' as u8);
|
||||
v.push(' ' as u8);
|
||||
v.push(' ' as u8);
|
||||
}
|
||||
}
|
||||
v.push('\t' as u8);
|
||||
|
||||
for &byte in chunk {
|
||||
if let Some(code) = code_from_offset(&offsets, l) {
|
||||
if let Some(current) = current_code2 {
|
||||
if current != code {
|
||||
reset_color(&mut v);
|
||||
current_code2 = Some(code);
|
||||
if let Some(&color) = colors.get(&code) {
|
||||
write_color(&mut v, color);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
current_code2 = Some(code);
|
||||
if let Some(&color) = colors.get(&code) {
|
||||
write_color(&mut v, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (byte >=32 && byte <= 126) || byte >= 128 {
|
||||
v.push(byte);
|
||||
} else {
|
||||
v.push('.' as u8);
|
||||
}
|
||||
l = l + 1;
|
||||
}
|
||||
reset_color(&mut v);
|
||||
|
||||
v.push('\n' as u8);
|
||||
i = i + chunk_size;
|
||||
}
|
||||
|
||||
String::from_utf8_lossy(&v[..]).into_owned()
|
||||
}
|
||||
|
||||
pub trait AsBytes {
|
||||
fn as_bytes(&self) -> &[u8];
|
||||
}
|
||||
|
||||
impl<'a> AsBytes for &'a str {
|
||||
#[inline(always)]
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
str::as_bytes(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsBytes for str {
|
||||
#[inline(always)]
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
str::as_bytes(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsBytes for &'a [u8] {
|
||||
#[inline(always)]
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl AsBytes for [u8] {
|
||||
#[inline(always)]
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! array_impls {
|
||||
($($N:expr)+) => {
|
||||
$(
|
||||
impl<'a> AsBytes for &'a [u8; $N] {
|
||||
#[inline(always)]
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl AsBytes for [u8; $N] {
|
||||
#[inline(always)]
|
||||
fn as_bytes(&self) -> &[u8] {
|
||||
self
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
array_impls! {
|
||||
0 1 2 3 4 5 6 7 8 9
|
||||
10 11 12 13 14 15 16 17 18 19
|
||||
20 21 22 23 24 25 26 27 28 29
|
||||
30 31 32
|
||||
}
|
||||
|
||||
/// indicates which parser returned an error
|
||||
#[derive(Debug,PartialEq,Eq,Hash,Clone)]
|
||||
pub enum ErrorKind<E=u32> {
|
||||
Custom(E),
|
||||
Tag,
|
||||
MapRes,
|
||||
MapOpt,
|
||||
Alt,
|
||||
IsNot,
|
||||
IsA,
|
||||
SeparatedList,
|
||||
SeparatedNonEmptyList,
|
||||
Many0,
|
||||
Many1,
|
||||
ManyTill,
|
||||
Count,
|
||||
TakeUntilAndConsume,
|
||||
TakeUntil,
|
||||
TakeUntilEitherAndConsume,
|
||||
TakeUntilEither,
|
||||
LengthValue,
|
||||
TagClosure,
|
||||
Alpha,
|
||||
Digit,
|
||||
HexDigit,
|
||||
OctDigit,
|
||||
AlphaNumeric,
|
||||
Space,
|
||||
MultiSpace,
|
||||
LengthValueFn,
|
||||
Eof,
|
||||
ExprOpt,
|
||||
ExprRes,
|
||||
CondReduce,
|
||||
Switch,
|
||||
TagBits,
|
||||
OneOf,
|
||||
NoneOf,
|
||||
Char,
|
||||
CrLf,
|
||||
RegexpMatch,
|
||||
RegexpMatches,
|
||||
RegexpFind,
|
||||
RegexpCapture,
|
||||
RegexpCaptures,
|
||||
TakeWhile1,
|
||||
Complete,
|
||||
Fix,
|
||||
Escaped,
|
||||
EscapedTransform,
|
||||
TagStr,
|
||||
IsNotStr,
|
||||
IsAStr,
|
||||
TakeWhile1Str,
|
||||
NonEmpty,
|
||||
ManyMN,
|
||||
TakeUntilAndConsumeStr,
|
||||
TakeUntilStr,
|
||||
Not,
|
||||
Permutation,
|
||||
Verify,
|
||||
TakeTill1,
|
||||
}
|
||||
|
||||
pub fn error_to_u32<E>(e: &ErrorKind<E>) -> u32 {
|
||||
match *e {
|
||||
ErrorKind::Custom(_) => 0,
|
||||
ErrorKind::Tag => 1,
|
||||
ErrorKind::MapRes => 2,
|
||||
ErrorKind::MapOpt => 3,
|
||||
ErrorKind::Alt => 4,
|
||||
ErrorKind::IsNot => 5,
|
||||
ErrorKind::IsA => 6,
|
||||
ErrorKind::SeparatedList => 7,
|
||||
ErrorKind::SeparatedNonEmptyList => 8,
|
||||
ErrorKind::Many1 => 9,
|
||||
ErrorKind::Count => 10,
|
||||
ErrorKind::TakeUntilAndConsume => 11,
|
||||
ErrorKind::TakeUntil => 12,
|
||||
ErrorKind::TakeUntilEitherAndConsume => 13,
|
||||
ErrorKind::TakeUntilEither => 14,
|
||||
ErrorKind::LengthValue => 15,
|
||||
ErrorKind::TagClosure => 16,
|
||||
ErrorKind::Alpha => 17,
|
||||
ErrorKind::Digit => 18,
|
||||
ErrorKind::AlphaNumeric => 19,
|
||||
ErrorKind::Space => 20,
|
||||
ErrorKind::MultiSpace => 21,
|
||||
ErrorKind::LengthValueFn => 22,
|
||||
ErrorKind::Eof => 23,
|
||||
ErrorKind::ExprOpt => 24,
|
||||
ErrorKind::ExprRes => 25,
|
||||
ErrorKind::CondReduce => 26,
|
||||
ErrorKind::Switch => 27,
|
||||
ErrorKind::TagBits => 28,
|
||||
ErrorKind::OneOf => 29,
|
||||
ErrorKind::NoneOf => 30,
|
||||
ErrorKind::Char => 40,
|
||||
ErrorKind::CrLf => 41,
|
||||
ErrorKind::RegexpMatch => 42,
|
||||
ErrorKind::RegexpMatches => 43,
|
||||
ErrorKind::RegexpFind => 44,
|
||||
ErrorKind::RegexpCapture => 45,
|
||||
ErrorKind::RegexpCaptures => 46,
|
||||
ErrorKind::TakeWhile1 => 47,
|
||||
ErrorKind::Complete => 48,
|
||||
ErrorKind::Fix => 49,
|
||||
ErrorKind::Escaped => 50,
|
||||
ErrorKind::EscapedTransform => 51,
|
||||
ErrorKind::TagStr => 52,
|
||||
ErrorKind::IsNotStr => 53,
|
||||
ErrorKind::IsAStr => 54,
|
||||
ErrorKind::TakeWhile1Str => 55,
|
||||
ErrorKind::NonEmpty => 56,
|
||||
ErrorKind::ManyMN => 57,
|
||||
ErrorKind::TakeUntilAndConsumeStr => 58,
|
||||
ErrorKind::HexDigit => 59,
|
||||
ErrorKind::TakeUntilStr => 60,
|
||||
ErrorKind::OctDigit => 61,
|
||||
ErrorKind::Many0 => 62,
|
||||
ErrorKind::Not => 63,
|
||||
ErrorKind::Permutation => 64,
|
||||
ErrorKind::ManyTill => 65,
|
||||
ErrorKind::Verify => 66,
|
||||
ErrorKind::TakeTill1 => 67,
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> ErrorKind<E> {
|
||||
pub fn description(&self) -> &str {
|
||||
match *self {
|
||||
ErrorKind::Custom(_) => "Custom error",
|
||||
ErrorKind::Tag => "Tag",
|
||||
ErrorKind::MapRes => "Map on Result",
|
||||
ErrorKind::MapOpt => "Map on Option",
|
||||
ErrorKind::Alt => "Alternative",
|
||||
ErrorKind::IsNot => "IsNot",
|
||||
ErrorKind::IsA => "IsA",
|
||||
ErrorKind::SeparatedList => "Separated list",
|
||||
ErrorKind::SeparatedNonEmptyList => "Separated non empty list",
|
||||
ErrorKind::Many0 => "Many0",
|
||||
ErrorKind::Many1 => "Many1",
|
||||
ErrorKind::Count => "Count",
|
||||
ErrorKind::TakeUntilAndConsume => "Take until and consume",
|
||||
ErrorKind::TakeUntil => "Take until",
|
||||
ErrorKind::TakeUntilEitherAndConsume => "Take until either and consume",
|
||||
ErrorKind::TakeUntilEither => "Take until either",
|
||||
ErrorKind::LengthValue => "Length followed by value",
|
||||
ErrorKind::TagClosure => "Tag closure",
|
||||
ErrorKind::Alpha => "Alphabetic",
|
||||
ErrorKind::Digit => "Digit",
|
||||
ErrorKind::AlphaNumeric => "AlphaNumeric",
|
||||
ErrorKind::Space => "Space",
|
||||
ErrorKind::MultiSpace => "Multiple spaces",
|
||||
ErrorKind::LengthValueFn => "LengthValueFn",
|
||||
ErrorKind::Eof => "End of file",
|
||||
ErrorKind::ExprOpt => "Evaluate Option",
|
||||
ErrorKind::ExprRes => "Evaluate Result",
|
||||
ErrorKind::CondReduce => "Condition reduce",
|
||||
ErrorKind::Switch => "Switch",
|
||||
ErrorKind::TagBits => "Tag on bitstream",
|
||||
ErrorKind::OneOf => "OneOf",
|
||||
ErrorKind::NoneOf => "NoneOf",
|
||||
ErrorKind::Char => "Char",
|
||||
ErrorKind::CrLf => "CrLf",
|
||||
ErrorKind::RegexpMatch => "RegexpMatch",
|
||||
ErrorKind::RegexpMatches => "RegexpMatches",
|
||||
ErrorKind::RegexpFind => "RegexpFind",
|
||||
ErrorKind::RegexpCapture => "RegexpCapture",
|
||||
ErrorKind::RegexpCaptures => "RegexpCaptures",
|
||||
ErrorKind::TakeWhile1 => "TakeWhile1",
|
||||
ErrorKind::Complete => "Complete",
|
||||
ErrorKind::Fix => "Fix",
|
||||
ErrorKind::Escaped => "Escaped",
|
||||
ErrorKind::EscapedTransform => "EscapedTransform",
|
||||
ErrorKind::TagStr => "Tag on strings",
|
||||
ErrorKind::IsNotStr => "IsNot on strings",
|
||||
ErrorKind::IsAStr => "IsA on strings",
|
||||
ErrorKind::TakeWhile1Str => "TakeWhile1 on strings",
|
||||
ErrorKind::NonEmpty => "NonEmpty",
|
||||
ErrorKind::ManyMN => "Many(m, n)",
|
||||
ErrorKind::TakeUntilAndConsumeStr => "Take until and consume on strings",
|
||||
ErrorKind::HexDigit => "Hexadecimal Digit",
|
||||
ErrorKind::TakeUntilStr => "Take until on strings",
|
||||
ErrorKind::OctDigit => "Octal digit",
|
||||
ErrorKind::Not => "Negation",
|
||||
ErrorKind::Permutation => "Permutation",
|
||||
ErrorKind::ManyTill => "ManyTill",
|
||||
ErrorKind::Verify => "predicate verification",
|
||||
ErrorKind::TakeTill1 => "TakeTill1",
|
||||
}
|
||||
|
||||
}
|
||||
/// Convert Err into an ErrorKind.
|
||||
///
|
||||
/// This allows application code to use ErrorKind and stay independent from the `verbose-errors` features activation.
|
||||
pub fn into_error_kind(self) -> ErrorKind<E> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_offset_u8() {
|
||||
let s = b"abcd123";
|
||||
let a = &s[..];
|
||||
let b = &a[2..];
|
||||
let c = &a[..4];
|
||||
let d = &a[3..5];
|
||||
assert_eq!(a.offset(b), 2);
|
||||
assert_eq!(a.offset(c), 0);
|
||||
assert_eq!(a.offset(d), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_offset_str() {
|
||||
let s = "abcřèÂßÇd123";
|
||||
let a = &s[..];
|
||||
let b = &a[7..];
|
||||
let c = &a[..5];
|
||||
let d = &a[5..9];
|
||||
assert_eq!(a.offset(b), 7);
|
||||
assert_eq!(a.offset(c), 0);
|
||||
assert_eq!(a.offset(d), 5);
|
||||
}
|
||||
}
|
227
third_party/rust/nom-3.2.1/src/verbose_errors.rs
vendored
227
third_party/rust/nom-3.2.1/src/verbose_errors.rs
vendored
@ -1,227 +0,0 @@
|
||||
//! Error management
|
||||
//!
|
||||
//! there are two ways to handle errors in nom. The first one,
|
||||
//! activated by default, uses the `nom::ErrorKind<E=u32>` enum
|
||||
//! in the error branch of `IResult`. This enum can hold either
|
||||
//! a parser specific error code, or a custom error type you
|
||||
//! specify.
|
||||
//!
|
||||
//! If you need more advanced error management, you can activate
|
||||
//! the "verbose-errors" compilation feature, which will give you
|
||||
//! the error system available in nom 1.0. The verbose errors
|
||||
//! accumulate error codes and positions as you backtrack through
|
||||
//! the parser tree. From there, you can precisely identify which
|
||||
//! parts of the input triggered the error case.
|
||||
//!
|
||||
//! Please note that the verbose error management is a bit slower
|
||||
//! than the simple one.
|
||||
use util::ErrorKind;
|
||||
use internal::{IResult, IError};
|
||||
use internal::IResult::*;
|
||||
|
||||
/// Contains the error that a parser can return
|
||||
///
|
||||
/// If you use the `verbose-errors` compilation feature,
|
||||
/// `nom::Err` will be the enum defined here,
|
||||
/// otherwise, it will amount to a `ErrorKind<E=u32>`.
|
||||
///
|
||||
/// It can represent a linked list of errors, indicating the path taken in the parsing tree, with corresponding position in the input data.
|
||||
/// It depends on P, the input position (for a &[u8] parser, it would be a &[u8]), and E, the custom error type (by default, u32)
|
||||
#[derive(Debug,PartialEq,Eq,Clone)]
|
||||
pub enum Err<P,E=u32>{
|
||||
/// An error code, represented by an ErrorKind, which can contain a custom error code represented by E
|
||||
Code(ErrorKind<E>),
|
||||
/// An error code, and the next error
|
||||
Node(ErrorKind<E>, Vec<Err<P,E>>),
|
||||
/// An error code, and the input position
|
||||
Position(ErrorKind<E>, P),
|
||||
/// An error code, the input position and the next error
|
||||
NodePosition(ErrorKind<E>, P, Vec<Err<P,E>>)
|
||||
}
|
||||
|
||||
impl<P,E> Err<P,E> {
|
||||
/// Convert Err into ErrorKind.
|
||||
///
|
||||
/// This allows application code to use ErrorKind and stay independent from the verbose-errors features activation.
|
||||
pub fn into_error_kind(self) -> ErrorKind<E> {
|
||||
match self {
|
||||
Err::Code(kind) => kind,
|
||||
Err::Node(kind, _) => kind,
|
||||
Err::Position(kind, _) => kind,
|
||||
Err::NodePosition(kind, _, _) => kind,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I,O,E> IResult<I,O,E> {
|
||||
/// Maps a `IResult<I, O, E>` to `IResult<I, O, N>` by appling a function
|
||||
/// to a contained `Error` value, leaving `Done` and `Incomplete` value
|
||||
/// untouched.
|
||||
#[inline]
|
||||
pub fn map_err<N, F>(self, f: F) -> IResult<I, O, N>
|
||||
where F: FnOnce(Err<I, E>) -> Err<I, N> {
|
||||
match self {
|
||||
Error(e) => Error(f(e)),
|
||||
Incomplete(n) => Incomplete(n),
|
||||
Done(i, o) => Done(i, o),
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwrap the contained `Error(I, E)` value, or panic if the `IResult` is not
|
||||
/// `Error`.
|
||||
pub fn unwrap_err(self) -> Err<I, E> {
|
||||
match self {
|
||||
Error(e) => e,
|
||||
Done(_, _) => panic!("unwrap_err() called on an IResult that is Done"),
|
||||
Incomplete(_) => panic!("unwrap_err() called on an IResult that is Incomplete"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the IResult to a std::result::Result
|
||||
pub fn to_full_result(self) -> Result<O, IError<I,E>> {
|
||||
match self {
|
||||
Done(_, o) => Ok(o),
|
||||
Incomplete(n) => Err(IError::Incomplete(n)),
|
||||
Error(e) => Err(IError::Error(e))
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the IResult to a std::result::Result
|
||||
pub fn to_result(self) -> Result<O, Err<I,E>> {
|
||||
match self {
|
||||
Done(_, o) => Ok(o),
|
||||
Error(e) => Err(e),
|
||||
Incomplete(_) => panic!("to_result() called on an IResult that is Incomplete")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::any::Any;
|
||||
#[cfg(feature = "std")]
|
||||
use std::{error,fmt};
|
||||
#[cfg(feature = "std")]
|
||||
use std::fmt::Debug;
|
||||
#[cfg(feature = "std")]
|
||||
impl<P:Debug+Any,E:Debug+Any> error::Error for Err<P,E> {
|
||||
fn description(&self) -> &str {
|
||||
let kind = match *self {
|
||||
Err::Code(ref e) | Err::Node(ref e, _) | Err::Position(ref e, _) | Err::NodePosition(ref e, _, _) => e
|
||||
};
|
||||
kind.description()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<P:fmt::Debug,E:fmt::Debug> fmt::Display for Err<P,E> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Err::Code(ref e) | Err::Node(ref e, _) => {
|
||||
write!(f, "{:?}", e)
|
||||
},
|
||||
Err::Position(ref e, ref p) | Err::NodePosition(ref e, ref p, _) => {
|
||||
write!(f, "{:?}:{:?}", p, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// translate parser result from IResult<I,O,u32> to IResult<I,O,E> with a custom type
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate nom;
|
||||
/// # use std::collections;
|
||||
/// # use nom::IResult::Error;
|
||||
/// # use nom::Err::{Position,NodePosition};
|
||||
/// # use nom::ErrorKind;
|
||||
/// # fn main() {
|
||||
/// // will add a Custom(42) error to the error chain
|
||||
/// named!(err_test, add_return_error!(ErrorKind::Custom(42), tag!("abcd")));
|
||||
/// // Convert to IREsult<&[u8], &[u8], &str>
|
||||
/// named!(parser<&[u8], &[u8], &str>, add_return_error!(ErrorKind::Custom("custom error message"), fix_error!(&str, err_test)));
|
||||
///
|
||||
/// let a = &b"efghblah"[..];
|
||||
/// let res_a = parser(a);
|
||||
/// assert_eq!(res_a, Error(NodePosition( ErrorKind::Custom("custom error message"), a, vec!(Position(ErrorKind::Fix, a)))));
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! fix_error (
|
||||
($i:expr, $t:ty, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
match $submac!($i, $($args)*) {
|
||||
$crate::IResult::Incomplete(x) => $crate::IResult::Incomplete(x),
|
||||
$crate::IResult::Done(i, o) => $crate::IResult::Done(i, o),
|
||||
$crate::IResult::Error(e) => {
|
||||
let err = match e {
|
||||
$crate::Err::Code($crate::ErrorKind::Custom(_)) |
|
||||
$crate::Err::Node($crate::ErrorKind::Custom(_), _) => {
|
||||
let e: $crate::ErrorKind<$t> = $crate::ErrorKind::Fix;
|
||||
$crate::Err::Code(e)
|
||||
},
|
||||
$crate::Err::Position($crate::ErrorKind::Custom(_), p) |
|
||||
$crate::Err::NodePosition($crate::ErrorKind::Custom(_), p, _) => {
|
||||
let e: $crate::ErrorKind<$t> = $crate::ErrorKind::Fix;
|
||||
$crate::Err::Position(e, p)
|
||||
},
|
||||
$crate::Err::Code(_) |
|
||||
$crate::Err::Node(_, _) => {
|
||||
let e: $crate::ErrorKind<$t> = $crate::ErrorKind::Fix;
|
||||
$crate::Err::Code(e)
|
||||
},
|
||||
$crate::Err::Position(_, p) |
|
||||
$crate::Err::NodePosition(_, p, _) => {
|
||||
let e: $crate::ErrorKind<$t> = $crate::ErrorKind::Fix;
|
||||
$crate::Err::Position(e, p)
|
||||
},
|
||||
};
|
||||
$crate::IResult::Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
($i:expr, $t:ty, $f:expr) => (
|
||||
fix_error!($i, $t, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
/// `flat_map!(R -> IResult<R,S>, S -> IResult<S,T>) => R -> IResult<R, T>`
|
||||
///
|
||||
/// combines a parser R -> IResult<R,S> and
|
||||
/// a parser S -> IResult<S,T> to return another
|
||||
/// parser R -> IResult<R,T>
|
||||
#[macro_export]
|
||||
macro_rules! flat_map(
|
||||
($i:expr, $submac:ident!( $($args:tt)* ), $submac2:ident!( $($args2:tt)* )) => (
|
||||
{
|
||||
match $submac!($i, $($args)*) {
|
||||
$crate::IResult::Error(e) => $crate::IResult::Error(e),
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown) => $crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(i)) => $crate::IResult::Incomplete($crate::Needed::Size(i)),
|
||||
$crate::IResult::Done(i, o) => match $submac2!(o, $($args2)*) {
|
||||
$crate::IResult::Error(e) => {
|
||||
let err = match e {
|
||||
$crate::Err::Code(k) | $crate::Err::Node(k, _) | $crate::Err::Position(k, _) | $crate::Err::NodePosition(k, _, _) => {
|
||||
$crate::Err::Position(k, $i)
|
||||
}
|
||||
};
|
||||
$crate::IResult::Error(err)
|
||||
},
|
||||
$crate::IResult::Incomplete($crate::Needed::Unknown) => $crate::IResult::Incomplete($crate::Needed::Unknown),
|
||||
$crate::IResult::Incomplete($crate::Needed::Size(ref i2)) => $crate::IResult::Incomplete($crate::Needed::Size(*i2)),
|
||||
$crate::IResult::Done(_, o2) => $crate::IResult::Done(i, o2)
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
($i:expr, $submac:ident!( $($args:tt)* ), $g:expr) => (
|
||||
flat_map!($i, $submac!($($args)*), call!($g));
|
||||
);
|
||||
($i:expr, $f:expr, $g:expr) => (
|
||||
flat_map!($i, call!($f), call!($g));
|
||||
);
|
||||
($i:expr, $f:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
flat_map!($i, call!($f), $submac!($($args)*));
|
||||
);
|
||||
);
|
1056
third_party/rust/nom-3.2.1/src/whitespace.rs
vendored
1056
third_party/rust/nom-3.2.1/src/whitespace.rs
vendored
File diff suppressed because it is too large
Load Diff
87
third_party/rust/nom-3.2.1/tests/arithmetic.rs
vendored
87
third_party/rust/nom-3.2.1/tests/arithmetic.rs
vendored
@ -1,87 +0,0 @@
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::{IResult,digit};
|
||||
|
||||
// Parser definition
|
||||
|
||||
use std::str;
|
||||
use std::str::FromStr;
|
||||
|
||||
// We parse any expr surrounded by parens, ignoring all whitespaces around those
|
||||
named!(parens<i64>, ws!(delimited!( tag!("("), expr, tag!(")") )) );
|
||||
|
||||
// We transform an integer string into a i64, ignoring surrounding whitespaces
|
||||
// We look for a digit suite, and try to convert it.
|
||||
// If either str::from_utf8 or FromStr::from_str fail,
|
||||
// we fallback to the parens parser defined above
|
||||
named!(factor<i64>, alt!(
|
||||
map_res!(
|
||||
map_res!(
|
||||
ws!(digit),
|
||||
str::from_utf8
|
||||
),
|
||||
FromStr::from_str
|
||||
)
|
||||
| parens
|
||||
)
|
||||
);
|
||||
|
||||
// We read an initial factor and for each time we find
|
||||
// a * or / operator followed by another factor, we do
|
||||
// the math by folding everything
|
||||
named!(term <i64>, do_parse!(
|
||||
init: factor >>
|
||||
res: fold_many0!(
|
||||
pair!(alt!(tag!("*") | tag!("/")), factor),
|
||||
init,
|
||||
|acc, (op, val): (&[u8], i64)| {
|
||||
if (op[0] as char) == '*' { acc * val } else { acc / val }
|
||||
}
|
||||
) >>
|
||||
(res)
|
||||
)
|
||||
);
|
||||
|
||||
named!(expr <i64>, do_parse!(
|
||||
init: term >>
|
||||
res: fold_many0!(
|
||||
pair!(alt!(tag!("+") | tag!("-")), term),
|
||||
init,
|
||||
|acc, (op, val): (&[u8], i64)| {
|
||||
if (op[0] as char) == '+' { acc + val } else { acc - val }
|
||||
}
|
||||
) >>
|
||||
(res)
|
||||
)
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn factor_test() {
|
||||
assert_eq!(factor(&b"3"[..]), IResult::Done(&b""[..], 3));
|
||||
assert_eq!(factor(&b" 12"[..]), IResult::Done(&b""[..], 12));
|
||||
assert_eq!(factor(&b"537 "[..]), IResult::Done(&b""[..], 537));
|
||||
assert_eq!(factor(&b" 24 "[..]), IResult::Done(&b""[..], 24));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn term_test() {
|
||||
assert_eq!(term(&b" 12 *2 / 3"[..]), IResult::Done(&b""[..], 8));
|
||||
assert_eq!(term(&b" 2* 3 *2 *2 / 3"[..]), IResult::Done(&b""[..], 8));
|
||||
assert_eq!(term(&b" 48 / 3/2"[..]), IResult::Done(&b""[..], 8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expr_test() {
|
||||
assert_eq!(expr(&b" 1 + 2 "[..]), IResult::Done(&b""[..], 3));
|
||||
assert_eq!(expr(&b" 12 + 6 - 4+ 3"[..]), IResult::Done(&b""[..], 17));
|
||||
assert_eq!(expr(&b" 1 + 2*3 + 4"[..]), IResult::Done(&b""[..], 11));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parens_test() {
|
||||
assert_eq!(expr(&b" ( 2 )"[..]), IResult::Done(&b""[..], 2));
|
||||
assert_eq!(expr(&b" 2* ( 3 + 4 ) "[..]), IResult::Done(&b""[..], 14));
|
||||
assert_eq!(expr(&b" 2*2 / ( 5 - 1) + 3"[..]), IResult::Done(&b""[..], 4));
|
||||
}
|
137
third_party/rust/nom-3.2.1/tests/arithmetic_ast.rs
vendored
137
third_party/rust/nom-3.2.1/tests/arithmetic_ast.rs
vendored
@ -1,137 +0,0 @@
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use std::fmt;
|
||||
use std::fmt::{Display, Debug, Formatter};
|
||||
|
||||
use std::str;
|
||||
use std::str::FromStr;
|
||||
|
||||
use nom::{IResult, digit, multispace};
|
||||
|
||||
pub enum Expr {
|
||||
Value(i64),
|
||||
Add(Box<Expr>, Box<Expr>),
|
||||
Sub(Box<Expr>, Box<Expr>),
|
||||
Mul(Box<Expr>, Box<Expr>),
|
||||
Div(Box<Expr>, Box<Expr>),
|
||||
Paren(Box<Expr>),
|
||||
}
|
||||
|
||||
pub enum Oper {
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
}
|
||||
|
||||
impl Display for Expr {
|
||||
fn fmt(&self, format: &mut Formatter) -> fmt::Result {
|
||||
use self::Expr::*;
|
||||
match *self {
|
||||
Value(val) => write!(format, "{}", val),
|
||||
Add(ref left, ref right) => write!(format, "{} + {}", left, right),
|
||||
Sub(ref left, ref right) => write!(format, "{} - {}", left, right),
|
||||
Mul(ref left, ref right) => write!(format, "{} * {}", left, right),
|
||||
Div(ref left, ref right) => write!(format, "{} / {}", left, right),
|
||||
Paren(ref expr) => write!(format, "({})", expr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Expr {
|
||||
fn fmt(&self, format: &mut Formatter) -> fmt::Result {
|
||||
use self::Expr::*;
|
||||
match *self {
|
||||
Value(val) => write!(format, "{}", val),
|
||||
Add(ref left, ref right) => write!(format, "({:?} + {:?})", left, right),
|
||||
Sub(ref left, ref right) => write!(format, "({:?} - {:?})", left, right),
|
||||
Mul(ref left, ref right) => write!(format, "({:?} * {:?})", left, right),
|
||||
Div(ref left, ref right) => write!(format, "({:?} / {:?})", left, right),
|
||||
Paren(ref expr) => write!(format, "[{:?}]", expr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
named!(parens< Expr >, delimited!(
|
||||
delimited!(opt!(multispace), tag!("("), opt!(multispace)),
|
||||
map!(map!(expr, Box::new), Expr::Paren),
|
||||
delimited!(opt!(multispace), tag!(")"), opt!(multispace))
|
||||
)
|
||||
);
|
||||
|
||||
named!(factor< Expr >, alt_complete!(
|
||||
map!(
|
||||
map_res!(
|
||||
map_res!(
|
||||
delimited!(opt!(multispace), digit, opt!(multispace)),
|
||||
str::from_utf8
|
||||
),
|
||||
FromStr::from_str
|
||||
),
|
||||
Expr::Value)
|
||||
| parens
|
||||
)
|
||||
);
|
||||
|
||||
fn fold_exprs(initial: Expr, remainder: Vec<(Oper, Expr)>) -> Expr {
|
||||
remainder.into_iter().fold(initial, |acc, pair| {
|
||||
let (oper, expr) = pair;
|
||||
match oper {
|
||||
Oper::Add => Expr::Add(Box::new(acc), Box::new(expr)),
|
||||
Oper::Sub => Expr::Sub(Box::new(acc), Box::new(expr)),
|
||||
Oper::Mul => Expr::Mul(Box::new(acc), Box::new(expr)),
|
||||
Oper::Div => Expr::Div(Box::new(acc), Box::new(expr)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
named!(term< Expr >, do_parse!(
|
||||
initial: factor >>
|
||||
remainder: many0!(
|
||||
alt!(
|
||||
do_parse!(tag!("*") >> mul: factor >> (Oper::Mul, mul)) |
|
||||
do_parse!(tag!("/") >> div: factor >> (Oper::Div, div))
|
||||
)
|
||||
) >>
|
||||
(fold_exprs(initial, remainder))
|
||||
));
|
||||
|
||||
named!(expr< Expr >, do_parse!(
|
||||
initial: term >>
|
||||
remainder: many0!(
|
||||
alt!(
|
||||
do_parse!(tag!("+") >> add: term >> (Oper::Add, add)) |
|
||||
do_parse!(tag!("-") >> sub: term >> (Oper::Sub, sub))
|
||||
)
|
||||
) >>
|
||||
(fold_exprs(initial, remainder))
|
||||
));
|
||||
|
||||
#[test]
|
||||
fn factor_test() {
|
||||
assert_eq!(factor(&b" 3 "[..]).map(|x| format!("{:?}", x)),
|
||||
IResult::Done(&b""[..], String::from("3")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn term_test() {
|
||||
assert_eq!(term(&b" 3 * 5 "[..]).map(|x| format!("{:?}", x)),
|
||||
IResult::Done(&b""[..], String::from("(3 * 5)")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expr_test() {
|
||||
assert_eq!(expr(&b" 1 + 2 * 3 "[..]).map(|x| format!("{:?}", x)),
|
||||
IResult::Done(&b""[..], String::from("(1 + (2 * 3))")));
|
||||
assert_eq!(expr(&b" 1 + 2 * 3 / 4 - 5 "[..]).map(|x| format!("{:?}", x)),
|
||||
IResult::Done(&b""[..], String::from("((1 + ((2 * 3) / 4)) - 5)")));
|
||||
assert_eq!(expr(&b" 72 / 2 / 3 "[..]).map(|x| format!("{:?}", x)),
|
||||
IResult::Done(&b""[..], String::from("((72 / 2) / 3)")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parens_test() {
|
||||
assert_eq!(expr(&b" ( 1 + 2 ) * 3 "[..]).map(|x| format!("{:?}", x)),
|
||||
IResult::Done(&b""[..], String::from("([(1 + 2)] * 3)")));
|
||||
}
|
@ -1,321 +0,0 @@
|
||||
/*
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
extern crate bytes;
|
||||
|
||||
use nom::{Compare,CompareResult,InputLength,InputIter,Slice,HexDisplay};
|
||||
|
||||
use std::str;
|
||||
use std::str::FromStr;
|
||||
use bytes::{Buf,MutBuf};
|
||||
use bytes::buf::{BlockBuf,BlockBufCursor};
|
||||
use std::ops::{Range,RangeTo,RangeFrom,RangeFull};
|
||||
use std::iter::{Enumerate,Iterator};
|
||||
use std::fmt;
|
||||
use std::cmp::{min,PartialEq};
|
||||
|
||||
#[derive(Clone,Copy)]
|
||||
#[repr(C)]
|
||||
pub struct BlockSlice<'a> {
|
||||
buf: &'a BlockBuf,
|
||||
start: usize,
|
||||
end: usize,
|
||||
}
|
||||
|
||||
impl<'a> BlockSlice<'a> {
|
||||
fn cursor(&self) -> WrapCursor<'a> {
|
||||
let mut cur = self.buf.buf();
|
||||
cur.advance(self.start);
|
||||
WrapCursor {
|
||||
cursor: cur,
|
||||
length: self.end - self.start,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for BlockSlice<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "BlockSlice {{ start: {}, end: {}, data:\n{}\n}}", self.start, self.end, self.buf.bytes().unwrap_or(&b""[..]).to_hex(16))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq for BlockSlice<'a> {
|
||||
fn eq(&self, other: &BlockSlice<'a>) -> bool {
|
||||
let bufs = (self.buf as *const BlockBuf) == (other.buf as *const BlockBuf);
|
||||
self.start == other.start && self.end == other.end && bufs
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Slice<Range<usize>> for BlockSlice<'a> {
|
||||
fn slice(&self, range:Range<usize>) -> Self {
|
||||
BlockSlice {
|
||||
buf: self.buf,
|
||||
start: self.start + range.start,
|
||||
//FIXME: check for valid end here
|
||||
end: self.start + range.end,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Slice<RangeTo<usize>> for BlockSlice<'a> {
|
||||
fn slice(&self, range:RangeTo<usize>) -> Self {
|
||||
self.slice(0..range.end)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Slice<RangeFrom<usize>> for BlockSlice<'a> {
|
||||
fn slice(&self, range:RangeFrom<usize>) -> Self {
|
||||
self.slice(range.start..self.end - self.start)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Slice<RangeFull> for BlockSlice<'a> {
|
||||
fn slice(&self, _:RangeFull) -> Self {
|
||||
BlockSlice {
|
||||
buf: self.buf,
|
||||
start: self.start,
|
||||
end: self.end,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'a> InputIter for BlockSlice<'a> {
|
||||
type Item = u8;
|
||||
type RawItem = u8;
|
||||
type Iter = Enumerate<WrapCursor<'a>>;
|
||||
type IterElem = WrapCursor<'a>;
|
||||
|
||||
fn iter_indices(&self) -> Self::Iter {
|
||||
self.cursor().enumerate()
|
||||
}
|
||||
fn iter_elements(&self) -> Self::IterElem {
|
||||
self.cursor()
|
||||
}
|
||||
fn position<P>(&self, predicate: P) -> Option<usize> where P: Fn(Self::RawItem) -> bool {
|
||||
self.cursor().position(|b| predicate(b))
|
||||
}
|
||||
fn slice_index(&self, count:usize) -> Option<usize> {
|
||||
if self.end - self.start >= count {
|
||||
Some(count)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'a> InputLength for BlockSlice<'a> {
|
||||
fn input_len(&self) -> usize {
|
||||
self.end - self.start
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,'b> Compare<&'b[u8]> for BlockSlice<'a> {
|
||||
fn compare(&self, t: &'b[u8]) -> CompareResult {
|
||||
let len = self.end - self.start;
|
||||
let blen = t.len();
|
||||
let m = if len < blen { len } else { blen };
|
||||
let reduced = self.slice(..m);
|
||||
let b = &t[..m];
|
||||
|
||||
for (a,b) in reduced.cursor().zip(b.iter()) {
|
||||
if a != *b {
|
||||
return CompareResult::Error;
|
||||
}
|
||||
}
|
||||
if m < blen {
|
||||
CompareResult::Incomplete
|
||||
} else {
|
||||
CompareResult::Ok
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[inline(always)]
|
||||
fn compare_no_case(&self, t: &'b[u8]) -> CompareResult {
|
||||
let len = self.end - self.start;
|
||||
let blen = t.len();
|
||||
let m = if len < blen { len } else { blen };
|
||||
let reduced = self.slice(..m);
|
||||
let other = &t[..m];
|
||||
|
||||
if !reduced.cursor().zip(other).all(|(a, b)| {
|
||||
match (a,*b) {
|
||||
(0...64, 0...64) | (91...96, 91...96) | (123...255, 123...255) => a == *b,
|
||||
(65...90, 65...90) | (97...122, 97...122) | (65...90, 97...122 ) |(97...122, 65...90) => {
|
||||
a & 0b01000000 == *b & 0b01000000
|
||||
}
|
||||
_ => false
|
||||
}
|
||||
}) {
|
||||
CompareResult::Error
|
||||
} else if m < blen {
|
||||
CompareResult::Incomplete
|
||||
} else {
|
||||
CompareResult::Ok
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,'b> Compare<&'b str> for BlockSlice<'a> {
|
||||
fn compare(&self, t: &'b str) -> CompareResult {
|
||||
self.compare(str::as_bytes(t))
|
||||
}
|
||||
fn compare_no_case(&self, t: &'b str) -> CompareResult {
|
||||
self.compare_no_case(str::as_bytes(t))
|
||||
}
|
||||
}
|
||||
|
||||
//Wrapper to implement Iterator on BlockBufCursor
|
||||
pub struct WrapCursor<'a> {
|
||||
pub cursor: BlockBufCursor<'a>,
|
||||
pub length: usize,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for WrapCursor<'a> {
|
||||
type Item = u8;
|
||||
fn next(&mut self) -> Option<u8> {
|
||||
//println!("NEXT: length={}, remaining={}", self.length, self.cursor.remaining());
|
||||
if min(self.length, self.cursor.remaining()) > 0 {
|
||||
self.length -=1;
|
||||
Some(self.cursor.read_u8())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Reimplement eat_separator instead of fixing iterators
|
||||
#[macro_export]
|
||||
macro_rules! block_eat_separator (
|
||||
($i:expr, $arr:expr) => (
|
||||
{
|
||||
use nom::{InputLength,InputIter,Slice};
|
||||
if ($i).input_len() == 0 {
|
||||
nom::IResult::Done($i, ($i).slice(0..0))
|
||||
} else {
|
||||
match ($i).iter_indices().position(|(_, item)| {
|
||||
for (_,c) in ($arr).iter_indices() {
|
||||
if *c == item { return false; }
|
||||
}
|
||||
true
|
||||
}) {
|
||||
Some(index) => {
|
||||
nom::IResult::Done(($i).slice(index..), ($i).slice(..index))
|
||||
},
|
||||
None => {
|
||||
nom::IResult::Done(($i).slice(($i).input_len()..), $i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! block_named (
|
||||
($name:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name<'a>( i: BlockSlice<'a> ) -> nom::IResult<BlockSlice<'a>, BlockSlice<'a>, u32> {
|
||||
$submac!(i, $($args)*)
|
||||
}
|
||||
);
|
||||
($name:ident<$o:ty>, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name<'a>( i: BlockSlice<'a> ) -> nom::IResult<BlockSlice<'a>, $o, u32> {
|
||||
$submac!(i, $($args)*)
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
block_named!(sp, block_eat_separator!(&b" \t\r\n"[..]));
|
||||
|
||||
macro_rules! block_ws (
|
||||
($i:expr, $($args:tt)*) => (
|
||||
{
|
||||
sep!($i, sp, $($args)*)
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
block_named!(digit, is_a!("0123456789"));
|
||||
|
||||
block_named!(parens<i64>, block_ws!(delimited!( tag!("("), expr, tag!(")") )) );
|
||||
|
||||
|
||||
block_named!(factor<i64>, alt!(
|
||||
map_res!(
|
||||
block_ws!(digit),
|
||||
to_i64
|
||||
)
|
||||
| parens
|
||||
)
|
||||
);
|
||||
|
||||
block_named!(term <i64>, do_parse!(
|
||||
init: factor >>
|
||||
res: fold_many0!(
|
||||
pair!(alt!(tag!("*") | tag!("/")), factor),
|
||||
init,
|
||||
|acc, (op, val): (BlockSlice, i64)| {
|
||||
if (op.cursor().next().unwrap() as char) == '*' { acc * val } else { acc / val }
|
||||
}
|
||||
) >>
|
||||
(res)
|
||||
)
|
||||
);
|
||||
|
||||
block_named!(expr <i64>, do_parse!(
|
||||
init: term >>
|
||||
res: fold_many0!(
|
||||
pair!(alt!(tag!("+") | tag!("-")), term),
|
||||
init,
|
||||
|acc, (op, val): (BlockSlice, i64)| {
|
||||
if (op.cursor().next().unwrap() as char) == '+' { acc + val } else { acc - val }
|
||||
}
|
||||
) >>
|
||||
(res)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
fn blockbuf_from(input: &[u8]) -> BlockBuf {
|
||||
let mut b = BlockBuf::new(2, 100);
|
||||
b.copy_from(input);
|
||||
b
|
||||
}
|
||||
|
||||
|
||||
fn sl<'a>(input: &'a BlockBuf) -> BlockSlice<'a> {
|
||||
BlockSlice {
|
||||
buf: input,
|
||||
start: 0,
|
||||
end: input.len(),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_i64<'a>(input: BlockSlice<'a>) -> Result<i64, ()> {
|
||||
let v: Vec<u8> = input.cursor().collect();
|
||||
|
||||
match str::from_utf8(&v) {
|
||||
Err(_) => Err(()),
|
||||
Ok(s) => match FromStr::from_str(s) {
|
||||
Err(_) => Err(()),
|
||||
Ok(i) => Ok(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn factor_test() {
|
||||
let a = blockbuf_from(&b"3"[..]);
|
||||
println!("calculated: {:?}", factor(sl(&a)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parens_test() {
|
||||
let input1 = blockbuf_from(&b" 2* ( 3 + 4 ) "[..]);
|
||||
println!("calculated 1: {:?}", expr(sl(&input1)));
|
||||
let input2 = blockbuf_from(&b" 2*2 / ( 5 - 1) + 3"[..]);
|
||||
println!("calculated 2: {:?}", expr(sl(&input2)));
|
||||
}
|
||||
*/
|
@ -1,147 +0,0 @@
|
||||
/// this file tests a different backtracking behaviour. With the current
|
||||
/// `error!` macro, an early return is done in the current function, but
|
||||
/// backtracking continues normally outside of that function.
|
||||
///
|
||||
/// The solution here wraps `IResult` in a `Result`: a `Ok` indicates usual
|
||||
/// backtracking, `Err` indicates that we must "cut".
|
||||
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use] extern crate nom;
|
||||
|
||||
#[cfg(feature = "verbose_errors")]
|
||||
macro_rules! n (
|
||||
($name:ident( $i:ty ) -> $o:ty, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( i: $i ) -> std::result::Result<nom::IResult<$i,$o,u32>, nom::Err<$i, u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
($name:ident<$i:ty,$o:ty,$e:ty>, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( i: $i ) -> std::result::Result<nom::IResult<$i, $o, $e>, nom::Err<$i, $e>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
($name:ident<$i:ty,$o:ty>, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( i: $i ) -> std::result::Result<nom::IResult<$i, $o, u32>, nom::Err<$i, u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
($name:ident<$o:ty>, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name<'a>( i: &'a[u8] ) -> std::result::Result<nom::IResult<&'a [u8], $o, u32>, nom::Err<&'a [u8], u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
($name:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
fn $name( i: &[u8] ) -> std::result::Result<nom::IResult<&[u8], &[u8], u32>, nom::Err<&[u8], u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
(pub $name:ident( $i:ty ) -> $o:ty, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( i: $i ) -> std::result::Result<nom::IResult<$i,$o, u32>, nom::Err<$i, u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$i:ty,$o:ty,$e:ty>, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( i: $i ) -> std::result::Result<nom::IResult<$i, $o, $e>, nom::Err<$i, $e>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$i:ty,$o:ty>, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( i: $i ) -> std::result::Result<nom::IResult<$i, $o, u32>, nom::Err<$i, u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
(pub $name:ident<$o:ty>, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name( i: &[u8] ) -> std::result::Result<nom::IResult<&[u8], $o, u32>, nom::Err<&[u8], u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
(pub $name:ident, $submac:ident!( $($args:tt)* )) => (
|
||||
pub fn $name<'a>( i: &'a [u8] ) -> std::result::Result<nom::IResult<&[u8], &[u8], u32>, nom::Err<&[u8], u32>> {
|
||||
std::result::Result::Ok($submac!(i, $($args)*))
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
#[cfg(feature = "verbose_errors")]
|
||||
macro_rules! cut (
|
||||
($i:expr, $code:expr, $submac:ident!( $($args:tt)* )) => (
|
||||
{
|
||||
let cl = || {
|
||||
Ok($submac!($i, $($args)*))
|
||||
};
|
||||
|
||||
match cl() {
|
||||
std::result::Result::Ok(nom::IResult::Incomplete(x)) => nom::IResult::Incomplete(x),
|
||||
std::result::Result::Ok(nom::IResult::Done(i, o)) => nom::IResult::Done(i, o),
|
||||
std::result::Result::Ok(nom::IResult::Error(e)) | std::result::Result::Err(e) => {
|
||||
return std::result::Result::Err(nom::Err::NodePosition($code, $i, Box::new(e)))
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
($i:expr, $code:expr, $f:expr) => (
|
||||
cut!($i, $code, call!($f));
|
||||
);
|
||||
);
|
||||
|
||||
#[cfg(feature = "verbose_errors")]
|
||||
macro_rules! c (
|
||||
($i:expr, $f:expr) => (
|
||||
{
|
||||
match $f($i) {
|
||||
std::result::Result::Ok(nom::IResult::Incomplete(x)) => nom::IResult::Incomplete(x),
|
||||
std::result::Result::Ok(nom::IResult::Done(i, o)) => nom::IResult::Done(i, o),
|
||||
std::result::Result::Ok(nom::IResult::Error(e)) => nom::IResult::Error(e),
|
||||
std::result::Result::Err(e) => {
|
||||
return std::result::Result::Err(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
#[cfg(feature = "verbose_errors")]
|
||||
n!(pub foo< bool >,
|
||||
do_parse!(
|
||||
tag!("a") >>
|
||||
cut!(nom::ErrorKind::Custom(42),dbg_dmp!(tag!("b"))) >>
|
||||
(true)
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "verbose_errors")]
|
||||
n!(pub foos< Vec<bool> >,
|
||||
delimited!(
|
||||
tag!("("),
|
||||
many0!(c!(foo)),
|
||||
tag!(")")
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(feature = "verbose_errors")]
|
||||
#[test]
|
||||
fn test_ok() {
|
||||
let r = foos(b"(abab)");
|
||||
println!("result: {:?}", r);
|
||||
match r {
|
||||
Ok(nom::IResult::Done(_,result)) => assert_eq!(result,vec![true,true]),
|
||||
res => panic!("Oops {:?}.",res)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "verbose_errors")]
|
||||
#[test]
|
||||
fn test_err() {
|
||||
let input = b"(ac)";
|
||||
let r = foos(&input[..]);
|
||||
println!("result: {:?}", r);
|
||||
match r {
|
||||
//Ok(nom::IResult::Error(nom::Err::Position(kind,_))) => assert_eq!(kind,nom::ErrorKind::Custom(42)),
|
||||
Err(nom::Err::NodePosition(kind, position, _)) => {
|
||||
assert_eq!(kind, nom::ErrorKind::Custom(42));
|
||||
assert_eq!(position, &input[2..]);
|
||||
}
|
||||
res => panic!("Oops, {:?}",res)
|
||||
}
|
||||
}
|
||||
|
46
third_party/rust/nom-3.2.1/tests/float.rs
vendored
46
third_party/rust/nom-3.2.1/tests/float.rs
vendored
@ -1,46 +0,0 @@
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::{IResult,digit};
|
||||
|
||||
use std::str;
|
||||
use std::str::FromStr;
|
||||
|
||||
named!(unsigned_float <f32>, map_res!(
|
||||
map_res!(
|
||||
recognize!(
|
||||
alt!(
|
||||
delimited!(digit, tag!("."), opt!(complete!(digit))) |
|
||||
delimited!(opt!(digit), tag!("."), digit)
|
||||
)
|
||||
),
|
||||
str::from_utf8
|
||||
),
|
||||
FromStr::from_str
|
||||
));
|
||||
|
||||
named!(float <f32>, map!(
|
||||
pair!(
|
||||
opt!(alt!(tag!("+") | tag!("-"))),
|
||||
unsigned_float
|
||||
),
|
||||
|(sign, value): (Option<&[u8]>, f32)| {
|
||||
sign.and_then(|s| if s[0] == ('-' as u8) { Some(-1f32) } else { None }).unwrap_or(1f32) * value
|
||||
}
|
||||
));
|
||||
|
||||
#[test]
|
||||
fn unsigned_float_test() {
|
||||
assert_eq!(unsigned_float(&b"123.456"[..]), IResult::Done(&b""[..], 123.456));
|
||||
assert_eq!(unsigned_float(&b"0.123"[..]), IResult::Done(&b""[..], 0.123));
|
||||
assert_eq!(unsigned_float(&b"123.0"[..]), IResult::Done(&b""[..], 123.0));
|
||||
assert_eq!(unsigned_float(&b"123."[..]), IResult::Done(&b""[..], 123.0));
|
||||
assert_eq!(unsigned_float(&b".123"[..]), IResult::Done(&b""[..], 0.123));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn float_test() {
|
||||
assert_eq!(float(&b"123.456"[..]), IResult::Done(&b""[..], 123.456));
|
||||
assert_eq!(float(&b"+123.456"[..]), IResult::Done(&b""[..], 123.456));
|
||||
assert_eq!(float(&b"-123.456"[..]), IResult::Done(&b""[..], -123.456));
|
||||
}
|
219
third_party/rust/nom-3.2.1/tests/ini.rs
vendored
219
third_party/rust/nom-3.2.1/tests/ini.rs
vendored
@ -1,219 +0,0 @@
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::{IResult, space, alphanumeric, multispace};
|
||||
|
||||
use std::str;
|
||||
use std::collections::HashMap;
|
||||
|
||||
named!(category<&str>, map_res!(
|
||||
delimited!(
|
||||
char!('['),
|
||||
take_while!(call!(|c| c != ']' as u8)),
|
||||
char!(']')
|
||||
),
|
||||
str::from_utf8
|
||||
));
|
||||
|
||||
named!(key_value <&[u8],(&str,&str)>,
|
||||
do_parse!(
|
||||
key: map_res!(alphanumeric, str::from_utf8)
|
||||
>> opt!(space)
|
||||
>> char!('=')
|
||||
>> opt!(space)
|
||||
>> val: map_res!(
|
||||
take_while!(call!(|c| c != '\n' as u8 && c != ';' as u8)),
|
||||
str::from_utf8
|
||||
)
|
||||
>> opt!(pair!(char!(';'), take_while!(call!(|c| c != '\n' as u8))))
|
||||
>> (key, val)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
named!(keys_and_values<&[u8], HashMap<&str, &str> >,
|
||||
map!(
|
||||
many0!(terminated!(key_value, opt!(multispace))),
|
||||
|vec: Vec<_>| vec.into_iter().collect()
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
named!(category_and_keys<&[u8],(&str,HashMap<&str,&str>)>,
|
||||
do_parse!(
|
||||
category: category >>
|
||||
opt!(multispace) >>
|
||||
keys: keys_and_values >>
|
||||
(category, keys)
|
||||
)
|
||||
);
|
||||
|
||||
named!(categories<&[u8], HashMap<&str, HashMap<&str,&str> > >,
|
||||
map!(
|
||||
many0!(
|
||||
separated_pair!(
|
||||
category,
|
||||
opt!(multispace),
|
||||
map!(
|
||||
many0!(terminated!(key_value, opt!(multispace))),
|
||||
|vec: Vec<_>| vec.into_iter().collect()
|
||||
)
|
||||
)
|
||||
),
|
||||
|vec: Vec<_>| vec.into_iter().collect()
|
||||
)
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn parse_category_test() {
|
||||
let ini_file = &b"[category]
|
||||
|
||||
parameter=value
|
||||
key = value2"[..];
|
||||
|
||||
let ini_without_category = &b"\n\nparameter=value
|
||||
key = value2"[..];
|
||||
|
||||
let res = category(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_category, "category"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_key_value_test() {
|
||||
let ini_file = &b"parameter=value
|
||||
key = value2"[..];
|
||||
|
||||
let ini_without_key_value = &b"\nkey = value2"[..];
|
||||
|
||||
let res = key_value(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn parse_key_value_with_space_test() {
|
||||
let ini_file = &b"parameter = value
|
||||
key = value2"[..];
|
||||
|
||||
let ini_without_key_value = &b"\nkey = value2"[..];
|
||||
|
||||
let res = key_value(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_key_value_with_comment_test() {
|
||||
let ini_file = &b"parameter=value;abc
|
||||
key = value2"[..];
|
||||
|
||||
let ini_without_key_value = &b"\nkey = value2"[..];
|
||||
|
||||
let res = key_value(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, (o1, o2)) => println!("i: {:?} | o: ({:?},{:?})", str::from_utf8(i), o1, o2),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_multiple_keys_and_values_test() {
|
||||
let ini_file = &b"parameter=value;abc
|
||||
|
||||
key = value2
|
||||
|
||||
[category]"[..];
|
||||
|
||||
let ini_without_key_value = &b"[category]"[..];
|
||||
|
||||
let res = keys_and_values(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
let mut expected: HashMap<&str, &str> = HashMap::new();
|
||||
expected.insert("parameter", "value");
|
||||
expected.insert("key", "value2");
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, expected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_category_then_multiple_keys_and_values_test() {
|
||||
//FIXME: there can be an empty line or a comment line after a category
|
||||
let ini_file = &b"[abcd]
|
||||
parameter=value;abc
|
||||
|
||||
key = value2
|
||||
|
||||
[category]"[..];
|
||||
|
||||
let ini_after_parser = &b"[category]"[..];
|
||||
|
||||
let res = category_and_keys(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
let mut expected_h: HashMap<&str, &str> = HashMap::new();
|
||||
expected_h.insert("parameter", "value");
|
||||
expected_h.insert("key", "value2");
|
||||
assert_eq!(res, IResult::Done(ini_after_parser, ("abcd", expected_h)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_multiple_categories_test() {
|
||||
let ini_file = &b"[abcd]
|
||||
|
||||
parameter=value;abc
|
||||
|
||||
key = value2
|
||||
|
||||
[category]
|
||||
parameter3=value3
|
||||
key4 = value4
|
||||
"[..];
|
||||
|
||||
let ini_after_parser = &b""[..];
|
||||
|
||||
let res = categories(ini_file);
|
||||
//println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, ref o) => println!("i: {:?} | o: {:?}", str::from_utf8(i), o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
let mut expected_1: HashMap<&str, &str> = HashMap::new();
|
||||
expected_1.insert("parameter", "value");
|
||||
expected_1.insert("key", "value2");
|
||||
let mut expected_2: HashMap<&str, &str> = HashMap::new();
|
||||
expected_2.insert("parameter3", "value3");
|
||||
expected_2.insert("key4", "value4");
|
||||
let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
|
||||
expected_h.insert("abcd", expected_1);
|
||||
expected_h.insert("category", expected_2);
|
||||
assert_eq!(res, IResult::Done(ini_after_parser, expected_h));
|
||||
}
|
240
third_party/rust/nom-3.2.1/tests/ini_str.rs
vendored
240
third_party/rust/nom-3.2.1/tests/ini_str.rs
vendored
@ -1,240 +0,0 @@
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::IResult;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
fn is_alphabetic(chr:char) -> bool {
|
||||
(chr as u8 >= 0x41 && chr as u8 <= 0x5A) || (chr as u8 >= 0x61 && chr as u8 <= 0x7A)
|
||||
}
|
||||
|
||||
fn is_digit(chr: char) -> bool {
|
||||
chr as u8 >= 0x30 && chr as u8 <= 0x39
|
||||
}
|
||||
|
||||
fn is_alphanumeric(chr: char) -> bool {
|
||||
is_alphabetic(chr) || is_digit(chr)
|
||||
}
|
||||
|
||||
fn is_space(chr:char) -> bool {
|
||||
chr == ' ' || chr == '\t'
|
||||
}
|
||||
|
||||
fn is_line_ending_or_comment(chr:char) -> bool {
|
||||
chr == ';' || chr == '\n'
|
||||
}
|
||||
|
||||
named!(alphanumeric<&str,&str>, take_while_s!(is_alphanumeric));
|
||||
named!(not_line_ending<&str,&str>, is_not_s!("\r\n"));
|
||||
named!(space<&str,&str>, take_while_s!(is_space));
|
||||
named!(space_or_line_ending<&str,&str>, is_a_s!(" \r\n"));
|
||||
|
||||
fn right_bracket(c:char) -> bool {
|
||||
c == ']'
|
||||
}
|
||||
|
||||
named!(category <&str, &str>,
|
||||
do_parse!(
|
||||
tag_s!("[") >>
|
||||
name: take_till_s!(right_bracket) >>
|
||||
tag_s!("]") >>
|
||||
opt!(space_or_line_ending) >>
|
||||
(name)
|
||||
)
|
||||
);
|
||||
|
||||
named!(key_value <&str,(&str,&str)>,
|
||||
do_parse!(
|
||||
key: alphanumeric >>
|
||||
opt!(space) >>
|
||||
tag_s!("=") >>
|
||||
opt!(space) >>
|
||||
val: take_till_s!(is_line_ending_or_comment) >>
|
||||
opt!(space) >>
|
||||
opt!(pair!(tag_s!(";"), not_line_ending)) >>
|
||||
opt!(space_or_line_ending) >>
|
||||
(key, val)
|
||||
)
|
||||
);
|
||||
|
||||
named!(keys_and_values_aggregator<&str, Vec<(&str,&str)> >, many0!(key_value));
|
||||
|
||||
fn keys_and_values(input:&str) -> IResult<&str, HashMap<&str, &str> > {
|
||||
match keys_and_values_aggregator(input) {
|
||||
IResult::Done(i,tuple_vec) => {
|
||||
IResult::Done(i, tuple_vec.into_iter().collect())
|
||||
},
|
||||
IResult::Incomplete(a) => IResult::Incomplete(a),
|
||||
IResult::Error(a) => IResult::Error(a)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
named!(category_and_keys<&str,(&str,HashMap<&str,&str>)>,
|
||||
pair!(category, keys_and_values)
|
||||
);
|
||||
|
||||
named!(categories_aggregator<&str, Vec<(&str, HashMap<&str,&str>)> >, many0!(category_and_keys));
|
||||
|
||||
fn categories(input: &str) -> IResult<&str, HashMap<&str, HashMap<&str, &str> > > {
|
||||
match categories_aggregator(input) {
|
||||
IResult::Done(i,tuple_vec) => {
|
||||
IResult::Done(i, tuple_vec.into_iter().collect())
|
||||
},
|
||||
IResult::Incomplete(a) => IResult::Incomplete(a),
|
||||
IResult::Error(a) => IResult::Error(a)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn parse_category_test() {
|
||||
let ini_file = "[category]
|
||||
|
||||
parameter=value
|
||||
key = value2";
|
||||
|
||||
let ini_without_category = "parameter=value
|
||||
key = value2";
|
||||
|
||||
let res = category(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, o) => println!("i: {} | o: {:?}", i, o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_category, "category"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_key_value_test() {
|
||||
let ini_file = "parameter=value
|
||||
key = value2";
|
||||
|
||||
let ini_without_key_value = "key = value2";
|
||||
|
||||
let res = key_value(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_key_value_with_space_test() {
|
||||
let ini_file = "parameter = value
|
||||
key = value2";
|
||||
|
||||
let ini_without_key_value = "key = value2";
|
||||
|
||||
let res = key_value(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_key_value_with_comment_test() {
|
||||
let ini_file = "parameter=value;abc
|
||||
key = value2";
|
||||
|
||||
let ini_without_key_value = "key = value2";
|
||||
|
||||
let res = key_value(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_multiple_keys_and_values_test() {
|
||||
let ini_file = "parameter=value;abc
|
||||
|
||||
key = value2
|
||||
|
||||
[category]";
|
||||
|
||||
let ini_without_key_value = "[category]";
|
||||
|
||||
let res = keys_and_values(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, ref o) => println!("i: {} | o: {:?}", i, o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
let mut expected: HashMap<&str, &str> = HashMap::new();
|
||||
expected.insert("parameter", "value");
|
||||
expected.insert("key", "value2");
|
||||
assert_eq!(res, IResult::Done(ini_without_key_value, expected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_category_then_multiple_keys_and_values_test() {
|
||||
//FIXME: there can be an empty line or a comment line after a category
|
||||
let ini_file = "[abcd]
|
||||
parameter=value;abc
|
||||
|
||||
key = value2
|
||||
|
||||
[category]";
|
||||
|
||||
let ini_after_parser = "[category]";
|
||||
|
||||
let res = category_and_keys(ini_file);
|
||||
println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, ref o) => println!("i: {} | o: {:?}", i, o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
let mut expected_h: HashMap<&str, &str> = HashMap::new();
|
||||
expected_h.insert("parameter", "value");
|
||||
expected_h.insert("key", "value2");
|
||||
assert_eq!(res, IResult::Done(ini_after_parser, ("abcd", expected_h)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_multiple_categories_test() {
|
||||
let ini_file = "[abcd]
|
||||
|
||||
parameter=value;abc
|
||||
|
||||
key = value2
|
||||
|
||||
[category]
|
||||
parameter3=value3
|
||||
key4 = value4
|
||||
";
|
||||
|
||||
let res = categories(ini_file);
|
||||
//println!("{:?}", res);
|
||||
match res {
|
||||
IResult::Done(i, ref o) => println!("i: {} | o: {:?}", i, o),
|
||||
_ => println!("error")
|
||||
}
|
||||
|
||||
let mut expected_1: HashMap<&str, &str> = HashMap::new();
|
||||
expected_1.insert("parameter", "value");
|
||||
expected_1.insert("key", "value2");
|
||||
let mut expected_2: HashMap<&str, &str> = HashMap::new();
|
||||
expected_2.insert("parameter3", "value3");
|
||||
expected_2.insert("key4", "value4");
|
||||
let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
|
||||
expected_h.insert("abcd", expected_1);
|
||||
expected_h.insert("category", expected_2);
|
||||
assert_eq!(res, IResult::Done("", expected_h));
|
||||
}
|
157
third_party/rust/nom-3.2.1/tests/issues.rs
vendored
157
third_party/rust/nom-3.2.1/tests/issues.rs
vendored
@ -1,157 +0,0 @@
|
||||
//#![feature(trace_macros)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::{IResult,Needed,space,be_u16,le_u64};
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Range {
|
||||
start: char,
|
||||
end: char
|
||||
}
|
||||
|
||||
pub fn take_char(input: &[u8]) -> IResult<&[u8], char> {
|
||||
if input.len() > 0 {
|
||||
IResult::Done(&input[1..], input[0] as char)
|
||||
} else {
|
||||
IResult::Incomplete(Needed::Size(1))
|
||||
}
|
||||
}
|
||||
|
||||
//trace_macros!(true);
|
||||
|
||||
#[allow(dead_code)]
|
||||
named!(range<&[u8], Range>,
|
||||
alt!(
|
||||
do_parse!(
|
||||
start: take_char >>
|
||||
tag!("-") >>
|
||||
end: take_char >>
|
||||
(Range {
|
||||
start: start,
|
||||
end: end,
|
||||
})
|
||||
) |
|
||||
map!(
|
||||
take_char,
|
||||
|c| {
|
||||
Range {
|
||||
start: c,
|
||||
end: c,
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
#[allow(dead_code)]
|
||||
named!(literal<&[u8], Vec<char> >,
|
||||
map!(
|
||||
many1!(take_char),
|
||||
|cs| {
|
||||
cs
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn issue_58() {
|
||||
range(&b"abcd"[..]);
|
||||
literal(&b"abcd"[..]);
|
||||
}
|
||||
|
||||
//trace_macros!(false);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
mod parse_int {
|
||||
use nom::HexDisplay;
|
||||
use nom::{IResult,space,digit};
|
||||
use std::str;
|
||||
|
||||
named!(parse_ints< Vec<i32> >, many0!(spaces_or_int));
|
||||
|
||||
fn spaces_or_int(input: &[u8]) -> IResult<&[u8], i32>{
|
||||
println!("{}", input.to_hex(8));
|
||||
do_parse!(input,
|
||||
opt!(complete!(space)) >>
|
||||
res: map!(complete!(digit),
|
||||
|x| {
|
||||
println!("x: {:?}", x);
|
||||
let result = str::from_utf8(x).unwrap();
|
||||
println!("Result: {}", result);
|
||||
println!("int is empty?: {}", x.is_empty());
|
||||
match result.parse(){
|
||||
Ok(i) => i,
|
||||
Err(_) => panic!("UH OH! NOT A DIGIT!")
|
||||
}
|
||||
}) >>
|
||||
(res)
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_142(){
|
||||
let subject = parse_ints(&b"12 34 5689"[..]);
|
||||
let expected = IResult::Done(&b""[..], vec![12, 34, 5689]);
|
||||
assert_eq!(subject, expected);
|
||||
|
||||
let subject = parse_ints(&b"12 34 5689 "[..]);
|
||||
let expected = IResult::Done(&b" "[..], vec![12, 34, 5689]);
|
||||
assert_eq!(subject, expected)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn usize_length_bytes_issue(){
|
||||
length_bytes!(b"012346", be_u16);
|
||||
}
|
||||
|
||||
/*
|
||||
DOES NOT COMPILE
|
||||
#[test]
|
||||
fn issue_152() {
|
||||
named!(take4, take!(4));
|
||||
named!(xyz, tag!("XYZ"));
|
||||
named!(abc, tag!("abc"));
|
||||
|
||||
|
||||
named!(sw,
|
||||
switch!(take4,
|
||||
b"abcd" => xyz |
|
||||
b"efgh" => abc
|
||||
)
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
||||
#[test]
|
||||
fn take_till_issue() {
|
||||
named!(nothing,
|
||||
take_till!(call!(|_| true))
|
||||
);
|
||||
|
||||
assert_eq!(nothing(b""), IResult::Done(&b""[..], &b""[..]));
|
||||
assert_eq!(nothing(b"abc"), IResult::Done(&b"abc"[..], &b""[..]));
|
||||
}
|
||||
|
||||
named!(issue_498< Vec<&[u8]> >, separated_nonempty_list!( opt!(space), tag!("abcd") ));
|
||||
|
||||
named!(issue_308(&str) -> bool,
|
||||
do_parse! (
|
||||
tag_s! ("foo") >>
|
||||
b: alt_complete! (
|
||||
map! (tag_s! ("1"), |_: &str|->bool {true}) |
|
||||
value! (false)
|
||||
) >>
|
||||
(b) ));
|
||||
|
||||
|
||||
fn issue_302(input: &[u8]) -> IResult<&[u8], Option<Vec<u64>> > {
|
||||
do_parse!(input,
|
||||
entries: cond!(true, count!(le_u64, 3)) >>
|
||||
( entries )
|
||||
)
|
||||
}
|
130
third_party/rust/nom-3.2.1/tests/json.rs
vendored
130
third_party/rust/nom-3.2.1/tests/json.rs
vendored
@ -1,130 +0,0 @@
|
||||
//#![feature(trace_macros)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::{digit, alphanumeric};
|
||||
|
||||
use std::str::{self,FromStr};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug,PartialEq)]
|
||||
pub enum JsonValue {
|
||||
Str(String),
|
||||
Num(f32),
|
||||
Array(Vec<JsonValue>),
|
||||
Object(HashMap<String,JsonValue>)
|
||||
}
|
||||
|
||||
// FIXME: since we already parsed a serie of digits and dots,
|
||||
// we know it is correct UTF-8. no need to use from_utf8 to
|
||||
// verify it is correct
|
||||
// FIXME: use alt_complete (implement ws for alt_complete)
|
||||
named!(unsigned_float <f32>, map_res!(
|
||||
map_res!(
|
||||
recognize!(
|
||||
alt_complete!(
|
||||
delimited!(digit, tag!("."), opt!(complete!(digit))) |
|
||||
delimited!(opt!(digit), tag!("."), digit) |
|
||||
digit
|
||||
)
|
||||
),
|
||||
str::from_utf8
|
||||
),
|
||||
FromStr::from_str
|
||||
));
|
||||
named!(float<f32>, map!(
|
||||
pair!(
|
||||
opt!(alt!(tag!("+") | tag!("-"))),
|
||||
unsigned_float
|
||||
),
|
||||
|(sign, value): (Option<&[u8]>, f32)| {
|
||||
sign.and_then(|s| if s[0] == ('-' as u8) { Some(-1f32) } else { None }).unwrap_or(1f32) * value
|
||||
}
|
||||
));
|
||||
|
||||
//FIXME: verify how json strings are formatted
|
||||
named!(string<&str>,
|
||||
delimited!(
|
||||
tag!("\""),
|
||||
map_res!(escaped!(call!(alphanumeric), '\\', is_a!("\"n\\")), str::from_utf8),
|
||||
tag!("\"")
|
||||
)
|
||||
);
|
||||
|
||||
named!(array < Vec<JsonValue> >,
|
||||
ws!(
|
||||
delimited!(
|
||||
tag!("["),
|
||||
separated_list!(tag!(","), value),
|
||||
tag!("]")
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
named!(key_value<(&str,JsonValue)>,
|
||||
ws!(
|
||||
separated_pair!(
|
||||
string,
|
||||
tag!(":"),
|
||||
value
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
named!(hash< HashMap<String,JsonValue> >,
|
||||
ws!(
|
||||
map!(
|
||||
delimited!(
|
||||
tag!("{"),
|
||||
separated_list!(tag!(","), key_value),
|
||||
tag!("}")
|
||||
),
|
||||
|tuple_vec| {
|
||||
let mut h: HashMap<String, JsonValue> = HashMap::new();
|
||||
for (k, v) in tuple_vec {
|
||||
h.insert(String::from(k), v);
|
||||
}
|
||||
h
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
named!(value<JsonValue>,
|
||||
ws!(
|
||||
alt!(
|
||||
hash => { |h| JsonValue::Object(h) } |
|
||||
array => { |v| JsonValue::Array(v) } |
|
||||
string => { |s| JsonValue::Str(String::from(s)) } |
|
||||
float => { |num| JsonValue::Num(num) }
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
fn hash_test() {
|
||||
let test = &b" { \"a\"\t: 42,
|
||||
\"b\": \"x\"
|
||||
}";
|
||||
|
||||
//FIXME: top level value must be an object?
|
||||
println!("{:?}", value(&test[..]));
|
||||
//assert!(false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_example_test() {
|
||||
let test = &b" { \"a\"\t: 42,
|
||||
\"b\": [ \"x\", \"y\", 12 ] ,
|
||||
\"c\": { \"hello\" : \"world\"
|
||||
}
|
||||
}";
|
||||
|
||||
//FIXME: top level value must be an object?
|
||||
println!("{:?}", value(&test[..]));
|
||||
//assert!(false);
|
||||
}
|
||||
|
528
third_party/rust/nom-3.2.1/tests/mp4.rs
vendored
528
third_party/rust/nom-3.2.1/tests/mp4.rs
vendored
@ -1,528 +0,0 @@
|
||||
#![cfg(feature = "stream")]
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::{HexDisplay,Offset,Needed,IResult,be_u16,be_u32,be_u64,be_f32,ErrorKind};
|
||||
use nom::{Consumer,ConsumerState,Move,Input,Producer,FileProducer,FileProducerState};
|
||||
use nom::IResult::*;
|
||||
|
||||
use std::str;
|
||||
use std::io::SeekFrom;
|
||||
|
||||
fn mp4_box(input:&[u8]) -> IResult<&[u8], &[u8]> {
|
||||
match be_u32(input) {
|
||||
Done(i, offset) => {
|
||||
let sz: usize = offset as usize;
|
||||
if i.len() >= sz - 4 {
|
||||
Done(&i[(sz-4)..], &i[0..(sz-4)])
|
||||
} else {
|
||||
Incomplete(Needed::Size(offset as usize + 4))
|
||||
}
|
||||
}
|
||||
Error(e) => Error(e),
|
||||
Incomplete(e) => Incomplete(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq,Eq,Debug)]
|
||||
struct FileType<'a> {
|
||||
major_brand: &'a str,
|
||||
major_brand_version: &'a [u8],
|
||||
compatible_brands: Vec<&'a str>
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct Mvhd32 {
|
||||
version_flags: u32, // actually:
|
||||
// version: u8,
|
||||
// flags: u24 // 3 bytes
|
||||
created_date: u32,
|
||||
modified_date: u32,
|
||||
scale: u32,
|
||||
duration: u32,
|
||||
speed: f32,
|
||||
volume: u16, // actually a 2 bytes decimal
|
||||
/* 10 bytes reserved */
|
||||
scaleA: f32,
|
||||
rotateB: f32,
|
||||
angleU: f32,
|
||||
rotateC: f32,
|
||||
scaleD: f32,
|
||||
angleV: f32,
|
||||
positionX: f32,
|
||||
positionY: f32,
|
||||
scaleW: f32,
|
||||
preview: u64,
|
||||
poster: u32,
|
||||
selection: u64,
|
||||
current_time: u32,
|
||||
track_id: u32
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct Mvhd64 {
|
||||
version_flags: u32, // actually:
|
||||
// version: u8,
|
||||
// flags: u24 // 3 bytes
|
||||
created_date: u64,
|
||||
modified_date: u64,
|
||||
scale: u32,
|
||||
duration: u64,
|
||||
speed: f32,
|
||||
volume: u16, // actually a 2 bytes decimal
|
||||
/* 10 bytes reserved */
|
||||
scaleA: f32,
|
||||
rotateB: f32,
|
||||
angleU: f32,
|
||||
rotateC: f32,
|
||||
scaleD: f32,
|
||||
angleV: f32,
|
||||
positionX: f32,
|
||||
positionY: f32,
|
||||
scaleW: f32,
|
||||
preview: u64,
|
||||
poster: u32,
|
||||
selection: u64,
|
||||
current_time: u32,
|
||||
track_id: u32
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
named!(mvhd32 <&[u8], MvhdBox>,
|
||||
do_parse!(
|
||||
version_flags: be_u32 >>
|
||||
created_date: be_u32 >>
|
||||
modified_date: be_u32 >>
|
||||
scale: be_u32 >>
|
||||
duration: be_u32 >>
|
||||
speed: be_f32 >>
|
||||
volume: be_u16 >> // actually a 2 bytes decimal
|
||||
take!(10) >>
|
||||
scale_a: be_f32 >>
|
||||
rotate_b: be_f32 >>
|
||||
angle_u: be_f32 >>
|
||||
rotate_c: be_f32 >>
|
||||
scale_d: be_f32 >>
|
||||
angle_v: be_f32 >>
|
||||
position_x: be_f32 >>
|
||||
position_y: be_f32 >>
|
||||
scale_w: be_f32 >>
|
||||
preview: be_u64 >>
|
||||
poster: be_u32 >>
|
||||
selection: be_u64 >>
|
||||
current_time: be_u32 >>
|
||||
track_id: be_u32 >>
|
||||
(
|
||||
MvhdBox::M32(Mvhd32 {
|
||||
version_flags: version_flags,
|
||||
created_date: created_date,
|
||||
modified_date: modified_date,
|
||||
scale: scale,
|
||||
duration: duration,
|
||||
speed: speed,
|
||||
volume: volume,
|
||||
scaleA: scale_a,
|
||||
rotateB: rotate_b,
|
||||
angleU: angle_u,
|
||||
rotateC: rotate_c,
|
||||
scaleD: scale_d,
|
||||
angleV: angle_v,
|
||||
positionX: position_x,
|
||||
positionY: position_y,
|
||||
scaleW: scale_w,
|
||||
preview: preview,
|
||||
poster: poster,
|
||||
selection: selection,
|
||||
current_time: current_time,
|
||||
track_id: track_id
|
||||
})
|
||||
))
|
||||
);
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
named!(mvhd64 <&[u8], MvhdBox>,
|
||||
do_parse!(
|
||||
version_flags: be_u32 >>
|
||||
created_date: be_u64 >>
|
||||
modified_date: be_u64 >>
|
||||
scale: be_u32 >>
|
||||
duration: be_u64 >>
|
||||
speed: be_f32 >>
|
||||
volume: be_u16 >> // actually a 2 bytes decimal
|
||||
take!(10) >>
|
||||
scale_a: be_f32 >>
|
||||
rotate_b: be_f32 >>
|
||||
angle_u: be_f32 >>
|
||||
rotate_c: be_f32 >>
|
||||
scale_d: be_f32 >>
|
||||
angle_v: be_f32 >>
|
||||
position_x: be_f32 >>
|
||||
position_y: be_f32 >>
|
||||
scale_w: be_f32 >>
|
||||
preview: be_u64 >>
|
||||
poster: be_u32 >>
|
||||
selection: be_u64 >>
|
||||
current_time: be_u32 >>
|
||||
track_id: be_u32 >>
|
||||
(
|
||||
MvhdBox::M64(Mvhd64 {
|
||||
version_flags: version_flags,
|
||||
created_date: created_date,
|
||||
modified_date: modified_date,
|
||||
scale: scale,
|
||||
duration: duration,
|
||||
speed: speed,
|
||||
volume: volume,
|
||||
scaleA: scale_a,
|
||||
rotateB: rotate_b,
|
||||
angleU: angle_u,
|
||||
rotateC: rotate_c,
|
||||
scaleD: scale_d,
|
||||
angleV: angle_v,
|
||||
positionX: position_x,
|
||||
positionY: position_y,
|
||||
scaleW: scale_w,
|
||||
preview: preview,
|
||||
poster: poster,
|
||||
selection: selection,
|
||||
current_time: current_time,
|
||||
track_id: track_id
|
||||
})
|
||||
))
|
||||
);
|
||||
|
||||
#[derive(Debug,Clone)]
|
||||
pub enum MvhdBox {
|
||||
M32(Mvhd32),
|
||||
M64(Mvhd64)
|
||||
}
|
||||
|
||||
#[derive(Debug,Clone)]
|
||||
pub enum MoovBox {
|
||||
Mdra,
|
||||
Dref,
|
||||
Cmov,
|
||||
Rmra,
|
||||
Iods,
|
||||
Mvhd(MvhdBox),
|
||||
Clip,
|
||||
Trak,
|
||||
Udta
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum MP4BoxType {
|
||||
Ftyp,
|
||||
Moov,
|
||||
Mdat,
|
||||
Free,
|
||||
Skip,
|
||||
Wide,
|
||||
Mdra,
|
||||
Dref,
|
||||
Cmov,
|
||||
Rmra,
|
||||
Iods,
|
||||
Mvhd,
|
||||
Clip,
|
||||
Trak,
|
||||
Udta,
|
||||
Unknown
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MP4BoxHeader {
|
||||
length: u32,
|
||||
tag: MP4BoxType
|
||||
}
|
||||
|
||||
named!(brand_name<&[u8],&str>, map_res!(take!(4), str::from_utf8));
|
||||
|
||||
named!(filetype_parser<&[u8], FileType>,
|
||||
do_parse!(
|
||||
m: brand_name >>
|
||||
v: take!(4) >>
|
||||
c: many0!(brand_name) >>
|
||||
(FileType{ major_brand: m, major_brand_version:v, compatible_brands: c })
|
||||
)
|
||||
);
|
||||
|
||||
fn mvhd_box(input:&[u8]) -> IResult<&[u8],MvhdBox> {
|
||||
let res = if input.len() < 100 {
|
||||
Incomplete(Needed::Size(100))
|
||||
} else if input.len() == 100 {
|
||||
mvhd32(input)
|
||||
} else if input.len() == 112 {
|
||||
mvhd64(input)
|
||||
} else {
|
||||
Error(error_position!(ErrorKind::Custom(32),input))
|
||||
};
|
||||
println!("res: {:?}", res);
|
||||
res
|
||||
}
|
||||
|
||||
fn unknown_box_type(input:&[u8]) -> IResult<&[u8], MP4BoxType> {
|
||||
Done(input, MP4BoxType::Unknown)
|
||||
}
|
||||
|
||||
//named!(box_type<&[u8], MP4BoxType>,
|
||||
fn box_type(input: &[u8]) -> IResult<&[u8], MP4BoxType, u32> {
|
||||
alt!(input,
|
||||
tag!("ftyp") => { |_| MP4BoxType::Ftyp } |
|
||||
tag!("moov") => { |_| MP4BoxType::Moov } |
|
||||
tag!("mdat") => { |_| MP4BoxType::Mdat } |
|
||||
tag!("free") => { |_| MP4BoxType::Free } |
|
||||
tag!("skip") => { |_| MP4BoxType::Skip } |
|
||||
tag!("wide") => { |_| MP4BoxType::Wide } |
|
||||
unknown_box_type
|
||||
)
|
||||
}
|
||||
|
||||
// warning, an alt combinator with 9 branches containing a tag combinator
|
||||
// can make the compilation very slow. Use functions as sub parsers,
|
||||
// or split into multiple alt! parsers if it gets slow
|
||||
named!(moov_type<&[u8], MP4BoxType>,
|
||||
alt!(
|
||||
tag!("mdra") => { |_| MP4BoxType::Mdra } |
|
||||
tag!("dref") => { |_| MP4BoxType::Dref } |
|
||||
tag!("cmov") => { |_| MP4BoxType::Cmov } |
|
||||
tag!("rmra") => { |_| MP4BoxType::Rmra } |
|
||||
tag!("iods") => { |_| MP4BoxType::Iods } |
|
||||
tag!("mvhd") => { |_| MP4BoxType::Mvhd } |
|
||||
tag!("clip") => { |_| MP4BoxType::Clip } |
|
||||
tag!("trak") => { |_| MP4BoxType::Trak } |
|
||||
tag!("udta") => { |_| MP4BoxType::Udta }
|
||||
)
|
||||
);
|
||||
|
||||
named!(box_header<&[u8],MP4BoxHeader>,
|
||||
do_parse!(
|
||||
length: be_u32 >>
|
||||
tag: box_type >>
|
||||
(MP4BoxHeader{ length: length, tag: tag})
|
||||
)
|
||||
);
|
||||
|
||||
named!(moov_header<&[u8],MP4BoxHeader>,
|
||||
do_parse!(
|
||||
length: be_u32 >>
|
||||
tag: moov_type >>
|
||||
(MP4BoxHeader{ length: length, tag: tag})
|
||||
)
|
||||
);
|
||||
|
||||
#[derive(Debug,PartialEq,Eq)]
|
||||
enum MP4State {
|
||||
Main,
|
||||
Moov,
|
||||
Mvhd(usize)
|
||||
}
|
||||
|
||||
pub struct MP4Consumer {
|
||||
state: MP4State,
|
||||
moov_bytes: usize,
|
||||
c_state: ConsumerState<(), (), Move>
|
||||
}
|
||||
|
||||
impl MP4Consumer {
|
||||
fn new() -> MP4Consumer {
|
||||
MP4Consumer { state: MP4State::Main, moov_bytes: 0, c_state: ConsumerState::Continue(Move::Consume(0)) }
|
||||
}
|
||||
|
||||
fn consume_main(&mut self, input: Input<&[u8]>) -> ConsumerState<(), (), Move> {
|
||||
//println!("\nparsing box header:\n{}", input.to_hex(8));
|
||||
match input {
|
||||
Input::Eof(None) => ConsumerState::Done(Move::Consume(0), ()),
|
||||
Input::Empty => ConsumerState::Continue(Move::Consume(0)),
|
||||
Input::Element(sl) | Input::Eof(Some(sl)) => {
|
||||
match box_header(sl) {
|
||||
Done(i, header) => {
|
||||
match header.tag {
|
||||
MP4BoxType::Ftyp => {
|
||||
println!("-> FTYP");
|
||||
match filetype_parser(&i[0..(header.length as usize - 8)]) {
|
||||
Done(rest, filetype_header) => {
|
||||
println!("filetype header: {:?}", filetype_header);
|
||||
//return ConsumerState::Await(header.length as usize, header.length as usize - 8);
|
||||
return ConsumerState::Continue(Move::Consume(sl.offset(rest)));
|
||||
}
|
||||
Error(a) => {
|
||||
println!("ftyp parsing error: {:?}", a);
|
||||
assert!(false);
|
||||
return ConsumerState::Error(());
|
||||
},
|
||||
Incomplete(n) => {
|
||||
println!("ftyp incomplete -> await: {}", sl.len());
|
||||
return ConsumerState::Continue(Move::Await(n));
|
||||
//return ConsumerState::Await(0, input.len() + 100);
|
||||
}
|
||||
}
|
||||
},
|
||||
MP4BoxType::Moov => {
|
||||
println!("-> MOOV");
|
||||
self.state = MP4State::Moov;
|
||||
self.moov_bytes = header.length as usize - 8;
|
||||
return ConsumerState::Continue(Move::Consume(sl.offset(i)));
|
||||
},
|
||||
MP4BoxType::Mdat => println!("-> MDAT"),
|
||||
MP4BoxType::Free => println!("-> FREE"),
|
||||
MP4BoxType::Skip => println!("-> SKIP"),
|
||||
MP4BoxType::Wide => println!("-> WIDE"),
|
||||
MP4BoxType::Unknown => {
|
||||
println!("-> UNKNOWN");
|
||||
println!("bytes:\n{}", (sl).to_hex(8));
|
||||
//return ConsumerState::Continue(Move::Consume(sl.offset(i)));
|
||||
},
|
||||
_ => { println!("invalid"); return ConsumerState::Error(())}
|
||||
}
|
||||
return ConsumerState::Continue(Move::Seek(SeekFrom::Current((header.length) as i64)))
|
||||
},
|
||||
Error(a) => {
|
||||
println!("mp4 parsing error: {:?}", a);
|
||||
assert!(false);
|
||||
return ConsumerState::Error(());
|
||||
},
|
||||
Incomplete(i) => {
|
||||
// FIXME: incomplete should send the required size
|
||||
println!("mp4 incomplete -> await: {}", sl.len());
|
||||
return ConsumerState::Continue(Move::Await(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn consume_moov(&mut self, input: Input<&[u8]>) -> ConsumerState<(), (), Move> {
|
||||
//println!("\nparsing moov box(remaining {} bytes):\n{}", self.moov_bytes, input.to_hex(8));
|
||||
match input {
|
||||
Input::Eof(None) => return ConsumerState::Error(()),
|
||||
Input::Empty => return ConsumerState::Continue(Move::Consume(0)),
|
||||
Input::Element(sl) | Input::Eof(Some(sl)) => {
|
||||
if self.moov_bytes == 0 {
|
||||
//println!("finished parsing moov atom, continuing with main parser");
|
||||
self.state = MP4State::Main;
|
||||
return ConsumerState::Continue(Move::Consume(0));
|
||||
}
|
||||
match moov_header(sl) {
|
||||
Done(i, header) => {
|
||||
match header.tag {
|
||||
MP4BoxType::Mvhd => {
|
||||
println!("-> MVHD");
|
||||
self.state = MP4State::Mvhd(header.length as usize - 8);
|
||||
// TODO: check for overflow here
|
||||
self.moov_bytes = self.moov_bytes - (sl.len() - i.len());
|
||||
println!("remaining moov_bytes: {}", self.moov_bytes);
|
||||
return ConsumerState::Continue(Move::Consume(sl.offset(i)));
|
||||
},
|
||||
MP4BoxType::Wide => println!("-> WIDE"),
|
||||
MP4BoxType::Mdra => println!("-> MDRA"),
|
||||
MP4BoxType::Dref => println!("-> DREF"),
|
||||
MP4BoxType::Cmov => println!("-> CMOV"),
|
||||
MP4BoxType::Rmra => println!("-> RMRA"),
|
||||
MP4BoxType::Iods => println!("-> IODS"),
|
||||
MP4BoxType::Clip => println!("-> CLIP"),
|
||||
MP4BoxType::Trak => println!("-> TRAK"),
|
||||
MP4BoxType::Udta => println!("-> UDTA"),
|
||||
MP4BoxType::Unknown => println!("-> MOOV UNKNOWN"),
|
||||
_ => { println!("invalid header here: {:?}", header.tag); return ConsumerState::Error(());}
|
||||
};
|
||||
// TODO: check for overflow here
|
||||
self.moov_bytes = self.moov_bytes - header.length as usize;
|
||||
println!("remaining moov_bytes: {}", self.moov_bytes);
|
||||
return ConsumerState::Continue(Move::Seek(SeekFrom::Current((header.length) as i64)))
|
||||
},
|
||||
Error(a) => {
|
||||
println!("moov parsing error: {:?}", a);
|
||||
println!("data:\n{}", sl.to_hex(8));
|
||||
assert!(false);
|
||||
return ConsumerState::Error(());
|
||||
},
|
||||
Incomplete(i) => {
|
||||
println!("moov incomplete -> await: {}", sl.len());
|
||||
return ConsumerState::Continue(Move::Await(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
consumer_from_parser!(MvhdConsumer<MvhdBox>, mvhd_box);
|
||||
|
||||
impl<'a> Consumer<&'a[u8], (), (), Move> for MP4Consumer {
|
||||
fn handle(&mut self, input: Input<&[u8]>) -> &ConsumerState<(), (), Move> {
|
||||
match self.state {
|
||||
MP4State::Main => {
|
||||
self.c_state = self.consume_main(input);
|
||||
},
|
||||
MP4State::Moov => {
|
||||
self.c_state = self.consume_moov(input);
|
||||
},
|
||||
MP4State::Mvhd(sz) => {
|
||||
match input {
|
||||
Input::Eof(None) => self.c_state = ConsumerState::Error(()),
|
||||
Input::Empty => self.c_state = ConsumerState::Continue(Move::Consume(0)),
|
||||
Input::Element(sl) | Input::Eof(Some(sl)) => {
|
||||
let mut c = MvhdConsumer{ state:ConsumerState::Continue(Move::Consume(0)) };
|
||||
self.c_state = c.handle(Input::Element(&sl[..sz])).flat_map(|m, _| {
|
||||
self.state = MP4State::Moov;
|
||||
ConsumerState::Continue(m)
|
||||
});
|
||||
println!("found mvhd?: {:?}", c.state());
|
||||
match self.c_state {
|
||||
ConsumerState::Continue(Move::Consume(sz)) => self.moov_bytes = self.moov_bytes - sz,
|
||||
ConsumerState::Continue(Move::Seek(SeekFrom::Current(sz))) => self.moov_bytes = self.moov_bytes - (sz as usize),
|
||||
_ => ()
|
||||
};
|
||||
println!("remaining moov_bytes: {}", self.moov_bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
&self.c_state
|
||||
}
|
||||
|
||||
fn state(&self) -> &ConsumerState<(), (), Move> {
|
||||
&self.c_state
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_must_use)]
|
||||
fn explore_mp4_file(filename: &str) {
|
||||
let mut p = FileProducer::new(filename, 400).unwrap();
|
||||
let mut c = MP4Consumer{state: MP4State::Main, moov_bytes: 0, c_state: ConsumerState::Continue(Move::Consume(0))};
|
||||
//c.run(&mut p);
|
||||
while let &ConsumerState::Continue(mv) = p.apply(&mut c) {
|
||||
println!("move: {:?}", mv);
|
||||
}
|
||||
println!("last consumer state: {:?} | last state: {:?}", c.c_state, c.state);
|
||||
|
||||
if let ConsumerState::Done(Move::Consume(0), ()) = c.c_state {
|
||||
println!("consumer state ok");
|
||||
} else {
|
||||
assert!(false, "consumer should have reached Done state");
|
||||
}
|
||||
assert_eq!(c.state, MP4State::Main);
|
||||
assert_eq!(p.state(), FileProducerState::Eof);
|
||||
//assert!(false);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn small_test() {
|
||||
explore_mp4_file("assets/small.mp4");
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn big_bunny_test() {
|
||||
explore_mp4_file("assets/bigbuckbunny.mp4");
|
||||
}
|
||||
|
||||
|
||||
|
21
third_party/rust/nom-3.2.1/tests/multiline.rs
vendored
21
third_party/rust/nom-3.2.1/tests/multiline.rs
vendored
@ -1,21 +0,0 @@
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::{IResult,alphanumeric,eol};
|
||||
|
||||
use std::str;
|
||||
|
||||
named!(end_of_line, alt!(eof!() | eol));
|
||||
named!(read_line <&str>, map_res!(
|
||||
terminated!(alphanumeric, end_of_line),
|
||||
str::from_utf8
|
||||
));
|
||||
named!(read_lines <Vec<&str> >, many0!(read_line));
|
||||
|
||||
#[test]
|
||||
fn read_lines_test() {
|
||||
let res = IResult::Done(&b""[..], vec!["Duck", "Dog", "Cow"]);
|
||||
|
||||
assert_eq!(read_lines(&b"Duck\nDog\nCow\n"[..]), res);
|
||||
assert_eq!(read_lines(&b"Duck\nDog\nCow"[..]), res);
|
||||
}
|
108
third_party/rust/nom-3.2.1/tests/named_args.rs
vendored
108
third_party/rust/nom-3.2.1/tests/named_args.rs
vendored
@ -1,108 +0,0 @@
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::{IResult,digit};
|
||||
|
||||
// Parser definition
|
||||
|
||||
use std::str;
|
||||
use std::str::FromStr;
|
||||
|
||||
use self::Operator::*;
|
||||
|
||||
enum Operator {
|
||||
Slash,
|
||||
Star,
|
||||
}
|
||||
|
||||
impl Operator {
|
||||
fn to_str(&self) -> &'static str {
|
||||
match *self {
|
||||
Slash => "/",
|
||||
Star => "*",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the specified `Operator`.
|
||||
named_args!(operator(op: Operator) <&[u8]>,
|
||||
tag!(op.to_str())
|
||||
);
|
||||
|
||||
// We parse any expr surrounded by the tags `open_tag` and `close_tag`, ignoring all whitespaces around those
|
||||
named_args!(brackets<'a>(open_tag: &str, close_tag: &str) <i64>, ws!(delimited!( tag!(open_tag), expr, tag!(close_tag) )) );
|
||||
|
||||
// We transform an integer string into a i64, ignoring surrounding whitespaces
|
||||
// We look for a digit suite, and try to convert it.
|
||||
// If either str::from_utf8 or FromStr::from_str fail,
|
||||
// we fallback to the brackets parser defined above
|
||||
named!(factor<i64>, alt!(
|
||||
map_res!(
|
||||
map_res!(
|
||||
ws!(digit),
|
||||
str::from_utf8
|
||||
),
|
||||
FromStr::from_str
|
||||
)
|
||||
| call!(brackets, "(", ")")
|
||||
)
|
||||
);
|
||||
|
||||
// We read an initial factor and for each time we find
|
||||
// a * or / operator followed by another factor, we do
|
||||
// the math by folding everything
|
||||
named!(term <i64>, do_parse!(
|
||||
init: factor >>
|
||||
res: fold_many0!(
|
||||
pair!(alt!(call!(operator, Star) | call!(operator, Slash)), factor),
|
||||
init,
|
||||
|acc, (op, val): (&[u8], i64)| {
|
||||
if (op[0] as char) == '*' { acc * val } else { acc / val }
|
||||
}
|
||||
) >>
|
||||
(res)
|
||||
)
|
||||
);
|
||||
|
||||
named!(expr <i64>, do_parse!(
|
||||
init: term >>
|
||||
res: fold_many0!(
|
||||
pair!(alt!(tag!("+") | tag!("-")), term),
|
||||
init,
|
||||
|acc, (op, val): (&[u8], i64)| {
|
||||
if (op[0] as char) == '+' { acc + val } else { acc - val }
|
||||
}
|
||||
) >>
|
||||
(res)
|
||||
)
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn factor_test() {
|
||||
assert_eq!(factor(&b"3"[..]), IResult::Done(&b""[..], 3));
|
||||
assert_eq!(factor(&b" 12"[..]), IResult::Done(&b""[..], 12));
|
||||
assert_eq!(factor(&b"537 "[..]), IResult::Done(&b""[..], 537));
|
||||
assert_eq!(factor(&b" 24 "[..]), IResult::Done(&b""[..], 24));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn term_test() {
|
||||
assert_eq!(term(&b" 12 *2 / 3"[..]), IResult::Done(&b""[..], 8));
|
||||
assert_eq!(term(&b" 2* 3 *2 *2 / 3"[..]), IResult::Done(&b""[..], 8));
|
||||
assert_eq!(term(&b" 48 / 3/2"[..]), IResult::Done(&b""[..], 8));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expr_test() {
|
||||
assert_eq!(expr(&b" 1 + 2 "[..]), IResult::Done(&b""[..], 3));
|
||||
assert_eq!(expr(&b" 12 + 6 - 4+ 3"[..]), IResult::Done(&b""[..], 17));
|
||||
assert_eq!(expr(&b" 1 + 2*3 + 4"[..]), IResult::Done(&b""[..], 11));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parens_test() {
|
||||
assert_eq!(expr(&b" ( 2 )"[..]), IResult::Done(&b""[..], 2));
|
||||
assert_eq!(expr(&b" 2* ( 3 + 4 ) "[..]), IResult::Done(&b""[..], 14));
|
||||
assert_eq!(expr(&b" 2*2 / ( 5 - 1) + 3"[..]), IResult::Done(&b""[..], 4));
|
||||
}
|
160
third_party/rust/nom-3.2.1/tests/omnom.rs
vendored
160
third_party/rust/nom-3.2.1/tests/omnom.rs
vendored
@ -1,160 +0,0 @@
|
||||
#![cfg(feature = "stream")]
|
||||
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::{Producer,Consumer,ConsumerState,Input,Move,MemProducer,IResult,Offset};
|
||||
|
||||
#[derive(PartialEq,Eq,Debug)]
|
||||
enum State {
|
||||
Beginning,
|
||||
Middle,
|
||||
End,
|
||||
Done,
|
||||
Error
|
||||
}
|
||||
|
||||
struct TestConsumer {
|
||||
state: State,
|
||||
c_state: ConsumerState<usize,(),Move>,
|
||||
counter: usize,
|
||||
}
|
||||
|
||||
named!(om_parser, tag!("om"));
|
||||
named!(nomnom_parser<&[u8],Vec<&[u8]> >, many1!(tag!("nom")));
|
||||
named!(end_parser, tag!("kthxbye"));
|
||||
|
||||
impl<'a> Consumer<&'a[u8], usize, (), Move> for TestConsumer {
|
||||
fn state(&self) -> &ConsumerState<usize,(),Move> {
|
||||
&self.c_state
|
||||
}
|
||||
|
||||
fn handle(&mut self, input: Input<&'a [u8]>) -> &ConsumerState<usize,(),Move> {
|
||||
match self.state {
|
||||
State::Beginning => {
|
||||
match input {
|
||||
Input::Empty | Input::Eof(None) => {
|
||||
self.state = State::Error;
|
||||
self.c_state = ConsumerState::Error(());
|
||||
},
|
||||
Input::Element(sl) | Input::Eof(Some(sl)) => {
|
||||
match om_parser(sl) {
|
||||
IResult::Error(_) => {
|
||||
self.state = State::Error;
|
||||
self.c_state = ConsumerState::Error(());
|
||||
},
|
||||
IResult::Incomplete(n) => {
|
||||
self.c_state = ConsumerState::Continue(Move::Await(n));
|
||||
},
|
||||
IResult::Done(i,_) => {
|
||||
self.state = State::Middle;
|
||||
self.c_state = ConsumerState::Continue(Move::Consume(sl.offset(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
State::Middle => {
|
||||
match input {
|
||||
Input::Empty | Input::Eof(None) => {
|
||||
self.state = State::Error;
|
||||
self.c_state = ConsumerState::Error(());
|
||||
},
|
||||
Input::Element(sl) | Input::Eof(Some(sl)) => {
|
||||
match nomnom_parser(sl) {
|
||||
IResult::Error(_) => {
|
||||
self.state = State::End;
|
||||
self.c_state = ConsumerState::Continue(Move::Consume(0));
|
||||
},
|
||||
IResult::Incomplete(n) => {
|
||||
println!("Middle got Incomplete({:?})", n);
|
||||
self.c_state = ConsumerState::Continue(Move::Await(n));
|
||||
},
|
||||
IResult::Done(i,noms_vec) => {
|
||||
self.counter = self.counter + noms_vec.len();
|
||||
self.state = State::Middle;
|
||||
self.c_state = ConsumerState::Continue(Move::Consume(sl.offset(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
State::End => {
|
||||
match input {
|
||||
Input::Empty | Input::Eof(None) => {
|
||||
self.state = State::Error;
|
||||
self.c_state = ConsumerState::Error(());
|
||||
},
|
||||
Input::Element(sl) | Input::Eof(Some(sl)) => {
|
||||
match end_parser(sl) {
|
||||
IResult::Error(_) => {
|
||||
self.state = State::Error;
|
||||
self.c_state = ConsumerState::Error(());
|
||||
},
|
||||
IResult::Incomplete(n) => {
|
||||
self.c_state = ConsumerState::Continue(Move::Await(n));
|
||||
},
|
||||
IResult::Done(i,_) => {
|
||||
self.state = State::Done;
|
||||
self.c_state = ConsumerState::Done(Move::Consume(sl.offset(i)), self.counter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
State::Done | State::Error => {
|
||||
// this should not be called
|
||||
self.state = State::Error;
|
||||
self.c_state = ConsumerState::Error(())
|
||||
}
|
||||
};
|
||||
&self.c_state
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nom1() {
|
||||
let mut p = MemProducer::new(&b"omnomkthxbye"[..], 8);
|
||||
let mut c = TestConsumer{state: State::Beginning, counter: 0, c_state: ConsumerState::Continue(Move::Consume(0))};
|
||||
while let &ConsumerState::Continue(Move::Consume(_)) = p.apply(&mut c) {
|
||||
}
|
||||
|
||||
assert_eq!(c.counter, 1);
|
||||
assert_eq!(c.state, State::Done);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nomnomnom() {
|
||||
let mut p = MemProducer::new(&b"omnomnomnomkthxbye"[..], 9);
|
||||
let mut c = TestConsumer{state: State::Beginning, counter: 0, c_state: ConsumerState::Continue(Move::Consume(0))};
|
||||
while let &ConsumerState::Continue(_) = p.apply(&mut c) {
|
||||
}
|
||||
|
||||
assert_eq!(c.counter, 3);
|
||||
assert_eq!(c.state, State::Done);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_nomnom() {
|
||||
let mut p = MemProducer::new(&b"omkthxbye"[..], 8);
|
||||
let mut c = TestConsumer{state: State::Beginning, counter: 0, c_state: ConsumerState::Continue(Move::Consume(0))};
|
||||
while let &ConsumerState::Continue(_) = p.apply(&mut c) {
|
||||
}
|
||||
|
||||
assert_eq!(c.counter, 0);
|
||||
assert_eq!(c.state, State::Done);
|
||||
}
|
||||
|
||||
/*
|
||||
#[test]
|
||||
fn impolite() {
|
||||
let mut p = MemProducer::new(&b"omnomnomnom"[..], 11);
|
||||
let mut c = TestConsumer{state: State::Beginning, counter: 0, c_state: ConsumerState::Continue(Move::Consume(0))};
|
||||
while let &ConsumerState::Continue(cont) = p.apply(&mut c) {
|
||||
println!("continue {:?}", cont);
|
||||
}
|
||||
|
||||
assert_eq!(c.counter, 3);
|
||||
assert_eq!(c.state, State::End);
|
||||
}
|
||||
*/
|
99
third_party/rust/nom-3.2.1/tests/overflow.rs
vendored
99
third_party/rust/nom-3.2.1/tests/overflow.rs
vendored
@ -1,99 +0,0 @@
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::{IResult,Needed,be_u8,be_u64};
|
||||
|
||||
// Parser definition
|
||||
|
||||
// We request a length that would trigger an overflow if computing consumed + requested
|
||||
named!(parser01<&[u8],()>,
|
||||
do_parse!(
|
||||
hdr: take!(1) >>
|
||||
data: take!(18446744073709551615) >>
|
||||
( () )
|
||||
)
|
||||
);
|
||||
|
||||
// We request a length that would trigger an overflow if computing consumed + requested
|
||||
named!(parser02<&[u8],(&[u8],&[u8])>,
|
||||
tuple!(take!(1),take!(18446744073709551615))
|
||||
);
|
||||
|
||||
#[test]
|
||||
fn overflow_incomplete_do_parse() {
|
||||
assert_eq!(parser01(&b"3"[..]), IResult::Incomplete(Needed::Unknown));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overflow_incomplete_tuple() {
|
||||
assert_eq!(parser02(&b"3"[..]), IResult::Incomplete(Needed::Unknown));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overflow_incomplete_length_bytes() {
|
||||
named!(multi<&[u8], Vec<&[u8]> >, many0!( length_bytes!(be_u64) ) );
|
||||
|
||||
// Trigger an overflow in length_bytes
|
||||
assert_eq!(multi(&b"\x00\x00\x00\x00\x00\x00\x00\x01\xaa\xff\xff\xff\xff\xff\xff\xff\xff\xaa"[..]), IResult::Incomplete(Needed::Unknown));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overflow_incomplete_many0() {
|
||||
named!(multi<&[u8], Vec<&[u8]> >, many0!( length_bytes!(be_u64) ) );
|
||||
|
||||
// Trigger an overflow in many0
|
||||
assert_eq!(multi(&b"\x00\x00\x00\x00\x00\x00\x00\x01\xaa\xff\xff\xff\xff\xff\xff\xff\xef\xaa"[..]), IResult::Incomplete(Needed::Unknown));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "std")]
|
||||
fn overflow_incomplete_many1() {
|
||||
named!(multi<&[u8], Vec<&[u8]> >, many1!( length_bytes!(be_u64) ) );
|
||||
|
||||
// Trigger an overflow in many1
|
||||
assert_eq!(multi(&b"\x00\x00\x00\x00\x00\x00\x00\x01\xaa\xff\xff\xff\xff\xff\xff\xff\xef\xaa"[..]), IResult::Incomplete(Needed::Unknown));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overflow_incomplete_many_till() {
|
||||
named!(multi<&[u8], (Vec<&[u8]>, &[u8]) >, many_till!( length_bytes!(be_u64), tag!("abc") ) );
|
||||
|
||||
// Trigger an overflow in many_till
|
||||
assert_eq!(multi(&b"\x00\x00\x00\x00\x00\x00\x00\x01\xaa\xff\xff\xff\xff\xff\xff\xff\xef\xaa"[..]), IResult::Incomplete(Needed::Unknown));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overflow_incomplete_many_m_n() {
|
||||
named!(multi<&[u8], Vec<&[u8]> >, many_m_n!(2, 4, length_bytes!(be_u64) ) );
|
||||
|
||||
// Trigger an overflow in many_m_n
|
||||
assert_eq!(multi(&b"\x00\x00\x00\x00\x00\x00\x00\x01\xaa\xff\xff\xff\xff\xff\xff\xff\xef\xaa"[..]), IResult::Incomplete(Needed::Unknown));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overflow_incomplete_count() {
|
||||
named!(counter<&[u8], Vec<&[u8]> >, count!( length_bytes!(be_u64), 2 ) );
|
||||
|
||||
assert_eq!(counter(&b"\x00\x00\x00\x00\x00\x00\x00\x01\xaa\xff\xff\xff\xff\xff\xff\xff\xef\xaa"[..]), IResult::Incomplete(Needed::Unknown));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overflow_incomplete_count_fixed() {
|
||||
named!(counter< [&[u8]; 2] >, count_fixed!( &[u8], length_bytes!(be_u64), 2 ) );
|
||||
|
||||
assert_eq!(counter(&b"\x00\x00\x00\x00\x00\x00\x00\x01\xaa\xff\xff\xff\xff\xff\xff\xff\xef\xaa"[..]), IResult::Incomplete(Needed::Unknown));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overflow_incomplete_length_count() {
|
||||
named!(multi<&[u8], Vec<&[u8]> >, length_count!( be_u8, length_bytes!(be_u64) ) );
|
||||
|
||||
assert_eq!(multi(&b"\x04\x00\x00\x00\x00\x00\x00\x00\x01\xaa\xff\xff\xff\xff\xff\xff\xff\xee\xaa"[..]), IResult::Incomplete(Needed::Unknown));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overflow_incomplete_length_data() {
|
||||
named!(multi<&[u8], Vec<&[u8]> >, many0!( length_data!(be_u64) ) );
|
||||
|
||||
assert_eq!(multi(&b"\x00\x00\x00\x00\x00\x00\x00\x01\xaa\xff\xff\xff\xff\xff\xff\xff\xff\xaa"[..]), IResult::Incomplete(Needed::Unknown));
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_variables)]
|
||||
|
||||
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use std::str;
|
||||
|
||||
|
||||
named_args!(atom<'a>(tomb: &'a mut ())<String>,
|
||||
map!(map_res!(is_not_s!(" \t\r\n()"), str::from_utf8), ToString::to_string));
|
||||
|
||||
|
||||
named_args!(list<'a>(tomb: &'a mut ())<String>,
|
||||
delimited!(char!('('), fold_many0!(call!(atom, tomb), "".to_string(), |acc: String, next: String| acc + next.as_str()), char!(')')));
|
44
third_party/rust/nom-3.2.1/tests/test1.rs
vendored
44
third_party/rust/nom-3.2.1/tests/test1.rs
vendored
@ -1,44 +0,0 @@
|
||||
#![cfg(feature = "stream")]
|
||||
|
||||
#[macro_use]
|
||||
extern crate nom;
|
||||
|
||||
use nom::{IResult,Producer,FileProducer,not_line_ending};
|
||||
|
||||
use std::str;
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[test]
|
||||
#[allow(unused_must_use)]
|
||||
fn tag() {
|
||||
FileProducer::new("assets/links.txt", 20).map(|producer: FileProducer| {
|
||||
let mut p = producer;
|
||||
p.refill();
|
||||
|
||||
consumer_from_parser!(PrintConsumer<()>, flat_map!(map_res!(tag!("https!"), str::from_utf8), print));
|
||||
let mut cs = PrintConsumer::new();
|
||||
for _ in 1..4 {
|
||||
p.apply(&mut cs);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn print<T: Debug>(input: T) -> IResult<T,()> {
|
||||
println!("{:?}", input);
|
||||
IResult::Done(input, ())
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn is_not() {
|
||||
//is_not!(foo b"\r\n");
|
||||
named!(foo<&[u8],&[u8]>, is_not!(&b"\r\n"[..]));
|
||||
let a = &b"ab12cd\nefgh"[..];
|
||||
assert_eq!(foo(a), IResult::Done(&b"\nefgh"[..], &b"ab12cd"[..]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exported_public_method_defined_by_macro() {
|
||||
let a = &b"ab12cd\nefgh"[..];
|
||||
assert_eq!(not_line_ending(a), IResult::Done(&b"\nefgh"[..], &b"ab12cd"[..]));
|
||||
}
|
Loading…
Reference in New Issue
Block a user