Initial commit

This commit is contained in:
Philip (a-0) 2023-07-02 20:40:33 +02:00
commit 2fd06a5e83
40 changed files with 1354 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

202
Cargo.lock generated Normal file
View file

@ -0,0 +1,202 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41"
dependencies = [
"memchr",
]
[[package]]
name = "itoa"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "misplib"
version = "0.1.0"
dependencies = [
"phf",
"regex-macro",
"serde",
"serde_json",
]
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "phf"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [
"phf_macros",
"phf_shared",
]
[[package]]
name = "phf_generator"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
dependencies = [
"phf_shared",
"rand",
]
[[package]]
name = "phf_macros"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
dependencies = [
"phf_generator",
"phf_shared",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "phf_shared"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
dependencies = [
"siphasher",
]
[[package]]
name = "proc-macro2"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]]
name = "regex"
version = "1.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-macro"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12fa36e7add16db296640bba993a65dae2a0088a8e5cd5f935c8bfbd3710145b"
dependencies = [
"once_cell",
"regex",
]
[[package]]
name = "regex-syntax"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
[[package]]
name = "ryu"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
[[package]]
name = "serde"
version = "1.0.164"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.164"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "siphasher"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
[[package]]
name = "syn"
version = "2.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"

14
Cargo.toml Normal file
View file

@ -0,0 +1,14 @@
[package]
name = "misplib"
version = "0.1.0"
edition = "2021"
[dependencies]
phf = { version = "0.11", default-features = false, features = [ "macros" ] }
regex-macro = "0.2.0"
serde = { version = "1.0.164", features = ["derive"] }
serde_json = "1.0.99"
[lib]
name = "misplib"
path = "src/lib.rs"

18
README.md Normal file
View file

@ -0,0 +1,18 @@
# misplib
This crate contains all data structures present in the [MISP API](https://www.misp-project.org/openapi/), and accompanying helper functions.
## Versioning system
For every commit in the [main MISP repository](https://github.com/MISP/MISP) which changes the [API definition](https://raw.githubusercontent.com/MISP/MISP/develop/app/webroot/doc/openapi.yaml) a new version of misplib is created. Version numbers are assigned as ascending integers.
Multiple versions of misplib can be accessed, they are defined in `src/v<version number>/mod.rs`, in this file you can also find the unique commit hash of the MISP repository this version is developed for.
## Contributing
You can contribute to this project by
- adding tests to existing types
- proposing a new version if you detected a change in the MISP API
- adding helper functions (such as implementations of useful traits etc.)
- working on the serialization and deserialization of all types. The goal is to be automatically parse any MISP API object into valid rust misplib objects.
- anything else you can think of :)
## Contact
Feel free to discuss and ask questions in https://matrix.to/#/#misplib:a-0.me

10
src/lib.rs Normal file
View file

@ -0,0 +1,10 @@
macro_rules! expose_submodules {
( $( $x:ident ),* ) => {
$(
mod $x;
pub use self::$x::*;
)*
};
}
pub mod v1;

11
src/v1/mod.rs Normal file
View file

@ -0,0 +1,11 @@
macro_rules! default_derive {
($i:item) => {
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, PartialEq, Debug)]
$i
};
}
mod schemas;
pub use schemas::*;

View file

@ -0,0 +1,11 @@
default_derive!{
pub struct Attribute {
//TODO
}
}
#[test]
fn valid1() {
todo!()
}

View file

@ -0,0 +1,47 @@
use regex_macro::regex;
default_derive!{
pub struct AttributeAttachment {
content: String
}
}
impl TryFrom<&str> for AttributeAttachment {
type Error = &'static str;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let re = regex!("^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$");
if re.is_match(value) {
Ok(AttributeAttachment { content: String::from(value) })
}
else {
Err("Failed to parse AttributeAttachment")
}
}
}
impl Into<String> for AttributeAttachment {
fn into(self) -> String {
self.content
}
}
#[test]
fn forbidden_char() {
let att: Result<AttributeAttachment, _> = "uqiolgfnluiqoegn&".try_into();
println!("{:?}", att);
assert!(att.is_err())
}
#[test]
fn valid1() {
let att: Result<AttributeAttachment, _> = "".try_into();
assert_eq!(att, Ok(AttributeAttachment {content: String::from("")}))
}
#[test]
fn valid2() {
let att: Result<AttributeAttachment, _> = "TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu".try_into();
assert_eq!(att, Ok(AttributeAttachment{content: String::from("TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu")}))
}

View file

@ -0,0 +1,53 @@
use phf::phf_set;
default_derive!{
pub struct AttributeCategory {
cat: String,
}
}
impl TryFrom<&str> for AttributeCategory {
type Error = &'static str;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let valid_categories: phf::Set<&'static str> = phf_set!("Internal reference", "Targeting data", "Antivirus detection", "Payload delivery", "Artifacts dropped", "Payload installation", "Persistence mechanism", "Network activity", "Payload type", "Attribution", "External analysis", "Financial fraud", "Support Tool", "Social network", "Person", "Other");
if value.len() <= 255 && valid_categories.contains(value) {
Ok(AttributeCategory { cat: value.to_string() })
}
else {
Err("Failed to parse AttributeCategory")
}
}
}
impl Into<String> for AttributeCategory {
fn into(self) -> String {
self.cat
}
}
#[test]
fn empty() {
let cat: Result<AttributeCategory, _> = "".try_into();
assert!(cat.is_err())
}
#[test]
fn oversized() {
let cat: Result<AttributeCategory, _> = format!("{:>256}", "Test").as_str().try_into();
assert!(cat.is_err())
}
#[test]
fn valid1() {
let cat: Result<AttributeCategory, _> = "Internal reference".try_into();
assert_eq!(cat, Ok(AttributeCategory { cat: "Internal reference".to_string() }))
}
#[test]
fn valid2() {
let cat: Result<AttributeCategory, _> = "Person".try_into();
assert_eq!(cat, Ok(AttributeCategory {cat: String::from("Person")}))
}

View file

@ -0,0 +1,44 @@
default_derive!{
pub struct AttributeComment {
text: String
}
}
impl TryFrom<&str> for AttributeComment {
type Error = &'static str;
fn try_from(value: &str) -> Result<Self, Self::Error> {
if value.len() <= 65535 {
Ok(AttributeComment { text: value.to_string() })
}
else {
Err("Failed to parse AttributeComment")
}
}
}
impl Into<String> for AttributeComment {
fn into(self) -> String {
self.text
}
}
#[test]
fn oversized() {
let comment: Result<AttributeComment, _> = format!("{:>65536}", "Test").as_str().try_into();
assert!(comment.is_err())
}
#[test]
fn valid1() {
let comment: Result<AttributeComment, _> = "".try_into();
assert_eq!(comment, Ok(AttributeComment { text: "".to_string() }))
}
#[test]
fn valid2() {
let comment: Result<AttributeComment, _> = "Comment".try_into();
assert_eq!(comment, Ok(AttributeComment{text: String::from("Comment")}))
}

View file

@ -0,0 +1,44 @@
use super::UUID;
default_derive!{
pub struct AttributeEventUUID {
uuid: String
}
}
impl TryFrom<&str> for AttributeEventUUID {
type Error = &'static str;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let uuid: Result<UUID, _> = value.try_into();
match uuid {
Ok(id) => Ok(AttributeEventUUID{uuid: id.into()}),
Err(_) => Err("Failed to parse AttributeEventUUID"),
}
}
}
impl Into<String> for AttributeEventUUID {
fn into(self) -> String {
self.uuid
}
}
#[test]
fn empty() {
let uuid: Result<AttributeEventUUID, _> = "".try_into();
assert!(uuid.is_err())
}
#[test]
fn oversized() {
let uuid: Result<AttributeEventUUID, _> = format!("{:>37}", "Test").as_str().try_into();
assert!(uuid.is_err())
}
#[test]
fn valid1() {
let uuid: Result<AttributeEventUUID, _> = "c99506a6-1255-4b71-afa5-7b8ba48c3b1b".try_into();
assert_eq!(uuid, Ok(AttributeEventUUID{ uuid: "c99506a6-1255-4b71-afa5-7b8ba48c3b1b".to_string() }))
}

View file

@ -0,0 +1,57 @@
use regex_macro::regex;
default_derive!{
pub struct AttributeId {
id: String
}
}
impl TryFrom<&str> for AttributeId {
type Error = &'static str;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let re = regex!("^[[:digit:]]+$");
if re.is_match(value) && value.len() <= 10 {
Ok(AttributeId{ id: value.to_string() })
}
else {
Err("Failed to parse AttributeId")
}
}
}
impl Into<String> for AttributeId {
fn into(self) -> String {
self.id
}
}
#[test]
fn empty() {
let id: Result<AttributeId, _> = "".try_into();
assert!(id.is_err())
}
#[test]
fn oversized() {
let id: Result<AttributeId, _> = "12345678910".try_into();
assert!(id.is_err())
}
#[test]
fn forbidden_char() {
let id: Result<AttributeId, _> = "123r5".try_into();
assert!(id.is_err())
}
#[test]
fn valid1() {
let id: Result<AttributeId, _> = "0".try_into();
assert_eq!(id, Ok(AttributeId{id: String::from("0")}))
}
#[test]
fn valid2() {
let id: Result<AttributeId, _> = "123456789".try_into();
assert_eq!(id, Ok(AttributeId{id: String::from("123456789")}));
}

View file

@ -0,0 +1,25 @@
use super::Attribute;
default_derive!{
pub struct AttributeList {
attrs: Vec<Attribute>
}
}
impl From<Vec<Attribute>> for AttributeList {
fn from(value: Vec<Attribute>) -> Self {
AttributeList { attrs: value }
}
}
impl Into<Vec<Attribute>> for AttributeList {
fn into(self) -> Vec<Attribute> {
self.attrs
}
}
#[test]
fn valid1() {
todo!()
}

View file

@ -0,0 +1,11 @@
default_derive!{
pub struct AttributeWithoutId {
//TODO
}
}
#[test]
fn valid1() {
todo!()
}

View file

@ -0,0 +1,11 @@
default_derive!{
pub struct AttributeRestSearchFilter {
//TODO
}
}
#[test]
fn valid1() {
todo!()
}

View file

@ -0,0 +1,25 @@
use super::AttributeRestSearchListItem;
default_derive!{
pub struct AttributeRestSearchList {
items: Vec<AttributeRestSearchListItem>
}
}
impl From<Vec<AttributeRestSearchListItem>> for AttributeRestSearchList {
fn from(value: Vec<AttributeRestSearchListItem>) -> Self {
AttributeRestSearchList { items: value }
}
}
impl Into<Vec<AttributeRestSearchListItem>> for AttributeRestSearchList {
fn into(self) -> Vec<AttributeRestSearchListItem> {
self.items
}
}
#[test]
fn valid1() {
todo!()
}

View file

@ -0,0 +1,11 @@
default_derive!{
pub struct AttributeRestSearchListItem {
//TODO
}
}
#[test]
fn valid1() {
todo!()
}

View file

@ -0,0 +1,11 @@
default_derive!{
pub struct AttributeStatisticsResponse {
stats: Vec<(String, i64)>
}
}
#[test]
fn valid1() {
todo!()
}

View file

@ -0,0 +1,255 @@
use phf::phf_set;
default_derive! {
pub struct AttributeType {
t: String
}
}
impl TryFrom<&str> for AttributeType {
type Error = &'static str;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let valid_types: phf::Set<&'static str> = phf_set!(
"md5",
"sha1",
"sha256",
"filename",
"pdb",
"filename|md5",
"filename|sha1",
"filename|sha256",
"ip-src",
"ip-dst",
"hostname",
"domain",
"domain|ip",
"email",
"email-src",
"eppn",
"email-dst",
"email-subject",
"email-attachment",
"email-body",
"float",
"git-commit-id",
"url",
"http-method",
"user-agent",
"ja3-fingerprint-md5",
"jarm-fingerprint",
"favicon-mmh3",
"hassh-md5",
"hasshserver-md5",
"regkey",
"regkey|value",
"AS",
"snort",
"bro",
"zeek",
"community-id",
"pattern-in-file",
"pattern-in-traffic",
"pattern-in-memory",
"pattern-filename",
"pgp-public-key",
"pgp-private-key",
"yara",
"stix2-pattern",
"sigma",
"gene",
"kusto-query",
"mime-type",
"identity-card-number",
"cookie",
"vulnerability",
"cpe",
"weakness",
"attachment",
"malware-sample",
"link",
"comment",
"text",
"hex",
"other",
"named pipe",
"mutex",
"process-state",
"target-user",
"target-email",
"target-machine",
"target-org",
"target-location",
"target-external",
"btc",
"dash",
"xmr",
"iban",
"bic",
"bank-account-nr",
"aba-rtn",
"bin",
"cc-number",
"prtn",
"phone-number",
"threat-actor",
"campaign-name",
"campaign-id",
"malware-type",
"uri",
"authentihash",
"vhash",
"ssdeep",
"imphash",
"telfhash",
"pehash",
"impfuzzy",
"sha224",
"sha384",
"sha512",
"sha512/224",
"sha512/256",
"sha3-224",
"sha3-256",
"sha3-384",
"sha3-512",
"tlsh",
"cdhash",
"filename|authentihash",
"filename|vhash",
"filename|ssdeep",
"filename|imphash",
"filename|impfuzzy",
"filename|pehash",
"filename|sha224",
"filename|sha384",
"filename|sha512",
"filename|sha512/224",
"filename|sha512/256",
"filename|sha3-224",
"filename|sha3-256",
"filename|sha3-384",
"filename|sha3-512",
"filename|tlsh",
"windows-scheduled-task",
"windows-service-name",
"windows-service-displayname",
"whois-registrant-email",
"whois-registrant-phone",
"whois-registrant-name",
"whois-registrant-org",
"whois-registrar",
"whois-creation-date",
"x509-fingerprint-sha1",
"x509-fingerprint-md5",
"x509-fingerprint-sha256",
"dns-soa-email",
"size-in-bytes",
"counter",
"datetime",
"port",
"ip-dst|port",
"ip-src|port",
"hostname|port",
"mac-address",
"mac-eui-64",
"email-dst-display-name",
"email-src-display-name",
"email-header",
"email-reply-to",
"email-x-mailer",
"email-mime-boundary",
"email-thread-index",
"email-message-id",
"github-username",
"github-repository",
"github-organisation",
"jabber-id",
"twitter-id",
"dkim",
"dkim-signature",
"first-name",
"middle-name",
"last-name",
"full-name",
"date-of-birth",
"place-of-birth",
"gender",
"passport-number",
"passport-country",
"passport-expiration",
"redress-number",
"nationality",
"visa-number",
"issue-date-of-the-visa",
"primary-residence",
"country-of-residence",
"special-service-request",
"frequent-flyer-number",
"travel-details",
"payment-details",
"place-port-of-original-embarkation",
"place-port-of-clearance",
"place-port-of-onward-foreign-destination",
"passenger-name-record-locator-number",
"mobile-application-id",
"chrome-extension-id",
"cortex",
"boolean",
"anonymised"
);
if value.len() <= 100 && valid_types.contains(value) {
Ok(AttributeType {
t: value.to_string(),
})
} else {
Err("Failed to parse AttributeType")
}
}
}
impl Into<String> for AttributeType {
fn into(self) -> String {
self.t
}
}
#[test]
fn empty() {
let t: Result<AttributeType, _> = "".try_into();
assert!(t.is_err())
}
#[test]
fn oversized() {
let t: Result<AttributeType, _> = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvw".try_into();
assert!(t.is_err())
}
#[test]
fn unknown() {
let t: Result<AttributeType, _> = "abcde".try_into();
assert!(t.is_err())
}
#[test]
fn valid1() {
let t: Result<AttributeType, _> = "text".try_into();
assert_eq!(
t,
Ok(AttributeType {
t: "text".to_string()
})
)
}
#[test]
fn valid2() {
let t: Result<AttributeType, _> = "filename|sha512/224".try_into();
assert_eq!(
t,
Ok(AttributeType {
t: "filename|sha512/224".to_string()
})
)
}

View file

@ -0,0 +1,44 @@
default_derive!{
pub struct AttributeValue {
value: String
}
}
impl TryFrom<&str> for AttributeValue {
type Error = &'static str;
fn try_from(value: &str) -> Result<Self, Self::Error> {
if value.len() <= 131071 {
Ok(AttributeValue { value: value.to_string() })
}
else {
Err("Failed to parse AttributeValue")
}
}
}
impl Into<String> for AttributeValue {
fn into(self) -> String {
self.value
}
}
#[test]
fn oversized() {
let val: Result<AttributeValue, _> = format!("{:>131072}", "Test").as_str().try_into();
assert!(val.is_err())
}
#[test]
fn valid1() {
let val: Result<AttributeValue, _> = "".try_into();
assert_eq!(val, Ok(AttributeValue { value: "".to_string() }))
}
#[test]
fn valid2() {
let val: Result<AttributeValue, _> = "123456789".try_into();
assert_eq!(val, Ok(AttributeValue {value: String::from("123456789")}))
}

View file

@ -0,0 +1,17 @@
use super::{FullDecayingModel, DecayingModel};
default_derive!{
pub struct DecayScore {
score: f64,
base_score: f64,
decayed: bool,
decaying_model: DecayingModel,
full_decaying_model: FullDecayingModel,
}
}
#[test]
fn valid1() {
todo!()
}

View file

@ -0,0 +1,13 @@
use super::DecayScore;
default_derive!{
pub struct DecayScoreList {
scores: Vec<DecayScore>
}
}
#[test]
fn valid1() {
todo!()
}

View file

@ -0,0 +1,12 @@
default_derive!{
pub struct DecayingModel {
id: String,
name: String,
}
}
#[test]
fn valid1() {
todo!()
}

View file

@ -0,0 +1,15 @@
default_derive!{
pub struct DecayingModelParameters {
lifetime: f64,
decay_speed: f64,
threshold: f64,
default_base_score: f64,
base_score_config: Vec<(String, f64)>
}
}
#[test]
fn valid1() {
todo!()
}

View file

@ -0,0 +1,16 @@
use super::{AttributeType, AttributeCategory};
default_derive!{
pub struct DescribeAttributeTypesResponse {
sane_defaults: Vec<(AttributeType, (String, i64))>,
types: Vec<AttributeType>,
categories: Vec<AttributeCategory>,
category_type_mappings: Vec<(AttributeCategory, Vec<AttributeType>)>
}
}
#[test]
fn valid1() {
todo!()
}

11
src/v1/schemas/event.rs Normal file
View file

@ -0,0 +1,11 @@
default_derive!{
pub struct Event {
//TODO
}
}
#[test]
fn valid1() {
todo!()
}

View file

@ -0,0 +1,40 @@
use regex_macro::regex;
default_derive!{
pub struct EventAttributeCount {
count: String
}
}
impl TryFrom<&str> for EventAttributeCount {
type Error = &'static str;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let re = regex!("^[[:digit:]]+$");
if re.is_match(value) {
Ok(EventAttributeCount { count: value.to_string() })
}
else {
Err("Failed to parse EventId")
}
}
}
impl Into<String> for EventAttributeCount {
fn into(self) -> String {
self.count
}
}
#[test]
fn empty() {
let id: Result<EventAttributeCount, _> = "".try_into();
assert!(id.is_err())
}
#[test]
fn valid1() {
let id: Result<EventAttributeCount, _> = "12345".try_into();
assert_eq!(id, Ok(EventAttributeCount { count: "12345".to_string() }))
}

View file

@ -0,0 +1,52 @@
use regex_macro::regex;
default_derive!{
pub struct EventId {
id: String,
}
}
impl TryFrom<&str> for EventId {
type Error = &'static str;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let re = regex!("^[[:digit:]]+$");
if value.len() <= 10 && re.is_match(value) {
Ok(EventId { id: value.to_string() })
}
else {
Err("Failed to parse EventId")
}
}
}
impl Into<String> for EventId {
fn into(self) -> String {
self.id
}
}
#[test]
fn empty() {
let id: Result<EventId, _> = "".try_into();
assert!(id.is_err())
}
#[test]
fn oversized() {
let id: Result<EventId, _> = "12345678910".try_into();
assert!(id.is_err())
}
#[test]
fn valid1() {
let id: Result<EventId, _> = "12345".try_into();
assert_eq!(id, Ok(EventId { id: "12345".to_string() }))
}
#[test]
fn valid2() {
let id: Result<EventId, _> = "0123456789".try_into();
assert_eq!(id, Ok(EventId { id: "0123456789".to_string() }))
}

View file

@ -0,0 +1,37 @@
default_derive!{
pub struct EventInfo {
text: String
}
}
impl TryFrom<&str> for EventInfo {
type Error = &'static str;
fn try_from(value: &str) -> Result<Self, Self::Error> {
if value.len() <= 65535 {
Ok(EventInfo { text: value.to_string() })
}
else {
Err("Failed to parse EventInfo")
}
}
}
impl Into<String> for EventInfo {
fn into(self) -> String {
self.text
}
}
#[test]
fn oversized() {
let info: Result<EventInfo, _> = format!("{:>65536}", "Test").as_str().try_into();
assert!(info.is_err())
}
#[test]
fn valid1() {
let info: Result<EventInfo, _> = "Informational text".try_into();
assert_eq!(info, Ok(EventInfo { text: "Informational text".to_string() }))
}

View file

@ -0,0 +1,11 @@
default_derive!{
pub struct EventWithoutId {
//TODO
}
}
#[test]
fn valid1() {
todo!()
}

View file

@ -0,0 +1,11 @@
default_derive!{
pub struct EventOrganisation {
//TODO
}
}
#[test]
fn valid1() {
todo!()
}

View file

@ -0,0 +1,11 @@
default_derive!{
pub struct EventProposalEmailLock {
lock: bool
}
}
#[test]
fn valid1() {
todo!()
}

View file

@ -0,0 +1,11 @@
default_derive!{
pub struct EventReport {
//TODO, not described in OpenAPI documentation
}
}
#[test]
fn valid1() {
assert!(true)
}

View file

@ -0,0 +1,11 @@
default_derive!{
pub struct EventTag {
//TODO
}
}
#[test]
fn valid1() {
todo!()
}

View file

@ -0,0 +1,52 @@
use regex_macro::regex;
default_derive!{
pub struct EventTagId {
id: String
}
}
impl TryFrom<&str> for EventTagId {
type Error = &'static str;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let re = regex!("^[[:digit:]]+$");
if value.len() <= 10 && re.is_match(value) {
Ok(EventTagId { id: value.to_string() })
}
else {
Err("Failed to parse EventTagId")
}
}
}
impl Into<String> for EventTagId {
fn into(self) -> String {
self.id
}
}
#[test]
fn empty() {
let id: Result<EventTagId, _> = "".try_into();
assert!(id.is_err())
}
#[test]
fn oversized() {
let id: Result<EventTagId, _> = "12345678910".try_into();
assert!(id.is_err())
}
#[test]
fn valid1() {
let id: Result<EventTagId, _> = "12345".try_into();
assert_eq!(id, Ok(EventTagId { id: "12345".to_string() }))
}
#[test]
fn valid2() {
let id: Result<EventTagId, _> = "0123456789".try_into();
assert_eq!(id, Ok(EventTagId { id: "0123456789".to_string() }))
}

View file

@ -0,0 +1,25 @@
use super::EventTag;
default_derive!{
pub struct EventTagList {
event_tags: Vec<EventTag>
}
}
impl From<Vec<EventTag>> for EventTagList {
fn from(value: Vec<EventTag>) -> Self {
EventTagList { event_tags: value }
}
}
impl Into<Vec<EventTag>> for EventTagList {
fn into(self) -> Vec<EventTag> {
self.event_tags
}
}
#[test]
fn valid1() {
todo!()
}

View file

@ -0,0 +1,11 @@
default_derive!{
pub struct ExtendedAttribute {
//TODO
}
}
#[test]
fn valid1() {
todo!()
}

View file

@ -0,0 +1,11 @@
default_derive!{
pub struct FullDecayingModel {
//TODO
}
}
#[test]
fn valid1() {
todo!()
}

36
src/v1/schemas/mod.rs Normal file
View file

@ -0,0 +1,36 @@
expose_submodules!(
attribute_attachment,
attribute_category,
attribute_comment,
attribute_event_uuid,
attribute_id,
attribute_list,
attribute_no_id,
attribute_rest_search_filter,
attribute_rest_search_list_item,
attribute_rest_search_list,
attribute_statistics_response,
attribute_type,
attribute_value,
attribute,
decay_score_list,
decay_score,
decaying_model_parameters,
decaying_model,
describe_attribute_types_response,
event_attribute_count,
event_id,
event_info,
event_no_id,
event_organisation,
event_proposal_email_lock,
event_report,
event_tag_id,
event_tag_list,
event_tag,
event,
extended_attribute,
full_decaying_model,
uuid
);

46
src/v1/schemas/uuid.rs Normal file
View file

@ -0,0 +1,46 @@
use regex_macro::regex;
default_derive!{
pub struct UUID {
id: String
}
}
impl TryFrom<&str> for UUID {
type Error = &'static str;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let re = regex!("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$");
if value.len() <= 36 && re.is_match(value) {
Ok(UUID { id: value.to_string() })
}
else {
Err("Failed to parse UUID")
}
}
}
impl Into<String> for UUID {
fn into(self) -> String {
self.id
}
}
#[test]
fn empty() {
let uuid: Result<UUID, _> = "".try_into();
assert!(uuid.is_err())
}
#[test]
fn oversized() {
let uuid: Result<UUID, _> = format!("{:>37}", "Test").as_str().try_into();
assert!(uuid.is_err())
}
#[test]
fn valid1() {
let uuid: Result<UUID, _> = "c99506a6-1255-4b71-afa5-7b8ba48c3b1b".try_into();
assert_eq!(uuid, Ok(UUID { id: "c99506a6-1255-4b71-afa5-7b8ba48c3b1b".to_string() }))
}