Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 7f00619
Showing
19 changed files
with
3,387 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
name: CI | ||
|
||
on: | ||
push: | ||
branches: [ main ] | ||
pull_request: | ||
branches: [ main ] | ||
types: | ||
- opened | ||
- reopened | ||
- synchronize | ||
- ready_for_review | ||
|
||
env: | ||
CARGO_TERM_COLORS: always | ||
|
||
jobs: | ||
tests: | ||
name: Tests | ||
runs-on: ubuntu-latest | ||
if: ${{ !github.event.pull_request.draft }} | ||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v2 | ||
- name: Install Rust | ||
uses: actions-rs/toolchain@v1 | ||
with: | ||
toolchain: "1.66.0" | ||
profile: minimal | ||
override: true | ||
- name: Build | ||
run: cargo build --all-features --verbose | ||
- name: Run tests | ||
run: cargo test --all-features --verbose | ||
rustfmt: | ||
name: Formatting | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Checkout | ||
uses: actions/checkout@v2 | ||
- name: Install Rust | ||
uses: actions-rs/toolchain@v1 | ||
with: | ||
toolchain: "1.66.0" | ||
profile: minimal | ||
override: true | ||
components: rustfmt | ||
- name: Check formatting | ||
uses: actions-rs/cargo@v1 | ||
with: | ||
command: fmt | ||
args: --all -- --check | ||
clippy: | ||
name: Warnings | ||
runs-on: ubuntu-latest | ||
if: ${{ !github.event.pull_request.draft }} | ||
steps: | ||
- name: Checkout repository | ||
uses: actions/checkout@v2 | ||
- name: Install Rust | ||
uses: actions-rs/toolchain@v1 | ||
with: | ||
toolchain: "1.66.0" | ||
profile: minimal | ||
override: true | ||
components: clippy | ||
- name: Clippy Check | ||
uses: actions-rs/cargo@v1 | ||
with: | ||
command: clippy | ||
args: --all-features -- -D warnings |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/target | ||
/Cargo.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
hard_tabs = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
[package] | ||
name = "static-regular-grammar" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[lib] | ||
proc-macro = true | ||
|
||
[dependencies] | ||
# btree-range-map = "0.5.2" | ||
btree-range-map = { path = "/home/timothee/Projets/utils/data-structures/btree-range-map", features = ["serde"] } | ||
proc-macro2 = "1.0.60" | ||
proc-macro-error = "1.0.4" | ||
syn = "2.0.18" | ||
quote = "1.0.29" | ||
thiserror = "1.0.40" | ||
abnf = "0.13.0" | ||
indoc = "2.0.1" | ||
serde = { version = "1.0.164", features = ["derive"] } | ||
# serde_json = "1.0.99" | ||
ciborium = "0.2.1" | ||
sha2 = "0.10.7" | ||
hex_fmt = "0.3.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# Static Regular Grammars | ||
|
||
[![Build](https://img.shields.io/github/actions/workflow/status/timothee-haudebourg/static-regular-grammar/ci.yml?branch=main&style=flat-square)](https://github.com/timothee-haudebourg/static-regular-grammar/actions) | ||
[![Crate informations](https://img.shields.io/crates/v/static-regular-grammar.svg?style=flat-square)](https://crates.io/crates/static-regular-grammar) | ||
[![License](https://img.shields.io/crates/l/static-regular-grammar.svg?style=flat-square)](https://github.com/timothee-haudebourg/static-regular-grammar#license) | ||
[![Documentation](https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square)](https://docs.rs/static-regular-grammar) | ||
|
||
<!-- cargo-rdme start --> | ||
|
||
This library provides the `RegularGrammar` macro that helps you create | ||
unsized type wrapping byte or char strings validated by a regular grammar. | ||
For now, only the ABNF grammar format is supported. | ||
|
||
## Example | ||
|
||
The grammar is specified by code blocks in the type documentation. | ||
|
||
```rust | ||
use static_regular_grammar::RegularGrammar; | ||
|
||
/// Example grammar | ||
/// | ||
/// ```abnf | ||
/// foo = "f" 1*("oo") ; the first non-terminal is used as entry point. | ||
/// ``` | ||
#[derive(RegularGrammar)] | ||
pub struct Foo([u8]); | ||
|
||
let foo = Foo::new(b"foooooo").unwrap(); | ||
``` | ||
|
||
<!-- cargo-rdme end --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
use static_regular_grammar::RegularGrammar; | ||
|
||
/// Test | ||
/// | ||
/// # Grammar | ||
/// | ||
/// ```abnf | ||
/// test = "%" / "$" | ||
/// ``` | ||
#[derive(RegularGrammar)] | ||
pub struct Test([u8]); | ||
|
||
fn main() { | ||
Test::new(b"$").unwrap(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
use static_regular_grammar::RegularGrammar; | ||
|
||
/// URI Authority. | ||
/// | ||
/// # Grammar | ||
/// | ||
/// ```abnf | ||
/// authority = [ userinfo "@" ] host [ ":" port ] | ||
/// | ||
/// userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) | ||
/// | ||
/// host = IP-literal / IPv4address / reg-name | ||
/// | ||
/// port = *DIGIT | ||
/// ``` | ||
/// | ||
/// ## Host rules | ||
/// | ||
/// ```abnf | ||
/// IP-literal = "[" ( IPv6address / IPvFuture ) "]" | ||
/// | ||
/// IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ) | ||
/// | ||
/// IPv6address = 6( h16 ":" ) ls32 | ||
/// / "::" 5( h16 ":" ) ls32 | ||
/// / [ h16 ] "::" 4( h16 ":" ) ls32 | ||
/// / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 | ||
/// / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 | ||
/// / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 | ||
/// / [ *4( h16 ":" ) h16 ] "::" ls32 | ||
/// / [ *5( h16 ":" ) h16 ] "::" h16 | ||
/// / [ *6( h16 ":" ) h16 ] "::" | ||
/// | ||
/// ls32 = ( h16 ":" h16 ) / IPv4address | ||
/// ; least-significant 32 bits of address | ||
/// | ||
/// h16 = 1*4HEXDIG | ||
/// ; 16 bits of address represented in hexadecimal | ||
/// | ||
/// IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet | ||
/// | ||
/// dec-octet = DIGIT ; 0-9 | ||
/// / %x31-39 DIGIT ; 10-99 | ||
/// / "1" 2DIGIT ; 100-199 | ||
/// / "2" %x30-34 DIGIT ; 200-249 | ||
/// / "25" %x30-35 ; 250-255 | ||
/// | ||
/// reg-name = *( unreserved / pct-encoded / sub-delims ) | ||
/// ``` | ||
/// | ||
/// ## Misc rules | ||
/// | ||
/// ```abnf | ||
/// reserved = gen-delims / sub-delims | ||
/// | ||
/// gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" | ||
/// | ||
/// sub-delims = "!" / "$" / "&" / "'" / "(" / ")" | ||
/// / "*" / "+" / "," / ";" / "=" | ||
/// | ||
/// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" | ||
/// | ||
/// pct-encoded = "%" HEXDIG HEXDIG | ||
/// ``` | ||
#[derive(RegularGrammar, PartialEq, Eq)] | ||
#[title = "uri:authority"] | ||
#[buffer(AuthorityBuf, derive(PartialEq, Eq))] | ||
pub struct Authority([u8]); | ||
|
||
fn main() { | ||
Authority::new(b"timothee@example.org:12").unwrap(); | ||
AuthorityBuf::new(b"timothee@example.org:12".to_vec()).unwrap(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
use btree_range_map::{AnyRange, RangeSet}; | ||
use std::{ | ||
fmt, | ||
ops::{Deref, DerefMut, RangeInclusive}, | ||
}; | ||
|
||
use crate::utils::Sanitized; | ||
|
||
pub struct DisplayByte(pub u8); | ||
|
||
impl fmt::Display for DisplayByte { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
Sanitized(self.0).fmt(f) | ||
} | ||
} | ||
|
||
pub struct DisplayBytes<'a>(pub &'a str); | ||
|
||
impl<'a> fmt::Display for DisplayBytes<'a> { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
Sanitized(self.0).fmt(f) | ||
} | ||
} | ||
|
||
pub struct DisplayByteRange<'a>(pub &'a AnyRange<u8>); | ||
|
||
impl<'a> fmt::Display for DisplayByteRange<'a> { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
if let Some(first) = self.0.first() { | ||
let last = self.0.last().unwrap(); | ||
|
||
if first == last { | ||
fmt::Display::fmt(&DisplayByte(first), f) | ||
} else if first as u32 + 1 == last as u32 { | ||
// Note: no risk of overflowing here with `u8`. | ||
write!(f, "{}{}", DisplayByte(first), DisplayByte(last)) | ||
} else { | ||
write!(f, "{}-{}", DisplayByte(first), DisplayByte(last)) | ||
} | ||
} else { | ||
Ok(()) | ||
} | ||
} | ||
} | ||
|
||
#[derive(Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] | ||
pub struct ByteSet(RangeSet<u8>); | ||
|
||
impl ByteSet { | ||
pub fn new() -> ByteSet { | ||
ByteSet(RangeSet::new()) | ||
} | ||
|
||
pub fn is_empty(&self) -> bool { | ||
self.0.is_empty() | ||
} | ||
|
||
pub fn len(&self) -> u16 { | ||
self.0.len() | ||
} | ||
|
||
pub fn from_u8(c: u8, case_sensitive: bool) -> ByteSet { | ||
let mut set = ByteSet::new(); | ||
|
||
if case_sensitive { | ||
set.insert(c) | ||
} else { | ||
set.insert(c.to_ascii_uppercase()); | ||
set.insert(c.to_ascii_lowercase()) | ||
} | ||
|
||
set | ||
} | ||
|
||
pub fn from_ranges(ranges: impl IntoIterator<Item = RangeInclusive<u8>>) -> Self { | ||
let mut set = RangeSet::new(); | ||
|
||
for range in ranges { | ||
set.insert(range) | ||
} | ||
|
||
ByteSet(set) | ||
} | ||
|
||
pub fn from_u8s(u8s: impl IntoIterator<Item = u8>) -> Self { | ||
let mut set = RangeSet::new(); | ||
|
||
for c in u8s { | ||
set.insert(c) | ||
} | ||
|
||
ByteSet(set) | ||
} | ||
|
||
pub fn ranges(&self) -> Ranges { | ||
Ranges(self.0.iter()) | ||
} | ||
|
||
pub fn first(&self) -> Option<u8> { | ||
self.0.iter().next().and_then(|range| range.first()) | ||
} | ||
} | ||
|
||
pub struct Ranges<'a>( | ||
btree_range_map::generic::set::Iter<'a, u8, btree_range_map::DefaultSetContainer<u8>>, | ||
); | ||
|
||
impl<'a> Iterator for Ranges<'a> { | ||
type Item = AnyRange<u8>; | ||
|
||
fn next(&mut self) -> Option<Self::Item> { | ||
self.0.next().copied() | ||
} | ||
} | ||
|
||
impl From<AnyRange<u8>> for ByteSet { | ||
fn from(value: AnyRange<u8>) -> Self { | ||
let mut result = ByteSet::new(); | ||
result.insert(value); | ||
result | ||
} | ||
} | ||
|
||
impl From<u8> for ByteSet { | ||
fn from(value: u8) -> Self { | ||
let mut result = ByteSet::new(); | ||
result.insert(value); | ||
result | ||
} | ||
} | ||
|
||
impl<const N: usize> From<[u8; N]> for ByteSet { | ||
fn from(value: [u8; N]) -> Self { | ||
Self::from_u8s(value) | ||
} | ||
} | ||
|
||
impl<const N: usize> From<[RangeInclusive<u8>; N]> for ByteSet { | ||
fn from(value: [RangeInclusive<u8>; N]) -> Self { | ||
Self::from_ranges(value) | ||
} | ||
} | ||
|
||
impl fmt::Display for ByteSet { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
for range in &self.0 { | ||
DisplayByteRange(range).fmt(f)?; | ||
} | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
impl fmt::Debug for ByteSet { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
write!(f, "CharSet({})", self) | ||
} | ||
} | ||
|
||
impl Deref for ByteSet { | ||
type Target = RangeSet<u8>; | ||
|
||
fn deref(&self) -> &RangeSet<u8> { | ||
&self.0 | ||
} | ||
} | ||
|
||
impl DerefMut for ByteSet { | ||
fn deref_mut(&mut self) -> &mut RangeSet<u8> { | ||
&mut self.0 | ||
} | ||
} |
Oops, something went wrong.