Compare commits
4
Commits
latest
..
05e773d62c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05e773d62c
|
||
|
|
3ef95614f0
|
||
|
|
38ed49fbe3
|
||
|
|
a125c08032
|
No files matched your search
@@ -1,31 +0,0 @@
|
|||||||
clone:
|
|
||||||
git:
|
|
||||||
image: "woodpeckerci/plugin-git:next"
|
|
||||||
pipeline:
|
|
||||||
compliance:
|
|
||||||
commands:
|
|
||||||
- "rustup component add clippy"
|
|
||||||
- "cargo build --verbose"
|
|
||||||
- "cargo clippy --workspace --tests --all-targets --all-features"
|
|
||||||
- "cargo test --workspace --no-fail-fast"
|
|
||||||
image: "rust:1.77-buster"
|
|
||||||
when:
|
|
||||||
event: pull_request, push
|
|
||||||
build:
|
|
||||||
commands:
|
|
||||||
- "cargo build --release"
|
|
||||||
image: "rust:1.77-buster"
|
|
||||||
when:
|
|
||||||
branch: main
|
|
||||||
event: push
|
|
||||||
release:
|
|
||||||
image: jolheiser/drone-gitea-main:latest
|
|
||||||
settings:
|
|
||||||
token:
|
|
||||||
from_secret: gitea_token
|
|
||||||
base: https://git.jojodev.com
|
|
||||||
files:
|
|
||||||
- "target/release/formaty"
|
|
||||||
when:
|
|
||||||
branch: main
|
|
||||||
event: push
|
|
||||||
Generated
+101
-48
@@ -1,6 +1,6 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
@@ -66,9 +66,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bstr"
|
name = "bstr"
|
||||||
version = "1.9.1"
|
version = "1.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
|
checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -159,15 +159,18 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "formaty"
|
name = "formaty"
|
||||||
version = "0.2.0"
|
version = "0.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitvec",
|
"bitvec",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
|
"hex",
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"platform-dirs",
|
"platform-dirs",
|
||||||
|
"regex",
|
||||||
"rust-embed",
|
"rust-embed",
|
||||||
"serde",
|
"serde",
|
||||||
"structopt",
|
"structopt",
|
||||||
|
"thiserror 2.0.18",
|
||||||
"toml",
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -213,9 +216,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.14.5"
|
version = "0.16.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
@@ -236,10 +239,16 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "hex"
|
||||||
version = "2.2.6"
|
version = "0.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
@@ -334,9 +343,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.81"
|
version = "1.0.106"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
|
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@@ -373,7 +382,19 @@ checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"thiserror",
|
"thiserror 1.0.63",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -413,7 +434,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rust-embed-utils",
|
"rust-embed-utils",
|
||||||
"syn 2.0.60",
|
"syn 2.0.117",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -439,31 +460,41 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.199"
|
version = "1.0.228"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a"
|
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_core"
|
||||||
|
version = "1.0.228"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.199"
|
version = "1.0.228"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc"
|
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_spanned"
|
name = "serde_spanned"
|
||||||
version = "0.6.5"
|
version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
|
checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -520,9 +551,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.60"
|
version = "2.0.117"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
|
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -546,58 +577,83 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.31"
|
version = "1.0.63"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
|
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl 1.0.63",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "2.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl 2.0.18",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.31"
|
version = "1.0.63"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
|
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 1.0.75",
|
"syn 2.0.117",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "2.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.8.12"
|
version = "1.0.3+spec-1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3"
|
checksum = "c7614eaf19ad818347db24addfa201729cf2a9b6fdfd9eb0ab870fcacc606c0c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"indexmap",
|
||||||
|
"serde_core",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
"toml_edit",
|
"toml_parser",
|
||||||
|
"toml_writer",
|
||||||
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_datetime"
|
name = "toml_datetime"
|
||||||
version = "0.6.5"
|
version = "1.0.0+spec-1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
|
checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_edit"
|
name = "toml_parser"
|
||||||
version = "0.22.12"
|
version = "1.0.9+spec-1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef"
|
checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
|
||||||
"serde",
|
|
||||||
"serde_spanned",
|
|
||||||
"toml_datetime",
|
|
||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_writer"
|
||||||
|
version = "1.0.6+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.15.0"
|
version = "1.15.0"
|
||||||
@@ -690,12 +746,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.6.7"
|
version = "0.7.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578"
|
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wyz"
|
name = "wyz"
|
||||||
|
|||||||
+6
-3
@@ -1,18 +1,21 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "formaty"
|
name = "formaty"
|
||||||
version = "0.2.0"
|
version = "0.4.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
structopt = "0.3.23"
|
structopt = "0.3.23"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
toml = "0.8.12"
|
toml = "1.0.3"
|
||||||
byteorder = "1.5.0"
|
byteorder = "1.5.0"
|
||||||
num-bigint = "0.4.4"
|
num-bigint = "0.4.4"
|
||||||
bitvec = "1.0.1"
|
bitvec = "1.0.1"
|
||||||
platform-dirs = "0.3.0"
|
platform-dirs = "0.3.0"
|
||||||
|
hex = "0.4.3"
|
||||||
|
thiserror = "2.0.18"
|
||||||
|
regex = "1.10.6"
|
||||||
|
|
||||||
[dependencies.rust-embed]
|
[dependencies.rust-embed]
|
||||||
version = "8.3.0"
|
version = "8.3.0"
|
||||||
|
|||||||
@@ -5,14 +5,15 @@ A simple configurable binary data parser. Data structures are described using TO
|
|||||||
All formats in [formats](./formats) are included in the `formaty` binary. See [formats.md](./formats/formats.md) for
|
All formats in [formats](./formats) are included in the `formaty` binary. See [formats.md](./formats/formats.md) for
|
||||||
more info.
|
more info.
|
||||||
|
|
||||||
### Configuration
|
## Configuration
|
||||||
|
|
||||||
#### Format
|
### Format
|
||||||
A format is a collection of fields that describe the structure of data, so it can be parsed
|
A format is a collection of fields that describe the structure of data, so it can be parsed
|
||||||
|
|
||||||
Members:
|
Members:
|
||||||
* `name` - name of the format, used as the argument when selecting the format to parse the data as
|
* `name` - name of the format, used as the argument when selecting the format to parse the data as
|
||||||
* `bit_flip` - should individual bytes have their bits flipped within, individual fields can override this
|
* `bit_flip` - should individual bytes have their bits flipped within, individual fields can override this
|
||||||
|
* `deframer` - optional list of deframers to use before trying to parse the data
|
||||||
* `fields` - 1 or more fields to describe the structure of the data
|
* `fields` - 1 or more fields to describe the structure of the data
|
||||||
|
|
||||||
#### Field
|
#### Field
|
||||||
@@ -46,6 +47,15 @@ Members:
|
|||||||
* `String`: view fields as a string field
|
* `String`: view fields as a string field
|
||||||
* `Default`: use the default printer for the source type
|
* `Default`: use the default printer for the source type
|
||||||
|
|
||||||
|
### Deframer
|
||||||
|
A deframer configures how the data should be unpacked before being parsed. A deframer is made up of one or more
|
||||||
|
DeframeOperations. Deframers can be added to `Format` configs or be applied by passing in the `--deframer` option.
|
||||||
|
|
||||||
|
#### Deframe Operation
|
||||||
|
* `StartSeq` - a start sequence of bytes to check for and remove before parsing
|
||||||
|
* `StopSeq` - a stop sequence of bytes to check for and remove before parsing
|
||||||
|
* `Replace` - a simple find and replace with Regex support. All matches of `find` are replaced with `replace`
|
||||||
|
|
||||||
#### Example Config
|
#### Example Config
|
||||||
[Example](./formats/example.toml)
|
[Example](./formats/example.toml)
|
||||||
|
|
||||||
@@ -68,11 +78,11 @@ Data: [1, 2, 3, 4, 5]
|
|||||||
|
|
||||||
## Help
|
## Help
|
||||||
```
|
```
|
||||||
Formaty 0.2.0
|
Formaty 0.3.0
|
||||||
Arbitrary Binary Data Formatting
|
Arbitrary Binary Data Formatting
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
formaty [FLAGS] [OPTIONS] <format> [data]...
|
formaty [FLAGS] [OPTIONS] <format> [--] [data]...
|
||||||
|
|
||||||
FLAGS:
|
FLAGS:
|
||||||
-h, --help Prints help information
|
-h, --help Prints help information
|
||||||
@@ -82,6 +92,7 @@ FLAGS:
|
|||||||
OPTIONS:
|
OPTIONS:
|
||||||
-b, --base <base> Base of the input values
|
-b, --base <base> Base of the input values
|
||||||
-c, --config <config> Path to the format config [env: FORMATY_CONFIG=]
|
-c, --config <config> Path to the format config [env: FORMATY_CONFIG=]
|
||||||
|
-d, --deframer <deframer>... Additional deframers to apply to the data before trying to parse it
|
||||||
-g, --global_config <global-config> Path to the global config directory [env: FORMATY_GLOBAL_CONFIG=]
|
-g, --global_config <global-config> Path to the global config directory [env: FORMATY_GLOBAL_CONFIG=]
|
||||||
-f, --file <input-file> Input data from file
|
-f, --file <input-file> Input data from file
|
||||||
-i, --input_type <input-type> Input data type [default: array]
|
-i, --input_type <input-type> Input data type [default: array]
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# yaml-language-server: $schema=https://taskfile.dev/schema.json
|
||||||
|
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
version:
|
||||||
|
desc: Print out Rust version info
|
||||||
|
cmds:
|
||||||
|
- cargo --version
|
||||||
|
- rustc --version
|
||||||
|
- cargo clippy --version
|
||||||
|
build:
|
||||||
|
desc: Debug build
|
||||||
|
cmds:
|
||||||
|
- cargo build --timings --tests --bins
|
||||||
|
test:
|
||||||
|
desc: Test code
|
||||||
|
deps: [build]
|
||||||
|
cmds:
|
||||||
|
- cargo test --locked
|
||||||
|
build:release:
|
||||||
|
desc: Release build
|
||||||
|
cmds:
|
||||||
|
- cargo build --release --locked --timings
|
||||||
|
fmt:
|
||||||
|
desc: Format Rust code
|
||||||
|
cmds:
|
||||||
|
- cargo fmt
|
||||||
|
check:
|
||||||
|
desc: Lint code with Clippy
|
||||||
|
cmds:
|
||||||
|
- cargo check
|
||||||
|
- cargo fmt --check
|
||||||
|
- cargo clippy
|
||||||
|
clean:
|
||||||
|
desc: Purge Rust build cache
|
||||||
|
cmds:
|
||||||
|
- cargo clean
|
||||||
|
|
||||||
+1
-2
@@ -4,5 +4,4 @@ at run time by using the `--config` flag.
|
|||||||
|
|
||||||
## Formats Included
|
## Formats Included
|
||||||
* [CCSDS](ccsds.toml) - Standard CCSDS command/tlm packets
|
* [CCSDS](ccsds.toml) - Standard CCSDS command/tlm packets
|
||||||
* `ccsds`: CCSDS packet with primary header + raw payload
|
* `ccsds`: CCSDS packet with primary header + raw payload
|
||||||
* `ccsds_sec`: CCSDS packet with primary header + secondary header + raw payload
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Serial Line Internet Protocol Deframmer
|
||||||
|
# Ref: https://datatracker.ietf.org/doc/html/rfc1055
|
||||||
|
|
||||||
|
[[deframers]]
|
||||||
|
name = "slip"
|
||||||
|
|
||||||
|
[[deframers.operations]]
|
||||||
|
operation = "StartSeq"
|
||||||
|
start_seq = [0xC0]
|
||||||
|
|
||||||
|
[[deframers.operations]]
|
||||||
|
operation = "StopSeq"
|
||||||
|
stop_seq = [0xC0]
|
||||||
|
|
||||||
|
[[deframers.operations]]
|
||||||
|
operation = "Replace"
|
||||||
|
find = "\\xDB\\xDC"
|
||||||
|
replace = [0xC0]
|
||||||
|
|
||||||
|
[[deframers.operations]]
|
||||||
|
operation = "Replace"
|
||||||
|
find = "\\xDB\\xDD"
|
||||||
|
replace = [0xDB]
|
||||||
+23
-12
@@ -1,20 +1,13 @@
|
|||||||
use crate::formatter::format::ByteOrderOperations;
|
use crate::formatter::bytes_operations::ByteOrderOperations;
|
||||||
use bitvec::prelude::*;
|
use bitvec::prelude::*;
|
||||||
use std::fmt::{Display, Formatter};
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Error)]
|
||||||
pub enum ByteStreamError {
|
pub enum ByteStreamError {
|
||||||
|
#[error("Requested value out of range")]
|
||||||
OutOfRange,
|
OutOfRange,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for ByteStreamError {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
ByteStreamError::OutOfRange => write!(f, "Requested values out of range"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn bit_mask(mask: u8) -> u8 {
|
pub const fn bit_mask(mask: u8) -> u8 {
|
||||||
match mask {
|
match mask {
|
||||||
0 => 0x00,
|
0 => 0x00,
|
||||||
@@ -61,7 +54,7 @@ impl ByteStream {
|
|||||||
|
|
||||||
let mut data: Vec<u8> = vec![];
|
let mut data: Vec<u8> = vec![];
|
||||||
|
|
||||||
if bit_count % 8 != 0 {
|
if !bit_count.is_multiple_of(8) {
|
||||||
let bits_needed = bit_count % 8;
|
let bits_needed = bit_count % 8;
|
||||||
data.push(bits[bit_ndx..bit_ndx + bits_needed].load::<u8>());
|
data.push(bits[bit_ndx..bit_ndx + bits_needed].load::<u8>());
|
||||||
bit_ndx += bits_needed;
|
bit_ndx += bits_needed;
|
||||||
@@ -155,4 +148,22 @@ mod tests {
|
|||||||
let new_bytes = bit_stream.get_bytes::<BigEndian>(0, 3).unwrap();
|
let new_bytes = bit_stream.get_bytes::<BigEndian>(0, 3).unwrap();
|
||||||
assert_eq!(vec![0x02], new_bytes);
|
assert_eq!(vec![0x02], new_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_bytes_9_bits() {
|
||||||
|
let bytes: Vec<u8> = vec![0xff, 0x80];
|
||||||
|
let bit_stream = ByteStream::from(bytes.clone());
|
||||||
|
|
||||||
|
let new_bytes = bit_stream.get_bytes::<BigEndian>(0, 9).unwrap();
|
||||||
|
assert_eq!(vec![0x01, 0xff], new_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_bytes_9_bits_mid_byte() {
|
||||||
|
let bytes: Vec<u8> = vec![0x0f, 0xf8];
|
||||||
|
let bit_stream = ByteStream::from(bytes.clone());
|
||||||
|
|
||||||
|
let new_bytes = bit_stream.get_bytes::<BigEndian>(4, 9).unwrap();
|
||||||
|
assert_eq!(vec![0x01, 0xff], new_bytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
use regex::bytes::RegexBuilder;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum DeframeError {
|
||||||
|
#[error("Start sequence not found")]
|
||||||
|
StartSeqInvalid,
|
||||||
|
#[error("Stop sequence not found")]
|
||||||
|
StopSeqInvalid,
|
||||||
|
#[error("Regex Error")]
|
||||||
|
RegexError(#[from] regex::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[serde(tag = "operation")]
|
||||||
|
pub enum DeframeOperation {
|
||||||
|
StartSeq { start_seq: Vec<u8> },
|
||||||
|
StopSeq { stop_seq: Vec<u8> },
|
||||||
|
Replace { find: String, replace: Vec<u8> },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeframeOperation {
|
||||||
|
pub fn do_operation(&self, mut data: Vec<u8>) -> Result<Vec<u8>, DeframeError> {
|
||||||
|
Ok(match self {
|
||||||
|
DeframeOperation::StartSeq { start_seq } => {
|
||||||
|
if data.starts_with(start_seq) {
|
||||||
|
data.drain(0..start_seq.len());
|
||||||
|
|
||||||
|
data
|
||||||
|
} else {
|
||||||
|
return Err(DeframeError::StartSeqInvalid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DeframeOperation::StopSeq { stop_seq } => {
|
||||||
|
if data.ends_with(stop_seq) {
|
||||||
|
data.drain(data.len() - stop_seq.len()..data.len());
|
||||||
|
|
||||||
|
data
|
||||||
|
} else {
|
||||||
|
return Err(DeframeError::StopSeqInvalid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DeframeOperation::Replace { find, replace } => {
|
||||||
|
let reg = RegexBuilder::new(find).unicode(false).build()?;
|
||||||
|
|
||||||
|
reg.replace_all(&data, replace).to_vec()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
pub struct Deframer {
|
||||||
|
pub name: String,
|
||||||
|
pub operations: Vec<DeframeOperation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deframer {
|
||||||
|
pub fn deframe(&self, mut data: Vec<u8>) -> Vec<u8> {
|
||||||
|
for operation in &self.operations {
|
||||||
|
data = operation.do_operation(data).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::deframer::{DeframeOperation, Deframer};
|
||||||
|
use crate::formatter::format_config::FormatConfig;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_start_seq() {
|
||||||
|
let op = DeframeOperation::StartSeq {
|
||||||
|
start_seq: vec![0x11, 0x00],
|
||||||
|
};
|
||||||
|
|
||||||
|
let data = vec![0x11, 0x00, 0x55, 0x55];
|
||||||
|
let output = op.do_operation(data).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(output, vec![0x55, 0x55]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_stop_seq() {
|
||||||
|
let op = DeframeOperation::StopSeq {
|
||||||
|
stop_seq: vec![0x11, 0x00],
|
||||||
|
};
|
||||||
|
|
||||||
|
let data = vec![0x55, 0x55, 0x11, 0x00];
|
||||||
|
let output = op.do_operation(data).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(output, vec![0x55, 0x55]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_replace_seq() {
|
||||||
|
let op = DeframeOperation::Replace {
|
||||||
|
find: "\x55\x11".to_string(),
|
||||||
|
replace: vec![0x11],
|
||||||
|
};
|
||||||
|
|
||||||
|
let data = vec![0x55, 0x11, 0x55, 0x11, 0x00, 0x55];
|
||||||
|
let output = op.do_operation(data).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(output, vec![0x11, 0x11, 0x00, 0x55]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deframer() {
|
||||||
|
let deframer = Deframer {
|
||||||
|
name: "Test SLIP Deframmer".to_string(),
|
||||||
|
operations: vec![
|
||||||
|
DeframeOperation::StartSeq {
|
||||||
|
start_seq: vec![0xc0],
|
||||||
|
},
|
||||||
|
DeframeOperation::StopSeq {
|
||||||
|
stop_seq: vec![0xc0],
|
||||||
|
},
|
||||||
|
DeframeOperation::Replace {
|
||||||
|
find: r"\xDB\xDC".to_string(),
|
||||||
|
replace: vec![0xC0],
|
||||||
|
},
|
||||||
|
DeframeOperation::Replace {
|
||||||
|
find: r"\xDB\xDD".to_string(),
|
||||||
|
replace: vec![0xDB],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let data = vec![0xC0, 0xDB, 0xDC, 0xDB, 0xDD, 0xC0];
|
||||||
|
let output = deframer.deframe(data);
|
||||||
|
|
||||||
|
assert_eq!(output, vec![0xc0, 0xDB]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserialize_deframer() {
|
||||||
|
let config = FormatConfig::new(&None, &None).unwrap();
|
||||||
|
let slip = config.get_deframer("slip").unwrap();
|
||||||
|
|
||||||
|
assert_eq!(slip.name, "slip");
|
||||||
|
|
||||||
|
assert_eq!(slip.operations.len(), 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
use crate::formatter::format::FormatError;
|
|
||||||
use crate::formatter::FormatConfigError;
|
use crate::formatter::FormatConfigError;
|
||||||
|
use crate::formatter::format::FormatError;
|
||||||
use crate::parser::ByteArrayParseErr;
|
use crate::parser::ByteArrayParseErr;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
use byteorder::{BigEndian, ByteOrder, LittleEndian};
|
||||||
|
use num_bigint::{BigInt, BigUint};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub trait ByteOrderOperations: ByteOrder {
|
||||||
|
fn last_byte(buf: &mut Vec<u8>) -> Option<&mut u8>;
|
||||||
|
|
||||||
|
fn big_int(buf: &[u8], trim: usize) -> BigInt;
|
||||||
|
|
||||||
|
fn big_uint(buf: &[u8], trim: usize) -> BigUint;
|
||||||
|
|
||||||
|
fn big_u_int_to_bytes(big_int: BigUint) -> Vec<u8>;
|
||||||
|
|
||||||
|
fn big_int_to_bytes(big_int: BigInt) -> Vec<u8>;
|
||||||
|
|
||||||
|
fn pad_bytes(buf: &mut Vec<u8>, size: usize);
|
||||||
|
|
||||||
|
fn flip() -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ByteOrderOperations for BigEndian {
|
||||||
|
fn last_byte(buf: &mut Vec<u8>) -> Option<&mut u8> {
|
||||||
|
buf.first_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn big_int(buf: &[u8], trim: usize) -> BigInt {
|
||||||
|
let mut buf = buf.to_vec();
|
||||||
|
let byte = buf.first_mut().unwrap();
|
||||||
|
*byte = (*byte << trim) >> trim;
|
||||||
|
BigInt::from_signed_bytes_be(&buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn big_uint(buf: &[u8], trim: usize) -> BigUint {
|
||||||
|
let mut buf = buf.to_vec();
|
||||||
|
let byte = buf.first_mut().unwrap();
|
||||||
|
*byte = (*byte << trim) >> trim;
|
||||||
|
BigUint::from_bytes_be(&buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn big_u_int_to_bytes(big_int: BigUint) -> Vec<u8> {
|
||||||
|
big_int.to_bytes_be()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn big_int_to_bytes(big_int: BigInt) -> Vec<u8> {
|
||||||
|
big_int.to_signed_bytes_be()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_bytes(buf: &mut Vec<u8>, size: usize) {
|
||||||
|
if size <= buf.len() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pad_to = size - buf.len();
|
||||||
|
buf.splice(0..0, vec![0_u8; pad_to].iter().cloned());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flip() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ByteOrderOperations for LittleEndian {
|
||||||
|
fn last_byte(buf: &mut Vec<u8>) -> Option<&mut u8> {
|
||||||
|
buf.last_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn big_int(buf: &[u8], trim: usize) -> BigInt {
|
||||||
|
BigInt::from_signed_bytes_le(buf) >> trim
|
||||||
|
}
|
||||||
|
|
||||||
|
fn big_uint(buf: &[u8], trim: usize) -> BigUint {
|
||||||
|
BigUint::from_bytes_le(buf) >> trim
|
||||||
|
}
|
||||||
|
|
||||||
|
fn big_u_int_to_bytes(big_int: BigUint) -> Vec<u8> {
|
||||||
|
big_int.to_bytes_le()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn big_int_to_bytes(big_int: BigInt) -> Vec<u8> {
|
||||||
|
big_int.to_signed_bytes_le()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_bytes(buf: &mut Vec<u8>, size: usize) {
|
||||||
|
if size <= buf.len() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pad_to = size - buf.len();
|
||||||
|
|
||||||
|
let mut pad = vec![0_u8; pad_to];
|
||||||
|
buf.append(&mut pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flip() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
+51
-134
@@ -1,141 +1,28 @@
|
|||||||
use std::fmt::{Display, Formatter, Write};
|
use std::fmt::Write;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
use std::string::FromUtf8Error;
|
use std::string::FromUtf8Error;
|
||||||
|
|
||||||
use byteorder::{BigEndian, ByteOrder, LittleEndian, ReadBytesExt};
|
use crate::byte_stream::{ByteStream, ByteStreamError, bit_mask};
|
||||||
use num_bigint::{BigInt, BigUint};
|
use crate::formatter::bytes_operations::ByteOrderOperations;
|
||||||
use serde::Deserialize;
|
use crate::formatter::format_config::FormatConfig;
|
||||||
|
|
||||||
use crate::byte_stream::{bit_mask, ByteStream, ByteStreamError};
|
|
||||||
use crate::formatter::printers::PrintType;
|
use crate::formatter::printers::PrintType;
|
||||||
use crate::FormatConfig;
|
use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
pub trait ByteOrderOperations: ByteOrder {
|
#[derive(Debug, Clone, Error)]
|
||||||
fn last_byte(buf: &mut Vec<u8>) -> Option<&mut u8>;
|
|
||||||
|
|
||||||
fn big_int(buf: &[u8], trim: usize) -> BigInt;
|
|
||||||
|
|
||||||
fn big_uint(buf: &[u8], trim: usize) -> BigUint;
|
|
||||||
|
|
||||||
fn big_u_int_to_bytes(big_int: BigUint) -> Vec<u8>;
|
|
||||||
|
|
||||||
fn big_int_to_bytes(big_int: BigInt) -> Vec<u8>;
|
|
||||||
|
|
||||||
fn pad_bytes(buf: &mut Vec<u8>, size: usize);
|
|
||||||
|
|
||||||
fn flip() -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ByteOrderOperations for BigEndian {
|
|
||||||
fn last_byte(buf: &mut Vec<u8>) -> Option<&mut u8> {
|
|
||||||
buf.first_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn big_int(buf: &[u8], trim: usize) -> BigInt {
|
|
||||||
let mut buf = buf.to_vec();
|
|
||||||
let byte = buf.first_mut().unwrap();
|
|
||||||
*byte = (*byte << trim) >> trim;
|
|
||||||
BigInt::from_signed_bytes_be(&buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn big_uint(buf: &[u8], trim: usize) -> BigUint {
|
|
||||||
let mut buf = buf.to_vec();
|
|
||||||
let byte = buf.first_mut().unwrap();
|
|
||||||
*byte = (*byte << trim) >> trim;
|
|
||||||
BigUint::from_bytes_be(&buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn big_u_int_to_bytes(big_int: BigUint) -> Vec<u8> {
|
|
||||||
big_int.to_bytes_be()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn big_int_to_bytes(big_int: BigInt) -> Vec<u8> {
|
|
||||||
big_int.to_signed_bytes_be()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pad_bytes(buf: &mut Vec<u8>, size: usize) {
|
|
||||||
if size <= buf.len() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let pad_to = size - buf.len();
|
|
||||||
buf.splice(0..0, vec![0_u8; pad_to].iter().cloned());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flip() -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ByteOrderOperations for LittleEndian {
|
|
||||||
fn last_byte(buf: &mut Vec<u8>) -> Option<&mut u8> {
|
|
||||||
buf.last_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn big_int(buf: &[u8], trim: usize) -> BigInt {
|
|
||||||
BigInt::from_signed_bytes_le(buf) >> trim
|
|
||||||
}
|
|
||||||
|
|
||||||
fn big_uint(buf: &[u8], trim: usize) -> BigUint {
|
|
||||||
BigUint::from_bytes_le(buf) >> trim
|
|
||||||
}
|
|
||||||
|
|
||||||
fn big_u_int_to_bytes(big_int: BigUint) -> Vec<u8> {
|
|
||||||
big_int.to_bytes_le()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn big_int_to_bytes(big_int: BigInt) -> Vec<u8> {
|
|
||||||
big_int.to_signed_bytes_le()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pad_bytes(buf: &mut Vec<u8>, size: usize) {
|
|
||||||
if size <= buf.len() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let pad_to = size - buf.len();
|
|
||||||
|
|
||||||
let mut pad = vec![0_u8; pad_to];
|
|
||||||
buf.append(&mut pad);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flip() -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum FormatError {
|
pub enum FormatError {
|
||||||
ByteSteamError(ByteStreamError),
|
#[error("Byte Stream Error")]
|
||||||
|
ByteSteamError(#[from] ByteStreamError),
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
#[error("Not Supported")]
|
||||||
NotSupported,
|
NotSupported,
|
||||||
StringParseError(FromUtf8Error),
|
#[error("String Parse Error")]
|
||||||
|
StringParseError(#[from] FromUtf8Error),
|
||||||
|
#[error("Format Not Found")]
|
||||||
FormatNotFound(String),
|
FormatNotFound(String),
|
||||||
}
|
#[error("Deframmer Not Found")]
|
||||||
|
DeframmerNotFound(String),
|
||||||
impl From<ByteStreamError> for FormatError {
|
|
||||||
fn from(e: ByteStreamError) -> Self {
|
|
||||||
FormatError::ByteSteamError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<FromUtf8Error> for FormatError {
|
|
||||||
fn from(e: FromUtf8Error) -> Self {
|
|
||||||
FormatError::StringParseError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for FormatError {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
FormatError::ByteSteamError(e) => writeln!(f, "Byte stream error: {}", e),
|
|
||||||
FormatError::NotSupported => write!(f, "Field type not supported"),
|
|
||||||
FormatError::StringParseError(e) => write!(f, "String parse error: {}", e),
|
|
||||||
FormatError::FormatNotFound(format) => {
|
|
||||||
write!(f, "Unable to find sub-format: {}", format)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
@@ -392,6 +279,9 @@ pub struct Format {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
/// Flip bits
|
/// Flip bits
|
||||||
pub bit_flip: bool,
|
pub bit_flip: bool,
|
||||||
|
/// Deframmers to apply before parsing,
|
||||||
|
#[serde(default)]
|
||||||
|
pub deframers: Vec<String>,
|
||||||
/// Elements of the format
|
/// Elements of the format
|
||||||
pub fields: Vec<Field>,
|
pub fields: Vec<Field>,
|
||||||
}
|
}
|
||||||
@@ -428,7 +318,29 @@ impl Format {
|
|||||||
Ok((format_str, bit_ndx))
|
Ok((format_str, bit_ndx))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format_data(&self, data: &[u8], config: &FormatConfig) -> Result<String, FormatError> {
|
pub fn format_data(
|
||||||
|
&self,
|
||||||
|
data: &[u8],
|
||||||
|
config: &FormatConfig,
|
||||||
|
additional_deframers: Option<Vec<String>>,
|
||||||
|
) -> Result<String, FormatError> {
|
||||||
|
let mut data = data.to_vec();
|
||||||
|
|
||||||
|
let deframers = if let Some(additional_deframers) = additional_deframers {
|
||||||
|
additional_deframers
|
||||||
|
.into_iter()
|
||||||
|
.chain(self.deframers.clone())
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
self.deframers.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
for deframer_name in deframers {
|
||||||
|
let deframer = config.get_deframer(&deframer_name)?;
|
||||||
|
|
||||||
|
data = deframer.deframe(data)
|
||||||
|
}
|
||||||
|
|
||||||
let mut byte_stream = ByteStream::from(data);
|
let mut byte_stream = ByteStream::from(data);
|
||||||
|
|
||||||
let (format_str, _) = self.format_byte_stream(&mut byte_stream, 0, config)?;
|
let (format_str, _) = self.format_byte_stream(&mut byte_stream, 0, config)?;
|
||||||
@@ -439,9 +351,10 @@ impl Format {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::Format;
|
||||||
use crate::byte_stream::ByteStream;
|
use crate::byte_stream::ByteStream;
|
||||||
use crate::formatter::format::{Endianness, Field, FieldType};
|
use crate::formatter::format::{Endianness, Field, FieldType};
|
||||||
use crate::{Format, FormatConfig};
|
use crate::formatter::format_config::FormatConfig;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_format_int_4_bits() {
|
fn test_format_int_4_bits() {
|
||||||
@@ -591,9 +504,11 @@ mod tests {
|
|||||||
|
|
||||||
let mut byte_stream = ByteStream::from(b"\x3D\x70\xA3\xD7".to_vec());
|
let mut byte_stream = ByteStream::from(b"\x3D\x70\xA3\xD7".to_vec());
|
||||||
|
|
||||||
assert!(field
|
assert!(
|
||||||
.format_data(&mut byte_stream, 0, &FormatConfig::default())
|
field
|
||||||
.is_err())
|
.format_data(&mut byte_stream, 0, &FormatConfig::default())
|
||||||
|
.is_err()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -724,9 +639,11 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let config = FormatConfig {
|
let config = FormatConfig {
|
||||||
|
deframers: vec![],
|
||||||
formats: vec![Format {
|
formats: vec![Format {
|
||||||
name: "sub_format".to_string(),
|
name: "sub_format".to_string(),
|
||||||
bit_flip: false,
|
bit_flip: false,
|
||||||
|
deframers: vec![],
|
||||||
fields: vec![Field {
|
fields: vec![Field {
|
||||||
name: "test".to_string(),
|
name: "test".to_string(),
|
||||||
field_type: FieldType::String {
|
field_type: FieldType::String {
|
||||||
|
|||||||
@@ -0,0 +1,132 @@
|
|||||||
|
use crate::deframer::Deframer;
|
||||||
|
use crate::formatter::FormatConfigError;
|
||||||
|
use crate::formatter::format::{Format, FormatError};
|
||||||
|
use platform_dirs::AppDirs;
|
||||||
|
use rust_embed::RustEmbed;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::fs::{File, read_dir};
|
||||||
|
use std::io::Read;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::{fs, str};
|
||||||
|
|
||||||
|
#[derive(RustEmbed)]
|
||||||
|
#[folder = "formats/"]
|
||||||
|
#[include = "*.toml"]
|
||||||
|
struct BuiltInFormats;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone, Default)]
|
||||||
|
pub struct FormatConfig {
|
||||||
|
#[serde(default)]
|
||||||
|
pub deframers: Vec<Deframer>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub formats: Vec<Format>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FormatConfig {
|
||||||
|
fn parse_config_file(
|
||||||
|
src_file_name: &str,
|
||||||
|
config_data: &str,
|
||||||
|
) -> Result<Self, FormatConfigError> {
|
||||||
|
toml::from_str(config_data).map_err(|e| FormatConfigError::TomlError {
|
||||||
|
file_name: src_file_name.to_string(),
|
||||||
|
err: e,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_built_in_config(&mut self) -> Result<(), FormatConfigError> {
|
||||||
|
for format_file_path in BuiltInFormats::iter() {
|
||||||
|
if format_file_path.ends_with("md") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let format_file = BuiltInFormats::get(&format_file_path).unwrap();
|
||||||
|
|
||||||
|
let config_str = str::from_utf8(&format_file.data).unwrap();
|
||||||
|
|
||||||
|
let mut built_in: FormatConfig =
|
||||||
|
Self::parse_config_file(&format_file_path, config_str)?;
|
||||||
|
|
||||||
|
self.formats.append(&mut built_in.formats);
|
||||||
|
self.deframers.append(&mut built_in.deframers)
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_file_config(&mut self, config_path: &Option<PathBuf>) -> Result<(), FormatConfigError> {
|
||||||
|
if let Some(config_path) = config_path {
|
||||||
|
let mut config_file = File::open(config_path)?;
|
||||||
|
let mut config_data = String::new();
|
||||||
|
config_file.read_to_string(&mut config_data)?;
|
||||||
|
let mut arg_config =
|
||||||
|
Self::parse_config_file(config_path.to_str().unwrap(), &config_data)?;
|
||||||
|
|
||||||
|
self.formats.append(&mut arg_config.formats);
|
||||||
|
self.deframers.append(&mut arg_config.deframers);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_directory(
|
||||||
|
&mut self,
|
||||||
|
directory_path: &Option<PathBuf>,
|
||||||
|
) -> Result<(), FormatConfigError> {
|
||||||
|
if let Some(directory_path) = directory_path {
|
||||||
|
for path in read_dir(directory_path)? {
|
||||||
|
let file_name = path?.file_name();
|
||||||
|
let config_file_path = directory_path.join(file_name);
|
||||||
|
|
||||||
|
self.get_file_config(&Some(config_file_path))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(
|
||||||
|
config_path: &Option<PathBuf>,
|
||||||
|
global_config_path: &Option<PathBuf>,
|
||||||
|
) -> Result<Self, FormatConfigError> {
|
||||||
|
let mut config = FormatConfig::default();
|
||||||
|
|
||||||
|
config.get_built_in_config()?;
|
||||||
|
|
||||||
|
config.get_file_config(config_path)?;
|
||||||
|
|
||||||
|
let global_dir = match global_config_path {
|
||||||
|
Some(g) => g.clone(),
|
||||||
|
None => {
|
||||||
|
let app_dirs = AppDirs::new(Some("formaty"), true).unwrap();
|
||||||
|
|
||||||
|
if !app_dirs.config_dir.exists() {
|
||||||
|
fs::create_dir(&app_dirs.config_dir)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
app_dirs.config_dir
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
config.parse_directory(&Some(global_dir))?;
|
||||||
|
|
||||||
|
Ok(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_format(&self, name: &str) -> Result<Format, FormatError> {
|
||||||
|
Ok(self
|
||||||
|
.formats
|
||||||
|
.iter()
|
||||||
|
.find(|f| f.name.as_str() == name)
|
||||||
|
.ok_or_else(|| FormatError::FormatNotFound(name.to_string()))?
|
||||||
|
.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_deframer(&self, name: &str) -> Result<Deframer, FormatError> {
|
||||||
|
Ok(self
|
||||||
|
.deframers
|
||||||
|
.iter()
|
||||||
|
.find(|f| f.name.as_str() == name)
|
||||||
|
.ok_or_else(|| FormatError::DeframmerNotFound(name.to_string()))?
|
||||||
|
.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
-154
@@ -1,171 +1,33 @@
|
|||||||
|
pub mod bytes_operations;
|
||||||
pub mod format;
|
pub mod format;
|
||||||
|
pub mod format_config;
|
||||||
mod printers;
|
mod printers;
|
||||||
|
|
||||||
use crate::formatter::format::Format;
|
use crate::formatter::format::FormatError;
|
||||||
use platform_dirs::AppDirs;
|
use std::str;
|
||||||
use rust_embed::RustEmbed;
|
use thiserror::Error;
|
||||||
use serde::Deserialize;
|
|
||||||
use std::error::Error;
|
|
||||||
use std::fmt::{Display, Formatter};
|
|
||||||
use std::fs::{read_dir, File};
|
|
||||||
use std::io::Read;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::{fs, str};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Error)]
|
||||||
#[allow(clippy::enum_variant_names)]
|
#[allow(clippy::enum_variant_names)]
|
||||||
pub enum FormatConfigError {
|
pub enum FormatConfigError {
|
||||||
IOError(std::io::Error),
|
#[error("IO error")]
|
||||||
|
IOError(#[from] std::io::Error),
|
||||||
|
#[error("TOML Format Error in '{file_name}': {err}")]
|
||||||
TomlError {
|
TomlError {
|
||||||
file_name: String,
|
file_name: String,
|
||||||
err: toml::de::Error,
|
err: toml::de::Error,
|
||||||
},
|
},
|
||||||
Utf8Error(str::Utf8Error),
|
#[error("UTF-8 String Decode Error")]
|
||||||
FormatNotFound(String),
|
Utf8Error(#[from] str::Utf8Error),
|
||||||
}
|
#[error("Format Error")]
|
||||||
|
FormatError(#[from] FormatError),
|
||||||
impl Error for FormatConfigError {}
|
|
||||||
|
|
||||||
impl From<std::io::Error> for FormatConfigError {
|
|
||||||
fn from(e: std::io::Error) -> Self {
|
|
||||||
Self::IOError(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<str::Utf8Error> for FormatConfigError {
|
|
||||||
fn from(e: str::Utf8Error) -> Self {
|
|
||||||
Self::Utf8Error(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for FormatConfigError {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let err_msg = match self {
|
|
||||||
FormatConfigError::IOError(e) => e.to_string(),
|
|
||||||
FormatConfigError::TomlError { file_name, err } => {
|
|
||||||
format!("Error parsing {}: {}", file_name, err)
|
|
||||||
}
|
|
||||||
FormatConfigError::Utf8Error(e) => e.to_string(),
|
|
||||||
FormatConfigError::FormatNotFound(s) => format!("{} was not found.", s),
|
|
||||||
};
|
|
||||||
|
|
||||||
write!(f, "Format Config Error: {}", err_msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(RustEmbed)]
|
|
||||||
#[folder = "formats/"]
|
|
||||||
#[include = "*.toml"]
|
|
||||||
struct BuiltInFormats;
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, Default)]
|
|
||||||
pub struct FormatConfig {
|
|
||||||
pub formats: Vec<Format>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FormatConfig {
|
|
||||||
fn parse_config_file(
|
|
||||||
src_file_name: &str,
|
|
||||||
config_data: &str,
|
|
||||||
) -> Result<Self, FormatConfigError> {
|
|
||||||
toml::from_str(config_data).map_err(|e| FormatConfigError::TomlError {
|
|
||||||
file_name: src_file_name.to_string(),
|
|
||||||
err: e,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_built_in_config(&mut self) -> Result<(), FormatConfigError> {
|
|
||||||
for format_file_path in BuiltInFormats::iter() {
|
|
||||||
if format_file_path.ends_with("md") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let format_file = BuiltInFormats::get(&format_file_path).unwrap();
|
|
||||||
|
|
||||||
let config_str = str::from_utf8(&format_file.data).unwrap();
|
|
||||||
|
|
||||||
let mut built_in: FormatConfig =
|
|
||||||
Self::parse_config_file(&format_file_path, config_str)?;
|
|
||||||
|
|
||||||
self.formats.append(&mut built_in.formats);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_file_config(&mut self, config_path: &Option<PathBuf>) -> Result<(), FormatConfigError> {
|
|
||||||
if let Some(config_path) = config_path {
|
|
||||||
let mut config_file = File::open(config_path)?;
|
|
||||||
let mut config_data = String::new();
|
|
||||||
config_file.read_to_string(&mut config_data)?;
|
|
||||||
let mut arg_config =
|
|
||||||
Self::parse_config_file(config_path.to_str().unwrap(), &config_data)?;
|
|
||||||
|
|
||||||
self.formats.append(&mut arg_config.formats);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_directory(
|
|
||||||
&mut self,
|
|
||||||
directory_path: &Option<PathBuf>,
|
|
||||||
) -> Result<(), FormatConfigError> {
|
|
||||||
if let Some(directory_path) = directory_path {
|
|
||||||
for path in read_dir(directory_path)? {
|
|
||||||
let file_name = path?.file_name();
|
|
||||||
let config_file_path = directory_path.join(file_name);
|
|
||||||
|
|
||||||
self.get_file_config(&Some(config_file_path))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(
|
|
||||||
config_path: &Option<PathBuf>,
|
|
||||||
global_config_path: &Option<PathBuf>,
|
|
||||||
) -> Result<Self, FormatConfigError> {
|
|
||||||
let mut config = FormatConfig::default();
|
|
||||||
|
|
||||||
config.get_built_in_config()?;
|
|
||||||
|
|
||||||
config.get_file_config(config_path)?;
|
|
||||||
|
|
||||||
let global_dir = match global_config_path {
|
|
||||||
Some(g) => g.clone(),
|
|
||||||
None => {
|
|
||||||
let app_dirs = AppDirs::new(Some("formaty"), true).unwrap();
|
|
||||||
|
|
||||||
if !app_dirs.config_dir.exists() {
|
|
||||||
fs::create_dir(&app_dirs.config_dir)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
app_dirs.config_dir
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
config.parse_directory(&Some(global_dir))?;
|
|
||||||
|
|
||||||
Ok(config)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_format(&self, name: &str) -> Result<Format, FormatConfigError> {
|
|
||||||
Ok(self
|
|
||||||
.formats
|
|
||||||
.iter()
|
|
||||||
.find(|f| f.name.as_str() == name)
|
|
||||||
.ok_or_else(|| FormatConfigError::FormatNotFound(name.to_string()))?
|
|
||||||
.clone())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::formatter::format::Format;
|
use crate::formatter::format::Format;
|
||||||
|
use crate::formatter::format_config::FormatConfig;
|
||||||
use crate::formatter::printers::print_bytes_as_array;
|
use crate::formatter::printers::print_bytes_as_array;
|
||||||
use crate::formatter::FormatConfig;
|
|
||||||
|
|
||||||
#[derive(PartialOrd, PartialEq, Debug, Clone)]
|
#[derive(PartialOrd, PartialEq, Debug, Clone)]
|
||||||
struct CCSDSPacket {
|
struct CCSDSPacket {
|
||||||
@@ -286,7 +148,9 @@ mod test {
|
|||||||
|
|
||||||
let format = get_ccsds_format();
|
let format = get_ccsds_format();
|
||||||
let data = ccsds_packet.to_bytes();
|
let data = ccsds_packet.to_bytes();
|
||||||
let output = format.format_data(&data, &FormatConfig::default()).unwrap();
|
let output = format
|
||||||
|
.format_data(&data, &FormatConfig::default(), None)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(ccsds_packet.print(), output)
|
assert_eq!(ccsds_packet.print(), output)
|
||||||
}
|
}
|
||||||
@@ -308,7 +172,9 @@ mod test {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let data = ccsds_packet.to_bytes();
|
let data = ccsds_packet.to_bytes();
|
||||||
let output = format.format_data(&data, &FormatConfig::default()).unwrap();
|
let output = format
|
||||||
|
.format_data(&data, &FormatConfig::default(), None)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(ccsds_packet.print(), output)
|
assert_eq!(ccsds_packet.print(), output)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use byteorder::WriteBytesExt;
|
|||||||
use num_bigint::{BigInt, BigUint};
|
use num_bigint::{BigInt, BigUint};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::formatter::format::ByteOrderOperations;
|
use crate::formatter::bytes_operations::ByteOrderOperations;
|
||||||
|
|
||||||
pub fn print_bytes_as_array(data: &[u8]) -> String {
|
pub fn print_bytes_as_array(data: &[u8]) -> String {
|
||||||
format!("{:?}", data)
|
format!("{:?}", data)
|
||||||
@@ -21,7 +21,9 @@ pub fn base_notation(b: u32) -> String {
|
|||||||
#[derive(Debug, Deserialize, Clone, Default)]
|
#[derive(Debug, Deserialize, Clone, Default)]
|
||||||
#[serde(tag = "print")]
|
#[serde(tag = "print")]
|
||||||
pub enum PrintType {
|
pub enum PrintType {
|
||||||
Base { base: u32 },
|
Base {
|
||||||
|
base: u32,
|
||||||
|
},
|
||||||
ByteArray,
|
ByteArray,
|
||||||
String,
|
String,
|
||||||
#[default]
|
#[default]
|
||||||
@@ -73,7 +75,7 @@ impl PrintType {
|
|||||||
pub fn print_bytes(&self, bytes: &[u8]) -> String {
|
pub fn print_bytes(&self, bytes: &[u8]) -> String {
|
||||||
match self {
|
match self {
|
||||||
PrintType::String => std::str::from_utf8(bytes).unwrap().to_string(),
|
PrintType::String => std::str::from_utf8(bytes).unwrap().to_string(),
|
||||||
_ => print_bytes_as_array(bytes)
|
_ => print_bytes_as_array(bytes),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+18
-10
@@ -1,11 +1,12 @@
|
|||||||
use crate::error::FormatyError;
|
use crate::error::FormatyError;
|
||||||
use crate::formatter::format::Format;
|
use crate::formatter::format::Format;
|
||||||
use crate::parser::InputTypes;
|
use crate::parser::InputTypes;
|
||||||
use formatter::FormatConfig;
|
use formatter::format_config::FormatConfig;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
mod byte_stream;
|
mod byte_stream;
|
||||||
|
mod deframer;
|
||||||
mod error;
|
mod error;
|
||||||
mod formatter;
|
mod formatter;
|
||||||
mod parser;
|
mod parser;
|
||||||
@@ -48,6 +49,13 @@ pub struct Args {
|
|||||||
#[structopt(help = "Input data from stdin", short = "s", long = "stdin")]
|
#[structopt(help = "Input data from stdin", short = "s", long = "stdin")]
|
||||||
input_stdin: bool,
|
input_stdin: bool,
|
||||||
|
|
||||||
|
#[structopt(
|
||||||
|
help = "Additional deframers to apply to the data before trying to parse it",
|
||||||
|
short = "d",
|
||||||
|
long = "deframer"
|
||||||
|
)]
|
||||||
|
deframer: Option<Vec<String>>,
|
||||||
|
|
||||||
#[structopt(help = "Format to parse data as")]
|
#[structopt(help = "Format to parse data as")]
|
||||||
format: String,
|
format: String,
|
||||||
|
|
||||||
@@ -55,26 +63,26 @@ pub struct Args {
|
|||||||
data: Vec<String>,
|
data: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init() -> Result<(Vec<u8>, Format, FormatConfig), FormatyError> {
|
fn init(args: &Args) -> Result<(Vec<u8>, Format, FormatConfig), FormatyError> {
|
||||||
let args: Args = Args::from_args();
|
|
||||||
|
|
||||||
let config = FormatConfig::new(&args.config, &args.global_config)?;
|
let config = FormatConfig::new(&args.config, &args.global_config)?;
|
||||||
|
|
||||||
let format = config.get_format(&args.format)?;
|
let format = config.get_format(&args.format)?;
|
||||||
|
|
||||||
let data = if args.input_stdin {
|
let data = if args.input_stdin {
|
||||||
args.input_type.parse_stdin(args.base)?
|
args.input_type.parse_stdin(args.base)?
|
||||||
} else if let Some(input_file) = args.input_file {
|
} else if let Some(input_file) = &args.input_file {
|
||||||
args.input_type.parse_file(&input_file, args.base)?
|
args.input_type.parse_file(input_file, args.base)?
|
||||||
} else {
|
} else {
|
||||||
args.input_type.parse_arg_input(args.data, args.base)?
|
args.input_type
|
||||||
|
.parse_arg_input(args.data.clone(), args.base)?
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((data, format, config))
|
Ok((data, format, config))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let (data, format, config) = match init() {
|
let args: Args = Args::from_args();
|
||||||
|
let (data, format, config) = match init(&args) {
|
||||||
Ok((data, format, config)) => (data, format, config),
|
Ok((data, format, config)) => (data, format, config),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error initializing: {}", e);
|
println!("Error initializing: {}", e);
|
||||||
@@ -82,8 +90,8 @@ fn main() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match format.format_data(&data, &config) {
|
match format.format_data(&data, &config, args.deframer) {
|
||||||
Ok(data) => println!("{}", data),
|
Ok(data) => println!("{}", data),
|
||||||
Err(_) => print!("Unable to parse data"),
|
Err(err) => print!("Unable to parse data: {}", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+24
-1
@@ -1,3 +1,4 @@
|
|||||||
|
use hex::FromHexError;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::io::{BufRead, BufReader, Error, Read};
|
use std::io::{BufRead, BufReader, Error, Read};
|
||||||
@@ -10,6 +11,7 @@ use std::string::FromUtf8Error;
|
|||||||
pub enum InputTypes {
|
pub enum InputTypes {
|
||||||
Array,
|
Array,
|
||||||
String,
|
String,
|
||||||
|
HexString,
|
||||||
Binary,
|
Binary,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,6 +24,7 @@ impl FromStr for InputTypes {
|
|||||||
match s.as_str() {
|
match s.as_str() {
|
||||||
"array" | "a" => Ok(Self::Array),
|
"array" | "a" => Ok(Self::Array),
|
||||||
"string" | "s" => Ok(Self::String),
|
"string" | "s" => Ok(Self::String),
|
||||||
|
"hex_string" | "h" => Ok(Self::HexString),
|
||||||
"binary" | "b" => Ok(Self::Binary),
|
"binary" | "b" => Ok(Self::Binary),
|
||||||
_ => Err(format!("Invalid input type '{}'", s)),
|
_ => Err(format!("Invalid input type '{}'", s)),
|
||||||
}
|
}
|
||||||
@@ -83,6 +86,7 @@ impl InputTypes {
|
|||||||
let data = match self {
|
let data = match self {
|
||||||
InputTypes::Array => Self::parse_array(str_arr, base)?,
|
InputTypes::Array => Self::parse_array(str_arr, base)?,
|
||||||
InputTypes::String => str_arr.join(" ").as_bytes().to_vec(),
|
InputTypes::String => str_arr.join(" ").as_bytes().to_vec(),
|
||||||
|
InputTypes::HexString => hex::decode(str_arr.join(" "))?,
|
||||||
InputTypes::Binary => return Err(ByteArrayParseErr::UnsupportedFormat),
|
InputTypes::Binary => return Err(ByteArrayParseErr::UnsupportedFormat),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -114,7 +118,7 @@ impl InputTypes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
InputTypes::Array | InputTypes::String => {
|
InputTypes::Array | InputTypes::String | InputTypes::HexString => {
|
||||||
let str_data = String::from_utf8(data)?;
|
let str_data = String::from_utf8(data)?;
|
||||||
self.parse_arg_input(vec![str_data], base)
|
self.parse_arg_input(vec![str_data], base)
|
||||||
}
|
}
|
||||||
@@ -144,6 +148,7 @@ pub enum ByteArrayParseErr {
|
|||||||
UnsupportedFormat,
|
UnsupportedFormat,
|
||||||
FileError(std::io::Error),
|
FileError(std::io::Error),
|
||||||
ParseStringError(FromUtf8Error),
|
ParseStringError(FromUtf8Error),
|
||||||
|
HexDecodeError(hex::FromHexError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ParseIntError> for ByteArrayParseErr {
|
impl From<ParseIntError> for ByteArrayParseErr {
|
||||||
@@ -164,6 +169,12 @@ impl From<FromUtf8Error> for ByteArrayParseErr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<FromHexError> for ByteArrayParseErr {
|
||||||
|
fn from(e: FromHexError) -> Self {
|
||||||
|
Self::HexDecodeError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for ByteArrayParseErr {
|
impl Display for ByteArrayParseErr {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
let err_msg = match self {
|
let err_msg = match self {
|
||||||
@@ -174,6 +185,7 @@ impl Display for ByteArrayParseErr {
|
|||||||
}
|
}
|
||||||
ByteArrayParseErr::FileError(e) => format!("Unable to parse file: {}", e),
|
ByteArrayParseErr::FileError(e) => format!("Unable to parse file: {}", e),
|
||||||
ByteArrayParseErr::ParseStringError(e) => format!("Unable to parse string: {}", e),
|
ByteArrayParseErr::ParseStringError(e) => format!("Unable to parse string: {}", e),
|
||||||
|
ByteArrayParseErr::HexDecodeError(e) => format!("Failed to parse hex string: {}", e),
|
||||||
};
|
};
|
||||||
|
|
||||||
write!(f, "{}", err_msg)
|
write!(f, "{}", err_msg)
|
||||||
@@ -261,4 +273,15 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(string.as_bytes(), out)
|
assert_eq!(string.as_bytes(), out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_hex_string() {
|
||||||
|
let hex_string = "00112233445566778899aabbccddeeff".to_string();
|
||||||
|
|
||||||
|
let out = InputTypes::HexString
|
||||||
|
.parse_arg_input(vec![hex_string.clone()], None)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(hex::decode(hex_string).unwrap(), out);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user