feat: initial commit

This commit is contained in:
Ashhhleyyy 2024-04-14 21:52:00 +01:00
commit 618a5a1ae7
Signed by: ash
GPG key ID: 83B789081A0878FB
16 changed files with 821 additions and 0 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/target
/.direnv

111
Cargo.lock generated Normal file
View file

@ -0,0 +1,111 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "derive-try-from-primitive"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "302ccf094df1151173bb6f5a2282fcd2f45accd5eae1bdf82dcbfefbc501ad5c"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "enumflags2"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5998b4f30320c9d93aed72f63af821bfdac50465b75428fce77b48ec482c3939"
dependencies = [
"enumflags2_derive",
]
[[package]]
name = "enumflags2_derive"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f95e2801cd355d4a1a3e3953ce6ee5ae9603a5c833455343a8bfe3f44d418246"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "jarr"
version = "0.1.0"
dependencies = [
"derive-try-from-primitive",
"enumflags2",
"nom",
]
[[package]]
name = "memchr"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "proc-macro2"
version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"

11
Cargo.toml Normal file
View file

@ -0,0 +1,11 @@
[package]
name = "jarr"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
derive-try-from-primitive = "1.0.0"
enumflags2 = "0.7.8"
nom = "7.1.3"

82
flake.lock Normal file
View file

@ -0,0 +1,82 @@
{
"nodes": {
"crane": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1706473964,
"narHash": "sha256-Fq6xleee/TsX6NbtoRuI96bBuDHMU57PrcK9z1QEKbk=",
"owner": "ipetkov",
"repo": "crane",
"rev": "c798790eabec3e3da48190ae3698ac227aab770c",
"type": "github"
},
"original": {
"owner": "ipetkov",
"repo": "crane",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1705309234,
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1706683685,
"narHash": "sha256-FtPPshEpxH/ewBOsdKBNhlsL2MLEFv1hEnQ19f/bFsQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "5ad9903c16126a7d949101687af0aa589b1d7d3d",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"crane": "crane",
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

54
flake.nix Normal file
View file

@ -0,0 +1,54 @@
{
description = "java class file schenanigans in rust";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
crane = {
url = "github:ipetkov/crane";
inputs.nixpkgs.follows = "nixpkgs";
};
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, crane, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
};
deps = with pkgs; [
] ++ pkgs.lib.optionals pkgs.stdenv.isDarwin [
pkgs.libiconv
];
craneLib = crane.lib.${system};
jarr = craneLib.buildPackage {
src = craneLib.cleanCargoSource (craneLib.path ./.);
buildInputs = deps;
};
in
{
checks = {
inherit jarr;
};
packages.default = jarr;
apps.default = flake-utils.lib.mkApp {
drv = jarr;
};
devShells.default = craneLib.devShell {
checks = self.checks.${system};
packages = with pkgs; [
rust-analyzer
jdk8
] ++ deps;
};
});
}

BIN
src/Test.class Normal file

Binary file not shown.

17
src/Test.java Normal file
View file

@ -0,0 +1,17 @@
//package dev.ashhhleyyy.jarr;
public class Test implements Runnable {
private final String beans = "hello, world!";
public void test() {
}
public void run() {
this.test();
}
public String asdasd(long longBoi, int i, boolean b, Object o, float f, double d, short s, char c) {
return "haii";
}
}

27
src/attribute.rs Normal file
View file

@ -0,0 +1,27 @@
use nom::{bytes::complete::take, error::context, number::complete::{be_u16, be_u32}, sequence::tuple};
use crate::{constant_pool::{self, ConstantPool}, parse};
#[derive(Debug)]
pub struct Attribute {
pub name: String,
pub info: Vec<u8>,
}
impl Attribute {
pub fn parse<'a>(i: parse::Input<'a>, constant_pool: &ConstantPool) -> parse::Result<'a, Self> {
let (i, (name_info, length)) = tuple((
context("name_info", be_u16),
context("length", be_u32),
))(i)?;
let name = constant_pool.get_utf8(name_info as usize).unwrap();
let (i, info) = context("info", take(length as usize))(i)?;
Ok((i, Self {
name: name.to_owned(),
info: info.to_vec(),
}))
}
}

45
src/constant_pool.rs Normal file
View file

@ -0,0 +1,45 @@
use crate::ConstantPoolEntry;
#[derive(Debug)]
pub struct Classinfo {
pub name: String,
}
#[derive(Debug)]
pub struct ConstantPool {
entries: Vec<ConstantPoolEntry>,
}
impl ConstantPool {
pub(crate) fn new(entries: Vec<ConstantPoolEntry>) -> Self {
Self {
entries,
}
}
fn get(&self, index: usize) -> Option<&ConstantPoolEntry> {
self.entries.get(index - 1)
}
pub fn get_utf8(&self, index: usize) -> Option<&str> {
self.get(index).and_then(|entry| match entry {
ConstantPoolEntry::Utf8(s) => Some(s.as_str()),
_ => None
})
}
pub fn get_class_info(&self, index: usize) -> Option<Classinfo> {
self.get(index).and_then(|entry| match entry {
ConstantPoolEntry::Class(name_index) => self.get_utf8(*name_index as usize)
.map(|s| Classinfo { name: s.to_owned() }),
_ => None,
})
}
pub fn get_string(&self, index: usize) -> Option<&str> {
self.get(index).and_then(|entry| match entry {
ConstantPoolEntry::String { string_index } => self.get_utf8(*string_index as usize),
_ => None,
})
}
}

41
src/field.rs Normal file
View file

@ -0,0 +1,41 @@
use enumflags2::BitFlags;
use nom::{error::context, number::complete::be_u16, sequence::tuple};
use crate::{attribute::Attribute, constant_pool::ConstantPool, parse, FieldAccessFlag};
#[derive(Debug)]
pub struct Field {
pub access_flags: BitFlags<FieldAccessFlag>,
pub name: String,
pub descriptor: String,
pub attributes: Vec<Attribute>,
}
impl Field {
pub fn parse<'a>(i: parse::Input<'a>, constant_pool: &ConstantPool) -> parse::Result<'a, Self> {
let (mut i, (access_flags, name_index, descriptor_index, attributes_count)) = tuple((
context("access_flags", FieldAccessFlag::parse),
context("name_index", be_u16),
context("descriptor_index", be_u16),
context("attributes_count", be_u16),
))(i)?;
// TODO: not shit error handling
let name = constant_pool.get_utf8(name_index as usize).expect("invalid class file").to_owned();
let descriptor = constant_pool.get_utf8(descriptor_index as usize).expect("invalid class file").to_owned();
let mut attributes = Vec::with_capacity(attributes_count as usize);
for _ in 0..attributes_count {
let attribute = Attribute::parse(i, constant_pool)?;
i = attribute.0;
attributes.push(attribute.1);
}
Ok((i, Self {
access_flags,
name,
descriptor,
attributes,
}))
}
}

56
src/java_utf8.rs Normal file
View file

@ -0,0 +1,56 @@
#[derive(Clone, Debug)]
pub enum InvalidJavaUtf8 {
NullCharacter,
Invalid,
}
pub fn parse(bytes: &[u8]) -> Result<String, InvalidJavaUtf8> {
let mut result = String::with_capacity(bytes.len());
let mut position = 0;
while position < bytes.len() {
let x = bytes[position];
if x == 0 {
return Err(InvalidJavaUtf8::NullCharacter);
}
position += 1;
if x < 0x80 {
result.push(x as char);
} else {
let x = x as u32;
let y = bytes[position] as u32;
position += 1;
let c = if (x & 0b11100000) == 0b11000000 && (y & 0b11000000) == 0b10000000 {
let c = ((x & 0x1f) << 6) + (y & 0x3f);
char::from_u32(c).expect("invalid utf8")
} else {
let x = x as u32;
let y = y as u32;
let z = bytes[position] as u32;
position += 1;
if (x & 0b11110000) == 0b11100000 && (y & 0b11000000) == 0b10000000 && (z & 0b11000000) == 0b10000000 {
let c = ((x & 0xf) << 12) + ((y & 0x3f) << 6) + (z & 0x3f);
char::from_u32(c).expect("invalid utf8")
} else {
let u = x;
let v = y;
let w = z;
let x = bytes[position] as u32;
position += 1;
let y = bytes[position] as u32;
position += 1;
let z = bytes[position] as u32;
position += 1;
if u == 0b11101101 && (v & 0b11110000) == 0b10100000 && (w & 0b11000000) == 0b10000000 && x == 0b11101101 && (y & 0xf0) == 0b10110000 && (z & 0b11000000) == 0b10000000 {
let c = 0x10000 + ((v & 0x0f) << 16) + ((w & 0x3f) << 10) + ((y & 0x0f) << 6) + (z & 0x3f);
char::from_u32(c).expect("invalid utf8")
} else {
return Err(InvalidJavaUtf8::Invalid);
}
}
};
result.push(c);
}
}
result.shrink_to_fit();
Ok(result)
}

283
src/lib.rs Normal file
View file

@ -0,0 +1,283 @@
use attribute::Attribute;
use constant_pool::{Classinfo, ConstantPool};
use derive_try_from_primitive::TryFromPrimitive;
use field::Field;
use method::Method;
use nom::{bytes::complete::{tag, take}, combinator::{map, map_res}, error::{context, ErrorKind}, number::complete::{be_u16, be_u8, le_u16}, sequence::tuple};
use enumflags2::{bitflags, BitFlags};
pub mod attribute;
pub mod constant_pool;
pub mod field;
pub mod java_utf8;
pub mod method;
pub mod parse;
pub struct HexDump<'a>(&'a [u8]);
use std::fmt;
impl<'a> fmt::Debug for HexDump<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for &x in self.0.iter().take(20) {
write!(f, "{:02x} ", x)?;
}
Ok(())
}
}
#[derive(Debug, TryFromPrimitive)]
#[repr(u8)]
pub enum ConstantPoolEntryType {
Class = 7,
Fieldref = 9,
Methodref = 10,
InterfaceMethodref = 11,
String = 8,
Integer = 3,
Float = 4,
Long = 5,
Double = 6,
NameAndType = 12,
Utf8 = 1,
MethodHandle = 15,
MethodType = 16,
InvokeDynamic = 18,
}
impl ConstantPoolEntryType {
pub fn parse(i: parse::Input) -> parse::Result<Self> {
map_res(be_u8, |x| Self::try_from(x).map_err(|_| ErrorKind::Alt))(i)
}
}
#[derive(Debug)]
pub enum ConstantPoolEntry {
Class(u16),
Fieldref {
class_index: u16,
name_and_type_index: u16,
},
Methodref {
class_index: u16,
name_and_type_index: u16,
},
InterfaceMethodref {
class_index: u16,
name_and_type_index: u16,
},
String {
string_index: u16,
},
Integer,
Float,
Long,
Double,
NameAndType {
name_index: u16,
descriptor_index: u16,
},
Utf8(String),
MethodHandle,
InvokeDynamic,
}
impl ConstantPoolEntry {
pub fn parse(i: parse::Input) -> parse::Result<Self> {
let (i, ty) = context("cp_info tag", ConstantPoolEntryType::parse)(i)?;
let (i, result) = match ty {
ConstantPoolEntryType::Class => {
let (i, name_index) = context("Class_info name_index", be_u16)(i)?;
(i, Self::Class(name_index))
},
ConstantPoolEntryType::Fieldref => {
let (i, (class_index, name_and_type_index)) = tuple((
context("Fieldref_info class_index", be_u16),
context("Fieldref_info name_and_type_index", be_u16),
))(i)?;
(i, Self::Fieldref { class_index, name_and_type_index })
},
ConstantPoolEntryType::Methodref => {
let (i, (class_index, name_and_type_index)) = tuple((
context("Methodref_info class_index", be_u16),
context("Methodref_info name_and_type_index", be_u16),
))(i)?;
(i, Self::Methodref { class_index, name_and_type_index })
},
ConstantPoolEntryType::InterfaceMethodref => {
let (i, (class_index, name_and_type_index)) = tuple((
context("InterfaceMethodref_info class_index", be_u16),
context("InterfaceMethodref_info name_and_type_index", be_u16),
))(i)?;
(i, Self::InterfaceMethodref { class_index, name_and_type_index })
},
ConstantPoolEntryType::Utf8 => {
let (i, length) = context("Utf8_info length", be_u16)(i)?;
let (i, data) = context("Uft8_info bytes", take(length as usize))(i)?;
let parsed = java_utf8::parse(data).expect("beans");
(i, Self::Utf8(parsed))
},
ConstantPoolEntryType::NameAndType => {
let (i, (name_index, descriptor_index)) = tuple((
context("NameAndType_info name_index", be_u16),
context("NameAndType_info descriptor_index", be_u16),
))(i)?;
(i, Self::NameAndType { name_index, descriptor_index })
}
ConstantPoolEntryType::String => {
let (i, string_index) = context("String_info string_index", be_u16)(i)?;
(i, Self::String { string_index })
}
v => unimplemented!("{v:?}"),
};
Ok((i, result))
}
}
#[bitflags]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum ClassAccessFlag {
Public = 0x001,
Final = 0x0010,
Super = 0x0020,
Interface = 0x0200,
Abstract = 0x0400,
Synthetic = 0x1000,
Annotation = 0x2000,
Enum = 0x4000,
}
#[bitflags]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum FieldAccessFlag {
Public = 0x0001,
Private = 0x0002,
Protected = 0x0004,
Static = 0x0008,
Final = 0x0010,
Volatile = 0x0040,
Transient = 0x0080,
Synthetic = 0x1000,
Enum = 0x4000,
}
impl_parse_for_enumflags!(ClassAccessFlag, be_u16);
impl_parse_for_enumflags!(FieldAccessFlag, be_u16);
#[derive(Debug)]
pub struct ClassFile {
pub minor_version: u16,
pub major_version: u16,
pub constant_pool: ConstantPool,
pub access_flags: BitFlags<ClassAccessFlag>,
pub this_class: u16,
pub super_class: u16,
pub interfaces: Vec<Classinfo>,
pub fields: Vec<Field>,
pub methods: Vec<Method>,
pub attributes: Vec<Attribute>,
}
impl ClassFile {
const MAGIC: &'static [u8] = &[0xCA, 0xFE, 0xBA, 0xBE];
pub fn parse(i: parse::Input) -> parse::Result<Self> {
let (i, _) = context("magic", tag(Self::MAGIC))(i)?;
let (mut i, (minor_version, major_version, constant_pool_count)) = tuple((
context("minor_version", be_u16),
context("major_version", be_u16),
context("constant_pool_count", be_u16),
))(i)?;
let constant_pool_count = constant_pool_count - 1;
let mut constant_pool_entries = Vec::with_capacity(constant_pool_count as usize);
for j in 0..constant_pool_count {
let res = context("constant_pool", ConstantPoolEntry::parse)(i)?;
i = res.0;
constant_pool_entries.push(res.1);
}
let constant_pool = ConstantPool::new(constant_pool_entries);
let (mut i, (access_flags, this_class, super_class, interfaces_count)) = tuple((
context("access_flags", ClassAccessFlag::parse),
context("this_class", be_u16),
context("super_class", be_u16),
context("interfaces_count", be_u16),
))(i)?;
let mut interfaces = Vec::with_capacity(interfaces_count as usize);
for _ in 0..interfaces_count {
let cp_index = context("interface", be_u16)(i)?;
i = cp_index.0;
let cp_index = cp_index.1;
// TODO: Not shit error handling
interfaces.push(constant_pool.get_class_info(cp_index as usize).expect("invalid class file"));
}
let (mut i, fields_count) = context("fields_count", be_u16)(i)?;
let mut fields = Vec::with_capacity(fields_count as usize);
for _ in 0..fields_count {
let field = Field::parse(i, &constant_pool)?;
i = field.0;
let field = field.1;
fields.push(field);
}
let (mut i, methods_count) = context("methods_count", be_u16)(i)?;
let mut methods = Vec::with_capacity(methods_count as usize);
for _ in 0..methods_count {
let method = Method::parse(i, &constant_pool)?;
i = method.0;
let method = method.1;
methods.push(method);
}
let (mut i, attributes_count) = context("attributes_count", be_u16)(i)?;
let mut attributes = Vec::with_capacity(attributes_count as usize);
for _ in 0..attributes_count {
let attribute = Attribute::parse(i, &constant_pool)?;
i = attribute.0;
let attribute = attribute.1;
attributes.push(attribute);
}
Ok((i, Self {
minor_version,
major_version,
constant_pool,
access_flags,
this_class,
super_class,
interfaces,
fields,
methods,
attributes,
}))
}
pub fn parse_or_print_error(i: parse::Input) -> Option<Self> {
match Self::parse(i) {
Ok((_, file)) => Some(file),
Err(nom::Err::Failure(err)) | Err(nom::Err::Error(err)) => {
eprintln!("Parsing failed:");
for (input, err) in err.errors {
use nom::Offset;
let offset = i.offset(input);
eprintln!("{:?} at position {}:", err, offset);
eprintln!("{:>08x}: {:?}", offset, HexDump(input));
}
None
}
Err(_) => panic!("unexpected nom error"),
}
}
}
impl ClassFile {
pub fn this_class(&self) -> Classinfo {
self.constant_pool.get_class_info(self.this_class as usize).expect("invalid class file")
}
}

10
src/main.rs Normal file
View file

@ -0,0 +1,10 @@
use std::{env, error::Error, fs};
fn main() -> Result<(), Box<dyn Error>> {
let input_path = env::args().nth(1).expect("usage: jarr FILE");
let input = fs::read(&input_path)?;
let file = jarr::ClassFile::parse_or_print_error(&input).unwrap();
println!("{file:#?}");
println!("{:?}", file.this_class());
Ok(())
}

60
src/method.rs Normal file
View file

@ -0,0 +1,60 @@
use enumflags2::{bitflags, BitFlags};
use nom::{error::context, number::complete::be_u16, sequence::tuple};
use crate::{attribute::Attribute, constant_pool::ConstantPool, impl_parse_for_enumflags, parse};
#[bitflags]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum MethodAccessFlag {
Public = 0x0001,
Private = 0x0002,
Protected = 0x004,
Static = 0x0008,
Final = 0x0010,
Synchronized = 0x0020,
Bridge = 0x0040,
Varargs = 0x0080,
Native = 0x0100,
Abstract = 0x0400,
Strict = 0x0800,
Synthetic = 0x1000,
}
impl_parse_for_enumflags!(MethodAccessFlag, be_u16);
#[derive(Debug)]
pub struct Method {
pub access_flags: BitFlags<MethodAccessFlag>,
pub name: String,
pub descriptor: String,
pub attributes: Vec<Attribute>,
}
impl Method {
pub fn parse<'a>(i: parse::Input<'a>, constant_pool: &ConstantPool) -> parse::Result<'a, Self> {
let (mut i, (access_flags, name_index, descriptor_index, attributes_count)) = tuple((
context("access_flags", MethodAccessFlag::parse),
context("name_index", be_u16),
context("descriptor_index", be_u16),
context("attributes_count", be_u16),
))(i)?;
let name = constant_pool.get_utf8(name_index as usize).expect("invalid class file").to_owned();
let descriptor = constant_pool.get_utf8(descriptor_index as usize).expect("invalid class file").to_owned();
let mut attributes = Vec::with_capacity(attributes_count as usize);
for _ in 0..attributes_count {
let attribute = Attribute::parse(i, constant_pool)?;
i = attribute.0;
attributes.push(attribute.1);
}
Ok((i, Self {
access_flags,
name,
descriptor,
attributes,
}))
}
}

21
src/parse.rs Normal file
View file

@ -0,0 +1,21 @@
pub type Input<'a> = &'a [u8];
pub type Result<'a, O> = nom::IResult<Input<'a>, O, nom::error::VerboseError<Input<'a>>>;
#[macro_export]
macro_rules! impl_parse_for_enumflags {
($type: ident, $number_parser: ident) => {
impl $type {
pub fn parse(i: crate::parse::Input) -> crate::parse::Result<enumflags2::BitFlags<Self>> {
use nom::{
combinator::map_res,
error::{context, ErrorKind},
number::complete::$number_parser,
};
let parser = map_res($number_parser, |x| {
enumflags2::BitFlags::<Self>::from_bits(x).map_err(|_| ErrorKind::Alt)
});
context(stringify!($type), parser)(i)
}
}
};
}