зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1642708 - Remove BinAST Rust modules r=arai,emilio
Differential Revision: https://phabricator.services.mozilla.com/D77947
This commit is contained in:
Родитель
9f73e2fb3b
Коммит
4b5638af5a
|
@ -1,11 +1,5 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "Inflector"
|
||||
version = "0.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
|
||||
|
||||
[[package]]
|
||||
name = "adler32"
|
||||
version = "1.0.4"
|
||||
|
@ -27,15 +21,6 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
dependencies = [
|
||||
"winapi 0.3.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.30"
|
||||
|
@ -257,19 +242,6 @@ version = "0.1.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88ceb0d16c4fd0e42876e298d7d3ce3780dd9ebdcbe4199816a32c77e08597ff"
|
||||
|
||||
[[package]]
|
||||
name = "binast"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"binjs_meta",
|
||||
"clap",
|
||||
"env_logger",
|
||||
"itertools",
|
||||
"log",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.2.1"
|
||||
|
@ -300,18 +272,6 @@ dependencies = [
|
|||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "binjs_meta"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d535cc5246fd9035268770420afd76c05f87e68b83ebed0ac94e8258e88fc353"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"itertools",
|
||||
"log",
|
||||
"weedle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.0"
|
||||
|
@ -610,14 +570,11 @@ version = "2.31.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags",
|
||||
"strsim",
|
||||
"term_size",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5561,15 +5518,6 @@ dependencies = [
|
|||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "weedle"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a7d4f9feb723a800d8f7b74edc9fa44ff35cb0b2ec64886714362f423427f37"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wgpu-core"
|
||||
version = "0.5.0"
|
||||
|
|
|
@ -8,7 +8,6 @@ members = [
|
|||
"js/rust",
|
||||
"js/src",
|
||||
"js/src/frontend/smoosh",
|
||||
"js/src/frontend/binast", # Code generator.
|
||||
"js/src/rust",
|
||||
"js/src/wasm/cranelift",
|
||||
"netwerk/test/http3server",
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
[package]
|
||||
name = "binast"
|
||||
version = "0.2.0"
|
||||
authors = ["David Teller <D.O.Teller@gmail.com>"]
|
||||
autobins = false
|
||||
|
||||
[dependencies]
|
||||
binjs_meta = "^0.5.2"
|
||||
clap = "^2"
|
||||
env_logger = "^0.6"
|
||||
Inflector = { version="^0.11", default-features=false }
|
||||
itertools = "^0.8"
|
||||
log = "0.4"
|
||||
yaml-rust = "^0.4.2"
|
||||
|
||||
[[bin]]
|
||||
name = "binast"
|
||||
path = "src/main.rs"
|
|
@ -1,16 +0,0 @@
|
|||
A parser generator used to generate the following files:
|
||||
|
||||
- js/src/frontend/BinASTParser.h
|
||||
- js/src/frontend/BinASTParser.cpp
|
||||
- js/src/frontend/BinASTToken.h
|
||||
|
||||
from the following files:
|
||||
|
||||
- js/src/frontend/BinAST.webidl_ (specifications of BinAST)
|
||||
- js/src/frontend/BinAST.yaml (parser generator driver)
|
||||
|
||||
To use it:
|
||||
```sh
|
||||
$ cd $(topsrcdir)/js/src/frontend/binast
|
||||
% ./build.sh
|
||||
```
|
|
@ -1,41 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
cargo run -- \
|
||||
../BinAST.webidl_ \
|
||||
../BinAST.yaml \
|
||||
--out-class ../BinASTParser-tmp.h \
|
||||
--out-impl ../BinASTParser-tmp.cpp \
|
||||
--out-enum ../BinASTEnum-tmp.h \
|
||||
--out-token ../BinASTToken-tmp.h
|
||||
|
||||
|
||||
(
|
||||
cd ../../../../;
|
||||
./mach clang-format -p \
|
||||
js/src/frontend/BinASTParser-tmp.h \
|
||||
js/src/frontend/BinASTParser-tmp.cpp \
|
||||
js/src/frontend/BinASTEnum-tmp.h \
|
||||
js/src/frontend/BinASTToken-tmp.h
|
||||
)
|
||||
|
||||
# Usage: update SRC DST
|
||||
#
|
||||
# If SRC file and DST file have different content, move SRC file to DST file.
|
||||
# If not, remove SRC file.
|
||||
update() {
|
||||
SRC=$1
|
||||
DST=$2
|
||||
|
||||
if diff -q ${SRC} ${DST} > /dev/null; then
|
||||
echo "SKIPPED: ${DST} was not modified"
|
||||
rm ${SRC}
|
||||
else
|
||||
echo "UPDATED: ${DST} was modified"
|
||||
mv ${SRC} ${DST}
|
||||
fi
|
||||
}
|
||||
|
||||
update ../BinASTParser-tmp.h ../BinASTParser.h
|
||||
update ../BinASTParser-tmp.cpp ../BinASTParser.cpp
|
||||
update ../BinASTEnum-tmp.h ../BinASTEnum.h
|
||||
update ../BinASTToken-tmp.h ../BinASTToken.h
|
|
@ -1,7 +0,0 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
HOST_RUST_PROGRAMS += ['binast']
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,96 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::rc::Rc;
|
||||
|
||||
/// A node in the reference graph
|
||||
struct ReferenceGraphNode {
|
||||
/// True if this node is used.
|
||||
used: RefCell<bool>,
|
||||
|
||||
/// The set of method names which node uses.
|
||||
edges: HashSet<Rc<String>>,
|
||||
}
|
||||
impl ReferenceGraphNode {
|
||||
fn new(edges: HashSet<Rc<String>>) -> Self {
|
||||
ReferenceGraphNode {
|
||||
used: RefCell::new(false),
|
||||
edges,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference graph of the method call.
|
||||
///
|
||||
/// Each auto-generated method has corresponding node in this reference graph,
|
||||
/// and the method definition/declaration are written to the file only if the
|
||||
/// method is marked as used.
|
||||
///
|
||||
/// This is necessary for the following reason:
|
||||
/// * we generate parseX and parseInterfaceX for `interface X`
|
||||
/// * parseX is not used if `interface X` appears only in sum interface
|
||||
pub struct ReferenceGraph {
|
||||
/// The map from the node name to node struct.
|
||||
/// Node name is the method name without leading "parse".
|
||||
refnodes: HashMap<Rc<String>, Rc<ReferenceGraphNode>>,
|
||||
}
|
||||
impl ReferenceGraph {
|
||||
pub fn new() -> Self {
|
||||
ReferenceGraph {
|
||||
refnodes: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Trace the reference graph from the node with `name and mark all nodes
|
||||
/// as used. `name` is the name of the method, without leading "parse".
|
||||
pub fn trace(&mut self, name: Rc<String>) {
|
||||
// The set of edges to trace in the current iteration.
|
||||
let mut edges: HashSet<Rc<String>> = HashSet::new();
|
||||
edges.insert(name);
|
||||
|
||||
// Iterate over the remaining edges until all edges are traced.
|
||||
loop {
|
||||
// The set of edges to trace in the next iteration.
|
||||
let mut next_edges: HashSet<Rc<String>> = HashSet::new();
|
||||
|
||||
for edge in edges {
|
||||
let refnode = self.refnodes.get(&edge).unwrap_or_else(|| {
|
||||
panic!("While computing dependencies, node {} doesn't exist", edge)
|
||||
});
|
||||
if *refnode.used.borrow() {
|
||||
continue;
|
||||
}
|
||||
|
||||
refnode.used.replace(true);
|
||||
|
||||
for next_edge in refnode.edges.iter() {
|
||||
next_edges.insert(next_edge.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if next_edges.len() == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
edges = next_edges;
|
||||
}
|
||||
}
|
||||
|
||||
/// Return true if the method named `name` (without leading "parse") is
|
||||
/// used.
|
||||
pub fn is_used(&self, name: Rc<String>) -> bool {
|
||||
match self.refnodes.get(&name) {
|
||||
Some(refnode) => *refnode.used.borrow(),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert a node with `name` to the graph.
|
||||
pub fn insert(&mut self, name: Rc<String>, edges: HashSet<Rc<String>>) {
|
||||
self.refnodes
|
||||
.insert(name, Rc::new(ReferenceGraphNode::new(edges)));
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
{"files":{"Cargo.toml":"8fb633b01ea17947fd1f76e0a447b027ddf48c019671f09e39c1338ab715605d","README.md":"3c7136fc446143eecf9668e6daf7f096eb5eb3a3e312cc674571719cb4c83bcc","src/cases/camelcase/mod.rs":"8e65fca78ea88acb32c0f214cafde39b849aef253253c3681e316f2559b26977","src/cases/case/mod.rs":"16323191c983d316debd50af11f94f4c525bb70d4d1a02db06a9aed67d4ba2a9","src/cases/classcase/mod.rs":"5b6b74530a2a693bf1ac89342f1b25f58f39336b1ee3242547c3d6ef468a878f","src/cases/kebabcase/mod.rs":"b317ebd42f22daab4b23bb4b83ce85f053d7088680d3a32eecbd13bd5331587a","src/cases/mod.rs":"e272853bcc1c5f6eb02594038febb9dcebb6eca8eac744d6e503db5082e585c6","src/cases/pascalcase/mod.rs":"a44feed6d8877fd8a31160076befe826960aa001d859587aef2dddc1aedc397b","src/cases/screamingsnakecase/mod.rs":"21582eb1ec2170d379bf3536c6ffb39b8bdc096efe2d493674458ee27b86e985","src/cases/sentencecase/mod.rs":"eb21d7d5bf0b23e1325d429dfdc149081d233a8b950c1fdfe04b4bebcc2c0ddb","src/cases/snakecase/mod.rs":"369739e37e700c028022f308aa78504873c10a5e88768f05249c1c8481b30c9d","src/cases/tablecase/mod.rs":"a6a50a397059d775a517d5dce6ba612b107919e209a9eb56871a5c1d42314664","src/cases/titlecase/mod.rs":"3f0dac5e5b434da9234d6c389f67bb2d3c8f138dc521fa29dbe3791f8eaf5341","src/cases/traincase/mod.rs":"4e2493d6594d3c505de293c69390b3f672c0fd4d35603ae1a1aae48166bc18c2","src/lib.rs":"6c5cf60f5c2f8778a3ad7638f37064527b8a86f164117d867b8b6532e2cc655e","src/numbers/deordinalize/mod.rs":"a3930b0315d20d2d86747bc4ae653a0fb7f7d80de497b0aaa9873aadd1459d18","src/numbers/mod.rs":"fed4e090f8b64a34ae64ddcb68d899cfa4dd8e8422a060be01a70dbdb71b85e0","src/numbers/ordinalize/mod.rs":"ce0d88977efaa50792e7311c0e0a73a3115928f9f7be77f914824c3d80eab66c","src/string/constants/mod.rs":"38de3d5060a5d224d28d184eab8af02203c65d74c1d380720c3260ea205f3e05","src/string/deconstantize/mod.rs":"c79f2170dc41bd6abb89a6e74fbdd87bf011f62cfe1f34d8886fda0724ade6fa","src/string/demodulize/mod.rs":"bbcb5314473e4ca02feee4903e31a332caaa912ed2cbca0f49c2fe411a826215","src/string/mod.rs":"570f7ea4dd646f2d633ddd67079db922cc2cadf916719fa19c2f59b4d522ee89","src/string/pluralize/mod.rs":"5f07fab8b5f4e7af546f1e907426724714b9b27af1ecb59a91e57dccd0833a6e","src/string/singularize/mod.rs":"9c2d833cbcdc1489013642de22578d51f558a31e8d2fea4536a27f8fa1114169","src/suffix/foreignkey/mod.rs":"e7ad9a9a0a21fcb53becb36306a15eedf67958e2da18ae928ae592177e70e7a3","src/suffix/mod.rs":"f6f99ce6fc8794d5411d91533b67be5d4a2bc5994317d32f405b2fa3c5ec660d","tests/lib.rs":"e1cfcea8a146291396ff72b0a2e84c2b9ddaa0103717442c4921c165a2ab470d"},"package":"fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"}
|
|
@ -1,42 +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 = "Inflector"
|
||||
version = "0.11.4"
|
||||
authors = ["Josh Teeter<joshteeter@gmail.com>"]
|
||||
include = ["**/*.rs", "Cargo.toml", "README.md"]
|
||||
description = "Adds String based inflections for Rust. Snake, kebab, camel, sentence, class, title and table cases as well as ordinalize, deordinalize, demodulize, foreign key, and pluralize/singularize are supported as both traits and pure functions acting on String types.\n"
|
||||
homepage = "https://github.com/whatisinternet/inflector"
|
||||
documentation = "https://docs.rs/Inflector"
|
||||
readme = "README.md"
|
||||
keywords = ["pluralize", "Inflector", "camel", "snake", "inflection"]
|
||||
categories = ["text-processing", "value-formatting"]
|
||||
license = "BSD-2-Clause"
|
||||
repository = "https://github.com/whatisinternet/inflector"
|
||||
|
||||
[lib]
|
||||
name = "inflector"
|
||||
[dependencies.lazy_static]
|
||||
version = "1.2.0"
|
||||
optional = true
|
||||
|
||||
[dependencies.regex]
|
||||
version = "1.1"
|
||||
optional = true
|
||||
|
||||
[features]
|
||||
default = ["heavyweight"]
|
||||
heavyweight = ["regex", "lazy_static"]
|
||||
unstable = []
|
||||
[badges.travis-ci]
|
||||
repository = "whatisinternet/Inflector"
|
|
@ -1,136 +0,0 @@
|
|||
# Rust Inflector
|
||||
|
||||
|
||||
[![Build Status](https://travis-ci.org/whatisinternet/Inflector.svg?branch=master)](https://travis-ci.org/whatisinternet/Inflector) [![Crates.io](https://img.shields.io/crates/v/Inflector.svg)](https://crates.io/crates/inflector)[![Crate downloads](https://img.shields.io/crates/d/Inflector.svg)](https://crates.io/crates/inflector)
|
||||
|
||||
|
||||
Adds String based inflections for Rust. Snake, kebab, train, camel,
|
||||
sentence, class, and title cases as well as ordinalize,
|
||||
deordinalize, demodulize, deconstantize, foreign key, table case, and pluralize/singularize are supported as both traits and pure functions
|
||||
acting on &str and String types.
|
||||
|
||||
-----
|
||||
## Documentation:
|
||||
|
||||
Documentation can be found here at the README or via rust docs below.
|
||||
|
||||
[Rust docs with examples](https://docs.rs/Inflector)
|
||||
|
||||
-----
|
||||
|
||||
## Installation:
|
||||
|
||||
### As a [crate](http://crates.io)
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
Inflector = "*"
|
||||
```
|
||||
|
||||
### Compile yourself:
|
||||
|
||||
1. Install [Rust and cargo](http://doc.crates.io/)
|
||||
2. git clone https://github.com/whatisinternet/Inflector
|
||||
3. Library: cd inflector && cargo build --release --lib
|
||||
4. You can find the library in target/release
|
||||
|
||||
## Usage / Example:
|
||||
|
||||
```rust
|
||||
...
|
||||
// to use methods like String.to_lower_case();
|
||||
extern crate inflector;
|
||||
use inflector::Inflector;
|
||||
...
|
||||
fn main() {
|
||||
...
|
||||
let camel_case_string: String = "some_string".to_camel_case();
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Or
|
||||
|
||||
```rust
|
||||
...
|
||||
// to use methods like to_snake_case(&str);
|
||||
extern crate inflector;
|
||||
|
||||
// use inflector::cases::classcase::to_class_case;
|
||||
// use inflector::cases::classcase::is_class_case;
|
||||
|
||||
// use inflector::cases::camelcase::to_camel_case;
|
||||
// use inflector::cases::camelcase::is_camel_case;
|
||||
|
||||
// use inflector::cases::pascalcase::to_pascal_case;
|
||||
// use inflector::cases::pascalcase::is_pascal_case;
|
||||
|
||||
// use inflector::cases::screamingsnakecase::to_screamingsnake_case;
|
||||
// use inflector::cases::screamingsnakecase::is_screamingsnake_case;
|
||||
|
||||
// use inflector::cases::snakecase::to_snake_case;
|
||||
// use inflector::cases::snakecase::is_snake_case;
|
||||
|
||||
// use inflector::cases::kebabcase::to_kebab_case;
|
||||
// use inflector::cases::kebabcase::is_kebab_case;
|
||||
|
||||
// use inflector::cases::traincase::to_train_case;
|
||||
// use inflector::cases::traincase::is_train_case;
|
||||
|
||||
// use inflector::cases::sentencecase::to_sentence_case;
|
||||
// use inflector::cases::sentencecase::is_sentence_case;
|
||||
|
||||
// use inflector::cases::titlecase::to_title_case;
|
||||
// use inflector::cases::titlecase::is_title_case;
|
||||
|
||||
// use inflector::cases::tablecase::to_table_case;
|
||||
// use inflector::cases::tablecase::is_table_case;
|
||||
|
||||
// use inflector::numbers::ordinalize::ordinalize;
|
||||
// use inflector::numbers::deordinalize::deordinalize;
|
||||
|
||||
// use inflector::suffix::foreignkey::to_foreign_key;
|
||||
// use inflector::suffix::foreignkey::is_foreign_key;
|
||||
|
||||
// use inflector::string::demodulize::demodulize;
|
||||
// use inflector::string::deconstantize::deconstantize;
|
||||
|
||||
// use inflector::string::pluralize::to_plural;
|
||||
// use inflector::string::singularize::to_singular;
|
||||
...
|
||||
fn main() {
|
||||
...
|
||||
let camel_case_string: String = to_camel_case("some_string");
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Advanced installation and usage:
|
||||
|
||||
If the project doesn't require singularize, pluralize, class, table, demodulize,
|
||||
deconstantize. Then in your `cargo.toml` you may wish to specify:
|
||||
|
||||
```toml
|
||||
[dependencies.Inflector]
|
||||
version = "*"
|
||||
default-features = false
|
||||
```
|
||||
|
||||
Or
|
||||
|
||||
```toml
|
||||
Inflector = {version="*", default-features=false}
|
||||
|
||||
```
|
||||
|
||||
To test this crate locally with features off try:
|
||||
|
||||
```shell
|
||||
cargo test --no-default-features
|
||||
```
|
||||
|
||||
## [Contributing](CONTRIBUTING.md)
|
||||
|
||||
This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
|
@ -1,370 +0,0 @@
|
|||
#![deny(warnings)]
|
||||
use cases::case::*;
|
||||
|
||||
/// Converts a `&str` to camelCase `String`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::camelcase::to_camel_case;
|
||||
/// let mock_string: &str = "fooBar";
|
||||
/// let expected_string: String = "fooBar".to_string();
|
||||
/// let asserted_string: String = to_camel_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::camelcase::to_camel_case;
|
||||
/// let mock_string: &str = "FOO_BAR";
|
||||
/// let expected_string: String = "fooBar".to_string();
|
||||
/// let asserted_string: String = to_camel_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::camelcase::to_camel_case;
|
||||
/// let mock_string: &str = "Foo Bar";
|
||||
/// let expected_string: String = "fooBar".to_string();
|
||||
/// let asserted_string: String = to_camel_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::camelcase::to_camel_case;
|
||||
/// let mock_string: &str = "foo_bar";
|
||||
/// let expected_string: String = "fooBar".to_string();
|
||||
/// let asserted_string: String = to_camel_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::camelcase::to_camel_case;
|
||||
/// let mock_string: &str = "Foo bar";
|
||||
/// let expected_string: String = "fooBar".to_string();
|
||||
/// let asserted_string: String = to_camel_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::camelcase::to_camel_case;
|
||||
/// let mock_string: &str = "foo-bar";
|
||||
/// let expected_string: String = "fooBar".to_string();
|
||||
/// let asserted_string: String = to_camel_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::camelcase::to_camel_case;
|
||||
/// let mock_string: &str = "FooBar";
|
||||
/// let expected_string: String = "fooBar".to_string();
|
||||
/// let asserted_string: String = to_camel_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::camelcase::to_camel_case;
|
||||
/// let mock_string: &str = "FooBar3";
|
||||
/// let expected_string: String = "fooBar3".to_string();
|
||||
/// let asserted_string: String = to_camel_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::camelcase::to_camel_case;
|
||||
/// let mock_string: &str = "Foo-Bar";
|
||||
/// let expected_string: String = "fooBar".to_string();
|
||||
/// let asserted_string: String = to_camel_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
pub fn to_camel_case(non_camelized_string: &str) -> String {
|
||||
let options = CamelOptions {
|
||||
new_word: false,
|
||||
last_char: ' ',
|
||||
first_word: false,
|
||||
injectable_char: ' ',
|
||||
has_seperator: false,
|
||||
inverted: false,
|
||||
};
|
||||
to_case_camel_like(&non_camelized_string, options)
|
||||
}
|
||||
|
||||
/// Determines if a `&str` is camelCase bool``
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::camelcase::is_camel_case;
|
||||
/// let mock_string: &str = "Foo";
|
||||
/// let asserted_bool: bool = is_camel_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::camelcase::is_camel_case;
|
||||
/// let mock_string: &str = "foo";
|
||||
/// let asserted_bool: bool = is_camel_case(mock_string);
|
||||
/// assert!(asserted_bool == true);
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::camelcase::is_camel_case;
|
||||
/// let mock_string: &str = "foo-bar-string-that-is-really-really-long";
|
||||
/// let asserted_bool: bool = is_camel_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::camelcase::is_camel_case;
|
||||
/// let mock_string: &str = "FooBarIsAReallyReallyLongString";
|
||||
/// let asserted_bool: bool = is_camel_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::camelcase::is_camel_case;
|
||||
/// let mock_string: &str = "fooBarIsAReallyReally3LongString";
|
||||
/// let asserted_bool: bool = is_camel_case(mock_string);
|
||||
/// assert!(asserted_bool == true);
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::camelcase::is_camel_case;
|
||||
/// let mock_string: &str = "fooBarIsAReallyReallyLongString";
|
||||
/// let asserted_bool: bool = is_camel_case(mock_string);
|
||||
/// assert!(asserted_bool == true);
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::camelcase::is_camel_case;
|
||||
/// let mock_string: &str = "FOO_BAR_STRING_THAT_IS_REALLY_REALLY_LONG";
|
||||
/// let asserted_bool: bool = is_camel_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::camelcase::is_camel_case;
|
||||
/// let mock_string: &str = "foo_bar_string_that_is_really_really_long";
|
||||
/// let asserted_bool: bool = is_camel_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::camelcase::is_camel_case;
|
||||
/// let mock_string: &str = "Foo bar string that is really really long";
|
||||
/// let asserted_bool: bool = is_camel_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::camelcase::is_camel_case;
|
||||
/// let mock_string: &str = "Foo Bar Is A Really Really Long String";
|
||||
/// let asserted_bool: bool = is_camel_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
/// ```
|
||||
pub fn is_camel_case(test_string: &str) -> bool {
|
||||
to_camel_case(&test_string.clone()) == test_string
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", test))]
|
||||
mod benchmarks {
|
||||
extern crate test;
|
||||
use self::test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_camel0(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let test_string = "Foo bar";
|
||||
super::to_camel_case(test_string)
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_camel1(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let test_string = "foo_bar";
|
||||
super::to_camel_case(test_string)
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_camel2(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let test_string = "fooBar";
|
||||
super::to_camel_case(test_string)
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_is_camel(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let test_string: &str = "Foo bar";
|
||||
super::is_camel_case(test_string)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ::to_camel_case;
|
||||
use ::is_camel_case;
|
||||
|
||||
#[test]
|
||||
fn from_camel_case() {
|
||||
let convertable_string: String = "fooBar".to_owned();
|
||||
let expected: String = "fooBar".to_owned();
|
||||
assert_eq!(to_camel_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_pascal_case() {
|
||||
let convertable_string: String = "FooBar".to_owned();
|
||||
let expected: String = "fooBar".to_owned();
|
||||
assert_eq!(to_camel_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_kebab_case() {
|
||||
let convertable_string: String = "foo-bar".to_owned();
|
||||
let expected: String = "fooBar".to_owned();
|
||||
assert_eq!(to_camel_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_sentence_case() {
|
||||
let convertable_string: String = "Foo bar".to_owned();
|
||||
let expected: String = "fooBar".to_owned();
|
||||
assert_eq!(to_camel_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_title_case() {
|
||||
let convertable_string: String = "Foo Bar".to_owned();
|
||||
let expected: String = "fooBar".to_owned();
|
||||
assert_eq!(to_camel_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_train_case() {
|
||||
let convertable_string: String = "Foo-Bar".to_owned();
|
||||
let expected: String = "fooBar".to_owned();
|
||||
assert_eq!(to_camel_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_screaming_snake_case() {
|
||||
let convertable_string: String = "FOO_BAR".to_owned();
|
||||
let expected: String = "fooBar".to_owned();
|
||||
assert_eq!(to_camel_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_snake_case() {
|
||||
let convertable_string: String = "foo_bar".to_owned();
|
||||
let expected: String = "fooBar".to_owned();
|
||||
assert_eq!(to_camel_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_case_with_loads_of_space() {
|
||||
let convertable_string: String = "foo bar".to_owned();
|
||||
let expected: String = "fooBar".to_owned();
|
||||
assert_eq!(to_camel_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_name_with_a_dot() {
|
||||
let convertable_string: String = "Robert C. Martin".to_owned();
|
||||
let expected: String = "robertCMartin".to_owned();
|
||||
assert_eq!(to_camel_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn random_text_with_bad_chars() {
|
||||
let convertable_string: String = "Random text with *(bad) chars".to_owned();
|
||||
let expected: String = "randomTextWithBadChars".to_owned();
|
||||
assert_eq!(to_camel_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trailing_bad_chars() {
|
||||
let convertable_string: String = "trailing bad_chars*(()())".to_owned();
|
||||
let expected: String = "trailingBadChars".to_owned();
|
||||
assert_eq!(to_camel_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leading_bad_chars() {
|
||||
let convertable_string: String = "-!#$%leading bad chars".to_owned();
|
||||
let expected: String = "leadingBadChars".to_owned();
|
||||
assert_eq!(to_camel_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wrapped_in_bad_chars() {
|
||||
let convertable_string: String = "-!#$%wrapped in bad chars&*^*&(&*^&(<><?>><?><>))".to_owned();
|
||||
let expected: String = "wrappedInBadChars".to_owned();
|
||||
assert_eq!(to_camel_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_a_sign() {
|
||||
let convertable_string: String = "has a + sign".to_owned();
|
||||
let expected: String = "hasASign".to_owned();
|
||||
assert_eq!(to_camel_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_camel_case() {
|
||||
let convertable_string: String = "fooBar".to_owned();
|
||||
assert_eq!(is_camel_case(&convertable_string), true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_pascal_case() {
|
||||
let convertable_string: String = "FooBar".to_owned();
|
||||
assert_eq!(is_camel_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_kebab_case() {
|
||||
let convertable_string: String = "foo-bar".to_owned();
|
||||
assert_eq!(is_camel_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_sentence_case() {
|
||||
let convertable_string: String = "Foo bar".to_owned();
|
||||
assert_eq!(is_camel_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_title_case() {
|
||||
let convertable_string: String = "Foo Bar".to_owned();
|
||||
assert_eq!(is_camel_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_train_case() {
|
||||
let convertable_string: String = "Foo-Bar".to_owned();
|
||||
assert_eq!(is_camel_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_screaming_snake_case() {
|
||||
let convertable_string: String = "FOO_BAR".to_owned();
|
||||
assert_eq!(is_camel_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_snake_case() {
|
||||
let convertable_string: String = "foo_bar".to_owned();
|
||||
assert_eq!(is_camel_case(&convertable_string), false)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,303 +0,0 @@
|
|||
#![deny(warnings)]
|
||||
#[allow(unknown_lints)]
|
||||
#[allow(unused_imports)]
|
||||
use std::ascii::*;
|
||||
|
||||
pub struct CamelOptions {
|
||||
pub new_word: bool,
|
||||
pub last_char: char,
|
||||
pub first_word: bool,
|
||||
pub injectable_char: char,
|
||||
pub has_seperator: bool,
|
||||
pub inverted: bool,
|
||||
}
|
||||
|
||||
pub fn to_case_snake_like(convertable_string: &str, replace_with: &str, case: &str) -> String {
|
||||
let mut first_character: bool = true;
|
||||
let mut result: String = String::with_capacity(convertable_string.len() * 2);
|
||||
for char_with_index in trim_right(convertable_string).char_indices() {
|
||||
if char_is_seperator(&char_with_index.1) {
|
||||
if !first_character {
|
||||
first_character = true;
|
||||
result.push(replace_with.chars().nth(0).unwrap_or('_'));
|
||||
}
|
||||
} else if requires_seperator(char_with_index, first_character, &convertable_string) {
|
||||
first_character = false;
|
||||
result = snake_like_with_seperator(result, replace_with, &char_with_index.1, case)
|
||||
} else {
|
||||
first_character = false;
|
||||
result = snake_like_no_seperator(result, &char_with_index.1, case)
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn to_case_camel_like(convertable_string: &str, camel_options: CamelOptions) -> String {
|
||||
let mut new_word: bool = camel_options.new_word;
|
||||
let mut first_word: bool = camel_options.first_word;
|
||||
let mut last_char: char = camel_options.last_char;
|
||||
let mut found_real_char: bool = false;
|
||||
let mut result: String = String::with_capacity(convertable_string.len() * 2);
|
||||
for character in trim_right(convertable_string).chars() {
|
||||
if char_is_seperator(&character) && found_real_char {
|
||||
new_word = true;
|
||||
} else if !found_real_char && is_not_alphanumeric(character) {
|
||||
continue;
|
||||
} else if character.is_numeric() {
|
||||
found_real_char = true;
|
||||
new_word = true;
|
||||
result.push(character);
|
||||
} else if last_char_lower_current_is_upper_or_new_word(new_word, last_char, character) {
|
||||
found_real_char = true;
|
||||
new_word = false;
|
||||
result = append_on_new_word(result, first_word, character, &camel_options);
|
||||
first_word = false;
|
||||
} else {
|
||||
found_real_char = true;
|
||||
last_char = character;
|
||||
result.push(character.to_ascii_lowercase());
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn append_on_new_word(mut result: String, first_word: bool, character: char, camel_options: &CamelOptions) -> String {
|
||||
if not_first_word_and_has_seperator(first_word, camel_options.has_seperator) {
|
||||
result.push(camel_options.injectable_char);
|
||||
}
|
||||
if first_word_or_not_inverted(first_word, camel_options.inverted) {
|
||||
result.push(character.to_ascii_uppercase());
|
||||
} else {
|
||||
result.push(character.to_ascii_lowercase());
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn not_first_word_and_has_seperator(first_word: bool, has_seperator: bool) -> bool {
|
||||
has_seperator && !first_word
|
||||
}
|
||||
|
||||
fn first_word_or_not_inverted(first_word: bool, inverted: bool) -> bool {
|
||||
!inverted || first_word
|
||||
}
|
||||
|
||||
|
||||
fn last_char_lower_current_is_upper_or_new_word(new_word: bool, last_char: char, character: char) -> bool{
|
||||
new_word ||
|
||||
((last_char.is_lowercase() && character.is_uppercase()) &&
|
||||
(last_char != ' '))
|
||||
}
|
||||
|
||||
fn char_is_seperator(character: &char) -> bool {
|
||||
is_not_alphanumeric(*character)
|
||||
}
|
||||
|
||||
fn trim_right(convertable_string: &str) -> &str {
|
||||
convertable_string.trim_end_matches(is_not_alphanumeric)
|
||||
}
|
||||
|
||||
fn is_not_alphanumeric(character: char) -> bool {
|
||||
!character.is_alphanumeric()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn requires_seperator(char_with_index: (usize, char), first_character: bool, convertable_string: &str) -> bool {
|
||||
!first_character &&
|
||||
char_is_uppercase(char_with_index.1) &&
|
||||
next_or_previous_char_is_lowercase(convertable_string, char_with_index.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn snake_like_no_seperator(mut accumlator: String, current_char: &char, case: &str) -> String {
|
||||
if case == "lower" {
|
||||
accumlator.push(current_char.to_ascii_lowercase());
|
||||
accumlator
|
||||
} else {
|
||||
accumlator.push(current_char.to_ascii_uppercase());
|
||||
accumlator
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn snake_like_with_seperator(mut accumlator: String, replace_with: &str, current_char: &char, case: &str) -> String {
|
||||
if case == "lower" {
|
||||
accumlator.push(replace_with.chars().nth(0).unwrap_or('_'));
|
||||
accumlator.push(current_char.to_ascii_lowercase());
|
||||
accumlator
|
||||
} else {
|
||||
accumlator.push(replace_with.chars().nth(0).unwrap_or('_'));
|
||||
accumlator.push(current_char.to_ascii_uppercase());
|
||||
accumlator
|
||||
}
|
||||
}
|
||||
|
||||
fn next_or_previous_char_is_lowercase(convertable_string: &str, char_with_index: usize) -> bool {
|
||||
convertable_string.chars().nth(char_with_index + 1).unwrap_or('A').is_lowercase() ||
|
||||
convertable_string.chars().nth(char_with_index - 1).unwrap_or('A').is_lowercase()
|
||||
}
|
||||
|
||||
fn char_is_uppercase(test_char: char) -> bool {
|
||||
test_char == test_char.to_ascii_uppercase()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trim_bad_chars() {
|
||||
assert_eq!("abc", trim_right("abc----^"))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trim_bad_chars_when_none_are_bad() {
|
||||
assert_eq!("abc", trim_right("abc"))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_not_alphanumeric_on_is_alphanumeric() {
|
||||
assert!(!is_not_alphanumeric('a'))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_not_alphanumeric_on_is_not_alphanumeric() {
|
||||
assert!(is_not_alphanumeric('_'))
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_char_is_uppercase_when_it_is() {
|
||||
assert_eq!(char_is_uppercase('A'), true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_char_is_uppercase_when_it_is_not() {
|
||||
assert_eq!(char_is_uppercase('a'), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_next_or_previous_char_is_lowercase_true() {
|
||||
assert_eq!(next_or_previous_char_is_lowercase("TestWWW", 3), true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_next_or_previous_char_is_lowercase_false() {
|
||||
assert_eq!(next_or_previous_char_is_lowercase("TestWWW", 5), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn snake_like_with_seperator_lowers() {
|
||||
assert_eq!(snake_like_with_seperator("".to_owned(), "^", &'c', "lower"), "^c".to_string())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn snake_like_with_seperator_upper() {
|
||||
assert_eq!(snake_like_with_seperator("".to_owned(), "^", &'c', "upper"), "^C".to_string())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn snake_like_no_seperator_lower() {
|
||||
assert_eq!(snake_like_no_seperator("".to_owned(), &'C', "lower"), "c".to_string())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn snake_like_no_seperator_upper() {
|
||||
assert_eq!(snake_like_no_seperator("".to_owned(), &'c', "upper"), "C".to_string())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn requires_seperator_upper_not_first_wrap_is_safe_current_upper() {
|
||||
assert_eq!(requires_seperator((2, 'C'), false, "test"), true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn requires_seperator_upper_not_first_wrap_is_safe_current_lower() {
|
||||
assert_eq!(requires_seperator((2, 'c'), false, "test"), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn requires_seperator_upper_first_wrap_is_safe_current_upper() {
|
||||
assert_eq!(requires_seperator((0, 'T'), true, "Test"), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn requires_seperator_upper_first_wrap_is_safe_current_lower() {
|
||||
assert_eq!(requires_seperator((0, 't'), true, "Test"), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn requires_seperator_upper_first_wrap_is_safe_current_lower_next_is_too() {
|
||||
assert_eq!(requires_seperator((0, 't'), true, "test"), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_char_is_seperator_dash() {
|
||||
assert_eq!(char_is_seperator(&'-'), true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_char_is_seperator_underscore() {
|
||||
assert_eq!(char_is_seperator(&'_'), true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_char_is_seperator_space() {
|
||||
assert_eq!(char_is_seperator(&' '), true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_char_is_seperator_when_not() {
|
||||
assert_eq!(char_is_seperator(&'A'), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_last_char_lower_current_is_upper_or_new_word_with_new_word() {
|
||||
assert_eq!(last_char_lower_current_is_upper_or_new_word(true, ' ', '-'), true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_last_char_lower_current_is_upper_or_new_word_last_char_space() {
|
||||
assert_eq!(last_char_lower_current_is_upper_or_new_word(false, ' ', '-'), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_last_char_lower_current_is_upper_or_new_word_last_char_lower_current_upper() {
|
||||
assert_eq!(last_char_lower_current_is_upper_or_new_word(false, 'a', 'A'), true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_last_char_lower_current_is_upper_or_new_word_last_char_upper_current_upper() {
|
||||
assert_eq!(last_char_lower_current_is_upper_or_new_word(false, 'A', 'A'), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_last_char_lower_current_is_upper_or_new_word_last_char_upper_current_lower() {
|
||||
assert_eq!(last_char_lower_current_is_upper_or_new_word(false, 'A', 'a'), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_first_word_or_not_inverted_with_first_word() {
|
||||
assert_eq!(first_word_or_not_inverted(true, false), true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_first_word_or_not_inverted_not_first_word_not_inverted() {
|
||||
assert_eq!(first_word_or_not_inverted(false, false), true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_first_word_or_not_inverted_not_first_word_is_inverted() {
|
||||
assert_eq!(first_word_or_not_inverted(false, true), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_not_first_word_and_has_seperator_is_first_and_not_seperator() {
|
||||
assert_eq!(not_first_word_and_has_seperator(true, false), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_not_first_word_and_has_seperator_not_first_and_not_seperator() {
|
||||
assert_eq!(not_first_word_and_has_seperator(false, false), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_not_first_word_and_has_seperator_not_first_and_has_seperator() {
|
||||
assert_eq!(not_first_word_and_has_seperator(false, true), true)
|
||||
}
|
|
@ -1,393 +0,0 @@
|
|||
#![deny(warnings)]
|
||||
use cases::case::*;
|
||||
#[cfg(feature = "heavyweight")]
|
||||
use string::singularize::to_singular;
|
||||
#[cfg(feature = "heavyweight")]
|
||||
/// Converts a `&str` to `ClassCase` `String`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::classcase::to_class_case;
|
||||
/// let mock_string: &str = "FooBar";
|
||||
/// let expected_string: String = "FooBar".to_string();
|
||||
/// let asserted_string: String = to_class_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::classcase::to_class_case;
|
||||
/// let mock_string: &str = "FooBars";
|
||||
/// let expected_string: String = "FooBar".to_string();
|
||||
/// let asserted_string: String = to_class_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::classcase::to_class_case;
|
||||
/// let mock_string: &str = "Foo Bar";
|
||||
/// let expected_string: String = "FooBar".to_string();
|
||||
/// let asserted_string: String = to_class_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::classcase::to_class_case;
|
||||
/// let mock_string: &str = "foo-bar";
|
||||
/// let expected_string: String = "FooBar".to_string();
|
||||
/// let asserted_string: String = to_class_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::classcase::to_class_case;
|
||||
/// let mock_string: &str = "fooBar";
|
||||
/// let expected_string: String = "FooBar".to_string();
|
||||
/// let asserted_string: String = to_class_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::classcase::to_class_case;
|
||||
/// let mock_string: &str = "FOO_BAR";
|
||||
/// let expected_string: String = "FooBar".to_string();
|
||||
/// let asserted_string: String = to_class_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::classcase::to_class_case;
|
||||
/// let mock_string: &str = "foo_bar";
|
||||
/// let expected_string: String = "FooBar".to_string();
|
||||
/// let asserted_string: String = to_class_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::classcase::to_class_case;
|
||||
/// let mock_string: &str = "foo_bars";
|
||||
/// let expected_string: String = "FooBar".to_string();
|
||||
/// let asserted_string: String = to_class_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::classcase::to_class_case;
|
||||
/// let mock_string: &str = "Foo bar";
|
||||
/// let expected_string: String = "FooBar".to_string();
|
||||
/// let asserted_string: String = to_class_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
pub fn to_class_case(non_class_case_string: &str) -> String {
|
||||
let options = CamelOptions {
|
||||
new_word: true,
|
||||
last_char: ' ',
|
||||
first_word: false,
|
||||
injectable_char: ' ',
|
||||
has_seperator: false,
|
||||
inverted: false,
|
||||
};
|
||||
let class_plural = to_case_camel_like(non_class_case_string, options);
|
||||
let split: (&str, &str) =
|
||||
class_plural.split_at(class_plural.rfind(char::is_uppercase).unwrap_or(0));
|
||||
format!("{}{}", split.0, to_singular(split.1))
|
||||
}
|
||||
|
||||
#[cfg(feature = "heavyweight")]
|
||||
/// Determines if a `&str` is `ClassCase` `bool`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::classcase::is_class_case;
|
||||
/// let mock_string: &str = "Foo";
|
||||
/// let asserted_bool: bool = is_class_case(mock_string);
|
||||
/// assert!(asserted_bool == true);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::classcase::is_class_case;
|
||||
/// let mock_string: &str = "foo";
|
||||
/// let asserted_bool: bool = is_class_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::classcase::is_class_case;
|
||||
/// let mock_string: &str = "FooBarIsAReallyReallyLongStrings";
|
||||
/// let asserted_bool: bool = is_class_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::classcase::is_class_case;
|
||||
/// let mock_string: &str = "FooBarIsAReallyReallyLongString";
|
||||
/// let asserted_bool: bool = is_class_case(mock_string);
|
||||
/// assert!(asserted_bool == true);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::classcase::is_class_case;
|
||||
/// let mock_string: &str = "foo-bar-string-that-is-really-really-long";
|
||||
/// let asserted_bool: bool = is_class_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::classcase::is_class_case;
|
||||
/// let mock_string: &str = "foo_bar_is_a_really_really_long_strings";
|
||||
/// let asserted_bool: bool = is_class_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::classcase::is_class_case;
|
||||
/// let mock_string: &str = "fooBarIsAReallyReallyLongString";
|
||||
/// let asserted_bool: bool = is_class_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::classcase::is_class_case;
|
||||
/// let mock_string: &str = "FOO_BAR_STRING_THAT_IS_REALLY_REALLY_LONG";
|
||||
/// let asserted_bool: bool = is_class_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::classcase::is_class_case;
|
||||
/// let mock_string: &str = "foo_bar_string_that_is_really_really_long";
|
||||
/// let asserted_bool: bool = is_class_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::classcase::is_class_case;
|
||||
/// let mock_string: &str = "Foo bar string that is really really long";
|
||||
/// let asserted_bool: bool = is_class_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::classcase::is_class_case;
|
||||
/// let mock_string: &str = "Foo Bar Is A Really Really Long String";
|
||||
/// let asserted_bool: bool = is_class_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
pub fn is_class_case(test_string: &str) -> bool {
|
||||
to_class_case(&test_string.clone()) == test_string
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", test))]
|
||||
#[cfg(feature = "heavyweight")]
|
||||
mod benchmarks {
|
||||
extern crate test;
|
||||
use self::test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_class_case(b: &mut Bencher) {
|
||||
b.iter(|| super::to_class_case("Foo bar"));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_is_class(b: &mut Bencher) {
|
||||
b.iter(|| super::is_class_case("Foo bar"));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_class_from_snake(b: &mut Bencher) {
|
||||
b.iter(|| super::to_class_case("foo_bar"));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "heavyweight")]
|
||||
mod tests {
|
||||
use ::to_class_case;
|
||||
use ::is_class_case;
|
||||
|
||||
#[test]
|
||||
fn from_camel_case() {
|
||||
let convertable_string: String = "fooBar".to_owned();
|
||||
let expected: String = "FooBar".to_owned();
|
||||
assert_eq!(to_class_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_pascal_case() {
|
||||
let convertable_string: String = "FooBar".to_owned();
|
||||
let expected: String = "FooBar".to_owned();
|
||||
assert_eq!(to_class_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_kebab_case() {
|
||||
let convertable_string: String = "foo-bar".to_owned();
|
||||
let expected: String = "FooBar".to_owned();
|
||||
assert_eq!(to_class_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_sentence_case() {
|
||||
let convertable_string: String = "Foo bar".to_owned();
|
||||
let expected: String = "FooBar".to_owned();
|
||||
assert_eq!(to_class_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_title_case() {
|
||||
let convertable_string: String = "Foo Bar".to_owned();
|
||||
let expected: String = "FooBar".to_owned();
|
||||
assert_eq!(to_class_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_train_case() {
|
||||
let convertable_string: String = "Foo-Bar".to_owned();
|
||||
let expected: String = "FooBar".to_owned();
|
||||
assert_eq!(to_class_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_screaming_class_case() {
|
||||
let convertable_string: String = "FOO_BAR".to_owned();
|
||||
let expected: String = "FooBar".to_owned();
|
||||
assert_eq!(to_class_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_snake_case() {
|
||||
let convertable_string: String = "foo_bar".to_owned();
|
||||
let expected: String = "FooBar".to_owned();
|
||||
assert_eq!(to_class_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_table_case() {
|
||||
let convertable_string: String = "foo_bars".to_owned();
|
||||
let expected: String = "FooBar".to_owned();
|
||||
assert_eq!(to_class_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_case_with_loads_of_space() {
|
||||
let convertable_string: String = "foo bar".to_owned();
|
||||
let expected: String = "FooBar".to_owned();
|
||||
assert_eq!(to_class_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_name_with_a_dot() {
|
||||
let convertable_string: String = "Robert C. Martin".to_owned();
|
||||
let expected: String = "RobertCMartin".to_owned();
|
||||
assert_eq!(to_class_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn random_text_with_bad_chars() {
|
||||
let convertable_string: String = "Random text with *(bad) chars".to_owned();
|
||||
let expected: String = "RandomTextWithBadChar".to_owned();
|
||||
assert_eq!(to_class_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trailing_bad_chars() {
|
||||
let convertable_string: String = "trailing bad_chars*(()())".to_owned();
|
||||
let expected: String = "TrailingBadChar".to_owned();
|
||||
assert_eq!(to_class_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leading_bad_chars() {
|
||||
let convertable_string: String = "-!#$%leading bad chars".to_owned();
|
||||
let expected: String = "LeadingBadChar".to_owned();
|
||||
assert_eq!(to_class_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wrapped_in_bad_chars() {
|
||||
let convertable_string: String = "-!#$%wrapped in bad chars&*^*&(&*^&(<><?>><?><>))".to_owned();
|
||||
let expected: String = "WrappedInBadChar".to_owned();
|
||||
assert_eq!(to_class_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_a_sign() {
|
||||
let convertable_string: String = "has a + sign".to_owned();
|
||||
let expected: String = "HasASign".to_owned();
|
||||
assert_eq!(to_class_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_class_case() {
|
||||
let convertable_string: String = "fooBar".to_owned();
|
||||
assert_eq!(is_class_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_pascal_case() {
|
||||
let convertable_string: String = "FooBar".to_owned();
|
||||
assert_eq!(is_class_case(&convertable_string), true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_kebab_case() {
|
||||
let convertable_string: String = "foo-bar".to_owned();
|
||||
assert_eq!(is_class_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_sentence_case() {
|
||||
let convertable_string: String = "Foo bar".to_owned();
|
||||
assert_eq!(is_class_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_title_case() {
|
||||
let convertable_string: String = "Foo Bar".to_owned();
|
||||
assert_eq!(is_class_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_train_case() {
|
||||
let convertable_string: String = "Foo-Bar".to_owned();
|
||||
assert_eq!(is_class_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_screaming_snake_case() {
|
||||
let convertable_string: String = "FOO_BAR".to_owned();
|
||||
assert_eq!(is_class_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_snake_case() {
|
||||
let convertable_string: String = "foo_bar".to_owned();
|
||||
assert_eq!(is_class_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_table_case() {
|
||||
let convertable_string: String = "FooBar".to_owned();
|
||||
assert_eq!(is_class_case(&convertable_string), true)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,262 +0,0 @@
|
|||
#![deny(warnings)]
|
||||
use cases::case::*;
|
||||
/// Determines if a `&str` is `kebab-case`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::kebabcase::is_kebab_case;
|
||||
/// let mock_string: &str = "foo-bar-string-that-is-really-really-long";
|
||||
/// let asserted_bool: bool = is_kebab_case(mock_string);
|
||||
/// assert!(asserted_bool == true);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::kebabcase::is_kebab_case;
|
||||
/// let mock_string: &str = "FooBarIsAReallyReallyLongString";
|
||||
/// let asserted_bool: bool = is_kebab_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::kebabcase::is_kebab_case;
|
||||
/// let mock_string: &str = "fooBarIsAReallyReallyLongString";
|
||||
/// let asserted_bool: bool = is_kebab_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::kebabcase::is_kebab_case;
|
||||
/// let mock_string: &str = "FOO_BAR_STRING_THAT_IS_REALLY_REALLY_LONG";
|
||||
/// let asserted_bool: bool = is_kebab_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::kebabcase::is_kebab_case;
|
||||
/// let mock_string: &str = "foo_bar_string_that_is_really_really_long";
|
||||
/// let asserted_bool: bool = is_kebab_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::kebabcase::is_kebab_case;
|
||||
/// let mock_string: &str = "Foo bar string that is really really long";
|
||||
/// let asserted_bool: bool = is_kebab_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::kebabcase::is_kebab_case;
|
||||
/// let mock_string: &str = "Foo Bar Is A Really Really Long String";
|
||||
/// let asserted_bool: bool = is_kebab_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
pub fn is_kebab_case(test_string: &str) -> bool {
|
||||
test_string == to_kebab_case(test_string.clone())
|
||||
}
|
||||
|
||||
/// Converts a `&str` to `kebab-case` `String`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::kebabcase::to_kebab_case;
|
||||
/// let mock_string: &str = "foo-bar";
|
||||
/// let expected_string: String = "foo-bar".to_string();
|
||||
/// let asserted_string: String = to_kebab_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::kebabcase::to_kebab_case;
|
||||
/// let mock_string: &str = "FOO_BAR";
|
||||
/// let expected_string: String = "foo-bar".to_string();
|
||||
/// let asserted_string: String = to_kebab_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::kebabcase::to_kebab_case;
|
||||
/// let mock_string: &str = "foo_bar";
|
||||
/// let expected_string: String = "foo-bar".to_string();
|
||||
/// let asserted_string: String = to_kebab_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::kebabcase::to_kebab_case;
|
||||
/// let mock_string: &str = "Foo Bar";
|
||||
/// let expected_string: String = "foo-bar".to_string();
|
||||
/// let asserted_string: String = to_kebab_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::kebabcase::to_kebab_case;
|
||||
/// let mock_string: &str = "Foo bar";
|
||||
/// let expected_string: String = "foo-bar".to_string();
|
||||
/// let asserted_string: String = to_kebab_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::kebabcase::to_kebab_case;
|
||||
/// let mock_string: &str = "FooBar";
|
||||
/// let expected_string: String = "foo-bar".to_string();
|
||||
/// let asserted_string: String = to_kebab_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::kebabcase::to_kebab_case;
|
||||
/// let mock_string: &str = "fooBar";
|
||||
/// let expected_string: String = "foo-bar".to_string();
|
||||
/// let asserted_string: String = to_kebab_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
pub fn to_kebab_case(non_kebab_case_string: &str) -> String {
|
||||
to_case_snake_like(non_kebab_case_string, "-", "lower")
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", test))]
|
||||
mod benchmarks {
|
||||
extern crate test;
|
||||
use self::test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_kebab(b: &mut Bencher) {
|
||||
b.iter(|| super::to_kebab_case("Foo bar"));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_is_kebab(b: &mut Bencher) {
|
||||
b.iter(|| super::is_kebab_case("Foo bar"));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_kebab_from_snake(b: &mut Bencher) {
|
||||
b.iter(|| super::to_kebab_case("test_test_test"));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ::to_kebab_case;
|
||||
use ::is_kebab_case;
|
||||
|
||||
#[test]
|
||||
fn from_camel_case() {
|
||||
let convertable_string: String = "fooBar".to_owned();
|
||||
let expected: String = "foo-bar".to_owned();
|
||||
assert_eq!(to_kebab_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_pascal_case() {
|
||||
let convertable_string: String = "FooBar".to_owned();
|
||||
let expected: String = "foo-bar".to_owned();
|
||||
assert_eq!(to_kebab_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_kebab_case() {
|
||||
let convertable_string: String = "foo-bar".to_owned();
|
||||
let expected: String = "foo-bar".to_owned();
|
||||
assert_eq!(to_kebab_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_sentence_case() {
|
||||
let convertable_string: String = "Foo bar".to_owned();
|
||||
let expected: String = "foo-bar".to_owned();
|
||||
assert_eq!(to_kebab_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_title_case() {
|
||||
let convertable_string: String = "Foo Bar".to_owned();
|
||||
let expected: String = "foo-bar".to_owned();
|
||||
assert_eq!(to_kebab_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_train_case() {
|
||||
let convertable_string: String = "Foo-Bar".to_owned();
|
||||
let expected: String = "foo-bar".to_owned();
|
||||
assert_eq!(to_kebab_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_screaming_snake_case() {
|
||||
let convertable_string: String = "FOO_BAR".to_owned();
|
||||
let expected: String = "foo-bar".to_owned();
|
||||
assert_eq!(to_kebab_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_snake_case() {
|
||||
let convertable_string: String = "foo_bar".to_owned();
|
||||
let expected: String = "foo-bar".to_owned();
|
||||
assert_eq!(to_kebab_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_camel_case() {
|
||||
let convertable_string: String = "fooBar".to_owned();
|
||||
assert_eq!(is_kebab_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_pascal_case() {
|
||||
let convertable_string: String = "FooBar".to_owned();
|
||||
assert_eq!(is_kebab_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_kebab_case() {
|
||||
let convertable_string: String = "foo-bar".to_owned();
|
||||
assert_eq!(is_kebab_case(&convertable_string), true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_sentence_case() {
|
||||
let convertable_string: String = "Foo bar".to_owned();
|
||||
assert_eq!(is_kebab_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_title_case() {
|
||||
let convertable_string: String = "Foo Bar".to_owned();
|
||||
assert_eq!(is_kebab_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_train_case() {
|
||||
let convertable_string: String = "Foo-Bar".to_owned();
|
||||
assert_eq!(is_kebab_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_screaming_snake_case() {
|
||||
let convertable_string: String = "FOO_BAR".to_owned();
|
||||
assert_eq!(is_kebab_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_snake_case() {
|
||||
let convertable_string: String = "foo_bar".to_owned();
|
||||
assert_eq!(is_kebab_case(&convertable_string), false)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
mod case;
|
||||
/// Provides conversion to and detection of class case strings.
|
||||
///
|
||||
/// This version singularizes strings.
|
||||
///
|
||||
/// Example string `ClassCase`
|
||||
pub mod classcase;
|
||||
|
||||
/// Provides conversion to and detection of camel case strings.
|
||||
///
|
||||
/// Example string `camelCase`
|
||||
pub mod camelcase;
|
||||
|
||||
/// Provides conversion to and detection of snake case strings.
|
||||
///
|
||||
/// Example string `snake_case`
|
||||
pub mod snakecase;
|
||||
|
||||
/// Provides conversion to and detection of screaming snake case strings.
|
||||
///
|
||||
/// Example string `SCREAMING_SNAKE_CASE`
|
||||
pub mod screamingsnakecase;
|
||||
|
||||
/// Provides conversion to and detection of kebab case strings.
|
||||
///
|
||||
/// Example string `kebab-case`
|
||||
pub mod kebabcase;
|
||||
|
||||
/// Provides conversion to and detection of train case strings.
|
||||
///
|
||||
/// Example string `Train-Case`
|
||||
pub mod traincase;
|
||||
|
||||
/// Provides conversion to and detection of sentence case strings.
|
||||
///
|
||||
/// Example string `Sentence case`
|
||||
pub mod sentencecase;
|
||||
|
||||
/// Provides conversion to and detection of title case strings.
|
||||
///
|
||||
/// Example string `Title Case`
|
||||
pub mod titlecase;
|
||||
|
||||
/// Provides conversion to and detection of table case strings.
|
||||
///
|
||||
/// Example string `table_cases`
|
||||
pub mod tablecase;
|
||||
|
||||
/// Provides conversion to pascal case strings.
|
||||
///
|
||||
/// Example string `PascalCase`
|
||||
pub mod pascalcase;
|
|
@ -1,360 +0,0 @@
|
|||
#![deny(warnings)]
|
||||
use cases::case::*;
|
||||
/// Converts a `&str` to pascalCase `String`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::pascalcase::to_pascal_case;
|
||||
/// let mock_string: &str = "fooBar";
|
||||
/// let expected_string: String = "FooBar".to_string();
|
||||
/// let asserted_string: String = to_pascal_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::pascalcase::to_pascal_case;
|
||||
/// let mock_string: &str = "FOO_BAR";
|
||||
/// let expected_string: String = "FooBar".to_string();
|
||||
/// let asserted_string: String = to_pascal_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::pascalcase::to_pascal_case;
|
||||
/// let mock_string: &str = "Foo Bar";
|
||||
/// let expected_string: String = "FooBar".to_string();
|
||||
/// let asserted_string: String = to_pascal_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::pascalcase::to_pascal_case;
|
||||
/// let mock_string: &str = "foo_bar";
|
||||
/// let expected_string: String = "FooBar".to_string();
|
||||
/// let asserted_string: String = to_pascal_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::pascalcase::to_pascal_case;
|
||||
/// let mock_string: &str = "Foo bar";
|
||||
/// let expected_string: String = "FooBar".to_string();
|
||||
/// let asserted_string: String = to_pascal_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::pascalcase::to_pascal_case;
|
||||
/// let mock_string: &str = "foo-bar";
|
||||
/// let expected_string: String = "FooBar".to_string();
|
||||
/// let asserted_string: String = to_pascal_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::pascalcase::to_pascal_case;
|
||||
/// let mock_string: &str = "FooBar";
|
||||
/// let expected_string: String = "FooBar".to_string();
|
||||
/// let asserted_string: String = to_pascal_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::pascalcase::to_pascal_case;
|
||||
/// let mock_string: &str = "FooBar3";
|
||||
/// let expected_string: String = "FooBar3".to_string();
|
||||
/// let asserted_string: String = to_pascal_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
pub fn to_pascal_case(non_pascalized_string: &str) -> String {
|
||||
let options = CamelOptions {
|
||||
new_word: true,
|
||||
last_char: ' ',
|
||||
first_word: false,
|
||||
injectable_char: ' ',
|
||||
has_seperator: false,
|
||||
inverted: false,
|
||||
};
|
||||
to_case_camel_like(non_pascalized_string, options)
|
||||
}
|
||||
|
||||
/// Determines if a `&str` is pascalCase bool``
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::pascalcase::is_pascal_case;
|
||||
/// let mock_string: &str = "Foo";
|
||||
/// let asserted_bool: bool = is_pascal_case(mock_string);
|
||||
/// assert!(asserted_bool == true);
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::pascalcase::is_pascal_case;
|
||||
/// let mock_string: &str = "foo";
|
||||
/// let asserted_bool: bool = is_pascal_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::pascalcase::is_pascal_case;
|
||||
/// let mock_string: &str = "foo-bar-string-that-is-really-really-long";
|
||||
/// let asserted_bool: bool = is_pascal_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::pascalcase::is_pascal_case;
|
||||
/// let mock_string: &str = "FooBarIsAReallyReallyLongString";
|
||||
/// let asserted_bool: bool = is_pascal_case(mock_string);
|
||||
/// assert!(asserted_bool == true);
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::pascalcase::is_pascal_case;
|
||||
/// let mock_string: &str = "FooBarIsAReallyReally3LongString";
|
||||
/// let asserted_bool: bool = is_pascal_case(mock_string);
|
||||
/// assert!(asserted_bool == true);
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::pascalcase::is_pascal_case;
|
||||
/// let mock_string: &str = "FooBarIsAReallyReallyLongString";
|
||||
/// let asserted_bool: bool = is_pascal_case(mock_string);
|
||||
/// assert!(asserted_bool == true);
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::pascalcase::is_pascal_case;
|
||||
/// let mock_string: &str = "FOO_BAR_STRING_THAT_IS_REALLY_REALLY_LONG";
|
||||
/// let asserted_bool: bool = is_pascal_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::pascalcase::is_pascal_case;
|
||||
/// let mock_string: &str = "foo_bar_string_that_is_really_really_long";
|
||||
/// let asserted_bool: bool = is_pascal_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::pascalcase::is_pascal_case;
|
||||
/// let mock_string: &str = "Foo bar string that is really really long";
|
||||
/// let asserted_bool: bool = is_pascal_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::pascalcase::is_pascal_case;
|
||||
/// let mock_string: &str = "Foo Bar Is A Really Really Long String";
|
||||
/// let asserted_bool: bool = is_pascal_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
/// ```
|
||||
pub fn is_pascal_case(test_string: &str) -> bool {
|
||||
to_pascal_case(test_string.clone()) == test_string
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", test))]
|
||||
mod benchmarks {
|
||||
extern crate test;
|
||||
use self::test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_pascal0(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let test_string = "Foo bar";
|
||||
super::to_pascal_case(test_string)
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_pascal1(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let test_string = "foo_bar";
|
||||
super::to_pascal_case(test_string)
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_pascal2(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let test_string = "fooBar";
|
||||
super::to_pascal_case(test_string)
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_is_pascal(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let test_string: &str = "Foo bar";
|
||||
super::is_pascal_case(test_string)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ::to_pascal_case;
|
||||
use ::is_pascal_case;
|
||||
|
||||
#[test]
|
||||
fn from_camel_case() {
|
||||
let convertable_string: String = "fooBar".to_owned();
|
||||
let expected: String = "FooBar".to_owned();
|
||||
assert_eq!(to_pascal_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_pascal_case() {
|
||||
let convertable_string: String = "FooBar".to_owned();
|
||||
let expected: String = "FooBar".to_owned();
|
||||
assert_eq!(to_pascal_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_kebab_case() {
|
||||
let convertable_string: String = "foo-bar".to_owned();
|
||||
let expected: String = "FooBar".to_owned();
|
||||
assert_eq!(to_pascal_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_sentence_case() {
|
||||
let convertable_string: String = "Foo bar".to_owned();
|
||||
let expected: String = "FooBar".to_owned();
|
||||
assert_eq!(to_pascal_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_title_case() {
|
||||
let convertable_string: String = "Foo Bar".to_owned();
|
||||
let expected: String = "FooBar".to_owned();
|
||||
assert_eq!(to_pascal_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_train_case() {
|
||||
let convertable_string: String = "Foo-Bar".to_owned();
|
||||
let expected: String = "FooBar".to_owned();
|
||||
assert_eq!(to_pascal_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_screaming_snake_case() {
|
||||
let convertable_string: String = "FOO_BAR".to_owned();
|
||||
let expected: String = "FooBar".to_owned();
|
||||
assert_eq!(to_pascal_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_snake_case() {
|
||||
let convertable_string: String = "foo_bar".to_owned();
|
||||
let expected: String = "FooBar".to_owned();
|
||||
assert_eq!(to_pascal_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_case_with_loads_of_space() {
|
||||
let convertable_string: String = "foo bar".to_owned();
|
||||
let expected: String = "FooBar".to_owned();
|
||||
assert_eq!(to_pascal_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_name_with_a_dot() {
|
||||
let convertable_string: String = "Robert C. Martin".to_owned();
|
||||
let expected: String = "RobertCMartin".to_owned();
|
||||
assert_eq!(to_pascal_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn random_text_with_bad_chars() {
|
||||
let convertable_string: String = "Random text with *(bad) chars".to_owned();
|
||||
let expected: String = "RandomTextWithBadChars".to_owned();
|
||||
assert_eq!(to_pascal_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trailing_bad_chars() {
|
||||
let convertable_string: String = "trailing bad_chars*(()())".to_owned();
|
||||
let expected: String = "TrailingBadChars".to_owned();
|
||||
assert_eq!(to_pascal_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leading_bad_chars() {
|
||||
let convertable_string: String = "-!#$%leading bad chars".to_owned();
|
||||
let expected: String = "LeadingBadChars".to_owned();
|
||||
assert_eq!(to_pascal_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wrapped_in_bad_chars() {
|
||||
let convertable_string: String = "-!#$%wrapped in bad chars&*^*&(&*^&(<><?>><?><>))".to_owned();
|
||||
let expected: String = "WrappedInBadChars".to_owned();
|
||||
assert_eq!(to_pascal_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_a_sign() {
|
||||
let convertable_string: String = "has a + sign".to_owned();
|
||||
let expected: String = "HasASign".to_owned();
|
||||
assert_eq!(to_pascal_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_camel_case() {
|
||||
let convertable_string: String = "fooBar".to_owned();
|
||||
assert_eq!(is_pascal_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_pascal_case() {
|
||||
let convertable_string: String = "FooBar".to_owned();
|
||||
assert_eq!(is_pascal_case(&convertable_string), true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_kebab_case() {
|
||||
let convertable_string: String = "foo-bar".to_owned();
|
||||
assert_eq!(is_pascal_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_sentence_case() {
|
||||
let convertable_string: String = "Foo bar".to_owned();
|
||||
assert_eq!(is_pascal_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_title_case() {
|
||||
let convertable_string: String = "Foo Bar".to_owned();
|
||||
assert_eq!(is_pascal_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_train_case() {
|
||||
let convertable_string: String = "Foo-Bar".to_owned();
|
||||
assert_eq!(is_pascal_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_screaming_snake_case() {
|
||||
let convertable_string: String = "FOO_BAR".to_owned();
|
||||
assert_eq!(is_pascal_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_snake_case() {
|
||||
let convertable_string: String = "foo_bar".to_owned();
|
||||
assert_eq!(is_pascal_case(&convertable_string), false)
|
||||
}
|
||||
}
|
|
@ -1,253 +0,0 @@
|
|||
#![deny(warnings)]
|
||||
use cases::case::*;
|
||||
/// Converts a `&str` to `SCREAMING_SNAKE_CASE` `String`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::screamingsnakecase::to_screaming_snake_case;
|
||||
/// let mock_string: &str = "foo_bar";
|
||||
/// let expected_string: String = "FOO_BAR".to_string();
|
||||
/// let asserted_string: String = to_screaming_snake_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::screamingsnakecase::to_screaming_snake_case;
|
||||
/// let mock_string: &str = "HTTP Foo bar";
|
||||
/// let expected_string: String = "HTTP_FOO_BAR".to_string();
|
||||
/// let asserted_string: String = to_screaming_snake_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::screamingsnakecase::to_screaming_snake_case;
|
||||
/// let mock_string: &str = "Foo bar";
|
||||
/// let expected_string: String = "FOO_BAR".to_string();
|
||||
/// let asserted_string: String = to_screaming_snake_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::screamingsnakecase::to_screaming_snake_case;
|
||||
/// let mock_string: &str = "Foo Bar";
|
||||
/// let expected_string: String = "FOO_BAR".to_string();
|
||||
/// let asserted_string: String = to_screaming_snake_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::screamingsnakecase::to_screaming_snake_case;
|
||||
/// let mock_string: &str = "FooBar";
|
||||
/// let expected_string: String = "FOO_BAR".to_string();
|
||||
/// let asserted_string: String = to_screaming_snake_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::screamingsnakecase::to_screaming_snake_case;
|
||||
/// let mock_string: &str = "fooBar";
|
||||
/// let expected_string: String = "FOO_BAR".to_string();
|
||||
/// let asserted_string: String = to_screaming_snake_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::screamingsnakecase::to_screaming_snake_case;
|
||||
/// let mock_string: &str = "fooBar3";
|
||||
/// let expected_string: String = "FOO_BAR_3".to_string();
|
||||
/// let asserted_string: String = to_screaming_snake_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
pub fn to_screaming_snake_case(non_snake_case_string: &str) -> String {
|
||||
to_case_snake_like(non_snake_case_string, "_", "upper")
|
||||
}
|
||||
|
||||
/// Determines of a `&str` is `SCREAMING_SNAKE_CASE`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::screamingsnakecase::is_screaming_snake_case;
|
||||
/// let mock_string: &str = "Foo bar string that is really really long";
|
||||
/// let asserted_bool: bool = is_screaming_snake_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::screamingsnakecase::is_screaming_snake_case;
|
||||
/// let mock_string: &str = "foo-bar-string-that-is-really-really-long";
|
||||
/// let asserted_bool: bool = is_screaming_snake_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::screamingsnakecase::is_screaming_snake_case;
|
||||
/// let mock_string: &str = "FooBarIsAReallyReallyLongString";
|
||||
/// let asserted_bool: bool = is_screaming_snake_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::screamingsnakecase::is_screaming_snake_case;
|
||||
/// let mock_string: &str = "Foo Bar Is A Really Really Long String";
|
||||
/// let asserted_bool: bool = is_screaming_snake_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::screamingsnakecase::is_screaming_snake_case;
|
||||
/// let mock_string: &str = "fooBarIsAReallyReallyLongString";
|
||||
/// let asserted_bool: bool = is_screaming_snake_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::screamingsnakecase::is_screaming_snake_case;
|
||||
/// let mock_string: &str = "FOO_BAR_STRING_THAT_IS_REALLY_REALLY_LONG";
|
||||
/// let asserted_bool: bool = is_screaming_snake_case(mock_string);
|
||||
/// assert!(asserted_bool == true);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::screamingsnakecase::is_screaming_snake_case;
|
||||
/// let mock_string: &str = "FOO_BAR1_STRING_THAT_IS_REALLY_REALLY_LONG";
|
||||
/// let asserted_bool: bool = is_screaming_snake_case(mock_string);
|
||||
/// assert!(asserted_bool == true);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::screamingsnakecase::is_screaming_snake_case;
|
||||
/// let mock_string: &str = "FOO_BAR_1_STRING_THAT_IS_REALLY_REALLY_LONG";
|
||||
/// let asserted_bool: bool = is_screaming_snake_case(mock_string);
|
||||
/// assert!(asserted_bool == true);
|
||||
///
|
||||
/// ```
|
||||
pub fn is_screaming_snake_case(test_string: &str) -> bool {
|
||||
test_string == to_screaming_snake_case(test_string.clone())
|
||||
}
|
||||
|
||||
|
||||
#[cfg(all(feature = "unstable", test))]
|
||||
mod benchmarks {
|
||||
extern crate test;
|
||||
use self::test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_screaming_snake(b: &mut Bencher) {
|
||||
b.iter(|| super::to_screaming_snake_case("Foo bar"));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_is_screaming_snake(b: &mut Bencher) {
|
||||
b.iter(|| super::is_screaming_snake_case("Foo bar"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ::to_screaming_snake_case;
|
||||
use ::is_screaming_snake_case;
|
||||
|
||||
#[test]
|
||||
fn from_camel_case() {
|
||||
let convertable_string: String = "fooBar".to_owned();
|
||||
let expected: String = "FOO_BAR".to_owned();
|
||||
assert_eq!(to_screaming_snake_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_pascal_case() {
|
||||
let convertable_string: String = "FooBar".to_owned();
|
||||
let expected: String = "FOO_BAR".to_owned();
|
||||
assert_eq!(to_screaming_snake_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_kebab_case() {
|
||||
let convertable_string: String = "foo-bar".to_owned();
|
||||
let expected: String = "FOO_BAR".to_owned();
|
||||
assert_eq!(to_screaming_snake_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_sentence_case() {
|
||||
let convertable_string: String = "Foo bar".to_owned();
|
||||
let expected: String = "FOO_BAR".to_owned();
|
||||
assert_eq!(to_screaming_snake_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_title_case() {
|
||||
let convertable_string: String = "Foo Bar".to_owned();
|
||||
let expected: String = "FOO_BAR".to_owned();
|
||||
assert_eq!(to_screaming_snake_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_train_case() {
|
||||
let convertable_string: String = "Foo-Bar".to_owned();
|
||||
let expected: String = "FOO_BAR".to_owned();
|
||||
assert_eq!(to_screaming_snake_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_screaming_screaming_snake_case() {
|
||||
let convertable_string: String = "FOO_BAR".to_owned();
|
||||
let expected: String = "FOO_BAR".to_owned();
|
||||
assert_eq!(to_screaming_snake_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_snake_case() {
|
||||
let convertable_string: String = "foo_bar".to_owned();
|
||||
let expected: String = "FOO_BAR".to_owned();
|
||||
assert_eq!(to_screaming_snake_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_camel_case() {
|
||||
let convertable_string: String = "fooBar".to_owned();
|
||||
assert_eq!(is_screaming_snake_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_pascal_case() {
|
||||
let convertable_string: String = "FooBar".to_owned();
|
||||
assert_eq!(is_screaming_snake_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_kebab_case() {
|
||||
let convertable_string: String = "foo-bar".to_owned();
|
||||
assert_eq!(is_screaming_snake_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_sentence_case() {
|
||||
let convertable_string: String = "Foo bar".to_owned();
|
||||
assert_eq!(is_screaming_snake_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_title_case() {
|
||||
let convertable_string: String = "Foo Bar".to_owned();
|
||||
assert_eq!(is_screaming_snake_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_train_case() {
|
||||
let convertable_string: String = "Foo-Bar".to_owned();
|
||||
assert_eq!(is_screaming_snake_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_screaming_screaming_snake_case() {
|
||||
let convertable_string: String = "FOO_BAR".to_owned();
|
||||
assert_eq!(is_screaming_snake_case(&convertable_string), true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_snake_case() {
|
||||
let convertable_string: String = "foo_bar".to_owned();
|
||||
assert_eq!(is_screaming_snake_case(&convertable_string), false)
|
||||
}
|
||||
}
|
|
@ -1,313 +0,0 @@
|
|||
#![deny(warnings)]
|
||||
use cases::case::*;
|
||||
/// Converts a `&str` to `Sentence case` `String`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::sentencecase::to_sentence_case;
|
||||
/// let mock_string: &str = "Foo bar";
|
||||
/// let expected_string: String = "Foo bar".to_string();
|
||||
/// let asserted_string: String = to_sentence_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::sentencecase::to_sentence_case;
|
||||
/// let mock_string: &str = "FooBar";
|
||||
/// let expected_string: String = "Foo bar".to_string();
|
||||
/// let asserted_string: String = to_sentence_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::sentencecase::to_sentence_case;
|
||||
/// let mock_string: &str = "fooBar";
|
||||
/// let expected_string: String = "Foo bar".to_string();
|
||||
/// let asserted_string: String = to_sentence_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::sentencecase::to_sentence_case;
|
||||
/// let mock_string: &str = "FOO_BAR";
|
||||
/// let expected_string: String = "Foo bar".to_string();
|
||||
/// let asserted_string: String = to_sentence_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::sentencecase::to_sentence_case;
|
||||
/// let mock_string: &str = "foo_bar";
|
||||
/// let expected_string: String = "Foo bar".to_string();
|
||||
/// let asserted_string: String = to_sentence_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::sentencecase::to_sentence_case;
|
||||
/// let mock_string: &str = "foo-bar";
|
||||
/// let expected_string: String = "Foo bar".to_string();
|
||||
/// let asserted_string: String = to_sentence_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
pub fn to_sentence_case(non_sentence_case_string: &str) -> String {
|
||||
let options = CamelOptions {
|
||||
new_word: true,
|
||||
last_char: ' ',
|
||||
first_word: true,
|
||||
injectable_char: ' ',
|
||||
has_seperator: true,
|
||||
inverted: true,
|
||||
};
|
||||
to_case_camel_like(non_sentence_case_string, options)
|
||||
}
|
||||
/// Determines of a `&str` is `Sentence case`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::sentencecase::is_sentence_case;
|
||||
/// let mock_string: &str = "foo-bar-string-that-is-really-really-long";
|
||||
/// let asserted_bool: bool = is_sentence_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::sentencecase::is_sentence_case;
|
||||
/// let mock_string: &str = "FooBarIsAReallyReallyLongString";
|
||||
/// let asserted_bool: bool = is_sentence_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::sentencecase::is_sentence_case;
|
||||
/// let mock_string: &str = "fooBarIsAReallyReallyLongString";
|
||||
/// let asserted_bool: bool = is_sentence_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::sentencecase::is_sentence_case;
|
||||
/// let mock_string: &str = "Foo Bar Is A Really Really Long String";
|
||||
/// let asserted_bool: bool = is_sentence_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::sentencecase::is_sentence_case;
|
||||
/// let mock_string: &str = "FOO_BAR_STRING_THAT_IS_REALLY_REALLY_LONG";
|
||||
/// let asserted_bool: bool = is_sentence_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::sentencecase::is_sentence_case;
|
||||
/// let mock_string: &str = "foo_bar_string_that_is_really_really_long";
|
||||
/// let asserted_bool: bool = is_sentence_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::sentencecase::is_sentence_case;
|
||||
/// let mock_string: &str = "Foo";
|
||||
/// let asserted_bool: bool = is_sentence_case(mock_string);
|
||||
/// assert!(asserted_bool == true);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::sentencecase::is_sentence_case;
|
||||
/// let mock_string: &str = "foo";
|
||||
/// let asserted_bool: bool = is_sentence_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::sentencecase::is_sentence_case;
|
||||
/// let mock_string: &str = "Foo bar string that is really really long";
|
||||
/// let asserted_bool: bool = is_sentence_case(mock_string);
|
||||
/// assert!(asserted_bool == true);
|
||||
///
|
||||
/// ```
|
||||
pub fn is_sentence_case(test_string: &str) -> bool {
|
||||
test_string == to_sentence_case(test_string.clone())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", test))]
|
||||
mod benchmarks {
|
||||
extern crate test;
|
||||
use self::test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_sentence(b: &mut Bencher) {
|
||||
b.iter(|| super::to_sentence_case("Foo BAR"));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_is_sentence(b: &mut Bencher) {
|
||||
b.iter(|| super::is_sentence_case("Foo bar"));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_sentence_from_snake(b: &mut Bencher) {
|
||||
b.iter(|| super::to_sentence_case("foo_bar"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ::to_sentence_case;
|
||||
use ::is_sentence_case;
|
||||
|
||||
#[test]
|
||||
fn from_camel_case() {
|
||||
let convertable_string: String = "fooBar".to_owned();
|
||||
let expected: String = "Foo bar".to_owned();
|
||||
assert_eq!(to_sentence_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_pascal_case() {
|
||||
let convertable_string: String = "FooBar".to_owned();
|
||||
let expected: String = "Foo bar".to_owned();
|
||||
assert_eq!(to_sentence_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_kebab_case() {
|
||||
let convertable_string: String = "foo-bar".to_owned();
|
||||
let expected: String = "Foo bar".to_owned();
|
||||
assert_eq!(to_sentence_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_sentence_case() {
|
||||
let convertable_string: String = "Foo bar".to_owned();
|
||||
let expected: String = "Foo bar".to_owned();
|
||||
assert_eq!(to_sentence_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_title_case() {
|
||||
let convertable_string: String = "Foo Bar".to_owned();
|
||||
let expected: String = "Foo bar".to_owned();
|
||||
assert_eq!(to_sentence_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_train_case() {
|
||||
let convertable_string: String = "Foo-Bar".to_owned();
|
||||
let expected: String = "Foo bar".to_owned();
|
||||
assert_eq!(to_sentence_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_screaming_snake_case() {
|
||||
let convertable_string: String = "FOO_BAR".to_owned();
|
||||
let expected: String = "Foo bar".to_owned();
|
||||
assert_eq!(to_sentence_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_snake_case() {
|
||||
let convertable_string: String = "foo_bar".to_owned();
|
||||
let expected: String = "Foo bar".to_owned();
|
||||
assert_eq!(to_sentence_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_case_with_loads_of_space() {
|
||||
let convertable_string: String = "foo bar".to_owned();
|
||||
let expected: String = "Foo bar".to_owned();
|
||||
assert_eq!(to_sentence_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_name_with_a_dot() {
|
||||
let convertable_string: String = "Robert C. Martin".to_owned();
|
||||
let expected: String = "Robert c martin".to_owned();
|
||||
assert_eq!(to_sentence_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn random_text_with_bad_chars() {
|
||||
let convertable_string: String = "Random text with *(bad) chars".to_owned();
|
||||
let expected: String = "Random text with bad chars".to_owned();
|
||||
assert_eq!(to_sentence_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trailing_bad_chars() {
|
||||
let convertable_string: String = "trailing bad_chars*(()())".to_owned();
|
||||
let expected: String = "Trailing bad chars".to_owned();
|
||||
assert_eq!(to_sentence_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leading_bad_chars() {
|
||||
let convertable_string: String = "-!#$%leading bad chars".to_owned();
|
||||
let expected: String = "Leading bad chars".to_owned();
|
||||
assert_eq!(to_sentence_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wrapped_in_bad_chars() {
|
||||
let convertable_string: String = "-!#$%wrapped in bad chars&*^*&(&*^&(<><?>><?><>))".to_owned();
|
||||
let expected: String = "Wrapped in bad chars".to_owned();
|
||||
assert_eq!(to_sentence_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_a_sign() {
|
||||
let convertable_string: String = "has a + sign".to_owned();
|
||||
let expected: String = "Has a sign".to_owned();
|
||||
assert_eq!(to_sentence_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_camel_case() {
|
||||
let convertable_string: String = "fooBar".to_owned();
|
||||
assert_eq!(is_sentence_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_pascal_case() {
|
||||
let convertable_string: String = "FooBar".to_owned();
|
||||
assert_eq!(is_sentence_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_kebab_case() {
|
||||
let convertable_string: String = "foo-bar".to_owned();
|
||||
assert_eq!(is_sentence_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_sentence_case() {
|
||||
let convertable_string: String = "Foo bar".to_owned();
|
||||
assert_eq!(is_sentence_case(&convertable_string), true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_title_case() {
|
||||
let convertable_string: String = "Foo Bar".to_owned();
|
||||
assert_eq!(is_sentence_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_train_case() {
|
||||
let convertable_string: String = "Foo-Bar".to_owned();
|
||||
assert_eq!(is_sentence_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_screaming_snake_case() {
|
||||
let convertable_string: String = "FOO_BAR".to_owned();
|
||||
assert_eq!(is_sentence_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_snake_case() {
|
||||
let convertable_string: String = "foo_bar".to_owned();
|
||||
assert_eq!(is_sentence_case(&convertable_string), false)
|
||||
}
|
||||
}
|
|
@ -1,334 +0,0 @@
|
|||
#![deny(warnings)]
|
||||
use cases::case::*;
|
||||
/// Converts a `&str` to `snake_case` `String`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::snakecase::to_snake_case;
|
||||
/// let mock_string: &str = "foo_bar";
|
||||
/// let expected_string: String = "foo_bar".to_string();
|
||||
/// let asserted_string: String = to_snake_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::snakecase::to_snake_case;
|
||||
/// let mock_string: &str = "HTTP Foo bar";
|
||||
/// let expected_string: String = "http_foo_bar".to_string();
|
||||
/// let asserted_string: String = to_snake_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::snakecase::to_snake_case;
|
||||
/// let mock_string: &str = "HTTPFooBar";
|
||||
/// let expected_string: String = "http_foo_bar".to_string();
|
||||
/// let asserted_string: String = to_snake_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::snakecase::to_snake_case;
|
||||
/// let mock_string: &str = "Foo bar";
|
||||
/// let expected_string: String = "foo_bar".to_string();
|
||||
/// let asserted_string: String = to_snake_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::snakecase::to_snake_case;
|
||||
/// let mock_string: &str = "Foo Bar";
|
||||
/// let expected_string: String = "foo_bar".to_string();
|
||||
/// let asserted_string: String = to_snake_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::snakecase::to_snake_case;
|
||||
/// let mock_string: &str = "FooBar";
|
||||
/// let expected_string: String = "foo_bar".to_string();
|
||||
/// let asserted_string: String = to_snake_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::snakecase::to_snake_case;
|
||||
/// let mock_string: &str = "FOO_BAR";
|
||||
/// let expected_string: String = "foo_bar".to_string();
|
||||
/// let asserted_string: String = to_snake_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::snakecase::to_snake_case;
|
||||
/// let mock_string: &str = "fooBar";
|
||||
/// let expected_string: String = "foo_bar".to_string();
|
||||
/// let asserted_string: String = to_snake_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::snakecase::to_snake_case;
|
||||
/// let mock_string: &str = "fooBar3";
|
||||
/// let expected_string: String = "foo_bar_3".to_string();
|
||||
/// let asserted_string: String = to_snake_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
pub fn to_snake_case(non_snake_case_string: &str) -> String {
|
||||
to_case_snake_like(non_snake_case_string, "_", "lower")
|
||||
}
|
||||
|
||||
/// Determines of a `&str` is `snake_case`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::snakecase::is_snake_case;
|
||||
/// let mock_string: &str = "Foo bar string that is really really long";
|
||||
/// let asserted_bool: bool = is_snake_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::snakecase::is_snake_case;
|
||||
/// let mock_string: &str = "foo-bar-string-that-is-really-really-long";
|
||||
/// let asserted_bool: bool = is_snake_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::snakecase::is_snake_case;
|
||||
/// let mock_string: &str = "FooBarIsAReallyReallyLongString";
|
||||
/// let asserted_bool: bool = is_snake_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::snakecase::is_snake_case;
|
||||
/// let mock_string: &str = "Foo Bar Is A Really Really Long String";
|
||||
/// let asserted_bool: bool = is_snake_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::snakecase::is_snake_case;
|
||||
/// let mock_string: &str = "FOO_BAR_IS_A_REALLY_REALLY_LONG_STRING";
|
||||
/// let asserted_bool: bool = is_snake_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::snakecase::is_snake_case;
|
||||
/// let mock_string: &str = "fooBarIsAReallyReallyLongString";
|
||||
/// let asserted_bool: bool = is_snake_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::snakecase::is_snake_case;
|
||||
/// let mock_string: &str = "foo_bar_string_that_is_really_really_long";
|
||||
/// let asserted_bool: bool = is_snake_case(mock_string);
|
||||
/// assert!(asserted_bool == true);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::snakecase::is_snake_case;
|
||||
/// let mock_string: &str = "foo_bar1_string_that_is_really_really_long";
|
||||
/// let asserted_bool: bool = is_snake_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::snakecase::is_snake_case;
|
||||
/// let mock_string: &str = "foo_bar_1_string_that_is_really_really_long";
|
||||
/// let asserted_bool: bool = is_snake_case(mock_string);
|
||||
/// assert!(asserted_bool == true);
|
||||
///
|
||||
/// ```
|
||||
pub fn is_snake_case(test_string: &str) -> bool {
|
||||
test_string == to_snake_case(test_string.clone())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", test))]
|
||||
mod benchmarks {
|
||||
extern crate test;
|
||||
use self::test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_snake_from_title(b: &mut Bencher) {
|
||||
b.iter(|| super::to_snake_case("Foo bar"));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_snake_from_camel(b: &mut Bencher) {
|
||||
b.iter(|| super::to_snake_case("fooBar"));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_snake_from_snake(b: &mut Bencher) {
|
||||
b.iter(|| super::to_snake_case("foo_bar_bar_bar"));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_is_snake(b: &mut Bencher) {
|
||||
b.iter(|| super::is_snake_case("Foo bar"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ::to_snake_case;
|
||||
use ::is_snake_case;
|
||||
|
||||
#[test]
|
||||
fn from_camel_case() {
|
||||
let convertable_string: String = "fooBar".to_owned();
|
||||
let expected: String = "foo_bar".to_owned();
|
||||
assert_eq!(to_snake_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_pascal_case() {
|
||||
let convertable_string: String = "FooBar".to_owned();
|
||||
let expected: String = "foo_bar".to_owned();
|
||||
assert_eq!(to_snake_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_kebab_case() {
|
||||
let convertable_string: String = "foo-bar".to_owned();
|
||||
let expected: String = "foo_bar".to_owned();
|
||||
assert_eq!(to_snake_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_sentence_case() {
|
||||
let convertable_string: String = "Foo bar".to_owned();
|
||||
let expected: String = "foo_bar".to_owned();
|
||||
assert_eq!(to_snake_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_title_case() {
|
||||
let convertable_string: String = "Foo Bar".to_owned();
|
||||
let expected: String = "foo_bar".to_owned();
|
||||
assert_eq!(to_snake_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_train_case() {
|
||||
let convertable_string: String = "Foo-Bar".to_owned();
|
||||
let expected: String = "foo_bar".to_owned();
|
||||
assert_eq!(to_snake_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_screaming_snake_case() {
|
||||
let convertable_string: String = "FOO_BAR".to_owned();
|
||||
let expected: String = "foo_bar".to_owned();
|
||||
assert_eq!(to_snake_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_snake_case() {
|
||||
let convertable_string: String = "foo_bar".to_owned();
|
||||
let expected: String = "foo_bar".to_owned();
|
||||
assert_eq!(to_snake_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_case_with_loads_of_space() {
|
||||
let convertable_string: String = "foo bar".to_owned();
|
||||
let expected: String = "foo_bar".to_owned();
|
||||
assert_eq!(to_snake_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_name_with_a_dot() {
|
||||
let convertable_string: String = "Robert C. Martin".to_owned();
|
||||
let expected: String = "robert_c_martin".to_owned();
|
||||
assert_eq!(to_snake_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn random_text_with_bad_chars() {
|
||||
let convertable_string: String = "Random text with *(bad) chars".to_owned();
|
||||
let expected: String = "random_text_with_bad_chars".to_owned();
|
||||
assert_eq!(to_snake_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trailing_bad_chars() {
|
||||
let convertable_string: String = "trailing bad_chars*(()())".to_owned();
|
||||
let expected: String = "trailing_bad_chars".to_owned();
|
||||
assert_eq!(to_snake_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leading_bad_chars() {
|
||||
let convertable_string: String = "-!#$%leading bad chars".to_owned();
|
||||
let expected: String = "leading_bad_chars".to_owned();
|
||||
assert_eq!(to_snake_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wrapped_in_bad_chars() {
|
||||
let convertable_string: String = "-!#$%wrapped in bad chars&*^*&(&*^&(<><?>><?><>))".to_owned();
|
||||
let expected: String = "wrapped_in_bad_chars".to_owned();
|
||||
assert_eq!(to_snake_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_a_sign() {
|
||||
let convertable_string: String = "has a + sign".to_owned();
|
||||
let expected: String = "has_a_sign".to_owned();
|
||||
assert_eq!(to_snake_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_camel_case() {
|
||||
let convertable_string: String = "fooBar".to_owned();
|
||||
assert_eq!(is_snake_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_pascal_case() {
|
||||
let convertable_string: String = "FooBar".to_owned();
|
||||
assert_eq!(is_snake_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_kebab_case() {
|
||||
let convertable_string: String = "foo-bar".to_owned();
|
||||
assert_eq!(is_snake_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_sentence_case() {
|
||||
let convertable_string: String = "Foo bar".to_owned();
|
||||
assert_eq!(is_snake_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_title_case() {
|
||||
let convertable_string: String = "Foo Bar".to_owned();
|
||||
assert_eq!(is_snake_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_train_case() {
|
||||
let convertable_string: String = "Foo-Bar".to_owned();
|
||||
assert_eq!(is_snake_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_screaming_snake_case() {
|
||||
let convertable_string: String = "FOO_BAR".to_owned();
|
||||
assert_eq!(is_snake_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_snake_case() {
|
||||
let convertable_string: String = "foo_bar".to_owned();
|
||||
assert_eq!(is_snake_case(&convertable_string), true)
|
||||
}
|
||||
}
|
|
@ -1,271 +0,0 @@
|
|||
#![deny(warnings)]
|
||||
#[cfg(feature = "heavyweight")]
|
||||
use string::pluralize::to_plural;
|
||||
#[cfg(feature = "heavyweight")]
|
||||
use cases::case::*;
|
||||
#[cfg(feature = "heavyweight")]
|
||||
/// Converts a `&str` to `table-case` `String`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::tablecase::to_table_case;
|
||||
/// let mock_string: &str = "foo-bar";
|
||||
/// let expected_string: String = "foo_bars".to_string();
|
||||
/// let asserted_string: String = to_table_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::tablecase::to_table_case;
|
||||
/// let mock_string: &str = "FOO_BAR";
|
||||
/// let expected_string: String = "foo_bars".to_string();
|
||||
/// let asserted_string: String = to_table_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::tablecase::to_table_case;
|
||||
/// let mock_string: &str = "foo_bar";
|
||||
/// let expected_string: String = "foo_bars".to_string();
|
||||
/// let asserted_string: String = to_table_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::tablecase::to_table_case;
|
||||
/// let mock_string: &str = "Foo Bar";
|
||||
/// let expected_string: String = "foo_bars".to_string();
|
||||
/// let asserted_string: String = to_table_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::tablecase::to_table_case;
|
||||
/// let mock_string: &str = "Foo bar";
|
||||
/// let expected_string: String = "foo_bars".to_string();
|
||||
/// let asserted_string: String = to_table_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::tablecase::to_table_case;
|
||||
/// let mock_string: &str = "FooBar";
|
||||
/// let expected_string: String = "foo_bars".to_string();
|
||||
/// let asserted_string: String = to_table_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::tablecase::to_table_case;
|
||||
/// let mock_string: &str = "fooBar";
|
||||
/// let expected_string: String = "foo_bars".to_string();
|
||||
/// let asserted_string: String = to_table_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
/// ```
|
||||
pub fn to_table_case(non_table_case_string: &str) -> String {
|
||||
let snaked: String = to_case_snake_like(non_table_case_string, "_", "lower");
|
||||
let split: (&str, &str) = snaked.split_at(snaked.rfind('_').unwrap_or(0));
|
||||
format!("{}{}", split.0, to_plural(split.1))
|
||||
}
|
||||
|
||||
#[cfg(feature = "heavyweight")]
|
||||
/// Determines if a `&str` is `table-case`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::tablecase::is_table_case;
|
||||
/// let mock_string: &str = "foo_bar_strings";
|
||||
/// let asserted_bool: bool = is_table_case(mock_string);
|
||||
/// assert!(asserted_bool == true);
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::tablecase::is_table_case;
|
||||
/// let mock_string: &str = "foo-bar-string-that-is-really-really-long";
|
||||
/// let asserted_bool: bool = is_table_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::tablecase::is_table_case;
|
||||
/// let mock_string: &str = "FooBarIsAReallyReallyLongString";
|
||||
/// let asserted_bool: bool = is_table_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::tablecase::is_table_case;
|
||||
/// let mock_string: &str = "fooBarIsAReallyReallyLongString";
|
||||
/// let asserted_bool: bool = is_table_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::tablecase::is_table_case;
|
||||
/// let mock_string: &str = "FOO_BAR_STRING_THAT_IS_REALLY_REALLY_LONG";
|
||||
/// let asserted_bool: bool = is_table_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::tablecase::is_table_case;
|
||||
/// let mock_string: &str = "foo_bar_string_that_is_really_really_long";
|
||||
/// let asserted_bool: bool = is_table_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::tablecase::is_table_case;
|
||||
/// let mock_string: &str = "Foo bar string that is really really long";
|
||||
/// let asserted_bool: bool = is_table_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::tablecase::is_table_case;
|
||||
/// let mock_string: &str = "Foo Bar Is A Really Really Long String";
|
||||
/// let asserted_bool: bool = is_table_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
/// ```
|
||||
pub fn is_table_case(test_string: &str) -> bool {
|
||||
to_table_case(&test_string.clone()) == test_string
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", test))]
|
||||
#[cfg(feature = "heavyweight")]
|
||||
mod benchmarks {
|
||||
extern crate test;
|
||||
use self::test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_table_case(b: &mut Bencher) {
|
||||
b.iter(|| super::to_table_case("Foo bar"));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_is_table_case(b: &mut Bencher) {
|
||||
b.iter(|| super::is_table_case("Foo bar"));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "heavyweight")]
|
||||
mod tests {
|
||||
use ::to_table_case;
|
||||
use ::is_table_case;
|
||||
|
||||
#[test]
|
||||
fn from_camel_case() {
|
||||
let convertable_string: String = "fooBar".to_owned();
|
||||
let expected: String = "foo_bars".to_owned();
|
||||
assert_eq!(to_table_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_pascal_case() {
|
||||
let convertable_string: String = "FooBar".to_owned();
|
||||
let expected: String = "foo_bars".to_owned();
|
||||
assert_eq!(to_table_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_kebab_case() {
|
||||
let convertable_string: String = "foo-bar".to_owned();
|
||||
let expected: String = "foo_bars".to_owned();
|
||||
assert_eq!(to_table_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_sentence_case() {
|
||||
let convertable_string: String = "Foo bar".to_owned();
|
||||
let expected: String = "foo_bars".to_owned();
|
||||
assert_eq!(to_table_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_title_case() {
|
||||
let convertable_string: String = "Foo Bar".to_owned();
|
||||
let expected: String = "foo_bars".to_owned();
|
||||
assert_eq!(to_table_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_train_case() {
|
||||
let convertable_string: String = "Foo-Bar".to_owned();
|
||||
let expected: String = "foo_bars".to_owned();
|
||||
assert_eq!(to_table_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_screaming_snake_case() {
|
||||
let convertable_string: String = "FOO_BAR".to_owned();
|
||||
let expected: String = "foo_bars".to_owned();
|
||||
assert_eq!(to_table_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_snake_case() {
|
||||
let convertable_string: String = "foo_bar".to_owned();
|
||||
let expected: String = "foo_bars".to_owned();
|
||||
assert_eq!(to_table_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_table_case() {
|
||||
let convertable_string: String = "foo_bars".to_owned();
|
||||
let expected: String = "foo_bars".to_owned();
|
||||
assert_eq!(to_table_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_camel_case() {
|
||||
let convertable_string: String = "fooBar".to_owned();
|
||||
assert_eq!(is_table_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_pascal_case() {
|
||||
let convertable_string: String = "FooBar".to_owned();
|
||||
assert_eq!(is_table_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_kebab_case() {
|
||||
let convertable_string: String = "foo-bar".to_owned();
|
||||
assert_eq!(is_table_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_sentence_case() {
|
||||
let convertable_string: String = "Foo bar".to_owned();
|
||||
assert_eq!(is_table_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_title_case() {
|
||||
let convertable_string: String = "Foo Bar".to_owned();
|
||||
assert_eq!(is_table_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_train_case() {
|
||||
let convertable_string: String = "Foo-Bar".to_owned();
|
||||
assert_eq!(is_table_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_screaming_snake_case() {
|
||||
let convertable_string: String = "FOO_BAR".to_owned();
|
||||
assert_eq!(is_table_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_snake_case() {
|
||||
let convertable_string: String = "foo_bar".to_owned();
|
||||
assert_eq!(is_table_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_table_case() {
|
||||
let convertable_string: String = "foo_bars".to_owned();
|
||||
assert_eq!(is_table_case(&convertable_string), true)
|
||||
}
|
||||
}
|
|
@ -1,308 +0,0 @@
|
|||
#![deny(warnings)]
|
||||
use cases::case::*;
|
||||
/// Converts a `&str` to `Title Case` `String`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::titlecase::to_title_case;
|
||||
/// let mock_string: &str = "Foo bar";
|
||||
/// let expected_string: String = "Foo Bar".to_string();
|
||||
/// let asserted_string: String = to_title_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::titlecase::to_title_case;
|
||||
/// let mock_string: &str = "FooBar";
|
||||
/// let expected_string: String = "Foo Bar".to_string();
|
||||
/// let asserted_string: String = to_title_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::titlecase::to_title_case;
|
||||
/// let mock_string: &str = "fooBar";
|
||||
/// let expected_string: String = "Foo Bar".to_string();
|
||||
/// let asserted_string: String = to_title_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::titlecase::to_title_case;
|
||||
/// let mock_string: &str = "FOO_BAR";
|
||||
/// let expected_string: String = "Foo Bar".to_string();
|
||||
/// let asserted_string: String = to_title_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::titlecase::to_title_case;
|
||||
/// let mock_string: &str = "foo_bar";
|
||||
/// let expected_string: String = "Foo Bar".to_string();
|
||||
/// let asserted_string: String = to_title_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::titlecase::to_title_case;
|
||||
/// let mock_string: &str = "foo-bar";
|
||||
/// let expected_string: String = "Foo Bar".to_string();
|
||||
/// let asserted_string: String = to_title_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
pub fn to_title_case(non_title_case_string: &str) -> String {
|
||||
let options = CamelOptions {
|
||||
new_word: true,
|
||||
last_char: ' ',
|
||||
first_word: true,
|
||||
injectable_char: ' ',
|
||||
has_seperator: true,
|
||||
inverted: false,
|
||||
};
|
||||
to_case_camel_like(non_title_case_string, options)
|
||||
}
|
||||
|
||||
/// Determines if a `&str` is `Title Case`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::titlecase::is_title_case;
|
||||
/// let mock_string: &str = "foo-bar-string-that-is-really-really-long";
|
||||
/// let asserted_bool: bool = is_title_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::titlecase::is_title_case;
|
||||
/// let mock_string: &str = "FooBarIsAReallyReallyLongString";
|
||||
/// let asserted_bool: bool = is_title_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::titlecase::is_title_case;
|
||||
/// let mock_string: &str = "fooBarIsAReallyReallyLongString";
|
||||
/// let asserted_bool: bool = is_title_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::titlecase::is_title_case;
|
||||
/// let mock_string: &str = "FOO_BAR_STRING_THAT_IS_REALLY_REALLY_LONG";
|
||||
/// let asserted_bool: bool = is_title_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::titlecase::is_title_case;
|
||||
/// let mock_string: &str = "foo_bar_string_that_is_really_really_long";
|
||||
/// let asserted_bool: bool = is_title_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::titlecase::is_title_case;
|
||||
/// let mock_string: &str = "Foo bar string that is really really long";
|
||||
/// let asserted_bool: bool = is_title_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::titlecase::is_title_case;
|
||||
/// let mock_string: &str = "foo";
|
||||
/// let asserted_bool: bool = is_title_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::cases::titlecase::is_title_case;
|
||||
/// let mock_string: &str = "Foo Bar String That Is Really Really Long";
|
||||
/// let asserted_bool: bool = is_title_case(mock_string);
|
||||
/// assert!(asserted_bool == true);
|
||||
///
|
||||
/// ```
|
||||
pub fn is_title_case(test_string: &str) -> bool {
|
||||
test_string == to_title_case(test_string.clone())
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", test))]
|
||||
mod benchmarks {
|
||||
extern crate test;
|
||||
use self::test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_title(b: &mut Bencher) {
|
||||
b.iter(|| super::to_title_case("Foo BAR"));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_is_title(b: &mut Bencher) {
|
||||
b.iter(|| super::is_title_case("Foo bar"));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_title_from_snake(b: &mut Bencher) {
|
||||
b.iter(|| super::to_title_case("foo_bar"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ::to_title_case;
|
||||
use ::is_title_case;
|
||||
|
||||
#[test]
|
||||
fn from_camel_case() {
|
||||
let convertable_string: String = "fooBar".to_owned();
|
||||
let expected: String = "Foo Bar".to_owned();
|
||||
assert_eq!(to_title_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_pascal_case() {
|
||||
let convertable_string: String = "FooBar".to_owned();
|
||||
let expected: String = "Foo Bar".to_owned();
|
||||
assert_eq!(to_title_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_kebab_case() {
|
||||
let convertable_string: String = "foo-bar".to_owned();
|
||||
let expected: String = "Foo Bar".to_owned();
|
||||
assert_eq!(to_title_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_sentence_case() {
|
||||
let convertable_string: String = "Foo bar".to_owned();
|
||||
let expected: String = "Foo Bar".to_owned();
|
||||
assert_eq!(to_title_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_title_case() {
|
||||
let convertable_string: String = "Foo Bar".to_owned();
|
||||
let expected: String = "Foo Bar".to_owned();
|
||||
assert_eq!(to_title_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_train_case() {
|
||||
let convertable_string: String = "Foo-Bar".to_owned();
|
||||
let expected: String = "Foo Bar".to_owned();
|
||||
assert_eq!(to_title_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_screaming_snake_case() {
|
||||
let convertable_string: String = "FOO_BAR".to_owned();
|
||||
let expected: String = "Foo Bar".to_owned();
|
||||
assert_eq!(to_title_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_snake_case() {
|
||||
let convertable_string: String = "foo_bar".to_owned();
|
||||
let expected: String = "Foo Bar".to_owned();
|
||||
assert_eq!(to_title_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_case_with_loads_of_space() {
|
||||
let convertable_string: String = "foo bar".to_owned();
|
||||
let expected: String = "Foo Bar".to_owned();
|
||||
assert_eq!(to_title_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_name_with_a_dot() {
|
||||
let convertable_string: String = "Robert C. Martin".to_owned();
|
||||
let expected: String = "Robert C Martin".to_owned();
|
||||
assert_eq!(to_title_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn random_text_with_bad_chars() {
|
||||
let convertable_string: String = "Random text with *(bad) chars".to_owned();
|
||||
let expected: String = "Random Text With Bad Chars".to_owned();
|
||||
assert_eq!(to_title_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trailing_bad_chars() {
|
||||
let convertable_string: String = "trailing bad_chars*(()())".to_owned();
|
||||
let expected: String = "Trailing Bad Chars".to_owned();
|
||||
assert_eq!(to_title_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leading_bad_chars() {
|
||||
let convertable_string: String = "-!#$%leading bad chars".to_owned();
|
||||
let expected: String = "Leading Bad Chars".to_owned();
|
||||
assert_eq!(to_title_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wrapped_in_bad_chars() {
|
||||
let convertable_string: String = "-!#$%wrapped in bad chars&*^*&(&*^&(<><?>><?><>))".to_owned();
|
||||
let expected: String = "Wrapped In Bad Chars".to_owned();
|
||||
assert_eq!(to_title_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_a_sign() {
|
||||
let convertable_string: String = "has a + sign".to_owned();
|
||||
let expected: String = "Has A Sign".to_owned();
|
||||
assert_eq!(to_title_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_camel_case() {
|
||||
let convertable_string: String = "fooBar".to_owned();
|
||||
assert_eq!(is_title_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_pascal_case() {
|
||||
let convertable_string: String = "FooBar".to_owned();
|
||||
assert_eq!(is_title_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_kebab_case() {
|
||||
let convertable_string: String = "foo-bar".to_owned();
|
||||
assert_eq!(is_title_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_sentence_case() {
|
||||
let convertable_string: String = "Foo bar".to_owned();
|
||||
assert_eq!(is_title_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_title_case() {
|
||||
let convertable_string: String = "Foo Bar".to_owned();
|
||||
assert_eq!(is_title_case(&convertable_string), true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_train_case() {
|
||||
let convertable_string: String = "Foo-Bar".to_owned();
|
||||
assert_eq!(is_title_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_screaming_snake_case() {
|
||||
let convertable_string: String = "FOO_BAR".to_owned();
|
||||
assert_eq!(is_title_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_snake_case() {
|
||||
let convertable_string: String = "foo_bar".to_owned();
|
||||
assert_eq!(is_title_case(&convertable_string), false)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,320 +0,0 @@
|
|||
#![deny(warnings)]
|
||||
use cases::case::*;
|
||||
/// Determines if a `&str` is `Train-Case`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::traincase::is_train_case;
|
||||
/// let mock_string: &str = "Foo-Bar-String-That-Is-Really-Really-Long";
|
||||
/// let asserted_bool: bool = is_train_case(mock_string);
|
||||
/// assert!(asserted_bool == true);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::traincase::is_train_case;
|
||||
/// let mock_string: &str = "foo-bar-string-that-is-really-really-long";
|
||||
/// let asserted_bool: bool = is_train_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::traincase::is_train_case;
|
||||
/// let mock_string: &str = "FooBarIsAReallyReallyLongString";
|
||||
/// let asserted_bool: bool = is_train_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::traincase::is_train_case;
|
||||
/// let mock_string: &str = "fooBarIsAReallyReallyLongString";
|
||||
/// let asserted_bool: bool = is_train_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::traincase::is_train_case;
|
||||
/// let mock_string: &str = "foo_bar_string_that_is_really_really_long";
|
||||
/// let asserted_bool: bool = is_train_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::traincase::is_train_case;
|
||||
/// let mock_string: &str = "Foo bar string that is really really long";
|
||||
/// let asserted_bool: bool = is_train_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::traincase::is_train_case;
|
||||
/// let mock_string: &str = "Foo Bar Is A Really Really Long String";
|
||||
/// let asserted_bool: bool = is_train_case(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
pub fn is_train_case(test_string: &str) -> bool {
|
||||
test_string == to_train_case(test_string.clone())
|
||||
}
|
||||
|
||||
|
||||
/// Converts a `&str` to `Train-Case` `String`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::traincase::to_train_case;
|
||||
/// let mock_string: &str = "foo-bar";
|
||||
/// let expected_string: String = "Foo-Bar".to_string();
|
||||
/// let asserted_string: String = to_train_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::traincase::to_train_case;
|
||||
/// let mock_string: &str = "FOO_BAR";
|
||||
/// let expected_string: String = "Foo-Bar".to_string();
|
||||
/// let asserted_string: String = to_train_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::traincase::to_train_case;
|
||||
/// let mock_string: &str = "foo_bar";
|
||||
/// let expected_string: String = "Foo-Bar".to_string();
|
||||
/// let asserted_string: String = to_train_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::traincase::to_train_case;
|
||||
/// let mock_string: &str = "Foo Bar";
|
||||
/// let expected_string: String = "Foo-Bar".to_string();
|
||||
/// let asserted_string: String = to_train_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::traincase::to_train_case;
|
||||
/// let mock_string: &str = "Foo bar";
|
||||
/// let expected_string: String = "Foo-Bar".to_string();
|
||||
/// let asserted_string: String = to_train_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::traincase::to_train_case;
|
||||
/// let mock_string: &str = "FooBar";
|
||||
/// let expected_string: String = "Foo-Bar".to_string();
|
||||
/// let asserted_string: String = to_train_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::cases::traincase::to_train_case;
|
||||
/// let mock_string: &str = "fooBar";
|
||||
/// let expected_string: String = "Foo-Bar".to_string();
|
||||
/// let asserted_string: String = to_train_case(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
pub fn to_train_case(non_train_case_string: &str) -> String {
|
||||
let options = CamelOptions {
|
||||
new_word: true,
|
||||
last_char: ' ',
|
||||
first_word: true,
|
||||
injectable_char: '-',
|
||||
has_seperator: true,
|
||||
inverted: false,
|
||||
};
|
||||
to_case_camel_like(non_train_case_string, options)
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "unstable", test))]
|
||||
mod benchmarks {
|
||||
extern crate test;
|
||||
use self::test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_train(b: &mut Bencher) {
|
||||
b.iter(|| super::to_train_case("Foo bar"));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_is_train(b: &mut Bencher) {
|
||||
b.iter(|| super::is_train_case("Foo bar"));
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_train_from_snake(b: &mut Bencher) {
|
||||
b.iter(|| super::to_train_case("test_test_test"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ::to_train_case;
|
||||
use ::is_train_case;
|
||||
|
||||
#[test]
|
||||
fn from_camel_case() {
|
||||
let convertable_string: String = "fooBar".to_owned();
|
||||
let expected: String = "Foo-Bar".to_owned();
|
||||
assert_eq!(to_train_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_pascal_case() {
|
||||
let convertable_string: String = "FooBar".to_owned();
|
||||
let expected: String = "Foo-Bar".to_owned();
|
||||
assert_eq!(to_train_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_kebab_case() {
|
||||
let convertable_string: String = "foo-bar".to_owned();
|
||||
let expected: String = "Foo-Bar".to_owned();
|
||||
assert_eq!(to_train_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_sentence_case() {
|
||||
let convertable_string: String = "Foo bar".to_owned();
|
||||
let expected: String = "Foo-Bar".to_owned();
|
||||
assert_eq!(to_train_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_title_case() {
|
||||
let convertable_string: String = "Foo Bar".to_owned();
|
||||
let expected: String = "Foo-Bar".to_owned();
|
||||
assert_eq!(to_train_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_train_case() {
|
||||
let convertable_string: String = "Foo-Bar".to_owned();
|
||||
let expected: String = "Foo-Bar".to_owned();
|
||||
assert_eq!(to_train_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_screaming_snake_case() {
|
||||
let convertable_string: String = "FOO_BAR".to_owned();
|
||||
let expected: String = "Foo-Bar".to_owned();
|
||||
assert_eq!(to_train_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_snake_case() {
|
||||
let convertable_string: String = "foo_bar".to_owned();
|
||||
let expected: String = "Foo-Bar".to_owned();
|
||||
assert_eq!(to_train_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_case_with_loads_of_space() {
|
||||
let convertable_string: String = "foo bar".to_owned();
|
||||
let expected: String = "Foo-Bar".to_owned();
|
||||
assert_eq!(to_train_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_name_with_a_dot() {
|
||||
let convertable_string: String = "Robert C. Martin".to_owned();
|
||||
let expected: String = "Robert-C-Martin".to_owned();
|
||||
assert_eq!(to_train_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn random_text_with_bad_chars() {
|
||||
let convertable_string: String = "Random text with *(bad) chars".to_owned();
|
||||
let expected: String = "Random-Text-With-Bad-Chars".to_owned();
|
||||
assert_eq!(to_train_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trailing_bad_chars() {
|
||||
let convertable_string: String = "trailing bad_chars*(()())".to_owned();
|
||||
let expected: String = "Trailing-Bad-Chars".to_owned();
|
||||
assert_eq!(to_train_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn leading_bad_chars() {
|
||||
let convertable_string: String = "-!#$%leading bad chars".to_owned();
|
||||
let expected: String = "Leading-Bad-Chars".to_owned();
|
||||
assert_eq!(to_train_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wrapped_in_bad_chars() {
|
||||
let convertable_string: String = "-!#$%wrapped in bad chars&*^*&(&*^&(<><?>><?><>))".to_owned();
|
||||
let expected: String = "Wrapped-In-Bad-Chars".to_owned();
|
||||
assert_eq!(to_train_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_a_sign() {
|
||||
let convertable_string: String = "has a + sign".to_owned();
|
||||
let expected: String = "Has-A-Sign".to_owned();
|
||||
assert_eq!(to_train_case(&convertable_string), expected)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_camel_case() {
|
||||
let convertable_string: String = "fooBar".to_owned();
|
||||
assert_eq!(is_train_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_pascal_case() {
|
||||
let convertable_string: String = "FooBar".to_owned();
|
||||
assert_eq!(is_train_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_kebab_case() {
|
||||
let convertable_string: String = "foo-bar".to_owned();
|
||||
assert_eq!(is_train_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_sentence_case() {
|
||||
let convertable_string: String = "Foo bar".to_owned();
|
||||
assert_eq!(is_train_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_title_case() {
|
||||
let convertable_string: String = "Foo Bar".to_owned();
|
||||
assert_eq!(is_train_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_train_case() {
|
||||
let convertable_string: String = "Foo-Bar".to_owned();
|
||||
assert_eq!(is_train_case(&convertable_string), true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_screaming_snake_case() {
|
||||
let convertable_string: String = "FOO_BAR".to_owned();
|
||||
assert_eq!(is_train_case(&convertable_string), false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_correct_from_snake_case() {
|
||||
let convertable_string: String = "foo_bar".to_owned();
|
||||
assert_eq!(is_train_case(&convertable_string), false)
|
||||
}
|
||||
}
|
|
@ -1,332 +0,0 @@
|
|||
#![deny(warnings, unused_variables, missing_docs, unsafe_code, unused_extern_crates)]
|
||||
#![cfg_attr(feature = "unstable", feature(test))]
|
||||
|
||||
//! Adds String based inflections for Rust. Snake, kebab, train, camel,
|
||||
//! sentence, class, and title cases as well as ordinalize,
|
||||
//! deordinalize, demodulize, deconstantize, and foreign key are supported as
|
||||
//! both traits and pure functions acting on String types.
|
||||
//! ```rust
|
||||
//! use inflector::Inflector;
|
||||
//! let camel_case_string: String = "some_string".to_camel_case();
|
||||
//! let is_camel_cased: bool= camel_case_string.is_camel_case();
|
||||
//! assert!(is_camel_cased == true);
|
||||
//! ```
|
||||
|
||||
#[cfg(feature = "heavyweight")]
|
||||
extern crate regex;
|
||||
|
||||
#[cfg(feature = "heavyweight")]
|
||||
#[macro_use] extern crate lazy_static;
|
||||
|
||||
/// Provides case inflections
|
||||
/// - Camel case
|
||||
/// - Class case
|
||||
/// - Kebab case
|
||||
/// - Train case
|
||||
/// - Screaming snake case
|
||||
/// - Table case
|
||||
/// - Sentence case
|
||||
/// - Snake case
|
||||
/// - Pascal case
|
||||
pub mod cases;
|
||||
/// Provides number inflections
|
||||
/// - Ordinalize
|
||||
/// - Deordinalize
|
||||
pub mod numbers;
|
||||
/// Provides suffix inflections
|
||||
/// - Foreign key
|
||||
pub mod suffix;
|
||||
/// Provides string inflections
|
||||
/// - Deconstantize
|
||||
/// - Demodulize
|
||||
/// - Pluralize
|
||||
/// - Singularize
|
||||
#[cfg(feature = "heavyweight")]
|
||||
pub mod string;
|
||||
|
||||
|
||||
#[cfg(feature = "heavyweight")]
|
||||
use cases::classcase::to_class_case;
|
||||
#[cfg(feature = "heavyweight")]
|
||||
use cases::classcase::is_class_case;
|
||||
|
||||
use cases::camelcase::to_camel_case;
|
||||
use cases::camelcase::is_camel_case;
|
||||
|
||||
use cases::pascalcase::to_pascal_case;
|
||||
use cases::pascalcase::is_pascal_case;
|
||||
|
||||
use cases::snakecase::to_snake_case;
|
||||
use cases::snakecase::is_snake_case;
|
||||
|
||||
use cases::screamingsnakecase::to_screaming_snake_case;
|
||||
use cases::screamingsnakecase::is_screaming_snake_case;
|
||||
|
||||
use cases::kebabcase::to_kebab_case;
|
||||
use cases::kebabcase::is_kebab_case;
|
||||
|
||||
use cases::traincase::to_train_case;
|
||||
use cases::traincase::is_train_case;
|
||||
|
||||
use cases::sentencecase::to_sentence_case;
|
||||
use cases::sentencecase::is_sentence_case;
|
||||
|
||||
use cases::titlecase::to_title_case;
|
||||
use cases::titlecase::is_title_case;
|
||||
|
||||
#[cfg(feature = "heavyweight")]
|
||||
use cases::tablecase::to_table_case;
|
||||
#[cfg(feature = "heavyweight")]
|
||||
use cases::tablecase::is_table_case;
|
||||
|
||||
use numbers::ordinalize::ordinalize;
|
||||
use numbers::deordinalize::deordinalize;
|
||||
|
||||
use suffix::foreignkey::to_foreign_key;
|
||||
use suffix::foreignkey::is_foreign_key;
|
||||
|
||||
#[cfg(feature = "heavyweight")]
|
||||
use string::demodulize::demodulize;
|
||||
#[cfg(feature = "heavyweight")]
|
||||
use string::deconstantize::deconstantize;
|
||||
|
||||
#[cfg(feature = "heavyweight")]
|
||||
use string::pluralize::to_plural;
|
||||
#[cfg(feature = "heavyweight")]
|
||||
use string::singularize::to_singular;
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub trait Inflector {
|
||||
|
||||
fn to_camel_case(&self) -> String;
|
||||
fn is_camel_case(&self) -> bool;
|
||||
|
||||
fn to_pascal_case(&self) -> String;
|
||||
fn is_pascal_case(&self) -> bool;
|
||||
|
||||
fn to_snake_case(&self) -> String;
|
||||
fn is_snake_case(&self) -> bool;
|
||||
|
||||
fn to_screaming_snake_case(&self) -> String;
|
||||
fn is_screaming_snake_case(&self) -> bool;
|
||||
|
||||
fn to_kebab_case(&self) -> String;
|
||||
fn is_kebab_case(&self) -> bool;
|
||||
|
||||
fn to_train_case(&self) -> String;
|
||||
fn is_train_case(&self) -> bool;
|
||||
|
||||
fn to_sentence_case(&self) -> String;
|
||||
fn is_sentence_case(&self) -> bool;
|
||||
|
||||
fn to_title_case(&self) -> String;
|
||||
fn is_title_case(&self) -> bool;
|
||||
|
||||
fn ordinalize(&self) -> String;
|
||||
fn deordinalize(&self) -> String;
|
||||
|
||||
fn to_foreign_key(&self) -> String;
|
||||
fn is_foreign_key(&self) -> bool;
|
||||
|
||||
#[cfg(feature = "heavyweight")]
|
||||
fn demodulize(&self) -> String;
|
||||
#[cfg(feature = "heavyweight")]
|
||||
fn deconstantize(&self) -> String;
|
||||
|
||||
#[cfg(feature = "heavyweight")]
|
||||
fn to_class_case(&self) -> String;
|
||||
#[cfg(feature = "heavyweight")]
|
||||
fn is_class_case(&self) -> bool;
|
||||
|
||||
#[cfg(feature = "heavyweight")]
|
||||
fn to_table_case(&self) -> String;
|
||||
#[cfg(feature = "heavyweight")]
|
||||
fn is_table_case(&self) -> bool;
|
||||
#[cfg(feature = "heavyweight")]
|
||||
fn to_plural(&self) -> String;
|
||||
#[cfg(feature = "heavyweight")]
|
||||
fn to_singular(&self) -> String;
|
||||
}
|
||||
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub trait InflectorNumbers {
|
||||
fn ordinalize(&self) -> String;
|
||||
}
|
||||
|
||||
|
||||
macro_rules! define_implementations {
|
||||
( $slf:ident; $($imp_trait:ident => $typ:ident), *) => {
|
||||
$(
|
||||
#[inline]
|
||||
fn $imp_trait(&$slf) -> $typ {
|
||||
$imp_trait($slf)
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_number_implementations {
|
||||
( $slf:ident; $($imp_trait:ident => $typ:ident), *) => {
|
||||
$(
|
||||
#[inline]
|
||||
fn $imp_trait(&$slf) -> $typ {
|
||||
$imp_trait(&$slf.to_string())
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_gated_implementations {
|
||||
( $slf:ident; $($imp_trait:ident => $typ:ident), *) => {
|
||||
$(
|
||||
#[inline]
|
||||
#[cfg(feature = "heavyweight")]
|
||||
fn $imp_trait(&$slf) -> $typ {
|
||||
$imp_trait($slf)
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! implement_string_for {
|
||||
( $trt:ident; $($typ:ident), *) => {
|
||||
$(
|
||||
impl $trt for $typ {
|
||||
define_implementations![self;
|
||||
to_camel_case => String,
|
||||
is_camel_case => bool,
|
||||
to_pascal_case => String,
|
||||
is_pascal_case => bool,
|
||||
to_screaming_snake_case => String,
|
||||
is_screaming_snake_case => bool,
|
||||
to_snake_case => String,
|
||||
is_snake_case => bool,
|
||||
to_kebab_case => String,
|
||||
is_kebab_case => bool,
|
||||
to_train_case => String,
|
||||
is_train_case => bool,
|
||||
to_sentence_case => String,
|
||||
is_sentence_case => bool,
|
||||
to_title_case => String,
|
||||
is_title_case => bool,
|
||||
to_foreign_key => String,
|
||||
is_foreign_key => bool,
|
||||
ordinalize => String,
|
||||
deordinalize => String
|
||||
];
|
||||
define_gated_implementations![self;
|
||||
to_class_case => String,
|
||||
is_class_case => bool,
|
||||
to_table_case => String,
|
||||
is_table_case => bool,
|
||||
to_plural => String,
|
||||
to_singular => String,
|
||||
demodulize => String,
|
||||
deconstantize => String
|
||||
];
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! implement_number_for {
|
||||
( $trt:ident; $($typ:ident), *) => {
|
||||
$(
|
||||
impl $trt for $typ {
|
||||
define_number_implementations![self;
|
||||
ordinalize => String
|
||||
];
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
implement_string_for![
|
||||
Inflector;
|
||||
String, str
|
||||
];
|
||||
|
||||
implement_number_for![
|
||||
InflectorNumbers;
|
||||
i8, i16, i32, i64, u8, u16, u32, u64, isize, usize, f32, f64
|
||||
];
|
||||
|
||||
#[cfg(all(feature = "unstable", test))]
|
||||
mod benchmarks {
|
||||
extern crate test;
|
||||
use self::test::Bencher;
|
||||
use ::Inflector;
|
||||
|
||||
macro_rules! benchmarks {
|
||||
( $($test_name:ident => $imp_trait:ident => $to_cast:expr), *) => {
|
||||
$(
|
||||
#[bench]
|
||||
fn $test_name(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
$to_cast.$imp_trait()
|
||||
});
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
benchmarks![
|
||||
benchmark_str_to_camel => to_camel_case => "foo_bar",
|
||||
benchmark_str_is_camel => is_camel_case => "fooBar",
|
||||
benchmark_str_to_screaming_snake => to_screaming_snake_case => "fooBar",
|
||||
benchmark_str_is_screaming_snake => is_screaming_snake_case => "FOO_BAR",
|
||||
benchmark_str_to_snake => to_snake_case => "fooBar",
|
||||
benchmark_str_is_snake => is_snake_case => "foo_bar",
|
||||
benchmark_str_to_kebab => to_kebab_case => "fooBar",
|
||||
benchmark_str_is_kebab => is_kebab_case => "foo-bar",
|
||||
benchmark_str_to_train => to_train_case => "fooBar",
|
||||
benchmark_str_is_train => is_train_case => "Foo-Bar",
|
||||
benchmark_str_to_sentence => to_sentence_case => "fooBar",
|
||||
benchmark_str_is_sentence => is_sentence_case => "Foo bar",
|
||||
benchmark_str_to_title => to_title_case => "fooBar",
|
||||
benchmark_str_is_title => is_title_case => "Foo Bar",
|
||||
benchmark_str_ordinalize => ordinalize => "1",
|
||||
benchmark_str_deordinalize => deordinalize => "1st",
|
||||
benchmark_str_to_foreign_key => to_foreign_key => "Foo::Bar",
|
||||
benchmark_str_is_foreign_key => is_foreign_key => "bar_id",
|
||||
benchmark_string_to_camel => to_camel_case => "foo_bar".to_string(),
|
||||
benchmark_string_is_camel => is_camel_case => "fooBar".to_string(),
|
||||
benchmark_string_to_screaming_snake => to_screaming_snake_case => "fooBar".to_string(),
|
||||
benchmark_string_is_screaming_snake => is_screaming_snake_case => "FOO_BAR".to_string(),
|
||||
benchmark_string_to_snake => to_snake_case => "fooBar".to_string(),
|
||||
benchmark_string_is_snake => is_snake_case => "foo_bar".to_string(),
|
||||
benchmark_string_to_kebab => to_kebab_case => "fooBar".to_string(),
|
||||
benchmark_string_is_kebab => is_kebab_case => "foo-bar".to_string(),
|
||||
benchmark_string_to_train => to_train_case => "fooBar".to_string(),
|
||||
benchmark_string_is_train => is_train_case => "Foo-Bar".to_string(),
|
||||
benchmark_string_to_sentence => to_sentence_case => "fooBar".to_string(),
|
||||
benchmark_string_is_sentence => is_sentence_case => "Foo bar".to_string(),
|
||||
benchmark_string_to_title => to_title_case => "fooBar".to_string(),
|
||||
benchmark_string_is_title => is_title_case => "Foo Bar".to_string(),
|
||||
benchmark_string_ordinalize => ordinalize => "1".to_string(),
|
||||
benchmark_string_deordinalize => deordinalize => "1st".to_string(),
|
||||
benchmark_string_to_foreign_key => to_foreign_key => "Foo::Bar".to_string(),
|
||||
benchmark_string_is_foreign_key => is_foreign_key => "bar_id".to_string()
|
||||
];
|
||||
|
||||
#[cfg(feature = "heavyweight")]
|
||||
benchmarks![
|
||||
benchmark_str_to_class => to_class_case => "foo",
|
||||
benchmark_str_is_class => is_class_case => "Foo",
|
||||
benchmark_str_to_table => to_table_case => "fooBar",
|
||||
benchmark_str_is_table => is_table_case => "foo_bars",
|
||||
benchmark_str_pluralize => to_plural => "crate",
|
||||
benchmark_str_singular => to_singular => "crates",
|
||||
benchmark_string_to_class => to_class_case => "foo".to_string(),
|
||||
benchmark_string_is_class => is_class_case => "Foo".to_string(),
|
||||
benchmark_string_to_table => to_table_case => "fooBar".to_string(),
|
||||
benchmark_string_is_table => is_table_case => "foo_bars".to_string(),
|
||||
benchmark_string_pluralize => to_plural => "crate".to_string(),
|
||||
benchmark_string_singular => to_singular => "crates".to_string(),
|
||||
benchmark_string_demodulize => demodulize => "Foo::Bar".to_string(),
|
||||
benchmark_string_deconstantize => deconstantize => "Foo::Bar".to_string(),
|
||||
benchmark_str_demodulize => demodulize => "Foo::Bar",
|
||||
benchmark_str_deconstantize => deconstantize => "Foo::Bar"
|
||||
];
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
/// Deorginalizes a `&str`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::numbers::deordinalize::deordinalize;
|
||||
/// let mock_string: &str = "0.1";
|
||||
/// let expected_string: String = "0.1".to_owned();
|
||||
/// let asserted_string: String = deordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::deordinalize::deordinalize;
|
||||
/// let mock_string: &str = "-1st";
|
||||
/// let expected_string: String = "-1".to_owned();
|
||||
/// let asserted_string: String = deordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::deordinalize::deordinalize;
|
||||
/// let mock_string: &str = "0th";
|
||||
/// let expected_string: String = "0".to_owned();
|
||||
/// let asserted_string: String = deordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::deordinalize::deordinalize;
|
||||
/// let mock_string: &str = "1st";
|
||||
/// let expected_string: String = "1".to_owned();
|
||||
/// let asserted_string: String = deordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::deordinalize::deordinalize;
|
||||
/// let mock_string: &str = "2nd";
|
||||
/// let expected_string: String = "2".to_owned();
|
||||
/// let asserted_string: String = deordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::deordinalize::deordinalize;
|
||||
/// let mock_string: &str = "3rd";
|
||||
/// let expected_string: String = "3".to_owned();
|
||||
/// let asserted_string: String = deordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::deordinalize::deordinalize;
|
||||
/// let mock_string: &str = "9th";
|
||||
/// let expected_string: String = "9".to_owned();
|
||||
/// let asserted_string: String = deordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::deordinalize::deordinalize;
|
||||
/// let mock_string: &str = "12th";
|
||||
/// let expected_string: String = "12".to_owned();
|
||||
/// let asserted_string: String = deordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::deordinalize::deordinalize;
|
||||
/// let mock_string: &str = "12000th";
|
||||
/// let expected_string: String = "12000".to_owned();
|
||||
/// let asserted_string: String = deordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::deordinalize::deordinalize;
|
||||
/// let mock_string: &str = "12001th";
|
||||
/// let expected_string: String = "12001".to_owned();
|
||||
/// let asserted_string: String = deordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::deordinalize::deordinalize;
|
||||
/// let mock_string: &str = "12002nd";
|
||||
/// let expected_string: String = "12002".to_owned();
|
||||
/// let asserted_string: String = deordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::deordinalize::deordinalize;
|
||||
/// let mock_string: &str = "12003rd";
|
||||
/// let expected_string: String = "12003".to_owned();
|
||||
/// let asserted_string: String = deordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::deordinalize::deordinalize;
|
||||
/// let mock_string: &str = "12004th";
|
||||
/// let expected_string: String = "12004".to_owned();
|
||||
/// let asserted_string: String = deordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
pub fn deordinalize(non_ordinalized_string: &str) -> String {
|
||||
if non_ordinalized_string.contains('.') {
|
||||
non_ordinalized_string.to_owned()
|
||||
} else {
|
||||
non_ordinalized_string.trim_end_matches("st")
|
||||
.trim_end_matches("nd")
|
||||
.trim_end_matches("rd")
|
||||
.trim_end_matches("th")
|
||||
.to_owned()
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
#![deny(warnings)]
|
||||
/// Provides ordinalization of a string.
|
||||
///
|
||||
/// Example string "1" becomes "1st"
|
||||
pub mod ordinalize;
|
||||
/// Provides deordinalization of a string.
|
||||
///
|
||||
/// Example string "1st" becomes "1"
|
||||
pub mod deordinalize;
|
|
@ -1,147 +0,0 @@
|
|||
/// Orginalizes a `&str`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::numbers::ordinalize::ordinalize;
|
||||
/// let mock_string: &str = "a";
|
||||
/// let expected_string: String = "a".to_owned();
|
||||
/// let asserted_string: String = ordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::ordinalize::ordinalize;
|
||||
/// let mock_string: &str = "0.1";
|
||||
/// let expected_string: String = "0.1".to_owned();
|
||||
/// let asserted_string: String = ordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::ordinalize::ordinalize;
|
||||
/// let mock_string: &str = "-1";
|
||||
/// let expected_string: String = "-1st".to_owned();
|
||||
/// let asserted_string: String = ordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::ordinalize::ordinalize;
|
||||
/// let mock_string: &str = "0";
|
||||
/// let expected_string: String = "0th".to_owned();
|
||||
/// let asserted_string: String = ordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::ordinalize::ordinalize;
|
||||
/// let mock_string: &str = "1";
|
||||
/// let expected_string: String = "1st".to_owned();
|
||||
/// let asserted_string: String = ordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::ordinalize::ordinalize;
|
||||
/// let mock_string: &str = "2";
|
||||
/// let expected_string: String = "2nd".to_owned();
|
||||
/// let asserted_string: String = ordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::ordinalize::ordinalize;
|
||||
/// let mock_string: &str = "3";
|
||||
/// let expected_string: String = "3rd".to_owned();
|
||||
/// let asserted_string: String = ordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::ordinalize::ordinalize;
|
||||
/// let mock_string: &str = "9";
|
||||
/// let expected_string: String = "9th".to_owned();
|
||||
/// let asserted_string: String = ordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::ordinalize::ordinalize;
|
||||
/// let mock_string: &str = "12";
|
||||
/// let expected_string: String = "12th".to_owned();
|
||||
/// let asserted_string: String = ordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::ordinalize::ordinalize;
|
||||
/// let mock_string: &str = "12000";
|
||||
/// let expected_string: String = "12000th".to_owned();
|
||||
/// let asserted_string: String = ordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::ordinalize::ordinalize;
|
||||
/// let mock_string: &str = "12001";
|
||||
/// let expected_string: String = "12001st".to_owned();
|
||||
/// let asserted_string: String = ordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::ordinalize::ordinalize;
|
||||
/// let mock_string: &str = "12002";
|
||||
/// let expected_string: String = "12002nd".to_owned();
|
||||
/// let asserted_string: String = ordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::ordinalize::ordinalize;
|
||||
/// let mock_string: &str = "12003";
|
||||
/// let expected_string: String = "12003rd".to_owned();
|
||||
/// let asserted_string: String = ordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::numbers::ordinalize::ordinalize;
|
||||
/// let mock_string: &str = "12004";
|
||||
/// let expected_string: String = "12004th".to_owned();
|
||||
/// let asserted_string: String = ordinalize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
pub fn ordinalize(non_ordinalized_string: &str) -> String {
|
||||
let chars: Vec<char> = non_ordinalized_string.clone().chars().collect();
|
||||
let last_number: char = chars[chars.len() - 1];
|
||||
if is_ordinalizable(last_number) {
|
||||
return non_ordinalized_string.to_owned();
|
||||
}
|
||||
if chars.len() > 1 {
|
||||
if second_last_number_is_one(chars) {
|
||||
return format!("{}{}", non_ordinalized_string, "th");
|
||||
} else if string_contains_decimal(non_ordinalized_string.to_owned()) {
|
||||
return non_ordinalized_string.to_owned();
|
||||
}
|
||||
}
|
||||
match last_number {
|
||||
'1' => format!("{}{}", non_ordinalized_string, "st"),
|
||||
'2' => format!("{}{}", non_ordinalized_string, "nd"),
|
||||
'3' => format!("{}{}", non_ordinalized_string, "rd"),
|
||||
_ => format!("{}{}", non_ordinalized_string, "th"),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_ordinalizable(last_number: char) -> bool {
|
||||
!last_number.is_numeric()
|
||||
}
|
||||
|
||||
fn second_last_number_is_one(chars: Vec<char>) -> bool {
|
||||
let second_last_number: char = chars[chars.len() - 2];
|
||||
second_last_number == '1'
|
||||
}
|
||||
|
||||
fn string_contains_decimal(non_ordinalized_string: String) -> bool {
|
||||
non_ordinalized_string.contains('.')
|
||||
}
|
|
@ -1,225 +0,0 @@
|
|||
pub const UNACCONTABLE_WORDS: [&'static str; 202] = ["accommodation",
|
||||
"adulthood",
|
||||
"advertising",
|
||||
"advice",
|
||||
"aggression",
|
||||
"aid",
|
||||
"air",
|
||||
"aircraft",
|
||||
"alcohol",
|
||||
"anger",
|
||||
"applause",
|
||||
"arithmetic",
|
||||
"assistance",
|
||||
"athletics",
|
||||
|
||||
"bacon",
|
||||
"baggage",
|
||||
"beef",
|
||||
"biology",
|
||||
"blood",
|
||||
"botany",
|
||||
"bread",
|
||||
"butter",
|
||||
|
||||
"carbon",
|
||||
"cardboard",
|
||||
"cash",
|
||||
"chalk",
|
||||
"chaos",
|
||||
"chess",
|
||||
"crossroads",
|
||||
"countryside",
|
||||
|
||||
"dancing",
|
||||
"deer",
|
||||
"dignity",
|
||||
"dirt",
|
||||
"dust",
|
||||
|
||||
"economics",
|
||||
"education",
|
||||
"electricity",
|
||||
"engineering",
|
||||
"enjoyment",
|
||||
"envy",
|
||||
"equipment",
|
||||
"ethics",
|
||||
"evidence",
|
||||
"evolution",
|
||||
|
||||
"fame",
|
||||
"fiction",
|
||||
"flour",
|
||||
"flu",
|
||||
"food",
|
||||
"fuel",
|
||||
"fun",
|
||||
"furniture",
|
||||
|
||||
"gallows",
|
||||
"garbage",
|
||||
"garlic",
|
||||
"genetics",
|
||||
"gold",
|
||||
"golf",
|
||||
"gossip",
|
||||
"grammar",
|
||||
"gratitude",
|
||||
"grief",
|
||||
"guilt",
|
||||
"gymnastics",
|
||||
|
||||
"happiness",
|
||||
"hardware",
|
||||
"harm",
|
||||
"hate",
|
||||
"hatred",
|
||||
"health",
|
||||
"heat",
|
||||
"help",
|
||||
"homework",
|
||||
"honesty",
|
||||
"honey",
|
||||
"hospitality",
|
||||
"housework",
|
||||
"humour",
|
||||
"hunger",
|
||||
"hydrogen",
|
||||
|
||||
"ice",
|
||||
"importance",
|
||||
"inflation",
|
||||
"information",
|
||||
"innocence",
|
||||
"iron",
|
||||
"irony",
|
||||
|
||||
"jam",
|
||||
"jewelry",
|
||||
"judo",
|
||||
|
||||
"karate",
|
||||
"knowledge",
|
||||
|
||||
"lack",
|
||||
"laughter",
|
||||
"lava",
|
||||
"leather",
|
||||
"leisure",
|
||||
"lightning",
|
||||
"linguine",
|
||||
"linguini",
|
||||
"linguistics",
|
||||
"literature",
|
||||
"litter",
|
||||
"livestock",
|
||||
"logic",
|
||||
"loneliness",
|
||||
"luck",
|
||||
"luggage",
|
||||
|
||||
"macaroni",
|
||||
"machinery",
|
||||
"magic",
|
||||
"management",
|
||||
"mankind",
|
||||
"marble",
|
||||
"mathematics",
|
||||
"mayonnaise",
|
||||
"measles",
|
||||
"methane",
|
||||
"milk",
|
||||
"money",
|
||||
"mud",
|
||||
"music",
|
||||
"mumps",
|
||||
|
||||
"nature",
|
||||
"news",
|
||||
"nitrogen",
|
||||
"nonsense",
|
||||
"nurture",
|
||||
"nutrition",
|
||||
|
||||
"obedience",
|
||||
"obesity",
|
||||
"oxygen",
|
||||
|
||||
"pasta",
|
||||
"patience",
|
||||
"physics",
|
||||
"poetry",
|
||||
"pollution",
|
||||
"poverty",
|
||||
"pride",
|
||||
"psychology",
|
||||
"publicity",
|
||||
"punctuation",
|
||||
|
||||
"quartz",
|
||||
|
||||
"racism",
|
||||
"relaxation",
|
||||
"reliability",
|
||||
"research",
|
||||
"respect",
|
||||
"revenge",
|
||||
"rice",
|
||||
"rubbish",
|
||||
"rum",
|
||||
|
||||
"safety",
|
||||
"scenery",
|
||||
"seafood",
|
||||
"seaside",
|
||||
"series",
|
||||
"shame",
|
||||
"sheep",
|
||||
"shopping",
|
||||
"sleep",
|
||||
"smoke",
|
||||
"smoking",
|
||||
"snow",
|
||||
"soap",
|
||||
"software",
|
||||
"soil",
|
||||
"spaghetti",
|
||||
"species",
|
||||
"steam",
|
||||
"stuff",
|
||||
"stupidity",
|
||||
"sunshine",
|
||||
"symmetry",
|
||||
|
||||
"tennis",
|
||||
"thirst",
|
||||
"thunder",
|
||||
"timber",
|
||||
"traffic",
|
||||
"transportation",
|
||||
"trust",
|
||||
|
||||
"underwear",
|
||||
"unemployment",
|
||||
"unity",
|
||||
|
||||
"validity",
|
||||
"veal",
|
||||
"vegetation",
|
||||
"vegetarianism",
|
||||
"vengeance",
|
||||
"violence",
|
||||
"vitality",
|
||||
|
||||
"warmth",
|
||||
"wealth",
|
||||
"weather",
|
||||
"welfare",
|
||||
"wheat",
|
||||
"wildlife",
|
||||
"wisdom",
|
||||
"yoga",
|
||||
|
||||
"zinc",
|
||||
"zoology"];
|
|
@ -1,50 +0,0 @@
|
|||
#[cfg(feature = "heavyweight")]
|
||||
use cases::classcase::to_class_case;
|
||||
|
||||
#[cfg(feature = "heavyweight")]
|
||||
/// Deconstantizes a `&str`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::string::deconstantize::deconstantize;
|
||||
/// let mock_string: &str = "Bar";
|
||||
/// let expected_string: String = "".to_owned();
|
||||
/// let asserted_string: String = deconstantize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::string::deconstantize::deconstantize;
|
||||
/// let mock_string: &str = "::Bar";
|
||||
/// let expected_string: String = "".to_owned();
|
||||
/// let asserted_string: String = deconstantize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::string::deconstantize::deconstantize;
|
||||
/// let mock_string: &str = "Foo::Bar";
|
||||
/// let expected_string: String = "Foo".to_owned();
|
||||
/// let asserted_string: String = deconstantize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::string::deconstantize::deconstantize;
|
||||
/// let mock_string: &str = "Test::Foo::Bar";
|
||||
/// let expected_string: String = "Foo".to_owned();
|
||||
/// let asserted_string: String = deconstantize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
pub fn deconstantize(non_deconstantized_string: &str) -> String {
|
||||
if non_deconstantized_string.contains("::") {
|
||||
let split_string: Vec<&str> = non_deconstantized_string.split("::").collect();
|
||||
if split_string.len() > 1 {
|
||||
to_class_case(split_string[split_string.len() - 2])
|
||||
} else {
|
||||
"".to_owned()
|
||||
}
|
||||
} else {
|
||||
"".to_owned()
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
#[cfg(feature = "heavyweight")]
|
||||
use cases::classcase::to_class_case;
|
||||
|
||||
#[cfg(feature = "heavyweight")]
|
||||
/// Demodulize a `&str`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::string::demodulize::demodulize;
|
||||
/// let mock_string: &str = "Bar";
|
||||
/// let expected_string: String = "Bar".to_owned();
|
||||
/// let asserted_string: String = demodulize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::string::demodulize::demodulize;
|
||||
/// let mock_string: &str = "::Bar";
|
||||
/// let expected_string: String = "Bar".to_owned();
|
||||
/// let asserted_string: String = demodulize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::string::demodulize::demodulize;
|
||||
/// let mock_string: &str = "Foo::Bar";
|
||||
/// let expected_string: String = "Bar".to_owned();
|
||||
/// let asserted_string: String = demodulize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::string::demodulize::demodulize;
|
||||
/// let mock_string: &str = "Test::Foo::Bar";
|
||||
/// let expected_string: String = "Bar".to_owned();
|
||||
/// let asserted_string: String = demodulize(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
pub fn demodulize(non_demodulize_string: &str) -> String {
|
||||
if non_demodulize_string.contains("::") {
|
||||
let split_string: Vec<&str> = non_demodulize_string.split("::").collect();
|
||||
to_class_case(split_string[split_string.len() - 1])
|
||||
} else {
|
||||
non_demodulize_string.to_owned()
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
#![deny(warnings)]
|
||||
/// Provides demodulize a string.
|
||||
///
|
||||
/// Example string `Foo::Bar` becomes `Bar`
|
||||
#[cfg(feature = "heavyweight")]
|
||||
pub mod demodulize;
|
||||
/// Provides deconstantizea string.
|
||||
///
|
||||
/// Example string `Foo::Bar` becomes `Foo`
|
||||
#[cfg(feature = "heavyweight")]
|
||||
pub mod deconstantize;
|
||||
/// Provides conversion to plural strings.
|
||||
///
|
||||
/// Example string `FooBar` -> `FooBars`
|
||||
#[cfg(feature = "heavyweight")]
|
||||
pub mod pluralize;
|
||||
/// Provides conversion to singular strings.
|
||||
///
|
||||
/// Example string `FooBars` -> `FooBar`
|
||||
#[cfg(feature = "heavyweight")]
|
||||
pub mod singularize;
|
||||
|
||||
mod constants;
|
|
@ -1,194 +0,0 @@
|
|||
#![deny(warnings)]
|
||||
use regex::Regex;
|
||||
use string::constants::UNACCONTABLE_WORDS;
|
||||
|
||||
macro_rules! add_rule{
|
||||
($r:ident, $rule:expr => $replace:expr) => {
|
||||
$r.push((Regex::new($rule).unwrap(), $replace));
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! rules{
|
||||
($r:ident; $($rule:expr => $replace:expr), *) => {
|
||||
$(
|
||||
add_rule!{$r, $rule => $replace}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lazy_static!{
|
||||
static ref RULES: Vec<(Regex, &'static str)> = {
|
||||
let mut r = Vec::with_capacity(24);
|
||||
rules![r;
|
||||
r"(\w*)s$" => "s",
|
||||
r"(\w*([^aeiou]ese))$" => "",
|
||||
r"(\w*(ax|test))is$" => "es",
|
||||
r"(\w*(alias|[^aou]us|tlas|gas|ris))$" => "es",
|
||||
r"(\w*(e[mn]u))s?$" => "s",
|
||||
r"(\w*([^l]ias|[aeiou]las|[emjzr]as|[iu]am))$" => "",
|
||||
r"(\w*(alumn|syllab|octop|vir|radi|nucle|fung|cact|stimul|termin|bacill|foc|uter|loc|strat))(?:us|i)$" => "i",
|
||||
r"(\w*(alumn|alg|vertebr))(?:a|ae)$" => "ae",
|
||||
r"(\w*(seraph|cherub))(?:im)?$" => "im",
|
||||
r"(\w*(her|at|gr))o$" => "oes",
|
||||
r"(\w*(agend|addend|millenni|dat|extrem|bacteri|desiderat|strat|candelabr|errat|ov|symposi|curricul|automat|quor))(?:a|um)$" => "a",
|
||||
r"(\w*(apheli|hyperbat|periheli|asyndet|noumen|phenomen|criteri|organ|prolegomen|hedr|automat))(?:a|on)$" => "a",
|
||||
r"(\w*)sis$" => "ses",
|
||||
r"(\w*(kni|wi|li))fe$" => "ves",
|
||||
r"(\w*(ar|l|ea|eo|oa|hoo))f$" => "ves",
|
||||
r"(\w*([^aeiouy]|qu))y$" => "ies",
|
||||
r"(\w*([^ch][ieo][ln]))ey$" => "ies",
|
||||
r"(\w*(x|ch|ss|sh|zz)es)$" => "",
|
||||
r"(\w*(x|ch|ss|sh|zz))$" => "es",
|
||||
r"(\w*(matr|cod|mur|sil|vert|ind|append))(?:ix|ex)$" => "ices",
|
||||
r"(\w*(m|l)(?:ice|ouse))$" => "ice",
|
||||
r"(\w*(pe)(?:rson|ople))$" => "ople",
|
||||
r"(\w*(child))(?:ren)?$" => "ren",
|
||||
r"(\w*eaux)$" => ""
|
||||
];
|
||||
r
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! special_cases{
|
||||
($s:ident, $($singular: expr => $plural:expr), *) => {
|
||||
match &$s[..] {
|
||||
$(
|
||||
$singular => {
|
||||
return $plural.to_owned();
|
||||
},
|
||||
)*
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Converts a `&str` to pluralized `String`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::string::pluralize::to_plural;
|
||||
/// let mock_string: &str = "foo_bar";
|
||||
/// let expected_string: String = "foo_bars".to_owned();
|
||||
/// let asserted_string: String = to_plural(mock_string);
|
||||
/// assert_eq!(asserted_string, expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::string::pluralize::to_plural;
|
||||
/// let mock_string: &str = "ox";
|
||||
/// let expected_string: String = "oxen".to_owned();
|
||||
/// let asserted_string: String = to_plural(mock_string);
|
||||
/// assert_eq!(asserted_string, expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::string::pluralize::to_plural;
|
||||
/// let mock_string: &str = "crate";
|
||||
/// let expected_string: String = "crates".to_owned();
|
||||
/// let asserted_string: String = to_plural(mock_string);
|
||||
/// assert_eq!(asserted_string, expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::string::pluralize::to_plural;
|
||||
/// let mock_string: &str = "boxes";
|
||||
/// let expected_string: String = "boxes".to_owned();
|
||||
/// let asserted_string: String = to_plural(mock_string);
|
||||
/// assert_eq!(asserted_string, expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::string::pluralize::to_plural;
|
||||
/// let mock_string: &str = "vengeance";
|
||||
/// let expected_string: String = "vengeance".to_owned();
|
||||
/// let asserted_string: String = to_plural(mock_string);
|
||||
/// assert_eq!(asserted_string, expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::string::pluralize::to_plural;
|
||||
/// let mock_string: &str = "yoga";
|
||||
/// let expected_string: String = "yoga".to_owned();
|
||||
/// let asserted_string: String = to_plural(mock_string);
|
||||
/// assert_eq!(asserted_string, expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::string::pluralize::to_plural;
|
||||
/// let mock_string: &str = "geometry";
|
||||
/// let expected_string: String = "geometries".to_owned();
|
||||
/// let asserted_string: String = to_plural(mock_string);
|
||||
/// assert_eq!(asserted_string, expected_string);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
pub fn to_plural(non_plural_string: &str) -> String {
|
||||
if UNACCONTABLE_WORDS.contains(&non_plural_string.as_ref()) {
|
||||
non_plural_string.to_owned()
|
||||
} else {
|
||||
special_cases![non_plural_string,
|
||||
"ox" => "oxen",
|
||||
"man" => "men",
|
||||
"woman" => "women",
|
||||
"die" => "dice",
|
||||
"yes" => "yeses",
|
||||
"foot" => "feet",
|
||||
"eave" => "eaves",
|
||||
"goose" => "geese",
|
||||
"tooth" => "teeth",
|
||||
"quiz" => "quizzes"
|
||||
];
|
||||
for &(ref rule, replace) in RULES.iter().rev() {
|
||||
if let Some(c) = rule.captures(&non_plural_string) {
|
||||
if let Some(c) = c.get(1) {
|
||||
return format!("{}{}", c.as_str(), replace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
format!("{}s", non_plural_string)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
macro_rules! as_item {
|
||||
($i:item) => { $i };
|
||||
}
|
||||
|
||||
macro_rules! make_tests{
|
||||
($($singular:ident => $plural:ident); *) =>{
|
||||
$(
|
||||
as_item! {
|
||||
#[test]
|
||||
fn $singular(){
|
||||
assert_eq!(
|
||||
stringify!($plural),
|
||||
super::to_plural(stringify!($singular))
|
||||
);
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boxes() {
|
||||
assert_eq!("boxes", super::to_plural("box"));
|
||||
}
|
||||
|
||||
make_tests!{
|
||||
geometry => geometries;
|
||||
ox => oxen;
|
||||
woman => women;
|
||||
test => tests;
|
||||
axis => axes;
|
||||
knife => knives;
|
||||
agendum => agenda;
|
||||
elf => elves;
|
||||
zoology => zoology
|
||||
}
|
||||
}
|
|
@ -1,189 +0,0 @@
|
|||
use regex::Regex;
|
||||
use string::constants::UNACCONTABLE_WORDS;
|
||||
|
||||
macro_rules! special_cases{
|
||||
($s:ident, $($singular: expr => $plural:expr), *) => {
|
||||
match &$s[..] {
|
||||
$(
|
||||
$singular => {
|
||||
return $plural.to_owned();
|
||||
},
|
||||
)*
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Converts a `&str` to singularized `String`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::string::singularize::to_singular;
|
||||
/// let mock_string: &str = "foo_bars";
|
||||
/// let expected_string: String = "foo_bar".to_owned();
|
||||
/// let asserted_string: String = to_singular(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::string::singularize::to_singular;
|
||||
/// let mock_string: &str = "oxen";
|
||||
/// let expected_string: String = "ox".to_owned();
|
||||
/// let asserted_string: String = to_singular(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::string::singularize::to_singular;
|
||||
/// let mock_string: &str = "crates";
|
||||
/// let expected_string: String = "crate".to_owned();
|
||||
/// let asserted_string: String = to_singular(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::string::singularize::to_singular;
|
||||
/// let mock_string: &str = "oxen";
|
||||
/// let expected_string: String = "ox".to_owned();
|
||||
/// let asserted_string: String = to_singular(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::string::singularize::to_singular;
|
||||
/// let mock_string: &str = "boxes";
|
||||
/// let expected_string: String = "box".to_owned();
|
||||
/// let asserted_string: String = to_singular(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::string::singularize::to_singular;
|
||||
/// let mock_string: &str = "vengeance";
|
||||
/// let expected_string: String = "vengeance".to_owned();
|
||||
/// let asserted_string: String = to_singular(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::string::singularize::to_singular;
|
||||
/// let mock_string: &str = "yoga";
|
||||
/// let expected_string: String = "yoga".to_owned();
|
||||
/// let asserted_string: String = to_singular(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
pub fn to_singular(non_singular_string: &str) -> String {
|
||||
if UNACCONTABLE_WORDS.contains(&non_singular_string.as_ref()) {
|
||||
non_singular_string.to_owned()
|
||||
} else {
|
||||
special_cases![non_singular_string,
|
||||
"oxen" => "ox",
|
||||
"boxes" => "box",
|
||||
"men" => "man",
|
||||
"women" => "woman",
|
||||
"dice" => "die",
|
||||
"yeses" => "yes",
|
||||
"feet" => "foot",
|
||||
"eaves" => "eave",
|
||||
"geese" => "goose",
|
||||
"teeth" => "tooth",
|
||||
"quizzes" => "quiz"
|
||||
];
|
||||
for &(ref rule, replace) in RULES.iter().rev() {
|
||||
if let Some(captures) = rule.captures(&non_singular_string) {
|
||||
if let Some(c) = captures.get(1) {
|
||||
let mut buf = String::new();
|
||||
captures.expand(&format!("{}{}", c.as_str(), replace), &mut buf);
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
format!("{}", non_singular_string)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! add_rule{
|
||||
($r:ident, $rule:expr => $replace:expr) => {
|
||||
$r.push((Regex::new($rule).unwrap(), $replace));
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! rules{
|
||||
($r:ident; $($rule:expr => $replace:expr), *) => {
|
||||
$(
|
||||
add_rule!{$r, $rule => $replace}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lazy_static!{
|
||||
static ref RULES: Vec<(Regex, &'static str)> = {
|
||||
let mut r = Vec::with_capacity(27);
|
||||
rules![r;
|
||||
r"(\w*)s$" => "",
|
||||
r"(\w*)(ss)$" => "$2",
|
||||
r"(n)ews$" => "ews",
|
||||
r"(\w*)(o)es$" => "",
|
||||
r"(\w*)([ti])a$" => "um",
|
||||
r"((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$" => "sis",
|
||||
r"(^analy)(sis|ses)$" => "sis",
|
||||
r"(\w*)([^f])ves$" => "fe",
|
||||
r"(\w*)(hive)s$" => "",
|
||||
r"(\w*)(tive)s$" => "",
|
||||
r"(\w*)([lr])ves$" => "f",
|
||||
r"(\w*([^aeiouy]|qu))ies$" => "y",
|
||||
r"(s)eries$" => "eries",
|
||||
r"(m)ovies$" => "ovie",
|
||||
r"(\w*)(x|ch|ss|sh)es$" => "$2",
|
||||
r"(m|l)ice$" => "ouse",
|
||||
r"(bus)(es)?$" => "",
|
||||
r"(shoe)s$" => "",
|
||||
r"(cris|test)(is|es)$" => "is",
|
||||
r"^(a)x[ie]s$" => "xis",
|
||||
r"(octop|vir)(us|i)$" => "us",
|
||||
r"(alias|status)(es)?$" => "",
|
||||
r"^(ox)en" => "",
|
||||
r"(vert|ind)ices$" => "ex",
|
||||
r"(matr)ices$" => "ix",
|
||||
r"(quiz)zes$" => "",
|
||||
r"(database)s$" => ""
|
||||
];
|
||||
r
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn singularize_ies_suffix() {
|
||||
assert_eq!("reply", to_singular("replies"));
|
||||
assert_eq!("lady", to_singular("ladies"));
|
||||
assert_eq!("soliloquy", to_singular("soliloquies"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn singularize_ss_suffix() {
|
||||
assert_eq!("glass", to_singular("glass"));
|
||||
assert_eq!("access", to_singular("access"));
|
||||
assert_eq!("glass", to_singular("glasses"));
|
||||
assert_eq!("witch", to_singular("witches"));
|
||||
assert_eq!("dish", to_singular("dishes"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn singularize_string_if_a_regex_will_match() {
|
||||
let expected_string: String = "ox".to_owned();
|
||||
let asserted_string: String = to_singular("oxen");
|
||||
assert!(expected_string == asserted_string);
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn singularize_string_returns_none_option_if_no_match() {
|
||||
let expected_string: String = "bacon".to_owned();
|
||||
let asserted_string: String = to_singular("bacon");
|
||||
|
||||
assert!(expected_string == asserted_string);
|
||||
}
|
|
@ -1,139 +0,0 @@
|
|||
use cases::snakecase::to_snake_case;
|
||||
|
||||
/// Converts a `&str` to a `foreign_key`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::suffix::foreignkey::to_foreign_key;
|
||||
/// let mock_string: &str = "foo_bar";
|
||||
/// let expected_string: String = "foo_bar_id".to_owned();
|
||||
/// let asserted_string: String = to_foreign_key(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::suffix::foreignkey::to_foreign_key;
|
||||
/// let mock_string: &str = "Foo bar";
|
||||
/// let expected_string: String = "foo_bar_id".to_owned();
|
||||
/// let asserted_string: String = to_foreign_key(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::suffix::foreignkey::to_foreign_key;
|
||||
/// let mock_string: &str = "Foo Bar";
|
||||
/// let expected_string: String = "foo_bar_id".to_owned();
|
||||
/// let asserted_string: String = to_foreign_key(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::suffix::foreignkey::to_foreign_key;
|
||||
/// let mock_string: &str = "Foo::Bar";
|
||||
/// let expected_string: String = "bar_id".to_owned();
|
||||
/// let asserted_string: String = to_foreign_key(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::suffix::foreignkey::to_foreign_key;
|
||||
/// let mock_string: &str = "Test::Foo::Bar";
|
||||
/// let expected_string: String = "bar_id".to_owned();
|
||||
/// let asserted_string: String = to_foreign_key(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::suffix::foreignkey::to_foreign_key;
|
||||
/// let mock_string: &str = "FooBar";
|
||||
/// let expected_string: String = "foo_bar_id".to_owned();
|
||||
/// let asserted_string: String = to_foreign_key(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::suffix::foreignkey::to_foreign_key;
|
||||
/// let mock_string: &str = "fooBar";
|
||||
/// let expected_string: String = "foo_bar_id".to_owned();
|
||||
/// let asserted_string: String = to_foreign_key(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::suffix::foreignkey::to_foreign_key;
|
||||
/// let mock_string: &str = "fooBar3";
|
||||
/// let expected_string: String = "foo_bar_3_id".to_owned();
|
||||
/// let asserted_string: String = to_foreign_key(mock_string);
|
||||
/// assert!(asserted_string == expected_string);
|
||||
///
|
||||
/// ```
|
||||
pub fn to_foreign_key(non_foreign_key_string: &str) -> String {
|
||||
if non_foreign_key_string.contains("::") {
|
||||
let split_string: Vec<&str> = non_foreign_key_string.split("::").collect();
|
||||
safe_convert(split_string[split_string.len() - 1])
|
||||
} else {
|
||||
safe_convert(non_foreign_key_string)
|
||||
}
|
||||
}
|
||||
fn safe_convert(safe_string: &str) -> String {
|
||||
let snake_cased: String = to_snake_case(safe_string);
|
||||
if snake_cased.ends_with("_id") {
|
||||
snake_cased
|
||||
} else {
|
||||
format!("{}{}", snake_cased, "_id")
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines if a `&str` is a `foreign_key`
|
||||
///
|
||||
/// ```
|
||||
/// use inflector::suffix::foreignkey::is_foreign_key;
|
||||
/// let mock_string: &str = "Foo bar string that is really really long";
|
||||
/// let asserted_bool: bool = is_foreign_key(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::suffix::foreignkey::is_foreign_key;
|
||||
/// let mock_string: &str = "foo-bar-string-that-is-really-really-long";
|
||||
/// let asserted_bool: bool = is_foreign_key(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::suffix::foreignkey::is_foreign_key;
|
||||
/// let mock_string: &str = "FooBarIsAReallyReallyLongString";
|
||||
/// let asserted_bool: bool = is_foreign_key(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::suffix::foreignkey::is_foreign_key;
|
||||
/// let mock_string: &str = "Foo Bar Is A Really Really Long String";
|
||||
/// let asserted_bool: bool = is_foreign_key(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::suffix::foreignkey::is_foreign_key;
|
||||
/// let mock_string: &str = "fooBarIsAReallyReallyLongString";
|
||||
/// let asserted_bool: bool = is_foreign_key(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::suffix::foreignkey::is_foreign_key;
|
||||
/// let mock_string: &str = "foo_bar_string_that_is_really_really_long";
|
||||
/// let asserted_bool: bool = is_foreign_key(mock_string);
|
||||
/// assert!(asserted_bool == false);
|
||||
///
|
||||
/// ```
|
||||
/// ```
|
||||
/// use inflector::suffix::foreignkey::is_foreign_key;
|
||||
/// let mock_string: &str = "foo_bar_string_that_is_really_really_long_id";
|
||||
/// let asserted_bool: bool = is_foreign_key(mock_string);
|
||||
/// assert!(asserted_bool == true);
|
||||
///
|
||||
/// ```
|
||||
pub fn is_foreign_key(test_string: &str) -> bool {
|
||||
to_foreign_key(test_string.clone()) == test_string
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
#![deny(warnings)]
|
||||
/// Provides foreign key conversion for String.
|
||||
///
|
||||
/// Example string `foo` becomes `foo_id`
|
||||
pub mod foreignkey;
|
|
@ -1,162 +0,0 @@
|
|||
#![deny(warnings)]
|
||||
extern crate inflector;
|
||||
|
||||
use inflector::Inflector;
|
||||
use inflector::InflectorNumbers;
|
||||
|
||||
macro_rules! str_tests {
|
||||
( $($test_name:ident => $imp_trait:ident => $to_cast:expr => $casted:expr), *) => {
|
||||
$(
|
||||
#[test]
|
||||
fn $test_name() {
|
||||
assert_eq!($to_cast.$imp_trait(), $casted)
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! string_tests {
|
||||
( $($test_name:ident => $imp_trait:ident => $to_cast:expr => $casted:expr), *) => {
|
||||
$(
|
||||
#[test]
|
||||
fn $test_name() {
|
||||
assert_eq!($to_cast.to_string().$imp_trait(), $casted)
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! number_tests {
|
||||
( $($test_name:ident => $imp_trait:ident => $typ:ident => $to_cast:expr => $casted:expr), *) => {
|
||||
$(
|
||||
#[test]
|
||||
fn $test_name() {
|
||||
let to_cast: $typ = $to_cast;
|
||||
assert_eq!(to_cast.$imp_trait(), $casted)
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! gated_str_tests {
|
||||
( $($test_name:ident => $imp_trait:ident => $to_cast:expr => $casted:expr), *) => {
|
||||
$(
|
||||
#[test]
|
||||
#[cfg(feature = "heavyweight")]
|
||||
fn $test_name() {
|
||||
assert_eq!($to_cast.$imp_trait(), $casted)
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! gated_string_tests {
|
||||
( $($test_name:ident => $imp_trait:ident => $to_cast:expr => $casted:expr), *) => {
|
||||
$(
|
||||
#[test]
|
||||
#[cfg(feature = "heavyweight")]
|
||||
fn $test_name() {
|
||||
assert_eq!($to_cast.to_string().$imp_trait(), $casted)
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
str_tests![
|
||||
str_to_camel => to_camel_case => "foo_bar" => "fooBar".to_string(),
|
||||
str_is_camel => is_camel_case => "fooBar" => true,
|
||||
str_is_not_camel => is_camel_case => "foo_bar" => false,
|
||||
str_to_screaming_snake => to_screaming_snake_case => "fooBar" => "FOO_BAR".to_string(),
|
||||
str_is_screaming_snake => is_screaming_snake_case => "FOO_BAR" => true,
|
||||
str_is_not_screaming_snake => is_screaming_snake_case => "foo_bar" => false,
|
||||
str_to_snake => to_snake_case => "fooBar" => "foo_bar".to_string(),
|
||||
str_is_snake => is_snake_case => "foo_bar" => true,
|
||||
str_is_not_snake => is_snake_case => "fooBar" => false,
|
||||
str_to_kebab => to_kebab_case => "fooBar" => "foo-bar".to_string(),
|
||||
str_is_kebab => is_kebab_case => "foo-bar" => true,
|
||||
str_is_not_kebab => is_kebab_case => "fooBar" => false,
|
||||
str_to_train => to_train_case => "fooBar" => "Foo-Bar".to_string(),
|
||||
str_is_train => is_train_case => "Foo-Bar" => true,
|
||||
str_is_not_train => is_train_case => "FOO-Bar" => false,
|
||||
str_to_sentence => to_sentence_case => "fooBar" => "Foo bar".to_string(),
|
||||
str_is_sentence => is_sentence_case => "Foo bar" => true,
|
||||
str_is_not_sentence => is_sentence_case => "foo_bar" => false,
|
||||
str_to_title => to_title_case => "fooBar" => "Foo Bar".to_string(),
|
||||
str_is_title => is_title_case => "Foo Bar" => true,
|
||||
str_is_not_title => is_title_case => "Foo_Bar" => false,
|
||||
str_ordinalize => ordinalize => "1" => "1st".to_string(),
|
||||
str_deordinalize => deordinalize => "1st" => "1".to_string(),
|
||||
str_to_foreign_key => to_foreign_key => "Foo::Bar" => "bar_id".to_string(),
|
||||
str_is_foreign_key => is_foreign_key => "bar_id" => true,
|
||||
str_is_not_foreign_key => is_foreign_key => "bar" => false
|
||||
];
|
||||
|
||||
gated_str_tests![
|
||||
str_to_class_case => to_class_case => "foo" => "Foo".to_string(),
|
||||
str_is_class_case => is_class_case => "Foo" => true,
|
||||
str_is_not_class_case => is_class_case => "foo" => false,
|
||||
str_to_table => to_table_case => "fooBar" => "foo_bars".to_string(),
|
||||
str_is_table => is_table_case => "foo_bars" => true,
|
||||
str_is_not_table => is_table_case => "fooBars" => false,
|
||||
str_pluralize => to_plural => "crate" => "crates".to_string(),
|
||||
str_singular => to_singular => "crates" => "crate".to_string(),
|
||||
str_demodulize => demodulize => "Foo::Bar" => "Bar".to_string(),
|
||||
str_deconstantize => deconstantize => "Foo::Bar" => "Foo".to_string()
|
||||
];
|
||||
|
||||
string_tests![
|
||||
string_to_camel => to_camel_case => "foo_bar".to_string() => "fooBar".to_string(),
|
||||
string_is_camel => is_camel_case => "fooBar".to_string() => true,
|
||||
string_is_not_camel => is_camel_case => "foo_bar".to_string() => false,
|
||||
string_to_screaming_snake => to_screaming_snake_case => "fooBar".to_string() => "FOO_BAR".to_string(),
|
||||
string_is_screaming_snake => is_screaming_snake_case => "FOO_BAR".to_string() => true,
|
||||
string_is_not_screaming_snake => is_screaming_snake_case => "foo_bar".to_string() => false,
|
||||
string_to_snake => to_snake_case => "fooBar".to_string() => "foo_bar".to_string(),
|
||||
string_is_snake => is_snake_case => "foo_bar".to_string() => true,
|
||||
string_is_not_snake => is_snake_case => "fooBar".to_string() => false,
|
||||
string_to_kebab => to_kebab_case => "fooBar".to_string() => "foo-bar".to_string(),
|
||||
string_is_kebab => is_kebab_case => "foo-bar".to_string() => true,
|
||||
string_is_not_kebab => is_kebab_case => "fooBar".to_string() => false,
|
||||
string_to_train => to_train_case => "fooBar".to_string() => "Foo-Bar".to_string(),
|
||||
string_is_train => is_train_case => "Foo-Bar".to_string() => true,
|
||||
string_is_not_train => is_train_case => "foo-Bar".to_string() => false,
|
||||
string_to_sentence => to_sentence_case => "fooBar".to_string() => "Foo bar".to_string(),
|
||||
string_is_sentence => is_sentence_case => "Foo bar".to_string() => true,
|
||||
string_is_not_sentence => is_sentence_case => "fooBar".to_string() => false,
|
||||
string_to_title => to_title_case => "fooBar".to_string() => "Foo Bar".to_string(),
|
||||
string_is_title => is_title_case => "Foo Bar".to_string() => true,
|
||||
string_is_not_title => is_title_case => "fooBar".to_string() => false,
|
||||
string_ordinalize => ordinalize => "1".to_string() => "1st".to_string(),
|
||||
string_deordinalize => deordinalize => "1st".to_string() => "1".to_string(),
|
||||
string_to_foreign_key => to_foreign_key => "Foo::Bar".to_string() => "bar_id".to_string(),
|
||||
string_is_foreign_key => is_foreign_key => "bar_id".to_string() => true,
|
||||
string_is_not_foreign_key => is_foreign_key => "bar".to_string() => false
|
||||
];
|
||||
|
||||
gated_string_tests![
|
||||
string_to_class_case => to_class_case => "foo".to_string() => "Foo".to_string(),
|
||||
string_is_class_case => is_class_case => "Foo".to_string() => true,
|
||||
string_is_not_class_case => is_class_case => "ooBar".to_string() => false,
|
||||
string_to_table => to_table_case => "fooBar".to_string() => "foo_bars".to_string(),
|
||||
string_is_table => is_table_case => "foo_bars".to_string() => true,
|
||||
string_is_not_table => is_table_case => "fooBar".to_string() => false,
|
||||
string_pluralize => to_plural => "crate".to_string() => "crates".to_string(),
|
||||
string_singular => to_singular => "crates".to_string() => "crate".to_string(),
|
||||
string_demodulize => demodulize => "Foo::Bar".to_string() => "Bar".to_string(),
|
||||
string_deconstantize => deconstantize => "Foo::Bar".to_string() => "Foo".to_string()
|
||||
];
|
||||
|
||||
number_tests![
|
||||
i8_ordinalize => ordinalize => i8 => 1 => "1st".to_string(),
|
||||
i16_ordinalize => ordinalize => i16 => 1 => "1st".to_string(),
|
||||
i32_ordinalize => ordinalize => i32 => 1 => "1st".to_string(),
|
||||
i64_ordinalize => ordinalize => i64 => 1 => "1st".to_string(),
|
||||
u8_ordinalize => ordinalize => u8 => 1 => "1st".to_string(),
|
||||
u16_ordinalize => ordinalize => u16 => 1 => "1st".to_string(),
|
||||
u32_ordinalize => ordinalize => u32 => 1 => "1st".to_string(),
|
||||
u64_ordinalize => ordinalize => u64 => 1 => "1st".to_string(),
|
||||
isize_ordinalize => ordinalize => isize => 1 => "1st".to_string(),
|
||||
usize_ordinalize => ordinalize => usize => 1 => "1st".to_string(),
|
||||
f32_ordinalize => ordinalize => f32 => 1.0 => "1st".to_string(),
|
||||
f64_ordinalize => ordinalize => f64 => 1.0 => "1st".to_string()
|
||||
];
|
|
@ -1 +0,0 @@
|
|||
{"files":{"Cargo.toml":"48df4570f3bbfba5b3c19731a54abf70ac8608e4dab6f4dae6a694b18d6ad102","LICENCE":"a24742368cf773bbb8b6f0fcbe86ca4b802c2b7c081bc8bebf14ac38618e7c63","README.md":"779b02ebacd6f4d08e01ef289bd7976a4467054f40355593817fd6df7e8c9dd4","examples/colours.rs":"e4870671adb9574607e37a0e4145643f9047c881c310113de114ec20d76aaf4b","src/ansi.rs":"b8f5de966e7ec2fba7a4d5a373d0aceafe19ea6e20a3f4daaf448e119c989ae7","src/debug.rs":"0ab28b65c39538825707d8b7e81c6f91c78310856c936bba0ee609e06d138543","src/difference.rs":"da68156310cbaf57a3619160d0fb966f496f970c32a2e57601127cc8f54a2fbf","src/display.rs":"a43f19b7cf4d95e90e4f3954399405d8350523d423f0beed9f01399e17527b17","src/lib.rs":"b6df00ab61ca0d82c9f7b3798d516384dd2617fe73e8981f37125ecccc970dd7","src/style.rs":"7c5c2524428f0dfbe3b8d5876ddb81c47d8635704ee31ef63ddeb2429a67f457","src/windows.rs":"3b52469eed89fdc258139e4fd978f0e30c72168863966247df781da8715f0841","src/write.rs":"247c518f8b0c103c970bbe7bc70caba3ee961ab0d37095e2da5c69db98d2fc24"},"package":"ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"}
|
|
@ -1,27 +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 = "ansi_term"
|
||||
version = "0.11.0"
|
||||
authors = ["ogham@bsago.me", "Ryan Scheel (Havvy) <ryan.havvy@gmail.com>", "Josh Triplett <josh@joshtriplett.org>"]
|
||||
description = "Library for ANSI terminal colours and styles (bold, underline)"
|
||||
homepage = "https://github.com/ogham/rust-ansi-term"
|
||||
documentation = "https://docs.rs/ansi_term"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
|
||||
[lib]
|
||||
name = "ansi_term"
|
||||
[target."cfg(target_os=\"windows\")".dependencies.winapi]
|
||||
version = "0.3.4"
|
||||
features = ["errhandlingapi", "consoleapi", "processenv"]
|
|
@ -1,21 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Benjamin Sago
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,174 +0,0 @@
|
|||
# rust-ansi-term [![ansi-term on crates.io](http://meritbadge.herokuapp.com/ansi-term)](https://crates.io/crates/ansi_term) [![Build status](https://travis-ci.org/ogham/rust-ansi-term.svg?branch=master)](https://travis-ci.org/ogham/rust-ansi-term) [![Coverage status](https://coveralls.io/repos/ogham/rust-ansi-term/badge.svg?branch=master&service=github)](https://coveralls.io/github/ogham/rust-ansi-term?branch=master)
|
||||
|
||||
This is a library for controlling colours and formatting, such as red bold text or blue underlined text, on ANSI terminals.
|
||||
|
||||
### [View the Rustdoc](https://docs.rs/ansi_term/0.9.0/ansi_term/)
|
||||
|
||||
|
||||
# Installation
|
||||
|
||||
This crate works with [Cargo](http://crates.io). Add the following to your `Cargo.toml` dependencies section:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
ansi_term = "0.9"
|
||||
```
|
||||
|
||||
|
||||
## Basic usage
|
||||
|
||||
There are two main data structures in this crate that you need to be concerned with: `ANSIString` and `Style`.
|
||||
A `Style` holds stylistic information: colours, whether the text should be bold, or blinking, or whatever.
|
||||
There are also `Colour` variants that represent simple foreground colour styles.
|
||||
An `ANSIString` is a string paired with a `Style`.
|
||||
|
||||
(Yes, it’s British English, but you won’t have to write “colour” very often. `Style` is used the majority of the time.)
|
||||
|
||||
To format a string, call the `paint` method on a `Style` or a `Colour`, passing in the string you want to format as the argument.
|
||||
For example, here’s how to get some red text:
|
||||
|
||||
```rust
|
||||
use ansi_term::Colour::Red;
|
||||
println!("This is in red: {}", Red.paint("a red string"));
|
||||
```
|
||||
|
||||
It’s important to note that the `paint` method does *not* actually return a string with the ANSI control characters surrounding it.
|
||||
Instead, it returns an `ANSIString` value that has a `Display` implementation that, when formatted, returns the characters.
|
||||
This allows strings to be printed with a minimum of `String` allocations being performed behind the scenes.
|
||||
|
||||
If you *do* want to get at the escape codes, then you can convert the `ANSIString` to a string as you would any other `Display` value:
|
||||
|
||||
```rust
|
||||
use ansi_term::Colour::Red;
|
||||
use std::string::ToString;
|
||||
let red_string = Red.paint("a red string").to_string();
|
||||
```
|
||||
|
||||
**Note for Windows 10 users:** On Windows 10, the application must enable ANSI support first:
|
||||
|
||||
```rust
|
||||
let enabled = ansi_term::enable_ansi_support();
|
||||
```
|
||||
|
||||
## Bold, underline, background, and other styles
|
||||
|
||||
For anything more complex than plain foreground colour changes, you need to construct `Style` objects themselves, rather than beginning with a `Colour`.
|
||||
You can do this by chaining methods based on a new `Style`, created with `Style::new()`.
|
||||
Each method creates a new style that has that specific property set.
|
||||
For example:
|
||||
|
||||
```rust
|
||||
use ansi_term::Style;
|
||||
println!("How about some {} and {}?",
|
||||
Style::new().bold().paint("bold"),
|
||||
Style::new().underline().paint("underline"));
|
||||
```
|
||||
|
||||
For brevity, these methods have also been implemented for `Colour` values, so you can give your styles a foreground colour without having to begin with an empty `Style` value:
|
||||
|
||||
```rust
|
||||
use ansi_term::Colour::{Blue, Yellow};
|
||||
println!("Demonstrating {} and {}!",
|
||||
Blue.bold().paint("blue bold"),
|
||||
Yellow.underline().paint("yellow underline"));
|
||||
println!("Yellow on blue: {}", Yellow.on(Blue).paint("wow!"));
|
||||
```
|
||||
|
||||
The complete list of styles you can use are:
|
||||
`bold`, `dimmed`, `italic`, `underline`, `blink`, `reverse`, `hidden`, and `on` for background colours.
|
||||
|
||||
In some cases, you may find it easier to change the foreground on an existing `Style` rather than starting from the appropriate `Colour`.
|
||||
You can do this using the `fg` method:
|
||||
|
||||
```rust
|
||||
use ansi_term::Style;
|
||||
use ansi_term::Colour::{Blue, Cyan, Yellow};
|
||||
println!("Yellow on blue: {}", Style::new().on(Blue).fg(Yellow).paint("yow!"));
|
||||
println!("Also yellow on blue: {}", Cyan.on(Blue).fg(Yellow).paint("zow!"));
|
||||
```
|
||||
|
||||
Finally, you can turn a `Colour` into a `Style` with the `normal` method.
|
||||
This will produce the exact same `ANSIString` as if you just used the `paint` method on the `Colour` directly, but it’s useful in certain cases: for example, you may have a method that returns `Styles`, and need to represent both the “red bold” and “red, but not bold” styles with values of the same type. The `Style` struct also has a `Default` implementation if you want to have a style with *nothing* set.
|
||||
|
||||
```rust
|
||||
use ansi_term::Style;
|
||||
use ansi_term::Colour::Red;
|
||||
Red.normal().paint("yet another red string");
|
||||
Style::default().paint("a completely regular string");
|
||||
```
|
||||
|
||||
|
||||
## Extended colours
|
||||
|
||||
You can access the extended range of 256 colours by using the `Fixed` colour variant, which takes an argument of the colour number to use.
|
||||
This can be included wherever you would use a `Colour`:
|
||||
|
||||
```rust
|
||||
use ansi_term::Colour::Fixed;
|
||||
Fixed(134).paint("A sort of light purple");
|
||||
Fixed(221).on(Fixed(124)).paint("Mustard in the ketchup");
|
||||
```
|
||||
|
||||
The first sixteen of these values are the same as the normal and bold standard colour variants.
|
||||
There’s nothing stopping you from using these as `Fixed` colours instead, but there’s nothing to be gained by doing so either.
|
||||
|
||||
You can also access full 24-bit color by using the `RGB` colour variant, which takes separate `u8` arguments for red, green, and blue:
|
||||
|
||||
```rust
|
||||
use ansi_term::Colour::RGB;
|
||||
RGB(70, 130, 180).paint("Steel blue");
|
||||
```
|
||||
|
||||
## Combining successive coloured strings
|
||||
|
||||
The benefit of writing ANSI escape codes to the terminal is that they *stack*: you do not need to end every coloured string with a reset code if the text that follows it is of a similar style.
|
||||
For example, if you want to have some blue text followed by some blue bold text, it’s possible to send the ANSI code for blue, followed by the ANSI code for bold, and finishing with a reset code without having to have an extra one between the two strings.
|
||||
|
||||
This crate can optimise the ANSI codes that get printed in situations like this, making life easier for your terminal renderer.
|
||||
The `ANSIStrings` struct takes a slice of several `ANSIString` values, and will iterate over each of them, printing only the codes for the styles that need to be updated as part of its formatting routine.
|
||||
|
||||
The following code snippet uses this to enclose a binary number displayed in red bold text inside some red, but not bold, brackets:
|
||||
|
||||
```rust
|
||||
use ansi_term::Colour::Red;
|
||||
use ansi_term::{ANSIString, ANSIStrings};
|
||||
let some_value = format!("{:b}", 42);
|
||||
let strings: &[ANSIString<'static>] = &[
|
||||
Red.paint("["),
|
||||
Red.bold().paint(some_value),
|
||||
Red.paint("]"),
|
||||
];
|
||||
println!("Value: {}", ANSIStrings(strings));
|
||||
```
|
||||
|
||||
There are several things to note here.
|
||||
Firstly, the `paint` method can take *either* an owned `String` or a borrowed `&str`.
|
||||
Internally, an `ANSIString` holds a copy-on-write (`Cow`) string value to deal with both owned and borrowed strings at the same time.
|
||||
This is used here to display a `String`, the result of the `format!` call, using the same mechanism as some statically-available `&str` slices.
|
||||
Secondly, that the `ANSIStrings` value works in the same way as its singular counterpart, with a `Display` implementation that only performs the formatting when required.
|
||||
|
||||
## Byte strings
|
||||
|
||||
This library also supports formatting `[u8]` byte strings; this supports
|
||||
applications working with text in an unknown encoding. `Style` and
|
||||
`Color` support painting `[u8]` values, resulting in an `ANSIByteString`.
|
||||
This type does not implement `Display`, as it may not contain UTF-8, but
|
||||
it does provide a method `write_to` to write the result to any
|
||||
`io::Write`:
|
||||
|
||||
```rust
|
||||
use ansi_term::Colour::Green;
|
||||
Green.paint("user data".as_bytes()).write_to(&mut std::io::stdout()).unwrap();
|
||||
```
|
||||
|
||||
Similarly, the type `ANSIByteStrings` supports writing a list of
|
||||
`ANSIByteString` values with minimal escape sequences:
|
||||
|
||||
```rust
|
||||
use ansi_term::Colour::Green;
|
||||
use ansi_term::ANSIByteStrings;
|
||||
ANSIByteStrings(&[
|
||||
Green.paint("user data 1\n".as_bytes()),
|
||||
Green.bold().paint("user data 2\n".as_bytes()),
|
||||
]).write_to(&mut std::io::stdout()).unwrap();
|
||||
```
|
|
@ -1,13 +0,0 @@
|
|||
extern crate ansi_term;
|
||||
use ansi_term::Colour::*;
|
||||
|
||||
fn main() {
|
||||
println!("{}", Black.paint("Black"));
|
||||
println!("{}", Red.paint("Red"));
|
||||
println!("{}", Green.paint("Green"));
|
||||
println!("{}", Yellow.paint("Yellow"));
|
||||
println!("{}", Blue.paint("Blue"));
|
||||
println!("{}", Purple.paint("Purple"));
|
||||
println!("{}", Cyan.paint("Cyan"));
|
||||
println!("{}", White.paint("White"));
|
||||
}
|
|
@ -1,258 +0,0 @@
|
|||
use style::{Colour, Style};
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use write::AnyWrite;
|
||||
|
||||
|
||||
// ---- generating ANSI codes ----
|
||||
|
||||
impl Style {
|
||||
|
||||
/// Write any ANSI codes that go *before* a piece of text. These should be
|
||||
/// the codes to set the terminal to a different colour or font style.
|
||||
fn write_prefix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
|
||||
|
||||
// If there are actually no styles here, then don’t write *any* codes
|
||||
// as the prefix. An empty ANSI code may not affect the terminal
|
||||
// output at all, but a user may just want a code-free string.
|
||||
if self.is_plain() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Write the codes’ prefix, then write numbers, separated by
|
||||
// semicolons, for each text style we want to apply.
|
||||
write!(f, "\x1B[")?;
|
||||
let mut written_anything = false;
|
||||
|
||||
{
|
||||
let mut write_char = |c| {
|
||||
if written_anything { write!(f, ";")?; }
|
||||
written_anything = true;
|
||||
write!(f, "{}", c)?;
|
||||
Ok(())
|
||||
};
|
||||
|
||||
if self.is_bold { write_char('1')? }
|
||||
if self.is_dimmed { write_char('2')? }
|
||||
if self.is_italic { write_char('3')? }
|
||||
if self.is_underline { write_char('4')? }
|
||||
if self.is_blink { write_char('5')? }
|
||||
if self.is_reverse { write_char('7')? }
|
||||
if self.is_hidden { write_char('8')? }
|
||||
if self.is_strikethrough { write_char('9')? }
|
||||
}
|
||||
|
||||
// The foreground and background colours, if specified, need to be
|
||||
// handled specially because the number codes are more complicated.
|
||||
// (see `write_background_code` and `write_foreground_code`)
|
||||
if let Some(bg) = self.background {
|
||||
if written_anything { write!(f, ";")?; }
|
||||
written_anything = true;
|
||||
bg.write_background_code(f)?;
|
||||
}
|
||||
|
||||
if let Some(fg) = self.foreground {
|
||||
if written_anything { write!(f, ";")?; }
|
||||
fg.write_foreground_code(f)?;
|
||||
}
|
||||
|
||||
// All the codes end with an `m`, because reasons.
|
||||
write!(f, "m")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write any ANSI codes that go *after* a piece of text. These should be
|
||||
/// the codes to *reset* the terminal back to its normal colour and style.
|
||||
fn write_suffix<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
|
||||
if self.is_plain() {
|
||||
Ok(())
|
||||
}
|
||||
else {
|
||||
write!(f, "{}", RESET)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// The code to send to reset all styles and return to `Style::default()`.
|
||||
pub static RESET: &str = "\x1B[0m";
|
||||
|
||||
|
||||
|
||||
impl Colour {
|
||||
fn write_foreground_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
|
||||
match *self {
|
||||
Colour::Black => write!(f, "30"),
|
||||
Colour::Red => write!(f, "31"),
|
||||
Colour::Green => write!(f, "32"),
|
||||
Colour::Yellow => write!(f, "33"),
|
||||
Colour::Blue => write!(f, "34"),
|
||||
Colour::Purple => write!(f, "35"),
|
||||
Colour::Cyan => write!(f, "36"),
|
||||
Colour::White => write!(f, "37"),
|
||||
Colour::Fixed(num) => write!(f, "38;5;{}", &num),
|
||||
Colour::RGB(r,g,b) => write!(f, "38;2;{};{};{}", &r, &g, &b),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_background_code<W: AnyWrite + ?Sized>(&self, f: &mut W) -> Result<(), W::Error> {
|
||||
match *self {
|
||||
Colour::Black => write!(f, "40"),
|
||||
Colour::Red => write!(f, "41"),
|
||||
Colour::Green => write!(f, "42"),
|
||||
Colour::Yellow => write!(f, "43"),
|
||||
Colour::Blue => write!(f, "44"),
|
||||
Colour::Purple => write!(f, "45"),
|
||||
Colour::Cyan => write!(f, "46"),
|
||||
Colour::White => write!(f, "47"),
|
||||
Colour::Fixed(num) => write!(f, "48;5;{}", &num),
|
||||
Colour::RGB(r,g,b) => write!(f, "48;2;{};{};{}", &r, &g, &b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Like `ANSIString`, but only displays the style prefix.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Prefix(Style);
|
||||
|
||||
/// Like `ANSIString`, but only displays the difference between two
|
||||
/// styles.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Infix(Style, Style);
|
||||
|
||||
/// Like `ANSIString`, but only displays the style suffix.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Suffix(Style);
|
||||
|
||||
|
||||
impl Style {
|
||||
|
||||
/// The prefix for this style.
|
||||
pub fn prefix(self) -> Prefix {
|
||||
Prefix(self)
|
||||
}
|
||||
|
||||
/// The infix between this style and another.
|
||||
pub fn infix(self, other: Style) -> Infix {
|
||||
Infix(self, other)
|
||||
}
|
||||
|
||||
/// The suffix for this style.
|
||||
pub fn suffix(self) -> Suffix {
|
||||
Suffix(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Colour {
|
||||
|
||||
/// The prefix for this colour.
|
||||
pub fn prefix(self) -> Prefix {
|
||||
Prefix(self.normal())
|
||||
}
|
||||
|
||||
/// The infix between this colour and another.
|
||||
pub fn infix(self, other: Colour) -> Infix {
|
||||
Infix(self.normal(), other.normal())
|
||||
}
|
||||
|
||||
/// The suffix for this colour.
|
||||
pub fn suffix(self) -> Suffix {
|
||||
Suffix(self.normal())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for Prefix {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let f: &mut fmt::Write = f;
|
||||
self.0.write_prefix(f)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for Infix {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use difference::Difference;
|
||||
|
||||
match Difference::between(&self.0, &self.1) {
|
||||
Difference::ExtraStyles(style) => {
|
||||
let f: &mut fmt::Write = f;
|
||||
style.write_prefix(f)
|
||||
},
|
||||
Difference::Reset => {
|
||||
let f: &mut fmt::Write = f;
|
||||
write!(f, "{}{}", RESET, self.0.prefix())
|
||||
},
|
||||
Difference::NoDifference => {
|
||||
Ok(()) // nothing to write
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for Suffix {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let f: &mut fmt::Write = f;
|
||||
self.0.write_suffix(f)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use style::Style;
|
||||
use style::Colour::*;
|
||||
|
||||
macro_rules! test {
|
||||
($name: ident: $style: expr; $input: expr => $result: expr) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
assert_eq!($style.paint($input).to_string(), $result.to_string());
|
||||
|
||||
let mut v = Vec::new();
|
||||
$style.paint($input.as_bytes()).write_to(&mut v).unwrap();
|
||||
assert_eq!(v.as_slice(), $result.as_bytes());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test!(plain: Style::default(); "text/plain" => "text/plain");
|
||||
test!(red: Red; "hi" => "\x1B[31mhi\x1B[0m");
|
||||
test!(black: Black.normal(); "hi" => "\x1B[30mhi\x1B[0m");
|
||||
test!(yellow_bold: Yellow.bold(); "hi" => "\x1B[1;33mhi\x1B[0m");
|
||||
test!(yellow_bold_2: Yellow.normal().bold(); "hi" => "\x1B[1;33mhi\x1B[0m");
|
||||
test!(blue_underline: Blue.underline(); "hi" => "\x1B[4;34mhi\x1B[0m");
|
||||
test!(green_bold_ul: Green.bold().underline(); "hi" => "\x1B[1;4;32mhi\x1B[0m");
|
||||
test!(green_bold_ul_2: Green.underline().bold(); "hi" => "\x1B[1;4;32mhi\x1B[0m");
|
||||
test!(purple_on_white: Purple.on(White); "hi" => "\x1B[47;35mhi\x1B[0m");
|
||||
test!(purple_on_white_2: Purple.normal().on(White); "hi" => "\x1B[47;35mhi\x1B[0m");
|
||||
test!(yellow_on_blue: Style::new().on(Blue).fg(Yellow); "hi" => "\x1B[44;33mhi\x1B[0m");
|
||||
test!(yellow_on_blue_2: Cyan.on(Blue).fg(Yellow); "hi" => "\x1B[44;33mhi\x1B[0m");
|
||||
test!(cyan_bold_on_white: Cyan.bold().on(White); "hi" => "\x1B[1;47;36mhi\x1B[0m");
|
||||
test!(cyan_ul_on_white: Cyan.underline().on(White); "hi" => "\x1B[4;47;36mhi\x1B[0m");
|
||||
test!(cyan_bold_ul_on_white: Cyan.bold().underline().on(White); "hi" => "\x1B[1;4;47;36mhi\x1B[0m");
|
||||
test!(cyan_ul_bold_on_white: Cyan.underline().bold().on(White); "hi" => "\x1B[1;4;47;36mhi\x1B[0m");
|
||||
test!(fixed: Fixed(100); "hi" => "\x1B[38;5;100mhi\x1B[0m");
|
||||
test!(fixed_on_purple: Fixed(100).on(Purple); "hi" => "\x1B[45;38;5;100mhi\x1B[0m");
|
||||
test!(fixed_on_fixed: Fixed(100).on(Fixed(200)); "hi" => "\x1B[48;5;200;38;5;100mhi\x1B[0m");
|
||||
test!(rgb: RGB(70,130,180); "hi" => "\x1B[38;2;70;130;180mhi\x1B[0m");
|
||||
test!(rgb_on_blue: RGB(70,130,180).on(Blue); "hi" => "\x1B[44;38;2;70;130;180mhi\x1B[0m");
|
||||
test!(blue_on_rgb: Blue.on(RGB(70,130,180)); "hi" => "\x1B[48;2;70;130;180;34mhi\x1B[0m");
|
||||
test!(rgb_on_rgb: RGB(70,130,180).on(RGB(5,10,15)); "hi" => "\x1B[48;2;5;10;15;38;2;70;130;180mhi\x1B[0m");
|
||||
test!(bold: Style::new().bold(); "hi" => "\x1B[1mhi\x1B[0m");
|
||||
test!(underline: Style::new().underline(); "hi" => "\x1B[4mhi\x1B[0m");
|
||||
test!(bunderline: Style::new().bold().underline(); "hi" => "\x1B[1;4mhi\x1B[0m");
|
||||
test!(dimmed: Style::new().dimmed(); "hi" => "\x1B[2mhi\x1B[0m");
|
||||
test!(italic: Style::new().italic(); "hi" => "\x1B[3mhi\x1B[0m");
|
||||
test!(blink: Style::new().blink(); "hi" => "\x1B[5mhi\x1B[0m");
|
||||
test!(reverse: Style::new().reverse(); "hi" => "\x1B[7mhi\x1B[0m");
|
||||
test!(hidden: Style::new().hidden(); "hi" => "\x1B[8mhi\x1B[0m");
|
||||
test!(stricken: Style::new().strikethrough(); "hi" => "\x1B[9mhi\x1B[0m");
|
||||
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
use std::fmt;
|
||||
|
||||
use style::Style;
|
||||
|
||||
|
||||
/// Styles have a special `Debug` implementation that only shows the fields that
|
||||
/// are set. Fields that haven’t been touched aren’t included in the output.
|
||||
///
|
||||
/// This behaviour gets bypassed when using the alternate formatting mode
|
||||
/// `format!("{:#?}")`.
|
||||
///
|
||||
/// use ansi_term::Colour::{Red, Blue};
|
||||
/// assert_eq!("Style { fg(Red), on(Blue), bold, italic }",
|
||||
/// format!("{:?}", Red.on(Blue).bold().italic()));
|
||||
impl fmt::Debug for Style {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
if fmt.alternate() {
|
||||
fmt.debug_struct("Style")
|
||||
.field("foreground", &self.foreground)
|
||||
.field("background", &self.background)
|
||||
.field("blink", &self.is_blink)
|
||||
.field("bold", &self.is_bold)
|
||||
.field("dimmed", &self.is_dimmed)
|
||||
.field("hidden", &self.is_hidden)
|
||||
.field("italic", &self.is_italic)
|
||||
.field("reverse", &self.is_reverse)
|
||||
.field("strikethrough", &self.is_strikethrough)
|
||||
.field("underline", &self.is_underline)
|
||||
.finish()
|
||||
}
|
||||
else if self.is_plain() {
|
||||
fmt.write_str("Style {}")
|
||||
}
|
||||
else {
|
||||
fmt.write_str("Style { ")?;
|
||||
|
||||
let mut written_anything = false;
|
||||
|
||||
if let Some(fg) = self.foreground {
|
||||
if written_anything { fmt.write_str(", ")? }
|
||||
written_anything = true;
|
||||
write!(fmt, "fg({:?})", fg)?
|
||||
}
|
||||
|
||||
if let Some(bg) = self.background {
|
||||
if written_anything { fmt.write_str(", ")? }
|
||||
written_anything = true;
|
||||
write!(fmt, "on({:?})", bg)?
|
||||
}
|
||||
|
||||
{
|
||||
let mut write_flag = |name| {
|
||||
if written_anything { fmt.write_str(", ")? }
|
||||
written_anything = true;
|
||||
fmt.write_str(name)
|
||||
};
|
||||
|
||||
if self.is_blink { write_flag("blink")? }
|
||||
if self.is_bold { write_flag("bold")? }
|
||||
if self.is_dimmed { write_flag("dimmed")? }
|
||||
if self.is_hidden { write_flag("hidden")? }
|
||||
if self.is_italic { write_flag("italic")? }
|
||||
if self.is_reverse { write_flag("reverse")? }
|
||||
if self.is_strikethrough { write_flag("strikethrough")? }
|
||||
if self.is_underline { write_flag("underline")? }
|
||||
}
|
||||
|
||||
write!(fmt, " }}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use style::Colour::*;
|
||||
use style::Style;
|
||||
|
||||
fn style() -> Style {
|
||||
Style::new()
|
||||
}
|
||||
|
||||
macro_rules! test {
|
||||
($name: ident: $obj: expr => $result: expr) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
assert_eq!($result, format!("{:?}", $obj));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test!(empty: style() => "Style {}");
|
||||
test!(bold: style().bold() => "Style { bold }");
|
||||
test!(italic: style().italic() => "Style { italic }");
|
||||
test!(both: style().bold().italic() => "Style { bold, italic }");
|
||||
|
||||
test!(red: Red.normal() => "Style { fg(Red) }");
|
||||
test!(redblue: Red.normal().on(RGB(3, 2, 4)) => "Style { fg(Red), on(RGB(3, 2, 4)) }");
|
||||
|
||||
test!(everything:
|
||||
Red.on(Blue).blink().bold().dimmed().hidden().italic().reverse().strikethrough().underline() =>
|
||||
"Style { fg(Red), on(Blue), blink, bold, dimmed, hidden, italic, reverse, strikethrough, underline }");
|
||||
|
||||
#[test]
|
||||
fn long_and_detailed() {
|
||||
let debug = r##"Style {
|
||||
foreground: Some(
|
||||
Blue
|
||||
),
|
||||
background: None,
|
||||
blink: false,
|
||||
bold: true,
|
||||
dimmed: false,
|
||||
hidden: false,
|
||||
italic: false,
|
||||
reverse: false,
|
||||
strikethrough: false,
|
||||
underline: false
|
||||
}"##;
|
||||
assert_eq!(debug, format!("{:#?}", Blue.bold()));
|
||||
}
|
||||
}
|
|
@ -1,179 +0,0 @@
|
|||
use super::Style;
|
||||
|
||||
|
||||
/// When printing out one coloured string followed by another, use one of
|
||||
/// these rules to figure out which *extra* control codes need to be sent.
|
||||
#[derive(PartialEq, Clone, Copy, Debug)]
|
||||
pub enum Difference {
|
||||
|
||||
/// Print out the control codes specified by this style to end up looking
|
||||
/// like the second string's styles.
|
||||
ExtraStyles(Style),
|
||||
|
||||
/// Converting between these two is impossible, so just send a reset
|
||||
/// command and then the second string's styles.
|
||||
Reset,
|
||||
|
||||
/// The before style is exactly the same as the after style, so no further
|
||||
/// control codes need to be printed.
|
||||
NoDifference,
|
||||
}
|
||||
|
||||
|
||||
impl Difference {
|
||||
|
||||
/// Compute the 'style difference' required to turn an existing style into
|
||||
/// the given, second style.
|
||||
///
|
||||
/// For example, to turn green text into green bold text, it's redundant
|
||||
/// to write a reset command then a second green+bold command, instead of
|
||||
/// just writing one bold command. This method should see that both styles
|
||||
/// use the foreground colour green, and reduce it to a single command.
|
||||
///
|
||||
/// This method returns an enum value because it's not actually always
|
||||
/// possible to turn one style into another: for example, text could be
|
||||
/// made bold and underlined, but you can't remove the bold property
|
||||
/// without also removing the underline property. So when this has to
|
||||
/// happen, this function returns None, meaning that the entire set of
|
||||
/// styles should be reset and begun again.
|
||||
pub fn between(first: &Style, next: &Style) -> Difference {
|
||||
use self::Difference::*;
|
||||
|
||||
// XXX(Havvy): This algorithm is kind of hard to replicate without
|
||||
// having the Plain/Foreground enum variants, so I'm just leaving
|
||||
// it commented out for now, and defaulting to Reset.
|
||||
|
||||
if first == next {
|
||||
return NoDifference;
|
||||
}
|
||||
|
||||
// Cannot un-bold, so must Reset.
|
||||
if first.is_bold && !next.is_bold {
|
||||
return Reset;
|
||||
}
|
||||
|
||||
if first.is_dimmed && !next.is_dimmed {
|
||||
return Reset;
|
||||
}
|
||||
|
||||
if first.is_italic && !next.is_italic {
|
||||
return Reset;
|
||||
}
|
||||
|
||||
// Cannot un-underline, so must Reset.
|
||||
if first.is_underline && !next.is_underline {
|
||||
return Reset;
|
||||
}
|
||||
|
||||
if first.is_blink && !next.is_blink {
|
||||
return Reset;
|
||||
}
|
||||
|
||||
if first.is_reverse && !next.is_reverse {
|
||||
return Reset;
|
||||
}
|
||||
|
||||
if first.is_hidden && !next.is_hidden {
|
||||
return Reset;
|
||||
}
|
||||
|
||||
if first.is_strikethrough && !next.is_strikethrough {
|
||||
return Reset;
|
||||
}
|
||||
|
||||
// Cannot go from foreground to no foreground, so must Reset.
|
||||
if first.foreground.is_some() && next.foreground.is_none() {
|
||||
return Reset;
|
||||
}
|
||||
|
||||
// Cannot go from background to no background, so must Reset.
|
||||
if first.background.is_some() && next.background.is_none() {
|
||||
return Reset;
|
||||
}
|
||||
|
||||
let mut extra_styles = Style::default();
|
||||
|
||||
if first.is_bold != next.is_bold {
|
||||
extra_styles.is_bold = true;
|
||||
}
|
||||
|
||||
if first.is_dimmed != next.is_dimmed {
|
||||
extra_styles.is_dimmed = true;
|
||||
}
|
||||
|
||||
if first.is_italic != next.is_italic {
|
||||
extra_styles.is_italic = true;
|
||||
}
|
||||
|
||||
if first.is_underline != next.is_underline {
|
||||
extra_styles.is_underline = true;
|
||||
}
|
||||
|
||||
if first.is_blink != next.is_blink {
|
||||
extra_styles.is_blink = true;
|
||||
}
|
||||
|
||||
if first.is_reverse != next.is_reverse {
|
||||
extra_styles.is_reverse = true;
|
||||
}
|
||||
|
||||
if first.is_hidden != next.is_hidden {
|
||||
extra_styles.is_hidden = true;
|
||||
}
|
||||
|
||||
if first.is_strikethrough != next.is_strikethrough {
|
||||
extra_styles.is_strikethrough = true;
|
||||
}
|
||||
|
||||
if first.foreground != next.foreground {
|
||||
extra_styles.foreground = next.foreground;
|
||||
}
|
||||
|
||||
if first.background != next.background {
|
||||
extra_styles.background = next.background;
|
||||
}
|
||||
|
||||
ExtraStyles(extra_styles)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use super::Difference::*;
|
||||
use style::Colour::*;
|
||||
use style::Style;
|
||||
|
||||
fn style() -> Style {
|
||||
Style::new()
|
||||
}
|
||||
|
||||
macro_rules! test {
|
||||
($name: ident: $first: expr; $next: expr => $result: expr) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
assert_eq!($result, Difference::between(&$first, &$next));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test!(nothing: Green.normal(); Green.normal() => NoDifference);
|
||||
test!(uppercase: Green.normal(); Green.bold() => ExtraStyles(style().bold()));
|
||||
test!(lowercase: Green.bold(); Green.normal() => Reset);
|
||||
test!(nothing2: Green.bold(); Green.bold() => NoDifference);
|
||||
|
||||
test!(colour_change: Red.normal(); Blue.normal() => ExtraStyles(Blue.normal()));
|
||||
|
||||
test!(addition_of_blink: style(); style().blink() => ExtraStyles(style().blink()));
|
||||
test!(addition_of_dimmed: style(); style().dimmed() => ExtraStyles(style().dimmed()));
|
||||
test!(addition_of_hidden: style(); style().hidden() => ExtraStyles(style().hidden()));
|
||||
test!(addition_of_reverse: style(); style().reverse() => ExtraStyles(style().reverse()));
|
||||
test!(addition_of_strikethrough: style(); style().strikethrough() => ExtraStyles(style().strikethrough()));
|
||||
|
||||
test!(removal_of_strikethrough: style().strikethrough(); style() => Reset);
|
||||
test!(removal_of_reverse: style().reverse(); style() => Reset);
|
||||
test!(removal_of_hidden: style().hidden(); style() => Reset);
|
||||
test!(removal_of_dimmed: style().dimmed(); style() => Reset);
|
||||
test!(removal_of_blink: style().blink(); style() => Reset);
|
||||
}
|
|
@ -1,279 +0,0 @@
|
|||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::ops::Deref;
|
||||
|
||||
use ansi::RESET;
|
||||
use difference::Difference;
|
||||
use style::{Style, Colour};
|
||||
use write::AnyWrite;
|
||||
|
||||
|
||||
/// An `ANSIGenericString` includes a generic string type and a `Style` to
|
||||
/// display that string. `ANSIString` and `ANSIByteString` are aliases for
|
||||
/// this type on `str` and `[u8]`, respectively.
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct ANSIGenericString<'a, S: 'a + ToOwned + ?Sized>
|
||||
where <S as ToOwned>::Owned: fmt::Debug {
|
||||
style: Style,
|
||||
string: Cow<'a, S>,
|
||||
}
|
||||
|
||||
|
||||
/// Cloning an `ANSIGenericString` will clone its underlying string.
|
||||
///
|
||||
/// ### Examples
|
||||
///
|
||||
/// ```
|
||||
/// use ansi_term::ANSIString;
|
||||
///
|
||||
/// let plain_string = ANSIString::from("a plain string");
|
||||
/// let clone_string = plain_string.clone();
|
||||
/// assert_eq!(clone_string, plain_string);
|
||||
/// ```
|
||||
impl<'a, S: 'a + ToOwned + ?Sized> Clone for ANSIGenericString<'a, S>
|
||||
where <S as ToOwned>::Owned: fmt::Debug {
|
||||
fn clone(&self) -> ANSIGenericString<'a, S> {
|
||||
ANSIGenericString {
|
||||
style: self.style,
|
||||
string: self.string.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// You might think that the hand-written Clone impl above is the same as the
|
||||
// one that gets generated with #[derive]. But it’s not *quite* the same!
|
||||
//
|
||||
// `str` is not Clone, and the derived Clone implementation puts a Clone
|
||||
// constraint on the S type parameter (generated using --pretty=expanded):
|
||||
//
|
||||
// ↓_________________↓
|
||||
// impl <'a, S: ::std::clone::Clone + 'a + ToOwned + ?Sized> ::std::clone::Clone
|
||||
// for ANSIGenericString<'a, S> where
|
||||
// <S as ToOwned>::Owned: fmt::Debug { ... }
|
||||
//
|
||||
// This resulted in compile errors when you tried to derive Clone on a type
|
||||
// that used it:
|
||||
//
|
||||
// #[derive(PartialEq, Debug, Clone, Default)]
|
||||
// pub struct TextCellContents(Vec<ANSIString<'static>>);
|
||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
// error[E0277]: the trait `std::clone::Clone` is not implemented for `str`
|
||||
//
|
||||
// The hand-written impl above can ignore that constraint and still compile.
|
||||
|
||||
|
||||
|
||||
/// An ANSI String is a string coupled with the `Style` to display it
|
||||
/// in a terminal.
|
||||
///
|
||||
/// Although not technically a string itself, it can be turned into
|
||||
/// one with the `to_string` method.
|
||||
///
|
||||
/// ### Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use ansi_term::ANSIString;
|
||||
/// use ansi_term::Colour::Red;
|
||||
///
|
||||
/// let red_string = Red.paint("a red string");
|
||||
/// println!("{}", red_string);
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// use ansi_term::ANSIString;
|
||||
///
|
||||
/// let plain_string = ANSIString::from("a plain string");
|
||||
/// assert_eq!(&*plain_string, "a plain string");
|
||||
/// ```
|
||||
pub type ANSIString<'a> = ANSIGenericString<'a, str>;
|
||||
|
||||
/// An `ANSIByteString` represents a formatted series of bytes. Use
|
||||
/// `ANSIByteString` when styling text with an unknown encoding.
|
||||
pub type ANSIByteString<'a> = ANSIGenericString<'a, [u8]>;
|
||||
|
||||
impl<'a, I, S: 'a + ToOwned + ?Sized> From<I> for ANSIGenericString<'a, S>
|
||||
where I: Into<Cow<'a, S>>,
|
||||
<S as ToOwned>::Owned: fmt::Debug {
|
||||
fn from(input: I) -> ANSIGenericString<'a, S> {
|
||||
ANSIGenericString {
|
||||
string: input.into(),
|
||||
style: Style::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + ToOwned + ?Sized> Deref for ANSIGenericString<'a, S>
|
||||
where <S as ToOwned>::Owned: fmt::Debug {
|
||||
type Target = S;
|
||||
|
||||
fn deref(&self) -> &S {
|
||||
self.string.deref()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// A set of `ANSIGenericString`s collected together, in order to be
|
||||
/// written with a minimum of control characters.
|
||||
pub struct ANSIGenericStrings<'a, S: 'a + ToOwned + ?Sized>
|
||||
(pub &'a [ANSIGenericString<'a, S>])
|
||||
where <S as ToOwned>::Owned: fmt::Debug;
|
||||
|
||||
/// A set of `ANSIString`s collected together, in order to be written with a
|
||||
/// minimum of control characters.
|
||||
pub type ANSIStrings<'a> = ANSIGenericStrings<'a, str>;
|
||||
|
||||
/// A function to construct an `ANSIStrings` instance.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn ANSIStrings<'a>(arg: &'a [ANSIString<'a>]) -> ANSIStrings<'a> {
|
||||
ANSIGenericStrings(arg)
|
||||
}
|
||||
|
||||
/// A set of `ANSIByteString`s collected together, in order to be
|
||||
/// written with a minimum of control characters.
|
||||
pub type ANSIByteStrings<'a> = ANSIGenericStrings<'a, [u8]>;
|
||||
|
||||
/// A function to construct an `ANSIByteStrings` instance.
|
||||
#[allow(non_snake_case)]
|
||||
pub fn ANSIByteStrings<'a>(arg: &'a [ANSIByteString<'a>]) -> ANSIByteStrings<'a> {
|
||||
ANSIGenericStrings(arg)
|
||||
}
|
||||
|
||||
|
||||
// ---- paint functions ----
|
||||
|
||||
impl Style {
|
||||
|
||||
/// Paints the given text with this colour, returning an ANSI string.
|
||||
pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> ANSIGenericString<'a, S>
|
||||
where I: Into<Cow<'a, S>>,
|
||||
<S as ToOwned>::Owned: fmt::Debug {
|
||||
ANSIGenericString {
|
||||
string: input.into(),
|
||||
style: self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Colour {
|
||||
|
||||
/// Paints the given text with this colour, returning an ANSI string.
|
||||
/// This is a short-cut so you don’t have to use `Blue.normal()` just
|
||||
/// to get blue text.
|
||||
///
|
||||
/// ```
|
||||
/// use ansi_term::Colour::Blue;
|
||||
/// println!("{}", Blue.paint("da ba dee"));
|
||||
/// ```
|
||||
pub fn paint<'a, I, S: 'a + ToOwned + ?Sized>(self, input: I) -> ANSIGenericString<'a, S>
|
||||
where I: Into<Cow<'a, S>>,
|
||||
<S as ToOwned>::Owned: fmt::Debug {
|
||||
ANSIGenericString {
|
||||
string: input.into(),
|
||||
style: self.normal(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---- writers for individual ANSI strings ----
|
||||
|
||||
impl<'a> fmt::Display for ANSIString<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let w: &mut fmt::Write = f;
|
||||
self.write_to_any(w)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ANSIByteString<'a> {
|
||||
/// Write an `ANSIByteString` to an `io::Write`. This writes the escape
|
||||
/// sequences for the associated `Style` around the bytes.
|
||||
pub fn write_to<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
|
||||
let w: &mut io::Write = w;
|
||||
self.write_to_any(w)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + ToOwned + ?Sized> ANSIGenericString<'a, S>
|
||||
where <S as ToOwned>::Owned: fmt::Debug, &'a S: AsRef<[u8]> {
|
||||
fn write_to_any<W: AnyWrite<wstr=S> + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
|
||||
write!(w, "{}", self.style.prefix())?;
|
||||
w.write_str(self.string.as_ref())?;
|
||||
write!(w, "{}", self.style.suffix())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---- writers for combined ANSI strings ----
|
||||
|
||||
impl<'a> fmt::Display for ANSIStrings<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let f: &mut fmt::Write = f;
|
||||
self.write_to_any(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ANSIByteStrings<'a> {
|
||||
/// Write `ANSIByteStrings` to an `io::Write`. This writes the minimal
|
||||
/// escape sequences for the associated `Style`s around each set of
|
||||
/// bytes.
|
||||
pub fn write_to<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
|
||||
let w: &mut io::Write = w;
|
||||
self.write_to_any(w)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: 'a + ToOwned + ?Sized> ANSIGenericStrings<'a, S>
|
||||
where <S as ToOwned>::Owned: fmt::Debug, &'a S: AsRef<[u8]> {
|
||||
fn write_to_any<W: AnyWrite<wstr=S> + ?Sized>(&self, w: &mut W) -> Result<(), W::Error> {
|
||||
use self::Difference::*;
|
||||
|
||||
let first = match self.0.first() {
|
||||
None => return Ok(()),
|
||||
Some(f) => f,
|
||||
};
|
||||
|
||||
write!(w, "{}", first.style.prefix())?;
|
||||
w.write_str(first.string.as_ref())?;
|
||||
|
||||
for window in self.0.windows(2) {
|
||||
match Difference::between(&window[0].style, &window[1].style) {
|
||||
ExtraStyles(style) => write!(w, "{}", style.prefix())?,
|
||||
Reset => write!(w, "{}{}", RESET, window[1].style.prefix())?,
|
||||
NoDifference => {/* Do nothing! */},
|
||||
}
|
||||
|
||||
w.write_str(&window[1].string)?;
|
||||
}
|
||||
|
||||
// Write the final reset string after all of the ANSIStrings have been
|
||||
// written, *except* if the last one has no styles, because it would
|
||||
// have already been written by this point.
|
||||
if let Some(last) = self.0.last() {
|
||||
if !last.style.is_plain() {
|
||||
write!(w, "{}", RESET)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---- tests ----
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
pub use super::super::ANSIStrings;
|
||||
pub use style::Style;
|
||||
pub use style::Colour::*;
|
||||
|
||||
#[test]
|
||||
fn no_control_codes_for_plain() {
|
||||
let one = Style::default().paint("one");
|
||||
let two = Style::default().paint("two");
|
||||
let output = format!("{}", ANSIStrings( &[ one, two ] ));
|
||||
assert_eq!(&*output, "onetwo");
|
||||
}
|
||||
}
|
|
@ -1,205 +0,0 @@
|
|||
//! This is a library for controlling colours and formatting, such as
|
||||
//! red bold text or blue underlined text, on ANSI terminals.
|
||||
//!
|
||||
//!
|
||||
//! ## Basic usage
|
||||
//!
|
||||
//! There are two main data structures in this crate that you need to be
|
||||
//! concerned with: `ANSIString` and `Style`. A `Style` holds stylistic
|
||||
//! information: colours, whether the text should be bold, or blinking, or
|
||||
//! whatever. There are also `Colour` variants that represent simple foreground
|
||||
//! colour styles. An `ANSIString` is a string paired with a `Style`.
|
||||
//!
|
||||
//! (Yes, it’s British English, but you won’t have to write “colour” very often.
|
||||
//! `Style` is used the majority of the time.)
|
||||
//!
|
||||
//! To format a string, call the `paint` method on a `Style` or a `Colour`,
|
||||
//! passing in the string you want to format as the argument. For example,
|
||||
//! here’s how to get some red text:
|
||||
//!
|
||||
//! use ansi_term::Colour::Red;
|
||||
//! println!("This is in red: {}", Red.paint("a red string"));
|
||||
//!
|
||||
//! It’s important to note that the `paint` method does *not* actually return a
|
||||
//! string with the ANSI control characters surrounding it. Instead, it returns
|
||||
//! an `ANSIString` value that has a `Display` implementation that, when
|
||||
//! formatted, returns the characters. This allows strings to be printed with a
|
||||
//! minimum of `String` allocations being performed behind the scenes.
|
||||
//!
|
||||
//! If you *do* want to get at the escape codes, then you can convert the
|
||||
//! `ANSIString` to a string as you would any other `Display` value:
|
||||
//!
|
||||
//! use ansi_term::Colour::Red;
|
||||
//! use std::string::ToString;
|
||||
//! let red_string = Red.paint("a red string").to_string();
|
||||
//!
|
||||
//!
|
||||
//! ## Bold, underline, background, and other styles
|
||||
//!
|
||||
//! For anything more complex than plain foreground colour changes, you need to
|
||||
//! construct `Style` objects themselves, rather than beginning with a `Colour`.
|
||||
//! You can do this by chaining methods based on a new `Style`, created with
|
||||
//! `Style::new()`. Each method creates a new style that has that specific
|
||||
//! property set. For example:
|
||||
//!
|
||||
//! use ansi_term::Style;
|
||||
//! println!("How about some {} and {}?",
|
||||
//! Style::new().bold().paint("bold"),
|
||||
//! Style::new().underline().paint("underline"));
|
||||
//!
|
||||
//! For brevity, these methods have also been implemented for `Colour` values,
|
||||
//! so you can give your styles a foreground colour without having to begin with
|
||||
//! an empty `Style` value:
|
||||
//!
|
||||
//! use ansi_term::Colour::{Blue, Yellow};
|
||||
//! println!("Demonstrating {} and {}!",
|
||||
//! Blue.bold().paint("blue bold"),
|
||||
//! Yellow.underline().paint("yellow underline"));
|
||||
//! println!("Yellow on blue: {}", Yellow.on(Blue).paint("wow!"));
|
||||
//!
|
||||
//! The complete list of styles you can use are: `bold`, `dimmed`, `italic`,
|
||||
//! `underline`, `blink`, `reverse`, `hidden`, `strikethrough`, and `on` for
|
||||
//! background colours.
|
||||
//!
|
||||
//! In some cases, you may find it easier to change the foreground on an
|
||||
//! existing `Style` rather than starting from the appropriate `Colour`.
|
||||
//! You can do this using the `fg` method:
|
||||
//!
|
||||
//! use ansi_term::Style;
|
||||
//! use ansi_term::Colour::{Blue, Cyan, Yellow};
|
||||
//! println!("Yellow on blue: {}", Style::new().on(Blue).fg(Yellow).paint("yow!"));
|
||||
//! println!("Also yellow on blue: {}", Cyan.on(Blue).fg(Yellow).paint("zow!"));
|
||||
//!
|
||||
//! Finally, you can turn a `Colour` into a `Style` with the `normal` method.
|
||||
//! This will produce the exact same `ANSIString` as if you just used the
|
||||
//! `paint` method on the `Colour` directly, but it’s useful in certain cases:
|
||||
//! for example, you may have a method that returns `Styles`, and need to
|
||||
//! represent both the “red bold” and “red, but not bold” styles with values of
|
||||
//! the same type. The `Style` struct also has a `Default` implementation if you
|
||||
//! want to have a style with *nothing* set.
|
||||
//!
|
||||
//! use ansi_term::Style;
|
||||
//! use ansi_term::Colour::Red;
|
||||
//! Red.normal().paint("yet another red string");
|
||||
//! Style::default().paint("a completely regular string");
|
||||
//!
|
||||
//!
|
||||
//! ## Extended colours
|
||||
//!
|
||||
//! You can access the extended range of 256 colours by using the `Fixed` colour
|
||||
//! variant, which takes an argument of the colour number to use. This can be
|
||||
//! included wherever you would use a `Colour`:
|
||||
//!
|
||||
//! use ansi_term::Colour::Fixed;
|
||||
//! Fixed(134).paint("A sort of light purple");
|
||||
//! Fixed(221).on(Fixed(124)).paint("Mustard in the ketchup");
|
||||
//!
|
||||
//! The first sixteen of these values are the same as the normal and bold
|
||||
//! standard colour variants. There’s nothing stopping you from using these as
|
||||
//! `Fixed` colours instead, but there’s nothing to be gained by doing so
|
||||
//! either.
|
||||
//!
|
||||
//! You can also access full 24-bit color by using the `RGB` colour variant,
|
||||
//! which takes separate `u8` arguments for red, green, and blue:
|
||||
//!
|
||||
//! use ansi_term::Colour::RGB;
|
||||
//! RGB(70, 130, 180).paint("Steel blue");
|
||||
//!
|
||||
//! ## Combining successive coloured strings
|
||||
//!
|
||||
//! The benefit of writing ANSI escape codes to the terminal is that they
|
||||
//! *stack*: you do not need to end every coloured string with a reset code if
|
||||
//! the text that follows it is of a similar style. For example, if you want to
|
||||
//! have some blue text followed by some blue bold text, it’s possible to send
|
||||
//! the ANSI code for blue, followed by the ANSI code for bold, and finishing
|
||||
//! with a reset code without having to have an extra one between the two
|
||||
//! strings.
|
||||
//!
|
||||
//! This crate can optimise the ANSI codes that get printed in situations like
|
||||
//! this, making life easier for your terminal renderer. The `ANSIStrings`
|
||||
//! struct takes a slice of several `ANSIString` values, and will iterate over
|
||||
//! each of them, printing only the codes for the styles that need to be updated
|
||||
//! as part of its formatting routine.
|
||||
//!
|
||||
//! The following code snippet uses this to enclose a binary number displayed in
|
||||
//! red bold text inside some red, but not bold, brackets:
|
||||
//!
|
||||
//! use ansi_term::Colour::Red;
|
||||
//! use ansi_term::{ANSIString, ANSIStrings};
|
||||
//! let some_value = format!("{:b}", 42);
|
||||
//! let strings: &[ANSIString<'static>] = &[
|
||||
//! Red.paint("["),
|
||||
//! Red.bold().paint(some_value),
|
||||
//! Red.paint("]"),
|
||||
//! ];
|
||||
//! println!("Value: {}", ANSIStrings(strings));
|
||||
//!
|
||||
//! There are several things to note here. Firstly, the `paint` method can take
|
||||
//! *either* an owned `String` or a borrowed `&str`. Internally, an `ANSIString`
|
||||
//! holds a copy-on-write (`Cow`) string value to deal with both owned and
|
||||
//! borrowed strings at the same time. This is used here to display a `String`,
|
||||
//! the result of the `format!` call, using the same mechanism as some
|
||||
//! statically-available `&str` slices. Secondly, that the `ANSIStrings` value
|
||||
//! works in the same way as its singular counterpart, with a `Display`
|
||||
//! implementation that only performs the formatting when required.
|
||||
//!
|
||||
//! ## Byte strings
|
||||
//!
|
||||
//! This library also supports formatting `[u8]` byte strings; this supports
|
||||
//! applications working with text in an unknown encoding. `Style` and
|
||||
//! `Color` support painting `[u8]` values, resulting in an `ANSIByteString`.
|
||||
//! This type does not implement `Display`, as it may not contain UTF-8, but
|
||||
//! it does provide a method `write_to` to write the result to any
|
||||
//! `io::Write`:
|
||||
//!
|
||||
//! use ansi_term::Colour::Green;
|
||||
//! Green.paint("user data".as_bytes()).write_to(&mut std::io::stdout()).unwrap();
|
||||
//!
|
||||
//! Similarly, the type `ANSIByteStrings` supports writing a list of
|
||||
//! `ANSIByteString` values with minimal escape sequences:
|
||||
//!
|
||||
//! use ansi_term::Colour::Green;
|
||||
//! use ansi_term::ANSIByteStrings;
|
||||
//! ANSIByteStrings(&[
|
||||
//! Green.paint("user data 1\n".as_bytes()),
|
||||
//! Green.bold().paint("user data 2\n".as_bytes()),
|
||||
//! ]).write_to(&mut std::io::stdout()).unwrap();
|
||||
|
||||
|
||||
#![crate_name = "ansi_term"]
|
||||
#![crate_type = "rlib"]
|
||||
#![crate_type = "dylib"]
|
||||
|
||||
#![warn(missing_copy_implementations)]
|
||||
#![warn(missing_docs)]
|
||||
#![warn(trivial_casts, trivial_numeric_casts)]
|
||||
#![warn(unused_extern_crates, unused_qualifications)]
|
||||
|
||||
#[cfg(target_os="windows")]
|
||||
extern crate winapi;
|
||||
|
||||
mod ansi;
|
||||
pub use ansi::{Prefix, Infix, Suffix};
|
||||
|
||||
mod style;
|
||||
pub use style::{Colour, Style};
|
||||
|
||||
/// Color is a type alias for Colour for those who can't be bothered.
|
||||
pub use Colour as Color;
|
||||
|
||||
// I'm not beyond calling Colour Colour, rather than Color, but I did
|
||||
// purposefully name this crate 'ansi-term' so people wouldn't get
|
||||
// confused when they tried to install it.
|
||||
//
|
||||
// Only *after* they'd installed it.
|
||||
|
||||
mod difference;
|
||||
mod display;
|
||||
pub use display::*;
|
||||
|
||||
mod write;
|
||||
|
||||
mod windows;
|
||||
pub use windows::*;
|
||||
|
||||
mod debug;
|
|
@ -1,259 +0,0 @@
|
|||
/// A style is a collection of properties that can format a string
|
||||
/// using ANSI escape codes.
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
pub struct Style {
|
||||
|
||||
/// The style's foreground colour, if it has one.
|
||||
pub foreground: Option<Colour>,
|
||||
|
||||
/// The style's background colour, if it has one.
|
||||
pub background: Option<Colour>,
|
||||
|
||||
/// Whether this style is bold.
|
||||
pub is_bold: bool,
|
||||
|
||||
/// Whether this style is dimmed.
|
||||
pub is_dimmed: bool,
|
||||
|
||||
/// Whether this style is italic.
|
||||
pub is_italic: bool,
|
||||
|
||||
/// Whether this style is underlined.
|
||||
pub is_underline: bool,
|
||||
|
||||
/// Whether this style is blinking.
|
||||
pub is_blink: bool,
|
||||
|
||||
/// Whether this style has reverse colours.
|
||||
pub is_reverse: bool,
|
||||
|
||||
/// Whether this style is hidden.
|
||||
pub is_hidden: bool,
|
||||
|
||||
/// Whether this style is struckthrough.
|
||||
pub is_strikethrough: bool
|
||||
}
|
||||
|
||||
impl Style {
|
||||
/// Creates a new Style with no differences.
|
||||
pub fn new() -> Style {
|
||||
Style::default()
|
||||
}
|
||||
|
||||
/// Returns a `Style` with the bold property set.
|
||||
pub fn bold(&self) -> Style {
|
||||
Style { is_bold: true, .. *self }
|
||||
}
|
||||
|
||||
/// Returns a `Style` with the dimmed property set.
|
||||
pub fn dimmed(&self) -> Style {
|
||||
Style { is_dimmed: true, .. *self }
|
||||
}
|
||||
|
||||
/// Returns a `Style` with the italic property set.
|
||||
pub fn italic(&self) -> Style {
|
||||
Style { is_italic: true, .. *self }
|
||||
}
|
||||
|
||||
/// Returns a `Style` with the underline property set.
|
||||
pub fn underline(&self) -> Style {
|
||||
Style { is_underline: true, .. *self }
|
||||
}
|
||||
|
||||
/// Returns a `Style` with the blink property set.
|
||||
pub fn blink(&self) -> Style {
|
||||
Style { is_blink: true, .. *self }
|
||||
}
|
||||
|
||||
/// Returns a `Style` with the reverse property set.
|
||||
pub fn reverse(&self) -> Style {
|
||||
Style { is_reverse: true, .. *self }
|
||||
}
|
||||
|
||||
/// Returns a `Style` with the hidden property set.
|
||||
pub fn hidden(&self) -> Style {
|
||||
Style { is_hidden: true, .. *self }
|
||||
}
|
||||
|
||||
/// Returns a `Style` with the hidden property set.
|
||||
pub fn strikethrough(&self) -> Style {
|
||||
Style { is_strikethrough: true, .. *self }
|
||||
}
|
||||
|
||||
/// Returns a `Style` with the foreground colour property set.
|
||||
pub fn fg(&self, foreground: Colour) -> Style {
|
||||
Style { foreground: Some(foreground), .. *self }
|
||||
}
|
||||
|
||||
/// Returns a `Style` with the background colour property set.
|
||||
pub fn on(&self, background: Colour) -> Style {
|
||||
Style { background: Some(background), .. *self }
|
||||
}
|
||||
|
||||
/// Return true if this `Style` has no actual styles, and can be written
|
||||
/// without any control characters.
|
||||
pub fn is_plain(self) -> bool {
|
||||
self == Style::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Style {
|
||||
|
||||
/// Returns a style with *no* properties set. Formatting text using this
|
||||
/// style returns the exact same text.
|
||||
///
|
||||
/// ```
|
||||
/// use ansi_term::Style;
|
||||
/// assert_eq!(None, Style::default().foreground);
|
||||
/// assert_eq!(None, Style::default().background);
|
||||
/// assert_eq!(false, Style::default().is_bold);
|
||||
/// assert_eq!("txt", Style::default().paint("txt").to_string());
|
||||
/// ```
|
||||
fn default() -> Style {
|
||||
Style {
|
||||
foreground: None,
|
||||
background: None,
|
||||
is_bold: false,
|
||||
is_dimmed: false,
|
||||
is_italic: false,
|
||||
is_underline: false,
|
||||
is_blink: false,
|
||||
is_reverse: false,
|
||||
is_hidden: false,
|
||||
is_strikethrough: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---- colours ----
|
||||
|
||||
/// A colour is one specific type of ANSI escape code, and can refer
|
||||
/// to either the foreground or background colour.
|
||||
///
|
||||
/// These use the standard numeric sequences.
|
||||
/// See <http://invisible-island.net/xterm/ctlseqs/ctlseqs.html>
|
||||
#[derive(PartialEq, Clone, Copy, Debug)]
|
||||
pub enum Colour {
|
||||
|
||||
/// Colour #0 (foreground code `30`, background code `40`).
|
||||
///
|
||||
/// This is not necessarily the background colour, and using it as one may
|
||||
/// render the text hard to read on terminals with dark backgrounds.
|
||||
Black,
|
||||
|
||||
/// Colour #1 (foreground code `31`, background code `41`).
|
||||
Red,
|
||||
|
||||
/// Colour #2 (foreground code `32`, background code `42`).
|
||||
Green,
|
||||
|
||||
/// Colour #3 (foreground code `33`, background code `43`).
|
||||
Yellow,
|
||||
|
||||
/// Colour #4 (foreground code `34`, background code `44`).
|
||||
Blue,
|
||||
|
||||
/// Colour #5 (foreground code `35`, background code `45`).
|
||||
Purple,
|
||||
|
||||
/// Colour #6 (foreground code `36`, background code `46`).
|
||||
Cyan,
|
||||
|
||||
/// Colour #7 (foreground code `37`, background code `47`).
|
||||
///
|
||||
/// As above, this is not necessarily the foreground colour, and may be
|
||||
/// hard to read on terminals with light backgrounds.
|
||||
White,
|
||||
|
||||
/// A colour number from 0 to 255, for use in 256-colour terminal
|
||||
/// environments.
|
||||
///
|
||||
/// - Colours 0 to 7 are the `Black` to `White` variants respectively.
|
||||
/// These colours can usually be changed in the terminal emulator.
|
||||
/// - Colours 8 to 15 are brighter versions of the eight colours above.
|
||||
/// These can also usually be changed in the terminal emulator, or it
|
||||
/// could be configured to use the original colours and show the text in
|
||||
/// bold instead. It varies depending on the program.
|
||||
/// - Colours 16 to 231 contain several palettes of bright colours,
|
||||
/// arranged in six squares measuring six by six each.
|
||||
/// - Colours 232 to 255 are shades of grey from black to white.
|
||||
///
|
||||
/// It might make more sense to look at a [colour chart][cc].
|
||||
///
|
||||
/// [cc]: https://upload.wikimedia.org/wikipedia/en/1/15/Xterm_256color_chart.svg
|
||||
Fixed(u8),
|
||||
|
||||
/// A 24-bit RGB color, as specified by ISO-8613-3.
|
||||
RGB(u8, u8, u8),
|
||||
}
|
||||
|
||||
|
||||
impl Colour {
|
||||
/// Return a `Style` with the foreground colour set to this colour.
|
||||
pub fn normal(self) -> Style {
|
||||
Style { foreground: Some(self), .. Style::default() }
|
||||
}
|
||||
|
||||
/// Returns a `Style` with the bold property set.
|
||||
pub fn bold(self) -> Style {
|
||||
Style { foreground: Some(self), is_bold: true, .. Style::default() }
|
||||
}
|
||||
|
||||
/// Returns a `Style` with the dimmed property set.
|
||||
pub fn dimmed(self) -> Style {
|
||||
Style { foreground: Some(self), is_dimmed: true, .. Style::default() }
|
||||
}
|
||||
|
||||
/// Returns a `Style` with the italic property set.
|
||||
pub fn italic(self) -> Style {
|
||||
Style { foreground: Some(self), is_italic: true, .. Style::default() }
|
||||
}
|
||||
|
||||
/// Returns a `Style` with the underline property set.
|
||||
pub fn underline(self) -> Style {
|
||||
Style { foreground: Some(self), is_underline: true, .. Style::default() }
|
||||
}
|
||||
|
||||
/// Returns a `Style` with the blink property set.
|
||||
pub fn blink(self) -> Style {
|
||||
Style { foreground: Some(self), is_blink: true, .. Style::default() }
|
||||
}
|
||||
|
||||
/// Returns a `Style` with the reverse property set.
|
||||
pub fn reverse(self) -> Style {
|
||||
Style { foreground: Some(self), is_reverse: true, .. Style::default() }
|
||||
}
|
||||
|
||||
/// Returns a `Style` with the hidden property set.
|
||||
pub fn hidden(self) -> Style {
|
||||
Style { foreground: Some(self), is_hidden: true, .. Style::default() }
|
||||
}
|
||||
|
||||
/// Returns a `Style` with the strikethrough property set.
|
||||
pub fn strikethrough(self) -> Style {
|
||||
Style { foreground: Some(self), is_strikethrough: true, .. Style::default() }
|
||||
}
|
||||
|
||||
/// Returns a `Style` with the background colour property set.
|
||||
pub fn on(self, background: Colour) -> Style {
|
||||
Style { foreground: Some(self), background: Some(background), .. Style::default() }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Colour> for Style {
|
||||
|
||||
/// You can turn a `Colour` into a `Style` with the foreground colour set
|
||||
/// with the `From` trait.
|
||||
///
|
||||
/// ```
|
||||
/// use ansi_term::{Style, Colour};
|
||||
/// let green_foreground = Style::default().fg(Colour::Green);
|
||||
/// assert_eq!(green_foreground, Colour::Green.normal());
|
||||
/// assert_eq!(green_foreground, Colour::Green.into());
|
||||
/// assert_eq!(green_foreground, Style::from(Colour::Green));
|
||||
/// ```
|
||||
fn from(colour: Colour) -> Style {
|
||||
colour.normal()
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
/// Enables ANSI code support on Windows 10.
|
||||
///
|
||||
/// This uses Windows API calls to alter the properties of the console that
|
||||
/// the program is running in.
|
||||
///
|
||||
/// https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx
|
||||
///
|
||||
/// Returns a `Result` with the Windows error code if unsuccessful.
|
||||
#[cfg(windows)]
|
||||
pub fn enable_ansi_support() -> Result<(), u32> {
|
||||
use winapi::um::processenv::GetStdHandle;
|
||||
use winapi::um::errhandlingapi::GetLastError;
|
||||
use winapi::um::consoleapi::{GetConsoleMode, SetConsoleMode};
|
||||
|
||||
const STD_OUT_HANDLE: u32 = -11i32 as u32;
|
||||
const ENABLE_VIRTUAL_TERMINAL_PROCESSING: u32 = 0x0004;
|
||||
|
||||
unsafe {
|
||||
// https://docs.microsoft.com/en-us/windows/console/getstdhandle
|
||||
let std_out_handle = GetStdHandle(STD_OUT_HANDLE);
|
||||
let error_code = GetLastError();
|
||||
if error_code != 0 { return Err(error_code); }
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/console/getconsolemode
|
||||
let mut console_mode: u32 = 0;
|
||||
GetConsoleMode(std_out_handle, &mut console_mode);
|
||||
let error_code = GetLastError();
|
||||
if error_code != 0 { return Err(error_code); }
|
||||
|
||||
// VT processing not already enabled?
|
||||
if console_mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0 {
|
||||
// https://docs.microsoft.com/en-us/windows/console/setconsolemode
|
||||
SetConsoleMode(std_out_handle, console_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
||||
let error_code = GetLastError();
|
||||
if error_code != 0 { return Err(error_code); }
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
use std::fmt;
|
||||
use std::io;
|
||||
|
||||
|
||||
pub trait AnyWrite {
|
||||
type wstr: ?Sized;
|
||||
type Error;
|
||||
|
||||
fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<(), Self::Error>;
|
||||
|
||||
fn write_str(&mut self, s: &Self::wstr) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
|
||||
impl<'a> AnyWrite for fmt::Write + 'a {
|
||||
type wstr = str;
|
||||
type Error = fmt::Error;
|
||||
|
||||
fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<(), Self::Error> {
|
||||
fmt::Write::write_fmt(self, fmt)
|
||||
}
|
||||
|
||||
fn write_str(&mut self, s: &Self::wstr) -> Result<(), Self::Error> {
|
||||
fmt::Write::write_str(self, s)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'a> AnyWrite for io::Write + 'a {
|
||||
type wstr = [u8];
|
||||
type Error = io::Error;
|
||||
|
||||
fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<(), Self::Error> {
|
||||
io::Write::write_fmt(self, fmt)
|
||||
}
|
||||
|
||||
fn write_str(&mut self, s: &Self::wstr) -> Result<(), Self::Error> {
|
||||
io::Write::write_all(self, s)
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
{"files":{"Cargo.lock":"c4f860261c1fd22b88a416ddc2546fde7ac6b8d0ee584979b2cc930709840edd","Cargo.toml":"9fd70e5e3152c11487347f14f4f2703486e3de3a1c9f0249f6196ca3b0308511","README.md":"17e5ed3a3bd9b898e73c3056711daabe1238fe9682d24d255f8263fae4eb783d","examples/generate_spidermonkey.rs":"913a34e84f45bd8bbe305629ca5abb2818370333a769074a757ce1bb4bb54778","src/export.rs":"12d94158d1285ac880e3c105f086ad1c22c3c55432fdc3123f8f2ab4ccc71152","src/import.rs":"2f587c0737310e3b50280e0b62069de2bfa585d0812e2f0eb469cdada8502291","src/lib.rs":"546b6b13669d659d35a130dd29cfe3eac547a41d210adb194db1b214194295d7","src/spec.rs":"9adb6ff0168e3ec29735a5a9198756076385b6f6063883e47190846f46fa7247","src/util.rs":"9b19ace8cfc0e47e6090ba9223123eba0214d05c1969414272b6785b25de36d2"},"package":"d535cc5246fd9035268770420afd76c05f87e68b83ebed0ac94e8258e88fc353"}
|
|
@ -1,315 +0,0 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "Inflector"
|
||||
version = "0.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "binjs_meta"
|
||||
version = "0.5.4"
|
||||
dependencies = [
|
||||
"Inflector 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clap 2.30.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"weedle 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"yaml-rust 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "redox_termios"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "utf8-ranges"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "weedle"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"nom 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[metadata]
|
||||
"checksum Inflector 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
|
||||
"checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4"
|
||||
"checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455"
|
||||
"checksum atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8352656fd42c30a0c3c89d26dea01e3b77c0ab2af18230835c15e2e13cd51859"
|
||||
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
|
||||
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
|
||||
"checksum clap 2.30.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1c07b9257a00f3fc93b7f3c417fc15607ec7a56823bc2c37ec744e266387de5b"
|
||||
"checksum either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "740178ddf48b1a9e878e6d6509a1442a2d42fd2928aae8e7a6f8a36fb01981b3"
|
||||
"checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3"
|
||||
"checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
|
||||
"checksum itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484"
|
||||
"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
|
||||
"checksum libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1e5d97d6708edaa407429faa671b942dc0f2727222fb6b6539bf1db936e4b121"
|
||||
"checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e"
|
||||
"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2"
|
||||
"checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d"
|
||||
"checksum nom 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b471253da97532da4b61552249c521e01e736071f71c1a4f7ebbfbf0a06aad6"
|
||||
"checksum quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
"checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd"
|
||||
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
|
||||
"checksum regex 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3d8c9f33201f46669484bacc312b00e7541bed6aaf296dffe2bb4e0ac6b8ce2a"
|
||||
"checksum regex-syntax 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "1132f845907680735a84409c3bebc64d1364a5683ffbce899550cd09d5eaefc1"
|
||||
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
|
||||
"checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
|
||||
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
|
||||
"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693"
|
||||
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
|
||||
"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f"
|
||||
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
|
||||
"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c"
|
||||
"checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
|
||||
"checksum weedle 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a7d4f9feb723a800d8f7b74edc9fa44ff35cb0b2ec64886714362f423427f37"
|
||||
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
|
||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
"checksum winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80"
|
||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
"checksum yaml-rust 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57ab38ee1a4a266ed033496cf9af1828d8d6e6c1cfa5f643a2809effcae4d628"
|
|
@ -1,46 +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 = "binjs_meta"
|
||||
version = "0.5.4"
|
||||
authors = ["David Teller <D.O.Teller@gmail.com>"]
|
||||
description = "Part of binjs-ref. Tools for manipulating grammars. You probably do not want to use this crate directly unless you're writing an encoder, decoder or parser generator for binjs."
|
||||
homepage = "https://binast.github.io/ecmascript-binary-ast/"
|
||||
readme = "README.md"
|
||||
keywords = ["javascript", "js", "binjs", "ast"]
|
||||
categories = ["compression", "parsing", "web-programming"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/binast/binjs-ref"
|
||||
[dependencies.Inflector]
|
||||
version = "^0.11.4"
|
||||
default-features = false
|
||||
|
||||
[dependencies.itertools]
|
||||
version = "^0.8"
|
||||
|
||||
[dependencies.log]
|
||||
version = "^0.4"
|
||||
|
||||
[dependencies.weedle]
|
||||
version = "0.11"
|
||||
[dev-dependencies.clap]
|
||||
version = "^2.0"
|
||||
|
||||
[dev-dependencies.env_logger]
|
||||
version = "^0.6"
|
||||
|
||||
[dev-dependencies.yaml-rust]
|
||||
version = "^0.4"
|
||||
[badges.travis-ci]
|
||||
branch = "master"
|
||||
repository = "https://github.com/binast/binjs-ref"
|
|
@ -1,7 +0,0 @@
|
|||
# About
|
||||
|
||||
This crate contains the tools used to manipulate the abstract syntax of JavaScript,
|
||||
i.e. generate parsers, encoders, etc from a specification of ASTs of the language.
|
||||
|
||||
Actual tools to manipulate ASTs for a specific version of JavaScript are
|
||||
in dependent crates.
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,531 +0,0 @@
|
|||
use spec::*;
|
||||
use util::*;
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
/// A tool designed to replace all anonymous types in a specification
|
||||
/// of the language by explicitly named types.
|
||||
///
|
||||
/// Consider the following mini-specifications for JSON:
|
||||
///
|
||||
/// ```idl
|
||||
/// interface Value {
|
||||
/// attribute (Object or String or Number or Array or Boolean)? value;
|
||||
/// }
|
||||
/// interface Object {
|
||||
/// attribute FrozenArray<Property> properties;
|
||||
/// }
|
||||
/// interface Property {
|
||||
/// attribute DOMString name;
|
||||
/// attribute Value value;
|
||||
/// }
|
||||
/// interface Array {
|
||||
/// attribute FrozenArray<Value?> items;
|
||||
/// }
|
||||
/// // ... Skipping definitions of String, Number, Boolean
|
||||
/// ```
|
||||
///
|
||||
/// The deanonymizer will rewrite them as follows:
|
||||
///
|
||||
/// ```idl
|
||||
/// interface Value { // Deanonymized optional sum
|
||||
/// attribute OptionalObjectOrStringOrNumberOrArrayOrBoolean value;
|
||||
/// }
|
||||
/// interface Object { // Deanonymized list
|
||||
/// attribute ListOfProperty properties;
|
||||
/// }
|
||||
/// interface Property { // No change
|
||||
/// attribute DOMString name;
|
||||
/// attribute Value value;
|
||||
/// }
|
||||
/// interface Array { // Deanonymized list of options
|
||||
/// attribute ListOfOptionalValue items;
|
||||
/// }
|
||||
/// // ... Skipping definitions of String, Number, Boolean
|
||||
///
|
||||
/// typedef ObjectOrStringOrNumberOrArrayOrBoolean? OptionalObjectOrStringOrNumberOrArrayOrBoolean;
|
||||
/// typedef (Object
|
||||
/// or String
|
||||
/// or Number
|
||||
/// or Array
|
||||
/// or Boolean)
|
||||
/// ObjectOrStringOrNumberOrArrayOrBoolean;
|
||||
/// typedef FrozenArray<Property> ListOfProperty;
|
||||
/// typedef FrozenArray<OptionalValue> ListOfOptionalValue;
|
||||
/// typedef Value? Optionalvalue;
|
||||
/// ```
|
||||
///
|
||||
/// This deanonymization lets us cleanly define intermediate data structures and/or parsers
|
||||
/// implementing the webidl specification.
|
||||
pub struct TypeDeanonymizer {
|
||||
builder: SpecBuilder,
|
||||
|
||||
/// When we encounter `typedef (A or B) C`
|
||||
/// and `typedef (C or D) E`, we deanonymize into
|
||||
/// `typedef (A or B or D) E`.
|
||||
///
|
||||
/// This maintains the relationship that `E` (value)
|
||||
/// contains `C` (key).
|
||||
supersums_of: HashMap<NodeName, HashSet<NodeName>>,
|
||||
}
|
||||
impl TypeDeanonymizer {
|
||||
/// Create an empty TypeDeanonymizer.
|
||||
pub fn new(spec: &Spec) -> Self {
|
||||
let mut result = TypeDeanonymizer {
|
||||
builder: SpecBuilder::new(),
|
||||
supersums_of: HashMap::new(),
|
||||
};
|
||||
let mut skip_name_map: HashMap<&FieldName, FieldName> = HashMap::new();
|
||||
|
||||
// Copy field names
|
||||
for (_, name) in spec.field_names() {
|
||||
result.builder.import_field_name(name)
|
||||
}
|
||||
|
||||
for (_, interface) in spec.interfaces_by_name() {
|
||||
for field in interface.contents().fields() {
|
||||
if field.is_lazy() {
|
||||
let skip_name = result
|
||||
.builder
|
||||
.field_name(format!("{}_skip", field.name().to_str()).to_str());
|
||||
skip_name_map.insert(field.name(), skip_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy and deanonymize interfaces.
|
||||
for (name, interface) in spec.interfaces_by_name() {
|
||||
result.builder.import_node_name(name);
|
||||
// Collect interfaces to copy them into the `builder`
|
||||
// and walk through their fields to deanonymize types.
|
||||
|
||||
let mut fields = vec![];
|
||||
|
||||
// Copy other fields.
|
||||
for field in interface.contents().fields() {
|
||||
result.import_type(spec, field.type_(), None);
|
||||
fields.push(field.clone());
|
||||
}
|
||||
|
||||
// Copy the declaration.
|
||||
let mut declaration = result.builder.add_interface(name).unwrap();
|
||||
for field in fields.drain(..) {
|
||||
// Create *_skip field just before the lazy field.
|
||||
// See also tagged_tuple in write.rs.
|
||||
if field.is_lazy() {
|
||||
declaration.with_field(
|
||||
skip_name_map.get(field.name()).unwrap(),
|
||||
Type::offset().required(),
|
||||
);
|
||||
}
|
||||
declaration.with_field_laziness(
|
||||
field.name(),
|
||||
field.type_().clone(),
|
||||
field.laziness(),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(ref field_name) = interface.scoped_dictionary() {
|
||||
declaration.with_scoped_dictionary(field_name);
|
||||
}
|
||||
}
|
||||
// Copy and deanonymize typedefs
|
||||
for (name, definition) in spec.typedefs_by_name() {
|
||||
result.builder.import_node_name(name);
|
||||
if result.builder.get_typedef(name).is_some() {
|
||||
// Already imported by following links.
|
||||
continue;
|
||||
}
|
||||
result.import_type(spec, &definition, Some(name.clone()));
|
||||
}
|
||||
// Copy and deanonymize string enums
|
||||
for (name, definition) in spec.string_enums_by_name() {
|
||||
result.builder.import_node_name(name);
|
||||
let mut strings: Vec<_> = definition.strings().iter().collect();
|
||||
let mut declaration = result.builder.add_string_enum(name).unwrap();
|
||||
for string in strings.drain(..) {
|
||||
declaration.with_string(&string);
|
||||
}
|
||||
}
|
||||
debug!(target: "export_utils", "Names: {:?}", result.builder.names().keys().format(", "));
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn supersums(&self) -> &HashMap<NodeName, HashSet<NodeName>> {
|
||||
&self.supersums_of
|
||||
}
|
||||
|
||||
/// Convert into a new specification.
|
||||
pub fn into_spec(self, options: SpecOptions) -> Spec {
|
||||
self.builder.into_spec(options)
|
||||
}
|
||||
|
||||
/// If `name` is the name of a (deanonymized) type, return the corresponding type.
|
||||
pub fn get_node_name(&self, name: &str) -> Option<NodeName> {
|
||||
self.builder.get_node_name(name)
|
||||
}
|
||||
|
||||
/// Returns `(sum, name)` where `sum` is `Some(names)` iff this type can be resolved to a sum of interfaces.
|
||||
fn import_type(
|
||||
&mut self,
|
||||
spec: &Spec,
|
||||
type_: &Type,
|
||||
public_name: Option<NodeName>,
|
||||
) -> (Option<HashSet<NodeName>>, NodeName) {
|
||||
debug!(target: "export_utils", "import_type {:?} => {:?}", public_name, type_);
|
||||
if type_.is_optional() {
|
||||
let (_, spec_name) = self.import_typespec(spec, &type_.spec, None);
|
||||
let my_name = match public_name {
|
||||
None => self.builder.node_name(&format!("Optional{}", spec_name)),
|
||||
Some(ref name) => name.clone(),
|
||||
};
|
||||
let deanonymized = Type::named(&spec_name).optional().unwrap(); // Named types can always be made optional.
|
||||
if let Some(ref mut typedef) = self.builder.add_typedef(&my_name) {
|
||||
debug!(target: "export_utils", "import_type introduced {:?}", my_name);
|
||||
typedef.with_type(deanonymized.clone());
|
||||
} else {
|
||||
debug!(target: "export_utils", "import_type: Attempting to redefine typedef {name}", name = my_name.to_str());
|
||||
}
|
||||
(None, my_name)
|
||||
} else {
|
||||
self.import_typespec(spec, &type_.spec, public_name)
|
||||
}
|
||||
}
|
||||
fn import_typespec(
|
||||
&mut self,
|
||||
spec: &Spec,
|
||||
type_spec: &TypeSpec,
|
||||
public_name: Option<NodeName>,
|
||||
) -> (Option<HashSet<NodeName>>, NodeName) {
|
||||
debug!(target: "export_utils", "import_typespec {:?} => {:?}", public_name, type_spec);
|
||||
match *type_spec {
|
||||
TypeSpec::Boolean
|
||||
| TypeSpec::Number
|
||||
| TypeSpec::UnsignedLong
|
||||
| TypeSpec::PropertyKey
|
||||
| TypeSpec::IdentifierName
|
||||
| TypeSpec::String
|
||||
| TypeSpec::Offset
|
||||
| TypeSpec::Void => {
|
||||
if let Some(ref my_name) = public_name {
|
||||
if let Some(ref mut typedef) = self.builder.add_typedef(&my_name) {
|
||||
debug!(target: "export_utils", "import_typespec: Defining {name} (primitive)", name = my_name.to_str());
|
||||
typedef.with_type(type_spec.clone().required());
|
||||
} else {
|
||||
debug!(target: "export_utils", "import_typespec: Attempting to redefine typedef {name}", name = my_name.to_str());
|
||||
}
|
||||
}
|
||||
// This is a workaround for typedefs in the webidl that are not truly typedefs.
|
||||
// See https://github.com/Yoric/ecmascript-binary-ast/pull/1
|
||||
let name = match *type_spec {
|
||||
TypeSpec::PropertyKey => self.builder.node_name("PropertyKey"),
|
||||
TypeSpec::IdentifierName => self.builder.node_name("IdentifierName"),
|
||||
_ => self.builder.node_name(&format!("@@{:?}", type_spec)),
|
||||
};
|
||||
(None, name)
|
||||
}
|
||||
TypeSpec::NamedType(ref link) => {
|
||||
let resolved = spec.get_type_by_name(link)
|
||||
.unwrap_or_else(|| panic!("While deanonymizing, could not find the definition of {} in the original spec.", link.to_str()));
|
||||
let (sum, rewrite, primitive) = match resolved {
|
||||
NamedType::StringEnum(_) => {
|
||||
// - Can't use in a sum
|
||||
// - No rewriting happened.
|
||||
(None, None, None)
|
||||
}
|
||||
NamedType::Typedef(ref type_) => {
|
||||
// - Might use in a sum.
|
||||
// - Might be rewritten.
|
||||
let (sum, name) = self.import_type(spec, type_, Some(link.clone()));
|
||||
(sum, Some(name), type_.get_primitive(spec))
|
||||
}
|
||||
NamedType::Interface(_) => {
|
||||
// - May use in a sum.
|
||||
// - If a rewriting takes place, it didn't change the names.
|
||||
let sum = [link.clone()].iter().cloned().collect();
|
||||
(Some(sum), None, None)
|
||||
}
|
||||
};
|
||||
debug!(target: "export_utils", "import_typespec dealing with named type {}, public name {:?} => {:?}",
|
||||
link, public_name, rewrite);
|
||||
if let Some(ref my_name) = public_name {
|
||||
// If we have a public name, alias it to `content`
|
||||
if let Some(content) = rewrite {
|
||||
let deanonymized = match primitive {
|
||||
None
|
||||
| Some(IsNullable {
|
||||
is_nullable: true, ..
|
||||
})
|
||||
| Some(IsNullable {
|
||||
content: Primitive::Interface(_),
|
||||
..
|
||||
}) => Type::named(&content).required(),
|
||||
Some(IsNullable {
|
||||
content: Primitive::String,
|
||||
..
|
||||
}) => Type::string().required(),
|
||||
Some(IsNullable {
|
||||
content: Primitive::IdentifierName,
|
||||
..
|
||||
}) => Type::identifier_name().required(),
|
||||
Some(IsNullable {
|
||||
content: Primitive::PropertyKey,
|
||||
..
|
||||
}) => Type::property_key().required(),
|
||||
Some(IsNullable {
|
||||
content: Primitive::Number,
|
||||
..
|
||||
}) => Type::number().required(),
|
||||
Some(IsNullable {
|
||||
content: Primitive::UnsignedLong,
|
||||
..
|
||||
}) => Type::unsigned_long().required(),
|
||||
Some(IsNullable {
|
||||
content: Primitive::Boolean,
|
||||
..
|
||||
}) => Type::bool().required(),
|
||||
Some(IsNullable {
|
||||
content: Primitive::Offset,
|
||||
..
|
||||
}) => Type::offset().required(),
|
||||
Some(IsNullable {
|
||||
content: Primitive::Void,
|
||||
..
|
||||
}) => Type::void().required(),
|
||||
};
|
||||
debug!(target: "export_utils", "import_typespec aliasing {:?} => {:?}",
|
||||
my_name, deanonymized);
|
||||
if let Some(ref mut typedef) = self.builder.add_typedef(&my_name) {
|
||||
debug!(target: "export_utils", "import_typespec: Defining {name} (name to content)", name = my_name.to_str());
|
||||
typedef.with_type(deanonymized.clone());
|
||||
} else {
|
||||
debug!(target: "export_utils", "import_typespec: Attempting to redefine typedef {name}", name = my_name.to_str());
|
||||
}
|
||||
}
|
||||
// Also, don't forget to copy the typedef and alias `link`
|
||||
let deanonymized = Type::named(link).required();
|
||||
if let Some(ref mut typedef) = self.builder.add_typedef(&my_name) {
|
||||
debug!(target: "export_utils", "import_typespec: Defining {name} (name to link)", name = my_name.to_str());
|
||||
typedef.with_type(deanonymized.clone());
|
||||
} else {
|
||||
debug!(target: "export_utils", "import_typespec: Attempting to redefine typedef {name}", name = my_name.to_str());
|
||||
}
|
||||
}
|
||||
(sum, link.clone())
|
||||
}
|
||||
TypeSpec::Array {
|
||||
ref contents,
|
||||
ref supports_empty,
|
||||
} => {
|
||||
let (_, contents_name) = self.import_type(spec, contents, None);
|
||||
let my_name = match public_name {
|
||||
None => self.builder.node_name(&format!(
|
||||
"{non_empty}ListOf{content}",
|
||||
non_empty = if *supports_empty { "" } else { "NonEmpty" },
|
||||
content = contents_name.to_str()
|
||||
)),
|
||||
Some(ref name) => name.clone(),
|
||||
};
|
||||
let deanonymized = if *supports_empty {
|
||||
Type::named(&contents_name).array()
|
||||
} else {
|
||||
Type::named(&contents_name).non_empty_array()
|
||||
};
|
||||
if let Some(ref mut typedef) = self.builder.add_typedef(&my_name) {
|
||||
debug!(target: "export_utils", "import_typespec: Defining {name} (name to list)",
|
||||
name = my_name.to_str());
|
||||
typedef.with_type(deanonymized.clone());
|
||||
} else {
|
||||
debug!(target: "export_utils", "import_typespec: Attempting to redefine typedef {name}", name = my_name.to_str());
|
||||
}
|
||||
(None, my_name)
|
||||
}
|
||||
TypeSpec::TypeSum(ref sum) => {
|
||||
let mut full_sum = HashSet::new();
|
||||
let mut names = vec![];
|
||||
let mut subsums = vec![];
|
||||
for sub_type in sum.types() {
|
||||
let (sub_sum, name) = self.import_typespec(spec, sub_type, None);
|
||||
let mut sub_sum = sub_sum.unwrap_or_else(
|
||||
|| panic!("While treating {:?}, attempting to create a sum containing {}, which isn't an interface or a sum of interfaces", type_spec, name)
|
||||
);
|
||||
if sub_sum.len() > 1 {
|
||||
// The subtype is itself a sum.
|
||||
subsums.push(name.clone())
|
||||
}
|
||||
names.push(name);
|
||||
for item in sub_sum.drain() {
|
||||
full_sum.insert(item);
|
||||
}
|
||||
}
|
||||
let my_name = match public_name {
|
||||
None => self.builder.node_name(&format!(
|
||||
"{}",
|
||||
names.into_iter().sorted().into_iter().format("Or")
|
||||
)),
|
||||
Some(ref name) => name.clone(),
|
||||
};
|
||||
for subsum_name in subsums {
|
||||
// So, `my_name` is a superset of `subsum_name`.
|
||||
let supersum_entry = self
|
||||
.supersums_of
|
||||
.entry(subsum_name.clone())
|
||||
.or_insert_with(|| HashSet::new());
|
||||
supersum_entry.insert(my_name.clone());
|
||||
}
|
||||
let sum: Vec<_> = full_sum.iter().map(Type::named).collect();
|
||||
let deanonymized = Type::sum(&sum).required();
|
||||
if let Some(ref mut typedef) = self.builder.add_typedef(&my_name) {
|
||||
debug!(target: "export_utils", "import_typespec: Defining {name} (name to sum)", name = my_name.to_str());
|
||||
typedef.with_type(deanonymized.clone());
|
||||
} else {
|
||||
debug!(target: "export_utils", "import_type: Attempting to redefine typedef {name}", name = my_name.to_str());
|
||||
}
|
||||
(Some(full_sum), my_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Utility to give a name to a type or type spec.
|
||||
pub struct TypeName;
|
||||
impl TypeName {
|
||||
pub fn type_(type_: &Type) -> String {
|
||||
let spec_name = Self::type_spec(type_.spec());
|
||||
if type_.is_optional() {
|
||||
format!("Optional{}", spec_name)
|
||||
} else {
|
||||
spec_name
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_spec(spec: &TypeSpec) -> String {
|
||||
match *spec {
|
||||
TypeSpec::Array {
|
||||
ref contents,
|
||||
supports_empty: false,
|
||||
} => format!("NonEmptyListOf{}", Self::type_(contents)),
|
||||
TypeSpec::Array {
|
||||
ref contents,
|
||||
supports_empty: true,
|
||||
} => format!("ListOf{}", Self::type_(contents)),
|
||||
TypeSpec::NamedType(ref name) => name.to_string().clone(),
|
||||
TypeSpec::Offset => "_Offset".to_string(),
|
||||
TypeSpec::Boolean => "_Bool".to_string(),
|
||||
TypeSpec::Number => "_Number".to_string(),
|
||||
TypeSpec::UnsignedLong => "_UnsignedLong".to_string(),
|
||||
TypeSpec::String => "_String".to_string(),
|
||||
TypeSpec::Void => "_Void".to_string(),
|
||||
TypeSpec::IdentifierName => "IdentifierName".to_string(),
|
||||
TypeSpec::PropertyKey => "PropertyKey".to_string(),
|
||||
TypeSpec::TypeSum(ref sum) => format!(
|
||||
"{}",
|
||||
sum.types()
|
||||
.iter()
|
||||
.map(Self::type_spec)
|
||||
.sorted()
|
||||
.into_iter()
|
||||
.format("Or")
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Export a type specification as webidl.
|
||||
///
|
||||
/// Designed for generating documentation.
|
||||
pub struct ToWebidl;
|
||||
impl ToWebidl {
|
||||
/// Export a TypeSpec.
|
||||
pub fn spec(spec: &TypeSpec, prefix: &str, indent: &str) -> Option<String> {
|
||||
let result = match *spec {
|
||||
TypeSpec::Offset => {
|
||||
return None;
|
||||
}
|
||||
TypeSpec::Array {
|
||||
ref contents,
|
||||
ref supports_empty,
|
||||
} => match Self::type_(&*contents, prefix, indent) {
|
||||
None => {
|
||||
return None;
|
||||
}
|
||||
Some(description) => format!(
|
||||
"{emptiness}FrozenArray<{}>",
|
||||
description,
|
||||
emptiness = if *supports_empty { "" } else { "[NonEmpty] " }
|
||||
),
|
||||
},
|
||||
TypeSpec::Boolean => "bool".to_string(),
|
||||
TypeSpec::String => "string".to_string(),
|
||||
TypeSpec::PropertyKey => "[PropertyKey] string".to_string(),
|
||||
TypeSpec::IdentifierName => "[IdentifierName] string".to_string(),
|
||||
TypeSpec::Number => "number".to_string(),
|
||||
TypeSpec::UnsignedLong => "unsigned long".to_string(),
|
||||
TypeSpec::NamedType(ref name) => name.to_str().to_string(),
|
||||
TypeSpec::TypeSum(ref sum) => format!(
|
||||
"({})",
|
||||
sum.types()
|
||||
.iter()
|
||||
.filter_map(|x| Self::spec(x, "", indent))
|
||||
.format(" or ")
|
||||
),
|
||||
TypeSpec::Void => "void".to_string(),
|
||||
};
|
||||
Some(result)
|
||||
}
|
||||
|
||||
/// Export a Type
|
||||
pub fn type_(type_: &Type, prefix: &str, indent: &str) -> Option<String> {
|
||||
let pretty_type = Self::spec(type_.spec(), prefix, indent);
|
||||
match pretty_type {
|
||||
None => None,
|
||||
Some(pretty_type) => Some(format!(
|
||||
"{}{}",
|
||||
pretty_type,
|
||||
if type_.is_optional() { "?" } else { "" }
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Export an Interface
|
||||
pub fn interface(interface: &Interface, prefix: &str, indent: &str) -> String {
|
||||
let mut result = format!(
|
||||
"{prefix} interface {name} : Node {{\n",
|
||||
prefix = prefix,
|
||||
name = interface.name().to_str()
|
||||
);
|
||||
{
|
||||
let prefix = format!("{prefix}{indent}", prefix = prefix, indent = indent);
|
||||
for field in interface.contents().fields() {
|
||||
match Self::type_(field.type_(), &prefix, indent) {
|
||||
None =>
|
||||
/* generated field, ignore */
|
||||
{}
|
||||
Some(description) => {
|
||||
if let Some(ref doc) = field.doc() {
|
||||
result.push_str(&format!(
|
||||
"{prefix}// {doc}\n",
|
||||
prefix = prefix,
|
||||
doc = doc
|
||||
));
|
||||
}
|
||||
result.push_str(&format!(
|
||||
"{prefix}{description} {name};\n",
|
||||
prefix = prefix,
|
||||
name = field.name().to_str(),
|
||||
description = description
|
||||
));
|
||||
if field.doc().is_some() {
|
||||
result.push_str("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
result.push_str(&format!("{prefix} }}\n", prefix = prefix));
|
||||
result
|
||||
}
|
||||
}
|
|
@ -1,358 +0,0 @@
|
|||
use spec::{self, Laziness, SpecBuilder, TypeSpec, TypeSum};
|
||||
use weedle::common::Identifier;
|
||||
use weedle::types::*;
|
||||
use weedle::*;
|
||||
|
||||
fn nullable<T: std::fmt::Debug>(src: &MayBeNull<T>, dst: TypeSpec) -> spec::Type {
|
||||
if src.q_mark.is_some() {
|
||||
dst.optional()
|
||||
.unwrap_or_else(|| panic!("This type could not be made optional {:?}", src.type_))
|
||||
} else {
|
||||
dst.required()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Importer {
|
||||
builder: SpecBuilder,
|
||||
/// The interfaces we have traversed so far.
|
||||
path: Vec<String>,
|
||||
}
|
||||
|
||||
impl Importer {
|
||||
/// Import a WebIDL spec into a SpecBuilder.
|
||||
///
|
||||
/// A WebIDL spec may consist in several files. Files are parsed in the order
|
||||
/// of `sources`. An extension file (e.g. `es6-extended.webidl`) MUST appear
|
||||
/// after the files it extends.
|
||||
///
|
||||
/// ```
|
||||
/// extern crate binjs_meta;
|
||||
/// use binjs_meta::spec::SpecOptions;
|
||||
///
|
||||
/// let mut builder = binjs_meta::import::Importer::import(vec!["
|
||||
/// interface FooContents {
|
||||
/// attribute boolean value;
|
||||
/// };
|
||||
/// interface LazyFoo {
|
||||
/// [Lazy] attribute FooContents contents;
|
||||
/// };
|
||||
/// interface EagerFoo {
|
||||
/// attribute FooContents contents;
|
||||
/// };
|
||||
/// "].into_iter()).expect("Could not parse");
|
||||
///
|
||||
/// let fake_root = builder.node_name("@@ROOT@@"); // Unused
|
||||
/// let null = builder.node_name(""); // Used
|
||||
/// let spec = builder.into_spec(SpecOptions {
|
||||
/// root: &fake_root,
|
||||
/// null: &null,
|
||||
/// });
|
||||
///
|
||||
/// let name_eager = spec.get_node_name("EagerFoo")
|
||||
/// .expect("Missing name EagerFoo");
|
||||
/// let name_lazy = spec.get_node_name("LazyFoo")
|
||||
/// .expect("Missing name LazyFoo");
|
||||
/// let name_contents = spec.get_field_name("contents")
|
||||
/// .expect("Missing name contents");
|
||||
///
|
||||
/// {
|
||||
/// let interface_eager = spec.get_interface_by_name(&name_eager)
|
||||
/// .expect("Missing interface EagerFoo");
|
||||
/// let contents_field =
|
||||
/// interface_eager.get_field_by_name(&name_contents)
|
||||
/// .expect("Missing field contents");
|
||||
/// assert_eq!(contents_field.is_lazy(), false);
|
||||
/// }
|
||||
///
|
||||
/// {
|
||||
/// let interface_lazy = spec.get_interface_by_name(&name_lazy)
|
||||
/// .expect("Missing interface LazyFoo");
|
||||
/// let contents_field =
|
||||
/// interface_lazy.get_field_by_name(&name_contents)
|
||||
/// .expect("Missing field contents");
|
||||
/// assert_eq!(contents_field.is_lazy(), true);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn import<'a>(
|
||||
sources: impl IntoIterator<Item = &'a str>,
|
||||
) -> Result<SpecBuilder, weedle::Err<(&'a str, ErrorKind)>> {
|
||||
let mut importer = Importer {
|
||||
path: Vec::with_capacity(256),
|
||||
builder: SpecBuilder::new(),
|
||||
};
|
||||
for source in sources {
|
||||
let ast = weedle::parse(source)?;
|
||||
importer.import_all_definitions(&ast);
|
||||
}
|
||||
Ok(importer.builder)
|
||||
}
|
||||
|
||||
fn import_all_definitions(&mut self, ast: &Definitions) {
|
||||
for definition in ast {
|
||||
self.import_definition(&definition)
|
||||
}
|
||||
}
|
||||
|
||||
fn import_definition(&mut self, def: &Definition) {
|
||||
match *def {
|
||||
Definition::Enum(ref enum_) => self.import_enum(enum_),
|
||||
Definition::Typedef(ref typedef) => self.import_typedef(typedef),
|
||||
Definition::Interface(ref interface) => self.import_interface(interface),
|
||||
_ => panic!("Not implemented: importing {:?}", def),
|
||||
}
|
||||
}
|
||||
|
||||
fn import_enum(&mut self, enum_: &EnumDefinition) {
|
||||
let name = self.builder.node_name(enum_.identifier.0);
|
||||
let mut node = self
|
||||
.builder
|
||||
.add_string_enum(&name)
|
||||
.expect("Name already present");
|
||||
for variant in &enum_.values.body.list {
|
||||
node.with_string(&variant.0);
|
||||
}
|
||||
}
|
||||
|
||||
fn import_typedef(&mut self, typedef: &TypedefDefinition) {
|
||||
let name = self.builder.node_name(typedef.identifier.0);
|
||||
// The following are, unfortunately, not true typedefs.
|
||||
// Ignore their definition.
|
||||
let type_ = match typedef.identifier.0 {
|
||||
"Identifier" => TypeSpec::IdentifierName.required(),
|
||||
"IdentifierName" => TypeSpec::IdentifierName.required(),
|
||||
"PropertyKey" => TypeSpec::PropertyKey.required(),
|
||||
_ => self.convert_type(&typedef.type_.type_),
|
||||
};
|
||||
debug!(target: "meta::import", "Importing typedef {type_:?} {name:?}",
|
||||
type_ = type_,
|
||||
name = name);
|
||||
let mut node = self.builder.add_typedef(&name).unwrap_or_else(|| {
|
||||
panic!(
|
||||
"Error: Name {} is defined more than once in the spec.",
|
||||
name
|
||||
)
|
||||
});
|
||||
assert!(!type_.is_optional());
|
||||
node.with_spec(type_.spec);
|
||||
}
|
||||
|
||||
fn import_interface(&mut self, interface: &InterfaceDefinition) {
|
||||
// Handle special, hardcoded, interfaces.
|
||||
match interface.identifier.0 {
|
||||
"Node" => {
|
||||
// We're not interested in the root interface.
|
||||
return;
|
||||
}
|
||||
"IdentifierName" => unimplemented!(),
|
||||
_ => {}
|
||||
}
|
||||
if let Some(ref parent) = interface.inheritance {
|
||||
assert_eq!(parent.identifier.0, "Node");
|
||||
}
|
||||
|
||||
self.path.push(interface.identifier.0.to_owned());
|
||||
|
||||
// Now handle regular stuff.
|
||||
let mut fields = Vec::new();
|
||||
for member in &interface.members.body {
|
||||
if let interface::InterfaceMember::Attribute(interface::AttributeInterfaceMember {
|
||||
modifier: None,
|
||||
attributes,
|
||||
identifier,
|
||||
type_,
|
||||
..
|
||||
}) = member
|
||||
{
|
||||
let name = self.builder.field_name(identifier.0);
|
||||
let type_ = self.convert_type(&type_.type_);
|
||||
|
||||
let is_lazy = attributes
|
||||
.iter()
|
||||
.flat_map(|attribute| &attribute.body.list)
|
||||
.find(|attribute| match attribute {
|
||||
attribute::ExtendedAttribute::NoArgs(
|
||||
attribute::ExtendedAttributeNoArgs(Identifier("Lazy")),
|
||||
) => true,
|
||||
_ => false,
|
||||
})
|
||||
.is_some();
|
||||
|
||||
fields.push((
|
||||
name,
|
||||
type_,
|
||||
if is_lazy {
|
||||
Laziness::Lazy
|
||||
} else {
|
||||
Laziness::Eager
|
||||
},
|
||||
));
|
||||
} else {
|
||||
panic!("Expected an attribute, got {:?}", member);
|
||||
}
|
||||
}
|
||||
let name = self.builder.node_name(interface.identifier.0);
|
||||
|
||||
// Set to `Some("Foo")` if this interface has attribute
|
||||
// `[ExtendsTypeSum=Foo]`.
|
||||
let mut extends_type_sum = None;
|
||||
let mut scoped_dictionary = None;
|
||||
{
|
||||
let mut node = self
|
||||
.builder
|
||||
.add_interface(&name)
|
||||
.expect("Name already present");
|
||||
for (field_name, field_type, laziness) in fields.drain(..) {
|
||||
node.with_field_laziness(&field_name, field_type, laziness);
|
||||
}
|
||||
|
||||
for attribute in interface
|
||||
.attributes
|
||||
.iter()
|
||||
.flat_map(|attribute| &attribute.body.list)
|
||||
{
|
||||
use weedle::attribute::ExtendedAttribute::*;
|
||||
use weedle::attribute::*;
|
||||
match *attribute {
|
||||
NoArgs(ExtendedAttributeNoArgs(Identifier("Skippable"))) => {
|
||||
panic!("Encountered deprecated attribute [Skippable]");
|
||||
}
|
||||
NoArgs(ExtendedAttributeNoArgs(Identifier("Scope"))) => {
|
||||
node.with_scope(true);
|
||||
}
|
||||
Ident(ExtendedAttributeIdent {
|
||||
lhs_identifier: Identifier("ExtendsTypeSum"),
|
||||
assign: _,
|
||||
rhs: IdentifierOrString::Identifier(ref rhs),
|
||||
}) => {
|
||||
assert!(extends_type_sum.is_none());
|
||||
extends_type_sum = Some(rhs.0);
|
||||
}
|
||||
Ident(ExtendedAttributeIdent {
|
||||
lhs_identifier: Identifier("ScopedDictionary"),
|
||||
assign: _,
|
||||
rhs: IdentifierOrString::Identifier(ref rhs),
|
||||
}) => {
|
||||
assert!(scoped_dictionary.is_none());
|
||||
scoped_dictionary = Some(rhs.0);
|
||||
}
|
||||
_ => panic!("Unknown attribute {:?}", attribute),
|
||||
}
|
||||
}
|
||||
|
||||
// If the node contains an attribute `[ScopedDictionary=field]`,
|
||||
// mark the node as inserting a scoped dictionary with this field.
|
||||
if let Some(ref field_name) = scoped_dictionary {
|
||||
node.with_scoped_dictionary_str(field_name);
|
||||
}
|
||||
}
|
||||
|
||||
// If the node contains an attribute `[ExtendsTypeSum=Foobar]`,
|
||||
// extend `typedef (... or ... or ...) Foobar` into
|
||||
// `typedef (... or ... or ... or CurrentNode) Foobar`.
|
||||
if let Some(ref extended) = extends_type_sum {
|
||||
let node_name = self
|
||||
.builder
|
||||
.get_node_name(extended)
|
||||
.unwrap_or_else(|| panic!("Could not find node name {}", extended));
|
||||
let mut typedef = self
|
||||
.builder
|
||||
.get_typedef_mut(&node_name)
|
||||
.unwrap_or_else(|| panic!("Could not find typedef {}", extended));
|
||||
let typespec = typedef.spec_mut();
|
||||
let typesum = if let TypeSpec::TypeSum(ref mut typesum) = *typespec {
|
||||
typesum
|
||||
} else {
|
||||
panic!(
|
||||
"Attempting to extend a node that is not a type sum {}",
|
||||
extended
|
||||
);
|
||||
};
|
||||
typesum.with_type_case(TypeSpec::NamedType(name));
|
||||
}
|
||||
|
||||
self.path.pop();
|
||||
}
|
||||
|
||||
fn convert_single_type(&mut self, t: &NonAnyType) -> spec::Type {
|
||||
match t {
|
||||
NonAnyType::Boolean(ref b) => nullable(b, TypeSpec::Boolean),
|
||||
NonAnyType::Identifier(ref id) => nullable(id, {
|
||||
let name = self.builder.node_name(id.type_.0);
|
||||
// Sadly, some identifiers are not truly `typedef`s.
|
||||
match name.to_str() {
|
||||
"IdentifierName" if self.is_at_interface("StaticMemberAssignmentTarget") => {
|
||||
TypeSpec::PropertyKey
|
||||
}
|
||||
"IdentifierName" if self.is_at_interface("StaticMemberExpression") => {
|
||||
TypeSpec::PropertyKey
|
||||
}
|
||||
"IdentifierName" if self.is_at_interface("ImportSpecifier") => {
|
||||
TypeSpec::PropertyKey
|
||||
}
|
||||
"IdentifierName" if self.is_at_interface("ExportSpecifier") => {
|
||||
TypeSpec::PropertyKey
|
||||
}
|
||||
"IdentifierName" if self.is_at_interface("ExportLocalSpecifier") => {
|
||||
TypeSpec::PropertyKey
|
||||
}
|
||||
"IdentifierName" => TypeSpec::IdentifierName,
|
||||
"Identifier" => TypeSpec::IdentifierName,
|
||||
_ => TypeSpec::NamedType(name.clone()),
|
||||
}
|
||||
}),
|
||||
NonAnyType::DOMString(ref s) => nullable(
|
||||
s,
|
||||
if self.is_at_interface("LiteralPropertyName") {
|
||||
TypeSpec::PropertyKey
|
||||
} else {
|
||||
TypeSpec::String
|
||||
},
|
||||
),
|
||||
NonAnyType::FrozenArrayType(ref t) => nullable(
|
||||
t,
|
||||
TypeSpec::Array {
|
||||
contents: Box::new(self.convert_type(&t.type_.generics.body)),
|
||||
supports_empty: true,
|
||||
},
|
||||
),
|
||||
NonAnyType::FloatingPoint(ref t) => nullable(t, TypeSpec::Number),
|
||||
NonAnyType::Integer(ref t) => nullable(t, TypeSpec::UnsignedLong),
|
||||
_ => {
|
||||
panic!("I don't know how to import {:?} yet", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_union_type(&mut self, types: &MayBeNull<UnionType>) -> spec::Type {
|
||||
let converted_types: Vec<_> = types
|
||||
.type_
|
||||
.body
|
||||
.list
|
||||
.iter()
|
||||
.map(|t| match t {
|
||||
UnionMemberType::Single(AttributedNonAnyType { type_: t, .. }) => {
|
||||
self.convert_single_type(t)
|
||||
}
|
||||
UnionMemberType::Union(t) => self.convert_union_type(t),
|
||||
})
|
||||
.map(|t| t.spec)
|
||||
.collect();
|
||||
|
||||
nullable(types, TypeSpec::TypeSum(TypeSum::new(converted_types)))
|
||||
}
|
||||
|
||||
fn convert_type(&mut self, t: &Type) -> spec::Type {
|
||||
match t {
|
||||
Type::Single(SingleType::NonAny(t)) => self.convert_single_type(t),
|
||||
Type::Union(types) => self.convert_union_type(types),
|
||||
_ => panic!("I don't know how to import {:?} yet", t),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_at_interface(&self, name: &str) -> bool {
|
||||
if self.path.len() == 0 {
|
||||
return false;
|
||||
}
|
||||
self.path[0].as_str() == name
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
//! This crate offers tools designed to aid with the implementation
|
||||
//! of the JavaScript syntax and BinAST encoders/decoders/manipulators.
|
||||
|
||||
extern crate inflector;
|
||||
extern crate itertools;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate weedle;
|
||||
|
||||
/// Generic tools for generating implementations of the Syntax.
|
||||
pub mod export;
|
||||
|
||||
/// Import a specification of the Syntax.
|
||||
pub mod import;
|
||||
|
||||
/// Manipulating the specifications of the language.
|
||||
pub mod spec;
|
||||
|
||||
/// Misc. utilities.
|
||||
pub mod util;
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,535 +0,0 @@
|
|||
//! Miscellaneous utilities.
|
||||
|
||||
pub trait ToStr {
|
||||
/// Return the value as a `str`.
|
||||
fn to_str(&self) -> &str;
|
||||
fn newline(&self) -> String {
|
||||
format!("{}\n", self.to_str())
|
||||
}
|
||||
|
||||
/// Append newline if the string is not empty.
|
||||
fn newline_if_not_empty(&self) -> String {
|
||||
let s = self.to_str();
|
||||
if s.len() == 0 {
|
||||
"".to_string()
|
||||
} else {
|
||||
format!("{}\n", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToStr for &'a str {
|
||||
fn to_str(&self) -> &str {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl ToStr for str {
|
||||
fn to_str(&self) -> &str {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ToStr for String {
|
||||
fn to_str(&self) -> &str {
|
||||
&self
|
||||
}
|
||||
}
|
||||
|
||||
/// A string or string-like construction that can be
|
||||
/// converted to upper case, lower case, class case, etc
|
||||
pub trait ToCases: ToStr {
|
||||
/// Return the value in class case, e.g.
|
||||
///
|
||||
/// ```
|
||||
/// use binjs_meta::util::ToCases;
|
||||
///
|
||||
/// assert_eq!(&"foo_bar".to_class_cases(), "FooBar");
|
||||
/// assert_eq!(&"fooBars".to_class_cases(), "FooBars");
|
||||
/// ```
|
||||
fn to_class_cases(&self) -> String {
|
||||
self.to_str().to_class_cases()
|
||||
}
|
||||
|
||||
/// Return the value in a format suitable for use as a cpp `enum`
|
||||
/// variants.
|
||||
///
|
||||
/// ```
|
||||
/// use binjs_meta::util::ToCases;
|
||||
///
|
||||
/// assert_eq!(&"foo_bar".to_cpp_enum_case(), "FooBar");
|
||||
/// assert_eq!(&"fooBars".to_cpp_enum_case(), "FooBars");
|
||||
/// assert_eq!(&"+=".to_cpp_enum_case(), "PlusAssign");
|
||||
/// ```
|
||||
fn to_cpp_enum_case(&self) -> String {
|
||||
self.to_str().to_cpp_enum_case()
|
||||
}
|
||||
|
||||
/// Return the value in a format suitable for use as a C++ field name
|
||||
/// or identifier.
|
||||
///
|
||||
/// ```
|
||||
/// use binjs_meta::util::ToCases;
|
||||
///
|
||||
/// assert_eq!(&"foo_bar".to_cpp_field_case(), "fooBar");
|
||||
/// assert_eq!(&"fooBars".to_cpp_field_case(), "fooBars");
|
||||
/// assert_eq!(&"class".to_cpp_field_case(), "class_");
|
||||
/// ```
|
||||
fn to_cpp_field_case(&self) -> String {
|
||||
self.to_str().to_cpp_field_case()
|
||||
}
|
||||
|
||||
/// Return the value in a format suitable for use as a Rust field name
|
||||
/// or identifier.
|
||||
///
|
||||
/// ```
|
||||
/// use binjs_meta::util::ToCases;
|
||||
///
|
||||
/// assert_eq!(&"foo_bar".to_rust_identifier_case(), "foo_bar");
|
||||
/// assert_eq!(&"fooBars".to_rust_identifier_case(), "foo_bars");
|
||||
/// assert_eq!(&"self".to_rust_identifier_case(), "self_");
|
||||
/// ```
|
||||
fn to_rust_identifier_case(&self) -> String {
|
||||
self.to_str().to_rust_identifier_case()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ToCases for T
|
||||
where
|
||||
T: ToStr,
|
||||
{
|
||||
fn to_class_cases(&self) -> String {
|
||||
match self.to_str() {
|
||||
"" => "Null".to_string(),
|
||||
other => {
|
||||
let result = inflector::cases::pascalcase::to_pascal_case(other);
|
||||
assert!(
|
||||
result.to_str().len() != 0,
|
||||
"Could not convert '{}' to class case",
|
||||
other
|
||||
);
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
fn to_cpp_enum_case(&self) -> String {
|
||||
match self.to_str() {
|
||||
"+=" => "PlusAssign".to_string(),
|
||||
"-=" => "MinusAssign".to_string(),
|
||||
"*=" => "MulAssign".to_string(),
|
||||
"/=" => "DivAssign".to_string(),
|
||||
"%=" => "ModAssign".to_string(),
|
||||
"**=" => "PowAssign".to_string(),
|
||||
"<<=" => "LshAssign".to_string(),
|
||||
">>=" => "RshAssign".to_string(),
|
||||
">>>=" => "UrshAssign".to_string(),
|
||||
"|=" => "BitOrAssign".to_string(),
|
||||
"^=" => "BitXorAssign".to_string(),
|
||||
"&=" => "BitAndAssign".to_string(),
|
||||
"," => "Comma".to_string(),
|
||||
"||" => "LogicalOr".to_string(),
|
||||
"&&" => "LogicalAnd".to_string(),
|
||||
"|" => "BitOr".to_string(),
|
||||
"^" => "BitXor".to_string(),
|
||||
"&" => "BitAnd".to_string(),
|
||||
"==" => "Eq".to_string(),
|
||||
"!=" => "Neq".to_string(),
|
||||
"===" => "StrictEq".to_string(),
|
||||
"!==" => "StrictNeq".to_string(),
|
||||
"<" => "LessThan".to_string(),
|
||||
"<=" => "LeqThan".to_string(),
|
||||
">" => "GreaterThan".to_string(),
|
||||
">=" => "GeqThan".to_string(),
|
||||
"<<" => "Lsh".to_string(),
|
||||
">>" => "Rsh".to_string(),
|
||||
">>>" => "Ursh".to_string(),
|
||||
"+" => "Plus".to_string(),
|
||||
"-" => "Minus".to_string(),
|
||||
"~" => "BitNot".to_string(),
|
||||
"*" => "Mul".to_string(),
|
||||
"/" => "Div".to_string(),
|
||||
"%" => "Mod".to_string(),
|
||||
"**" => "Pow".to_string(),
|
||||
"!" => "Not".to_string(),
|
||||
"++" => "Incr".to_string(),
|
||||
"--" => "Decr".to_string(),
|
||||
"" => "_Null".to_string(),
|
||||
_ => {
|
||||
let class_cased = self.to_class_cases();
|
||||
assert!(
|
||||
&class_cased != "",
|
||||
"FIXME: `to_class_cases` does not handle {} yet",
|
||||
self.to_str()
|
||||
);
|
||||
class_cased
|
||||
}
|
||||
}
|
||||
}
|
||||
fn to_cpp_field_case(&self) -> String {
|
||||
let snake = inflector::cases::camelcase::to_camel_case(self.to_str());
|
||||
match &snake as &str {
|
||||
"class" => "class_".to_string(),
|
||||
"operator" => "operator_".to_string(),
|
||||
"const" => "const_".to_string(),
|
||||
"void" => "void_".to_string(),
|
||||
"delete" => "delete_".to_string(),
|
||||
"in" => "in_".to_string(),
|
||||
// Names reserved by us
|
||||
"result" => "result_".to_string(),
|
||||
"kind" => "kind_".to_string(),
|
||||
// Special cases
|
||||
"" => unimplemented!(
|
||||
"FIXME: `to_cpp_field_case` does not handle {} yet",
|
||||
self.to_str()
|
||||
),
|
||||
_ => snake,
|
||||
}
|
||||
}
|
||||
fn to_rust_identifier_case(&self) -> String {
|
||||
let snake = inflector::cases::snakecase::to_snake_case(self.to_str());
|
||||
match &snake as &str {
|
||||
"self" => "self_".to_string(),
|
||||
"super" => "super_".to_string(),
|
||||
"type" => "type_".to_string(),
|
||||
"" if self.to_str() == "" => "null".to_string(),
|
||||
"" => unimplemented!(
|
||||
"FIXME: `to_rust_identifier_case` does not handle {} yet",
|
||||
self.to_str()
|
||||
),
|
||||
_ => snake,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A string or string-like construction that can be reindented.
|
||||
pub trait Reindentable {
|
||||
/// Remove leading whitespace, replace it with `prefix`.
|
||||
///
|
||||
/// If `self` spans more than one line, the leading whitespace
|
||||
/// is computed from the first line and extracted from all lines
|
||||
/// and `prefix` is added to all lines.
|
||||
///
|
||||
/// ```
|
||||
/// use binjs_meta::util::Reindentable;
|
||||
///
|
||||
/// assert_eq!(&"abc".reindent(" "), " abc");
|
||||
/// assert_eq!(&" def".reindent(" "), " def");
|
||||
/// assert_eq!(&" ghi".reindent(" "), " ghi");
|
||||
/// assert_eq!(&" jkl\n mno".reindent(" "), " jkl\n mno");
|
||||
/// ```
|
||||
fn reindent(&self, prefix: &str) -> String;
|
||||
|
||||
/// Remove leading whitespace, replace it with `prefix`,
|
||||
/// ensure that the text fits within `width` columns.
|
||||
///
|
||||
/// If `self` spans more than one line, the leading whitespace
|
||||
/// is computed from the first line and extracted from all lines.
|
||||
/// and `prefix` is added to all lines.
|
||||
///
|
||||
/// If the result goes past `width` columns, `self` is split
|
||||
/// into several lines to try and fit within `width` columns.
|
||||
///
|
||||
/// ```
|
||||
/// use binjs_meta::util::Reindentable;
|
||||
///
|
||||
/// assert_eq!(&"abc".fit("// ", 30), "// abc");
|
||||
/// assert_eq!(&" def".fit("// ", 30), "// def");
|
||||
/// assert_eq!(&" ghi".fit("// ", 30), "// ghi");
|
||||
/// assert_eq!(&" jkl\n mno".fit("// ", 30), "// jkl\n// mno");
|
||||
/// assert_eq!(&"abc def ghi".fit("// ", 8), "// abc\n// def\n// ghi");
|
||||
/// assert_eq!(&"abc def ghi".fit("// ", 5), "// abc\n// def\n// ghi");
|
||||
/// ```
|
||||
fn fit(&self, prefix: &str, width: usize) -> String;
|
||||
}
|
||||
|
||||
impl<T> Reindentable for T
|
||||
where
|
||||
T: ToStr,
|
||||
{
|
||||
fn reindent(&self, prefix: &str) -> String {
|
||||
use itertools::Itertools;
|
||||
|
||||
let str = self.to_str();
|
||||
|
||||
// Determine the number of whitespace chars on the first line.
|
||||
// Trim that many whitespace chars on the following lines.
|
||||
if let Some(first_line) = str.lines().next() {
|
||||
let indent_len = first_line
|
||||
.chars()
|
||||
.take_while(|c| char::is_whitespace(*c))
|
||||
.count();
|
||||
format!(
|
||||
"{}",
|
||||
str.lines()
|
||||
.map(|line| if line.len() > indent_len {
|
||||
format!(
|
||||
"{prefix}{text}",
|
||||
prefix = prefix,
|
||||
text = line[indent_len..].to_string()
|
||||
)
|
||||
} else {
|
||||
"".to_string()
|
||||
})
|
||||
.format("\n")
|
||||
)
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn fit(&self, prefix: &str, columns: usize) -> String {
|
||||
use itertools::Itertools;
|
||||
|
||||
let str = self.to_str();
|
||||
// Determine the number of whitespace chars on the first line.
|
||||
// Trim that many whitespace chars on the following lines.
|
||||
if let Some(first_line) = str.lines().next() {
|
||||
let indent_len = first_line
|
||||
.chars()
|
||||
.take_while(|c| char::is_whitespace(*c))
|
||||
.count();
|
||||
let mut lines = vec![];
|
||||
'per_line: for line in str.lines() {
|
||||
eprintln!("Inspecting line {}", line);
|
||||
let text = &line[indent_len..];
|
||||
let mut gobbled = 0;
|
||||
while text.len() > gobbled {
|
||||
let rest = &text[gobbled..];
|
||||
eprintln!("Line still contains {} ({})", rest, gobbled);
|
||||
if rest.len() + prefix.len() > columns {
|
||||
// Try and find the largest prefix of `text` that fits within `columns`.
|
||||
let mut iterator = rest
|
||||
.chars()
|
||||
.enumerate()
|
||||
.filter(|&(_, c)| char::is_whitespace(c));
|
||||
let mut last_whitespace_before_break = None;
|
||||
let mut first_whitespace_after_break = None;
|
||||
while let Some((found_pos, _)) = iterator.next() {
|
||||
if found_pos + prefix.len() <= columns {
|
||||
last_whitespace_before_break = Some(found_pos);
|
||||
} else {
|
||||
first_whitespace_after_break = Some(found_pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
match (last_whitespace_before_break, first_whitespace_after_break) {
|
||||
(None, None) => {
|
||||
eprintln!("Ok, string didn't contain any whitespace: '{}'", rest);
|
||||
// Oh, `rest` does not contain any whitespace. Well, use everything.
|
||||
lines.push(format!("{prefix}{rest}", prefix = prefix, rest = rest));
|
||||
continue 'per_line;
|
||||
}
|
||||
(Some(pos), _) | (None, Some(pos)) if pos != 0 => {
|
||||
eprintln!("Best whitespace found at {}", pos);
|
||||
// Use `rest[0..pos]`, trimmed right.
|
||||
gobbled += pos + 1;
|
||||
let line = format!(
|
||||
"{prefix}{rest}",
|
||||
prefix = prefix,
|
||||
rest = rest[0..pos].trim_end()
|
||||
);
|
||||
lines.push(line)
|
||||
}
|
||||
_else => panic!("{:?}", _else),
|
||||
}
|
||||
} else {
|
||||
let line = format!("{prefix}{rest}", prefix = prefix, rest = rest);
|
||||
lines.push(line);
|
||||
continue 'per_line;
|
||||
}
|
||||
}
|
||||
}
|
||||
format!("{lines}", lines = lines.iter().format("\n"))
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Reindentable for Option<String> {
|
||||
fn reindent(&self, prefix: &str) -> String {
|
||||
match *self {
|
||||
None => "".to_string(),
|
||||
Some(ref string) => string.reindent(prefix),
|
||||
}
|
||||
}
|
||||
fn fit(&self, prefix: &str, columns: usize) -> String {
|
||||
match *self {
|
||||
None => "".to_string(),
|
||||
Some(ref string) => string.fit(prefix, columns),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod name_sorter {
|
||||
use std;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// A type used to sort names by length, then prefixes, to speed
|
||||
/// up lookups.
|
||||
pub struct NameSorter<T> {
|
||||
per_length: HashMap<usize, Node<T>>,
|
||||
len: usize,
|
||||
}
|
||||
impl<T> NameSorter<T> {
|
||||
pub fn new() -> Self {
|
||||
NameSorter {
|
||||
per_length: HashMap::new(),
|
||||
len: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the number of items in the sorter.
|
||||
pub fn len(&self) -> usize {
|
||||
debug_assert!({
|
||||
// Let's check that the length is always the sum of sublengths.
|
||||
let len = self
|
||||
.per_length
|
||||
.values()
|
||||
.map(|v| match v {
|
||||
&Node::Leaf(Some(_)) => 1,
|
||||
&Node::Leaf(_) => panic!("Invariant error: empty leaf!"),
|
||||
&Node::Internal { ref len, .. } => *len,
|
||||
})
|
||||
.fold(0, |x, y| (x + y));
|
||||
len == self.len
|
||||
});
|
||||
self.len
|
||||
}
|
||||
|
||||
/// Insert a value in a sorter.
|
||||
///
|
||||
/// ```
|
||||
/// let mut sorter = binjs_meta::util::name_sorter::NameSorter::new();
|
||||
/// assert_eq!(sorter.len(), 0);
|
||||
///
|
||||
/// assert!(sorter.insert("abcd", 0).is_none());
|
||||
/// assert_eq!(sorter.len(), 1);
|
||||
/// assert_eq!(*sorter.get("abcd").unwrap(), 0);
|
||||
/// assert!(sorter.get("dbca").is_none());
|
||||
/// assert!(sorter.get("").is_none());
|
||||
///
|
||||
/// assert!(sorter.insert("dcba", 1).is_none());
|
||||
/// assert_eq!(sorter.len(), 2);
|
||||
/// assert_eq!(*sorter.get("abcd").unwrap(), 0);
|
||||
/// assert_eq!(*sorter.get("dcba").unwrap(), 1);
|
||||
/// assert!(sorter.get("").is_none());
|
||||
///
|
||||
/// assert_eq!(sorter.insert("abcd", 3).unwrap(), 0);
|
||||
/// assert_eq!(sorter.len(), 2);
|
||||
/// assert_eq!(*sorter.get("abcd").unwrap(), 3);
|
||||
/// assert_eq!(*sorter.get("dcba").unwrap(), 1);
|
||||
/// assert!(sorter.get("").is_none());
|
||||
///
|
||||
/// assert!(sorter.insert("", 4).is_none());
|
||||
/// assert_eq!(sorter.len(), 3);
|
||||
/// assert_eq!(*sorter.get("abcd").unwrap(), 3);
|
||||
/// assert_eq!(*sorter.get("dcba").unwrap(), 1);
|
||||
/// assert_eq!(*sorter.get("").unwrap(), 4);
|
||||
///
|
||||
/// assert_eq!(sorter.insert("", 5).unwrap(), 4);
|
||||
/// assert_eq!(sorter.len(), 3);
|
||||
/// assert_eq!(*sorter.get("abcd").unwrap(), 3);
|
||||
/// assert_eq!(*sorter.get("dcba").unwrap(), 1);
|
||||
/// assert_eq!(*sorter.get("").unwrap(), 5);
|
||||
/// ```
|
||||
pub fn insert(&mut self, key: &str, value: T) -> Option<T> {
|
||||
if let Some(node) = self.per_length.get_mut(&key.len()) {
|
||||
let result = node.insert(key, value);
|
||||
if result.is_none() {
|
||||
self.len += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
let node = Node::new(key, value);
|
||||
self.per_length.insert(key.len(), node);
|
||||
self.len += 1;
|
||||
None
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (usize, &Node<T>)> {
|
||||
self.per_length.iter().map(|(&len, node)| (len, node))
|
||||
}
|
||||
|
||||
pub fn get(&self, key: &str) -> Option<&T> {
|
||||
self.per_length
|
||||
.get(&key.len())
|
||||
.and_then(|node| node.get(key))
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Node<T> {
|
||||
Leaf(Option<T>),
|
||||
Internal {
|
||||
/// The children of this node.
|
||||
///
|
||||
/// Invariant: May only be empty during a call to `insert()`.
|
||||
children: HashMap<char, Node<T>>,
|
||||
|
||||
/// Number of leaves in this subtree.
|
||||
len: usize,
|
||||
},
|
||||
}
|
||||
impl<T> Node<T> {
|
||||
fn get(&self, key: &str) -> Option<&T> {
|
||||
match (self, key.chars().next()) {
|
||||
(&Node::Leaf(Some(ref result)), None) => Some(result),
|
||||
(&Node::Internal { ref children, .. }, Some(c)) => {
|
||||
debug_assert!(children.len() != 0);
|
||||
children.get(&c).and_then(|node| node.get(&key[1..]))
|
||||
}
|
||||
_ => panic!("Invariant error: length"),
|
||||
}
|
||||
}
|
||||
|
||||
fn insert(&mut self, key: &str, value: T) -> Option<T> {
|
||||
match (self, key.chars().next()) {
|
||||
(&mut Node::Leaf(ref mut old), None) => {
|
||||
// We have reached the end of `name`.
|
||||
let mut data = Some(value);
|
||||
std::mem::swap(&mut data, old);
|
||||
data
|
||||
}
|
||||
(
|
||||
&mut Node::Internal {
|
||||
ref mut children,
|
||||
ref mut len,
|
||||
},
|
||||
Some(c),
|
||||
) => {
|
||||
let result = {
|
||||
let entry = if key.len() == 1 {
|
||||
children.entry(c).or_insert_with(|| Node::Leaf(None))
|
||||
} else {
|
||||
children.entry(c).or_insert_with(|| Node::Internal {
|
||||
children: HashMap::new(),
|
||||
len: 0,
|
||||
})
|
||||
};
|
||||
entry.insert(&key[1..], value)
|
||||
};
|
||||
if result.is_none() {
|
||||
*len += 1;
|
||||
}
|
||||
debug_assert!(*len > 0);
|
||||
debug_assert!(children.len() != 0);
|
||||
result
|
||||
}
|
||||
_ => panic!("Invariant error: length"),
|
||||
}
|
||||
}
|
||||
fn new(key: &str, value: T) -> Self {
|
||||
if key.len() == 0 {
|
||||
Node::Leaf(Some(value))
|
||||
} else {
|
||||
let mut node = Node::Internal {
|
||||
children: HashMap::new(),
|
||||
len: 0,
|
||||
};
|
||||
assert!(node.insert(key, value).is_none());
|
||||
node
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
{"files":{"Cargo.toml":"84dc605d368f68a3e25b2e240552ce6e2a8731e80f73cfd4cb0b931528b473a7","LICENSE.md":"47cdc00faacda4d0ac0dabfcef03ab6f4bef6a02f80cb3fd0482047f00f9a16d","README.md":"e4363ea12b63bb6d3e461396f6fd46fe4cbf9a8635258499f4d551dadfc06655","src/argument.rs":"be162d59a9f1ef7d9d51d0f3492946f0dd263202af8355eeaee14ce4181775fc","src/attribute.rs":"b3577059be9c1262d3360b4b4cf4cc6cb052251e5b9d64906fb40fc5e925da69","src/common.rs":"7ac264f83ff596914af32fb08947cc500a75df57e43d9b2fce93163e5a20a738","src/dictionary.rs":"be209d70b0db33acf753ca63998a63dd37a9d4b193583a1490782781360c2d01","src/interface.rs":"4e1f37079dec75633b26289b82bda0d3b4a1479a4aede74c01e20fe6c2d93496","src/lib.rs":"762c35dc6ef77b6b1c871bd0391d09203a479937e4688a029a17c8d8a5f9e8ba","src/literal.rs":"dd58d410e2329633e2b2151157b29671ad5f78c8be0bec5c81be083168311f94","src/macros.rs":"aa95fd957d5175fb2779faec5d40b717fb5efd581f0f13ef7ba3510f37092da3","src/mixin.rs":"d65ff3a49615dabdf3f7845723f01ae730a83c8ef5bdd9a945d35149a9ef2858","src/namespace.rs":"fe6b406c2ab8bd904d0dff8a12321c77f4082ab364559e578e53ab415c8541fa","src/term.rs":"48ac583ae73d115afbedca6e7e9f32b316b7a308ba37cb9153eeac9773561028","src/types.rs":"3f81a1d820f0b6777f46c74366359953bf79465c3eb1ace4b3a383c22f4e1d88","src/whitespace.rs":"069df2ae0f37d621a8ef84b08408c090ff1ead8164e52caa8ea7d57838f04cac","tests/defs/dom.webidl":"93a70734538233a6b9db7578375b5a3d8783bcdcfc9970cc61cddcd03c832d6c","tests/defs/html.webidl":"4b2be1a0435b1fc26e60083006e2ecf7158611c190f95e5c4c8290510de22777","tests/defs/interface-constructor.webidl":"8b31240c838607e460086cb55a57a111605199f88f4291a83f7125c8c3c4fd1a","tests/defs/mediacapture-streams.webidl":"82615b15129e87aac56a0ca74bd763c5416732020a2b567dc4582835e0846144","tests/webidl.rs":"097683a01f3c058be3341d7d8b0af18964686059485941cbee22e63b94406c1f"},"package":"8a7d4f9feb723a800d8f7b74edc9fa44ff35cb0b2ec64886714362f423427f37"}
|
|
@ -1,27 +0,0 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "weedle"
|
||||
version = "0.11.0"
|
||||
authors = ["Sharad Chand <sharad.d.chand@gmail.com>"]
|
||||
description = "A WebIDL Parser"
|
||||
homepage = "https://github.com/rustwasm/weedle"
|
||||
documentation = "https://docs.rs/weedle"
|
||||
readme = "./README.md"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/rustwasm/weedle"
|
||||
[dependencies.nom]
|
||||
version = "5.0.0"
|
||||
features = ["std"]
|
||||
default-features = false
|
|
@ -1,15 +0,0 @@
|
|||
Copyright 2018-Present Sharad Chand
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
|
@ -1,49 +0,0 @@
|
|||
<div align="center">
|
||||
|
||||
<h1>Weedle</h1>
|
||||
|
||||
<strong>A Web IDL parser</strong>
|
||||
|
||||
<p>
|
||||
<a href="https://travis-ci.org/rustwasm/weedle"><img src="https://img.shields.io/travis/rustwasm/weedle.svg?style=flat-square" alt="Build Status" /></a>
|
||||
<a href="https://crates.io/crates/weedle"><img src="https://img.shields.io/crates/v/weedle.svg?style=flat-square" alt="Crates.io version" /></a>
|
||||
<a href="https://crates.io/crates/weedle"><img src="https://img.shields.io/crates/d/weedle.svg?style=flat-square" alt="Download" /></a>
|
||||
<a href="https://docs.rs/weedle"><img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square" alt="docs.rs docs" /></a>
|
||||
</p>
|
||||
|
||||
<h3>
|
||||
<a href="https://docs.rs/weedle">API Docs</a>
|
||||
<span> | </span>
|
||||
<a href="https://discordapp.com/channels/442252698964721669/443151097398296587">Chat</a>
|
||||
</h3>
|
||||
|
||||
<sub>Built with 🦀🕸 by <a href="https://rustwasm.github.io/">The Rust and WebAssembly Working Group</a></sub>
|
||||
</div>
|
||||
|
||||
## About
|
||||
|
||||
Parses valid WebIDL definitions & produces a data structure starting from
|
||||
[`Definitions`](https://docs.rs/weedle/latest/weedle/type.Definitions.html).
|
||||
|
||||
## Usage
|
||||
|
||||
### `Cargo.toml`
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
weedle = "0.9.0"
|
||||
```
|
||||
|
||||
### `src/main.rs`
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let parsed = weedle::parse("
|
||||
interface Window {
|
||||
readonly attribute Storage sessionStorage;
|
||||
};
|
||||
").unwrap();
|
||||
|
||||
println!("{:?}", parsed);
|
||||
}
|
||||
```
|
|
@ -1,76 +0,0 @@
|
|||
use crate::attribute::ExtendedAttributeList;
|
||||
use crate::common::{Default, Identifier, Punctuated};
|
||||
use crate::types::{AttributedType, Type};
|
||||
|
||||
/// Parses a list of argument. Ex: `double v1, double v2, double v3, optional double alpha`
|
||||
pub type ArgumentList<'a> = Punctuated<Argument<'a>, term!(,)>;
|
||||
|
||||
ast_types! {
|
||||
/// Parses an argument. Ex: `double v1|double... v1s`
|
||||
enum Argument<'a> {
|
||||
/// Parses `[attributes]? optional? attributedtype identifier ( = default )?`
|
||||
///
|
||||
/// Note: `= default` is only allowed if `optional` is present
|
||||
Single(struct SingleArgument<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
optional: Option<term!(optional)>,
|
||||
type_: AttributedType<'a>,
|
||||
identifier: Identifier<'a>,
|
||||
default: Option<Default<'a>> = map!(
|
||||
cond!(optional.is_some(), weedle!(Option<Default<'a>>)),
|
||||
|default| default.unwrap_or(None)
|
||||
),
|
||||
}),
|
||||
/// Parses `[attributes]? type... identifier`
|
||||
Variadic(struct VariadicArgument<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
type_: Type<'a>,
|
||||
ellipsis: term!(...),
|
||||
identifier: Identifier<'a>,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::literal::{DecLit, DefaultValue, IntegerLit};
|
||||
use crate::Parse;
|
||||
|
||||
test!(should_parse_single_argument { "short a" =>
|
||||
"";
|
||||
SingleArgument;
|
||||
attributes.is_none();
|
||||
optional.is_none();
|
||||
identifier.0 == "a";
|
||||
default.is_none();
|
||||
});
|
||||
|
||||
test!(should_parse_variadic_argument { "short... a" =>
|
||||
"";
|
||||
VariadicArgument;
|
||||
attributes.is_none();
|
||||
identifier.0 == "a";
|
||||
});
|
||||
|
||||
test!(should_parse_optional_single_argument { "optional short a" =>
|
||||
"";
|
||||
SingleArgument;
|
||||
attributes.is_none();
|
||||
optional.is_some();
|
||||
identifier.0 == "a";
|
||||
default.is_none();
|
||||
});
|
||||
|
||||
test!(should_parse_optional_single_argument_with_default { "optional short a = 5" =>
|
||||
"";
|
||||
SingleArgument;
|
||||
attributes.is_none();
|
||||
optional.is_some();
|
||||
identifier.0 == "a";
|
||||
default == Some(Default {
|
||||
assign: term!(=),
|
||||
value: DefaultValue::Integer(IntegerLit::Dec(DecLit("5"))),
|
||||
});
|
||||
});
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
use crate::argument::ArgumentList;
|
||||
use crate::common::{Bracketed, Identifier, Parenthesized, Punctuated};
|
||||
use crate::literal::StringLit;
|
||||
|
||||
/// Parses a list of attributes. Ex: `[ attribute1, attribute2 ]`
|
||||
pub type ExtendedAttributeList<'a> = Bracketed<Punctuated<ExtendedAttribute<'a>, term!(,)>>;
|
||||
|
||||
/// Matches comma separated identifier list
|
||||
pub type IdentifierList<'a> = Punctuated<Identifier<'a>, term!(,)>;
|
||||
|
||||
ast_types! {
|
||||
/// Parses on of the forms of attribute
|
||||
enum ExtendedAttribute<'a> {
|
||||
/// Parses an argument list. Ex: `Constructor((double x, double y))`
|
||||
///
|
||||
/// (( )) means ( ) chars
|
||||
ArgList(struct ExtendedAttributeArgList<'a> {
|
||||
identifier: Identifier<'a>,
|
||||
args: Parenthesized<ArgumentList<'a>>,
|
||||
}),
|
||||
/// Parses a named argument list. Ex: `NamedConstructor=Image((DOMString src))`
|
||||
///
|
||||
/// (( )) means ( ) chars
|
||||
NamedArgList(struct ExtendedAttributeNamedArgList<'a> {
|
||||
lhs_identifier: Identifier<'a>,
|
||||
assign: term!(=),
|
||||
rhs_identifier: Identifier<'a>,
|
||||
args: Parenthesized<ArgumentList<'a>>,
|
||||
|
||||
}),
|
||||
/// Parses an identifier list. Ex: `Exposed=((Window,Worker))`
|
||||
///
|
||||
/// (( )) means ( ) chars
|
||||
IdentList(struct ExtendedAttributeIdentList<'a> {
|
||||
identifier: Identifier<'a>,
|
||||
assign: term!(=),
|
||||
list: Parenthesized<IdentifierList<'a>>,
|
||||
}),
|
||||
/// Parses an attribute with an identifier. Ex: `PutForwards=name`
|
||||
#[derive(Copy)]
|
||||
Ident(struct ExtendedAttributeIdent<'a> {
|
||||
lhs_identifier: Identifier<'a>,
|
||||
assign: term!(=),
|
||||
rhs: IdentifierOrString<'a>,
|
||||
}),
|
||||
/// Parses a plain attribute. Ex: `Replaceable`
|
||||
#[derive(Copy)]
|
||||
NoArgs(struct ExtendedAttributeNoArgs<'a>(
|
||||
Identifier<'a>,
|
||||
)),
|
||||
}
|
||||
|
||||
/// Parses `stringifier|static`
|
||||
#[derive(Copy)]
|
||||
enum IdentifierOrString<'a> {
|
||||
Identifier(Identifier<'a>),
|
||||
String(StringLit<'a>),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::Parse;
|
||||
|
||||
test!(should_parse_attribute_no_args { "Replaceable" =>
|
||||
"";
|
||||
ExtendedAttributeNoArgs => ExtendedAttributeNoArgs(Identifier("Replaceable"))
|
||||
});
|
||||
|
||||
test!(should_parse_attribute_arg_list { "Constructor(double x, double y)" =>
|
||||
"";
|
||||
ExtendedAttributeArgList;
|
||||
identifier.0 == "Constructor";
|
||||
args.body.list.len() == 2;
|
||||
});
|
||||
|
||||
test!(should_parse_attribute_ident { "PutForwards=name" =>
|
||||
"";
|
||||
ExtendedAttributeIdent;
|
||||
lhs_identifier.0 == "PutForwards";
|
||||
rhs == IdentifierOrString::Identifier(Identifier("name"));
|
||||
});
|
||||
|
||||
test!(should_parse_ident_list { "Exposed=(Window,Worker)" =>
|
||||
"";
|
||||
ExtendedAttributeIdentList;
|
||||
identifier.0 == "Exposed";
|
||||
list.body.list.len() == 2;
|
||||
});
|
||||
|
||||
test!(should_parse_named_arg_list { "NamedConstructor=Image(DOMString src)" =>
|
||||
"";
|
||||
ExtendedAttributeNamedArgList;
|
||||
lhs_identifier.0 == "NamedConstructor";
|
||||
rhs_identifier.0 == "Image";
|
||||
args.body.list.len() == 1;
|
||||
});
|
||||
}
|
|
@ -1,208 +0,0 @@
|
|||
use crate::literal::DefaultValue;
|
||||
use crate::term;
|
||||
use crate::Parse;
|
||||
|
||||
impl<'a, T: Parse<'a>> Parse<'a> for Option<T> {
|
||||
parser!(opt!(weedle!(T)));
|
||||
}
|
||||
|
||||
impl<'a, T: Parse<'a>> Parse<'a> for Box<T> {
|
||||
parser!(do_parse!(inner: weedle!(T) >> (Box::new(inner))));
|
||||
}
|
||||
|
||||
/// Parses `item1 item2 item3...`
|
||||
impl<'a, T: Parse<'a>> Parse<'a> for Vec<T> {
|
||||
parser!(many0!(weedle!(T)));
|
||||
}
|
||||
|
||||
impl<'a, T: Parse<'a>, U: Parse<'a>> Parse<'a> for (T, U) {
|
||||
parser!(do_parse!(t: weedle!(T) >> u: weedle!(U) >> ((t, u))));
|
||||
}
|
||||
|
||||
impl<'a, T: Parse<'a>, U: Parse<'a>, V: Parse<'a>> Parse<'a> for (T, U, V) {
|
||||
parser!(do_parse!(
|
||||
t: weedle!(T) >> u: weedle!(U) >> v: weedle!(V) >> ((t, u, v))
|
||||
));
|
||||
}
|
||||
|
||||
ast_types! {
|
||||
/// Parses `( body )`
|
||||
#[derive(Copy, Default)]
|
||||
struct Parenthesized<T> where [T: Parse<'a>] {
|
||||
open_paren: term::OpenParen,
|
||||
body: T,
|
||||
close_paren: term::CloseParen,
|
||||
}
|
||||
|
||||
/// Parses `[ body ]`
|
||||
#[derive(Copy, Default)]
|
||||
struct Bracketed<T> where [T: Parse<'a>] {
|
||||
open_bracket: term::OpenBracket,
|
||||
body: T,
|
||||
close_bracket: term::CloseBracket,
|
||||
}
|
||||
|
||||
/// Parses `{ body }`
|
||||
#[derive(Copy, Default)]
|
||||
struct Braced<T> where [T: Parse<'a>] {
|
||||
open_brace: term::OpenBrace,
|
||||
body: T,
|
||||
close_brace: term::CloseBrace,
|
||||
}
|
||||
|
||||
/// Parses `< body >`
|
||||
#[derive(Copy, Default)]
|
||||
struct Generics<T> where [T: Parse<'a>] {
|
||||
open_angle: term::LessThan,
|
||||
body: T,
|
||||
close_angle: term::GreaterThan,
|
||||
}
|
||||
|
||||
/// Parses `(item1, item2, item3,...)?`
|
||||
struct Punctuated<T, S> where [T: Parse<'a>, S: Parse<'a> + ::std::default::Default] {
|
||||
list: Vec<T> = separated_list!(weedle!(S), weedle!(T)),
|
||||
separator: S = marker,
|
||||
}
|
||||
|
||||
/// Parses `item1, item2, item3, ...`
|
||||
struct PunctuatedNonEmpty<T, S> where [T: Parse<'a>, S: Parse<'a> + ::std::default::Default] {
|
||||
list: Vec<T> = terminated!(
|
||||
separated_nonempty_list!(weedle!(S), weedle!(T)),
|
||||
opt!(weedle!(S))
|
||||
),
|
||||
separator: S = marker,
|
||||
}
|
||||
|
||||
/// Represents an identifier
|
||||
///
|
||||
/// Follows `/_?[A-Za-z][0-9A-Z_a-z-]*/`
|
||||
#[derive(Copy)]
|
||||
struct Identifier<'a>(
|
||||
// See https://heycam.github.io/webidl/#idl-names for why the leading
|
||||
// underscore is trimmed
|
||||
&'a str = ws!(do_parse!(
|
||||
opt!(char!('_')) >>
|
||||
id: recognize!(do_parse!(
|
||||
take_while1!(|c: char| c.is_ascii_alphabetic()) >>
|
||||
take_while!(|c: char| c.is_ascii_alphanumeric() || c == '_' || c == '-') >>
|
||||
(())
|
||||
)) >>
|
||||
(id)
|
||||
)),
|
||||
)
|
||||
|
||||
/// Parses rhs of an assignment expression. Ex: `= 45`
|
||||
#[derive(Copy)]
|
||||
struct Default<'a> {
|
||||
assign: term!(=),
|
||||
value: DefaultValue<'a>,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
test!(should_parse_optional_present { "one" =>
|
||||
"";
|
||||
Option<Identifier>;
|
||||
is_some();
|
||||
});
|
||||
|
||||
test!(should_parse_optional_not_present { "" =>
|
||||
"";
|
||||
Option<Identifier>;
|
||||
is_none();
|
||||
});
|
||||
|
||||
test!(should_parse_boxed { "one" =>
|
||||
"";
|
||||
Box<Identifier>;
|
||||
});
|
||||
|
||||
test!(should_parse_vec { "one two three" =>
|
||||
"";
|
||||
Vec<Identifier>;
|
||||
len() == 3;
|
||||
});
|
||||
|
||||
test!(should_parse_parenthesized { "( one )" =>
|
||||
"";
|
||||
Parenthesized<Identifier>;
|
||||
body.0 == "one";
|
||||
});
|
||||
|
||||
test!(should_parse_bracketed { "[ one ]" =>
|
||||
"";
|
||||
Bracketed<Identifier>;
|
||||
body.0 == "one";
|
||||
});
|
||||
|
||||
test!(should_parse_braced { "{ one }" =>
|
||||
"";
|
||||
Braced<Identifier>;
|
||||
body.0 == "one";
|
||||
});
|
||||
|
||||
test!(should_parse_generics { "<one>" =>
|
||||
"";
|
||||
Generics<Identifier>;
|
||||
body.0 == "one";
|
||||
});
|
||||
|
||||
test!(should_parse_generics_two { "<one, two>" =>
|
||||
"";
|
||||
Generics<(Identifier, term!(,), Identifier)> =>
|
||||
Generics {
|
||||
open_angle: term!(<),
|
||||
body: (Identifier("one"), term!(,), Identifier("two")),
|
||||
close_angle: term!(>),
|
||||
}
|
||||
});
|
||||
|
||||
test!(should_parse_comma_separated_values { "one, two, three" =>
|
||||
"";
|
||||
Punctuated<Identifier, term!(,)>;
|
||||
list.len() == 3;
|
||||
});
|
||||
|
||||
test!(err should_not_parse_comma_separated_values_empty { "" =>
|
||||
PunctuatedNonEmpty<Identifier, term!(,)>
|
||||
});
|
||||
|
||||
test!(should_parse_identifier { "hello" =>
|
||||
"";
|
||||
Identifier;
|
||||
0 == "hello";
|
||||
});
|
||||
|
||||
test!(should_parse_numbered_identifier { "hello5" =>
|
||||
"";
|
||||
Identifier;
|
||||
0 == "hello5";
|
||||
});
|
||||
|
||||
test!(should_parse_underscored_identifier { "_hello_" =>
|
||||
"";
|
||||
Identifier;
|
||||
0 == "hello_";
|
||||
});
|
||||
|
||||
test!(should_parse_identifier_surrounding_with_spaces { " hello " =>
|
||||
"";
|
||||
Identifier;
|
||||
0 == "hello";
|
||||
});
|
||||
|
||||
test!(should_parse_identifier_preceeding_others { "hello note" =>
|
||||
"note";
|
||||
Identifier;
|
||||
0 == "hello";
|
||||
});
|
||||
|
||||
test!(should_parse_identifier_attached_to_symbol { "hello=" =>
|
||||
"=";
|
||||
Identifier;
|
||||
0 == "hello";
|
||||
});
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
use crate::attribute::ExtendedAttributeList;
|
||||
use crate::common::{Default, Identifier};
|
||||
use crate::types::Type;
|
||||
|
||||
/// Parses dictionary members
|
||||
pub type DictionaryMembers<'a> = Vec<DictionaryMember<'a>>;
|
||||
|
||||
ast_types! {
|
||||
/// Parses dictionary member `[attributes]? required? type identifier ( = default )?;`
|
||||
struct DictionaryMember<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
required: Option<term!(required)>,
|
||||
type_: Type<'a>,
|
||||
identifier: Identifier<'a>,
|
||||
default: Option<Default<'a>>,
|
||||
semi_colon: term!(;),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::Parse;
|
||||
|
||||
test!(should_parse_dictionary_member { "required long num = 5;" =>
|
||||
"";
|
||||
DictionaryMember;
|
||||
attributes.is_none();
|
||||
required.is_some();
|
||||
identifier.0 == "num";
|
||||
default.is_some();
|
||||
});
|
||||
}
|
|
@ -1,202 +0,0 @@
|
|||
use crate::argument::ArgumentList;
|
||||
use crate::attribute::ExtendedAttributeList;
|
||||
use crate::common::{Generics, Identifier, Parenthesized};
|
||||
use crate::literal::ConstValue;
|
||||
use crate::types::{AttributedType, ConstType, ReturnType};
|
||||
|
||||
/// Parses interface members
|
||||
pub type InterfaceMembers<'a> = Vec<InterfaceMember<'a>>;
|
||||
|
||||
ast_types! {
|
||||
/// Parses inheritance clause `: identifier`
|
||||
#[derive(Copy)]
|
||||
struct Inheritance<'a> {
|
||||
colon: term!(:),
|
||||
identifier: Identifier<'a>,
|
||||
}
|
||||
|
||||
/// Parses one of the interface member variants
|
||||
enum InterfaceMember<'a> {
|
||||
/// Parses a const interface member `[attributes]? const type identifier = value;`
|
||||
Const(struct ConstMember<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
const_: term!(const),
|
||||
const_type: ConstType<'a>,
|
||||
identifier: Identifier<'a>,
|
||||
assign: term!(=),
|
||||
const_value: ConstValue<'a>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
/// Parses `[attributes]? (stringifier|inherit|static)? readonly? attribute attributedtype identifier;`
|
||||
Attribute(struct AttributeInterfaceMember<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
modifier: Option<StringifierOrInheritOrStatic>,
|
||||
readonly: Option<term!(readonly)>,
|
||||
attribute: term!(attribute),
|
||||
type_: AttributedType<'a>,
|
||||
identifier: Identifier<'a>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
/// Parses `[attributes]? constructor(( args ));`
|
||||
///
|
||||
/// (( )) means ( ) chars
|
||||
Constructor(struct ConstructorInterfaceMember<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
constructor: term!(constructor),
|
||||
args: Parenthesized<ArgumentList<'a>>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
/// Parses `[attributes]? (stringifier|static)? special? returntype identifier? (( args ));`
|
||||
///
|
||||
/// (( )) means ( ) chars
|
||||
Operation(struct OperationInterfaceMember<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
modifier: Option<StringifierOrStatic>,
|
||||
special: Option<Special>,
|
||||
return_type: ReturnType<'a>,
|
||||
identifier: Option<Identifier<'a>>,
|
||||
args: Parenthesized<ArgumentList<'a>>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
/// Parses an iterable declaration `[attributes]? (iterable<attributedtype> | iterable<attributedtype, attributedtype>) ;`
|
||||
Iterable(enum IterableInterfaceMember<'a> {
|
||||
/// Parses an iterable declaration `[attributes]? iterable<attributedtype>;`
|
||||
Single(struct SingleTypedIterable<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
iterable: term!(iterable),
|
||||
generics: Generics<AttributedType<'a>>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
/// Parses an iterable declaration `[attributes]? iterable<attributedtype, attributedtype>;`
|
||||
Double(struct DoubleTypedIterable<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
iterable: term!(iterable),
|
||||
generics: Generics<(AttributedType<'a>, term!(,), AttributedType<'a>)>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
}),
|
||||
/// Parses an maplike declaration `[attributes]? readonly? maplike<attributedtype, attributedtype>;`
|
||||
Maplike(struct MaplikeInterfaceMember<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
readonly: Option<term!(readonly)>,
|
||||
maplike: term!(maplike),
|
||||
generics: Generics<(AttributedType<'a>, term!(,), AttributedType<'a>)>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
Setlike(struct SetlikeInterfaceMember<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
readonly: Option<term!(readonly)>,
|
||||
setlike: term!(setlike),
|
||||
generics: Generics<AttributedType<'a>>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
/// Parses `stringifier;`
|
||||
#[derive(Default)]
|
||||
Stringifier(struct StringifierMember<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
stringifier: term!(stringifier),
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
}
|
||||
|
||||
/// Parses one of the special keyword `getter|setter|deleter`
|
||||
#[derive(Copy)]
|
||||
enum Special {
|
||||
Getter(term!(getter)),
|
||||
Setter(term!(setter)),
|
||||
Deleter(term!(deleter)),
|
||||
LegacyCaller(term!(legacycaller)),
|
||||
}
|
||||
|
||||
/// Parses `stringifier|inherit|static`
|
||||
#[derive(Copy)]
|
||||
enum StringifierOrInheritOrStatic {
|
||||
Stringifier(term!(stringifier)),
|
||||
Inherit(term!(inherit)),
|
||||
Static(term!(static)),
|
||||
}
|
||||
|
||||
/// Parses `stringifier|static`
|
||||
#[derive(Copy)]
|
||||
enum StringifierOrStatic {
|
||||
Stringifier(term!(stringifier)),
|
||||
Static(term!(static)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::Parse;
|
||||
|
||||
test!(should_parse_stringifier_member { "stringifier;" =>
|
||||
"";
|
||||
StringifierMember;
|
||||
});
|
||||
|
||||
test!(should_parse_stringifier_or_static { "static" =>
|
||||
"";
|
||||
StringifierOrStatic;
|
||||
});
|
||||
|
||||
test!(should_parse_stringifier_or_inherit_or_static { "inherit" =>
|
||||
"";
|
||||
StringifierOrInheritOrStatic;
|
||||
});
|
||||
|
||||
test!(should_parse_setlike_interface_member { "readonly setlike<long>;" =>
|
||||
"";
|
||||
SetlikeInterfaceMember;
|
||||
attributes.is_none();
|
||||
readonly == Some(term!(readonly));
|
||||
});
|
||||
|
||||
test!(should_parse_maplike_interface_member { "readonly maplike<long, short>;" =>
|
||||
"";
|
||||
MaplikeInterfaceMember;
|
||||
attributes.is_none();
|
||||
readonly == Some(term!(readonly));
|
||||
});
|
||||
|
||||
test!(should_parse_attribute_interface_member { "readonly attribute unsigned long width;" =>
|
||||
"";
|
||||
AttributeInterfaceMember;
|
||||
attributes.is_none();
|
||||
readonly == Some(term!(readonly));
|
||||
identifier.0 == "width";
|
||||
});
|
||||
|
||||
test!(should_parse_double_typed_iterable { "iterable<long, long>;" =>
|
||||
"";
|
||||
DoubleTypedIterable;
|
||||
attributes.is_none();
|
||||
});
|
||||
|
||||
test!(should_parse_single_typed_iterable { "iterable<long>;" =>
|
||||
"";
|
||||
SingleTypedIterable;
|
||||
attributes.is_none();
|
||||
});
|
||||
|
||||
test!(should_parse_constructor_interface_member { "constructor(long a);" =>
|
||||
"";
|
||||
ConstructorInterfaceMember;
|
||||
attributes.is_none();
|
||||
});
|
||||
|
||||
test!(should_parse_operation_interface_member { "void readString(long a, long b);" =>
|
||||
"";
|
||||
OperationInterfaceMember;
|
||||
attributes.is_none();
|
||||
modifier.is_none();
|
||||
special.is_none();
|
||||
identifier.is_some();
|
||||
});
|
||||
|
||||
test!(should_parse_const_member { "const long name = 5;" =>
|
||||
"";
|
||||
ConstMember;
|
||||
attributes.is_none();
|
||||
identifier.0 == "name";
|
||||
});
|
||||
}
|
|
@ -1,425 +0,0 @@
|
|||
//! Weedle - A WebIDL Parser
|
||||
//!
|
||||
//! Parses valid WebIDL definitions & produces a data structure starting from
|
||||
//! [`Definitions`](struct.Definitions.html).
|
||||
//!
|
||||
//! ### Example
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate weedle;
|
||||
//!
|
||||
//! let parsed = weedle::parse("
|
||||
//! interface Window {
|
||||
//! readonly attribute Storage sessionStorage;
|
||||
//! };
|
||||
//! ").unwrap();
|
||||
//! println!("{:?}", parsed);
|
||||
//! ```
|
||||
//!
|
||||
//! Note:
|
||||
//! This parser follows the grammar given at [WebIDL](https://heycam.github.io/webidl).
|
||||
//!
|
||||
//! If any flaws found when parsing string with a valid grammar, create an issue.
|
||||
|
||||
// need a higher recusion limit for macros
|
||||
#![recursion_limit = "128"]
|
||||
|
||||
#[macro_use(
|
||||
alt,
|
||||
cond,
|
||||
do_parse,
|
||||
map,
|
||||
many0,
|
||||
opt,
|
||||
recognize,
|
||||
separated_list,
|
||||
separated_nonempty_list,
|
||||
terminated
|
||||
)]
|
||||
extern crate nom;
|
||||
|
||||
use self::argument::ArgumentList;
|
||||
use self::attribute::ExtendedAttributeList;
|
||||
use self::common::{Braced, Identifier, Parenthesized, PunctuatedNonEmpty};
|
||||
use self::dictionary::DictionaryMembers;
|
||||
use self::interface::{Inheritance, InterfaceMembers};
|
||||
use self::literal::StringLit;
|
||||
use self::mixin::MixinMembers;
|
||||
use self::namespace::NamespaceMembers;
|
||||
use self::types::{AttributedType, ReturnType};
|
||||
pub use nom::{error::ErrorKind, Err, IResult};
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
#[macro_use]
|
||||
mod whitespace;
|
||||
#[macro_use]
|
||||
pub mod term;
|
||||
pub mod argument;
|
||||
pub mod attribute;
|
||||
pub mod common;
|
||||
pub mod dictionary;
|
||||
pub mod interface;
|
||||
pub mod literal;
|
||||
pub mod mixin;
|
||||
pub mod namespace;
|
||||
pub mod types;
|
||||
|
||||
/// A convenient parse function
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```
|
||||
/// extern crate weedle;
|
||||
///
|
||||
/// let parsed = weedle::parse("
|
||||
/// interface Window {
|
||||
/// readonly attribute Storage sessionStorage;
|
||||
/// };
|
||||
/// ").unwrap();
|
||||
///
|
||||
/// println!("{:?}", parsed);
|
||||
/// ```
|
||||
pub fn parse(raw: &str) -> Result<Definitions<'_>, Err<(&str, ErrorKind)>> {
|
||||
let (remaining, parsed) = Definitions::parse(raw)?;
|
||||
assert!(
|
||||
remaining.is_empty(),
|
||||
"There is redundant raw data after parsing"
|
||||
);
|
||||
Ok(parsed)
|
||||
}
|
||||
|
||||
pub trait Parse<'a>: Sized {
|
||||
fn parse(input: &'a str) -> IResult<&'a str, Self>;
|
||||
}
|
||||
|
||||
/// Parses WebIDL definitions. It is the root struct for a complete WebIDL definition.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```
|
||||
/// use weedle::{Definitions, Parse};
|
||||
///
|
||||
/// let (_, parsed) = Definitions::parse("
|
||||
/// interface Window {
|
||||
/// readonly attribute Storage sessionStorage;
|
||||
/// };
|
||||
/// ").unwrap();
|
||||
///
|
||||
/// println!("{:?}", parsed);
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use [`parse`](fn.parse.html) instead.
|
||||
pub type Definitions<'a> = Vec<Definition<'a>>;
|
||||
|
||||
ast_types! {
|
||||
/// Parses a definition
|
||||
enum Definition<'a> {
|
||||
/// Parses `[attributes]? callback identifier = type ( (arg1, arg2, ..., argN)? );`
|
||||
Callback(struct CallbackDefinition<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
callback: term!(callback),
|
||||
identifier: Identifier<'a>,
|
||||
assign: term!(=),
|
||||
return_type: ReturnType<'a>,
|
||||
arguments: Parenthesized<ArgumentList<'a>>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
/// Parses `[attributes]? callback interface identifier ( : inheritance )? { members };`
|
||||
CallbackInterface(struct CallbackInterfaceDefinition<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
callback: term!(callback),
|
||||
interface: term!(interface),
|
||||
identifier: Identifier<'a>,
|
||||
inheritance: Option<Inheritance<'a>>,
|
||||
members: Braced<InterfaceMembers<'a>>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
/// Parses `[attributes]? interface identifier ( : inheritance )? { members };`
|
||||
Interface(struct InterfaceDefinition<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
interface: term!(interface),
|
||||
identifier: Identifier<'a>,
|
||||
inheritance: Option<Inheritance<'a>>,
|
||||
members: Braced<InterfaceMembers<'a>>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
/// Parses `[attributes]? interface mixin identifier { members };`
|
||||
InterfaceMixin(struct InterfaceMixinDefinition<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
interface: term!(interface),
|
||||
mixin: term!(mixin),
|
||||
identifier: Identifier<'a>,
|
||||
members: Braced<MixinMembers<'a>>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
/// Parses `[attributes]? namespace identifier { members };`
|
||||
Namespace(struct NamespaceDefinition<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
namespace: term!(namespace),
|
||||
identifier: Identifier<'a>,
|
||||
members: Braced<NamespaceMembers<'a>>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
/// Parses `[attributes]? dictionary identifier ( : inheritance )? { members };`
|
||||
Dictionary(struct DictionaryDefinition<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
dictionary: term!(dictionary),
|
||||
identifier: Identifier<'a>,
|
||||
inheritance: Option<Inheritance<'a>>,
|
||||
members: Braced<DictionaryMembers<'a>>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
/// Parses `[attributes]? partial interface identifier { members };`
|
||||
PartialInterface(struct PartialInterfaceDefinition<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
partial: term!(partial),
|
||||
interface: term!(interface),
|
||||
identifier: Identifier<'a>,
|
||||
members: Braced<InterfaceMembers<'a>>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
/// Parses `[attributes]? partial interface mixin identifier { members };`
|
||||
PartialInterfaceMixin(struct PartialInterfaceMixinDefinition<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
partial: term!(partial),
|
||||
interface: term!(interface),
|
||||
mixin: term!(mixin),
|
||||
identifier: Identifier<'a>,
|
||||
members: Braced<MixinMembers<'a>>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
/// Parses `[attributes]? partial dictionary identifier { members };`
|
||||
PartialDictionary(struct PartialDictionaryDefinition<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
partial: term!(partial),
|
||||
dictionary: term!(dictionary),
|
||||
identifier: Identifier<'a>,
|
||||
members: Braced<DictionaryMembers<'a>>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
/// Parses `[attributes]? partial namespace identifier { members };`
|
||||
PartialNamespace(struct PartialNamespaceDefinition<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
partial: term!(partial),
|
||||
namespace: term!(namespace),
|
||||
identifier: Identifier<'a>,
|
||||
members: Braced<NamespaceMembers<'a>>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
/// Parses `[attributes]? enum identifier { values };`
|
||||
Enum(struct EnumDefinition<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
enum_: term!(enum),
|
||||
identifier: Identifier<'a>,
|
||||
values: Braced<EnumValueList<'a>>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
/// Parses `[attributes]? typedef attributedtype identifier;`
|
||||
Typedef(struct TypedefDefinition<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
typedef: term!(typedef),
|
||||
type_: AttributedType<'a>,
|
||||
identifier: Identifier<'a>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
/// Parses `[attributes]? identifier includes identifier;`
|
||||
IncludesStatement(struct IncludesStatementDefinition<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
lhs_identifier: Identifier<'a>,
|
||||
includes: term!(includes),
|
||||
rhs_identifier: Identifier<'a>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
/// Parses `[attributes]? identifier implements identifier;`
|
||||
Implements(struct ImplementsDefinition<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
lhs_identifier: Identifier<'a>,
|
||||
includes: term!(implements),
|
||||
rhs_identifier: Identifier<'a>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a non-empty enum value list
|
||||
pub type EnumValueList<'a> = PunctuatedNonEmpty<StringLit<'a>, term!(,)>;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
test!(should_parse_includes_statement { "first includes second;" =>
|
||||
"";
|
||||
IncludesStatementDefinition;
|
||||
attributes.is_none();
|
||||
lhs_identifier.0 == "first";
|
||||
rhs_identifier.0 == "second";
|
||||
});
|
||||
|
||||
test!(should_parse_typedef { "typedef short Short;" =>
|
||||
"";
|
||||
TypedefDefinition;
|
||||
attributes.is_none();
|
||||
identifier.0 == "Short";
|
||||
});
|
||||
|
||||
test!(should_parse_enum { r#"enum name { "first", "second" }; "# =>
|
||||
"";
|
||||
EnumDefinition;
|
||||
attributes.is_none();
|
||||
identifier.0 == "name";
|
||||
values.body.list.len() == 2;
|
||||
});
|
||||
|
||||
test!(should_parse_dictionary { "dictionary A { long c; long g; };" =>
|
||||
"";
|
||||
DictionaryDefinition;
|
||||
attributes.is_none();
|
||||
identifier.0 == "A";
|
||||
inheritance.is_none();
|
||||
members.body.len() == 2;
|
||||
});
|
||||
|
||||
test!(should_parse_dictionary_inherited { "dictionary C : B { long e; long f; };" =>
|
||||
"";
|
||||
DictionaryDefinition;
|
||||
attributes.is_none();
|
||||
identifier.0 == "C";
|
||||
inheritance.is_some();
|
||||
members.body.len() == 2;
|
||||
});
|
||||
|
||||
test!(should_parse_partial_namespace { "
|
||||
partial namespace VectorUtils {
|
||||
readonly attribute Vector unit;
|
||||
double dotProduct(Vector x, Vector y);
|
||||
Vector crossProduct(Vector x, Vector y);
|
||||
};
|
||||
" =>
|
||||
"";
|
||||
PartialNamespaceDefinition;
|
||||
attributes.is_none();
|
||||
identifier.0 == "VectorUtils";
|
||||
members.body.len() == 3;
|
||||
});
|
||||
|
||||
test!(should_parse_partial_dictionary { "partial dictionary C { long e; long f; };" =>
|
||||
"";
|
||||
PartialDictionaryDefinition;
|
||||
attributes.is_none();
|
||||
identifier.0 == "C";
|
||||
members.body.len() == 2;
|
||||
});
|
||||
|
||||
test!(should_parse_partial_interface_mixin { "
|
||||
partial interface mixin WindowSessionStorage {
|
||||
readonly attribute Storage sessionStorage;
|
||||
};
|
||||
" =>
|
||||
"";
|
||||
PartialInterfaceMixinDefinition;
|
||||
attributes.is_none();
|
||||
identifier.0 == "WindowSessionStorage";
|
||||
members.body.len() == 1;
|
||||
});
|
||||
|
||||
test!(should_parse_partial_interface { "
|
||||
partial interface Window {
|
||||
readonly attribute Storage sessionStorage;
|
||||
};
|
||||
" =>
|
||||
"";
|
||||
PartialInterfaceDefinition;
|
||||
attributes.is_none();
|
||||
identifier.0 == "Window";
|
||||
members.body.len() == 1;
|
||||
});
|
||||
|
||||
test!(should_parse_namespace { "
|
||||
namespace VectorUtils {
|
||||
readonly attribute Vector unit;
|
||||
double dotProduct(Vector x, Vector y);
|
||||
Vector crossProduct(Vector x, Vector y);
|
||||
};
|
||||
" =>
|
||||
"";
|
||||
NamespaceDefinition;
|
||||
attributes.is_none();
|
||||
identifier.0 == "VectorUtils";
|
||||
members.body.len() == 3;
|
||||
});
|
||||
|
||||
test!(should_parse_interface_mixin { "
|
||||
interface mixin WindowSessionStorage {
|
||||
readonly attribute Storage sessionStorage;
|
||||
};
|
||||
" =>
|
||||
"";
|
||||
InterfaceMixinDefinition;
|
||||
attributes.is_none();
|
||||
identifier.0 == "WindowSessionStorage";
|
||||
members.body.len() == 1;
|
||||
});
|
||||
|
||||
test!(should_parse_interface { "
|
||||
interface Window {
|
||||
readonly attribute Storage sessionStorage;
|
||||
};
|
||||
" =>
|
||||
"";
|
||||
InterfaceDefinition;
|
||||
attributes.is_none();
|
||||
identifier.0 == "Window";
|
||||
members.body.len() == 1;
|
||||
});
|
||||
|
||||
test!(should_parse_callback_interface {"
|
||||
callback interface Options {
|
||||
attribute DOMString? option1;
|
||||
attribute DOMString? option2;
|
||||
attribute long? option3;
|
||||
};
|
||||
" =>
|
||||
"";
|
||||
CallbackInterfaceDefinition;
|
||||
attributes.is_none();
|
||||
identifier.0 == "Options";
|
||||
members.body.len() == 3;
|
||||
});
|
||||
|
||||
test!(should_parse_callback { "callback AsyncOperationCallback = void (DOMString status);" =>
|
||||
"";
|
||||
CallbackDefinition;
|
||||
attributes.is_none();
|
||||
identifier.0 == "AsyncOperationCallback";
|
||||
arguments.body.list.len() == 1;
|
||||
});
|
||||
|
||||
test!(should_parse_with_line_comments { "
|
||||
// This is a comment
|
||||
callback AsyncOperationCallback = void (DOMString status);
|
||||
" =>
|
||||
"";
|
||||
CallbackDefinition;
|
||||
});
|
||||
|
||||
test!(should_parse_with_block_comments { "
|
||||
/* This is a comment */
|
||||
callback AsyncOperationCallback = void (DOMString status);
|
||||
" =>
|
||||
"";
|
||||
CallbackDefinition;
|
||||
});
|
||||
|
||||
test!(should_parse_with_multiple_comments { "
|
||||
// This is a comment
|
||||
// This is a comment
|
||||
// This is a comment
|
||||
|
||||
// This is a comment
|
||||
callback AsyncOperationCallback = void (DOMString status);
|
||||
" =>
|
||||
"";
|
||||
CallbackDefinition;
|
||||
});
|
||||
}
|
|
@ -1,281 +0,0 @@
|
|||
ast_types! {
|
||||
/// Represents an integer value
|
||||
#[derive(Copy)]
|
||||
enum IntegerLit<'a> {
|
||||
/// Parses `-?[1-9][0-9]*`
|
||||
#[derive(Copy)]
|
||||
Dec(struct DecLit<'a>(
|
||||
&'a str = ws!(recognize!(do_parse!(
|
||||
opt!(char!('-')) >>
|
||||
one_of!("123456789") >>
|
||||
take_while!(|c: char| c.is_ascii_digit()) >>
|
||||
(())
|
||||
))),
|
||||
)),
|
||||
/// Parses `-?0[Xx][0-9A-Fa-f]+)`
|
||||
#[derive(Copy)]
|
||||
Hex(struct HexLit<'a>(
|
||||
&'a str = ws!(recognize!(do_parse!(
|
||||
opt!(char!('-')) >>
|
||||
char!('0') >>
|
||||
alt!(char!('x') | char!('X')) >>
|
||||
take_while!(|c: char| c.is_ascii_hexdigit()) >>
|
||||
(())
|
||||
))),
|
||||
)),
|
||||
/// Parses `-?0[0-7]*`
|
||||
#[derive(Copy)]
|
||||
Oct(struct OctLit<'a>(
|
||||
&'a str = ws!(recognize!(do_parse!(
|
||||
opt!(char!('-')) >>
|
||||
char!('0') >>
|
||||
take_while!(|c| '0' <= c && c <= '7') >>
|
||||
(())
|
||||
))),
|
||||
)),
|
||||
}
|
||||
|
||||
/// Represents a string value
|
||||
///
|
||||
/// Follow `/"[^"]*"/`
|
||||
#[derive(Copy)]
|
||||
struct StringLit<'a>(
|
||||
&'a str = ws!(do_parse!(
|
||||
char!('"') >>
|
||||
s: take_while!(|c| c != '"') >>
|
||||
char!('"') >>
|
||||
(s)
|
||||
)),
|
||||
)
|
||||
|
||||
/// Represents a default literal value. Ex: `34|34.23|"value"|[ ]|true|false|null`
|
||||
#[derive(Copy)]
|
||||
enum DefaultValue<'a> {
|
||||
Boolean(BooleanLit),
|
||||
/// Represents `[ ]`
|
||||
#[derive(Copy, Default)]
|
||||
EmptyArray(struct EmptyArrayLit {
|
||||
open_bracket: term!(OpenBracket),
|
||||
close_bracket: term!(CloseBracket),
|
||||
}),
|
||||
/// Represents `{ }`
|
||||
#[derive(Copy, Default)]
|
||||
EmptyDictionary(struct EmptyDictionaryLit {
|
||||
open_brace: term!(OpenBrace),
|
||||
close_brace: term!(CloseBrace),
|
||||
}),
|
||||
Float(FloatLit<'a>),
|
||||
Integer(IntegerLit<'a>),
|
||||
Null(term!(null)),
|
||||
String(StringLit<'a>),
|
||||
}
|
||||
|
||||
/// Represents `true`, `false`, `34.23`, `null`, `56`, ...
|
||||
#[derive(Copy)]
|
||||
enum ConstValue<'a> {
|
||||
Boolean(BooleanLit),
|
||||
Float(FloatLit<'a>),
|
||||
Integer(IntegerLit<'a>),
|
||||
Null(term!(null)),
|
||||
}
|
||||
|
||||
/// Represents either `true` or `false`
|
||||
#[derive(Copy)]
|
||||
struct BooleanLit(
|
||||
bool = alt!(
|
||||
weedle!(term!(true)) => {|_| true} |
|
||||
weedle!(term!(false)) => {|_| false}
|
||||
),
|
||||
)
|
||||
|
||||
/// Represents a floating point value, `NaN`, `Infinity`, '+Infinity`
|
||||
#[derive(Copy)]
|
||||
enum FloatLit<'a> {
|
||||
/// Parses `/-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)/`
|
||||
#[derive(Copy)]
|
||||
Value(struct FloatValueLit<'a>(
|
||||
&'a str = ws!(recognize!(do_parse!(
|
||||
opt!(char!('-')) >>
|
||||
alt!(
|
||||
do_parse!(
|
||||
// (?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)
|
||||
alt!(
|
||||
do_parse!(
|
||||
take_while1!(|c: char| c.is_ascii_digit()) >>
|
||||
char!('.') >>
|
||||
take_while!(|c: char| c.is_ascii_digit()) >>
|
||||
(())
|
||||
)
|
||||
|
|
||||
do_parse!(
|
||||
take_while!(|c: char| c.is_ascii_digit()) >>
|
||||
char!('.') >>
|
||||
take_while1!(|c: char| c.is_ascii_digit()) >>
|
||||
(())
|
||||
)
|
||||
) >>
|
||||
// (?:[Ee][+-]?[0-9]+)?
|
||||
opt!(do_parse!(
|
||||
alt!(char!('e') | char!('E')) >>
|
||||
opt!(alt!(char!('+') | char!('-'))) >>
|
||||
take_while1!(|c: char| c.is_ascii_digit()) >>
|
||||
(())
|
||||
)) >>
|
||||
(())
|
||||
)
|
||||
|
|
||||
// [0-9]+[Ee][+-]?[0-9]+
|
||||
do_parse!(
|
||||
take_while1!(|c: char| c.is_ascii_digit()) >>
|
||||
alt!(char!('e') | char!('E')) >>
|
||||
opt!(alt!(char!('+') | char!('-'))) >>
|
||||
take_while1!(|c: char| c.is_ascii_digit()) >>
|
||||
(())
|
||||
)
|
||||
) >>
|
||||
(())
|
||||
))),
|
||||
)),
|
||||
NegInfinity(term!(-Infinity)),
|
||||
Infinity(term!(Infinity)),
|
||||
NaN(term!(NaN)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::term::*;
|
||||
use crate::Parse;
|
||||
|
||||
test!(should_parse_integer { "45" =>
|
||||
"";
|
||||
IntegerLit => IntegerLit::Dec(DecLit("45"))
|
||||
});
|
||||
|
||||
test!(should_parse_integer_surrounding_with_spaces { " 123123 " =>
|
||||
"";
|
||||
IntegerLit => IntegerLit::Dec(DecLit("123123"))
|
||||
});
|
||||
|
||||
test!(should_parse_integer_preceeding_others { "3453 string" =>
|
||||
"string";
|
||||
IntegerLit => IntegerLit::Dec(DecLit("3453"))
|
||||
});
|
||||
|
||||
test!(should_parse_neg_integer { "-435" =>
|
||||
"";
|
||||
IntegerLit => IntegerLit::Dec(DecLit("-435"))
|
||||
});
|
||||
|
||||
test!(should_parse_hex_number { "0X08" =>
|
||||
"";
|
||||
IntegerLit => IntegerLit::Hex(HexLit("0X08"))
|
||||
});
|
||||
|
||||
test!(should_parse_hex_large_number { "0xA" =>
|
||||
"";
|
||||
IntegerLit => IntegerLit::Hex(HexLit("0xA"))
|
||||
});
|
||||
|
||||
test!(should_parse_zero { "0" =>
|
||||
"";
|
||||
IntegerLit => IntegerLit::Oct(OctLit("0"))
|
||||
});
|
||||
|
||||
test!(should_parse_oct_number { "-07561" =>
|
||||
"";
|
||||
IntegerLit => IntegerLit::Oct(OctLit("-07561"))
|
||||
});
|
||||
|
||||
test!(should_parse_float { "45.434" =>
|
||||
"";
|
||||
FloatLit => FloatLit::Value(FloatValueLit("45.434"))
|
||||
});
|
||||
|
||||
test!(should_parse_float_surrounding_with_spaces { " 2345.2345 " =>
|
||||
"";
|
||||
FloatLit => FloatLit::Value(FloatValueLit("2345.2345"))
|
||||
});
|
||||
|
||||
test!(should_parse_float_preceeding_others { "3453.32334 string" =>
|
||||
"string";
|
||||
FloatLit => FloatLit::Value(FloatValueLit("3453.32334"))
|
||||
});
|
||||
|
||||
test!(should_parse_neg_float { "-435.3435" =>
|
||||
"";
|
||||
FloatLit => FloatLit::Value(FloatValueLit("-435.3435"))
|
||||
});
|
||||
|
||||
test!(should_parse_float_exp { "5.3434e23" =>
|
||||
"";
|
||||
FloatLit => FloatLit::Value(FloatValueLit("5.3434e23"))
|
||||
});
|
||||
|
||||
test!(should_parse_float_exp_with_decimal { "3e23" =>
|
||||
"";
|
||||
FloatLit => FloatLit::Value(FloatValueLit("3e23"))
|
||||
});
|
||||
|
||||
test!(should_parse_neg_infinity { "-Infinity" =>
|
||||
"";
|
||||
FloatLit => FloatLit::NegInfinity(term!(-Infinity))
|
||||
});
|
||||
|
||||
test!(should_parse_infinity { "Infinity" =>
|
||||
"";
|
||||
FloatLit => FloatLit::Infinity(term!(Infinity))
|
||||
});
|
||||
|
||||
test!(should_parse_string { r#""this is a string""# =>
|
||||
"";
|
||||
StringLit => StringLit("this is a string")
|
||||
});
|
||||
|
||||
test!(should_parse_string_surround_with_spaces { r#" "this is a string" "# =>
|
||||
"";
|
||||
StringLit => StringLit("this is a string")
|
||||
});
|
||||
|
||||
test!(should_parse_string_followed_by_string { r#" "this is first" "this is second" "# =>
|
||||
r#""this is second" "#;
|
||||
StringLit => StringLit("this is first")
|
||||
});
|
||||
|
||||
test!(should_parse_string_with_spaces { r#" " this is a string " "# =>
|
||||
"";
|
||||
StringLit => StringLit(" this is a string ")
|
||||
});
|
||||
|
||||
test!(should_parse_string_with_comment { r#" "// this is still a string"
|
||||
"# =>
|
||||
"";
|
||||
StringLit => StringLit("// this is still a string")
|
||||
});
|
||||
|
||||
test!(should_parse_string_with_multiline_comment { r#" "/*" "*/" "# =>
|
||||
r#""*/" "#;
|
||||
StringLit => StringLit("/*")
|
||||
});
|
||||
|
||||
test!(should_parse_null { "null" =>
|
||||
"";
|
||||
Null => Null
|
||||
});
|
||||
|
||||
test!(should_parse_empty_array { "[]" =>
|
||||
"";
|
||||
EmptyArrayLit => Default::default()
|
||||
});
|
||||
|
||||
test!(should_parse_bool_true { "true" =>
|
||||
"";
|
||||
BooleanLit => BooleanLit(true)
|
||||
});
|
||||
|
||||
test!(should_parse_bool_false { "false" =>
|
||||
"";
|
||||
BooleanLit => BooleanLit(false)
|
||||
});
|
||||
}
|
|
@ -1,614 +0,0 @@
|
|||
macro_rules! tag {
|
||||
($i:expr, $tag: expr) => {
|
||||
nom::bytes::complete::tag($tag)($i)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! take_while {
|
||||
($input:expr, $submac:ident!( $($args:tt)* )) => {
|
||||
$crate::macros::take_while!($input, (|c| $submac!(c, $($args)*)))
|
||||
};
|
||||
($input:expr, $f:expr) => {
|
||||
nom::bytes::complete::take_while($f)($input)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! take_while1 {
|
||||
($input:expr, $submac:ident!( $($args:tt)* )) => {
|
||||
$crate::macros::take_while1!($input, (|c| $submac!(c, $($args)*)))
|
||||
};
|
||||
($input:expr, $f:expr) => {
|
||||
nom::bytes::complete::take_while1($f)($input)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! take_until {
|
||||
($i:expr, $substr:expr) => {
|
||||
nom::bytes::complete::take_until($substr)($i)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! one_of {
|
||||
($i:expr, $inp: expr) => {
|
||||
nom::character::complete::one_of($inp)($i)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! char {
|
||||
($i:expr, $c: expr) => {
|
||||
nom::character::complete::char($c)($i)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! parser {
|
||||
($submac:ident!( $($args:tt)* )) => {
|
||||
fn parse(input: &'a str) -> $crate::IResult<&'a str, Self> {
|
||||
$submac!(input, $($args)*)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! weedle {
|
||||
($i:expr, $t:ty) => {
|
||||
<$t as $crate::Parse<'a>>::parse($i)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! ast_types {
|
||||
(@extract_type struct $name:ident<'a> $($rest:tt)*) => ($name<'a>);
|
||||
(@extract_type struct $name:ident $($rest:tt)*) => ($name);
|
||||
(@extract_type enum $name:ident<'a> $($rest:tt)*) => ($name<'a>);
|
||||
(@extract_type enum $name:ident $($rest:tt)*) => ($name);
|
||||
|
||||
() => ();
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
struct $name:ident<'a> {
|
||||
$($fields:tt)*
|
||||
}
|
||||
$($rest:tt)*
|
||||
) => (
|
||||
__ast_struct! {
|
||||
@launch_pad
|
||||
$(#[$attr])*
|
||||
$name
|
||||
[ 'a ]
|
||||
[ ]
|
||||
{ $($fields)* }
|
||||
}
|
||||
ast_types!($($rest)*);
|
||||
);
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
struct $name:ident<$($generics:ident),+> where [$($bounds:tt)+] {
|
||||
$($fields:tt)*
|
||||
}
|
||||
$($rest:tt)*
|
||||
) => (
|
||||
__ast_struct! {
|
||||
@launch_pad
|
||||
$(#[$attr])*
|
||||
$name
|
||||
[$($generics)+]
|
||||
[$($bounds)+]
|
||||
{ $($fields)* }
|
||||
}
|
||||
ast_types!($($rest)*);
|
||||
);
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
struct $name:ident {
|
||||
$($fields:tt)*
|
||||
}
|
||||
$($rest:tt)*
|
||||
) => (
|
||||
__ast_struct! {
|
||||
@launch_pad
|
||||
$(#[$attr])*
|
||||
$name
|
||||
[ ]
|
||||
[ ]
|
||||
{ $($fields)* }
|
||||
}
|
||||
ast_types!($($rest)*);
|
||||
);
|
||||
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
struct $name:ident<'a> (
|
||||
$($fields:tt)*
|
||||
)
|
||||
$($rest:tt)*
|
||||
) => (
|
||||
__ast_tuple_struct! {
|
||||
@launch_pad
|
||||
$(#[$attr])*
|
||||
$name
|
||||
[ 'a ]
|
||||
( $($fields)* )
|
||||
}
|
||||
ast_types!($($rest)*);
|
||||
);
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
struct $name:ident (
|
||||
$($fields:tt)*
|
||||
)
|
||||
$($rest:tt)*
|
||||
) => (
|
||||
__ast_tuple_struct! {
|
||||
@launch_pad
|
||||
$(#[$attr])*
|
||||
$name
|
||||
[ ]
|
||||
( $($fields)* )
|
||||
}
|
||||
ast_types!($($rest)*);
|
||||
);
|
||||
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
enum $name:ident<'a> {
|
||||
$($variants:tt)*
|
||||
}
|
||||
$($rest:tt)*
|
||||
) => (
|
||||
__ast_enum! {
|
||||
@launch_pad
|
||||
$(#[$attr])*
|
||||
$name
|
||||
[ 'a ]
|
||||
{ $($variants)* }
|
||||
}
|
||||
ast_types!($($rest)*);
|
||||
);
|
||||
(
|
||||
$(#[$attr:meta])*
|
||||
enum $name:ident {
|
||||
$($variants:tt)*
|
||||
}
|
||||
$($rest:tt)*
|
||||
) => (
|
||||
__ast_enum! {
|
||||
@launch_pad
|
||||
$(#[$attr])*
|
||||
$name
|
||||
[ ]
|
||||
{ $($variants)* }
|
||||
}
|
||||
ast_types!($($rest)*);
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! __ast_tuple_struct {
|
||||
(@launch_pad
|
||||
$(#[$attr:meta])*
|
||||
$name:ident
|
||||
[ $($maybe_a:tt)* ]
|
||||
( $inner:ty = $submac:ident!( $($args:tt)* ), )
|
||||
) => (
|
||||
$(#[$attr])*
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct $name<$($maybe_a)*>(pub $inner);
|
||||
|
||||
impl<'a> $crate::Parse<'a> for $name<$($maybe_a)*> {
|
||||
fn parse(input: &'a str) -> $crate::IResult<&'a str, Self> {
|
||||
use $crate::nom::lib::std::result::Result::*;
|
||||
|
||||
match $submac!(input, $($args)*) {
|
||||
Err(e) => Err(e),
|
||||
Ok((i, inner)) => Ok((i, $name(inner))),
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
(@launch_pad
|
||||
$(#[$attr:meta])*
|
||||
$name:ident
|
||||
[ $($maybe_a:tt)* ]
|
||||
( $inner:ty, )
|
||||
) => (
|
||||
__ast_tuple_struct! {
|
||||
@launch_pad
|
||||
$(#[$attr])*
|
||||
$name
|
||||
[ $($maybe_a)* ]
|
||||
( $inner = weedle!($inner), )
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! __ast_struct {
|
||||
(@build_struct_decl
|
||||
{
|
||||
$(#[$attr:meta])*
|
||||
$name:ident
|
||||
[ $($generics:tt)* ]
|
||||
$($field:ident : $type:ty)*
|
||||
}
|
||||
{ }
|
||||
) => {
|
||||
$(#[$attr])*
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct $name<$($generics)*> {
|
||||
$(pub $field : $type,)*
|
||||
}
|
||||
};
|
||||
(@build_struct_decl
|
||||
{ $($prev:tt)* }
|
||||
{ $field:ident : $type:ty, $($rest:tt)* }
|
||||
) => (
|
||||
__ast_struct! {
|
||||
@build_struct_decl
|
||||
{ $($prev)* $field : $type }
|
||||
{ $($rest)* }
|
||||
}
|
||||
);
|
||||
(@build_struct_decl
|
||||
{ $($prev:tt)* }
|
||||
{ $field:ident : $type:ty = $submac:ident!( $($args:tt)* ), $($rest:tt)* }
|
||||
) => (
|
||||
__ast_struct! {
|
||||
@build_struct_decl
|
||||
{ $($prev)* $field : $type }
|
||||
{ $($rest)* }
|
||||
}
|
||||
);
|
||||
(@build_struct_decl
|
||||
{ $($prev:tt)* }
|
||||
{ $field:ident : $type:ty = marker, $($rest:tt)* }
|
||||
) => (
|
||||
__ast_struct! {
|
||||
@build_struct_decl
|
||||
{ $($prev)* $field : $type }
|
||||
{ $($rest)* }
|
||||
}
|
||||
);
|
||||
|
||||
(@build_parser
|
||||
{ $i:expr, $($field:ident)* }
|
||||
{ }
|
||||
) => ({
|
||||
use $crate::nom::lib::std::result::Result::Ok;
|
||||
Ok(($i, Self { $($field,)* }))
|
||||
});
|
||||
(@build_parser
|
||||
{ $i:expr, $($prev:tt)* }
|
||||
{ $field:ident : $type:ty = $submac:ident!( $($args:tt)* ), $($rest:tt)* }
|
||||
) => ({
|
||||
use $crate::nom::lib::std::result::Result::*;
|
||||
|
||||
match $submac!($i, $($args)*) {
|
||||
Err(e) => Err(e),
|
||||
Ok((i, $field)) => {
|
||||
__ast_struct! {
|
||||
@build_parser
|
||||
{ i, $($prev)* $field }
|
||||
{ $($rest)* }
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
(@build_parser
|
||||
{ $($prev:tt)* }
|
||||
{ $field:ident : $type:ty = marker, $($rest:tt)* }
|
||||
) => ({
|
||||
let $field = ::std::default::Default::default();
|
||||
__ast_struct! {
|
||||
@build_parser
|
||||
{ $($prev)* $field }
|
||||
{ $($rest)* }
|
||||
}
|
||||
});
|
||||
(@build_parser
|
||||
{ $($prev:tt)* }
|
||||
{ $field:ident : $type:ty, $($rest:tt)* }
|
||||
) => (
|
||||
__ast_struct! {
|
||||
@build_parser
|
||||
{ $($prev)* }
|
||||
{ $field : $type = weedle!($type), $($rest)* }
|
||||
}
|
||||
);
|
||||
|
||||
(
|
||||
@launch_pad
|
||||
$(#[$attr:meta])*
|
||||
$name:ident
|
||||
[ ]
|
||||
[ ]
|
||||
{ $($fields:tt)* }
|
||||
) => {
|
||||
__ast_struct! {
|
||||
@build_struct_decl
|
||||
{
|
||||
$(#[$attr])*
|
||||
$name
|
||||
[ ]
|
||||
}
|
||||
{ $($fields)* }
|
||||
}
|
||||
|
||||
impl<'a> $crate::Parse<'a> for $name {
|
||||
fn parse(input: &'a str) -> $crate::IResult<&'a str, Self> {
|
||||
__ast_struct! {
|
||||
@build_parser
|
||||
{ input, }
|
||||
{ $($fields)* }
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
(
|
||||
@launch_pad
|
||||
$(#[$attr:meta])*
|
||||
$name:ident
|
||||
[ 'a ]
|
||||
[ ]
|
||||
{ $($fields:tt)* }
|
||||
) => {
|
||||
__ast_struct! {
|
||||
@build_struct_decl
|
||||
{
|
||||
$(#[$attr])*
|
||||
$name
|
||||
[ 'a ]
|
||||
}
|
||||
{ $($fields)* }
|
||||
}
|
||||
|
||||
impl<'a> $crate::Parse<'a> for $name<'a> {
|
||||
fn parse(input: &'a str) -> $crate::IResult<&'a str, Self> {
|
||||
__ast_struct! {
|
||||
@build_parser
|
||||
{ input, }
|
||||
{ $($fields)* }
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
(
|
||||
@launch_pad
|
||||
$(#[$attr:meta])*
|
||||
$name:ident
|
||||
[$($generics:ident)+]
|
||||
[$($bounds:tt)+]
|
||||
{ $($fields:tt)* }
|
||||
) => {
|
||||
__ast_struct! {
|
||||
@build_struct_decl
|
||||
{
|
||||
$(#[$attr])*
|
||||
$name
|
||||
[$($generics),+]
|
||||
}
|
||||
{ $($fields)* }
|
||||
}
|
||||
|
||||
impl<'a, $($generics),+> $crate::Parse<'a> for $name<$($generics),+> where $($bounds)+ {
|
||||
fn parse(input: &'a str) -> $crate::IResult<&'a str, Self> {
|
||||
__ast_struct! {
|
||||
@build_parser
|
||||
{ input, }
|
||||
{ $($fields)* }
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! __ast_enum {
|
||||
(@build_enum_decl
|
||||
{
|
||||
$(#[$attr:meta])*
|
||||
$name:ident
|
||||
[ $($maybe_a:tt)* ]
|
||||
$($variant:ident($member:ty))*
|
||||
}
|
||||
{ }
|
||||
) => (
|
||||
$(#[$attr])*
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub enum $name<$($maybe_a)*> {
|
||||
$($variant($member),)*
|
||||
}
|
||||
);
|
||||
(@build_enum_decl
|
||||
{ $($prev:tt)* }
|
||||
{ $variant:ident($member:ty), $($rest:tt)* }
|
||||
) => (
|
||||
__ast_enum! {
|
||||
@build_enum_decl
|
||||
{ $($prev)* $variant($member) }
|
||||
{ $($rest)* }
|
||||
}
|
||||
);
|
||||
(@build_enum_decl
|
||||
{ $($prev:tt)* }
|
||||
{ $(#[$attr:meta])* $variant:ident( $($member:tt)* ), $($rest:tt)* }
|
||||
) => (
|
||||
__ast_enum! {
|
||||
@build_enum_decl
|
||||
{ $($prev)* $variant(ast_types! { @extract_type $($member)* }) }
|
||||
{ $($rest)* }
|
||||
}
|
||||
);
|
||||
|
||||
(@build_sub_types { }) => ();
|
||||
(@build_sub_types
|
||||
{ $variant:ident($member:ty), $($rest:tt)* }
|
||||
) => (
|
||||
__ast_enum! {
|
||||
@build_sub_types
|
||||
{ $($rest)* }
|
||||
}
|
||||
);
|
||||
(@build_sub_types
|
||||
{ $(#[$attr:meta])* $variant:ident( $($member:tt)* ), $($rest:tt)* }
|
||||
) => (
|
||||
ast_types! {
|
||||
$(#[$attr])*
|
||||
$($member)*
|
||||
}
|
||||
__ast_enum! {
|
||||
@build_sub_types
|
||||
{ $($rest)* }
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
(@build_conversions $name:ident [ $($maybe_a:tt)* ] { }) => ();
|
||||
(@build_conversions
|
||||
$name:ident
|
||||
[ $($maybe_a:tt)* ]
|
||||
{ $variant:ident($member:ty), $($rest:tt)* }
|
||||
) => (
|
||||
impl<$($maybe_a)*> From<$member> for $name<$($maybe_a)*> {
|
||||
fn from(x: $member) -> Self {
|
||||
$name::$variant(x)
|
||||
}
|
||||
}
|
||||
__ast_enum! {
|
||||
@build_conversions
|
||||
$name
|
||||
[ $($maybe_a)* ]
|
||||
{ $($rest)* }
|
||||
}
|
||||
);
|
||||
(@build_conversions
|
||||
$name:ident
|
||||
[ $($maybe_a:tt)* ]
|
||||
{ $(#[$attr:meta])* $variant:ident( $($member:tt)* ), $($rest:tt)* }
|
||||
) => (
|
||||
__ast_enum! {
|
||||
@build_conversions
|
||||
$name
|
||||
[ $($maybe_a)* ]
|
||||
{ $variant(ast_types! { @extract_type $($member)* }), $($rest)* }
|
||||
}
|
||||
);
|
||||
|
||||
(@build_parse
|
||||
{ $name:ident [ $($maybe_a:tt)* ] $($member:ty)* }
|
||||
{ }
|
||||
) => (
|
||||
impl<'a> $crate::Parse<'a> for $name<$($maybe_a)*> {
|
||||
parser!(alt!(
|
||||
$(weedle!($member) => {From::from})|*
|
||||
));
|
||||
}
|
||||
);
|
||||
(@build_parse
|
||||
{ $($prev:tt)* }
|
||||
{ $variant:ident($member:ty), $($rest:tt)* }
|
||||
) => (
|
||||
__ast_enum! {
|
||||
@build_parse
|
||||
{ $($prev)* $member }
|
||||
{ $($rest)* }
|
||||
}
|
||||
);
|
||||
(@build_parse
|
||||
{ $($prev:tt)* }
|
||||
{ $(#[$attr:meta])* $variant:ident( $($member:tt)* ), $($rest:tt)* }
|
||||
) => (
|
||||
__ast_enum! {
|
||||
@build_parse
|
||||
{ $($prev)* ast_types! { @extract_type $($member)* } }
|
||||
{ $($rest)* }
|
||||
}
|
||||
);
|
||||
|
||||
(@launch_pad
|
||||
$(#[$attr:meta])*
|
||||
$name:ident
|
||||
[ $($maybe_a:tt)* ]
|
||||
{ $($variants:tt)* }
|
||||
) => (
|
||||
__ast_enum! {
|
||||
@build_enum_decl
|
||||
{ $(#[$attr])* $name [ $($maybe_a)* ] }
|
||||
{ $($variants)* }
|
||||
}
|
||||
|
||||
__ast_enum! {
|
||||
@build_sub_types
|
||||
{ $($variants)* }
|
||||
}
|
||||
|
||||
__ast_enum! {
|
||||
@build_conversions
|
||||
$name
|
||||
[ $($maybe_a)* ]
|
||||
{ $($variants)* }
|
||||
}
|
||||
|
||||
__ast_enum! {
|
||||
@build_parse
|
||||
{ $name [ $($maybe_a)* ] }
|
||||
{ $($variants)* }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
macro_rules! test {
|
||||
(@arg $parsed:ident) => {};
|
||||
(@arg $parsed:ident $($lhs:tt).+ == $rhs:expr; $($rest:tt)*) => {
|
||||
assert_eq!($parsed.$($lhs).+, $rhs);
|
||||
test!(@arg $parsed $($rest)*);
|
||||
};
|
||||
(@arg $parsed:ident $($lhs:tt).+(); $($rest:tt)*) => {
|
||||
assert!($parsed.$($lhs).+());
|
||||
test!(@arg $parsed $($rest)*);
|
||||
};
|
||||
(@arg $parsed:ident $($lhs:tt).+() == $rhs:expr; $($rest:tt)*) => {
|
||||
assert_eq!($parsed.$($lhs).+(), $rhs);
|
||||
test!(@arg $parsed $($rest)*);
|
||||
};
|
||||
(err $name:ident { $raw:expr => $typ:ty }) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
<$typ>::parse($raw).unwrap_err();
|
||||
}
|
||||
};
|
||||
($name:ident { $raw:expr => $rem:expr; $typ:ty => $val:expr }) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
let (rem, parsed) = <$typ>::parse($raw).unwrap();
|
||||
assert_eq!(rem, $rem);
|
||||
assert_eq!(parsed, $val);
|
||||
}
|
||||
};
|
||||
($name:ident { $raw:expr => $rem:expr; $typ:ty; $($body:tt)* }) => {
|
||||
#[test]
|
||||
fn $name() {
|
||||
let (_rem, _parsed) = <$typ>::parse($raw).unwrap();
|
||||
assert_eq!(_rem, $rem);
|
||||
test!(@arg _parsed $($body)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
macro_rules! test_variants {
|
||||
($struct_:ident { $( $variant:ident == $value:expr ),* $(,)* }) => {
|
||||
#[allow(non_snake_case)]
|
||||
mod $struct_ {
|
||||
$(
|
||||
mod $variant {
|
||||
use $crate::types::*;
|
||||
#[test]
|
||||
fn should_parse() {
|
||||
let (rem, parsed) = $struct_::parse($value).unwrap();
|
||||
assert_eq!(rem, "");
|
||||
match parsed {
|
||||
$struct_::$variant(_) => {},
|
||||
_ => { panic!("Failed to parse"); }
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
use crate::argument::ArgumentList;
|
||||
use crate::attribute::ExtendedAttributeList;
|
||||
use crate::common::{Identifier, Parenthesized};
|
||||
use crate::interface::{ConstMember, StringifierMember};
|
||||
use crate::types::{AttributedType, ReturnType};
|
||||
|
||||
/// Parses the members declarations of a mixin
|
||||
pub type MixinMembers<'a> = Vec<MixinMember<'a>>;
|
||||
|
||||
ast_types! {
|
||||
/// Parses one of the variants of a mixin member
|
||||
enum MixinMember<'a> {
|
||||
Const(ConstMember<'a>),
|
||||
/// Parses `[attributes]? stringifier? returntype identifier? (( args ));`
|
||||
///
|
||||
/// (( )) means ( ) chars
|
||||
Operation(struct OperationMixinMember<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
stringifier: Option<term!(stringifier)>,
|
||||
return_type: ReturnType<'a>,
|
||||
identifier: Option<Identifier<'a>>,
|
||||
args: Parenthesized<ArgumentList<'a>>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
/// Parses `[attributes]? stringifier? readonly? attribute attributedtype identifier;`
|
||||
Attribute(struct AttributeMixinMember<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
stringifier: Option<term!(stringifier)>,
|
||||
readonly: Option<term!(readonly)>,
|
||||
attribute: term!(attribute),
|
||||
type_: AttributedType<'a>,
|
||||
identifier: Identifier<'a>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
Stringifier(StringifierMember<'a>),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::Parse;
|
||||
|
||||
test!(should_parse_attribute_mixin_member { "stringifier readonly attribute short name;" =>
|
||||
"";
|
||||
AttributeMixinMember;
|
||||
attributes.is_none();
|
||||
stringifier.is_some();
|
||||
readonly.is_some();
|
||||
identifier.0 == "name";
|
||||
});
|
||||
|
||||
test!(should_parse_operation_mixin_member { "short fnName(long a);" =>
|
||||
"";
|
||||
OperationMixinMember;
|
||||
attributes.is_none();
|
||||
stringifier.is_none();
|
||||
identifier.is_some();
|
||||
});
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
use crate::argument::ArgumentList;
|
||||
use crate::attribute::ExtendedAttributeList;
|
||||
use crate::common::{Identifier, Parenthesized};
|
||||
use crate::types::{AttributedType, ReturnType};
|
||||
|
||||
/// Parses namespace members declaration
|
||||
pub type NamespaceMembers<'a> = Vec<NamespaceMember<'a>>;
|
||||
|
||||
ast_types! {
|
||||
/// Parses namespace member declaration
|
||||
enum NamespaceMember<'a> {
|
||||
/// Parses `[attributes]? returntype identifier? (( args ));`
|
||||
///
|
||||
/// (( )) means ( ) chars
|
||||
Operation(struct OperationNamespaceMember<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
return_type: ReturnType<'a>,
|
||||
identifier: Option<Identifier<'a>>,
|
||||
args: Parenthesized<ArgumentList<'a>>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
/// Parses `[attribute]? readonly attributetype type identifier;`
|
||||
Attribute(struct AttributeNamespaceMember<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
readonly: term!(readonly),
|
||||
attribute: term!(attribute),
|
||||
type_: AttributedType<'a>,
|
||||
identifier: Identifier<'a>,
|
||||
semi_colon: term!(;),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::Parse;
|
||||
|
||||
test!(should_parse_attribute_namespace_member { "readonly attribute short name;" =>
|
||||
"";
|
||||
AttributeNamespaceMember;
|
||||
attributes.is_none();
|
||||
identifier.0 == "name";
|
||||
});
|
||||
|
||||
test!(should_parse_operation_namespace_member { "short (long a, long b);" =>
|
||||
"";
|
||||
OperationNamespaceMember;
|
||||
attributes.is_none();
|
||||
identifier.is_none();
|
||||
});
|
||||
}
|
|
@ -1,690 +0,0 @@
|
|||
macro_rules! generate_terms {
|
||||
($( $(#[$attr:meta])* $typ:ident => $tok:expr ),*) => {
|
||||
$(
|
||||
$(#[$attr])*
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct $typ;
|
||||
|
||||
impl<'a> $crate::Parse<'a> for $typ {
|
||||
parser!(do_parse!(
|
||||
ws!(tag!($tok)) >>
|
||||
($typ)
|
||||
));
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! ident_tag (
|
||||
($i:expr, $tok:expr) => (
|
||||
{
|
||||
match tag!($i, $tok) {
|
||||
Err(e) => Err(e),
|
||||
Ok((i, o)) => {
|
||||
use nom::{character::is_alphanumeric, Err as NomErr, error::ErrorKind};
|
||||
let mut res = Ok((i, o));
|
||||
if let Some(&c) = i.as_bytes().first() {
|
||||
if is_alphanumeric(c) || c == b'_' || c == b'-' {
|
||||
res = Err(NomErr::Error(($i, ErrorKind::Tag)));
|
||||
}
|
||||
}
|
||||
res
|
||||
},
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
macro_rules! generate_terms_for_names {
|
||||
($( $(#[$attr:meta])* $typ:ident => $tok:expr,)*) => {
|
||||
$(
|
||||
$(#[$attr])*
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct $typ;
|
||||
|
||||
impl<'a> $crate::Parse<'a> for $typ {
|
||||
parser!(do_parse!(
|
||||
ws!(ident_tag!($tok)) >>
|
||||
($typ)
|
||||
));
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
generate_terms! {
|
||||
/// Represents the terminal symbol `(`
|
||||
OpenParen => "(",
|
||||
|
||||
/// Represents the terminal symbol `)`
|
||||
CloseParen => ")",
|
||||
|
||||
/// Represents the terminal symbol `[`
|
||||
OpenBracket => "[",
|
||||
|
||||
/// Represents the terminal symbol `]`
|
||||
CloseBracket => "]",
|
||||
|
||||
/// Represents the terminal symbol `{`
|
||||
OpenBrace => "{",
|
||||
|
||||
/// Represents the terminal symbol `}`
|
||||
CloseBrace => "}",
|
||||
|
||||
/// Represents the terminal symbol `,`
|
||||
Comma => ",",
|
||||
|
||||
/// Represents the terminal symbol `-`
|
||||
Minus => "-",
|
||||
|
||||
/// Represents the terminal symbol `.`
|
||||
Dot => ".",
|
||||
|
||||
/// Represents the terminal symbol `...`
|
||||
Ellipsis => "...",
|
||||
|
||||
/// Represents the terminal symbol `:`
|
||||
Colon => ":",
|
||||
|
||||
/// Represents the terminal symbol `;`
|
||||
SemiColon => ";",
|
||||
|
||||
/// Represents the terminal symbol `<`
|
||||
LessThan => "<",
|
||||
|
||||
/// Represents the terminal symbol `=`
|
||||
Assign => "=",
|
||||
|
||||
/// Represents the terminal symbol `>`
|
||||
GreaterThan => ">",
|
||||
|
||||
/// Represents the terminal symbol `?`
|
||||
QMark => "?"
|
||||
}
|
||||
|
||||
generate_terms_for_names! {
|
||||
/// Represents the terminal symbol `or`
|
||||
Or => "or",
|
||||
|
||||
/// Represents the terminal symbol `optional`
|
||||
Optional => "optional",
|
||||
|
||||
/// Represents the terminal symbol `attribute`
|
||||
Attribute => "attribute",
|
||||
|
||||
/// Represents the terminal symbol `callback`
|
||||
Callback => "callback",
|
||||
|
||||
/// Represents the terminal symbol `const`
|
||||
Const => "const",
|
||||
|
||||
/// Represents the terminal symbol `deleter`
|
||||
Deleter => "deleter",
|
||||
|
||||
/// Represents the terminal symbol `dictionary`
|
||||
Dictionary => "dictionary",
|
||||
|
||||
/// Represents the terminal symbol `enum`
|
||||
Enum => "enum",
|
||||
|
||||
/// Represents the terminal symbol `getter`
|
||||
Getter => "getter",
|
||||
|
||||
/// Represents the terminal symbol `includes`
|
||||
Includes => "includes",
|
||||
|
||||
/// Represents the terminal symbol `inherit`
|
||||
Inherit => "inherit",
|
||||
|
||||
/// Represents the terminal symbol `interface`
|
||||
Interface => "interface",
|
||||
|
||||
/// Represents the terminal symbol `iterable`
|
||||
Iterable => "iterable",
|
||||
|
||||
/// Represents the terminal symbol `maplike`
|
||||
Maplike => "maplike",
|
||||
|
||||
/// Represents the terminal symbol `namespace`
|
||||
Namespace => "namespace",
|
||||
|
||||
/// Represents the terminal symbol `partial`
|
||||
Partial => "partial",
|
||||
|
||||
/// Represents the terminal symbol `required`
|
||||
Required => "required",
|
||||
|
||||
/// Represents the terminal symbol `setlike`
|
||||
Setlike => "setlike",
|
||||
|
||||
/// Represents the terminal symbol `setter`
|
||||
Setter => "setter",
|
||||
|
||||
/// Represents the terminal symbol `static`
|
||||
Static => "static",
|
||||
|
||||
/// Represents the terminal symbol `stringifier`
|
||||
Stringifier => "stringifier",
|
||||
|
||||
/// Represents the terminal symbol `typedef`
|
||||
Typedef => "typedef",
|
||||
|
||||
/// Represents the terminal symbol `unrestricted`
|
||||
Unrestricted => "unrestricted",
|
||||
|
||||
/// Represents the terminal symbol `symbol`
|
||||
Symbol => "symbol",
|
||||
|
||||
/// Represents the terminal symbol `Infinity`
|
||||
NegInfinity => "-Infinity",
|
||||
|
||||
/// Represents the terminal symbol `ByteString`
|
||||
ByteString => "ByteString",
|
||||
|
||||
/// Represents the terminal symbol `DOMString`
|
||||
DOMString => "DOMString",
|
||||
|
||||
/// Represents the terminal symbol `FrozenArray`
|
||||
FrozenArray => "FrozenArray",
|
||||
|
||||
/// Represents the terminal symbol `Infinity`
|
||||
Infinity => "Infinity",
|
||||
|
||||
/// Represents the terminal symbol `NaN`
|
||||
NaN => "NaN",
|
||||
|
||||
/// Represents the terminal symbol `USVString`
|
||||
USVString => "USVString",
|
||||
|
||||
/// Represents the terminal symbol `any`
|
||||
Any => "any",
|
||||
|
||||
/// Represents the terminal symbol `boolean`
|
||||
Boolean => "boolean",
|
||||
|
||||
/// Represents the terminal symbol `byte`
|
||||
Byte => "byte",
|
||||
|
||||
/// Represents the terminal symbol `double`
|
||||
Double => "double",
|
||||
|
||||
/// Represents the terminal symbol `false`
|
||||
False => "false",
|
||||
|
||||
/// Represents the terminal symbol `float`
|
||||
Float => "float",
|
||||
|
||||
/// Represents the terminal symbol `long`
|
||||
Long => "long",
|
||||
|
||||
/// Represents the terminal symbol `null`
|
||||
Null => "null",
|
||||
|
||||
/// Represents the terminal symbol `object`
|
||||
Object => "object",
|
||||
|
||||
/// Represents the terminal symbol `octet`
|
||||
Octet => "octet",
|
||||
|
||||
/// Represents the terminal symbol `sequence`
|
||||
Sequence => "sequence",
|
||||
|
||||
/// Represents the terminal symbol `short`
|
||||
Short => "short",
|
||||
|
||||
/// Represents the terminal symbol `true`
|
||||
True => "true",
|
||||
|
||||
/// Represents the terminal symbol `unsigned`
|
||||
Unsigned => "unsigned",
|
||||
|
||||
/// Represents the terminal symbol `void`
|
||||
Void => "void",
|
||||
|
||||
/// Represents the terminal symbol `record`
|
||||
Record => "record",
|
||||
|
||||
/// Represents the terminal symbol `ArrayBuffer`
|
||||
ArrayBuffer => "ArrayBuffer",
|
||||
|
||||
/// Represents the terminal symbol `DataView`
|
||||
DataView => "DataView",
|
||||
|
||||
/// Represents the terminal symbol `Int8Array`
|
||||
Int8Array => "Int8Array",
|
||||
|
||||
/// Represents the terminal symbol `Int16Array`
|
||||
Int16Array => "Int16Array",
|
||||
|
||||
/// Represents the terminal symbol `Int32Array`
|
||||
Int32Array => "Int32Array",
|
||||
|
||||
/// Represents the terminal symbol `Uint8Array`
|
||||
Uint8Array => "Uint8Array",
|
||||
|
||||
/// Represents the terminal symbol `Uint16Array`
|
||||
Uint16Array => "Uint16Array",
|
||||
|
||||
/// Represents the terminal symbol `Uint32Array`
|
||||
Uint32Array => "Uint32Array",
|
||||
|
||||
/// Represents the terminal symbol `Uint8ClampedArray`
|
||||
Uint8ClampedArray => "Uint8ClampedArray",
|
||||
|
||||
/// Represents the terminal symbol `Float32Array`
|
||||
Float32Array => "Float32Array",
|
||||
|
||||
/// Represents the terminal symbol `Float64Array`
|
||||
Float64Array => "Float64Array",
|
||||
|
||||
/// Represents the terminal symbol `ArrayBufferView`
|
||||
ArrayBufferView => "ArrayBufferView",
|
||||
|
||||
/// Represents the terminal symbol `BufferSource
|
||||
BufferSource => "BufferSource",
|
||||
|
||||
/// Represents the terminal symbol `Promise`
|
||||
Promise => "Promise",
|
||||
|
||||
/// Represents the terminal symbol `Error`
|
||||
Error => "Error",
|
||||
|
||||
/// Represents the terminal symbol `readonly`
|
||||
ReadOnly => "readonly",
|
||||
|
||||
/// Represents the terminal symbol `mixin`
|
||||
Mixin => "mixin",
|
||||
|
||||
/// Represents the terminal symbol `implements`
|
||||
Implements => "implements",
|
||||
|
||||
/// Represents the terminal symbol `legacycaller`
|
||||
LegacyCaller => "legacycaller",
|
||||
|
||||
/// Represents the terminal symbol `constructor`
|
||||
Constructor => "constructor",
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! term {
|
||||
(OpenParen) => {
|
||||
$crate::term::OpenParen
|
||||
};
|
||||
(CloseParen) => {
|
||||
$crate::term::CloseParen
|
||||
};
|
||||
(OpenBracket) => {
|
||||
$crate::term::OpenBracket
|
||||
};
|
||||
(CloseBracket) => {
|
||||
$crate::term::CloseBracket
|
||||
};
|
||||
(OpenBrace) => {
|
||||
$crate::term::OpenBrace
|
||||
};
|
||||
(CloseBrace) => {
|
||||
$crate::term::CloseBrace
|
||||
};
|
||||
(,) => {
|
||||
$crate::term::Comma
|
||||
};
|
||||
(-) => {
|
||||
$crate::term::Minus
|
||||
};
|
||||
(.) => {
|
||||
$crate::term::Dot
|
||||
};
|
||||
(...) => {
|
||||
$crate::term::Ellipsis
|
||||
};
|
||||
(:) => {
|
||||
$crate::term::Colon
|
||||
};
|
||||
(;) => {
|
||||
$crate::term::SemiColon
|
||||
};
|
||||
(<) => {
|
||||
$crate::term::LessThan
|
||||
};
|
||||
(=) => {
|
||||
$crate::term::Assign
|
||||
};
|
||||
(>) => {
|
||||
$crate::term::GreaterThan
|
||||
};
|
||||
(?) => {
|
||||
$crate::term::QMark
|
||||
};
|
||||
(or) => {
|
||||
$crate::term::Or
|
||||
};
|
||||
(optional) => {
|
||||
$crate::term::Optional
|
||||
};
|
||||
(attribute) => {
|
||||
$crate::term::Attribute
|
||||
};
|
||||
(callback) => {
|
||||
$crate::term::Callback
|
||||
};
|
||||
(const) => {
|
||||
$crate::term::Const
|
||||
};
|
||||
(deleter) => {
|
||||
$crate::term::Deleter
|
||||
};
|
||||
(dictionary) => {
|
||||
$crate::term::Dictionary
|
||||
};
|
||||
(enum) => {
|
||||
$crate::term::Enum
|
||||
};
|
||||
(getter) => {
|
||||
$crate::term::Getter
|
||||
};
|
||||
(includes) => {
|
||||
$crate::term::Includes
|
||||
};
|
||||
(inherit) => {
|
||||
$crate::term::Inherit
|
||||
};
|
||||
(interface) => {
|
||||
$crate::term::Interface
|
||||
};
|
||||
(iterable) => {
|
||||
$crate::term::Iterable
|
||||
};
|
||||
(maplike) => {
|
||||
$crate::term::Maplike
|
||||
};
|
||||
(namespace) => {
|
||||
$crate::term::Namespace
|
||||
};
|
||||
(partial) => {
|
||||
$crate::term::Partial
|
||||
};
|
||||
(required) => {
|
||||
$crate::term::Required
|
||||
};
|
||||
(setlike) => {
|
||||
$crate::term::Setlike
|
||||
};
|
||||
(setter) => {
|
||||
$crate::term::Setter
|
||||
};
|
||||
(static) => {
|
||||
$crate::term::Static
|
||||
};
|
||||
(stringifier) => {
|
||||
$crate::term::Stringifier
|
||||
};
|
||||
(typedef) => {
|
||||
$crate::term::Typedef
|
||||
};
|
||||
(unrestricted) => {
|
||||
$crate::term::Unrestricted
|
||||
};
|
||||
(symbol) => {
|
||||
$crate::term::Symbol
|
||||
};
|
||||
(- Infinity) => {
|
||||
$crate::term::NegInfinity
|
||||
};
|
||||
(ByteString) => {
|
||||
$crate::term::ByteString
|
||||
};
|
||||
(DOMString) => {
|
||||
$crate::term::DOMString
|
||||
};
|
||||
(FrozenArray) => {
|
||||
$crate::term::FrozenArray
|
||||
};
|
||||
(Infinity) => {
|
||||
$crate::term::Infinity
|
||||
};
|
||||
(NaN) => {
|
||||
$crate::term::NaN
|
||||
};
|
||||
(USVString) => {
|
||||
$crate::term::USVString
|
||||
};
|
||||
(any) => {
|
||||
$crate::term::Any
|
||||
};
|
||||
(boolean) => {
|
||||
$crate::term::Boolean
|
||||
};
|
||||
(byte) => {
|
||||
$crate::term::Byte
|
||||
};
|
||||
(double) => {
|
||||
$crate::term::Double
|
||||
};
|
||||
(false) => {
|
||||
$crate::term::False
|
||||
};
|
||||
(float) => {
|
||||
$crate::term::Float
|
||||
};
|
||||
(long) => {
|
||||
$crate::term::Long
|
||||
};
|
||||
(null) => {
|
||||
$crate::term::Null
|
||||
};
|
||||
(object) => {
|
||||
$crate::term::Object
|
||||
};
|
||||
(octet) => {
|
||||
$crate::term::Octet
|
||||
};
|
||||
(sequence) => {
|
||||
$crate::term::Sequence
|
||||
};
|
||||
(short) => {
|
||||
$crate::term::Short
|
||||
};
|
||||
(true) => {
|
||||
$crate::term::True
|
||||
};
|
||||
(unsigned) => {
|
||||
$crate::term::Unsigned
|
||||
};
|
||||
(void) => {
|
||||
$crate::term::Void
|
||||
};
|
||||
(record) => {
|
||||
$crate::term::Record
|
||||
};
|
||||
(ArrayBuffer) => {
|
||||
$crate::term::ArrayBuffer
|
||||
};
|
||||
(DataView) => {
|
||||
$crate::term::DataView
|
||||
};
|
||||
(Int8Array) => {
|
||||
$crate::term::Int8Array
|
||||
};
|
||||
(Int16Array) => {
|
||||
$crate::term::Int16Array
|
||||
};
|
||||
(Int32Array) => {
|
||||
$crate::term::Int32Array
|
||||
};
|
||||
(Uint8Array) => {
|
||||
$crate::term::Uint8Array
|
||||
};
|
||||
(Uint16Array) => {
|
||||
$crate::term::Uint16Array
|
||||
};
|
||||
(Uint32Array) => {
|
||||
$crate::term::Uint32Array
|
||||
};
|
||||
(Uint8ClampedArray) => {
|
||||
$crate::term::Uint8ClampedArray
|
||||
};
|
||||
(Float32Array) => {
|
||||
$crate::term::Float32Array
|
||||
};
|
||||
(Float64Array) => {
|
||||
$crate::term::Float64Array
|
||||
};
|
||||
(ArrayBufferView) => {
|
||||
$crate::term::ArrayBufferView
|
||||
};
|
||||
(BufferSource) => {
|
||||
$crate::term::BufferSource
|
||||
};
|
||||
(Promise) => {
|
||||
$crate::term::Promise
|
||||
};
|
||||
(Error) => {
|
||||
$crate::term::Error
|
||||
};
|
||||
(readonly) => {
|
||||
$crate::term::ReadOnly
|
||||
};
|
||||
(mixin) => {
|
||||
$crate::term::Mixin
|
||||
};
|
||||
(implements) => {
|
||||
$crate::term::Implements
|
||||
};
|
||||
(legacycaller) => {
|
||||
$crate::term::LegacyCaller
|
||||
};
|
||||
(constructor) => {
|
||||
$crate::term::Constructor
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
macro_rules! generate_tests {
|
||||
($($m:ident, $typ:ident, $string:expr;)*) => {
|
||||
$(
|
||||
mod $m {
|
||||
use super::super::$typ;
|
||||
use crate::Parse;
|
||||
|
||||
#[test]
|
||||
fn should_parse() {
|
||||
let (rem, parsed) = $typ::parse(concat!($string)).unwrap();
|
||||
assert_eq!(rem, "");
|
||||
assert_eq!(parsed, $typ);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_parse_with_preceding_spaces() {
|
||||
let (rem, parsed) = $typ::parse(concat!(" ", $string)).unwrap();
|
||||
assert_eq!(rem, "");
|
||||
assert_eq!(parsed, $typ);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_parse_with_succeeding_spaces() {
|
||||
let (rem, parsed) = $typ::parse(concat!($string, " ")).unwrap();
|
||||
assert_eq!(rem, "");
|
||||
assert_eq!(parsed, $typ);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_parse_with_surrounding_spaces() {
|
||||
let (rem, parsed) = $typ::parse(concat!(" ", $string, " ")).unwrap();
|
||||
assert_eq!(rem, "");
|
||||
assert_eq!(parsed, $typ);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_parse_if_anything_next() {
|
||||
let (rem, parsed) = $typ::parse(concat!($string, " anything")).unwrap();
|
||||
assert_eq!(rem, "anything");
|
||||
assert_eq!(parsed, $typ);
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
generate_tests![
|
||||
openparen, OpenParen, "(";
|
||||
closeparen, CloseParen, ")";
|
||||
openbracket, OpenBracket, "[";
|
||||
closebracket, CloseBracket, "]";
|
||||
openbrace, OpenBrace, "{";
|
||||
closebrace, CloseBrace, "}";
|
||||
comma, Comma, ",";
|
||||
minus, Minus, "-";
|
||||
dot, Dot, ".";
|
||||
ellipsis, Ellipsis, "...";
|
||||
colon, Colon, ":";
|
||||
semicolon, SemiColon, ";";
|
||||
lessthan, LessThan, "<";
|
||||
assign, Assign, "=";
|
||||
greaterthan, GreaterThan, ">";
|
||||
qmark, QMark, "?";
|
||||
or, Or, "or";
|
||||
optional, Optional, "optional";
|
||||
attribute, Attribute, "attribute";
|
||||
callback, Callback, "callback";
|
||||
const_, Const, "const";
|
||||
deleter, Deleter, "deleter";
|
||||
dictionary, Dictionary, "dictionary";
|
||||
enum_, Enum, "enum";
|
||||
getter, Getter, "getter";
|
||||
includes, Includes, "includes";
|
||||
inherit, Inherit, "inherit";
|
||||
interface, Interface, "interface";
|
||||
iterable, Iterable, "iterable";
|
||||
maplike, Maplike, "maplike";
|
||||
namespace, Namespace, "namespace";
|
||||
partial, Partial, "partial";
|
||||
required, Required, "required";
|
||||
setlike, Setlike, "setlike";
|
||||
setter, Setter, "setter";
|
||||
static_, Static, "static";
|
||||
stringifier, Stringifier, "stringifier";
|
||||
typedef, Typedef, "typedef";
|
||||
unrestricted, Unrestricted, "unrestricted";
|
||||
symbol, Symbol, "symbol";
|
||||
neginfinity, NegInfinity, "-Infinity";
|
||||
bytestring, ByteString, "ByteString";
|
||||
domstring, DOMString, "DOMString";
|
||||
frozenarray, FrozenArray, "FrozenArray";
|
||||
infinity, Infinity, "Infinity";
|
||||
nan, NaN, "NaN";
|
||||
usvstring, USVString, "USVString";
|
||||
any, Any, "any";
|
||||
boolean, Boolean, "boolean";
|
||||
byte, Byte, "byte";
|
||||
double, Double, "double";
|
||||
false_, False, "false";
|
||||
float, Float, "float";
|
||||
long, Long, "long";
|
||||
null, Null, "null";
|
||||
object, Object, "object";
|
||||
octet, Octet, "octet";
|
||||
sequence, Sequence, "sequence";
|
||||
short, Short, "short";
|
||||
true_, True, "true";
|
||||
unsigned, Unsigned, "unsigned";
|
||||
void, Void, "void";
|
||||
record, Record, "record";
|
||||
arraybuffer, ArrayBuffer, "ArrayBuffer";
|
||||
dataview, DataView, "DataView";
|
||||
int8array, Int8Array, "Int8Array";
|
||||
int16array, Int16Array, "Int16Array";
|
||||
int32array, Int32Array, "Int32Array";
|
||||
uint8array, Uint8Array, "Uint8Array";
|
||||
uint16array, Uint16Array, "Uint16Array";
|
||||
uint32array, Uint32Array, "Uint32Array";
|
||||
uint8clampedarray, Uint8ClampedArray, "Uint8ClampedArray";
|
||||
float32array, Float32Array, "Float32Array";
|
||||
float64array, Float64Array, "Float64Array";
|
||||
promise, Promise, "Promise";
|
||||
error, Error, "Error";
|
||||
implements, Implements, "implements";
|
||||
legacycaller, LegacyCaller, "legacycaller";
|
||||
constructor, Constructor, "constructor";
|
||||
];
|
||||
}
|
|
@ -1,380 +0,0 @@
|
|||
use crate::attribute::ExtendedAttributeList;
|
||||
use crate::common::{Generics, Identifier, Parenthesized, Punctuated};
|
||||
use crate::term;
|
||||
use crate::Parse;
|
||||
|
||||
/// Parses a union of types
|
||||
pub type UnionType<'a> = Parenthesized<Punctuated<UnionMemberType<'a>, term!(or)>>;
|
||||
|
||||
ast_types! {
|
||||
/// Parses either single type or a union type
|
||||
enum Type<'a> {
|
||||
/// Parses one of the single types
|
||||
Single(enum SingleType<'a> {
|
||||
Any(term!(any)),
|
||||
NonAny(NonAnyType<'a>),
|
||||
}),
|
||||
Union(MayBeNull<UnionType<'a>>),
|
||||
}
|
||||
|
||||
// Parses any single non-any type
|
||||
enum NonAnyType<'a> {
|
||||
Promise(PromiseType<'a>),
|
||||
Integer(MayBeNull<IntegerType>),
|
||||
FloatingPoint(MayBeNull<FloatingPointType>),
|
||||
Boolean(MayBeNull<term!(boolean)>),
|
||||
Byte(MayBeNull<term!(byte)>),
|
||||
Octet(MayBeNull<term!(octet)>),
|
||||
ByteString(MayBeNull<term!(ByteString)>),
|
||||
DOMString(MayBeNull<term!(DOMString)>),
|
||||
USVString(MayBeNull<term!(USVString)>),
|
||||
Sequence(MayBeNull<SequenceType<'a>>),
|
||||
Object(MayBeNull<term!(object)>),
|
||||
Symbol(MayBeNull<term!(symbol)>),
|
||||
Error(MayBeNull<term!(Error)>),
|
||||
ArrayBuffer(MayBeNull<term!(ArrayBuffer)>),
|
||||
DataView(MayBeNull<term!(DataView)>),
|
||||
Int8Array(MayBeNull<term!(Int8Array)>),
|
||||
Int16Array(MayBeNull<term!(Int16Array)>),
|
||||
Int32Array(MayBeNull<term!(Int32Array)>),
|
||||
Uint8Array(MayBeNull<term!(Uint8Array)>),
|
||||
Uint16Array(MayBeNull<term!(Uint16Array)>),
|
||||
Uint32Array(MayBeNull<term!(Uint32Array)>),
|
||||
Uint8ClampedArray(MayBeNull<term!(Uint8ClampedArray)>),
|
||||
Float32Array(MayBeNull<term!(Float32Array)>),
|
||||
Float64Array(MayBeNull<term!(Float64Array)>),
|
||||
ArrayBufferView(MayBeNull<term!(ArrayBufferView)>),
|
||||
BufferSource(MayBeNull<term!(BufferSource)>),
|
||||
FrozenArrayType(MayBeNull<FrozenArrayType<'a>>),
|
||||
RecordType(MayBeNull<RecordType<'a>>),
|
||||
Identifier(MayBeNull<Identifier<'a>>),
|
||||
}
|
||||
|
||||
/// Parses `sequence<Type>`
|
||||
struct SequenceType<'a> {
|
||||
sequence: term!(sequence),
|
||||
generics: Generics<Box<Type<'a>>>,
|
||||
}
|
||||
|
||||
/// Parses `FrozenArray<Type>`
|
||||
struct FrozenArrayType<'a> {
|
||||
frozen_array: term!(FrozenArray),
|
||||
generics: Generics<Box<Type<'a>>>,
|
||||
}
|
||||
|
||||
/// Parses a nullable type. Ex: `object | object??`
|
||||
///
|
||||
/// `??` means an actual ? not an optional requirement
|
||||
#[derive(Copy)]
|
||||
struct MayBeNull<T> where [T: Parse<'a>] {
|
||||
type_: T,
|
||||
q_mark: Option<term::QMark>,
|
||||
}
|
||||
|
||||
/// Parses a `Promise<Type|void>` type
|
||||
struct PromiseType<'a> {
|
||||
promise: term!(Promise),
|
||||
generics: Generics<Box<ReturnType<'a>>>,
|
||||
}
|
||||
|
||||
/// Parses `unsigned? short|long|(long long)`
|
||||
#[derive(Copy)]
|
||||
enum IntegerType {
|
||||
/// Parses `unsigned? long long`
|
||||
#[derive(Copy)]
|
||||
LongLong(struct LongLongType {
|
||||
unsigned: Option<term!(unsigned)>,
|
||||
long_long: (term!(long), term!(long)),
|
||||
}),
|
||||
/// Parses `unsigned? long`
|
||||
#[derive(Copy)]
|
||||
Long(struct LongType {
|
||||
unsigned: Option<term!(unsigned)>,
|
||||
long: term!(long),
|
||||
}),
|
||||
/// Parses `unsigned? short`
|
||||
#[derive(Copy)]
|
||||
Short(struct ShortType {
|
||||
unsigned: Option<term!(unsigned)>,
|
||||
short: term!(short),
|
||||
}),
|
||||
}
|
||||
|
||||
/// Parses `unrestricted? float|double`
|
||||
#[derive(Copy)]
|
||||
enum FloatingPointType {
|
||||
/// Parses `unrestricted? float`
|
||||
#[derive(Copy)]
|
||||
Float(struct FloatType {
|
||||
unrestricted: Option<term!(unrestricted)>,
|
||||
float: term!(float),
|
||||
}),
|
||||
/// Parses `unrestricted? double`
|
||||
#[derive(Copy)]
|
||||
Double(struct DoubleType {
|
||||
unrestricted: Option<term!(unrestricted)>,
|
||||
double: term!(double),
|
||||
}),
|
||||
}
|
||||
|
||||
/// Parses `record<StringType, Type>`
|
||||
struct RecordType<'a> {
|
||||
record: term!(record),
|
||||
generics: Generics<(StringType, term!(,), Box<Type<'a>>)>,
|
||||
}
|
||||
|
||||
/// Parses one of the string types `ByteString|DOMString|USVString`
|
||||
#[derive(Copy)]
|
||||
enum StringType {
|
||||
Byte(term!(ByteString)),
|
||||
DOM(term!(DOMString)),
|
||||
USV(term!(USVString)),
|
||||
}
|
||||
|
||||
/// Parses one of the member of a union type
|
||||
enum UnionMemberType<'a> {
|
||||
Single(AttributedNonAnyType<'a>),
|
||||
Union(MayBeNull<UnionType<'a>>),
|
||||
}
|
||||
|
||||
/// Parses a const type
|
||||
enum ConstType<'a> {
|
||||
Integer(MayBeNull<IntegerType>),
|
||||
FloatingPoint(MayBeNull<FloatingPointType>),
|
||||
Boolean(MayBeNull<term!(boolean)>),
|
||||
Byte(MayBeNull<term!(byte)>),
|
||||
Octet(MayBeNull<term!(octet)>),
|
||||
Identifier(MayBeNull<Identifier<'a>>),
|
||||
}
|
||||
|
||||
/// Parses the return type which may be `void` or any given Type
|
||||
enum ReturnType<'a> {
|
||||
Void(term!(void)),
|
||||
Type(Type<'a>),
|
||||
}
|
||||
|
||||
/// Parses `[attributes]? type`
|
||||
struct AttributedType<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
type_: Type<'a>,
|
||||
}
|
||||
|
||||
/// Parses `[attributes]? type` where the type is a single non-any type
|
||||
struct AttributedNonAnyType<'a> {
|
||||
attributes: Option<ExtendedAttributeList<'a>>,
|
||||
type_: NonAnyType<'a>,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
test!(should_parse_may_be_null { "short" =>
|
||||
"";
|
||||
MayBeNull<crate::types::IntegerType>;
|
||||
q_mark.is_none();
|
||||
});
|
||||
|
||||
test!(should_parse_nullable { "short?" =>
|
||||
"";
|
||||
MayBeNull<crate::types::IntegerType>;
|
||||
q_mark.is_some();
|
||||
});
|
||||
|
||||
test_variants!(
|
||||
ReturnType {
|
||||
Void == "void",
|
||||
Type == "any",
|
||||
}
|
||||
);
|
||||
|
||||
test_variants!(
|
||||
ConstType {
|
||||
Integer == "short",
|
||||
FloatingPoint == "float",
|
||||
Boolean == "boolean",
|
||||
Byte == "byte",
|
||||
Octet == "octet",
|
||||
Identifier == "name",
|
||||
}
|
||||
);
|
||||
|
||||
test_variants!(
|
||||
NonAnyType {
|
||||
Promise == "Promise<long>",
|
||||
Integer == "long",
|
||||
FloatingPoint == "float",
|
||||
Boolean == "boolean",
|
||||
Byte == "byte",
|
||||
Octet == "octet",
|
||||
ByteString == "ByteString",
|
||||
DOMString == "DOMString",
|
||||
USVString == "USVString",
|
||||
Sequence == "sequence<short>",
|
||||
Object == "object",
|
||||
Symbol == "symbol",
|
||||
Error == "Error",
|
||||
ArrayBuffer == "ArrayBuffer",
|
||||
DataView == "DataView",
|
||||
Int8Array == "Int8Array",
|
||||
Int16Array == "Int16Array",
|
||||
Int32Array == "Int32Array",
|
||||
Uint8Array == "Uint8Array",
|
||||
Uint16Array == "Uint16Array",
|
||||
Uint32Array == "Uint32Array",
|
||||
Uint8ClampedArray == "Uint8ClampedArray",
|
||||
Float32Array == "Float32Array",
|
||||
Float64Array == "Float64Array",
|
||||
ArrayBufferView == "ArrayBufferView",
|
||||
BufferSource == "BufferSource",
|
||||
FrozenArrayType == "FrozenArray<short>",
|
||||
RecordType == "record<DOMString, short>",
|
||||
Identifier == "mango"
|
||||
}
|
||||
);
|
||||
|
||||
test_variants!(
|
||||
UnionMemberType {
|
||||
Single == "byte",
|
||||
Union == "([Clamp] unsigned long or byte)"
|
||||
}
|
||||
);
|
||||
|
||||
test_variants!(
|
||||
StringType {
|
||||
DOM == "DOMString",
|
||||
USV == "USVString",
|
||||
Byte == "ByteString"
|
||||
}
|
||||
);
|
||||
|
||||
test!(should_parse_record_type { "record<DOMString, short>" =>
|
||||
"";
|
||||
RecordType;
|
||||
});
|
||||
|
||||
test!(should_parse_double_type { "double" =>
|
||||
"";
|
||||
DoubleType;
|
||||
});
|
||||
|
||||
test!(should_parse_float_type { "float" =>
|
||||
"";
|
||||
FloatType;
|
||||
});
|
||||
|
||||
test_variants!(
|
||||
FloatingPointType {
|
||||
Float == "float",
|
||||
Double == "double"
|
||||
}
|
||||
);
|
||||
|
||||
test!(should_parse_long_long_type { "long long" =>
|
||||
"";
|
||||
LongLongType;
|
||||
});
|
||||
|
||||
test!(should_parse_long_type { "long" =>
|
||||
"";
|
||||
LongType;
|
||||
});
|
||||
|
||||
test!(should_parse_short_type { "short" =>
|
||||
"";
|
||||
ShortType;
|
||||
});
|
||||
|
||||
test_variants!(
|
||||
IntegerType {
|
||||
Short == "short",
|
||||
Long == "long",
|
||||
LongLong == "long long"
|
||||
}
|
||||
);
|
||||
|
||||
test!(should_parse_promise_type { "Promise<short>" =>
|
||||
"";
|
||||
PromiseType;
|
||||
});
|
||||
|
||||
test!(should_parse_frozen_array_type { "FrozenArray<short>" =>
|
||||
"";
|
||||
FrozenArrayType;
|
||||
});
|
||||
|
||||
test!(should_parse_sequence_type { "sequence<short>" =>
|
||||
"";
|
||||
SequenceType;
|
||||
});
|
||||
|
||||
test_variants!(
|
||||
SingleType {
|
||||
Any == "any",
|
||||
NonAny == "Promise<short>",
|
||||
}
|
||||
);
|
||||
|
||||
test_variants!(
|
||||
Type {
|
||||
Single == "short",
|
||||
Union == "(short or float)"
|
||||
}
|
||||
);
|
||||
|
||||
test!(should_parse_attributed_type { "[Named] short" =>
|
||||
"";
|
||||
AttributedType;
|
||||
attributes.is_some();
|
||||
});
|
||||
|
||||
test!(should_parse_type_as_identifier { "DOMStringMap" =>
|
||||
// if type is not parsed as identifier, it is parsed as `DOMString` and 'Map' is left
|
||||
"";
|
||||
crate::types::Type;
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn should_parse_union_member_type_attributed_union() {
|
||||
use crate::types::UnionMemberType;
|
||||
let (rem, parsed) = UnionMemberType::parse("([Clamp] byte or [Named] byte)").unwrap();
|
||||
assert_eq!(rem, "");
|
||||
match parsed {
|
||||
UnionMemberType::Union(MayBeNull {
|
||||
type_:
|
||||
Parenthesized {
|
||||
body: Punctuated { list, .. },
|
||||
..
|
||||
},
|
||||
..
|
||||
}) => {
|
||||
assert_eq!(list.len(), 2);
|
||||
|
||||
match list[0] {
|
||||
UnionMemberType::Single(AttributedNonAnyType { ref attributes, .. }) => {
|
||||
assert!(attributes.is_some());
|
||||
}
|
||||
|
||||
_ => {
|
||||
panic!("Failed to parse list[0] attributes");
|
||||
}
|
||||
};
|
||||
|
||||
match list[1] {
|
||||
UnionMemberType::Single(AttributedNonAnyType { ref attributes, .. }) => {
|
||||
assert!(attributes.is_some());
|
||||
}
|
||||
|
||||
_ => {
|
||||
panic!("Failed to parse list[1] attributes");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
_ => {
|
||||
panic!("Failed to parse");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
use crate::IResult;
|
||||
|
||||
pub(crate) fn sp(input: &str) -> IResult<&str, &str> {
|
||||
recognize!(
|
||||
input,
|
||||
many0!(alt!(
|
||||
// ignores line comments
|
||||
do_parse!(tag!("//") >> take_until!("\n") >> char!('\n') >> (()))
|
||||
|
|
||||
// ignores whitespace
|
||||
map!(take_while1!(|c| c == '\t' || c == '\n' || c == '\r' || c == ' '), |_| ())
|
||||
|
|
||||
// ignores block comments
|
||||
do_parse!(tag!("/*") >> take_until!("*/") >> tag!("*/") >> (()))
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
/// ws! also ignores line & block comments
|
||||
macro_rules! ws (
|
||||
($i:expr, $($args:tt)*) => ({
|
||||
use $crate::whitespace::sp;
|
||||
|
||||
do_parse!($i,
|
||||
sp >>
|
||||
s: $($args)* >>
|
||||
sp >>
|
||||
(s)
|
||||
)
|
||||
});
|
||||
);
|
|
@ -1,549 +0,0 @@
|
|||
[Constructor(DOMString type, optional EventInit eventInitDict),
|
||||
Exposed=(Window,Worker,AudioWorklet)]
|
||||
interface Event {
|
||||
readonly attribute DOMString type;
|
||||
readonly attribute EventTarget? target;
|
||||
readonly attribute EventTarget? srcElement; // historical
|
||||
readonly attribute EventTarget? currentTarget;
|
||||
sequence<EventTarget> composedPath();
|
||||
|
||||
const unsigned short NONE = 0;
|
||||
const unsigned short CAPTURING_PHASE = 1;
|
||||
const unsigned short AT_TARGET = 2;
|
||||
const unsigned short BUBBLING_PHASE = 3;
|
||||
readonly attribute unsigned short eventPhase;
|
||||
|
||||
void stopPropagation();
|
||||
attribute boolean cancelBubble; // historical alias of .stopPropagation
|
||||
void stopImmediatePropagation();
|
||||
|
||||
readonly attribute boolean bubbles;
|
||||
readonly attribute boolean cancelable;
|
||||
attribute boolean returnValue; // historical
|
||||
void preventDefault();
|
||||
readonly attribute boolean defaultPrevented;
|
||||
readonly attribute boolean composed;
|
||||
|
||||
[Unforgeable] readonly attribute boolean isTrusted;
|
||||
readonly attribute DOMHighResTimeStamp timeStamp;
|
||||
|
||||
void initEvent(DOMString type, optional boolean bubbles = false, optional boolean cancelable = false); // historical
|
||||
};
|
||||
|
||||
dictionary EventInit {
|
||||
boolean bubbles = false;
|
||||
boolean cancelable = false;
|
||||
boolean composed = false;
|
||||
};
|
||||
|
||||
[Constructor(DOMString type, optional CustomEventInit eventInitDict),
|
||||
Exposed=(Window,Worker)]
|
||||
interface CustomEvent : Event {
|
||||
readonly attribute any detail;
|
||||
|
||||
void initCustomEvent(DOMString type, optional boolean bubbles = false, optional boolean cancelable = false, optional any detail = null);
|
||||
};
|
||||
|
||||
dictionary CustomEventInit : EventInit {
|
||||
any detail = null;
|
||||
};
|
||||
|
||||
[Constructor,
|
||||
Exposed=(Window,Worker,AudioWorklet)]
|
||||
interface EventTarget {
|
||||
void addEventListener(DOMString type, EventListener? callback, optional (AddEventListenerOptions or boolean) options);
|
||||
void removeEventListener(DOMString type, EventListener? callback, optional (EventListenerOptions or boolean) options);
|
||||
boolean dispatchEvent(Event event);
|
||||
};
|
||||
|
||||
callback interface EventListener {
|
||||
void handleEvent(Event event);
|
||||
};
|
||||
|
||||
dictionary EventListenerOptions {
|
||||
boolean capture = false;
|
||||
};
|
||||
|
||||
dictionary AddEventListenerOptions : EventListenerOptions {
|
||||
boolean passive = false;
|
||||
boolean once = false;
|
||||
};
|
||||
|
||||
[Constructor,
|
||||
Exposed=(Window,Worker)]
|
||||
interface AbortController {
|
||||
[SameObject] readonly attribute AbortSignal signal;
|
||||
|
||||
void abort();
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
interface AbortSignal : EventTarget {
|
||||
readonly attribute boolean aborted;
|
||||
|
||||
attribute EventHandler onabort;
|
||||
};
|
||||
|
||||
interface mixin NonElementParentNode {
|
||||
Element? getElementById(DOMString elementId);
|
||||
};
|
||||
Document includes NonElementParentNode;
|
||||
DocumentFragment includes NonElementParentNode;
|
||||
|
||||
interface mixin DocumentOrShadowRoot {
|
||||
};
|
||||
Document includes DocumentOrShadowRoot;
|
||||
ShadowRoot includes DocumentOrShadowRoot;
|
||||
|
||||
interface mixin ParentNode {
|
||||
[SameObject] readonly attribute HTMLCollection children;
|
||||
readonly attribute Element? firstElementChild;
|
||||
readonly attribute Element? lastElementChild;
|
||||
readonly attribute unsigned long childElementCount;
|
||||
|
||||
[CEReactions, Unscopable] void prepend((Node or DOMString)... nodes);
|
||||
[CEReactions, Unscopable] void append((Node or DOMString)... nodes);
|
||||
|
||||
Element? querySelector(DOMString selectors);
|
||||
[NewObject] NodeList querySelectorAll(DOMString selectors);
|
||||
};
|
||||
Document includes ParentNode;
|
||||
DocumentFragment includes ParentNode;
|
||||
Element includes ParentNode;
|
||||
|
||||
interface mixin NonDocumentTypeChildNode {
|
||||
readonly attribute Element? previousElementSibling;
|
||||
readonly attribute Element? nextElementSibling;
|
||||
};
|
||||
Element includes NonDocumentTypeChildNode;
|
||||
CharacterData includes NonDocumentTypeChildNode;
|
||||
|
||||
interface mixin ChildNode {
|
||||
[CEReactions, Unscopable] void before((Node or DOMString)... nodes);
|
||||
[CEReactions, Unscopable] void after((Node or DOMString)... nodes);
|
||||
[CEReactions, Unscopable] void replaceWith((Node or DOMString)... nodes);
|
||||
[CEReactions, Unscopable] void remove();
|
||||
};
|
||||
DocumentType includes ChildNode;
|
||||
Element includes ChildNode;
|
||||
CharacterData includes ChildNode;
|
||||
|
||||
interface mixin Slotable {
|
||||
readonly attribute HTMLSlotElement? assignedSlot;
|
||||
};
|
||||
Element includes Slotable;
|
||||
Text includes Slotable;
|
||||
|
||||
[Exposed=Window]
|
||||
interface NodeList {
|
||||
getter Node? item(unsigned long index);
|
||||
readonly attribute unsigned long length;
|
||||
iterable<Node>;
|
||||
};
|
||||
|
||||
[Exposed=Window, LegacyUnenumerableNamedProperties]
|
||||
interface HTMLCollection {
|
||||
readonly attribute unsigned long length;
|
||||
getter Element? item(unsigned long index);
|
||||
getter Element? namedItem(DOMString name);
|
||||
};
|
||||
|
||||
[Constructor(MutationCallback callback),
|
||||
Exposed=Window]
|
||||
interface MutationObserver {
|
||||
void observe(Node target, optional MutationObserverInit options);
|
||||
void disconnect();
|
||||
sequence<MutationRecord> takeRecords();
|
||||
};
|
||||
|
||||
callback MutationCallback = void (sequence<MutationRecord> mutations, MutationObserver observer);
|
||||
|
||||
dictionary MutationObserverInit {
|
||||
boolean childList = false;
|
||||
boolean attributes;
|
||||
boolean characterData;
|
||||
boolean subtree = false;
|
||||
boolean attributeOldValue;
|
||||
boolean characterDataOldValue;
|
||||
sequence<DOMString> attributeFilter;
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
interface MutationRecord {
|
||||
readonly attribute DOMString type;
|
||||
[SameObject] readonly attribute Node target;
|
||||
[SameObject] readonly attribute NodeList addedNodes;
|
||||
[SameObject] readonly attribute NodeList removedNodes;
|
||||
readonly attribute Node? previousSibling;
|
||||
readonly attribute Node? nextSibling;
|
||||
readonly attribute DOMString? attributeName;
|
||||
readonly attribute DOMString? attributeNamespace;
|
||||
readonly attribute DOMString? oldValue;
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
interface Node : EventTarget {
|
||||
const unsigned short ELEMENT_NODE = 1;
|
||||
const unsigned short ATTRIBUTE_NODE = 2;
|
||||
const unsigned short TEXT_NODE = 3;
|
||||
const unsigned short CDATA_SECTION_NODE = 4;
|
||||
const unsigned short ENTITY_REFERENCE_NODE = 5; // historical
|
||||
const unsigned short ENTITY_NODE = 6; // historical
|
||||
const unsigned short PROCESSING_INSTRUCTION_NODE = 7;
|
||||
const unsigned short COMMENT_NODE = 8;
|
||||
const unsigned short DOCUMENT_NODE = 9;
|
||||
const unsigned short DOCUMENT_TYPE_NODE = 10;
|
||||
const unsigned short DOCUMENT_FRAGMENT_NODE = 11;
|
||||
const unsigned short NOTATION_NODE = 12; // historical
|
||||
readonly attribute unsigned short nodeType;
|
||||
readonly attribute DOMString nodeName;
|
||||
|
||||
readonly attribute USVString baseURI;
|
||||
|
||||
readonly attribute boolean isConnected;
|
||||
readonly attribute Document? ownerDocument;
|
||||
Node getRootNode(optional GetRootNodeOptions options);
|
||||
readonly attribute Node? parentNode;
|
||||
readonly attribute Element? parentElement;
|
||||
boolean hasChildNodes();
|
||||
[SameObject] readonly attribute NodeList childNodes;
|
||||
readonly attribute Node? firstChild;
|
||||
readonly attribute Node? lastChild;
|
||||
readonly attribute Node? previousSibling;
|
||||
readonly attribute Node? nextSibling;
|
||||
|
||||
[CEReactions] attribute DOMString? nodeValue;
|
||||
[CEReactions] attribute DOMString? textContent;
|
||||
[CEReactions] void normalize();
|
||||
|
||||
[CEReactions, NewObject] Node cloneNode(optional boolean deep = false);
|
||||
boolean isEqualNode(Node? otherNode);
|
||||
boolean isSameNode(Node? otherNode); // historical alias of ===
|
||||
|
||||
const unsigned short DOCUMENT_POSITION_DISCONNECTED = 0x01;
|
||||
const unsigned short DOCUMENT_POSITION_PRECEDING = 0x02;
|
||||
const unsigned short DOCUMENT_POSITION_FOLLOWING = 0x04;
|
||||
const unsigned short DOCUMENT_POSITION_CONTAINS = 0x08;
|
||||
const unsigned short DOCUMENT_POSITION_CONTAINED_BY = 0x10;
|
||||
const unsigned short DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20;
|
||||
unsigned short compareDocumentPosition(Node other);
|
||||
boolean contains(Node? other);
|
||||
|
||||
DOMString? lookupPrefix(DOMString? namespace);
|
||||
DOMString? lookupNamespaceURI(DOMString? prefix);
|
||||
boolean isDefaultNamespace(DOMString? namespace);
|
||||
|
||||
[CEReactions] Node insertBefore(Node node, Node? child);
|
||||
[CEReactions] Node appendChild(Node node);
|
||||
[CEReactions] Node replaceChild(Node node, Node child);
|
||||
[CEReactions] Node removeChild(Node child);
|
||||
};
|
||||
|
||||
dictionary GetRootNodeOptions {
|
||||
boolean composed = false;
|
||||
};
|
||||
|
||||
[Constructor,
|
||||
Exposed=Window]
|
||||
interface Document : Node {
|
||||
[SameObject] readonly attribute DOMImplementation implementation;
|
||||
readonly attribute USVString URL;
|
||||
readonly attribute USVString documentURI;
|
||||
readonly attribute USVString origin;
|
||||
readonly attribute DOMString compatMode;
|
||||
readonly attribute DOMString characterSet;
|
||||
readonly attribute DOMString charset; // historical alias of .characterSet
|
||||
readonly attribute DOMString inputEncoding; // historical alias of .characterSet
|
||||
readonly attribute DOMString contentType;
|
||||
|
||||
readonly attribute DocumentType? doctype;
|
||||
readonly attribute Element? documentElement;
|
||||
HTMLCollection getElementsByTagName(DOMString qualifiedName);
|
||||
HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName);
|
||||
HTMLCollection getElementsByClassName(DOMString classNames);
|
||||
|
||||
[CEReactions, NewObject] Element createElement(DOMString localName, optional (DOMString or ElementCreationOptions) options);
|
||||
[CEReactions, NewObject] Element createElementNS(DOMString? namespace, DOMString qualifiedName, optional (DOMString or ElementCreationOptions) options);
|
||||
[NewObject] DocumentFragment createDocumentFragment();
|
||||
[NewObject] Text createTextNode(DOMString data);
|
||||
[NewObject] CDATASection createCDATASection(DOMString data);
|
||||
[NewObject] Comment createComment(DOMString data);
|
||||
[NewObject] ProcessingInstruction createProcessingInstruction(DOMString target, DOMString data);
|
||||
|
||||
[CEReactions, NewObject] Node importNode(Node node, optional boolean deep = false);
|
||||
[CEReactions] Node adoptNode(Node node);
|
||||
|
||||
[NewObject] Attr createAttribute(DOMString localName);
|
||||
[NewObject] Attr createAttributeNS(DOMString? namespace, DOMString qualifiedName);
|
||||
|
||||
[NewObject] Event createEvent(DOMString interface);
|
||||
|
||||
[NewObject] Range createRange();
|
||||
|
||||
// NodeFilter.SHOW_ALL = 0xFFFFFFFF
|
||||
[NewObject] NodeIterator createNodeIterator(Node root, optional unsigned long whatToShow = 0xFFFFFFFF, optional NodeFilter? filter = null);
|
||||
[NewObject] TreeWalker createTreeWalker(Node root, optional unsigned long whatToShow = 0xFFFFFFFF, optional NodeFilter? filter = null);
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
interface XMLDocument : Document {};
|
||||
|
||||
dictionary ElementCreationOptions {
|
||||
DOMString is;
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
interface DOMImplementation {
|
||||
[NewObject] DocumentType createDocumentType(DOMString qualifiedName, DOMString publicId, DOMString systemId);
|
||||
[NewObject] XMLDocument createDocument(DOMString? namespace, [TreatNullAs=EmptyString] DOMString qualifiedName, optional DocumentType? doctype = null);
|
||||
[NewObject] Document createHTMLDocument(optional DOMString title);
|
||||
|
||||
boolean hasFeature(); // useless; always returns true
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
interface DocumentType : Node {
|
||||
readonly attribute DOMString name;
|
||||
readonly attribute DOMString publicId;
|
||||
readonly attribute DOMString systemId;
|
||||
};
|
||||
|
||||
[Constructor,
|
||||
Exposed=Window]
|
||||
interface DocumentFragment : Node {
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
interface ShadowRoot : DocumentFragment {
|
||||
readonly attribute ShadowRootMode mode;
|
||||
readonly attribute Element host;
|
||||
};
|
||||
|
||||
enum ShadowRootMode { "open", "closed" };
|
||||
|
||||
[Exposed=Window]
|
||||
interface Element : Node {
|
||||
readonly attribute DOMString? namespaceURI;
|
||||
readonly attribute DOMString? prefix;
|
||||
readonly attribute DOMString localName;
|
||||
readonly attribute DOMString tagName;
|
||||
|
||||
[CEReactions] attribute DOMString id;
|
||||
[CEReactions] attribute DOMString className;
|
||||
[SameObject, PutForwards=value] readonly attribute DOMTokenList classList;
|
||||
[CEReactions, Unscopable] attribute DOMString slot;
|
||||
|
||||
boolean hasAttributes();
|
||||
[SameObject] readonly attribute NamedNodeMap attributes;
|
||||
sequence<DOMString> getAttributeNames();
|
||||
DOMString? getAttribute(DOMString qualifiedName);
|
||||
DOMString? getAttributeNS(DOMString? namespace, DOMString localName);
|
||||
[CEReactions] void setAttribute(DOMString qualifiedName, DOMString value);
|
||||
[CEReactions] void setAttributeNS(DOMString? namespace, DOMString qualifiedName, DOMString value);
|
||||
[CEReactions] void removeAttribute(DOMString qualifiedName);
|
||||
[CEReactions] void removeAttributeNS(DOMString? namespace, DOMString localName);
|
||||
boolean hasAttribute(DOMString qualifiedName);
|
||||
boolean hasAttributeNS(DOMString? namespace, DOMString localName);
|
||||
|
||||
Attr? getAttributeNode(DOMString qualifiedName);
|
||||
Attr? getAttributeNodeNS(DOMString? namespace, DOMString localName);
|
||||
[CEReactions] Attr? setAttributeNode(Attr attr);
|
||||
[CEReactions] Attr? setAttributeNodeNS(Attr attr);
|
||||
[CEReactions] Attr removeAttributeNode(Attr attr);
|
||||
|
||||
ShadowRoot attachShadow(ShadowRootInit init);
|
||||
readonly attribute ShadowRoot? shadowRoot;
|
||||
|
||||
Element? closest(DOMString selectors);
|
||||
boolean matches(DOMString selectors);
|
||||
boolean webkitMatchesSelector(DOMString selectors); // historical alias of .matches
|
||||
|
||||
HTMLCollection getElementsByTagName(DOMString qualifiedName);
|
||||
HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName);
|
||||
HTMLCollection getElementsByClassName(DOMString classNames);
|
||||
|
||||
[CEReactions] Element? insertAdjacentElement(DOMString where, Element element); // historical
|
||||
void insertAdjacentText(DOMString where, DOMString data); // historical
|
||||
};
|
||||
|
||||
dictionary ShadowRootInit {
|
||||
required ShadowRootMode mode;
|
||||
};
|
||||
|
||||
[Exposed=Window,
|
||||
LegacyUnenumerableNamedProperties]
|
||||
interface NamedNodeMap {
|
||||
readonly attribute unsigned long length;
|
||||
getter Attr? item(unsigned long index);
|
||||
getter Attr? getNamedItem(DOMString qualifiedName);
|
||||
Attr? getNamedItemNS(DOMString? namespace, DOMString localName);
|
||||
[CEReactions] Attr? setNamedItem(Attr attr);
|
||||
[CEReactions] Attr? setNamedItemNS(Attr attr);
|
||||
[CEReactions] Attr removeNamedItem(DOMString qualifiedName);
|
||||
[CEReactions] Attr removeNamedItemNS(DOMString? namespace, DOMString localName);
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
interface Attr : Node {
|
||||
readonly attribute DOMString? namespaceURI;
|
||||
readonly attribute DOMString? prefix;
|
||||
readonly attribute DOMString localName;
|
||||
readonly attribute DOMString name;
|
||||
[CEReactions] attribute DOMString value;
|
||||
|
||||
readonly attribute Element? ownerElement;
|
||||
|
||||
readonly attribute boolean specified; // useless; always returns true
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
interface CharacterData : Node {
|
||||
attribute [TreatNullAs=EmptyString] DOMString data;
|
||||
readonly attribute unsigned long length;
|
||||
DOMString substringData(unsigned long offset, unsigned long count);
|
||||
void appendData(DOMString data);
|
||||
void insertData(unsigned long offset, DOMString data);
|
||||
void deleteData(unsigned long offset, unsigned long count);
|
||||
void replaceData(unsigned long offset, unsigned long count, DOMString data);
|
||||
};
|
||||
|
||||
[Constructor(optional DOMString data = ""),
|
||||
Exposed=Window]
|
||||
interface Text : CharacterData {
|
||||
[NewObject] Text splitText(unsigned long offset);
|
||||
readonly attribute DOMString wholeText;
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
interface CDATASection : Text {
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
interface ProcessingInstruction : CharacterData {
|
||||
readonly attribute DOMString target;
|
||||
};
|
||||
|
||||
[Constructor(optional DOMString data = ""),
|
||||
Exposed=Window]
|
||||
interface Comment : CharacterData {
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
interface AbstractRange {
|
||||
readonly attribute Node startContainer;
|
||||
readonly attribute unsigned long startOffset;
|
||||
readonly attribute Node endContainer;
|
||||
readonly attribute unsigned long endOffset;
|
||||
readonly attribute boolean collapsed;
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
interface StaticRange : AbstractRange {
|
||||
};
|
||||
|
||||
[Constructor,
|
||||
Exposed=Window]
|
||||
interface Range : AbstractRange {
|
||||
readonly attribute Node commonAncestorContainer;
|
||||
|
||||
void setStart(Node node, unsigned long offset);
|
||||
void setEnd(Node node, unsigned long offset);
|
||||
void setStartBefore(Node node);
|
||||
void setStartAfter(Node node);
|
||||
void setEndBefore(Node node);
|
||||
void setEndAfter(Node node);
|
||||
void collapse(optional boolean toStart = false);
|
||||
void selectNode(Node node);
|
||||
void selectNodeContents(Node node);
|
||||
|
||||
const unsigned short START_TO_START = 0;
|
||||
const unsigned short START_TO_END = 1;
|
||||
const unsigned short END_TO_END = 2;
|
||||
const unsigned short END_TO_START = 3;
|
||||
short compareBoundaryPoints(unsigned short how, Range sourceRange);
|
||||
|
||||
[CEReactions] void deleteContents();
|
||||
[CEReactions, NewObject] DocumentFragment extractContents();
|
||||
[CEReactions, NewObject] DocumentFragment cloneContents();
|
||||
[CEReactions] void insertNode(Node node);
|
||||
[CEReactions] void surroundContents(Node newParent);
|
||||
|
||||
[NewObject] Range cloneRange();
|
||||
void detach();
|
||||
|
||||
boolean isPointInRange(Node node, unsigned long offset);
|
||||
short comparePoint(Node node, unsigned long offset);
|
||||
|
||||
boolean intersectsNode(Node node);
|
||||
|
||||
stringifier;
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
interface NodeIterator {
|
||||
[SameObject] readonly attribute Node root;
|
||||
readonly attribute Node referenceNode;
|
||||
readonly attribute boolean pointerBeforeReferenceNode;
|
||||
readonly attribute unsigned long whatToShow;
|
||||
readonly attribute NodeFilter? filter;
|
||||
|
||||
Node? nextNode();
|
||||
Node? previousNode();
|
||||
|
||||
void detach();
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
interface TreeWalker {
|
||||
[SameObject] readonly attribute Node root;
|
||||
readonly attribute unsigned long whatToShow;
|
||||
readonly attribute NodeFilter? filter;
|
||||
attribute Node currentNode;
|
||||
|
||||
Node? parentNode();
|
||||
Node? firstChild();
|
||||
Node? lastChild();
|
||||
Node? previousSibling();
|
||||
Node? nextSibling();
|
||||
Node? previousNode();
|
||||
Node? nextNode();
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
callback interface NodeFilter {
|
||||
// Constants for acceptNode()
|
||||
const unsigned short FILTER_ACCEPT = 1;
|
||||
const unsigned short FILTER_REJECT = 2;
|
||||
const unsigned short FILTER_SKIP = 3;
|
||||
|
||||
// Constants for whatToShow
|
||||
const unsigned long SHOW_ALL = 0xFFFFFFFF;
|
||||
const unsigned long SHOW_ELEMENT = 0x1;
|
||||
const unsigned long SHOW_ATTRIBUTE = 0x2;
|
||||
const unsigned long SHOW_TEXT = 0x4;
|
||||
const unsigned long SHOW_CDATA_SECTION = 0x8;
|
||||
const unsigned long SHOW_ENTITY_REFERENCE = 0x10; // historical
|
||||
const unsigned long SHOW_ENTITY = 0x20; // historical
|
||||
const unsigned long SHOW_PROCESSING_INSTRUCTION = 0x40;
|
||||
const unsigned long SHOW_COMMENT = 0x80;
|
||||
const unsigned long SHOW_DOCUMENT = 0x100;
|
||||
const unsigned long SHOW_DOCUMENT_TYPE = 0x200;
|
||||
const unsigned long SHOW_DOCUMENT_FRAGMENT = 0x400;
|
||||
const unsigned long SHOW_NOTATION = 0x800; // historical
|
||||
|
||||
unsigned short acceptNode(Node node);
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
interface DOMTokenList {
|
||||
readonly attribute unsigned long length;
|
||||
getter DOMString? item(unsigned long index);
|
||||
boolean contains(DOMString token);
|
||||
[CEReactions] void add(DOMString... tokens);
|
||||
[CEReactions] void remove(DOMString... tokens);
|
||||
[CEReactions] boolean toggle(DOMString token, optional boolean force);
|
||||
[CEReactions] boolean replace(DOMString token, DOMString newToken);
|
||||
boolean supports(DOMString token);
|
||||
[CEReactions] stringifier attribute DOMString value;
|
||||
iterable<DOMString>;
|
||||
};
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,4 +0,0 @@
|
|||
interface InterfaceWithConstructor {
|
||||
[Throws]
|
||||
constructor(long a);
|
||||
};
|
|
@ -1,246 +0,0 @@
|
|||
[Exposed=Window,
|
||||
Constructor,
|
||||
Constructor(MediaStream stream),
|
||||
Constructor(sequence<MediaStreamTrack> tracks)]
|
||||
interface MediaStream : EventTarget {
|
||||
readonly attribute DOMString id;
|
||||
sequence<MediaStreamTrack> getAudioTracks();
|
||||
sequence<MediaStreamTrack> getVideoTracks();
|
||||
sequence<MediaStreamTrack> getTracks();
|
||||
MediaStreamTrack? getTrackById(DOMString trackId);
|
||||
void addTrack(MediaStreamTrack track);
|
||||
void removeTrack(MediaStreamTrack track);
|
||||
MediaStream clone();
|
||||
readonly attribute boolean active;
|
||||
attribute EventHandler onaddtrack;
|
||||
attribute EventHandler onremovetrack;
|
||||
};
|
||||
|
||||
[Exposed=Window]
|
||||
interface MediaStreamTrack : EventTarget {
|
||||
readonly attribute DOMString kind;
|
||||
readonly attribute DOMString id;
|
||||
readonly attribute DOMString label;
|
||||
attribute boolean enabled;
|
||||
readonly attribute boolean muted;
|
||||
attribute EventHandler onmute;
|
||||
attribute EventHandler onunmute;
|
||||
readonly attribute MediaStreamTrackState readyState;
|
||||
attribute EventHandler onended;
|
||||
MediaStreamTrack clone();
|
||||
void stop();
|
||||
MediaTrackCapabilities getCapabilities();
|
||||
MediaTrackConstraints getConstraints();
|
||||
MediaTrackSettings getSettings();
|
||||
Promise<void> applyConstraints(optional MediaTrackConstraints constraints = {});
|
||||
};
|
||||
|
||||
enum MediaStreamTrackState {
|
||||
"live",
|
||||
"ended"
|
||||
};
|
||||
|
||||
dictionary MediaTrackSupportedConstraints {
|
||||
boolean width = true;
|
||||
boolean height = true;
|
||||
boolean aspectRatio = true;
|
||||
boolean frameRate = true;
|
||||
boolean facingMode = true;
|
||||
boolean resizeMode = true;
|
||||
boolean volume = true;
|
||||
boolean sampleRate = true;
|
||||
boolean sampleSize = true;
|
||||
boolean echoCancellation = true;
|
||||
boolean autoGainControl = true;
|
||||
boolean noiseSuppression = true;
|
||||
boolean latency = true;
|
||||
boolean channelCount = true;
|
||||
boolean deviceId = true;
|
||||
boolean groupId = true;
|
||||
};
|
||||
|
||||
dictionary MediaTrackCapabilities {
|
||||
ULongRange width;
|
||||
ULongRange height;
|
||||
DoubleRange aspectRatio;
|
||||
DoubleRange frameRate;
|
||||
sequence<DOMString> facingMode;
|
||||
sequence<DOMString> resizeMode;
|
||||
DoubleRange volume;
|
||||
ULongRange sampleRate;
|
||||
ULongRange sampleSize;
|
||||
sequence<boolean> echoCancellation;
|
||||
sequence<boolean> autoGainControl;
|
||||
sequence<boolean> noiseSuppression;
|
||||
DoubleRange latency;
|
||||
ULongRange channelCount;
|
||||
DOMString deviceId;
|
||||
DOMString groupId;
|
||||
};
|
||||
|
||||
dictionary MediaTrackConstraints : MediaTrackConstraintSet {
|
||||
sequence<MediaTrackConstraintSet> advanced;
|
||||
};
|
||||
|
||||
dictionary MediaTrackConstraintSet {
|
||||
ConstrainULong width;
|
||||
ConstrainULong height;
|
||||
ConstrainDouble aspectRatio;
|
||||
ConstrainDouble frameRate;
|
||||
ConstrainDOMString facingMode;
|
||||
ConstrainDOMString resizeMode;
|
||||
ConstrainDouble volume;
|
||||
ConstrainULong sampleRate;
|
||||
ConstrainULong sampleSize;
|
||||
ConstrainBoolean echoCancellation;
|
||||
ConstrainBoolean autoGainControl;
|
||||
ConstrainBoolean noiseSuppression;
|
||||
ConstrainDouble latency;
|
||||
ConstrainULong channelCount;
|
||||
ConstrainDOMString deviceId;
|
||||
ConstrainDOMString groupId;
|
||||
};
|
||||
|
||||
dictionary MediaTrackSettings {
|
||||
long width;
|
||||
long height;
|
||||
double aspectRatio;
|
||||
double frameRate;
|
||||
DOMString facingMode;
|
||||
DOMString resizeMode;
|
||||
double volume;
|
||||
long sampleRate;
|
||||
long sampleSize;
|
||||
boolean echoCancellation;
|
||||
boolean autoGainControl;
|
||||
boolean noiseSuppression;
|
||||
double latency;
|
||||
long channelCount;
|
||||
DOMString deviceId;
|
||||
DOMString groupId;
|
||||
};
|
||||
|
||||
enum VideoFacingModeEnum {
|
||||
"user",
|
||||
"environment",
|
||||
"left",
|
||||
"right"
|
||||
};
|
||||
|
||||
enum VideoResizeModeEnum {
|
||||
"none",
|
||||
"crop-and-scale"
|
||||
};
|
||||
|
||||
[Exposed=Window,
|
||||
Constructor(DOMString type, MediaStreamTrackEventInit eventInitDict)]
|
||||
interface MediaStreamTrackEvent : Event {
|
||||
[SameObject]
|
||||
readonly attribute MediaStreamTrack track;
|
||||
};
|
||||
|
||||
dictionary MediaStreamTrackEventInit : EventInit {
|
||||
required MediaStreamTrack track;
|
||||
};
|
||||
|
||||
partial interface Navigator {
|
||||
[SameObject, SecureContext]
|
||||
readonly attribute MediaDevices mediaDevices;
|
||||
};
|
||||
|
||||
[Exposed=Window, SecureContext]
|
||||
interface MediaDevices : EventTarget {
|
||||
attribute EventHandler ondevicechange;
|
||||
Promise<sequence<MediaDeviceInfo>> enumerateDevices();
|
||||
};
|
||||
|
||||
[Exposed=Window, SecureContext]
|
||||
interface MediaDeviceInfo {
|
||||
readonly attribute DOMString deviceId;
|
||||
readonly attribute MediaDeviceKind kind;
|
||||
readonly attribute DOMString label;
|
||||
readonly attribute DOMString groupId;
|
||||
[Default] object toJSON();
|
||||
};
|
||||
|
||||
enum MediaDeviceKind {
|
||||
"audioinput",
|
||||
"audiooutput",
|
||||
"videoinput"
|
||||
};
|
||||
|
||||
[Exposed=Window] interface InputDeviceInfo : MediaDeviceInfo {
|
||||
MediaTrackCapabilities getCapabilities();
|
||||
};
|
||||
|
||||
partial interface Navigator {
|
||||
[SecureContext]
|
||||
void getUserMedia(MediaStreamConstraints constraints, NavigatorUserMediaSuccessCallback successCallback, NavigatorUserMediaErrorCallback errorCallback);
|
||||
};
|
||||
|
||||
partial interface MediaDevices {
|
||||
MediaTrackSupportedConstraints getSupportedConstraints();
|
||||
Promise<MediaStream> getUserMedia(optional MediaStreamConstraints constraints = {});
|
||||
};
|
||||
|
||||
dictionary MediaStreamConstraints {
|
||||
(boolean or MediaTrackConstraints) video = false;
|
||||
(boolean or MediaTrackConstraints) audio = false;
|
||||
};
|
||||
|
||||
callback NavigatorUserMediaSuccessCallback = void (MediaStream stream);
|
||||
|
||||
callback NavigatorUserMediaErrorCallback = void (MediaStreamError error);
|
||||
|
||||
typedef object MediaStreamError;
|
||||
|
||||
dictionary DoubleRange {
|
||||
double max;
|
||||
double min;
|
||||
};
|
||||
|
||||
dictionary ConstrainDoubleRange : DoubleRange {
|
||||
double exact;
|
||||
double ideal;
|
||||
};
|
||||
|
||||
dictionary ULongRange {
|
||||
[Clamp] unsigned long max;
|
||||
[Clamp] unsigned long min;
|
||||
};
|
||||
|
||||
dictionary ConstrainULongRange : ULongRange {
|
||||
[Clamp] unsigned long exact;
|
||||
[Clamp] unsigned long ideal;
|
||||
};
|
||||
|
||||
dictionary ConstrainBooleanParameters {
|
||||
boolean exact;
|
||||
boolean ideal;
|
||||
};
|
||||
|
||||
dictionary ConstrainDOMStringParameters {
|
||||
(DOMString or sequence<DOMString>) exact;
|
||||
(DOMString or sequence<DOMString>) ideal;
|
||||
};
|
||||
|
||||
typedef ([Clamp] unsigned long or ConstrainULongRange) ConstrainULong;
|
||||
|
||||
typedef (double or ConstrainDoubleRange) ConstrainDouble;
|
||||
|
||||
typedef (boolean or ConstrainBooleanParameters) ConstrainBoolean;
|
||||
|
||||
typedef (DOMString or sequence<DOMString> or ConstrainDOMStringParameters) ConstrainDOMString;
|
||||
|
||||
dictionary Capabilities {
|
||||
};
|
||||
|
||||
dictionary Settings {
|
||||
};
|
||||
|
||||
dictionary ConstraintSet {
|
||||
};
|
||||
|
||||
dictionary Constraints : ConstraintSet {
|
||||
sequence<ConstraintSet> advanced;
|
||||
};
|
|
@ -1,99 +0,0 @@
|
|||
extern crate weedle;
|
||||
|
||||
use std::fs;
|
||||
use std::io::Read;
|
||||
|
||||
use weedle::*;
|
||||
|
||||
fn read_file(path: &str) -> String {
|
||||
let mut file = fs::File::open(path).unwrap();
|
||||
let mut file_content = String::new();
|
||||
file.read_to_string(&mut file_content).unwrap();
|
||||
file_content
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn should_parse_dom_webidl() {
|
||||
let content = read_file("./tests/defs/dom.webidl");
|
||||
let parsed = weedle::parse(&content).unwrap();
|
||||
|
||||
assert_eq!(parsed.len(), 62);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_parse_html_webidl() {
|
||||
let content = read_file("./tests/defs/html.webidl");
|
||||
let parsed = weedle::parse(&content).unwrap();
|
||||
|
||||
assert_eq!(parsed.len(), 325);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_parse_mediacapture_streams_webidl() {
|
||||
let content = read_file("./tests/defs/mediacapture-streams.webidl");
|
||||
let parsed = weedle::parse(&content).unwrap();
|
||||
|
||||
assert_eq!(parsed.len(), 37);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interface_constructor() {
|
||||
let content = read_file("./tests/defs/interface-constructor.webidl");
|
||||
let mut parsed = weedle::parse(&content).unwrap();
|
||||
|
||||
assert_eq!(parsed.len(), 1);
|
||||
|
||||
let definition = parsed.pop().unwrap();
|
||||
|
||||
match definition {
|
||||
Definition::Interface(mut interface) => {
|
||||
assert!(interface.attributes.is_none());
|
||||
assert_eq!(interface.interface, term!(interface));
|
||||
assert_eq!(interface.identifier.0, "InterfaceWithConstructor");
|
||||
assert_eq!(interface.inheritance, None);
|
||||
|
||||
assert_eq!(interface.members.body.len(), 1);
|
||||
|
||||
let body = interface.members.body.pop().unwrap();
|
||||
|
||||
match body {
|
||||
interface::InterfaceMember::Constructor(constructor) => {
|
||||
let mut attributes = constructor.attributes.unwrap().body.list;
|
||||
assert_eq!(attributes.len(), 1);
|
||||
let attribute = attributes.pop().unwrap();
|
||||
|
||||
match attribute {
|
||||
attribute::ExtendedAttribute::NoArgs(attribute) => {
|
||||
assert_eq!((attribute.0).0, "Throws");
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
let mut args = constructor.args.body.list;
|
||||
assert_eq!(args.len(), 1);
|
||||
let arg = args.pop().unwrap();
|
||||
|
||||
match arg {
|
||||
argument::Argument::Single(arg) => {
|
||||
assert!(arg.attributes.is_none());
|
||||
assert!(arg.optional.is_none());
|
||||
assert!(arg.type_.attributes.is_none());
|
||||
|
||||
match arg.type_.type_ {
|
||||
types::Type::Single(types::SingleType::NonAny(
|
||||
types::NonAnyType::Integer(_),
|
||||
)) => {}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
assert_eq!(constructor.constructor, term::Constructor);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
|
@ -13,7 +13,6 @@ clippy:
|
|||
- gfx/wr/webrender_build/
|
||||
- gfx/wr/wr_malloc_size_of/
|
||||
- js/src/
|
||||
- js/src/frontend/binast/
|
||||
- js/src/frontend/smoosh/
|
||||
- js/src/rust/shared/
|
||||
- js/src/wasm/cranelift/
|
||||
|
|
Загрузка…
Ссылка в новой задаче